JavaFX 1.3: Autosizing Everywhere

Resizable nodes (Controls & Containers) generally know best how big they should be and they express this from the inside-out through their “preferred size”; as the node’s internal state changes (content, styling, etc), it’s preferred size usually also changes.

When a resizable node’s preferred size changes, rather than immediately resizing itself, it invokes requestLayout() to inform its parent that it wants to be resized on the next layout pass. The reason the resizing task falls to the parent (rather than the child itself) is because the parent may need to balance the use of real estate across its children and hence it needs to call the shots (if you have kids, you understand). And this is what we mean by “autosizing”; that is, the parent automatically sets the width and height of the child (usually to the child’s preferred size) during layout.

In 1.2, only Container parents performed autosizing, so if you placed your button or textbox inside a Group, CustomNode, or at the root of a Scene, and the text in the button changed, the button would not be automatically resized to accommodate the new text.    This has lead to endless confusion because controls behaved differently depending on their parent type.

In 1.3, autosizing becomes universal across all Parent subclasses:  Container, Group, CustomNode, and that secret parent hiding as the root of every Scene.

This change means you need to just learn one set of rules which apply consistently across the scene-graph:

  • Rule: Do not set a Resizable’s width/height variables directly;  if you need to explicitly set its size, use LayoutInfo to override it’s preferred size:

Example:

Label {
    layoutInfo: LayoutInfo { width: 100 }
}

And note that this applies only to Resizable children; non-Resizable nodes (shapes, Text, ImageView) are not affected by autosizing.

If for some reason you don’t want a Group to auto-size its resizable children, you can set autoSizeChildren to false, restoring the 1.2 behavior.

Universal autosizing puts huge pressure on all resizables to correctly report their preferred size, which leads to a second rule:

  • Rule:  When creating Resizable classes, always override getPrefWidth()/getPrefHeight() functions to return valid values.

Internally after turning on universal autosizing, we discovered many bugs in our Control classes where preferred sizes weren’t being returned properly.  So if your resizable node suddenly disappears in 1.3, check to make sure its preferred size isn’t 0×0!

Why CSS Fueled Autosizing

In 1.2, prior to our CSS-stylable version of Controls, we were able to initialize Controls and Containers to their preferred size during initialization. This meant that if you then threw one of these guys into a Group, CustomNode, or Scene, it would appear with the correct initial size, even though subsequent changes to its preferred size wouldn’t update since those parents didn’t autosize in 1.2.

With 1.3’s introduction of CSS-stylable controls, we can no longer prime the size of controls during initialization.  This is due to the hierarchical nature of CSS, where styles are applied lazily to nodes on the first scene pulse (which you can think of as the heartbeat that drives rendering of the scene-graph to the display), which means that Controls (and the parents that contain them) are oblivious to their preferred sizes at init time.   Universal autosizing kicks in during that first layout pass (but after styles are applied) and ensures that everything is sized properly (just once) before you see it.

  • Rule:  You cannot reliably measure the size of node branches that contain Controls until they are connected to a displayed scene.

But what if you need to get the size of something before it’s displayed?   This is certainly a valid need in some applications.  Well, recall that layout happens regardless of whether a node is visible or not, so make it invisible, add it to a visible scene’s content, and it will be autosized and you can measure it.  Not exactly obvious or elegant, but works until we create a better mechanism.

About these ads
This entry was posted in Uncategorized. Bookmark the permalink.

5 Responses to JavaFX 1.3: Autosizing Everywhere

  1. Pingback: JavaFX links of the week, May 10 // JavaFX News, Demos and Insight // FX Experience

  2. Pingback: JavaFX 1.3: Managed Gets Promoted | Aim's Blog

  3. Steven Herod says:

    I guess I’m still struggling to understand when an element is constrained by its parent and when it isn’t.

    I have a Hierarchy which is

    CustomNode -> ListCell -> ListView -> Stack -> HBox -> Scene

    Basically I’d like the right hand side of the HBox (element 1 in the HBox’s contents: sequence) to expand to occupy the rest of the width of the scene.

    What I’m actually getting is the ListView expanding out beyond the right and bottom of the Scene.

    Intuitively, I feel like I should constrain the HBOX width to the width of the scene (with ‘bind scene.width’) and then it will automatically scale everything within itself (expanding the ListView horo/vertically if ‘hfill:true vfill:true’ )?

  4. Amy Fowler says:

    You should definitely bind the width/height of the HBox to the scene’s width & height, otherwise the HBox will not be constrained to fit within the scene. This will also ensure that if the user resizes the stage, the contents will track properly.

  5. Pingback: JavaFX 1.3 Layout // JavaFX News, Demos and Insight // FX Experience

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s