Monday, June 22, 2009

MigLayout for JavaFX Reloaded

JavaFX 1.2 brought many changes to the layout system in the JavaFX runtime. MigLayout for JavaFX has been fully updated to take advantage of these changes. The updated version is part of JFXtras v0.5 that was released a short time ago.

Getting Started with MigLayout

The first thing you need to do is download JFXtras version 0.5 and add the JFXtras-0.5.jar file to your project. You will also need to obtain the MigLayout v3.7 (for Java) jar file. This jar file is included in the JFXtras source distribution (in the lib directory) or you can get it directly from the MigLayout site. One other thing you should always keep handy is the MigLayout cheat sheet.

Simple Test

There are several examples of MigLayout usage included in the source distribution of JFXtras. You will find them in the tests/org/jfxtras/scene/layout directory. Let's start by taking a look at one of them: MigSimpleTest.fx. The code for this example is listed below.
package org.jfxtras.scene.layout;

import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import org.jfxtras.scene.ResizableScene;

Stage {
    title: "Mig Centering Test"
    scene: ResizableScene {
        width: 200
        height: 200
        fill: Color.PINK

        content: MigLayout {
            constraints: "fill"
            width: 100
            height: 100
            layoutInfo: MigNodeLayoutInfo {
                constraints: "center"
            }
        }
    }
}

This example shows the use of the layoutInfo variable that was added to the Node base class in JavaFX v1.2. The LayoutInfo class was created to provide a generic way of specifying layout constraints for any node in the scene graph. A subclass of LayoutInfo called MigNodeLayoutInfo is used to hold the specialized constraints string for a node in a MigLayout container. In the example above, the only node constraint specified is center which will keep the rectangle centered in the container.

Note: The node constraints string in the JavaFX version of MigLayout has the same syntax as the component constraints in the Java version. In JavaFX, we deal in nodes instead of components. See the MigLayout cheat sheet for details.
In this particular case, the rectangle also stays centered in the scene thanks to the use of a JFXtras ResizableScene which automatically passes resize information on to the MigLayout container.

LayoutInfo Convenience Functions

Setting the layoutInfo variable with a new MigNodeLayoutInfo class every time can get tedious. There are two convenience functions in the MigLayout class that reduce the amount of typing required. The first is the nodeConstraints function. It has the following signature:
public function nodeConstraints( constraints:String ):MigNodeLayoutInfo

This function simply constructs and returns a new MigNodeLayoutInfo object with the given constraints string. This is a module-level function in the MigLayout.fx file and therefore needs to be imported like this:

import org.jfxtras.scene.layout.MigLayout.*;
Using this new function, you might rewrite the Rectangle literal above as:
Rectangle {
    width: 100
    height: 100
    layoutInfo: nodeConstraints( "center" )
}
The second convenience function is named migNode and is used to associate a constraints string with a scene graph node that was created at some other point in the program. The function's signature is:
public function migNode( node:Node, constraints:String ):Node
This function is also a module-level function located in the MigLayout.fx file and therefore can be imported into your program using the same import statement as the one given above for the nodeConstraints function. Rewriting our simple test code to use this function might look something like this:
package org.jfxtras.scene.layout;

import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import org.jfxtras.scene.ResizableScene;
import org.jfxtras.scene.layout.MigLayout.*;

var blackRect = Rectangle {
    width: 100
    height: 100
}

Stage {
    title: "Mig Centering Test"
    scene: ResizableScene {
        width: 200
        height: 200
        fill: Color.PINK

        content: MigLayout {
            constraints: "fill"
            content: [
                migNode( blackRect, "center" )
               // ... other nodes are added here ...
            ]
        }
    }
}
This form is recommended when you have a non-trivial scene graph declaration. It allows you to easily see what nodes are being managed by your container and what their constraints are.

Migrating from a Previous Version

Those who have used MigLayout in JavaFX 1.0 or 1.1 will notice a few changes:

  • The layout constraints are now set using the constraints variable instead of the layout variable. The old name conflicts with the new layout facilities in JavaFX 1.2.
  • The fitParent variable has been removed from the MigLayout class. If you want your MigLayout container to expand to fill the entire scene, just use JFXtras' ResizeableScene class as above.
  • The MigNode class has been removed since it is no longer needed. The new layoutInfo variable in the Node class gives us a way to associate constraints with a node.
  • Since the MigNode class (and therefore MigNode.fx) no longer exists, the migNode convenience function was moved to the MigLayout.fx file.

Wrap Up

The MigLayout container released in JFXtras 0.5 was much improved for JavaFX 1.2. It not only incorporates the new layout system, but several bugs were fixed and it is now compatible with the latest release of MigLayout. There are more details about this updated version of MigLayout for JavaFX in the just-released Pro JavaFX™ Platform book of which I was fortunate enough to be a co-author.

Lastly, Amy Fowler has also updated her wonderful article on JavaFX layout. I highly recommend it!

7 comments:

  1. Thanks for your port Dean.

    Btw, I have now read your book. Really Good Stuff!

    Cheers,
    Mikael Grev

    ReplyDelete
  2. Thanks, Mikael!

    (for an awesome layout manager to port and for the nice comments about the book)

    ReplyDelete
  3. I hope that MigLayout proves to be as easy to use and elegant as TableLayout was for Swing.

    For your example I got the jars out as specified, but got a failure on the call to the migNode() function:

    java.lang.IllegalArgumentException: Illegal Constraint: 'centered'
    Unknown keyword.
    at net.miginfocom.layout.ConstraintParser.parseComponentConstraint(Unknown Source)
    at org.jfxtras.scene.layout.MigNodeLayoutInfo$_SBECL.onChange(MigNodeLayoutInfo.fx:50)
    at com.sun.javafx.runtime.location.ObjectVariable.notifyListeners(ObjectVariable.java:142)
    at com.sun.javafx.runtime.location.ObjectVariable.replaceValue(ObjectVariable.java:104)
    at com.sun.javafx.runtime.location.ObjectVariable.set(ObjectVariable.java:115)
    at org.jfxtras.scene.layout.MigNodeLayoutInfo.set$constraints(MigNodeLayoutInfo.fx:48)
    at org.jfxtras.scene.layout.MigLayout.nodeConstraints(MigLayout.fx:254)
    at org.jfxtras.scene.layout.MigLayout.migNode(MigLayout.fx:264)
    at com.seasoft.utils.MigLayoutTest.javafx$run$(MigLayoutTest.fx:32)
    ...

    ReplyDelete
  4. I have found that using the word "center" will stop the above stack trace. However it has no effect, or I am doing something wrong. In the below I would expect to see a black rectangle in the middle of rect. I do see the rectangle, but it positions in the top left hand corner of rect.

    public class PropertiesPanelExample extends CustomNode
    {
    public var rect: Rect;

    protected override function create(): Node
    {
    var container = Panel
    {
    translateX: bind rect.x;
    translateY: bind rect.y;
    width: bind rect.width;
    height: bind rect.height;
    content: MigLayout
    {
    constraints: "fill"
    content: Rectangle {
    width: 100
    height: 100
    }
    layoutInfo: nodeConstraints( "center")
    }
    };
    return container;
    }
    }

    (By the way substituting the word "centered" into the below does not crash as with the above).

    ReplyDelete
  5. Chris,

    Thanks for pointing out the "centered" typo. It's fixed now.

    I'm not sure I completely understand what you're trying to accomplish, but if you want the rectangle to be centered, then you need to move the nodeConstraints to the Rectangle. Right now you're setting them on the MigLayout itself, which I don't think is what you want.

    Also, you will need to set the width and height of the MigLayout container to match the Panel's in order for the Rectangle to be centered within the Panel.

    ReplyDelete
  6. Dean,

    Are these samples tested with JFXtras 0.6 ?. They are failing for me.

    Has org.jfxtras.scene.ResizableScene been deleted and/or moved in ver 0.6 ?.

    /rk

    ReplyDelete
  7. rk,

    In v0.6, JFXtras adapted a new naming convention for it's classes in order to distinguish them from current and future core JavaFX classes. So MigLayout became XMigLayout and ResizableScene is now XScene.

    Dean

    ReplyDelete

Please Note: All comments are moderated. That's why you won't see your comment appear right away. If it's not some stupid piece of spam, it will appear soon.

Note: Only a member of this blog may post a comment.