Decorations are used to style widgets. The idea is to have an independent layer around the widget content that can be freely styled. This way you can have separate decorators that define all kinds of decoration (colors, background image, corners, ...), and apply them to existing widgets, without interfering with the widget code itself.
Decorations are used for both, the shadow and the decorator property. They could be applied separately or together. There is no dependency between them.
Generally all decorators used should be part of the selected decorator theme. The convention is that each decorator instance is stored under a semantic name. To use names which describe the appearance of the decorator is bad because it may make themes less compatible to each other.
It is also regarded as bad style to make use of so-named inline decorators which are created by hand as part of a function call. The reason for this is that generally decorators defined by the theme may be used in multiple places. This means that widgets and application code should not directly deal with decorator instances.
As mentioned above, it is common to define the decorators in a decorator theme. This is really easy because you have to specify only a few details about the decorator.
"main" :
{
decorator: qx.ui.decoration.Uniform,
style :
{
width : 1,
color : "background-selected"
}
},
The first thing you see is the name of the decorator, in this case, main. The specified decorator is available using that name in the whole application code, especially in the appearance theme. The next thing you see in the map is the decorator key, that defines the decorator to use. The last thing is the styles map which contains values for the properties of the given decorator.
This is the way using prebuild decorators. You can also use the decorator mixins in the theme:
"scroll-knob" :
{
decorator : [
qx.ui.decoration.MBorderRadius,
qx.ui.decoration.MSingleBorder,
qx.ui.decoration.MBackgroundColor
],
style :
{
radius : 3,
width : 1,
color : "button-border",
backgroundColor : "scrollbar-bright"
}
},
The main difference here is that not a reference to a prebuild decorator is given. Instead, an array containing mixins implementing single features are used. The theming system combines those mixins in a decorator. The styles map should now containg values for properties defined by the mixins.
Sometimes it is very handy to change change only little details about the decorator. Imagine a special decorator for hovered buttons. Inheritance comes in very handy in such a case.
"scroll-knob-pressed" :
{
include : "scroll-knob",
style :
{
backgroundColor : "scrollbar-dark"
}
},
As you can see here, we include the previously defined decorator and override the backgroundColor property. Thats all you need to do!
Custom decorators are created by extending the decorator theme and adding new ones or overwriting existing ones. Each decorator class comes with a set of properties for configuration of the instance. Following a short description of the available decorators:
Each entry of the theme is automatically made available using the setDecorator/setShadow functions of the widget class. The instances needed are automatically created when required initially. This mechanism keeps instance numbers down and basically ignores decorators which are defined but never used.
Additionall to these explicit decorators, qooxdoo supplies a set of Mixins which supply separate features for decorators. These mixins can be used to build a decorator on runtime by the theming system. All feature mixins can be used in combination to get an individual decorator. The mixins also include some features not available in the standalone decorators.
As you may have guessed, the last three mixins do not work cross browser due to the fact that they rely on CSS propertes not available in all browsers. If you want more details, take a look at the API documentations of the mixins.
It is easily possible to write custom decorators. The interface is quite trivial to implement. There are only five methods which needs to be implemented:
One thing to additionally respect is that resize and tint should be as fast as possible. They should be as minimal as possible as they are executed on every switch to the decorator (e.g. hover effects). All things which are possible to do once, in getMarkup or init methods, should be done there for performance reasons. Decorators are regarded as imutable. Once they are used somewhere there is no need to be able to change them anymore.
Each decorator configuration means exactly one decorator instance (created with the first usage). Even when dozens of widgets use the decorator only one instance is used. To cache the markup is a good way to improve the initial time to create new element instances. These configured elements are reused e.g. a hover effect which moves from "Button 1" to "Button 2" uses the same DOM element when reaching "Button 2" as it has used in "Button 1". This way the number of DOM elements needed is reduced dramatically. Generally each decorator instance may be used to create dozens of these elements, but after some time enough elements may have been created to fulfill all further needs for the same styling.
If you want to use your custom decorator with some build in decorator mixins, you can write you decorator as mixin and use it in combination with all the other mixins. Its comparable to writing a standalone decorator. You are able to implement the following methods:
As you can see, every mixin can define its own methods for getMarkup, resize, tint and the insets. The theme system combines all the methods given by the separate widgets to one big working method. A single special cas is the _generateMarkup method, which can only be there once for the whole decorator. For example, the double border Mixin already implements that because it needs to handle the generation itself.