Thursday Feb 13, 2014

Dynamical layouts in FX

Since I took over JavaFX layout code, I had several bug reports that were related to dynamical layouts and noticed that many people struggle with the implementation. By dynamical layout I mean a layout Pane that not only resizes and positions it's children, but also manipulates with it's child list depending on the acquired size.

In a standard layout Pane, the typical workflow is this:

  1. Parent calls prefWidth/prefHeight; the preferred size is computed from the current set of children and Pane properties/constraints
  2. Parent assigns the actual width/height
  3. The layout is computed from the width/height and the same set of children used in step #1

A dynamical Pane would work like this:

  1. The preferred size is either fixed or depends just on Pane properties or some items (based on which the children are created)
  2. Parent assigns the actual width/height
  3. The layout is computed from the width/height and children are created accordingly

A simple dynamical Pane with fixed-size Rectangles may be implemented like this:

      private class GrowingPane extends Pane {

        private static final int PREF_NUMBER = 2;
        private static final int RECTANGLE_SIZE = 50;
        private static final int PADDING = 10;

        protected double computePrefWidth(double height) {
            return PREF_NUMBER * RECTANGLE_SIZE + (2 + PREF_NUMBER - 1) * PADDING;

        protected double computePrefHeight(double width) {
            return RECTANGLE_SIZE + 2 * PADDING;

        protected void layoutChildren() {
            final double w = getWidth();

            // count the number of Rectangles that will fit
            final int num = (int) (w - PADDING) / (RECTANGLE_SIZE + PADDING);

            int curX = PADDING;
            // Do the layout
            for (int i = 0; i < num; ++i) {
                Rectangle rec = new Rectangle(RECTANGLE_SIZE, RECTANGLE_SIZE, Color.LIGHTGREEN);
                rec.relocate(curX, PADDING);
                curX += RECTANGLE_SIZE + PADDING;


Now we look at a more sophisticated version with Buttons that are not-fixed size. For our sample, we generate the Button text - each subsequent button text is double the size. Notice the difference in computePrefWidth/computePrefHeight:

      private class GrowingPane2 extends Pane {

        private static final int PREF_NUMBER = 2;
        private static final String BASE_STRING = "A";
        private double prefWidth = -1;
        private double prefHeight = -1;
        private static final int PADDING = 10;

        protected double computePrefWidth(double height) {
            if (prefWidth == -1) {
            return prefWidth;

        private void computePrefSize() {
            String str = BASE_STRING;

            prefHeight = snapSpace(2 * PADDING + computeHeight());

            prefWidth = PADDING;
            for (int i = 0; i < PREF_NUMBER; ++i) {
                prefWidth += computeWidthForString(str) + PADDING;
                str += str;
            prefWidth = snapSpace(prefWidth);


        private double computeHeight() {
            Button b = new Button(BASE_STRING);
            double result = b.prefHeight(-1);
            return result;

        private double computeWidthForString(String str) {
            Button b = new Button(str);
            double result = b.prefWidth(-1);
            return result;

        protected double computePrefHeight(double width) {
            if (prefHeight == -1) {
            return prefHeight;

        protected void layoutChildren() {
            final double w = getWidth();


            double widthLeft = w - PADDING;
            double curX = PADDING;
            String s = BASE_STRING;

            while(widthLeft >= PADDING) {
                double bw = computeWidthForString(s);
                if (bw + PADDING > widthLeft) {
                Button b = new Button(s);
                s += s;

                b.relocate(curX, PADDING);
                curX += bw + PADDING;
                widthLeft -= bw + PADDING;


In real-life example, where you generate Buttons (or whatnot) based on some item list or other model, you'd invalidate the computed size on this model change.

Lets look at the computation. You may notice that we added and removed the child and also called applyCss() before computing it's preferred size. This is because the CSS pass is done after the layout pass, so modifying the children in layout pass require explicit execution of CSS pass for every new Node. The CSS depends on context (position in the scenegraph), so we need to add the child first. Of course, to be absolutely accurate, we should add the child to it's correct position and clear the list at the end of the computation. 

Tuesday Jul 30, 2013

The peculiarities of JavaFX layout, pt.1

While working on JavaFX layouts, I have seen a number of bugs caused by a false assumption of the developer about the layouts. There are multiple types of Nodes that behave differently during the layout pass and use the layout information in a different way, which often leads to confusion as it might be quite hard to see the big picture just by reading the javadoc of the classes. That is why I decided to do a few blog posts that would describe the layout from a different point of view.

In this first part, we will look at the way layout works in detail, so I assume you already know the basics of JavaFX layout like types of layout classes, minimum/preferred/maximum sizes, content bias and resizability. If you don't, I can recommend you the JavaFX Class Tour tutorial from Amy Fowler. The subsequent part(s) will cover the common traps and pitfalls for custom layout/control developers and also for those who just want to do some simple adjustments of a layout in their applications. 

Note: there are still open issues that, once resolved, might change the behaviour described in this post. I will keep it up-to-date with the current 8.0 build available.  

How the particular classes use the layout

Some of the confusion originates in a different ways the layout classes are treated during the layout pass. Let's look at this table that describes the way layout bounds and size hints are computed. The last column shows the order in which the layout properties are used/set on the particular Node type. 

layout bounds computation minimum/preferred size resizeable layout pass order


as "bounds in local"
without transformations, clip and effect
always equal to layout bounds no
  1. parent reads pref size to compute the area where the Node will be positioned
  2. the Node is positioned (node.relocate()) using it's current layoutBounds

a sum of current bounds of it's children
(incl. transforms, clip and effect)
Note: querying layout bounds will trigger a layout
in order to compute the most up-to-date layout bounds 

always equal to layout bounds no
  1. parent reads pref size to compute the area where the Group will be positioned
    1. In order to compute the pref size, the Group must trigger the internal layout at this stage using Group.layout().
      Without this side-effect, the layout of a Group would require a second pass
  2. the Group is positioned using it's newly computed layoutBounds in step #1.1
(not autosize)

a sum of current bounds of it's children
(incl. transforms, clip and effect)

always equal to layout bounds no
  1. parent reads pref size to compute the area where the Group will be positioned
  2. the Group is positioned using it's current layoutBounds
x,y : 0,0
width, height: set by the parent during the layout, by using resize() call
depends on the implementation
The parent of the Region uses the information to
compute the final size during the layout (which means
also the layout bounds)
  1. parent reads pref size to compute the area where the Region will be positioned
    1. This is computed internally, without any side-effect
  2. the Region is resized to the newly computed size, changing it's layoutBounds
  3. the Region is positioned using it's new layoutBounds
  4. Region.layout() is triggered to compute the internal layout using the new size from step #2.

While this may seem to be very complicated, there's actually a good reason why the steps vary between the different types of Nodes. Let's look at how a generic layout pass of a layout Pane looks like:

  1. The layout is computed using the min./max./pref. size of the children. ( + content bias, baseline; we will cover that later)
  2. Resizeable child (means only Region/Pane/Control) is resized to the computed size.
  3. The child (any kind) is positioned according to it's layout bounds.

As you can see from the table, with the exception of Region, the min/pref size ( = layout bounds) is needed in order to correctly compute the layout . The Region however does not need to know it's layout bounds,  the layout bounds are being computed by the it's parent instead.

Moreover, autosize Group does need to layout itself before computing it's layout bounds, which means on the step #1 of it's parent's layout.  This again is absolutely different in Region, which does it's layout after step #3 (though theoretically it's possible after step #2), because the internal layout depends on the size available for the Region, which is known after the resize (at step #2). 

Here's a little diagram that shows the dependencies:

Layout Roots 

JavaFX has a concept of special Parents, called "layout roots". A layout root is a kind of a Parent that doesn't require it's own parent to be laid out when something changes in it's subtree.
Any Parent that satisfies one of these properties is a layout root:

  • managedProperty() is set to false
  • the Parent is a root of a Scene or a SubScene

You may think anything under a non-autosize Group should be also a layout root, but that is not true as a Group must be re-positioned when it's layout bounds change.

You can also use layout roots to validate the layout immediately by using layout() call on the layout root of the Node you want to validate. Very useful for instant measurements of the Node size. Example coming in the following blog post. :-)

The layout invalidation

Now you know how the layout pass works internally, but before anything can be laid-out, the current layout must be invalidated. This is done automatically for you when some child changes, but you may want to trigger a layout when some of your properties that define your layout changes. This is done by simply calling a requestLayout() method, like this:

    public final IntegerProperty helloProperty() {
        if (helloProperty == null) {
            helloProperty = new SimpleIntegerProperty(this, "hello") {
                public void invalidated() {

        return helloProperty;

The same method is often used for invalidating pre-computed data of the layout. It's very useful to pre-compute data for computePrefWidth/computePrefHeight and layoutChildren methods, so you don't have to compute the same 3-times. You can then invalidate this data in requestLayout, which is guaranteed to be called whenever a layout-affecting change is made in the subtree of a Parent.

Size bounds

The last source of confusion we will cover in this part are the bounds for width & height of a Region. The Region allows you to override computed size by calling setPrefWidth(double) / setMinWidth(double) / setMaxWidth(double) methods  ( + the equivalent for height). What many of you might find surprising is that the minimum size always takes precedence over maximum size, whether the maximum size is computed or overridden by the developer.

To get a Region/Control below it's (computed) minimum size, you have to override also the minimum size, not just maximum. As the minimum size should be really the smallest size when the Region/Control is usable, this logic serves to prevent layout from resizing the Region below it and keep it functional.

So much about the insides of layout processing. we will look more at some examples in the next post. 


JavaFX is a Java GUI toolkit, partially developed from Prague, Czech Republic. The Prague team uses this blog to post articles, code samples and insights about the range of topics the team members specialize in. This includes JavaFX Scenegraph (javafx.scene.*), JavaFX Core libraries & animations, iOS port & Android port.


« July 2016