Extending the Rich UI widget set

You can use EGL or JavaScript™ to create a new widget type.

Rich UI widgets

A Rich UI widget is written in EGL and is of type RUIWidget (specifically, egl.ui.rui.RUIWidget). You often need to do only as follows:
  • Specify an HTML tag name in property tagName. Alternatively, specify a declared Widget in property targetWidget, in which case the following statements apply:
    • The HTML tag that is the basis of the type of the referenced widget is the basis of the Widget type that you are defining
    • You can use the name of the referenced widget to access the functions and properties that are defined for the type of that widget

    If you specify both tagName and targetWidget, the latter applies.

  • Specify the @VEWidget complex property if you want the widget to be represented in the palette.
  • Specify the @EGLProperty complex property for each EGL property. The values of EGL properties are set when a developer creates a widget that is based on the widget type that you are writing.
  • Specify an on-construction function and set a CSS class name in that function.
  • Build the functionality from other widgets.
Here is the outline of an H3 definition, which requires no import statements:
Handler H3 type RUIWidget{tagName = "h3", onConstructionFunction = start,
   @VEWidget{
				category = "New Widgets",
				template = "my.package.H3{ text=\"The Heading Text\" }",
				smallIcon = "icons/h3small.gif",
				largeIcon = "icons/h3large.gif" 	}}

   text String  { @EGLProperty { getMethod=getText, setMethod=setText }, 
                  @VEProperty{}};
   function start()
      class = "EglRuiH3";
   end

   function setText(textIn String in)
      text = textIn;
      this.innerText = textIn;
   end

   function getText() returns (String)
      return (text);
   end
end
Given this definition, you can create widgets that are based on the type H3. For example, the following declaration creates a box with a nested H3 widget:
ui Box { children = [ 
            new H3 { text = "Summary" }
       	]};  	

@VEWidget

When you write a Rich UI widget (or an external-type widget—type egl.ui.rui.Widget—as described in another topic), you can specify the complex property @VEWidget. The EGL editor uses the details of any @VEWidget property in the workspace, adding entries to the palette when you refresh the palette. The property has the following fields:
category (type STRING)
In the palette, the category in which the widget is listed. The categories are listed in alphabetical order.

If the category does not exist, a new category will be created with the name you specify. The category is particularly useful to distinguish same-named widgets such as Rich UI buttons and Dojo buttons.

description (type STRING)
In the palette, the description that is displayed when the user hovers over the widget entry.
displayName (type STRING).
In the palette, the widget name. The names are listed in alphabetical order within a category.

The default value is the name of either the external type or the handler of type RUIWidget.

template (type STRING)
The part of the widget type declaration that follows the widget name, excluding the semicolon. In the example, the declaration includes a set-value block, as is necessary to avoid an error. Here is an example declaration:
myH3 my.package.H3{ text="The Heading Text" };
If you want the EGL system code to specify the widget type and to include a qualifier (such as my.package) only if necessary, start the template string with the typeName variable. Here is an example:
template = "${typeName}{}"
Here is a related declaration, assuming that the widget type shown earlier is in the same package as the handler that receives the declaration:
myH3 H3{};
Here is another example:
template = "${typeName}{ text=\"The Heading Text\" }"
Here is the related declaration:
myH3 H3{ text="The Heading Text" };
smallIcon (type STRING)
The path for the gif file that contains the small icon that is displayable on the palette. The path is relative to the project directory. For example, if the project is com.egl, the gif files listed earlier are in com.egl/icons.
largeIcon (type STRING)
The path for the gif file that contains the large icon that is displayable on the palette. The path is relative to the project directory. For example, if the project is com.egl, the gif files listed earlier are in com.egl/icons.
container (type @VEContainer)
Specify the container field as follows:
container{@VEContainer{}}
The presence of container has the following implications:
  • At run time, the widget can include other widgets.
  • At development time, the EGL developer can drag-and-drop other widgets into the widget being defined.
  • The widget must include a field named children. The field named children must be of type Widget[] ; and you must define the property @EGLProperty for that field, as described later.
In addition, @VEWidget lets you specify which RUIWidget properties and events (and external-type properties and events) are to be displayed in the Properties view. For example, a displayed entry for backgroundColor is provided for any handler of type RUIWidget (or even for a widget provided by an external type); but backgroundColor might not be appropriate for the widget you are defining. Here are the @VEWidget property fields that filter the properties and events that are otherwise displayed by default:
eventFilter (STRING[])
An array of event names (such as ["onClick", "OnScroll"]) to include or exclude from display, depending on the value of eventFilterType.
eventFilterType (type INT)
One of the following values (either the integer or the related constant):
  1. RUILib.EXCLUDE_ALL excludes all the events defined for RUIWidget (type egl.ui.rui.RUIWidget) and, in the case of external types, for Widget (type egl.ui.rui.Widget). In this case, any values for eventFilter are ignored.
  2. RUILib.EXCLUDE_ALL_EXCEPT excludes all the events defined for RUIWidget and Widget except those specified in the value of eventFilter.
  3. RUILib.INCLUDE_ALL includes all the events defined for RUIWidget and Widget. In this case, values for eventFilter are ignored.
  4. RUILib.INCLUDE_ALL_EXCEPT includes all the events defined for RUIWidget and Widget except those specified in the value of eventFilter.
propertyFilter (STRING[])
An array of property names (such as ["font", "fontSize"]) to include or exclude from display, depending on the value of propertyFilterType.
propertyFilterType (type INT)
One of the following values (either the integer or the related constant):
  1. RUILib.EXCLUDE_ALL excludes all the properties defined for RUIWidget (type egl.ui.rui.RUIWidget) and, in the case of external types, for Widget (type egl.ui.rui.Widget). In this case, any values for propertyFilter are ignored.
  2. RUILib.EXCLUDE_ALL_EXCEPT excludes all the properties defined for RUIWidget and Widget except those specified in the value of propertyFilter.
  3. RUILib.INCLUDE_ALL includes all the events defined for RUIWidget and Widget. In this case, values for propertyFilter are ignored.
  4. RUILib.INCLUDE_ALL_EXCEPT includes all the events defined for RUIWidget and Widget except those specified in the value of propertyFilter.
Here is an example of an external type for which the only displayed event types are onScroll and onChange and for which the only displayed properties are color and backgroundColor:
ExternalType DojoButton extends DojoBase type JavaScriptObject {
    relativePath = "dojo/widgets",
    javaScriptName = "DojoButton",
    includeFile = "dojo/widgets/dojobutton.html",
    @VEWidget{
        category = "Dojo",
        template = "dojo.widgets.DojoButton { text = \"Button\" }",
        displayName = "Button",
        smallIcon = "icons/ctool16/dijit_button_pal16.gif",
        largeIcon = "",
        propertyFilterType  = RUILib.EXCLUDE_ALL_EXCEPT,
        propertyFilter  = ["color", "backgroundColor"],
				eventFilterType  = RUILib.EXCLUDE_ALL_EXCEPT,
        eventFilterType  = ["onScroll", "onChange"]
    }

Changes to @VEWidget are available to the EGL editor only if you refresh the palette. You refresh the palette by clicking the Refresh palette tool on the Design surface, as noted in Using the tools on the Design surface.

@EGLProperty

@EGLProperty identifies EGL functions that get and set the EGL field value when an EGL Rich UI application uses the property. You can use this property without specifying function names if the names of the functions are specified with the word get or set followed by the variable name. For example, if the variable is UpperLimit and the Rich UI Widget type includes functions named getUpperLimit() and setUpperLimit(), you only need to add the complex property, as in this example:
UpperLimit INT { @EGLProperty{} };
The property fields in @EGLProperty are as follows:
getMethod
A string (enclosed in quotation marks) containing the name of the get method for the specified variable (do not include parentheses). The method has no parameters, and its return value has the same type as the field.
setMethod
A string (enclosed in quotation marks) containing the name of the set method for the specified variable (do not include parentheses). The method has one parameter that has the same type as the field. By convention the setMethod does not have a return value, but no error condition results if the method returns a value.

To indicate that a field is read only or write only, you can specify only one of the two property fields. If the Rich UI application tries to read or write to a property for which the read or write is not supported, an error occurs before the EGL generator accesses the application code.

@VEProperty

When you write a Rich UI widget (or an external-type widget, as described in another topic), you can ensure that widget-specific properties are in the Properties view whenever you use the EGL editor to create a widget of the new type. You make the properties available by setting @VEProperty for each widget field that you want to list in the Properties view. @VEProperty is useful only when the @VEWidget and @EGLProperty properties are set.

Here are example uses of @VEProperty::
mySimpleProperty String {
   @EGLProperty{}, 
   @VEProperty{category = "Basic"}};

myChoiceProperty String{
   @EGLProperty{},
   @VEProperty{category = "Advanced",
               propertyType = "choice",
               choices = [
                  @VEPropertyChoice {displayName = "3D", id = "3"},
                  @VEPropertyChoice {displayName = "4D", id = "4"} 
               ]}};

Not shown are the functions that get and set the EGL properties.

The property fields in @VEProperty are as follows:
category
The category in which the property is listed in the Properties view. If the category does not exist, a new category is created with the name you specify. The category field takes a string.

The categories in the Properties view are in reverse order of their initial reference in the Rich UI widget or external type. The last-specified category is listed first, and the categories that are available for all widgets are displayed last. Similarly, the properties in a given category are in reverse order of declaration in the Rich UI widget or external type.

propertyType
The type of value required in the property. By default, the type is the type of the field in the widget definition. The only valid value for propertyType is choice, which is used if you intend to specify an array of choices, as shown in the previous example.
choices
An array of @VEPropertyChoice elements, each of which includes at least the first of the following two fields:
displayName
A string that represents the choice in the Properties view. This value is required.
id
A string that holds the content assigned to the property in the Rich UI application. This value is required. The type of the assigned value must be the same as the type of the property that is receiving the value.

Changes to @VEProperty are available to a file in the EGL editor only if you refresh the palette and the file. To refresh the palette, click the Refresh palette tool on the Design surface, as noted in Using the tools on the Design surface. To refresh the file, click the Refresh Web page tool on the Preview tab, as noted in Running a Web application in the EGL editor.

@VEEvent

When you write a Rich UI widget that uses the targetWidget property (or when you write an external-type widget, as described in another topic), you can declare event types that are not otherwise defined. In this way, you can let the business developer think more clearly about defining a sequence of tasks. Also, the developer can assign an event handler at the Events view.

The next example shows use of a customized button (MyButton), which uses the property @VEEvent. If you try out the example in your workspace, note that the EGL developer can now use the EGL editor for the following purposes: to create a button of type MyButton and to assign event handlers for the newly defined events (onPreClick and onPostClick).

Here is a handler that accesses a button of type MyButton:
package pkg;

import com.ibm.egl.rui.widgets.Button; 

handler MyHandler type RUIhandler {initialUI = [ someButton ]}
		
	  someButton MyButton{text = "fresh", 
	                      onPreClick ::= respondToPreClick, 
	                      onClick ::= respondToClick, 
	                      onPostClick ::= respondToPostClick};
		
   function respondToPreClick(e Event in)
      sysLib.writeStdout("in pre"); 
   end
	
   function respondToClick(e Event in) 
      sysLib.writeStdout("in click");
   end
	
   function respondToPostClick(e Event in) 
      sysLib.writeStdout("in post");
   end	
		
end
Here is the definition of MyButton:
package pkg;

import com.ibm.egl.rui.widgets.Button;
handler MyButton type RUIWidget {

   targetWidget = internalButton, 
   @VEWidget{ 
		   category = "New EGL Widgets",
		   template = "pkg.myButton{ text=\"myButton\" }"}
   }
	
   text string {@EGLProperty{setMethod = setText, 
                             getMethod = getText}, 
                @VEProperty{}};

   internalButton Button{onClick ::= respondToClick};

   onPreClick EventHandler[] {@VEEvent{}};
   onClick EventHandler[] {@VEEvent{}}; 
   onPostClick EventHandler[] {@VEEvent{}};	

   private function setText(text String in)
		   internalButton.text = text;
   end

   private function getText() returns (String)
		   return ( internalButton.text );
   end
   
   private function respondToClick(e Event in)

      preSize int = onPreClick.getSize();
      clickSize int = onClick.getSize();
      postSize int = onPostClick.getSize();	 
   
      for (i int from 1 to preSize by 1)
	        OnPreClick[i](e);
      end
	   
      for (i int from 1 to clickSize by 1)
	        OnClick[i](e);		  	
      end
	   
      for (i int from 1 to postSize by 1)
         OnPostClick[i](e);		
      end
   end
end

Changes to @VEEvent are available to a file in the EGL editor only if you refresh the palette and the file. To refresh the palette, click the Refresh palette tool on the Design surface, as noted in Using the tools on the Design surface. To refresh the file, click the Refresh Web page tool on the Preview tab, as noted in Running a Web application in the EGL editor.

@Override

Use the @Override property if want to override a function that is available in any widget. For example, the definition of the Box widget overrides the function removeChild, as shown here:
function removeChildren()  {@Override}
		// EGL statements are here 
end

For a list of functions that are available in all widgets, see Widget properties and functions.

External type widgets

You can create an external type widget by writing custom JavaScript or by accessing specialized JavaScript libraries.

To create a new Rich UI widget based on JavaScript, do as follows:
  1. Create a JavaScript file to contain the code for the widget. Invoke egl.defineWidget and specify the following arguments:
    • The package name of the EGL external type that defines the widget; for example, egl.rui.widgets)
    • The name of the widget class (for example, Button); that class name is the name of the external type you will create
    • The package name of the EGL external type that defines the parent widget; however, if that parent type is widget (the most elemental choice), specify egl.ui.rui
    • The name of the parent widget class (for example, Label)
    • The HTML element type (for example, button)
    Here is the example:
    egl.defineWidget( 'egl.rui.widgets', 'Button',
                      'egl.rui.widgets', 'Label',
                      'button', { } );
  2. Within the curly brackets ( { } ), define the JavaScript functions to reflect the following outline, separating one function from the next with a comma:
    "functionName" : function(/*parameters*/)
    { //JavaScript Source }
    Here is an example:
    "getSelected" : function() {
       return this.check.checked;
    },
    "setSelected" : function(value) {
       this.check.checked = value;
    },
    "toString" : function() {
       return "[CheckBox, text=\""+this.egl$$DOMElement.innerHTML+"\"]";
    }
  3. Each widget can specify the following functions for the JavaScript runtime to invoke:
    • The function constructor is invoked whenever a new widget instance is created, and that function can be used, for example, to initialize data
    • The function egl$$initializeDOMElement is invoked whenever the HTML element is being created for the widget, and that function can be used, for example, to set initial properties on the element
    • The function childrenChanged is invoked whenever the application developer updates the children property of the new widget, if any.

    Each widget also has a field called this.egl$$DOMElement, which represents the HTML element (or top-level HTML element) created for the widget.

  4. In an EGL source file, create an EGL external type that extends from egl.ui.rui.Widget or from an existing widget. The External Type needs a stereotype of JavaScriptObject, as described in External type for JavaScript.

Feedback