A catchall gui element

BitmapText is very nice if you need a quick output to the guiNode. but jme doesnt really have a way to quickly add buttons to the guiNode. You pretty much have to use a gui library or make keybindings with the input manager… so I made a new spatial called “GuiElement” it is a label, a button, a toggle button, and a text field, and requires no boilerplate code.

It’s very basic, and meant to be used for prototyping or debugging more than creating a professional looking interface. Anyone is free to do with it what they will. If anyone has any suggestions for changes feel free to post them.

The class: http://pastebin.com/LhKu8Vez

example usage: http://pastebin.com/RGRSVNyf

Sounds a lot like Lemur.

http://hub.jmonkeyengine.org/forum/topic/lemur-repo-link/

i’ve seen lemur (i even mentioned it in the description :D) my goal though was to create something that was just a single spatial you can add to the guiNode and then quickly type in the action it performs. I decided to actually put something together because last night i ran in to the same situation againwhere I just wanted to quickly put a button on the screen, but it would then mean having to set up a gui system with a library with an app state or something. This is specifically targeted for debugging and testing… need something to click on to see if the animation code works? add this.

it might not be for everyone, but I already know I plan to use it a lot. I thought others might too.

@icamefromspace said: i've seen lemur (i even mentioned it in the description :D) my goal though was to create something that was just a single spatial you can add to the guiNode and then quickly type in the action it performs. I decided to actually put something together because last night i ran in to the same situation againwhere I just wanted to quickly put a button on the screen, but it would then mean having to set up a gui system with a library with an app state or something. This is specifically targeted for debugging and testing.. need something to click on to see if the animation code works? add this.

it might not be for everyone, but I already know I plan to use it a lot. I thought others might too.

I’m not sure I understand.

Here is how you add a button to the gui node in lemur:

guiNode.attachChild(new Button(“My Button”));

You’d need the library… but then you’d need that for yours too, I suspect.

Other than adding the library the only other requirement for lemur setup is that somewhere in simpleInit() you call:
GuiGlobals.initialize(). And I mostly only did that to avoid having to pass a bunch of parameters to every constructor.

If you are interested, I could recode your example as a Lemur example. I believe it might be a bit shorter.

I haven’t heavily looked into lemur. TBH i saw that it had a bunch of support for advanced things like styles and layouts that I assumed it was of similiar weight to t0neg0dgui and didn’t feel too interested.

I’d be interested in seeing comparison code though if you dont mind throwing something together. (though I personally dont mind passing lots of parameters, I just push ctrl+space for auto complete anyway). the main thing i was trying to avoid was having to have multiple parts of code in multiple places. Though a single GuiGLobals.initalize() isnt too bad.

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.

Sorry for our forum’s inability to recognize line breaks after the first code block. :facepalm:

the reason center has parameters is so you can position it using an offset from center., its doing center() then move() in one statement. My thought being that setLocaltranslation() is a positon with an offset from bottom left. center() is a position with an offset from the center (i was going to add functions to like right() bottom() etc etc, i actually just forgot) .

Lemur clearly is quite powerful, espeically with being able to just add a GuiControl to anything, and it suprisingly keeps the code footprint down.

Though I really was just trying to make somethig that was essentialy just a bitmaptext but with the mouse interactions pre-coded… Every GuiElement is an island. For slapping something together I still think I’d prefer GuiElement. I might check out lemur next time I need to put a real gui together though.