Start with something like Basic.component, which defines the basic elements any component must have, even when deriving from an existing component. This new component is called EqualContainer. See the existing *.component files under the com.nokia.sdt.series60.componentlibrary plugin and review the component.qualifiedName attributes.
By using a base component, you inherit almost everything from CCoeControl. The major exceptions are the <designerImages>, <symbian>, <documentation>, and <sourceGen> elements. Each of these is expected to have a unique definition in any component.
An integer property for the padding between equal-sized regions is defined in the EqualContainer.component file:
<property name="padding" type="integer" descriptionKey="paddingDescription" category="Appearance" />
Then a new enumeration type and a property is defined. Note that the enumeration declaration lives inside <componentDefinition>, not inside <component>. The enumeration values have the same internal and external names unless a *.properties file redefines them, such as defining a different name for "horizontal”.
<enumPropertyDeclaration qualifiedName="com.example.EqualContainer.Direction"> <enumElement value="horizontal"/> <enumElement value="vertical"/> </enumPropertyDeclaration>
Next the property is defined. Note that the property is declared with the <enumProperty> element, not <property>.
<enumProperty name="direction" type="com.example.EqualContainer.Direction" descriptionKey="paddingDescription" category="Appearance" />
The component will now have all of CCoeControl’s properties, plus these two.
NOTE Properties from base components may be overridden using the <propertyOverrides> and <propertyOverride> elements.
For this example, no attributes are needed yet, since they are inherited. This makes EqualContainer a top-level layout container with a background property which accepts events, etc.
Implementations are inherited from CCoeControl, however we will override the layout behavior. Layout behavior is overridden by implementing the com.nokia.sdt.datamodel.adapter.ILayout implementation, as seen in the <implementations> section of the EqualContainer.component file. The layout can be defined in a script (EqualContainer.js) using the prototype EqualContainer. Unless numerous implementations are added, one script file will suffice to hold multiple interfaces.
In EqualContainer.js, make the basic prototype:
function EqualContainer() { }
Then add the layout function:
/** Lay out children. */ EqualContainer.prototype.layout = function(instance, laf) { var properties = instance.properties; … }
Refer to the EqualContainer.js file for the complete function definition. In the layout function, the current component is the parent, and its job is to resize and move its children. Some initial data is calculated, accessing properties from the passed-in instance in the properties property.
var properties = instance.properties; var width = properties.size.width; var height = properties.size.height; var padding = properties.padding;
NOTE A newer method for accessing the bounds of a component instance is accomplished by implementing Rectangle instance.getLayoutBounds() and providing settings to instance.setLayoutBounds(Rectangle). Direct access to properties is historical and may be more cumbersone.
The getLayoutChildren() utility function in the ComponentUtils.js file is used to get the layout children from the container. That is, excluding any menus, notes, or other items that are not actually laid out in the container’s bounds.
include("ComponentUtils.js"); //outside function
…
var kids = getLayoutChildren(instance.children);
Then iterate and modify the childrens’ properties.location and properties.size properties to lay them out horizontally or vertically depending on our instance’s properties.direction property. Note that in script, enumerators take on the same string value as in the <enumPropertyDeclaration value="..." /> attribute.
if (properties.direction == "horizontal") { … }
The layout routine recalculates absolute positions and sizes and directly changes child properties. The location and size for most components are recalculated at design time, such as when the Screen Layout changes from 176x208 to 240x320, etc. So, components have the appearance of being dynamic, although at design time, only absolute values for the current layout are retained.
Script support is available for getting the child's preferred size. Layout scripts can query the preferred size of a child's instance object, for example:
for (var i in instance.children) { var child = instance.children[i];
preferredSize = child.getPreferredSize(100, 20); // define child layout stuff }
The getPreferredSize() function defines two integer arguments, wHint and hHint (width and height). It is optional whether or not a component's implementation uses the hints.
The Equal Container component generates code that derives a class from CCoeControl, just like the CCoeControl component does, but overrides the generated LayoutControls() method and adds our own method to dynamically spread out the children.
Source generation is not inherited by derived components. This is because source generation for a container encompasses so many disparate files, locations, bits of code, and templates that it would be more difficult and error-prone to describe how to leave things out in the derived component than to describe what you want to bring in.The example uses most of what CCoeControl provides. Fortunately, this component’s sourcegen was declared in such a way as to make it relatively easy to inherit most of its behavior. The main categories are template groups and inherited prototypes.
Open the <sourceGen> element. In this state, a component takes full control of its sourcegen and invokes sourcegen on its children. Without <sourceGen>, the default behavior is to gather contributions from children and pass them up. The template groups define the header file and the main file, which defines the class that derives from CCoeControl.
<!-- import the main base component sourcegen --> <useTemplateGroup ids="HeaderFile SourceFile"/>
Each <template> inside a <templateGroup> must have an id attribute to be inherited via <useTemplateGroup>. If you look at the CCoeControl.component file, you will note that every <template> has an id for this purpose. There is a straggler template, which CCoeControl itself inherits from its base. However, presently it is not possible to double-inherit templates.
<useTemplate ids="DrawBodyBgColor"/>
A container must invoke sourcegen on children and pass its contributions along with its own. This is done explicitly to allow the component to massage, reorder, and collate such contributions as needed. A bit of <inline> script code is provided, which invokes CCoeControl’s child contribution gathering.
<inline> this.initSourceGen(contribs, instance, form); this.getChildSourceGen(contribs, instance, form);
this.finishSourceGen(contribs, instance, form); </inline>
These routines are added to the CCoeControl component’s sourceGen prototype for use by derived script. Less helpful components directly invoke the code contained in these routines.
For the most part, the script that is being called (in CCoeControl.component <inline>s) is the same as any container. A call to Engine.generateChildContributions() invokes the <sourceGen> element on all child contributions, with the passed-in form regular expression used to select particular <templates>. The form is also tested explicitly in <inline>. CCoeControl augments the incoming form regex to get CCoeControl-specific contributions from children (if any) along with generic contributions.Our equal container example is itself a child, and must provide a means for the parent to access it and control its lifetime. The container lets the parent gain access by adding contributions that contribute to phases (same as CCoeControl’s children).
These contributions are packaged in template groups.
< useTemplateGroup ids="ChildDeclContribs ChildConstructionContribs" />