1 2 Previous Next

aim

19 posts
aim

Back to our Roots for JavaOne Blog

Posted by aim Sep 15, 2010

On my wordpress blog, I've composed yet another digest of JavaOne highlights.

I'm relocating to wordpress: amyfowlersblog.wordpress.com.

My inaugural entry is on (no surprise) JavaFX 1.3 Layout - come over and say hello.

aim

heart and soul Blog

Posted by aim Jan 26, 2010

Though I've yet to receive my red pill or my blue pill, I cannot let this eve pass without a formal farewell to the company I've called home since college. Just the other day I came across my original Sun badge while cleaning out an old box:

Sun was 6.  I was 22.    I chose Sun because it seemed like networking might be important for the future.  We were battling IBM, DEC, and HP, and feared that Windows NT would be the death of Unix.   Apple's future seemed bleak.   Let that be a lesson to anyone who thinks they know what the future holds in this business. 

In addition to being a phenomenal place to be an engineer, Sun had tremendous heart and soul.  Thanks, Scott, for creating that.  I will miss it.


 

aim

JavaFX1.2: Layout Blog

Posted by aim Sep 10, 2009

Two Worlds Collide

On the one hand, JavaFX's powerful scene-graph and animation engine enables gamer types to rapidly create dynamic visual scenes that are functionally expressed through binding and triggers and timelines. On the other, it's growing controls and charts libraries clearl ]]>y stake out a more traditional GUI turf. As interfaces finally graduate to the 21st century, the lines between these two worlds is blurring in exciting ways. Our challenge is to evolve the FX platform to support this convergence, which speaks precisely to why layout in JavaFX is complicated enough

So let's get down to business. With JavaFX1.2 there are two approaches to laying out your scene:

  • app-managed: application directly sets the position and size of nodes and uses FX binding to express dynamic relationships.
  • container-managed: application places nodes inside containers which manage their size/position according to preferences set on the containers and nodes.

Typical JavaFX applications merrily blend both approaches, but before we get into the nitty-gritty, let's review a couple of core concepts that have arisen from our collision of purpose.

 

Resizable vs. Not

In JavaFX, every single visual element is represented by a stateful node in the scene graph. This means that the base javafx.scene.Node class needs to be very very very small and we must use all the restraint we can muster to resist the api creep that tends to invade the base class of even the best-intentioned toolkits. This is one reason we introduced the javafx.scene.layout.Resizable mixin; another reason is that not all node types want to be resizable.

The Resizable mixin provides the machinery necessary to allow a node to be externally resized:

 public var width:Number; public var height:Number; public function getMinWidth();Number; public function getMinHeight():Number; public function getPrefWidth(height:Number):Number; public function getPrefheight(width:Number):Number public function getMaxWidth():Number; public function getMaxHeight():Number; 

Resizable Node Classes:

  • Containers: HBox, VBox, Flow, Tile, Stack, Panel, ClipView
  • Controls: Button, TextBox, Label, ListView, Charts, etc..

Non-Resizable Node Classes:

  • Group, CustomNode, Text, Shapes, ImageView

It's important to understand that the non-Resizable nodes do in fact change size as their variables are manipulated, it's just that they have no consistent api for explicitly setting their size. Even Rectangle, which teases you with its width/height variables, isn't resizable, as it's width and height vars define it's shape geometry and do not include its stroke (which is centered on the geometry) so its actual bounds fall outside its width/height boundary, as shown in the image below:

A perfect seque ...

Layout Bounds

The layout bounds of a node is the rectangular area (e.g. bounding box) that is used for layout-related calculations. It's defined as a read-only variable on the Node class:

 public-read protected layoutBounds:Bounds 

It's type, javafx.geometry.Bounds, has the classical bounding box variables:

 public-init package minX:Number public-init package minY:Number public-init package width:Number public-init package height:Number public-read package maxX:Number public-read package maxY:Number 

It's perfectly normal for the min and max coordinates to be negative, so don't assume the upper-left bounds of a node are necessarily anchored at 0,0.

For non-Resizable leaf node classes (Shapes, Text, etc) the layout bounds will be equal to the node's geometric bounds (shape geometry plus stroke) and it does NOT include the effect, clip, or any transforms. This means you can add effects and transforms (shadows, rotation, scale, etc) and the layout bounds of shapes will remain unchanged.

For example, here's a circle (centered at 0,0 by default) with a drop shadow:


Now, for Group nodes, the layout bounds will be equal to the union of all the boundsInParent of all of its visible content nodes. This means that any effects, clipping, or transforms set on the child nodes will be factored into the group's layout bounds, however any effects, clip, or transforms set directly on the group will notbe included in its layout bounds.

This is demonstrated in the example below which shows that setting the reflection directly on the group has a different result than setting the reflection on the group's chlidren individually:

For Resizable nodes classes (Containers & Controls), the layout bounds will be wired to 0,0 width x height, regardless of what the actual physical bounds of the node is.

in fact, if you create a class which mixes in Resizable, you should ensure that this is wired properly by adding the following line to your class:

override var layoutBounds = bind lazy BoundingBox{ minX:0 minY:0 width:width height:height } 

insiders note: we may add a ResizableCustomNode base class in a future release to handle this book-keeping, although we're searching for a shorter class name, maybe CustomResizable.

It may seem odd that layout bounds isn't necessarily tied to the physical bounds of a node, but in this highly dynamic world of animating graphical objects, it's often desirable to separate the notion of layout (which often needs to be stable) from the physical bounds of a node which are changing in ways that often need not be factored into the layout -- like a bouncing icon in a task bar or drop shadows and glow effects. But if you're still scratching your head, you can read more detail on this in my Understanding Bounds blog.


App-Managed Layout

One approach to laying out your scene is to explicitly set the size and position of your nodes and establish dynamic behavior by using binding. In early versions of JavaFX this was the only option and it remains a perfectly valid approach.

 

 

Positioning Nodes

Node has two sets of translation variables, which often perplexes newcomers:

 public var layoutX:Number public var layoutY:Number public var translateX:Number public var translateY:Number 

The final translation on a node is computed by adding these together:

 tx= layoutX+translateX ty= layoutY+translateY 

This separation allows layoutX/layoutY to be used for controlling a node's stable layout position and leaves translateX/translateY for use in dynamic adjustments to that position. In fact, the javafx.animation.transition.TranslateTransitionclass does exactly that -- modifies translateX/translateY to animate the position of the node without disturbing the surrounding layout (e.g. great for flying, bouncing, or jiggling animations).

Therefore, to establish a node's stable layout position, set layoutX/layoutY. Be aware that these variables define a translation on the node's coordinate space to adjust it from its current layoutBounds.minX/minY location and are not final position values (in hindsight we probably should have named them "layoutTX"/"layoutTY" to emphasize this subtlety). This means you should use the following formula for positioning a node at x,y:

 node.layoutX = x - node.layoutBounds.minX node.layoutY = y - node.layoutBounds.minY 

Or, in the case of object literals, don't forget the bind:

 def p = Polygon { layoutX: bind x - p.layoutBounds.minX layoutY: bind y - p.layoutBounds.minY ... } 

I'll illustrate this simply with a Circle, which is centered on the origin by default, putting its minX and minY in the negative coordinate space:

The blue circle does not end up at 50,50 because that 50,50 translation is added to its current minX, minY, which is -25,-25, resulting in a final position of 25,25.

 

 

Sizing Nodes

For non-Resizable Shape classes, you can just set the various geometric variables to control their size (e.g. width/height/strokeWidth for Rectangle, radius for Circle, etc). You might be tempted to use a scale operation as an easy means for adjusting a shape's size, however be aware that this scales the entire coordinate space of the node, including it's stroke, fill, etc. so be cautious not to confuse resizing with scaling.

For javafx.scene.text.Text nodes (also non-Resizable), their size is defined by their content string, except in the case of multiline, where you can set the Text's wrappingWidth variable. Note that due to a bug, the layoutBounds of a Text node will not account for any spaces that pad the beginning or end of the string, so the layout bounds always shrink to fit visible character shapes (e.g. " hello " would be treated as "hello") ; we'll fix this in a future release, as this once sent me on a 6 hour wild goose chase.

Group nodes cannot be explicitly sized -- they just take on the collective bounds of their content nodes, expanding or shrinking to fit.

Any class which implements Resizable (again, Containers and Controls) can have width and height variables explicitly set, although one should be aware that Resizable classes have a preferred size typically based on some internal state (e.g. a Label's text or an HBox's content's preferred sizes). Most Resizable classes will initialize their own width/height vars (if not explicitly set by the app) to their preferred size, so you should only have to set the size if you need to deviate from the preferred.

 

 

Using Binding to Establish Dynamic Layout Behavior

I love using bind for establishing some of the simpler layout patterns in the scene. Following are some common idioms:

Centering a node within a scene

def scene:Scene = Scene { var group:Group; width: 500 height: 400 content: group = Group { layoutX: bind (scene.width - group.layoutBounds.width)/2 - group.layoutBounds.minX layoutY: bind (scene.height - group.layoutBounds.height)/2 - group.layoutBounds.minY content: [ ... ] } } 

Ensuring the toplevel container resizes to fill the scene when the user resizes the stage:

def scene:Scene = Scene { var hbox;HBox width: 400 height: 200 content: hbox = HBox { width: bind scene.width height: bind scene.height content: [ ... ] } } 

Attaching a background to something

Group { var something:Group; content: [ Rectangle { layoutX: bind something.layoutBounds.minX layoutY: bind something.layoutBounds.minY width: bind something.layoutBounds.width height: bind something.layoutBounds.height fill: LinearGradient {... } } something = Group { content: [ ...] } ] } 

Assembling the layout of private content within a CustomNode subclass:

class StatusField extends CustomNode { public var valid:Boolean = true; public-read protected var textbox:TextBox; var statusicon:Circle; override function create():Node { Group { content: [ textbox = TextBox { layoutY: bind statusicon.layoutBounds.height - 4 - textbox.layoutBounds.minY } statusicon = Circle { fill: Color.RED radius: 4 layoutX: bind textbox.layoutBounds.width - 4 - statusicon.layoutBounds.minX layoutY: bind 0 - statusicon.layoutBounds.minY visible: bind not valid effect: DropShadow{ offsetX: 2 offsetY: 2 radius: 4} } ] } } } 

 

 

 


Container-Managed Layout

If you need to layout your nodes in classical ways (rows, columns, stacks, etc) then nothing beats just being able to throw those nodes into a container class that efficiently performs all the aforementioned magic for you.

 

 

Containers

javafx.scene.layout.Containeris a Resizable Parent class that performs a layout algorithm on its content nodes as well as calculates reasonable values for its own minimum, preferred, and maximum sizes based on those of its content. This means you can nest containers to your heart's content and they will just do the right thing dynamically when layout conditions change (nodes are added/removed, state changes that cause control's preferred sizes to change, etc).

You can put both Resizable and non-Resizable nodes inside a Container. Containers will control the position of content by setting layoutX/layoutY and will typically strive to set the width/height of Resizables to their preferred size and will treat non-Resizable nodes as rigid, which means non-Resizables (like Shapes, Groups) will be positioned but not resized.

So, once a Resizable is inside a Container, that Container gets to control its size/position, obliterating any width/height values you may have hand-set on that Resizable. If you need to explicitly control the size of a Resizable node inside a Container, you have two choices:

  1. bind the width/height of that Resizable -- the Container will treat it as a non-Resizable node (position it, but won't resize it).
  2. override the Resizable's preferred size using LayoutInfo (described later).

When state within a Container changes such that it would need to re-layout its contents, it automatically invokes requestLayout(), marking itself and all ancestors as "dirty", such that that branch will be re-layed out on the next pulse.

 

 

Concrete Containers

JavaFX1.2 provides a handful of concrete container classes in javafx.scene.layout to cover common layout idioms.

  • HBox - horizontal row of nodes with configurable spacing and alignment
  • VBox - vertical column of nodes with configurable spacing and alignment
  • Flow - horizontal or vertically oriented flows that wrap at width/height boundaries
  • Stack - back-to-front stacking of nodes with configurable alignment
  • Tile - lays out nodes in grid of uniformly-sized tiles
  • Panel - allows layout customization using an object-literal
  • ClipView - provides a clipped, pan-able view of its content node

The basic usage of these classes is to simply set their content (as you would for a Group) and maybe customize a few variables. For example:

 HBox { spacing: 4 content: [ Label { text: "Name:"}, TextBox {} ] } Flow { vertical: true content: for (img in images) ImageView { image: img } } and so on 

The glaring hole in this list is a multi-purpose grid layout. Right now you can use either the Grid or MigLayout containers from the JFXtras Core extension package, but its addition to our runtime is inevitable.

Alignment

The Container classes provide variables to control the overall aiignment of the content within the container's own width/height:

 public var hpos:HPos: horizonal alignment of the content within the container's width public var vpos:VPos: vertical alignment of the content within the container's height 

Additionally, often a node cannot be resized to fill its allocated layout space, either because it isn't Resizable or its maximum size prevents it, and so the Containers provide variables for controlling the default node alignment within their own layout space:

 public var nodeHPos:HPos: default horizontal alignment of nodes within their layout space public var nodeVPos:VPos: default vertical alignment of nodes within their layout space 

The HBox example below shows how these options can be used to control alignment:

One final alignment caveat is that in JavaFX1.2, VPos.BASELINE alignment is not supported inside Containers. This will be fixed in the next major release, making it much easier to properly align Labels with other Controls.

 

 

Clipping

Something that often surprises developers coming from traditional toolkits such as Swing is that Containers do not automatically clip their content. It's quite possible for the physical bounds of a container's content to extend outside of its layout bounds, either because effects and transforms have been applied to the content or because they simply couldn't be resized to fit within the container's layout bounds.

If you want the old-fashioned clipping behavior, you can set the clip yourself:

def flow = Flow { clip: Rectangle { width: bind flow.layoutBounds.width height: bind flow.layoutBounds.height } content: [ ....] } 

But beware that by doing so, you'll be unable to have animation or effects that would extend beyond your container's layout bounds, as it will all be clipped out -- just as you specified.

 

 

Node Visibility

The concrete Container classes will layout nodes regardless of their visibility, so if you don't want invisible nodes layed out, then you should either remove them from the container or make them "unmanaged", which I'll show how to do in the next section on LayoutInfo.

 

 

LayoutInfo

Often for layout its necessary to be able to specify layout constraints on a per-node basis, so in 1.2 we added just such a hook on Node:

 public var layoutInfo:LayoutInfoBase 

This layoutInfo variable is only referenced if the node's parent is a Container that honors it, otherwise it's ignored. In other words, don't expect layoutInfo settings to have any affect on a node contained by a plain old Group -- Groups are agnostic to layout.

javafx.scene.layout.LayoutInfoBaseis an abstract class which includes only a single variable:

 public var managed:Boolean 

This variable (supported by all concrete LayoutInfo subclasses) enables a node in a container to be marked as unmanaged, telling the container not to factor it into layout calculations. By default all nodes are managed unless a LayoutInfo where managed equals false is set on it.

LayoutInfo instances may be shared across nodes, which is convenient and efficient when you want to apply a set of layout constraints to multiple nodes. Just be aware that changing any of that shared layoutInfo's vars will affect all nodes its attached to, a danger inherent in a stateful flyweight pattern.

LayoutInfois a concrete extension of LayoutInfoBase to add common constraint variables:

 public var minWidth:Number public var minHeight:Number public var width:Number public var height:Number public var maxWidth:Number public var maxHeight:Number public var hpos:HPos; public var vpos:VPos; 

Any values set on a node's LayoutInfo will reign supreme over the values normally computed by the Container, essentially allowing min/preferred/max sizes and alignment to be customized on a per- object literal basis. Note however that for most common layout scenarios using the Container classes, you should rarely have to set a LayoutInfo on a node -- it exists as a trap door for customization.

Here are some common usage scenarios for LayoutInfo:

Override preferred size of a node (this is how to effectively set the size of a Resizable managed by a Container):

def buttonLAYOUT = LayoutInfo { width: 80 }; // override Button's preferred width HBox { content: [ Button { text: "Apply" layoutInfo: buttonLAYOUT } Button { text: "Cancel" layoutInfo: buttonLAYOUT } ] } 

Insert a background Rectangle into a Container:

def UNMANAGED = LayoutInfo { managed: false }; def tile = Tile { content: [ bg = Rectangle { // note: this is simple because there is no stroke layoutInfo: UNMANAGED width: bind tile.layoutBounds.width height: bind tile.layoutBounds.height fill: LinearGradient {...} } ...other content... ] } 

Customize alignment:

Stack { // stack's default nodeVPos is CENTER content: [ ImageView { ...} Label { text: imageTitle layoutInfo: LayoutInfo { vpos: VPos.BOTTOM } ] } 

insider's note: the name of the LayoutInfo class was another infinite debate; initially we called it "LayoutConstraints", but Richard and I hated how this exploded object-literals into the right margin, e.g. layoutConstraints: LayoutConstraints { ...}. "LayoutInfo" seemed short and sweet.

 

 

Custom Containers

You can create your own re-usable Container subclasses by extending Container and overridding getPrefWidth()/getPrefHeight(), and doLayout(). Container also provides a number of script-level convenience functions that make the pursuit of Container-authoring easier:

 public function getManaged(content:Node[]):Node[] public function getNodeMinWidth(node:Node):Number public function getNodeMinHeight(node:Node):Number public function getNodePrefWidth(node:Node):Number public function getNodePrefHeight(node:Node):Number public function getNodeMaxWidth(node:Node):Number public function getNodeMaxHeight(node:Node):Number public function positionNode(node:Node, x:Number, y:Number) public function resizeNode(node:Node, width:Number, height:Number) public function layoutNode(node:Node, areaX:Number, areaY:Number, areaW:Number, areaH:Number hpos:HPos, vpos:VPos): 

These functions are smart -- they deal with Resizable vs. not, accessing LayoutInfo if it exists, adjusting layoutX/Y based on minX/minY, catching exceptions thrown when width/height are bound, etc.

I'll save my Container authoring example for a future "advanced Layout" blog, as this one is already long-winded.

 

 

Panel

Sometimes you'll want to customize layout without the added burden of creating your own Container subclass, which is something that comes up frequently when creating the hidden scenes inside of CustomNodes.

TheChris Oliver's blog

. This is the code of an FX-master and it puts me in awe. The downside it that it takes a high level of FX skill to both write and read recursive bound functions, so it's not an approach that will come easy to those new to FX.

The other problem is that extensive use of binding for the complex relationships needed to layout a reasonably large scene could pose a performance issue in the current runtime. Binding is a seductive solution to many problems, but currently it comes at a cost in both size and execution speed. The machinery behind bound variables takes up more memory and in the case of layout on a deeply nested scene, re-evaluation of the dependency chain when any node changes size or position could be an expensive operation. We encountered exactly this reality during development of 1.2, which is why we introduced the more procedural layout pass that batches up requests to adjust layout and performs the layout algorithm once per pulse before rendering.

That said, the ultimate goal is to make binding inexpensive enough that it enables the expressive FX code which Chris advocates. As we speak, our compiler team is working to reduce both the size and performance costs of binding; at the same time, we're making improvements to the scene-graph that will ensure apps using the pure binding approach for dynamic layout don't pay any of the procedural costs (which boil down to marking branches dirty and executing the layout pass before rendering). Again, we aim to serve both masters.

aim

Good Book, Great Authors Blog

Posted by aim Jul 15, 2009
Even before I started working on the JavaFX project, there were a handful of very brave outside individuals who have dedicated tremendous energy to tracking our bleeding edge. I've had the pleasure of working with two of them in particular, Stephen Chin and Jim Weaver, and I'd like to call attention to their JavaFX book (hot off the press):

Pro JavaFX Platform: Script, Desktop and Mobile RIA with Java Technology

This book is everything we'd like our documentation to be: organized, thorough, practical, and easy to read. In fact, I'll be using it as a general reference myself for the areas of JavaFX for which I don't personally slave over.

As early adopters, the authors of this book have been happy to share their experiences with us in ways that have strengthened the apis and platform and I am personally grateful for their feedback -- even when Stephen tells me I got something wrong (like LayoutInfo's width/height should have been called prefWidth/prefHeight :-).  
This is the first in a series of articles to cover layout for JavaFX 1.2. We changed the api a bit (for the better of course), but that hasn't made it any easier to explain. However, if you're an impatient reader and want to cut to the chase, I recommend jumping to the tailing section Bounds in Practice.

The visuals displayed within a JavaFX scene are fully represented by a 2D scene graph where each visual element (line, path, image, etc) is represented by a distinct node with variables that can be easily manipulated dynamically. The node's size and position (otherwise known as its "bounds") becomes complicated when considering these many variables which contribute to its bounds, such as shape geometry (startX/startY, width, radius, etc), transformations (scale, rotate, etc), effects (shadows, glow, etc), and clipping. Understanding how each of these variables affects the bounds calculations of a node is crucial to getting the scene layout you want. 

Bounds Class

In JavaFX1.2, a node's rectangular bounds are represented by the Bounds class which provides init-only minX, minY, maxX, maxY, width, height variables. Keep in mind that since the bounding box can be anywhere in the 2D coordinate space, the X/Y values may often be negative.

Note: The Bounds class was introduced in JavaFX1.2 in anticipation of adding Z in the future. BoundingBox (concrete 2D implementation of Bounds) has replaced Rectangle2D for bounds values, however Rectangle2D still exists as a general purpose geom class for other uses.


Node Bounds Variables

A Bounds object is always relative to a particular coordinate system, and for each node it is useful to look at bounds in both the node's local (untransformed) coordinate space, as well as in its parent's coordinate space once all transforms have been applied. The Node api provides 3 variables for these bounds values:

                
boundsInLocal(public-read)physical bounds in the node's local, untransformed coordinate space, including shape geometry, space required for a non-zero strokeWidth that may fall outside the shape's position/size geometry, the effect and the clip.
boundsInParent(public-read)physical bounds of the node after ALL transforms have been applied to the node's coordinate space, includingtransforms[], scaleX/scaleY, rotate,translateX/translateY, and layoutX/layoutY.
layoutBounds(public-read protected)logical bounds used as basis for layout calculations; by default only includes a node's shape geometry, however its definition may be overridden in subclasses. It does not necessarily correspond to the node's physical bounds.


It's worth pointing out that a node's visibility has no affect on its bounds; these bounds can be queried whether its visible or not.

This might be easier to visualize in the diagram below, which shows the sequence in which the bounds-affecting variables are applied; variables are applied left-to-right where each is applied to the result of the one preceding it (geometry first, layoutX/Y last):

NodeChainBounds3.png

The reason we need a more malleable definition for layout bounds (vs. just using boundsInParent) is because a dynamic, animating interface often needs to control which aspects of a node should be factored into layout vs. not (more on this in a forthcoming article on 1.2 layout).

Note: In JavaFX1.0, layoutBounds was originally defined to be boundsInLocal plus the transforms[] sequence; we changed this in 1.2 because we found that often layout did not want to factor in the effect or transforms and it feels cleaner to have no transforms included (vs. 'some' transforms). It also greatly simplifies implementations of Resizable nodes.



Stepping Through a Node Bounds Example



Example 1: Simple Rounded Rectangle
rectangle1b.png

Note that x and y are variables specific to Rectangle and that they position the rectangle within itsown coordinate space rather than moving the entire coordinate space of the node. I throw this out as the first example because it is often the first thing that trips up developers coming from traditional toolkits such as Swing, where changing x,y effectively performs a translation of the component's coordinate space. All of the javafx.scene.shape classes have variables for specifying appropriate shape geometry within their local coordinate space (e.g. Rectangle has x, y, width, height, Circle has centerX, centerY, radius, etc) and such position variables should not be confused with a translation on the coordinate space, as we'll look at in our next example.


Example 2: Translation
rectangle2c.png

Now boundsInParent has changed to reflect the translated rectangle, however boundsInLocal and layoutBounds remain unchanged because they are relative to the rectangle's coordinate space, which was what was shifted. Although layoutBounds is relative to the node's local coordinate space, I'm showing it in the parent's to emphasize how a parent container interprets its value when laying out the node.

inside design note: we debated endlessly on whether to rename translateX,translateY to "x","y" (as it's less typing and more familiar to traditional toolkit programming), however we decided that "translate" was more descriptive in the 2D sense and keeping it avoided renaming the x,y position variables in some shape classes.



Example 3: Effect
rectangle2b.png

The drop shadow is included in the physical bounds (boundsInLocal and boundsInParent), however it is not included in layoutBounds, which is often desirable for layout where the drop shadow is considered a subtle decoration rather than part of the object itself.


Example 4: Rotation & Scale
rectangle4.png

layoutBounds remains unchanged (from the perspective of the parent), even as the node's local coordinate system is scaled and rotated.


 

Group Bounds

The bounds of a Group node have a slight twist: layoutBounds is calculated by taking the union of the boundsInParenton all visible children (invisible children are not factored into its bounds). boundsInLocal will take the layoutBounds and add any effect or clip set on the group. Finally, the group's boundsInParent is calculated by takingboundsInLocal and applying any transforms set on the group.


Example 5: Group Bounds

group2.png

The group's layoutBounds are tightly wrapped around the union of it's children boundsInParent and do not include the drop shadow. 

Bounds in Practice

Now that you understand (in excruciating detail) the difference between these bounds variables, you might be wondering why you really need to know or care (as did J.Giles in his blog). Aside from the personal glory of mastering the complexity of 2D geometry, it comes mostly into play when laying out your scene. Here are a handful of practical tips:

Remember that Bounds minX/Y and maxX/Y can be negative; don't assume nodes are anchored at their origin. (e.g. Circle is centered on 0,0).

Remember that layoutBounds is a logical concept -- layoutBounds needn't be tied to the physical bounds of a node and often you don't want it to be (ex. you want to spin an icon without disturbing the layout of its neighbors).

Always use layoutBounds when measuring the current size and position of nodes for the purpose of layout (either when using binding for layout or authoring Container classes).

Be aware of the layoutBounds on nodes you throw in Containers as all containers in javafx.scene.layout reference the node's layoutBounds.

If you want layoutBounds to match a node's physical bounds (including effects, clip, and transforms) then wrap it in a Group (ex. if you want a node to scale up on mouse-over and you want its neighbors to scoot over to make room for the magnified node).

You should rarely need to use boundsInParent.

You almost never need to use boundsInLocal.

 

Finale: Boundisizer Demo

During our JavaFX Controls session at JavaOne, I flew through an hour's worth of layout material in the meager 15 minutes that Rich and Jasper left me (the perils of speaking last!). In the session I showed a little demo that lets you tweak the variables and watch how the various bounds are affected. Action speaks way louder than words or color-coded diagrams:

Run the demo and browse the source at JFXtras .



aim

The Ultimate Craftsman Blog

Posted by aim Jun 20, 2009
Some of us are just driven to create; we arn't happy unless we are making something - houses, software, furniture, blogs, chocolate cake. Turns out that software engineering is a pretty good gig for such a person (modulo the perils of skill-set depreciation). And although society has a tendency to associate creativity with artistry, it's often more about craftsmanship, and I inherited this gene from the ultimate craftsman: my Dad.
  
dad.png

My Dad (James Moore), Berkeley graduate and electrical engineer employed at the Livermore Lawrence Labratory for 38 years, was (is) a genuine geek. This goes beyond the glasses and pocket protector; he approaches everything with the critical sense of an engineer -- break it down, analyze, build it up. Growing up I don't recall us paying anyone to fix anything, ever. My childhood home is full of his handy work, from little custom shelves to hold his electric razor and gadgets, to a housing for a Lite Brite which includes drawers to sort the translucent colored pegs.

His engineering precision influences most of what he does. When he taught me around age 6 to snow ski, the lessons began at his desk, where he drew scale drawings of a chair lift to explain the physics of getting on and off the lift (until then, I'd pictured hopping 15 feet straight down, so this brought relief). Each day he went to work, my mother packed him the same lunch, which included a baggie containing 3 oreo cookies; he always ate two and brought back the third, which was my mother's ingenious way of getting the baggie back. A devoted PC hacker, he wrote BASIC programs to calculate golf club handicaps and when I gave him a copy of Visual BASIC (what program doesn't need a GUI?), he returned it for some low-level software to write his own printer drivers.

I think this nuts and bolts approach to life is how he survived raising four daughters. That enduring grin you see in the pictures is likely a direct result of estrogen asphixiation (especially in the 80's,when we were all teenagers) -- even our family dog and cat were girls. Yet there was never a moment where I wondered if he wished I was a boy. He's always showed nothing but pride in his girls (except maybe the time where he chased me around the dining table for what I'm sure was some awful adolescent backtalk -- and I'm certain I deserved it :-).

So Dad, whether you were helping me with calculus, rescuing me from college car breakdowns, or explaining the doppler effect, your unwavering love and precision has everything to do with why I'm still an engineer. You taught me to be a craftsman in life. Happy Father's Day!

I'll convince you to switch to a mac yet...  
Injecting the "extreme" into this year's GUI Makeover session at JavaOne, Jasper's finale of blowing up junk email with rocket explosions was really fun and of course completely absurd.

rocket3.png

Looking beyond the antics, our message in the session was actually very practical:

You can blend the strengths of Swing and JavaFX to give your Java applications that visual edge that users are coming to expect from modern clients.

If you weren't able to attend the session you can download the GUI Makeover slides (I couldn't resisting drawing a mustache & beard on Chet's face in slide 6).

If you're a Swing developer who's both skeptical and curious about JavaFX, I've broken down the process into 10 steps for integrating your Swing components into a JavaFX application.

Note: We also recognize the need for the inverse (embedding a JavaFX scene into a Swing app), however that is not supported with 1.2 as it requires a more formal mechanism for manipulating JavaFX objects from Java code. The structure of these JavaFX objects is a moving target (for some very good reasons) and we're not ready to lock that down (yet). However,as with most software, this can be hacked (see Rich & Jasper's hack on Josh's blog) but know that you are laying compatibility down on the tracks if you go there.

Honestly, you can pull large portions of your Swing GUIs into JavaFX very easily...

0. Find Your Design

Since every good list-of-10 should go to 11, I've inserted a Step 0 (really the most important step of all): use all means at your disposal to find a good design. If you arn't lucky enough to have that rare engineer who's good at both coding and visual design (e.g. a Jasper Potts or a Romain Guy) and you can't afford to hire a good designer, then study the interfaces you respect and "borrow" from them. The nicest interfaces use effects and animation in subtle ways to bring fluidity to the UI. Here are some random links on the subject (and I'd welcome suggested additions to this list): 

1. Touch Base with the Language

Turns out that most of the developers I encounter who have a negative impression of the JavaFX scripting language have actually never tried it. A little more than a year ago I was there too; it looked weird and I couldn't imagine why I'd ever want to use anything but Java to code my apps. But it turns out that using a language designed for interface construction is amazingly gratifying. I'll admit there was a little hump to get over in learning to think declaratively vs. procedurally, but once that clicked, I found I can't imagine living without the JavaFX script features of binding, function pointers, and sequences. So suspend your disbelief for just a moment and browse the tutorial: JavaFX Language Tutorial

Another critique I've heard is that the declarative, 'scripty' nature of JavaFX leads to unwieldy and hard-to-maintain code; to that I say that it is possible to write crappy code in any language and that JavaFX script does provide an object-oriented model well-suited to the discipline of nice class structure and well-factored code. Just keep using the skills that Java taught you. 

2. Download the SDK

Just do it. Go to javafx.com and click on the orange "Download Now" link. I recommend installing the NetBeans 6.5.1 bundle to get the JavaFX plugin automatically. 

3. Create NetBeans Project

You'll want to create a new JavaFX NetBeans project (File -> New Project...select "JavaFX" type). [Note: In the "Create Main File" checkbox, I usually change "Main" to a more descriptive classname for my app because I find it annoying to end up with a dozen "Main.fx" source tabs in NetBeans].

Once your project is there, you can just copy your existing Swing app source code into the "src" directory. For GUI Makeover, we placed the swing sources in their own subpackage for cleanliness. Another option would be to keep your Swing app source in its own Java project and create a jar file which your JavaFX project could use. In our case it was simpler to keep it all in a single project since we wanted to also make minor adjustments to the Swing code (tweaking colors, fonts, etc).

I'll take a moment to emphasize that the core of any serious application should remain in Java and I'm advocating that only the client veneer should be migrated to JavaFX (a good model for separation of concerns anyways). There are numerous reasons for this, but the glaring one is that JavaFX is single-threaded -- any code that needs to run off the main gui thread must execute in Java. 

4. Create a Stage

The root of all JavaFX interfaces is a Stage. When you created your JavaFX NetBeans project, it most likely generated a default Stage declaration in your main source file: 
Stage {
    title: "Application title"
    width: 250
    height: 80
    scene: Scene {
        content: [
            Text { ... }
        ]
    }
}
Customize that stage as desired. Stage has both astyle and an opacity variable to make it really easy to create non-rectangular UIs. If you choose a StageStyle of TRANSPARENT orUNDECORATED you will have to provide your own input handling for managing the toplevel window in the user's environment (on desktop). 

5. Define the Layout

You'll need to replace the content of the stage's Scene with your own UI. In our case with GUI Makeover, our stage was composed of a mixture of FX-node-based panels and Swing components. Since our designer tool isn't yet shipping, you'll have to do this by hand and here's where I'll do a bit of hand-waving, as layout in any sophisticated graphical toolkit is impossible to cover in a paragraph or two. For an overview of JavaFX scene graph layout, you can check out the JavaOne UI Controls session slides where I presented layout in detail (starting at slide 61 -- warning: it's a large slide deck!). My JavaFX layout blog will also be updated soon to reflect changes in 1.2.

Starting simply, you can look at the container classes in javafx.scene.layout (Flow, Tile, HBox, VBox, Stack, and Panel). It's amazing how far one can get by nesting HBox and VBox. If you want the more sophisticated power of a grid style layout, you can use either Stephen Chin's Grid container or MigLayout (ported by Dean Iverson) in the JFXtras library

6. Embed Swing Components

Any Swing component can be embedded in a JavaFX scene graph using the SwingComponent wrap() function. This conveniently allows you to directly leverage those Swing components which you've already configured, customized, and hooked to your application data; all that Java code can remain happily unmodified. Once you've created the structure of your scene's layout, you can pull your Swing components into the appropriate locations. Here's a simple example: 
def swingTable = new JTable();
Stage {
    scene: Scene {
         content: VBox {
             content: [
                 SwingComponent.wrap(swingTable),
                 // other FX nodes
            ]
        }
    }
}
The wrapped Swing component becomes a leaf node in the scene graph and as such can be manipulated like any other node -- rotated, scaled, skewed, made transluscent, etc. Through some hidden magic which transforms input events, the Swing component will just work, oblivious to its new 2D housing.

The limitation here is that the internals of the Swing component are not nodes in the scene and cannot be manipulated individually using JavaFX graphics ops. It's also worth noting that the entire Swing component can be made transulcent by setting theopacity variable on the wrapper, but this is not the same as having the Swing component's background be transluscent.

Another important restriction is that although FX script can freely invoke any of the Java apis on the Swing components, JavaFX script's language features (object literals, binding, functions) cannot be used directly with the component. This restriction can be mitigated by creating specialized FX classes which expose the Swing component apis as variables and functions. Many such classes are already provided in the javafx.ext.swing package (e.g. SwingButton) and the techniques for doing so are certainly worthy of another blog.

For our GUI Makeover client, we embedded a Swing JList and JTextArea, neither of which yet have pure FX counterparts. The JList already had a really nice custom cell renderer and we only had to modify the colors to better match the new visual design created by Jasper. 

7. Hook up Listeners

Although the magic of JavaFX's bind capability is not available on the embedded Swing components, it turns out to be easy to hook up appropriate listeners to those components within JavaFX script. In the GUI Makeover session, I showed how to create a JavaFX class which extends a Java Listener interface, however, after the session, Richard Bair showed me some syntactic sugar which makes this even easier: 
     // appcontroller is instance of Java class maintaining various app model state
     // No explicit FX listener extension class required!
     appcontroller.addPropertyChangeListener(PropertyChangeListener {
            override function propertyChange(event:PropertyChangeEvent):Void {
                if (event.getPropertyName().equals("selectedMailBox")) {
                    // handle property change...
                }
            }
      });

8. Add Effects

Now the fun begins. You no longer have to be a 2D mathematician to add effects like drop shadows, lighting affects, or box blurs into your interface. Chris Campbell has created the javafx.scene.effect and javafx.scene.effect.light packages in JavaFX that makes such visuals do-able often with just a single line of code. To mention just a subset....DropShadow, Glow, BoxBlur, Reflection, SpotLight. For GUI makeover, we put a reflection on our toolbar buttons:

<<img alt="toolbar3.png" src="http://weblogs.java.net/blog/aim/archive/javaone09/toolbar3.png" width="576" height="109" />

def reflection = Reflection { fraction: 0.33 }; // effects can be shared!
composeButtonLabel = VBox {
     spacing: 4
     content: bind [
         ImageView {
             image: Image { url: "{__DIR__}resources/compose.png"}
             effect: reflection
             cache: true // caching good idea for static effects! 
         },
         label = Text { ... }
     ]
}
You can achieve such effects in Swing (sans JavaFX) using clever Java2D imaging tricks, but the JavaFX language and scene graph capabilities make it vastly easier. What this means is that with very little effort you can experiment to explore what looks good rather than spending a bunch of time getting the math right to discover it wasn't what you wanted (I say this from personal experience :-). 

9. Add Movement (but don't over-do it)

Let's face it - the iPhone has raised the bar on interface design. The use of nice transitions to guide your users is now commonplace and expected. Obviously, as we proved with flying rockets in the Makeover session, animation can be taken too far (most engineers I know dream of being gamers) and I recently came across a blog post,flying pixels, which sheds some sensible light on the subject. However, adding subtle animation to give texture, guidance, and pleasure to your users is easy with JavaFX. The goods are in the javafx.animation and javafx.animation.transition packages. Timeline is the driver behind the general animation framework, however many common transitions are greatly simplified by using the canned transition classes, such as FadeTransition, RotateTransition, and PathTransition. In GUI makeover we added rollover effects to our toolbar buttons as follows: 
var label:Text; // label on toolbar button
var fadein:FadeTransition = FadeTransition { 
    node: bind label 
    fromValue: 0 toValue: 1.0 
    duration: 100ms // fade-in is fast, don't want UI to feel sluggish
};
var fadeout:FadeTransition = FadeTransition { 
    node: bind label 
    fromValue: 1.0 toValue: 0.0 
    duration: 500ms // fade-out is slower, like a trailing thought....
};
composeButtonLabel = VBox {
    content: bind [
        label = Text { ...
            opacity: 0.0 // label doesn't show until mouse-over
        }
    ]
    onMouseEntered:function(event:MouseEvent):Void {
        fadein.playFromStart();
    }
    onMouseExited:function(event:MouseEvent):Void {
        fadeout.playFromStart();
    }
}
And don't forget the rule of thumb that the best transitions are subliminal and arn't even noticed by your users. 

10. Draw Your own Conclusions

Skepticism is healthy -- especially in this internet age where we're all peddling something (technology, opinions, egos, tattered copies of The Mythical Man Month). But if you're a Swing developer looking to blend your Java code and skills with next-generation graphics, I'd encourage you to try JavaFX. You can even take your Java and Swing code with you.  
To follow up my article on JavaFX layout, I'll be presenting a section on layout at our JavaOne session on FX UI Controls. At this session, Richard, Jasper, and I will talk in detail about new apis which make it easier to build amazing interfaces: 
  • TS-5578 The New World: JavaFX
Don't believe anyone who tells you that you don't need layout management for rich internet applications. While it's true that this emerging class of interfaces are more graphical, fluid, organic, and animating than the traditional rectangular GUIs of the last 20 years, they, by their nature of being "applications" and not merely glitzy ads, must deal with dynamic content and user interaction.

Perhaps some of the bad rap on layout management is due to our own coddling of AWT "Layout Managers", a mostly programmatic approach for achieving layout in Java clients. Anyone who's ever had to design and construct a significant user interface knows that the more natural process is to draw the thing, or at least lay it out visually. But even with the most exceptional tools, the initial layout is just the beginning. The sticky wicket is how to then integrate the dynamic layout behavior -- what should happen if the container is resized or the content changes. Tools like Interface Builder, Dreamweaver, and NetBeans/Mattisse have strived to support this with varying success. The fact is that it's a really hard problem and the graphic sophistication of RIAs only increases the complexity. Dynamic layout now has to also work with timeline based animation and visual effects never anticipated in the aforementioned tool set.

The JavaFX api was designed with these needs in mind. Although the FX designer tool is not yet available to fully reveal the larger picture, developers dipping their toes into FX should understand the basic principles of scene graph layout, which are laid out (pun intended) in the following article. 

Ironic sidenote: you might be questioning how I could extoll the virtues of using tools for layout and then follow that with a detailed article on programmatic layout interfaces. The answer is that I'm a geek who likes to write for like-minded geeks. And fortunately the material translates to understanding 2D scene graph principles, whether programmatic or tool driven.


Note: The APIs covered in the article have been slightly modified in JavaFX1.2, so this article should be read with caution until I have a chance (very soon!) to replace it with the 1.2 version.

Layout Primer for JavaFX1.0

JavaFX1.0 provides a full-featured 2D scene graph API for creating dynamic and visually rich interfaces, however it does not yet include concrete layout container classes which automate the process of creating dynamic layout in a scene. A future version of JavaFX will more fully address automated layout, however this document will explain how to best achieve dynamic layout (including use with animated transitions) using the 1.0 API. 

This article assumes you have knowledge of basic 2D scene graph concepts and are somewhat familiar with the JavaFX scripting language. For more information on these prerequisites, see:



 
What Is Dynamic Layout?

Dynamic layout is the ability to dynamically position and size elements in the scene graph in response to internal and external changes to the scene which are often triggered by user interaction and involve unpredictable content. For example, the user resizes the stage (desktop profile) and expects the content to adjust to fill it, or on a mobile device the user loads a set of thumbnail images and expects them to flow horizontally and wrap at screen boundaries. Additionally critical is the ability to support animated transitions (zooming out, pulsing, sliding, etc) without disturbing the layout of other nodes in the scene.

In JavaFX1.0, there are hooks in the geometry APIs designed for this purpose. Dynamic layout can be achieved either by using binding on individual nodes to respond to layout changes in the scene or by creating Container classes which have the ability to algorithmically layout their children. This article will cover the related apis in detail.


Understanding Node Bounds Calculations

To layout a scene effectively, one must be able to query the rectangular bounds of nodes in the graph. The question of what is the current bounds of a node becomes complicated when considering the many variables which contribute to its bounds, such as local shape geometry (startX/startY, width, radius, etc), transformations (scale, rotate, etc), effects (shadows, glow, etc), and clipping. It turns out that each of these variables is applied to a node in a specific sequence, and that being able to query the bounds of the node at specific points in this transformation sequence is crucial to achieving desired layouts in the scene graph.

Below is a diagram which shows the transformation sequence for a javafx.scene.Node; transformations are applied left to right (effect first, translateX/Y last):

NodeChain.png

Rectangular bounds are represented by the javafx.geometry.Rectangle2D class which provides minX, minY, maxX, maxY, width, height variables. Since the bounding box can be anywhere in the 2D coordinate space, the X/Y values may often be negative, as we'll see in further examples.

To query the bounds of a node at particular points in the transformation sequence, the JavaFX api supports the following variables on the Node class:

boundsInLocal (read-only): The rectangular bounds in the node's untransformed coordinate space, including any space required for a non-zero stroke that may fall outside the shape's position/size geometry, the effect (if set) and theclip (if set).

layoutBounds: By default, layoutBounds is defined as boundsInLocal plus transforms set in the transforms[] chain variable. Unlike the other read-only bounds variables, layoutBounds may be set or bound to other values. Its purpose is to define the bounding box for layout code to use when performing layout calculations and it can be wired to something other than the default.

boundsInParent (read-only): The bounds of the node after ALL transforms have been applied to the boundInLocal, including those set by transforms[], scaleX/scaleY,rotate, translateX/translateY.

This is easier to visualize graphically, as depicted in the diagram below:

NodeChainBounds.png


Stepping Through a Node Bounds Example

Example 1: Let's look further at these bounds variables using a concrete code example which creates a rectangle.

rectangle1.png

Note that x and y are variables specific to javafx.scene.shape.Rectangle and that they position the rectangle within its own coordinate space rather than moving the entire coordinate space of the node. I throw this out as the first example because it is often the first thing that trips up developers coming from traditional toolkits such as Swing, where changing x,y effectively performs a translation of the component's coordinate space. All of the javafx.scene.shape classes have variables for specifying appropriate shape geometry within their local coordinate space (e.g. Rectangle has x, y, width, height, Circle has centerX, centerY, radius, etc) and such position variables should not be confused with a translation on the coordinate space, as we'll look at in our next example.


Example 2: To translate the rectangle along with its coordinate space (rather than move the rectangle within it), we instead set translateX/translateY, which are variables on Node.

rectangle2.png

Now boundsInParent has changed to reflect the translated rectangle, however boundsInLocal and layoutBounds remain unchanged because they are relative to the rectangle's coordinate space, which was what was shifted. 

inside design note: we debated endlessly on whether to rename translateX,translateY to "x","y" (as it's less typing and more familiar to traditional toolkit programming), however we decided that "translate" was more descriptive in the 2D sense and keeping it avoided renaming the x,y position variables in some shape classes.



Example 3: And now let's scale the same rectangle by 150% by placing a Scale object in the the transforms sequence variable.

rectangle3.png

Note that the default pivot point for javafx.scene.transform.Scale is 0,0, therefore the rectangle (and its coordinate space) grows down and to the right from its origin.


Example 4: And finally, if we instead scale the rectangle by using the scaleX/scaleY variables instead of a scale inside the transforms[] sequence, we'll see different results:

rectangle4.png

The most obvious difference is that the scaleX/scaleY variables on Node always scale about the node's center, which is different from the previous example which scaled from 0,0. Next, the layoutBounds does not account for the scale since it wasn't set in the transforms sequence, which means that code which lays out this rectangle will use its unscaled bounds (I'll explain why that's useful later). This distinction also applies to the rotation and translation transforms; if set in the transforms sequence, layoutBounds will account for them, however if set from Node's rotate, translateX/translateY variables, it will not.

Transformation Order Matters

Before moving on to layout, there is one more point to make about the order transformations are applied. This is easiest to explain if we rotate my Node chain diagram above vertically and show it in the context of a scene graph branch where a sequence of transforms has been set:

NodeTree.png

Transformations are applied bottom to top (again, effect first, translateX/translateY last) and by that I mean that a given node in the chain takes the one below it as input. However, "order" can be a misleading term. When it comes to understanding a series of transformations, it's often easier to visualize them by processing them top-down, which happens to be the order they are listed in the transforms sequence. For example, the transforms specified in the above diagram would be coded like this: 
    Rectangle {
        width: 50 height: 50
        fill: Color.GREEN
        transforms: [
            Translate { x: 50 }
            Scale { x: 1.5 y: 1.5 }
            Rotate { angle: 45 }
       ]

And the picture below shows how each transform effects the coordinate space and node:

transforms1.png

Now, if we invert the order of the transforms so the Scale is listed first: 
    Rectangle {
        width: 50 height: 50
        fill: Color.GREEN
        transforms: [
            Rotate { angle: 45 }
            Scale { x: 1.5 y: 1.5 }
            Translate { x: 50 }
       ]
We'll see that the result is very different:

transforms2.png

In fact, the reason the transforms[] sequence exists on the Node class is to give complete flexibility in both the transformations and the order in which they are applied.

This is all basic 2D graphics (recall your matrix math from college), but I illustrate it here because it can confuse those who haven't made a career in it.




Laying Out A Node

When laying out a node in a scene, it's often necessary to determine its current size so it can be aligned within a certain space (e.g. centered) and you must know it's current location so you can perform the appropriate translation to adjust it to where you want. This is why translateX and translateY are NOT included in the layoutBounds. layoutBounds is used to query the node's current position and size and THEN translateX and translateY are set to perform the adjustment for layout (if translateX and translateY were included in layoutBounds, you wouldn't be able to use them for positioning since it would cause a circular definition -- every time you updated translateX it would cause layoutBounds to change, which would recompute translateX!).

For example, if a rectangle is created: 
     var rect = Rectangle {
         x:10 y:10 width:100 height:100
     }
And later some code needs to layout that rectangle at an x,y location: 
    var x = 20;
    var y = 50;
    rect.translateX = x - rect.layoutBounds.minX;
    rect.translateY = y - rect.layoutBounds.minY;
Note that translateX and translateY are the deltasrequired to adjust the current position of the node (as defined by layoutBounds.minX, layoutBounds.minY) to get it to the desired location. translateX/translateY are NOT the final destination values. 

another insider design note: we realize this is cumbersome and are considering providing variables where the final position could be set directly without this extra math.


If node transformations need to be incorporated into layout calculations, then those should be set using the transforms[] variable. An example would be if you scaled a node larger and you wanted nodes laid out relative to it to adjust their positions to make room for the larger node.

On the flip side, if a transform is to be applied withoutaffecting its layout or those laid out relative to it, then it should be set using the scaleX, scaleY, and rotate variables, which will NOT be incorporated into layoutBounds. This is particularly useful for implementing animated transitions where you want to animate a node without affecting the layout of other nodes in the scene (e.g. a pulsating glow or zoom-fade). The transition classes in javafx.scene.transition are already built to work this way.

For example, the following code shows how you could add a pulse effect to a circle using javafx.animation.transition.ScaleTransition without disturbing the layout of the circle or nodes relative to it:

pulsecircle2.png



Using Binding to Layout Nodes

For cases where you need to configure dynamic layout for individual nodes, then you can use the powerful binding feature of JavaFX. For details on the language binding feature, see the current JavaFX Language specification.

For example, if you want to create a background color on a group of nodes, you would create a Rectangle node and use binding to ensure its bounds track the bounds of the group: 
var group = Group {
    content: [
         Rectangle {
             fill:Color.BLUE
             translateX: bind group.layoutBounds.minX
             translateY: bind group.layoutBounds.minY
             width: bind group.layoutBounds.width
             height: bind group.layoutBounds.height
         }
         other nodes...
    ]
 };

Another common case where binding is useful for layout is in centering a node within an area. Here is an example of how to create a circle that remains centered in the scene even it the stage is resized: 
Stage = Stage {
  var scene:Scene;
  var circle:Circle;

  width: 200 height: 200
  scene: scene = Scene {
      content: circle = Circle {
          fill:Color.RED radius:50
          translateX: bind (scene.width - circle.layoutBounds.width)/2 - circle.layoutBounds.minX
          translateY: bind (scene.height - circle.layoutBounds.height)/2 - circle.layoutBounds.minY
      };
  };
}


A Future for Containers

Where the binding approach becomes tedious is when you have a multitude of nodes that need to be placed relative to each other in some normalized scheme, such as a horizontal flow or a grid. In such cases it would be more convenient to use a Container class whose purpose is to layout its children using a particular algorithm.

The scene graph api includes a javafx.scene.Group class, and although it acts as a parent to its child nodes, it is not a "container" in the layout sense. The Group class merely allows nodes to be treated as a collection for scene graph operations (e.g. fade this group of nodes by 50% or shift this group of nodes to the right by 100, etc) . It is not designed to constrain its children to fit into a particular geometric area (it has no settable width/height variables) and it merely takes on the collective bounds of its child nodes.

For example, if a Circle (whose default centerX and centerY is at its origin) is placed inside a Group, the Group will take on the same bounds and be centered on its origin:

group.png

The 1.0 api provides two simple Group subclasses that will lay out nodes in either a row or a column, javafx.scene.layout.HBox and javafx.scene.layout.VBox.

The javafx.scene.layout.Container is the base class for creating nodes which are capable of both laying out children and constraining those children to fit within a particular area. The layout algorithm is performed in a layout function (defined by the concrete Container subclass) which is called by the scene graph automatically when it detects a change that requires layout. Currently the hooks for creating Containers include private api (public variables and functions prefixed with "impl") that will change in the future, therefore be aware that Containers written for 1.0 will need to be modified in future FX versions.

Ideally, for a container to perform an algorithm that positionsand resizes nodes, it must be able to query the minimum, preferred, and maximum dimensions of its child nodes, as well as be able to set width and height of the nodes. Node subclasses that wish to be resized by containers need to extend the javafx.scene.layout.Resizable class so that Containers can properly size them. Note that Node and the concrete shape classes do NOT currently extend Resizable, therefore when placed in containers, they will be positioned, but not resized (a deficiency we'll address in a future release).


Layout Beyond 1.0

Even the most graphical 3D games often need to present information to users in standard layouts and we've definitely found in our own FX experience that more support for layout management is crucial. And as a design tool emerges to enable visual-driven layout, the need for the scene graph to support the right apis is a top priority for our team (and me personally). In more concrete terms, we are planning on providing Container classes for common layout idoms -- flow and grid in particular, as well as defining a mechanism for animating changes in layout. And a primary goal of this effort will be to stablize the Container apis for external use. So watch this space and send feedback, ideas, and bugs my way.  
To me, one of the most enjoyable parts of JavaOne is hanging out in the audience in Hall E-135 (traditional home of the Desktop track) and chatting face-to-face with developers between presentations. Kirill Grouchnikov (of Substance look-and-feel fame) always asks us insightful and often difficult questions. This year he asked if he could interview me post-JavaOne to address many of the concerns which the conference raised for desktop developers. Read the interview if you're curious, and please post any follow-up questions.  

In the early days of Swing we spent many lunches arguing over the best way to do GUI layout. Tim Prinzing, the original architect of Swing's text package, believed that one sensical approach would be to leverage the power and popularity of HTML for GUI screen layout, and so from the very beginning he made sure it was possible to use the <object> tag to embed GUI components within HTML inside a JEditorPane. The page below embeds a JButton in the center of an HTML table:

  
    

I've used a JButton here for simplicity, but this would also work with any java.awt.Component subclass. And here is the Swing code that loads page into a JEditorPane:

    JEditorPane htmlPane = new JEditorPane();
    htmlPane.setContentType("text/html");
    htmlPane.setEditable(false); // very important, as the default is true (sorry about that!)
    try {
          htmlPane.setPage(Demo.class.getResource("resources/jbutton.html"));
    } catch (Exception e) {
          // handle load failure
    }

Now I'm not suggesting that we should abandon the use of tools and good layout managers in favor of HTML, but there may be an occasion where you'd find some utility in being able to embed components within HTML. In my case this is in writing some new demos for SwingSet3; I want to provide an HTML description (containing links, etc) of the demo and rather than placing that html adjacent to the running demo, it's a little more fun to just embed the demo within the descriptive html.

Displaying the components in the page is the easy part. The tricky bit is in how to access the handle to the component instance in order to make the GUI do something useful in your application. You'd think you could simply dig around in the editorpane's view hierarchy until you find a component of the proper class, however setPage() is asynchronous and it turns out it's hard to know precisely when you can count on finding the instantiated component of interest.

What you really want to do, if you're brave enough to tease open javax.swing.text, is to take control of the view factory to insert your own code at precisely the point when your component is instantiated. And just to rile up all those who think inheritence is the root of all evil, I'm going to neatly pack up all the code to do this in one simple JEditorPane subclass:

public class HTMLPanel extends JEditorPane {
    public HTMLPanel() {       
        setContentType("text/html");
        setEditorKit(new CompEditorKit()); // install our hook
        setEditable(false); // VERY IMPORTANT!
    }   
    protected class CompEditorKit extends HTMLEditorKit {
        @Override
        public ViewFactory getViewFactory() {
            return new CompFactory();
        }       
    }   
    protected class CompFactory extends HTMLEditorKit.HTMLFactory {
        public CompFactory() {
            super();
        }
        @Override
        public View create(Element element) {
            AttributeSet attrs = element.getAttributes();
        Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute);
        Object o = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute);
        if (o instanceof HTML.Tag) {       
                if ((HTML.Tag) o == HTML.Tag.OBJECT) {
                    return new CompView(element); 
                }
            }
            return super.create(element);
        }
    }   
    protected class CompView extends ObjectView {
        public CompView(Element element) {
            super(element);
        }
        @Override
        protected Component createComponent() {
            Component component = super.createComponent();  // COMPONENT IS CREATED HERE
                                 
            // DO SOMETHING USEFUL WITH COMPONENT INSTANCE HERE 
        return component;   
        }
    }
}

Note that we're using the "label" attribute in the <object> tag to set the label property on the button. By default, the text package's ObjectView factory (that we extended above) will use the JavaBean introspector to match up any html tag attributes with writable bean properties of type String. However, if a property name happens to also be defined as an attribute type in javax.swing.text.html.HTML.Attribute, then this mechanism will fail due to a bug in ObjectView. Unfortunately this happens to be the case for both the "text" and "name" properties (two I'd like to set for a JButton, drats).

So if you are putting more than one component in the page and want to use an attribute value to identify them, you cannot rely on "name" being set for you. You'll have to specifically check for some other non-conflicting attribute.

Now for the caveats. We've been appropriately skewered by developers for our less than stellar HTML support. We are steadily improving it, however JEditorPane may struggle with more complex layouts. Also, I don't even gloss over how CSS might play in all this, however if you can imagine skinning Swing components with CSS, I recommend checking out Josh Marinacci's 2003 article on Swing and CSS.

I do think Tim's idea of leveraging HTML for GUI layout still has merit, however the irony is that using HTML doesn't really make it any easier to get good ones. I've spent untold hours struggling in Dreamweaver to get my nested tables behaving just right; frankly I'd rather use GridBag and a good beer. But arguing over the holy grail keeps us all engaged.

A little more than 50 years ago, a team of brilliant engineers and mathematicians set out in the confines of an old battery factory in Philadelphia to build the world's first supercomputer, the UNIVAC LARC. My mother (Mary Cush, at the time) was on that team. So this year, as I sat in my seat at JavaOne, glancing at the sea of predominantly male geeks around me, I thought about how I came to be a software engineer and I realized that I pretty much owe that to her. After my mother graduated college with a Math degree in 1955, she considered her options; become a teacher or get married. With no desirable marriage prospects at that time (we'll get to that later), she did what any smart, resourceful individual would do. She sent out a bunch of letters to corporations and was ultimately hired by Remington-Rand (formerly Eckert-Mauchly) to be a Logical Designer. There she worked on a small team writing machine code to drive the LARC computer, which is pretty incredible if you realize that topping the list of important single career women characters of the 50's is Kitty of Gunsmoke. A few years later a team of Livermore Lawrence Laboratory engineers came out to Remington-Rand (then renamed Sperry Corp) to learn about maintaining the LARC and my Mom met James Moore (my Dad), they got married, moved to California, and started a family. She then did what every respectable woman of 1960 did; she left her career behind to dedicate her life to raising her four daughters. So I grew up with stories of rooms filled with transistors with gold-plated connections, counting by 1's and 0's, and a general love for math and logic. But most importantly I grew up in a household where the smartest person in the room was always my mother (my Dad is brilliant too, but that's a different story) and it never, ever occurred to me that there was anything boys could do that girls couldn't, except perhaps tackle football, which really only highlights an exceptional series of synapses in the woman's brain. Thanks to her, I emerged from the carnage of teenage-girlhood with an unshakable pride in being smart. Thanks to her, I've steered my way through a software career dominated by men without losing my sense of being a girl. Thanks to her, I've kept my pinky hooked into a job I love while raising my own children (yes, Sun has helped there too). So Mom, this year for Mother's Day I'm skipping cards and flowers and giving you this blog instead. You've made such a difference in my life. You're still the smartest person in the room. For fun I'm including an excerpt from the Univac LARC Programming Manual, which shows just how far (and not far) we've come in 50 years: "The Larc computer is an extremely high-speed computing device. Its high speed is obtained in part by using overlapping instructions, that is, the computer does not wait until and instruction has been executed before extracting the next from storage. Consequently, instructions follow each other rather closely through the stages of the control unit. In fact, as many as four instructions may be in the control unit at any one time. This overlapping of instructions of course increases the complexity of the computer and imposes certain sequencing restrictions on the programmer. In some cases by careless use of instructions a programmer may increase the running time of his program. In rare instances errors can be caused by failing to observe the restrictions. "  

For some time I've been peddling this theory that one of the greatest barriers to Swing adoption is our monolithic API documentation (javadoc). I'm a personal fan of javadoc and rely on it for solving a large percentage of my programming questions, but you could say I know where to look, and that, at least in the Swing case, over the years my brain has learned to block out that 80% which isn't actually needed to construct a GUI. Picking on JButton - I only really need 3 or 4 of the 300+ methods and the ones I need arn't even listed in the Method Summary table because they are actually defined in its superclass, AbstractButton. But I know that, so it's not a problem. But what about the poor soul embarking on his or her first Swing client? And of course this problem isn't limited to Swing, but afflicts many of the larger APIs in J2SE (JDBC, XML, etc). Sometimes bigness creeps up on you.

Javadoc's structure really hasn't changed at all in the last 10 years because it serves quite well as the Java platform's APIreference. The questions we are trying to answer for JSR260 are should it evolve? and how? So I entreat you to take this short(!) survey and tell us what you think:

http://java.sun.com/webapps/survey/display?survey_id=5382

Your answers will make a difference.

aim

The JDNC project debuts Blog

Posted by aim Jun 23, 2004

At JavaOne '97, we kicked off project Swing and as we raised the toolkit scaffolding in the crazy months that followed, we released frequent snapshots of the bits to get our developer base (or at least a hardy core) in on the action. Feedback, both positive and 'constructive', was relentless, and the API improved steadily because of it. We still get email from folks who tell us they've been using Swing since those 0.2 days, and we always bow our heads in honor.

So we're looking to get you in early on the action again. This time with a new technology called JDesktop Network Components (JDNC), which takes on the goal of simplifying development of data-centric Java desktop clients by providing shortcuts in the form of high level Swing components and an optional XML configuration language. Taking advantage of the open source boon, we're launching this as a javadesktop.org project, from which we can involve you not only in trying the stuff out, but in shaping and coding it directly. The JDNC project is a place where we can explore solutions and tune them long before they get baked into a standard. It is still in the rough early stage, so your impact could be significant.

So read up about JDNC in my latest article, visit our project site, and join in on making it happen. After all, who knows better than you what would make your development life easier.

And for those of you heading to San Francisco next week, we'll be at JavaOne talking more about the project. Come chat directly with us:

  • TS-2865: JDesktop Network Components (JDNC): Simplifying JavaTM Desktop Client Construction
    Tuesday 1:30 PM Hall E #135
  • Hands-on Lab-7222: Rapid Data-Driven Client Construction with JDesktop Network Components (JDNC) XML Configuration Language
     
    • Monday 2:15 PM Hall E 132/133
    • Tuesday 12:15 PM Hall E 132/133
    • Wednesday 12:15 PM Hall E 132/133
    • Thursday 1:00 PM Hall E 132/133
  • BOF-2859: Meet the JavaTM Foundation Classes (JFC/Swing) Team
    Wednesday 7:30 PM Esplanade 302
  • JDNC Demos in J2SE Client Pavilion Booth

Filter Blog

By date: