Index of pages
  Introduction
  Resources
  Windows and Controls
  The Data Base
  Scripting
  Templates
  Extensions



Purpose and goals

Templates can be used as a method of hiding advanced functionality behind a simple interface, or as a way of managing frequently used definitions in one centralized location, allowing the user to avoid creating multiple similar definitions and being able to make changes to one centralized location.

Templates currently operate only on window controls.

Interface definition and encapsulation for basic users

Templates allow the creation of custom control types by defining new control type tags, and hiding advanced functionality such as scripting and making the components easier to use.

The following example illustrates a complex control type based on numberfield. It takes care of monitoring other number fields and automatically tracking changes in them to update the value in the control. The linking is performed using the custom <source> definitions.

<linkednumber name="meleeattackbonus" source="attackbonus.melee">
  <anchored>
    <to>combatframe</to>
    <position>insidetop</position>
    <offset>-20,100</offset>
    <size>
      <height>30</height>
    </size>
  </anchored>
  <frame>
    <name>bonus</name>
    <offset>5,5,5,5</offset>
  </frame>
  <font>sheetnumber</font>
  <displaysign />
  <readonly />
  <description>
    <text>Melee attack</text>
  </description>
  
  <!-- Custom fields -->
  <source>
    <name>attackbonus.base</name>
    <op>+</op>
  </source>
  <source>
    <name>abilities.strength.bonus</name>
    <op>+</op>
  </source>
</linkednumber>

Object orientation support and design for advanced users

Object oriented design provides several benefits, such as being able to divide components into more easily maintained packages and being able to utilize basic components and to extend them for purposes that are similar to the original without having to start from scratch.

Templates can be nested (see below) allowing for single inheritance. Taking advantage of proper function overloading requires a degree of planning, due to the structure of Lua environments in use. The preference has been to make the system as simple as possible for the beginning scripter, and allowing for more advanced structures with a few special cases.

Defining templates

Templates are defined using the <template>. The template tag must have the "name" attribute defined.

The body of the template definition should start with a tag containing a type of windowcontrol being extended by the template. This tag should not contain any "name" or "source" attributes. A template name can also be used, in order to create an inherited template.

The following example defines a template called "checkbox". It is based on genericcontrol and defines default values for two properties used by the script block.

<template name="checkbox">
  <genericcontrol>
    <stateicons>
      <on>indicator_checkon</on>
      <off>indicator_checkoff</off>
    </stateicons>
    <script file="scripts/template_checkbox.lua" />
  </genericcontrol>
</template>

Using templates

To use a template, declare a control as normal, treating the template name as the control type. Define the name for the control as usual as an attribute to the element tag.

The template inherits any child tags and script blocks from the template. They can be overridden by the implementing control, useful for making default values available but overridable. See the section "Merge rules" below for more information.

The following declaration creates a control based on the "checkbox" template defined above. It supplements the generic template by defining a location and tooltip for the control, and overrides the default icons used.

<checkbox name="spontaneous">
  <anchored>
    <left>
      <anchor>left</anchor>
      <offset>5</offset>
    </left>
    <bottom>
      <anchor>bottom</anchor>
      <offset>-1</offset>
    </bottom>
    <size>
      <width>20</width>
      <height>20</height>
    </size>
  </anchored>
  <stateicons>
    <on>indicator_casterspontaneous</on>
    <off>indicator_casterprep</off>
  </stateicons>
  <tooltip>
    <text>Toggle spontaneous/prepared</text>
  </tooltip>
</checkbox>

Merge rules

By default, the template definition and the control definition using the template will have their child tags merged, meaning that any tag present in one will be present in the end result, including any child tags. If both the control definition and the template have a similar tag with different child tags, the tag will be created with both children.

For example, observe the template and control in the following example definition.

<template name="mergetest">
  <genericcontrol>
    <shared>
      <templatesubnode>From template</templatesubnode>
    </shared>
    <fromtemplate>Unique to template</fromtemplate>
  </genericcontrol>
</template>

<mergetest>
  <shared>
    <controlsubnode>From control</controlsubnode>
  </shared>
  <fromcontrol>Unique to control</fromcontrol>
</mergetest>

The result of the merge is given below.

<genericcontrol>
  <shared>
    <templatesubnode>From template</templatesubnode>
    <controlsubnode>From control</controlsubnode>
  </shared>
  <fromtemplate>Unique to template</fromtemplate>
  <fromcontrol>Unique to control</fromcontrol>
</genericcontrol>

Simply merging is not sufficient for some uses in templates. Therefore, any tag in a template may contain a "mergerule" attribute taking one of the following values. When a tag in the definition contains multiple children with the same name (i.e. a list of similar child tags), only the topmost is checked when looking for a merge rule.

  • "merge": Synonymous with the default operation
  • "replace": If the implementing control contains a tag in the same position as this one, removes the tag in the template and all its children. This is useful if a tag in the definition should contain values for e.g. multiple states as children of a parent tag, and an override should replace the child values as well as the main tag.
  • "add": Instead of replacing a value as a result of a merge, a tag in the same position as this one is instead created as another similar tag in the result. This is useful if a template presents a default list of states or values, but the implementing control is allowed to add new values to the list.
  • "resetandadd": This is similar to "replace", but after one replace, reverts to "add" mode. The result is that a set of similar tags in an implementing control will override a similar set in the template.

When a template is inherited by another template, care should be taken to confirm that the outer template provides any necessary merge rules as well.

The following example defines a template that accepts a list of source values, defined through the use of <source> tags. The empty tag with the "resetandadd" definition is required for the implementing control to inherit the "add" rule, necessary for defining a list of sources.

<template name="linkednumber">
  <numberfield>
    <source mergerule="resetandadd" />
    <script file="scripts/template_linkednumber.lua" />
  </numberfield>
</template>

Nesting templates

Templates can be nested, meaning that a template is defined with another template as the source type.

Taking merge rules into account, nested templating is rather straightforward as far as the XML definitions go. Achieving some special features such as function overloading requires special attention to the script block design, however.

Inheritance

A template has it's own script environment, similar to a normal script block in a control. Any control or nesting template derived from a template has a separate environment for its script block, as well. The indexing in a script block inheriting a template is set up so that all the variables in the template script block are usable in the script block inheriting the template as well. This means that a control script can simply call a function defined in a template script block, and it will work. There are two pitfalls, however.

First, the closure of the function called is the environment of the template script block, and it cannot access variables or functions in the inheriting control's environment. Second, any variable or function defined in the inheriting script block will make similarly named variables in the inherited environment unavailable.

The self and super variables

To remedy the problems described above, there are two special variables defined in each script block inheriting another.

The variable self always refers to the topmost environment of the control. This is useful as a means for functions in the inner, inherited script blocks to access variables and functions in the inheriting environment.

The variable super refers to the inherited template script block environment. This is useful for defining functions that overload, i.e. have the same name and function, as functions in the inherited environment. The overloading function can use super to optionally access items in the inherited environment, such as the original function.

Note that the environments pointed to by these two variables still implement the same inheritance rules described above. All non-overloaded members in environments inherited by the target environment are accessible.

Overloading functions

Overloading refers to creating a function in a script block that has the same name and functionality as a similar function in an inherited script block. The actual technical definition of the term might be considered to comprise other meaning as well, but the design methodology is useful for inherited template scripts as well.

An example of overloading follows. The two functions defined here are used to calculate the value of a field with several sources.

function sourceUpdate()
  if self.onSourceUpdate then
    self.onSourceUpdate();
  end
end

function onSourceUpdate(source)
  setValue(calculateSources());
end

The template functionality is to call the onSourceUpdate function provided, and simply set the value to the one returned by calculateSources. A control based on this template might contain the following function. This example is taken from the d20 AC calculation, where the field contains a base number of 10, to which a series of modifiers is applied. The onSourceUpdate function derived is called as a result of the self.onSourceUpdate call. It, in turn, calls the calculateSources from the inherited environment, adds the number 10, and returns the result.

function onSourceUpdate()
  setValue(10 + calculateSources());
end

'Fantasy Grounds' is a trademark of SmiteWorks Ltd. All other trademarks are the property of their respective owners.
© 2004-2010 SmiteWorks Ltd. ALL RIGHTS RESERVED.
Privacy policy