We’ve just recently announced the release of the “javafx-ui-common” project into OpenJFX. The name may be a little underwhelming, but the content is absolutely core to JavaFX. For most developers interested in contributing to JavaFX and in understanding how the system works, javafx-ui-common and javafx-ui-controls will be the two most important projects, both of which are now open source, and are part of the “JavaFX Public API” shown in the diagram. For developers interested in porting JavaFX to other platforms or improving the graphics performance — well we will be open sourcing Prism and Glass in the next few months :-).
Since javafx-ui-common is such a foundational part of the JavaFX platform, I thought I should give a short tour of what is in there, including the (gasp!) non-public portions. As always, non-public API (or rather, unsupported API, meaning anything that is not in the javafx namespace such as com.sun.*) cannot be depended on from release to release. But for those of you wondering how things work, there is some very important stuff buried in the unsupported packages, and for those of you wanting to actually hack on OpenJFX, this will be of even greater interest. If you haven’t yet, you might want to brush up on the overall architecture with this article.
In fact, our tour will begin in the unsupported package, with the Toolkit class, which you see as the second layer in the above diagram. This article is of interest to anybody wanting to work on JavaFX itself.
The Toolkit is the interface which sits between the “top half” of the JavaFX platform (which includes all of the public, supported API) and the “bottom half”. The bottom half of the platform is essentially made up of the windowing code, media engine, web engine, and graphics engine. The Toolkit APIs abstract away the implementation details of these engines from the code sitting above it.
In the early days of JavaFX, going all the way back to JavaFX 1.0, we had two different toolkits: Prism, and Scenario. Scenario was a complete Swing-based implementation, where the AWT EventQueue was used for events and all rendering was done with Java2D. It was the easiest way for us to bootstrap JavaFX while we continued work on the Prism hardware accelerated pipeline and Glass windowing framework which was intended to replace Swing and AWT.
For our own purposes, we have actually consolidated the real implementation on a single toolkit implementation called Quantum, which relies on Prism and Glass for graphics and windowing. But in theory you could write a completely different toolkit which used completely different techniques for rendering and windowing.
The Toolkit API is not pretty. It grew organically, with new methods appearing on an as-needed basis. But it works, and does its job well. It lives in the com.sun.javafx.tk package. The Toolkit API is not a supported API, meaning it can change in incompatible ways (and in fact does quite often). But when developing for the platform or if you are interested in porting JavaFX onto other systems or using alternative graphics / media / web / windowing implementations, the Toolkit would be the place to look.
Scene, Stage, Application, Node, and the Scene Graph
Now back to the supported public API. The core of the JavaFX platform is made up of Scene, Stage, Application, and Node. The Application class really doesn’t do much on its own, but is used as an entry point for launching the application. There is a JavaFX launcher class used to create and invoke the lifecycle methods on the Application.
The Stage and Scene however are much more interesting. Both have a “peer” on the toolkit side, the TKStage (standing, of course, for ToolkitStage) and TKScene interfaces. The Stage will create the peer at the appropriate time by asking the Toolkit to create one for it. The Scene likewise creates a TKScene peer at the appropriate times. Communication is always from the Stage or Scene to the TKStage and TKScene. However in some instances (such as when the window bounds change, or an event is to be delivered) the Toolkit needs to be able to notify the Stage or Scene. For this, we have a TKStageListener and TKSceneListener, and the Stage / Scene is notified of various events through these listeners. There is a TKPulseListener used for notifying the Scene of each animation “pulse”.
Generally speaking, these are the only two places where the Toolkit calls back up to JavaFX code.
The Toolkit will send a “pulse” event to the Scene. During each pulse, the Scene will process CSS, perform layout, update the “picked” node (which nodes have the mouse over them and which don’t), and synchronize with the toolkit.
Each Node has a corresponding “PGNode” peer. Probably we should have called these TKNodes (to stand for ToolkitNode, whereas PGNode stands for PlatformGraphicsNode). And maybe we’ll do such a rename just for clarity. Remember, these PGNodes are not supported API so we can change them at any time!
There are two basic types of Nodes: “leaf” and “parent” nodes. Every leaf node has a different type of PGNode peer. For example there is a PGRectangle, PGShape, PGImageView, and so forth. All parent nodes use the PGGroup as their peer. It is through these peers that the JavaFX nodes tell the toolkit what needs to be drawn where. For example, the Rectangle node tells its PGRectangle peer what the x, y, width, height, fill, and other attributes are that should be used to render the Rectangle. The PGRectangle implementation, provided by the Toolkit, is then responsible for taking care of this. The Toolkit is basically a factory for creating these peer instances. As such, a custom Toolkit can have a completely different implementation of these interfaces.
There is a ton of implementation in Scene and Node and Parent which form the fundamental building blocks of the scene graph and UI controls. Perhaps in the future I can provide additional detail on how these pieces fit together. For example, you can look into Node and see how the layout bounds are computed, the bounds in parent, and so forth. Or how we keep track of which Nodes are dirty and which nodes need their state synchronized with their peers. Or how we keep track of dirty layout and perform layout.
Parent is interesting from an implementation perspective because we went to great lengths to make the computation of the parent node’s bounds cheap. There are some fast paths in there for keeping track of whether a child node’s movement / bounds change impacts the bounds of the parent node (since computing the full bounds of the parent node can be quite expensive).
We get a lot of questions on the JavaFX Forums about layout. You will find that some of the layout basics (such as keeping track of which nodes and branches require layout and the actual layout methods themselves) are built into Parent and Node. You will also find that all of the built in layout managers live in the javafx.scene.layout package. Amy Fowler put a lot of work into writing reusable routines for performing common layout tasks correctly, encapsulating all of the rules regarding “managed” vs. unmanaged nodes, padding, margins, alignment, and so forth.
The other packages are fairly self explanatory, such as the javafx.scene.image package containing the ImageView node and associated APIs, the javafx.scene.input package containing InputEvent and related APIs (such as KeyEvent and MouseEvent), the javafx.scene.shape package containing Rectangle, Circle, Path, and the other shape nodes, the javafx.scene.text package containing the Text node, and so forth.
One other area of interest is the javafx.scene.effect package which contains all of the filter effects in JavaFX. Effects are interesting in that they also have peers. These peers are part of a package called “Decora” which was developed by Chris Campbell at Sun back in the early days of JavaFX, and for historical purposes are in a package called “com.sun.scenario.effect”. We expect this package to be changed to be renamed something sensible, like perhaps com.sun.decora or com.sun.prism.decora. In addition, we expect the way the peers are created to be done through the Toolkit, and the peers to be prefixed with PG or TK or D or something. I hate having two classes with the same name, just in different packages!
This is really just barely scratching the surface of course. The general layout of the code and package structure should be pretty easy to follow. I encourage you to get your hands dirty and learn how JavaFX is built. Not only will it give you insights into how the platform is designed to be used, but will also make it much easier to contribute bug fixes, features, and tweaks to the JavaFX platform.