Writing Custom qooxdoo Widgets
When writing applications you typically use plain qooxdoo widgets to compose the user interface. Sometimes however you need to create custom widgets. Custom widgets are reusable compositions of existing widgets. For example a label and an input field can be combined into a custom widgets to render form entries.
Characteristics of a Custom Widget
Widgets are either container widgets, which can be configured by the user to contain other widgets, or they can be final widgets. Final widgets can internally be composed of several other widgets but to the user they appear as one single widget.
In general a custom widget:
- is an instance of qx.ui.core.Widget
- is self contained
- depends only on common infrastructure and other custom widgets
Base Classes
In general widgets should inherit as few API as possible from their base class. All the public API calls, which are provided by the base classes, contribute to the public API of the custom widget. If for some reason the public API of any of the base classes changes, the users of the custom widget are affected. Sometimes methods, which make perfect sense in the base class, don't make any sense in the custom class and thus irritate the user.
If possible final custom widgets should inherit directly from qx.ui.core.Widget. This way only the basic widget API is inherited.
If the custom widget is a container widget, the author has to decide:
- Whether the children will be added directly to the widget or into some nested sub widget.
- Whether the user should be able to set a layout manager
This results in four possible combinations:
- The children are added directly to the container and setting a layout manager is possible: Inherit from
qx.ui.container.Composite. This class has the whole children and layout handling API exposed as public API. - The children are added directly to the container but setting a layout manager is not possible: Inherit from
qx.ui.core.Widgetand add the children handling API by including the mixinqx.ui.core.MChildrenHandling. (Example: HBox widget with predefined horizontal box layout) - The children are added to a nested sub widget and setting a layout manager is possible: Inherit from
qx.ui.core.Widgetand add the children and layout handling API by including the mixinsqx.ui.core.MRemoteChildrenHandlingandqx.ui.core.MRemoteLayoutHandling. The container must implement the method getChildrenContainer, which has to return the widget, to which the child widgets should be added. (Example: Window, Groupbox) - The children are added to a nested sub widget and setting a layout manager is not possible: The same as the previous case but don't include
qx.ui.core.MRemoteLayoutHandling. (Example: List, SelectBox)
Constructor Parameters
The number of constructor parameters should be kept short. Long parameter lists especially with optional parameters can result in code, which is very hard to read. As an example lets take this hypothetical LinkAtom widget:
qx.Class.define("custom.LinkAtom", { extend : qx.ui.basic.Atom, construct : function(text, icon, toolTipText, underline, bold, wrap, underlineOnMouseover) { ... } });
All parameters are optional. This can make the code using this class very hard to read:
var link = new custom.LinkAtom("Help"); var link = new custom.LinkAtom("create", null, null, false, true, false, true);
While the first line is perfectly readable, it is virtually impossible to understand the second line without referring to the API documentation of LinkAtom.
In this case it helps to keep only those parameters, which are required or used with almost every instance. All other parameters should be converted into properties:
qx.Class.define("custom.LinkAtom", { extend : qx.ui.basic.Atom, construct : function(text, icon) { ... }, properties { toolTipText: { ... }, underline: { ... }, bold: { ... }, wrap: { ... }, underlineOnMouseover: { ... } } });
Now only the text and icon parameter remain in the constructor. All other parameters have been converted into properties. The hard to read example now becomes:
var link = new custom.LinkAtom("create").set({ underline: false bold: true, wrap: false, underlineOnMouseover: true });
For someone reading this code it is immediately obvious, what's happening.
Keep parameter lists small and use properties instead of optional constructor parameters.
References
- Also take a look at the section custom widgets in the qooxdoo manual