Rethinking the stage

No doubt about it, JavaFX is a powerful technology – up there with the likes of Flash, Flex and Silverlight.  But that’s not to say that it doesn’t have its limitations.  A few of these limitations are starting to hit Clash.  My main gripes are the following:

  • No global key codes
  • The inability to visually disable a scene

Another issue, although potentially more subtle, is with Scenes. While I technically don’t have issue with them, I just have a hard time understanding the need for the ‘Scene‘ object.  As far as I can tell…its like it wants to be a Group – but with less function…

I took some time today and thought out these issues and how to overcome them.  The biggest one is, of course, the lack of global key codes.  Writing a real time simulation game will require key codes. Afterall, hard core RTS fans use the ‘one hand keyboard, one hand mouse’ strategy – so they’re basically a necessity.  JavaFX has great support for ‘node based’ keyboard input…but it doesn’t really allow a ‘global’ (or stage based) key code.  For the game, I need it so that when you press a key, no matter what object has focus, a given action will occur.

I also need the ability to pause the game…which should bring some visual layer up informing the user that they can no longer interact with the scene.  Right now, all nodes have a ‘disable’ flag – and groups can disable all of their children…so JavaFX is close.  But with Clash, we’re making a lot of custom nodes…and I didn’t program each one to change visually when the disable bit was flagged (cause I’m lazy sometimes).

So…I took all these issues into consideration, did some refactoring, some research, and some coding. I eventually came up with a new Stage object called ‘StagePlus’ (Yes, the name can probably use some work).  Let me explain what this StagePlus object can do.

Firstly, it merges in the function of a Stage and a Scene together as one entity.  I don’t know if other users have found a need to separate the two, but whenever I have a Scene I have exactly one Stage.  Therefore, I merged the two together. This means that instead of a Stage having a Scene, a StagePlus has ‘content’ (which is a Node Sequence).

Secondly, I added a visual ‘disable’.  Therefore, when the Stage is disabled, it draws a slightly translucent, gray box around all of the objects and turns their disable bits to true.  This provides the visual representation of disable that I was going for (and also saves me a lot of hassle integrating a disable change into each node).

Lastly, but most importantly…I’ve added Stage-wide key codes.  This allows the user to bind a specific key to a specific action, regardless of which node has focus.  For instance, take the following:

WindowKey {
    action: enable,
    key: KeyCode.VK_F2
}

This binds the ‘enable’ function to the stage for when the F2 key is pressed. We can also use modifiers (shift, alt, ctrl, and meta) by doing something like the following:

WindowKey {
    action: disable,
    key: KeyCode.VK_F2,
    modifiers: WindowKey.SHIFT
}

This binds the function ‘disable’ to the ’shift+F2′ key.

The Stage-wide key codes is by far the most important feature…but it has one significant drawback – it requires the Desktop profile. It uses some Swing things in the background to make it work…but since Clash is a desktop game, I’m happy with it.

Take a look at the result:

That example was made with the following code (I cut out the content for the sake of brevity):

var stage : StagePlus = null;

var disable : function() : Void = function() : Void {
    stage.enabled = false;
}

var enable : function() : Void = function() : Void {
    stage.enabled = true;
}

stage = StagePlus {
    title: "Application title"
    width: 400
    height: 400
    content: [...]
    windowKeys: [
        WindowKey { action: enable, key: KeyCode.VK_F2 },
        WindowKey { action: disable, key: KeyCode.VK_F2, modifiers: WindowKey.SHIFT }
    ]
}

There is still a bit of work to be done before I bundle this into Clash. For instance, right now the visual disable is tied to the Stage. For Clash, I need to move that into the Group. Should be easy…I’ll just make a ‘GroupPlus’ ;-)

Also, I’m not a fan of how JavaFX handles cursors. The 1.2 release (and possibly earlier releases?) allows you to change the cursor on a per-node basis, but the choices you have for cursors is limited, and they all resolve to the operating systems native cursors. This is fine for business type applications typically. However, I haven’t seen many games where you use the standard cursor… If I update this, I’m going to change it so that the cursor can be set to a Node. This would allow the user to have custom cursors instead of the built in ones.

I’ll eventually be integrating this into the Clash source tree…so if anyone wants to use it, keep an eye out in there. I’ll also post again here once I add it. If anyone has any other suggestions for UI features, let me know.

 

2 Responses to “Rethinking the stage”

  1. Ander Ruiz says:

    I’m rewriting an application for Tollroads. We are changing it from an obsolete Linux technology called VGraph to JavaFX. One of our first problem was to analize the viability of the new solution, because it was, and it needs to be a keyboard only controlled application.

    So I suffered the same problem that you have with Clash, I needed global keyCodes. My solution has been to extend Scene and to override imp_processKeyEvent function. We haven’t finished the migration yet, but at least we are receiving all key events.

    Are there any problems with this implementation apart from that I’m using an internal API that may change?. Are there any differences between you key capture an mine?

    Thanks and great job!

  2. Drew says:

    Technically, any of the methods that start with ‘imp_’ are not exposed (you probably found this with a ‘ctrl+space’ action or by decompiling that API). They are implementation methods and since they are not exposed via the API, then the JavaFX team can do whatever they want with that method. So if they want to move that method from the Scene to the Stage, they can do so without any concern because users are not supposed to be using that. Quite honestly, I doubt they do that though.

    I looked through the JIRA and I didn’t see anything in there that included global key support…but you never know – they may include that and just not put that info in the next release. The way I implemented the global key codes, uses a Swing basis, so they technically can’t change or remove the methods I’m using because they’re all ‘legal’ APIs – but it limits me to the Desktop profile.

Leave a Reply