Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Whilst we were ready to make SkinBase public API in JavaFX 8.0 we had to unfortunately backtrack from our intentions to also provide an API to allow for easy handling of mouse and key events in JavaFX 8.0. The primary reason was that this becomes increasingly complex the more you look into it, especially if you start to think of behaviors as more than just internal control mappings but rather internal and external mappings between inputs and actions. Additionally, there is a need to consider exposing API that can read these mappings and to know what mappings are available for a given control (for Scene Builder to expose this functionality to developers, for example). The Jira issue tracking this feature can be found at RT-21598.

The life cycle of a UI control

A JavaFX UI control has a relatively well-defined life cycle (except perhaps in its final stages of disposal, which I'll cover below). In short, a UI control goes through the following stages during its life:

  1. A UI control is instantiated via its constructor. For example, a developer will call Button submitButton = new Button("Submit"). This will instantiate the button and run its constructor.
  2. As part of the constructor, it is suggested that a default CSS style class be specified. By doing so this control will know what style class it belongs to, and it can therefore be styled via CSS. For the case of Button, there is code inside the constructor that will do something similar to getStyleclass().add("button").
  3. After the constructor is run, nothing else happens - no skin is instantiated, no layout is run, and no CSS is run. The control will in fact have zero width and height.
  4. At some point after instantiating a new control, the developer is likely to place the control in the scenegraph, by calling getChildren().add(button) somewhere in their code.
  5. At this point the control is now part of the scenegraph, and therefore is possibly considered on every pulse. This means that, if necessary, the control will have css, layout and rendering passes performed on it.
  6. When the pulse occurs, the first action is for CSS to be run. When CSS runs, the controls impl_processCSS() method is called, and inside here the following happens:
    1. If no skin is currently set, it checks whether getUserAgentStylesheet() has a stylesheet reference, and if so, installs it into the CSS engine for use in styling the control.
    2. The control then calls up the chain to the super.impl_processCSS(..) method. This may have the result of loading a skin instance, via reading in a -fx-skin property from either the default user agent stylesheet, or via the user agent stylesheet belonging to the UI control. If a skin is specified via -fx-skin, it is loaded via reflection - see the section below on reflection in UI controls.
    3. If the call to the parents impl_processCSS(..) method returns and the skin is still null, the control will then call into the createDefaultSkin() method detailed above (in change #2). If this method returns a Skin instance it will be used as the skin for the control.

At this point the control might have a skin. In summary, the order of precedence is that the controls user agent style sheet has highest priority, then the default user agent style sheet provided by JavaFX, and finally the result of a call to createDefaultSkin(). If the skin is null, an error message is logged, but the control is not prevented from being otherwise functional.

On the other side of the coin, disposal of a UI control is not so well-defined, as there is no hook in Control to listen for when it is no longer necessary to be kept alive. Despite this, there is the Skin.dispose() method that must be implemented by all implementations. Hopefully in the future a proper hook will be made such that dispose() will be called, but at present this is not the case.

Reflection in UI controls