Friday, November 20, 2009

GUI development and Java FX

I hate GUI development. I'm sure lots of developers feel exactly the same way. Even if you are gifted with a sense of aesthetics that allows you to appreciate how Finder or Windows Explorer are superior to a command-line, you might still hate GUI development.

One of the reasons for this could be the difficulty of getting from a paper UI mockup to something that actually works. I've often found programs with UI design that I appreciate, but the work required to duplicate the approach is often insurmountable.

Consider the tools developers generally have at their disposal. Most, if not all of them, are very good at building applications that conform to an aesthetic that (as far as I can remember) comes from Windows 95. I'm talking about the single, resizeable window, menus, dialog boxes maybe a tool bar or 2 or 3 and a big blank space in the middle where your work goes. The best tool I've used for this kind of UI design is Qt Designer. But this doesn't help you if you want animation, or zooming or free-form layout. As soon as you go out of the standard UI box you need to build almost everything yourself. And that sucks.

Java FX seems to be one of a few tools gaining popularity these days that help us break out of the old paradigm. Consider the following code snippet. It defines an object that can be used as an arbitrarily large surface and defines a viewport onto it so that the user can drag their view around instead of dragging the objects on the view around.
public class Viewport extends CustomNode {
    public var viewWidth :Float;
    public var viewHeight :Float;
    public var content :Node[];

    var currentX :Float = 0;
    var currentY :Float = 0;
    var dragStartX :Float = 0;
    var dragStartY :Float = 0;

    def viewport = Rectangle {
        x: bind -currentX
        y: bind -currentY
        width: bind viewWidth;
        height: bind viewHeight;
        fill: Color.TRANSPARENT;
    };

    override function create() :Node {
        insert viewport before content[0];

        return Group {
            content: bind content;
            translateX: bind currentX;
            translateY: bind currentY;
        }
    }

    override var onMousePressed =
        function(event :MouseEvent) :Void {

            dragStartX = event.sceneX + currentX;
            dragStartY = event.sceneY + currentY;
        };

    override var onMouseDragged =
        function(event :MouseEvent) :Void {

            currentX = dragStartX - event.sceneX;
            currentY = dragStartY - event.sceneY;
        };
}

I doubt that can be done as easily, quickly and readably in Qt or Apple's Interface Builder. That viewport class took about 1 hour to code. Now lets say you want to drag the widgets around on the viewport, no problem, just put them in a DraggableGroup like this one.
public class DraggableGroup extends CustomNode {
    public var content :Node[];

    override def blocksMouse = true;
    var currentX :Float = 0;
    var currentY :Float = 0;
    var dragStartX :Float = 0;
    var dragStartY :Float = 0;

    override function create() :Node {
        return Group {
            content: bind content;
            translateX: bind currentX;
            translateY: bind currentY;
        }
    }

    override var onMousePressed =
        function(event :MouseEvent) :Void {

            dragStartX = event.sceneX - currentX;
            dragStartY = event.sceneY - currentY;
    };

    override var onMouseDragged =
        function(event :MouseEvent) :Void {

            currentX = event.sceneX - dragStartX;
            currentY = event.sceneY - dragStartY;
    };
}

Although this isn't particularly exciting, it does show that with some basic built-ins you can create a GUI that would be quite hard to put together in one of the stalwart toolkits developers normally use, although you're in trouble if you want multi-line text editing!

1 comment: