Implementations Element

## Implementations

The preceding sections described characteristics of components which could be represented statically in XML. Implementations allow for script or code to dynamically extend a component's behavior in the UI Designer. Implementation also allows automatic layout, dynamic containment rules, direct label editing, component properties validation and more.

A component uses the <implementations> XML element to define implementations. This element contains zero or more <implementation> elements. A single implementation element contains one or more <interface> elements, telling which interfaces are implemented, and either a <script> or a <class> element which implements those interfaces. It's up to you how many interfaces to implement with one class or script. Implementations may be provided in JavaScript or Java code. For Java implementations, merely implement the interface and ensure its containing package is exported from the plugin. This document doesn't discuss Java implementations further, since a JavaScript implementation is easier to use in many respects.

Example implementations declaration:

    <implementations>
        <implementation>
           <interface  id="com.nokia.sdt.datamodel.adapter.IVisualAppearance"/>
           <interface  id="com.nokia.sdt.datamodel.adapter.IDirectLabelEdit"/>
           <script  file="MyComponent.js" prototype="MyPrototype"/>
        </implementation>
    </implementations>

Some of the most common interfaces are:

- com.nokia.sdt.datamodel.adapter.IVisualAppearance
- com.nokia.sdt.datamodel.adapter.ILayout
- com.nokia.sdt.datamodel.adapter.IQueryContainment
- com.nokia.sdt.datamodel.adapter.IDirectLabelEdit
- com.nokia.sdt.datamodel.adapter.IComponentInstancePropertyListener

### JavaScript

The UI Designer allows the use of JavaScript to customize components. This allows component development without the need of a JDK and more dynamic updating of component implementations while the UI Designer is running. The Mozilla Project’s Rhino engine is used for JavaScript interpretation (http://www.mozilla.org/rhino). This engine allows for JavaScript to call Java routines, and Java routines to call JavaScript. Other sources of JavaScript information include http://javascript-reference.info/ and http://www.mozilla.org/js/language/.

#### Connecting JavaScript to Implementations

For component implementations, the <implementation> element can contain a <script> statement that specifies a JavaScript file and prototype that implements an interface. UI Designer instantiates the prototype into an object that is used throughout the lifetime of a component instance. Prototypes can be named and referenced across components.

Script declaration example:

    <implementations>
      <implementation>
        <interface id="com.nokia.sdt.datamodel.adapter.IVisualAppearance"/>
        <script file=”MyComponent_render.js” prototype=”Render” />
      </implementation>
     …
    </implementations>

The file MyComponent_render.js should be located relative to the component definition (.component) file, and define a prototype named Render. Most implementations have two variants of the interface. One is for Java use, and another is for JavaScript. For example, IVisualAppearance and IScriptVisualAppearance. The <interface> statement references the non-script interface but the implementation in the script uses the “script” variant. Usually the only difference is the addition of a WrappedInstance argument.

Usually, a set of related implementations share the same JavaScript prototype, such as rendering, layout, and direct label editing. The method names used in interfaces are intended to be specific enough so they won’t clash.

MyComponent_render.js example:

    function Render() {
     }

    Render.prototype.draw = function(instance, laf, graphics) {
       var bounds = instance.getRenderingBounds();
       graphics.drawLine(bounds.x, bounds.y, bounds.w, bounds.h);
    }

    Render.prototype.getPreferredSize = function(instance, laf, wHint, hHint) {
       if (wHint < 0) wHint = 32;
       if (hHint < 0) hHint = 24;
       return new Point(wHint, hHint);
    }

#### Wrapped Types

JavaScript hosted in the UI Designer has access to component instances, properties, and attributes through a script-friendly interface.

- WrappedInstance — This class wraps IComponentInstance. A variable of this type called “instance” is usually the first argument to implementation methods, and is the primary point of entry to the other types.

WrappedInstance
Properties | Description
--- | ---
name | The instance name.
memberName | Name of the member (“i” + titlecased name)
className | Name of this instance’s className property, or null.
properties | Reference to WrappedProperties, where given properties are accessible as members or by dictionary lookup. For example, instance.properties[name] or instance.properties.name.
attributes | Reference to WrappedAttributes, where given attribute values are accessible as members or by dictionary lookup. Most attributes use dashes in names, so the dictionary lookup is probably mandatory. For example, instance.attributes[is-layout-object].
componentId | Qualified name of component
component | Reference to WrappedComponent
events | Array of WrappedEventBinding
children | Return array of children as WrappedInstances (0-element array if no children)
parent | Return parent WrappedInstance or null
Function | Description
toString() | Converts an object to a string and returns the result.
findChildrenOfType(componentId) | Return array of WrappedInstance of children derived from the given component id. Returns 0-element array if none match.
findChildOfType(componentId) | Like findChildrenOfType but returns single instance
forceLayout() | Forces ILayout#layout() on self and children
forceRedraw() | Forces redraw of self and children
isInstanceOf(componentId) | Returns true if component is instance of given component id or derived component id.
void updatePropertySource() | Force rebuild of property list for extension properties.
Rectangle getRenderingBounds() | Get the bounds for rendering, with x=y=0
Rectangle getLayoutBounds() | Get the layout bounds
setLayoutBounds(Rectangle) | Set the layout bounds
getPreferredSize() | get the preferred size from ILayout or IVisualAppearance

- WrappedComponent — This class wraps IComponent.

WrappedComponent
Properties | Description
--- | ---
id | The qualified component id. For example, com.nokia.sdt.series60.CAknView.
friendlyName | Localized component name, for example “Vertical Label”.
attributes | The WrappedAttributes for the component.
minSDKVersion | A org.osgi.framework.Version instance reflecting the minimum SDK version from the <symbian> element.
maxSDKVersion | A org.osgi.framework.Version instance reflecting the maximum SDK version from the <symbian> element.
Function | Description
toString() | Converts an object to a string and returns the result.
isOfType(componentId) | Return true if component matches or is derived from given component id.

- WrappedProperties — This is an object with property names as members. It can also be accessed as a dictionary. For example, if a component defines a property name, then properties.name or properties["name"] can be used to access the property. If no such property exists, the reference is undefined.

> For defined properties, access of a property returns a JavaScript-friendly type.

> - Booleans, strings, integers act naturally. Localized string properties map to the string entry for the current language.
> - Component references return strings that can be looked up with lookupInstanceByName().
> - Compound properties are themselves WrappedProperties (e.g. instance.properties.size.width). The “editableValue” property of compound properties resolves to the value edited in cell editors (through ICompoundPropertyValueConverter#getEditableValue() ), or returned by IReconcileProperty#createDisplayValue(). For the com.nokia.sdt.series60.CBA component, “instance.properties.cba” is a compound property and a WrappedProperties reference, but “instance.properties.cba.editableValue” is a string returned by reconciling the contained properties to a single string (e.g. OPTIONS_BACK or …CUSTOM).
> - Array properties are modifiable JavaScript arrays. The length property returns the size of the array. Elements are accessed as in instance.properties.arrayProperty[0].

- WrappedAttributes — This is an object of the component attributes for the instance’s component, accessible as members or a dictionary. Since most attributes use dashes, they are illegal as JavaScript members and should be accessed through the dictionary. An attribute’s value is the string placed in the component definition. Attributes are strings.

- WrappedEventBindings — The instance.events array is a reference to this class, which is a dictionary of events bound to the current instance. Each entry is a WrappedEventBinding. For example, instance.events[“selected”] returns a WrappedEventBinding or null.

> NOTE This is not for accessing all possible events supported by a component, which is not supported. If supported, this would appear on WrappedComponent.

- WrappedEventBinding — This wraps IEventBinding. The names used as properties match the names given to sourcegen globals that can be used to conditionally generate locations and templates based on whether a given event is bound to the current instance.

WrappedEventBinding
Properties | Description
--- | ---
eventId | Name of event (e.g. “selected”)
eventName | Friendly name of event (e.g. “Focus lost”)
handlerName | Base name of function handling the event (e.g. “HandleSelectedEvent”)
handlerSymbol | Encoded information describing the location of the event in source code. Not generally useful in script. The name starts with the domain name and contains domain-specific information to look up the symbol. In the cpp domain the location path is used. For example “cpp@src//MyFile.cpp/function(CContainer::HandleSelectedEvent(CCoeControl*))".

#### JavaScript Globals

Scripts have access to several sets of global routines for use in implementations. These are the commonly accessible routines.

##### Generic Routines

These routines do not depend on the current instance or data model.

> getLocalizedStrings(filebase) — Returns a reference to the com.nokia.sdt.component.symbian.ILocalizedStrings interface for the given base filename. This is usually used when a library script has *.properties files and is used as in:

> - strings = getLocalizedStrings(“utils”) // look in utils.properties, etc.
> - localized = strings.getString(“key”)

> print(string) — Print out the string to the console without a newline.

> println(string) — Print out the string to the console with a newline

> titleCase(string) — Return the string with all words titlecased (“Like This”).

> getPluginClass(pluginId, className) — Return a JavaScript reference (NativeJavaClass) of the given class loaded from the given plugin. This is primarily used to access static functions.

> formatString(format, …) — Invoke MessageFormat.format() on the given format string and its arguments.

> makeValidIdentifier(name) — Return the given name converted to a valid identifier by replacing invalid characters with ‘_’ and adding an initial ‘_’ if necessary.

> newStatusBuilder() — Return a new com.nokia.sdt.utils.StatusBuilder class.

##### Component-specific Routines

These routines rely on the current instance and data model to influence their behavior. As such, they do not take instance arguments.

> include(filename) — Include contents of the named file (relative to the current file) into the current script. This is not textual inclusion but semantic inclusion. The included file is compiled separately and reused if the same include() appears in other scripts. Thus, a file can include file1.js which can include utils.js, and then include file2.js which includes utils.js, and overall, only one copy of utils.js will exist.

> lookupString(key) — Return the localized entry for ‘key’ in the component’s *.properties file.

> lookupInstanceByName(name) — Return the WrappedInstance for the given instance name property in the current data model.

> getComponentVersions() — Return the org.osgi.framework.Version for the data model’s components (e.g. 2.6, 2.8, 3.0).

> logStatus(IStatus) — Log an IStatus to the Error Log (get an IStatus from StatusBuilder#createStatus).

> log(string) — Log an info message to the Problems view in Carbide.c++; associated with the *.uidesign file.

> error(string) — Log an error message to the Problems view; associated with the *.uidesign file.

> warning(string) — Log a warning message to the Problems view; associated with the *.uidesign file.

> isInRootModel() — Tell whether the current instance is in the root (application.uidesign) model.

> getProjectName() — Return the name of the current project.

> getRootModelInstanceOfType(componentId) — Return a WrappedInstance for the first root model instance with the given component id.

#### JavaScript Libraries

The include(filename) function opens the door for using libraries. The S60 component library plugin uses these to accelerate common tasks. Unfortunately our libraries are not easily accessible from custom component projects, since you would need to hardcode a path out of your workspace into our plugin.

You can copy code from the libraries for your own use, but do not directly reference the files since the contents are subject to change from release to release. Most useful are the implLibrary.js and renderLibrary.js files. The implLibrary.js file makes handy use of JavaScript to dynamically implement interfaces by adding members to a prototype. Helpers for hooking up common direct label and image editing implementations are provided. The renderLibrary.js file contains useful routines for finding out the appropriate background color (when rendering text) and rendering images. The formLibrary.js file helps automate support for hosting a control either in a container or in a form.

The srcgenLibrary.js file provides helpers for emitting property setting code in C++ for loading bitmap or SVG images, and for generating contributions. If appropriate resources exist, use source mapping instead.

#### Rendering

When implementing IVisualAppearance to render components in the editor, the UI Designer provides easy access to the SWT graphics library. The org.eclipse.swt.graphics.GC class is wrapped for scripting. An instance of this class is instantiated to draw onto a bitmap with a size corresponding to the current layout size. The bitmap supports a transparent pixel model (rather than alpha blending) and is initially fully transparent.

##### Wrapped org.eclipse.swt.graphics.GC Class

The SWT GC class is wrapped and extended. The GC is enhanced with the ability to modify colors that will conflict with the transparent pixel value (which may be retrieved via getTransparentColor() ).

Scripting Example:

Device getDevice() Returns the Device the GC was created on.

Draw a string which takes formatting flags.

string — The string to draw
bounds — The rectangle in which to draw
flags — Mask of IFont.xxx flags
lineGap — Extra pixel gap between lines

    public void drawFormattedString(String string, Rectangle bounds, int flags, int lineGap)
    public Device getDevice()          // Get the device the GC was created with.
    public int getOffX()               // Get the X offset all drawing is translated by.
    public void setOffX(int offX)      // Set the X offset. This is separate from gc.setTransform.
    public int getOffY()               // Get the Y offset.
    public void setOffY(int offY)      // Set the Y offset all drawing is translated by. This is separate from gc.setTransform.
    public Color getTransparentColor() // Get the color allocated for the transparent pixel.
    public Color getSubstituteColor()  // Get the color used when drawing operations try to use the transparent pixel.

##### Font Support

In the UI Designer, fonts are expected to be referenced through the ILookAndFeel instance passed to rendering and layout functions (usually called "laf"). This provides IFont instances that are rendered specially for the UI Designer. Host fonts may be referenced with the appropriate SWT methods, but if you want to use custom fonts, we recommend making them available to the look and feel system. For S60, see com.nokia.sdt.component.symbian under /data/s60/fonts/ for the typical font installation directory. In the same plugin, see /data/laf/qvga_fonts.inc for an example of how font and sizes are mapped to LAF keys for a QVGA layout.

##### IFontConstants bitmask constants

IFontConstants provides constants which are combined with the logical OR operator to control the GC#drawFormattedString() and GC#getFormattedStringTexts() family of methods.

    /** Ignore overflow (text is clipped) */
        public static final  int OVERFLOW_IGNORE = (0 << 0);

    /** Truncate text  before overflow */
        public static  final int OVERFLOW_TRUNCATE = (1 << 0);

    /** Use an  ellipsis to indicate truncation */
        public static  final int OVERFLOW_ELLIPSIS = (2 << 0);
        public static  final int OVERFLOW_MASK = (3 << 0);

        public static  final int DIRECTION_LEFT_TO_RIGHT = (0 << 2);
        public static  final int DIRECTION_RIGHT_TO_LEFT = (1 << 2);
        public static  final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = (2 << 2);
        public static  final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = (3 << 2);
        public static  final int DIRECTION_MASK = (3 << 2);

        public static  final int DRAW_TRANSPARENT = (0 << 4);
        public static  final int DRAW_OPAQUE = (1 << 4);
        public static  final int DRAW_MASK = (3 << 4);

        public static  final int ALIGN_LEFT = (0 << 6);
        public static  final int ALIGN_RIGHT = (1 << 6);
        public static  final int ALIGN_CENTER = (2 << 6);
        public static  final int ALIGN_MASK = (3 << 6);

        public static final  int WRAPPING_NONE = (0 << 8);
        public static  final int WRAPPING_ENABLED = (1 << 8);
        public static  final int WRAPPING_MASK = (1 << 8);

        public static  final int OPTIONS_NONE = (0 << 11);
        public static  final int OPTIONS_UNDERLINE = (1 << 11);
        public static  final int OPTIONS_STRIKETHROUGH = (2 << 11);
        public static  final int OPTIONS_BOLD = (4 << 11);
        public static  final int OPTIONS_EXTRABOLD = (12 << 11);
        // next: 16
        public static  final int OPTIONS_MASK = (15 << 11);

        // note: on by  default
        public static  final int ANTIALIAS_ON = (0 << 15);
        public static  final int ANTIALIAS_OFF = (1 << 15);
        public static final int ANTIALIAS_MASK = (1  << 15);

        /** default */
        public static final int VERTICAL_ALIGN_TOP = (0 << 16);
        public static final int VERTICAL_ALIGN_BOTTOM =(1 << 16);
        public static final int VERTICAL_ALIGN_CENTER = (2 << 16);
        public static final int VERTICAL_ALIGN_MASK = (3 << 16);

##### Rendering Globals

Use of the look and feel object (type ILookAndFeel) is recommended for looking up colors, fonts, and images that can vary based on the current layout, but globals are available for other uses as needed.

Example of Colors object:

>     getColor(int red, int green, int blue)

> Returns an SWT Color initialized with the given RGB value. The color is cached and automatically disposed. For example, Colors.getColor(128, 128, 255).

Example of Fonts object:

>     getLocalFont(filename, size)

> Returns an IFont instance, which is passed to GC routines ordinarily taking SWT Font. The filename is a Truetype font file relative to the current script’s directory. The font is cached and automatically disposed.

>     getGlobalFont(filename, size)

> Same as getLocalFont, but finds the file relative to the com.nokia.sdt.component.symbian plugin's /data directory.

Example of Image objects:

Image Objects
newImage(Device, width, height) | Creates a new SWT Image; device can be graphics.getDevice(). Such images are cached and automatically disposed.
public Image getImage(Device device, String relPath) | Get an image, accessed by key (filename). Such an image will be loaded only once. Use copyImage() if you want to change a shared image. The image shouldn't be disposed explicitly when created through this call. device relPath — path relative to the current script returns a cached Image
public Image copyImage(Device device, Image image) | Copy an image (from anywhere). The copy needn't be disposed explicitly when created through this call.
public Image createIcon(Image bitmap, Image bitmapMask) | Create an image from a foreground and a mask.
public Image createAlphaMaskedIcon(Image bitmap, Image alphaMask) | Create an image from a foreground and a mask.

### Common Issues and Troubleshooting

The following limitations should be considered when using JavaScript.

- JavaScript is a dynamically typed language and functions are matched based only by name. If a caller calls a routine with too few or too many arguments, there is no indication of the problem except that arguments are undefined or ignored.
- The interface between Java and JavaScript routines may convert function arguments between Java types and JavaScript types. Component implementations look like JavaScript and Java, so the type conversion may occasionally yield unexpected results when making calls between UI Designer prototype methods and other JavaScript methods in the same script. Most commonly, numbers may become strings. If you find odd behavior occurring, explicitly convert a string into a number with var – 0.
  Numbers are automatically converted to strings based on context, but if a number unexpectedly becomes a float before it turns into a string, use “” + var when you are sure you have an integer.
- switch() on strings — The values returned by WrappedProperties references are not necessarily the same kind of string JavaScript supports with the switch() statement. Thus, switch() on strings may not work as expected. Instead, use nested if statements or a lookup table. An example for calling routines using a lookup table:

>     table = {
>        “string1” : MyClass.prototype.routine1,
>        “string2” : MyClass.prototype.routine2
>     }
>     table[key].apply(this, [ <arguments> ])  // calls this.routine1(arguments) or this.routine2(arguments)

- Stack traces — When an exception occurs in JavaScript code, various entries in the stack trace may resolve to locations in script. Look for these at the end of lines coming from the class “org.mozilla.javascript.gen.*”.