I didn’t mean to put down your efforts and I hope it wasn’t taken that way. It’s just that Lemur tries to fill the exact gap you were trying to fill. In fact, it has some built in utility you could have used even if you wanted to roll your own GUI (like the built in pick handling, etc.)
I’ll try to rework your example as a Lemur example. I’m doing it “by eye” so forgive any minor syntax errors…
[java]
public void simpleInitApp() {
GuiGlobals.initialize(this);
// I'm leaving this in because you had them but did you know you can
// just leave them out of the app altogether?
setDisplayFps(false);
setDisplayStatView(false);
flyCam.setEnabled(false);
getViewPort().setBackgroundColor(new ColorRGBA(100 / 255f, 149 / 255f, 237 / 255f, 1f));
TextField textElement = new TextField("You can edit this element.");
textElement.setSize(210, 43.75f);
textElement.setTextHAlignment(HAlignment.CENTER);
textElement.center();
// Here is where things get a little 'weird' since Lemur doesn't (yet) have a way to
// get document change events because in general its more efficient to poll. I'll show
// that example separately so as not to muddy the rest of the source. I will be adding
// document change listeners soon.
Label labelElement = new Label("This example shows a label.");
labelElement.setSize(500, 100);
labelElement.center(); // not sure what center(0, 100) does
Button redElement = new Button("Red");
redElement.setSize(500, 100);
redElement.center(); // not sure what center(-110, -50) does
redElement.addClickCommands(new Command<Button>() {
public void execute(Button source) {
getViewPort().setBackgroundColor(ColorRGBA.Red);
}
});
Button blueElement = new Button("Red");
blueElement.setSize(500, 100);
blueElement.center(); // not sure what center(-110, -50) does
blueElement.addClickCommands(new Command<Button>() {
public void execute(Button source) {
getViewPort().setBackgroundColor(ColorRGBA.Blue);
}
});
// Lemur doesn't have a way to disable components yet but I'm adding that
// for one of my mythruna UI right now so it will be available soon. Still, I'll
// show the toggle boilerplate
Checkbox toggleElement = new Checkbox("Toggle Buttons");
toggleElement.setSize(200, 100);
toggleElement.center();
toggleElement.setChecked(true);
toggleElement.addClickCommands(new Command<Button>() {
public void execute(Button source) {
// Toggle things based on check state or whatever
// Note: the value of a checkbox can be watched similar to the
// way a document model can by watched and can often eliminate
// all listener boilerplate depending on what is being done with the value.
// I'll show that separately.
// I kept this logic the same though it's backwards for the checkbox state, I think.
if( ((Checkbox)source).isChecked() ) {
source.setText("Disable buttons");
} else {
source.setText("Enable buttons");
}
}
});
guiNode.attachChild(textElement);
guiNode.attachChild(labelElement);
guiNode.attachChild(redElement);
guiNode.attachChild(blueElement);
guiNode.attachChild(toggleElement);
}
[/java]
So the code is not so different. Before I get into the document model and toggle watching I mentioned…
Here are some things you gain.
Adding the elements to a layout so that you don’t have to manually position them:
[java]
Container container = new Container();
container.addChild(textElement);
container.addChild(labelElement);
container.addChild(redElement);
container.addChild(blueElement);
container.addChild(toggleElement);
guiNode.attachChild(container);
[/java]
Treat any scene graph element as a button:
[java]
MouseEventControl.addListenersToSpatial( myNode, new DefaultMouseListener() {
public void click( MouseButtonEvent event, Spatial target, Spatial capture ) {
// Do something with capture (the spatial that got the down event)
}
});
[/java]
VersionedObject…
Since JME apps by their nature are polling app (update, update, update, update) it is often more efficient to poll for state changes rather than send a cascade of events. Furthermore, it often makes ordering more specific and definitely eliminates infinite event cascade loops.
This is why the first implementation (and current implementation) of Lemur uses this for its ‘model’ data (in MVC terms).
So, let’s say you have a text field and you really do want to do something every time it changes…
[java]
private VersionedReference myDoc;
public void simpleInitApp() {
....create my GUI....
myDoc = textElement.getDocumentModel().createReference();
}
public void simpleUpdate() {
if( myDoc.update() ) {
// Do some stuff with the text
}
}
[/java]
A VersionedReference is basically just tracking your last view of the ‘version’ of the referenced object. Calling update() checks to see if the original object’s version is newer than the last time you called update(). Don’t worry, there is no data copying or anything in this case. myDoc.get() always returns the current document model. You just get some insight into changes.
So, let’s say some other code made a bunch of modifications to the text. Instead of getting 500 different change events you wait to see the change at the point you are really interested in it.
The checkbox is similar.
VersionedReference myToggle = toggleElement.getModel().createReference();
In this case, myToggle actually does have a copy of Boolean. So perhaps the code for enabled/disabling the buttons could then look like this:
[java]
public void simpleUpdate( float tpf ) {
if( myToggle.update() ) {
redElement.setEnabled(myToggle.get());
blueElement.setEnabled(myToggle.get());
…and so on…
}
}
[/java]
And at the risk of having totally bored you, here is an example of how you could make your existing elements play nice in a Lemur GUI. (Here is the real power of the framework’s modularity.)
Let’s say you wanted to take your existing textElement and add it to a Lemur container. This should work:
[java]
…create your textElement the same way as in your original example…
textElement.addControl(new GuiControl() {
// Let layouts know what size we prefer
public Vector3f getPreferredSize() {
return new Vector3f(200, 100, 0);
}
// Adjust our size based on what layouts say
public void setSize(Vector3f size) {
textElement.setDimensions(size.x, size.y);
super.setSize(size);
}
});
....
container.addChild(textElement);
[/java]
The GuiControl is what manages the relationships between Lemur GUI elements and it can essentially be added to any Node. As an added bonus, you could then add stretchable background quads and such to your existing elements just by adding components to that GuiControl.
In my case, I have lots of regular scene elements are non-traditional GUI code that I wanted to make play nice in my regular Lemur GUIs… so modularity and adaptability was a strong requirement.