Presentation JavaFX Layout Secrets with Amy Fowler

Amy Fowler gives an in-depth guide to building complex user interfaces with JavaFX Layouts. Includes preview information about JavaFX 1.3 layouts and features.

Speakers


Slides

WELCOME

VA JA LA FX UT YO January 13, 2010 amy.fowler@sun.com

AGENDA

AGENDA JavaFX Layout Fundamentals *Changes for 1.3 (in blue) * we reserve the right to change our minds (at least until we ship)

INTERFACES, 2010

INTERFACES, 2010

JAVAFX LAYOUT GOALS

JAVAFX LAYOUT GOALS • Make common layout idioms easy • rows/columns, forms, alignment, spacing, etc. • Don’t get in the way of creativity • animation - must allow things to move • free form shapes - no longer restricted by nested, clipped, rectangles! • Performance a major focus for 1.3

LAYOUT MECHANISM

LAYOUT MECHANISM • Scene graph layed out once per pulse, before rendering • nodes call requestLayout() when preferred size changes • requests all coalesced for next layout pass • layout executes top-down (dirty branches only) • MUCH more efficient in 1.3 • fine-tuned calls to requestLayout • more efficient bounds calculations

RESIZABLE VS. NOT

• Resizable mixin class enables nodes to be resized externally: public public public public public public public public var width:Number var height:Number function getMinWidth():Number function getMinHeight():Number function getPrefWidth(height:Number):Number function getPrefHeight(width:Number):NUmber function getMaxWidth():Number function getMaxHeight():Number RESIZABLE VS. NOT Resizable Containers Controls Not Resizable Group, CustomNode Text, ImageView, Shapes • Resizable & non-Resizable nodes can be freely mixed • non-Resizables treated as rigid (min = pref = max) • Resize != Scale

LAYOUT BOUNDS

LAYOUT BOUNDS • Logical bounds used for layout calculations public-read protected layoutBounds:Bounds layoutBounds boundsInLocal boundsInParent

LAYOUT BOUNDS (contd.)

LAYOUT BOUNDS CONTINUED Node type Non-Resizable (Shapes, Text, etc.) layoutBounds geometry only •no effect/clip/transforms 0, 0 width x height Resizable (Controls & Containers) •regardless of visual bounds Group union of childrens’ boundsInParent • effects/clip/transforms on children included • effects/clip/transforms on Group not included

TEXT LAYOUT BOUNDS

TEXT LAYOUT BOUNDS • In 1.2, layoutBounds for Text was tight visual bounds • very expensive! • problematic for layout • doesn’t include leading, trailing whitespace puppy • 1.3 provides Text var for controlling how bounds calculated public var boundsType:TextBoundsType TextBoundsType.LOGICAL TextBoundsType.VISUAL (default) puppy

LAYOUT APPROACHES

LAYOUT APPROACHES • App-managed • put nodes inside Groups • set translation to control positioning • use binding for dynamic layout behavior • Container-managed • put nodes inside Containers • Containers control location, sizing, dynamic behavior • recommended for common layout idioms • Blend them both

APP MANAGED - POSITIONING

APP MANAGED POSITIONING • Position nodes by setting translation • set layoutX,layoutY for general positioning • set translateX,translateY for animation or adjustments • Transition classes modify translateX, translateY • final tx,ty => (layoutX + translateX), (layoutY + translateY) • translation != final location node.layoutX = bind x - node.layoutBounds.minX node.layoutY = bind y - node.layoutBounds.minY

APP MANAGED - SIZING

APP MANAGED SIZING • Use binding to control dynamic sizing Stage { var scene:Scene; scene: scene = Scene { width: 300 height: 300 content: HBox { width: bind scene.width height: bind scene.height } } }

CONTAINER MANAGED

CONTAINER MANAGED • Put nodes inside Containers • Containers control positioning on all “managed” content • they set layoutX,layoutY (but don’t touch translateX/Y) • base all layout calcs on layoutBounds (not visual bounds) • Containers resize only Resizable nodes • treat non-Resizables (and nodes with bound width/height) as rigid VBox { spacing: 10 content: for (img in images) ImageView { image: img } } }

AUTO SIZING

AUTO SIZING • 1.2 dichotomy between Groups and Containers: • Resizables inside Containers are resized automatically when their preferred size changes changes • Resizables inside Groups will NOT be resized when their preferred size • In 1.3, Groups will also automatically resize Resizable children when their preferred sizes change. • Two ways to turn this off: • set the child to “unmanaged” • bind the child’s width/height

LAYOUT & TRANSFORMS

LAYOUT & TRANSFORMS • For nodes inside Containers: • modifying effect, clip, transforms will NOT affect layout • TranslateTransition, ScaleTransition, RotateTransition will NOT affect layout • Wrap node in Group if you want transforms to affect layout Stack { content: Group { content: Rectangle { rotate: 45 // rotate will affect layout } } }

CONTAINERS

CONTAINERS • Container class mixes Resizable into Group • pondering change to extend directly from Parent • Abstract base class for layout containers • layoutBounds will always be (0, 0 width x height) • even if visual bounds differ

CONCRETE CONTAINERS

CONCRETE CONTAINERS • Stack, HBox,VBox, Tile, Flow, Panel, Grid (in 1.3 preview) • lay out both visible and invisible nodes • do not clip contents to fit within layout bounds • honor layout constraints set in LayoutInfo • 1.3 adds var for adding white space around content: public var padding:Insets; • 1.3 adds var for aligning on pixel boundaries: public var snapToPixel:Boolean = false;

HBOX & VBOX

HBOX & VBOX • Simple horizontal row or vertical column of nodes • Configurable spacing & alignment • Resizes Resizables to their preferred sizes HBox { spacing: 4 content: for (in in [0..4]) Thing { text: “{i}” } ] }

STACK

STACK • Easy back-to-front layering • z-order matches order of content[] sequence • Its preferred size is largest preferred width/height of children • Resizes Resizables to “fill” stack (up to their max size limits) Stack { content: [ Rectangle { ... Circle { ... } Label { text: “3” } ] } }

TILE

TILE • Lays out nodes in grid of uniform-sized “tiles” • Horizontal or vertical orientation • Wraps tiles when Tile’s size changes • Size of each “tile” defaults to largest preferred content • Resizes nodes to “fill” tile (up to their max size limits) • Configurable spacing & alignment Tile { columns: 3 hgap: 3 vgap: 3 content: for (i in [0..5]) Thing { text: “{i}” } }

TILE (contd.)

TILE CONTINUED • In 1.3, columns var (horizontal) and rows var (vertical) used only to compute Tile’s preferred size • may not reflect actual rows/columns • In 1.3, new var controls whether tile size is fixed or recomputed as content sizes change: public var autoSizeTiles:Boolean = true; Tile { columns: 10 autoSizeTiles: false tileWidth: 150 tileHeight: 100 content: for (i in (sizeof images)) ImageView {image: Image { ... } } }

FLOW

FLOW • Horizontal or vertical flow that wrap on width/height boundaries • Always resizes Resizables to their preferred sizes • Configurable spacing & alignment • 1.3 adds var to control the preferred wrap dimension: public var wrapLength:Number = 400; Flow { wrapLength: 300 hgap: 5 vgap: 10 content: for (i in [0..7]) Thing { ... } }

PANEL

PANEL • Useful for custom layout on object literals • Provides function variables for container behaviors: public public public public public public public var var var var var var var minWidth:function():Number; minHeight:function():Number; prefWidth:function(h:Number):Number; prefHeight:function(w:Number):Number; maxWidth:function():Number; maxHeight:function():Number; onLayout:function():Void; Panel { onLayout: function():Void { // position/resize content nodes } }

IMPLEMENTING PANELS

IMPLEMENTING PANELS • Use convenience functions from Container! import javafx.scene.layout.Container.*; getManaged(content:Node[]):Node[] getNodePrefWidth(node)/getNodePrefHeight(node) positionNode(node, x, y) resizeNode(node, width, height) layoutNode(node, areaX, areaY, areaWidth, areaHeight, baseline, hfill, vfill, hpos, vpos) • They are smart... • handle subtracting minX, minY for positioning • deal with Resizable vs. non-Resizable nodes • honor LayoutInfo if set on node • swallow bind exceptions when width/height are bound

GRID

GRID • Based on Grid from JFXtra’s (thanks, Stephen!) • Supports rich, row-oriented grid layout • spanning, growing, alignment, etc Grid { hgap: 5 vgap: 8 rows: [ GridRow { cells: [ Label{}, ListView { layoutInfo: GridLayoutInfo { hspan:3 } } ]} GridRow { cells: [ ...] } ] }

LAYOUT INFO

LAYOUT INFO • Node hook to specify layout preferences: public var layoutInfo:LayoutInfoBase • Can be shared across nodes (values not copied) def sliderLAYOUT = LayoutInfo { width: 100 } def slider1 = Slider { layoutInfo: sliderLAYOUT } def slider2 = Slider { layoutInfo: sliderLAYOUT } • Should only be needed when customization is required • 3rd parties can extend LayoutInfoBase or LayoutInfo to create custom constraints

LAYOUT INFO VARIABLES

LAYOUT INFO public var managed:Boolean; public public public public public public var var var var var var minWidth:Number; minHeight:Number; width:Number; height:Number; maxWidth:Number; maxHeight:Number; size preference overrides alignment space around dynamic resize behavior public var hpos:HPos; public var vpos:VPos; public var margin:Insets; public public public public public public var var var var var var hgrow:Priority; vgrow:Priority; hshrink:Priority; vshrink:Priority; hfill:Boolean; vfill:boolean;

MANAGED VS. UNMANAGED

MANAGED VS. UNMANAGED • A managed node will have its layout managed by it parent Group Container Resizable child resized to preferred resized and positioned non-Resizable child no action positioned only • By default, all nodes are managed • An unmanaged node will be ignored (for layout) by parent • To unmanage, set bit in layoutInfo: VBox { content: [ Rectangle { layoutInfo: LayoutInfo.UNMANAGED // VBox will ignore } ...

OVERRIDING SIZE PREFS

OVERRIDING SIZE PREFS • Resizables have intrinsic values for min, pref, max sizes • Can use LayoutInfo to override values • To set a specific size on a Resizable, override it’s preferred: VBox { content: [ Button { // VBox will resize button to 100x100 layoutInfo: LayoutInfo { width: 100 height: 100 } }... ] } • DO NOT set width/height directly on Resizable - parent will obliterate values! (unless Resizable is unmanaged)

NODE ALIGNMENT

NODE ALIGNMENT • Sometimes node’s size is different from it’s allocated layout area • it cannot be resized (non-Resizable or has bound width/height) • it’s min or max size prevents it • Containers have default alignment vars for this case public var nodeHPos:HPos public var nodeVPos:VPos public enum HPos { LEFT, LEADING, CENTER, RIGHT, TRAILING } public enum VPos { TOP, PAGE_START, CENTER, BASELINE BOTTOM, PAGE_END }

NODE ALIGNMENT (contd.)

NODE ALIGNMENT CONTINUED • LayoutInfo can be used to override alignment for specific nodes VBox { // nodeHPos defaults to HPos.LEFT content: [ Thing { text: “0” } Thing { text: “1” } Thing { text: “2” layoutInfo: LayoutInfo { hpos: HPos.CENTER } } ] }

BASELINE ALIGNMENT

BASELINE ALIGNMENT • 1.3 Containers supports roman baseline vertical alignment! HBox { nodeVPos: VPos.BASELINE content: [ ... ] } • TextOffsets mixin must be implemented by classes that want to be aligned on baseline: public var baselineOffset:Number • Text, Container, and Controls all implement TextOffsets • Classes that don’t implement TextOffsets will be treated as if baseline was on bottom edge

CONTENT ALIGNMENT

CONTENT ALIGNMENT • Container’s content sometimes doesn’t fit it’s size • Containers have vars for overall content alignment public var hpos:HPos = HPos.LEFT; public var vpos:VPos = VPos.TOP; HBox { hpos: HPos.CENTER vpos: VPos.CENTER nodeVPos: VPos.BOTTOM ... • In 1.3, HBox/VBox get var for content fill instead of align public var vfill:Boolean = false; HBox { vfill: true nodeVPos: VPos.BOTTOM ...

FILLING

FILLING • “Filling” defines behavior when Resizable’s allocated layout area is larger than its preferred size fill = false fill = true keep node to preferred size expand node to fill layout area (up to max limit) • Stack and Tile do filling by default • HBox,VBox, Flow, and Grid do not fill by default • In 1.3 LayoutInfo can be used to change node’s fill behavior: Stack { content: [ Button { layoutInfo: LayoutInfo { vfill: false } }... ] }

GROWING & SHRINKING

GROWING & SHRINKING • “Growing” is priority mechanism used by Container to assign extra space when multiple nodes compete for that space node to fill the larger area (filling controls that) • applies to increasing layout area assigned to a node, NOT resizing • “Shrinking” is priority mechanism for taking away space when there is less than needed public enum Priority } { NEVER, SOMETIMES, ALWAYS • HBox supports horizontal grow/shrink • VBox supports vertical grow/shrink • Grid supports horizontal and vertical grow/shrink • Stack, Tile, Flow do not directly support grow/shrink, however...

GROWING & SHRINKING (contd.)

GROWING & SHRINKING • Grow/shrink priorities are propagated up scene-graph • if Container has child with a grow of ALWAYS, then its grow value will be ALWAYS • enables powerful default behavior without heavy customization HBox { content: [ Button{}, Button{}, TextBox { layoutInfo: LayoutInfo { hfill: true hgrow: Priority.ALWAYS hshrink: Priority.ALWAYS } } Label{} ] }

1.3 WORK IN PROGRESS

1.3 WORK IN PROGRESS • Support for layout roots • enable scene-graph branches to be laid out without affecting ancestors • useful for clipped content (scroll panes, viewports, etc) • Default LayoutInfo for Controls • sensible resizing “just works” out of the box • More efficient min/pref/max size calculations during layout • currently recalculated (for nested containers) at every level of layout pass

1O LAYOUT COMMANDMENTS

1O LAYOUT COMMANDMENTS 1.Freely mix both app-managed and container-managed layout approaches 2.If you create a Node in a Group, then you must set its position and size, as Groups don't do layout. 3.If you create a Node in a Container, you are handing control of that node's position (and size, if its Resizable) to the Container. 4.Use layoutBounds as the basis of all layout-related calcs. 5.If a node's effect, clip, or transforms should be factored into its layout, then wrap it in a Group.

7.5 LAYOUT COMMANDMENTS

1O LAYOUT COMMANDMENTS 6.Set layoutX/layoutY for stable layout position and translateX/translateY for animation or adjustments. 7.Remember layoutX/layoutY are offsets, not final location. 8.LayoutInfo is only relevant when set on a Node that has a Container as its parent, otherwise it's ignored. 9.If you need to control the size of a Resizable inside a Container, then either bind its width/height or override its preferred size using LayoutInfo. or unmanage it. 10.If you want a Resizable to automatically resize when its preferred size changes, then place it in a Container. 7.5

TEAM EFFORT

TEAM EFFORT

JOIN THE TEAM

JOIN THE TEAM We’re hiring! Senior Software Engineer/Text/UI-Controls contact: brian.beck@sun.com Senior Software Engineer/Graphics/OpenGL/shaders Senior Software Engineer/Text/Unicode/bi-di/OpenType contact: srividhya.Narayanan@sun.com

THE END.

THE END.