LemurProto - Action, ActionButton, and OptionPanel

So, I continue to add stuff to Lemur Proto here and there as I can. These new classes probably could have been added to Lemur core but I still wanted to let them incubate. It’s a sort of self-imposed rule at this point for new elements.

I haven’t yet cut a new release with these new components because I hope to be adding some more soon. If there is demand I can do it. In the mean time, as usual, the source can be found here:
https://code.google.com/p/jmonkeyplatform-contributions/source/browse/#svn%2Ftrunk%2FLemur%2Fextensions%2FLemurProto

In progress changelog:
https://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/Lemur/extensions/LemurProto/release/LemurProto-changelog.txt

Action

This is similar to javax.swing.Action in that it’s a general way of encapsulating some ‘action’ along with some generic display parameters like name, icon, etc… This can then be poked into buttons and (down the road) toolbars, menus, etc…


Action doSomething = new Action("Do It") {
        public void execute( Button b ) {
            System.out.println( "Do the thing.");
        }
    };

ActionButton

Is just a Button with Action support. Swing puts this all together and I may merge the behavior eventually but in the mean
time ActionButton simply makes sure the Button is setup with the action’s properties (name, icon, etc.).

Button b = new ActionButton(doSomething);

…simple.

Changes to the action will then be automatically reflected in the button itself. So if you change the action’s name then all using buttons will also change… and so on.

OptionPanel

This is similar to Swing’s JOptionPane though a little simpler. It’s a convenient way of presents some message to the user with a set of options at the bottom. For example, it can be used for error messages with just an “Ok” button or it could be used to present a “Yes”/“No”/“Cancel” style set of options.


Action cancel = new Action("Cancel");
OptionPanel shouldDo = new OptionPanel("Really do it?", doSomething, cancel);

OptionPanelState

It’s almost always the case that you want an OptionPanel to be modal, ie: supersede all other input. OptionPanelState facilitates this and also provides convenient access to pop open an OptionPanel at any time. Simply make sure it’s attached to your state manager and you can grab it where you need it.

getState(OptionPanelState.class).show("Some Title", "Really do it?", doSomething, cancel);

That will present the panel in the guiNode and block all other mouse input except to the option panel. Once the user has clicked one of the options then the panel will close and input will resume.

Next I’ll be setting up some default styling in the glass style which will serve as an example on how to customize it for different styles. Alternately, OptionPanel provides raw access to any of its internal elements and they can be tweaked directly as needed.

Similar to Swing’s JOptionPane, you can pre-create OptionPanels in advance and just pop them open when needed. The OptionPanelState supports this too:

getState(OptionPanelState.class).show(shouldDo);

Also, OptionPanel provides access to its internal center Container that holds the message text. So just like Swing’s JOptionPane you could add additional GUI elements as needed for some simple input like capture a text field or slider value or whatever.

4 Likes

I setup ‘glass’ styling for the new GUI elements. These will be automatically loaded whenever BaseStyles.loadGlassStyle() is called. The styling file can also show you how you might configure your own styling for these new GUI elements if you have your own custom style.

The styling file can be found here:
https://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/Lemur/extensions/LemurProto/src/main/resources/com/simsilica/lemur/style/base/glass-styles.groovy

(It’s also important to note how easy it is to augment a resource-based style like this as you can see. Those styles are added to the glass style that is defined in Lemur core simply because it is in the proper directory with the proper name. This works for your styles, too, if you load them with BaseStyles.loadStyleResources())

I also added a proto demo with some of the new GUI elements. It demonstrates the ListBox, Action, ActionButton, and OptionPanel/OptionPanelState. The code can be found here:
https://code.google.com/p/jmonkeyplatform-contributions/source/browse/trunk/Lemur/extensions/LemurProto/src/main/java/com/simsilica/lemur/demo/ProtoDemo.java

The salient bits:
[java]
// Create a window to hold our demo elements and add a title label
Container window = new Container(“glass”);
window.addChild(new Label(“Test List”, new ElementId(“title”), “glass”));

    // Make some test data for the list. 
    for( int i = 0; i < 10; i++ ) {
        testList.add("Item " + (i+1));
    }

    // Create a list box for the test data and add it to the window         
    listBox = new ListBox(testList, "glass");
    window.addChild(listBox);          

    // Create some actions
    final Action add = new Action("Add") {
            @Override
            public void execute( Button b ) {
                testList.add("New Item " + (testList.size() + 1));
            }
        };
    final Action delete = new Action("Delete") {
            @Override
            public void execute( Button b ) {
                Integer selected = listBox.getSelectionModel().getSelection();
                if( selected != null && selected < testList.size() ) {
                    testList.remove((int)selected);
                }
            }
        };
    final Action cancel = new Action("Cancel") {
            @Override
            public void execute( Button b ) {
            }
        };

    // Safe delete is a special action because it will pop open 
    // the option panel and delegate to the other delete action.           
    final Action safeDelete = new Action("Safe Delete") {
            @Override
            public void execute( Button b ) {
                Integer selected = listBox.getSelectionModel().getSelection();
                if( selected == null || selected >= testList.size() ) {
                    return;
                }
                String val = testList.get(selected);
                OptionPanelState ops = stateManager.getState(OptionPanelState.class);
                ops.show("Delete", "Really delete '" + val + "'?", delete, cancel);                        
            }
        };            

    // Create the button panel at the bottom of the window
    Container buttons = new Container(new SpringGridLayout(Axis.X, Axis.Y, FillMode.Even, FillMode.Even));
    window.addChild(buttons);       
    buttons.addChild(new ActionButton(add, "glass"));
    buttons.addChild(new ActionButton(safeDelete, "glass"));
    buttons.addChild(new ActionButton(delete, "glass"));
    

    // And stick the window somewhere we will see it       
    window.setLocalTranslation(300, 600, 0);
    guiNode.attachChild(window);

[/java]

With the option panel open and the new glass styling, it looks like this:

1 Like

Thanks, really useful things! =)

This is very cool!

Was curious if there are any plans to put that cool scrollbar functionality that’s with your ListBox into the TextField class, also? Would be nice to scroll in the TextField, and also nice to have a ScrollEvent so client code can detect when this occurs.

@sumdummonkey said: This is very cool!

Was curious if there are any plans to put that cool scrollbar functionality that’s with your ListBox into the TextField class, also? Would be nice to scroll in the TextField, and also nice to have a ScrollEvent so client code can detect when this occurs.

It’s a good idea for a multi-line text field. I’ll have to think about how to do it properly. Could be just another meta-element that combines the two. Not sure yet.

Do you have any recommendations for how to implement with TextField class? I started looking into it, was trying to create a new ScrollPanel, and then adjust the code in LetterQuad::clip() method to respect clipping on the y-axis, but based on the conversation here i’m not sure if that’s the best way to go.

You might be able to use a scroll bar to control the cursor position in a multiline TextField. I don’t remember if I give you the hooks to do that or not. I’m pretty sure that I do clip Y to the number of visible lines.

That part I’m unsure of is if DocumentModel gives you the line and the total number of lines. This would be what is necessary to have a scroll bar properly control which line the cursor was on. That would effectively give you a scrollable edit field.

Where is ‘do clip Y to the number of visible lines’ occuring, is in LetterQuad? Looks like currently it only does clipping to X, is there some other place where it is clipping to Y?

Yeah, I guess you’re right. I mean, it will clip in y because the text box is set… well, maybe BitmapText is not even doing that.

The proper way to do a multiline text field in this case is to render it as a bunch of single line BitmapTexts. DocumentModel keeps things as separate lines internally so in theory this could be done by hacking together ListBox with some TextField cells. Each TextField cell would get a single line of the DocumentModel… and it would set a KeyActionListener for up/down/enter to move to the appropriate line.

I don’t know what potential pitfalls there might be. Unfortunately, I haven’t really designed a multiline scrollable text field yet so I apologize that this is all a little ‘seat of the pants’.

Regardless of the ‘seat of the pants’ thinking, your advice is much appreciated! Will take a look into this, if I can mock up anything useful will post back and share if there is any interest.