That Infernal Scenegraph Warning Message: Part Deux

Stuart Marks has written up part II of the three-part series on the scenegraph warning message that you have seen in JavaFX1.2. I covered and linked to the first version previously. It really is a terrific read, and details some of the subtle semantics of bind. Stuart always does such an excellent job describing these subtle issues.

In my earlier post on this topic I hinted that we had found a resolution to the issue surrounding the warning message, I hinted further in some of my replies to comments, and I even left it as sort of a cliffhanger as to what the resolution was. So, here’s the resolution.

We’ve decided that when a node is added to a group, that node is automatically removed from the group that previously owned it, if any. (Let’s call this the “auto-remove” feature.) We’ve also decided to turn off the warning message by default, but to have it be enabled optionally, possibly via a system property, for debugging purposes. Finally, we’ve relaxed the enforcement of some scene graph invariants in cases where the group’s content sequence is bound.

During the development of our current release, we kept running into this issue. A couple of us wrote code that we thought was reasonable, yet it surprised us when the warning message came out! We had a few hallway conversations from time to time, but a clear-cut answer never emerged. Finally, we realized that we had to get the interested parties in a room and have a knock-down, drag-out meeting to resolve the issue. And so on October 7, 2009, Amy Fowler, Kevin Rushforth, Richard Bair, and I got into a conference room to decide the issue. Three hours later — with no breaks! — we had decided. Actually, it was a great meeting, without a lot of conflict. There were just a lot of issues to cover. Each of us came into the meeting with our initial opinions, but the issues were so close that I think each one of us switched sides at least once during the meeting.

Node Lookup

The Node class has a lookup function which is used for finding a node in the scenegraph based on an id. Every node has a String id, similar to HTML. In theory it should be unique within the scenegraph though in practice this isn’t necessarily the case.

This lookup function currently only operates over the “public” or “logical” scenegraph, not the physical one. For example, a Button is a Control which delegates its visuals to its Skin. The Skin has a sub-scenegraph. But since we typically want to encapsulate all that, we hide it from the programmer such that most things don’t find this part of the scenegraph. Seems like a reasonable thing to do (Swing went the other way and all the gory details were public which led to issues sometimes where people would dig around and rely on internal details — the horror! — and this made it incredibly difficult to fix bugs later in Swing).

CustomNode was our level of encapsulation. Any CustomNode’s children was explicitly not part of the public API. However, a number of people have complained, likely because they aren’t using CustomNode for encapsulation but rather for modularization of their source code (ie: break a huge file into several files where each piece extended from CustomNode).

So it seems like this partitioning of “logical” and “physical” scenegraphs is quite confusing at times. So the question is, should the lookup function operate on the physical scenegraph? Vote!

[poll id=”2″]

That Infernal Scene Graph Warning Message

Stuart Marks (one of the UI Controls “heavies”) has a great write-up on that “Infernal Scene Graph Warning Message”. You know, the one that showed up in JavaFX 1.2 in seemingly innocuous situations such as the following:

[jfx]
var g = bind Group {
translateX: someX
content: node
}
[/jfx]

In this code, whenever “someX” changes, a new Group is created and the node is mysteriously moved from this Group to the new one, and in the process the “infernal scenegraph warning message” is printed. Stuart goes into a lot of great history and detail to give context to the problem, but here is probably the money-quote as to why the warning message was useful:

When we added the enforcement code, we were surprised that it uncovered a few bugs and quite a number of questionable coding practices in our code, both in our runtime library and in our samples. In one case a node from the scene graph was also being used as a clip. This was illegal and didn’t actually work, but nobody had noticed up to that point. As for questionable coding practices, the enforcement turned up quite a number of cases where a scene graph was being constructed with some initial structure, and some code later on would rearrange the nodes into a different structure for no good reason. This caused the scene graph machinery to do a lot of extra work. The fix was to rewrite the code to create the scene graph in the desired structure, avoiding the rearranging and any error messages that the old code had caused.

Go visit his blog to get more information, I’m waiting for the second installment which I anticipate will go into all the nitty gritty details. Its kind of a cliff hanger because he doesn’t tell you what the final unanimous decision was regarding the warning message. Codify it as an error? Remove it? Something else?

UI Virtualization

When you have a lot of data to display in a Control such as a ListView, you need some way of virtualizing the Nodes created and used. For example, if you have 10 million data items, you don’t want to create 10 million Nodes. So you create enough Nodes to fill the display dynamically. Because of our heritage in Swing, we know how critical this is for real apps. I got an optimization issue reported this morning on “UI Virtualization”. (more…)

Controls: autoSize

One bug which gets filed over and over again is related to the idea of whether Controls should resize themselves when their content / settings change. The earliest such bug was with TextBox. TextBox has a columns property which, like in Swing, can be used to hint how many “columns” of text should be displayable in the Control by default. The TextBox skin implementation uses this information in conjunction with the font and other settings to determine the preferred width of the TextBox.

When a Control is created, if you do not explicitly specify its width or height, then it is sized according to its preferred width and/or preferred height. If you use a Control within a layout Container (which was anticipated as being the common case) then you (almost) never explicitly manage its dimensions – you leave that chore to the Container. However, if you do not use a Control within a layout Container (which turns out seems to happen quite a bit) then it does not explicitly resize itself whenever, say, columns changes.

Take Button as another example. If you create a Button with the text “OK” and do not specify a width or height, then the Button will be initialized to its preferred size. However, if you then change the text of the Button to be “Cancel”, then the Button will not automatically resize, and will therefore not be wide enough to display all the text and you’ll see something like “C…”, if you’re lucky. Or “…” if you’re not.

I’ve filed RFE http://javafx-jira.kenai.com/browse/RT-5122 targeted at the next release. The idea here is to have an “autoSize” variable on Control which will be true by default. Problem solved, right? Well, not quite. The problem with autoSize is that when placed in a Container, the Control should not attempt to resize itself. So we’ll end up with code essentially like this:

if (autoSize and not (parent instanceof Container)) {
    // resize the width because the text has changed, or whatnot
}

Not very pretty. But given the options and the amount of frustration it is causing developers, I’m willing to accept that bit of nastiness. What do you think? Have any good ideas? Leave comments & votes on the bug if you like!

Background Tasks in JavaFX

While on vacation I’ve been watching twitter to see what kinds of things people are doing with JavaFX. One thing I’ve seen a fair amount of tweets on that is pretty gratifying is tying JavaFX to backend data services. One project is a port of Cairngorm from Flex to Java/JavaFX. Another was an earlier demo based on Hessian (which needs to be ported to JavaFX 1.2). Another was about integrating Flamingo to JavaFX.

(more…)