qooxdoo

Universal JavaScript Framework

 

Custom Widgets

Most widgets are built using a combination of pre-existing, more basic widgets. This is also true for custom widgets made for a specific application or as an extension to the existing feature set of qooxdoo.

Inheritance Structure

A more complex widget usually extends the base class qx.ui.core.Widget. A widget can manage children using a set of protected methods. Extending from a richer widget often has the side effect that the final class contains APIs which do not make sense in the derived class anymore. Also be sure not to extend from Composite or a widget based on this class. This is mainly because it has public methods for the normally internal layout and children handling and would propagate all the internal information to the outside when children are added or the layout is modified by the derived class.

A good example: Most rich text editors implemented in JavaScript make use of an iframe. One could imagine using the Iframe class as a base to build such a component. The problem is that most of the methods and properties like setSource or reload do not make a lot of sense on an editor component. It's better to embed the needed widgets into the outer widget to hide their functionality in the custom class.

The qooxdoo Spinner for example extends the Widget as well and adds a TextField and two RepeatButton instances. The layout is done by a Grid layout. All the children and the chosen layout are hidden from the outside. There are no public accessors for the layout or the children. This makes sense as no one is interested in the children of a Spinner widget. These methods would also mean a lot of bloat added to the API of such an widget.

Setup Content

The following methods may be used to manage children:

  • _getChildren
  • _add, _addAt, _addBefore, _addAfter
  • _remove, _removeAt, _removeAll

It is possible to use any layout available. To set up the layout just use _setLayout. To access it afterwards use _getLayout.

For details refer to the API documentation of qx.ui.core.Widget.

Child Controls

qooxdoo supports a mechanism called child controls. A child control is a widget as part of another widget. Child controls were introduced to have a common way of accessing these controls and to make it easy to refine them when a class should be extended. Each child control is accessible using an identifier which is basically a string. By convention these strings are all lower-case and use dashes to structure complex identifiers. Typical identifiers are button, icon or arrow-up. Never slashes / as this might conflict with the appearance system.

Instances for the supported child controls are created dynamically as needed. A widget developer just needs to override the method _createChildControlImpl, let the method work on the customized controls, and just call the super class method when the incoming ID is not supported. For example, such a method might look like:

_createChildControlImpl : function(id)
{
  var control;

  switch(id)
  {
    case "icon":
      control = new qx.ui.basic.Image;
      this._add(control);
      break;
  }

  return control || this.base(arguments, id);
}

Each child control should directly add itself to the parent. As mentioned before child controls are automatically created as needed. This basically means that if nobody asks for a specific child control it is never created or added. This is an important feature for dynamic widgets as it reduces the initial memory and CPU usage. A child control is always created when some code asks for it. This can happen through different methods:

  • getChildControl(id, notcreate): Returns the child control with the given ID. May return null if the second argument is true. This is basically used to check if the child control has already been created and then apply something to it. In some more complex scenarios this makes sense, but it can be ignored for the moment.
  • _showChildControl(id): Executes show() on the child control. This method also creates the control if that hasn't happened yet. It also returns the control so other properties can be applied to it.
  • _excludeChildControl(id): Excludes the widget using exclude(). When the control is not yet created the function does nothing. The method has no return value.
  • _isChildControlVisible(id): Returns true if the child control with the given ID is created and visible.
  • hasChildControl(id): Returns true if the child control with the given ID has been created.

Styling

Child controls are automatically supported by the appearance system. For every child control a selector is generated which starts with the first widget which is not a child control itself. Typical selectors look like:

  • spinner/up-button
  • groupbox/legend
  • tree-item/icon

As a container for child controls may be a child control for another container as well, even more complex selectors are possible:

  • list/scrollbar-x/slider
  • splitbutton/button/icon

This means that even the deepest child control can be easily accessed by theme authors. Widget authors should define the styling of a widget in the appearance theme and not in the widget itself. The widget and the _createChildControlImpl method should only apply functional properties like zIndex or tabIndex, but no decorations, colors or fonts for example.

As mentioned, a key always starts with the appearance of the first widget which is not itself a child control. Appearance values of the inner widgets are ignored as long as they are used as a child control. Instead, the ID of the child control is used. The / is used to separate the child controls. All widgets added through user code start with their own appearance. For example, the items of the List widget have the appearance list-item. Their appearance key is also list-item and not list/item.

For details about styling please refer to the theming article.

HTML Elements

A normal qooxdoo widget consists of at least two HTML Elements (API). The first one is the container element which is the outer frame of each widget. The inner one is the content element which is the target for children added to the widget. The content element is also used for the iframe element of the Iframe widget and the image element of the Image widget. This means it may contain children or may be used by a native DOM element which does not allow any children.

There might be some other elements depending on the configuration:

  • shadow: Placed into the container with negative offsets to be visible behind the original widget.
  • decorator: Placed into the container with the same size as the container. Used to render all kinds of decorators.
  • protector: Helper to fix certain hover issues when changing decorators during event sequences, e.g. hover effects.

For widget authors, the content element is normally the most important, followed by the container element. The other elements are quite uninteresting. It is good to know that they are there, but one typically has little to do with them.

Both elements are instances of qx.html.Element so they come with a cross-browser fixed API to apply styles and attributes to the DOM nodes. All of these things can be done without the DOM element needing to be created or inserted. For details on qx.html.Element please have a look at the technical documentation.

The elements are accessible through the functions getContentElement() and getContainerElement(), respectively. The elements are stored privately in each widget instance and are only accessible through these methods in derived classes.

Custom Elements

qooxdoo normally generates a bunch of styled div elements. Some widgets like iframes or images need other elements, though. Normally the only element which is replaced is the content element. To achieve this, the method _createContentElement needs to be overwritten. The overwritten method should create an instance of qx.html.Element (or a derived class), configure it with some static attributes or styles, and finally return it. For most natively supported types there exists a class which can be used already. In special cases the widget author also needs to write a special low-level class which is derived from qx.html.Element.

Working with Events

Events can be added to the HTML elements as well as to the child controls. The names of the methods assigned should follow the following names for convention.

  • For the HTML elements use: _onContentXXX or _onContainerXXX
  • For the child controls use: _onIconXXX or _onFieldXXX etc.

Where XXX stands for the name of the event or of the change that happens. This will result in names like _onIframeLoad or _onContentInput.

Anonymous Widgets

Anonymous widgets are ignored in the event hierarchy. This is useful for combined widgets where the internal structure does not have a custom appearance with a different styling from the enclosing element. This is especially true for widgets like checkboxes or buttons where the text or icon are handled synchronously for state changes to the outer widget.

A good example is the SelectBox widget where the mouseover event should affect the entire widget at once and not the different child controls of which it consists. So setting the child controls (in this case an atom and an image widget) to anonymous keeps these child control widgets from receiving any events and the event handling is done completely by the parent widget (the SelectBox itself).

Table Of Contents

Previous topic

Inline Widgets

Next topic

Form Handling

This Page