Lemur Container Sorting

Hello. I have been working on my UI. I have just tried Nifty. But its not exactly what I’m going for. Then I ran into Lemur which fits what I’m going for much much more.

However, I cannot get containers to render over all other containers when clicked/dragged. Like a window.

The sorting is completely wrong.

And click events seem to be leaking through to containers behind the top on.

Am I missing something?

    GuiGlobals.initialize(this);
//        BaseStyles.loadGlassStyle();
    BaseStyles.loadStyleResources("Interface/glass-style.groovy");
    GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");

    for (int i = 0; i < 50; i++) {
        Container myWindow = new Container();
        myWindow.setPreferredSize(new Vector3f(200, 200, 0));
        CursorEventControl.addListenersToSpatial(myWindow, new DragHandler());
        guiNode.attachChild(myWindow);

        // Note: Lemur GUI elements grow down from the upper left corner.
        myWindow.setLocalTranslation(300, 300, 0);

        // Add some elements
        myWindow.addChild(new Label("Window: " + i));
        Button clickMe = myWindow.addChild(new Button("Click Me"));
        clickMe.addClickCommands(new Command<Button>() {
            @Override
            public void execute(Button source) {
                System.out.println("The world is yours.");
            }
        });
    }

Sorting is based on Z in the gui node. So moving the windows up in Z moves them forward in the ordering (to front).

This is how clicks are processed also… but note that if a window doesn’t intercept the click (set consumed) then it will fall to the one behind and so on.

By the way, if you put your glass-style.groovy file in the right place then it will be loaded automatically by loadGlassTyle() and it will override augment anything in the default glass style. This is useful if you want to grab most of Lemur’s default glass style but just override or add some of your own stuff.

Edit: do note if you go this route: glass-styles.groovy versus glass-style.groovy as the file name.

I needed to disable the container alpha. To better see the issue.

will setting the z be inherited by the children in the container?

It’s like a regular scene graph. The children are the children so their world location is including the parent… just like any other spatials.

Just asking because I;ve tried and it didn’t work. I’ll show you in a sec.

Can you try this? I’m not sure I’m using the Z correctly

    for (int i = 0; i < 50; i++) {
        Container myWindow = new Container();
        myWindow.setPreferredSize(new Vector3f(200, 200, 1));
        myWindow.addMouseListener(new DefaultMouseListener() {
            @Override
            public void mouseButtonEvent(MouseButtonEvent event, Spatial target, Spatial capture) {
                if (!event.isPressed()) {
                    return;
                }
                event.setConsumed();
                for (Spatial n : guiNode.getChildren()) {
                    n.getLocalTranslation().z -= 1;
                }
                capture.getLocalTranslation().z = 1000;
            }
        });
        CursorEventControl.addListenersToSpatial(myWindow, new DragHandler());
        myWindow.setLocalTranslation(300, 300, -i);
        guiNode.attachChild(myWindow);

        // Add some elements
        myWindow.addChild(new Label("Window: " + i));
        Button clickMe = myWindow.addChild(new Button("Click Me"));
        clickMe.getLocalTranslation().z = i;
        clickMe.addClickCommands(new Command<Button>() {
            @Override
            public void execute(Button source) {
                System.out.println("The world is yours.");
            }
        });
    }

Never ever ever ever set these values directly like this. And if you think you need to then still never ever ever do it.

If you don’t call setLocalTranslation() then JME has no idea that you’ve changed the vector and nothing happens. Always always always call the setters to change a value. Never set the fields directly.

Edit: note that you can set the fields directly only if you call setLocalTranslation(getLocalTranslation()) right after. For what you are doing, it might be better to use move() anyway.

I wasn’t aware of move(). Thanks I’ll try this out

Now the container is sometimes consuming the event before its children. And the children are still rendering above other windows that have been moved on top. If I click the window a few times the other children move down.

I’ve updated it to this:

            public void mouseButtonEvent(MouseButtonEvent event, Spatial target, Spatial capture) {
                if (!event.isPressed()) {
                    return;
                }
                event.setConsumed();
                for (Spatial n : guiNode.getChildren()) {
                    n.move(0, 0, -1);
                }
                capture.getLocalTranslation().z = 1000;
                capture.setLocalTranslation(capture.getLocalTranslation());
            }

I’ve never had a problem with Z-ordering and I use it all the time… so I’m not sure what to say directly.

If it were me, I’d start dumping the locations of things and who was actually receiving the events. Lots of system out printlns.

Note: printing what is passed in the event will also help determine if it is what we are expecting. For example, if a child is receiving the event then the capture will be the child and not the container and that would cause exactly the issues you describe.

Got it.

Now I have a new issue. When I click on a button from a non focused window it doesn’t set that window as the top window.

Is there a way of letting the container get the click event before the children?

Not without giving them something to intercept the event ahead of it… like a panel in front or something. It’s technically possible to let it get the down if the button ignores it but then the button won’t capture like it’s probably supposed to.

This is the kind of thing that would be managed better with focus. Focus management in Mythruna is a bit unfinished and so some of it’s kind of ‘back door’ so to speak.

But I think something like this might work:

  1. register a focus change listener with your window containers’ GuiControl. Something like:
myWindow.getControl(GuiControl.class).addFocusChangeListener(new FocusChangeListener() {
....
});

In that listener, do the logic to bring your window to front.

  1. I don’t think buttons focus themselves automatically but you can add a listener that will do it. Just call GuiGlobals.getInstance().requestFocus(theButton);

It might then be worth adding a FocusMouseListener to the window itself so that any clicks anywhere on the window would give it the focus.

You might (quite likely) have to set the focusable property of your window containers’ GuiControl.

myWindow.getControl(GuiControl.class).setFocusable(true);

That lets myWindow track focused state even if it doesn’t have any components (not children but components) that track focus.

A lot of this will be way more automatic in the future when focus traversal is properly implemented.

Awesome @pspeed

All I had to do was add the FocusChangeListener to the containers. But I had to keep the MouseListener for when I click on the container it self (I couldn’t find FocusMouseListener)

EDIT: found it.

FocusMouseListener only gets focus when the window is clicked. not on mouse down event. So if you drag it while its behind another it wont focus.

Cool. Glad you got it working. :slight_smile:

Last question for awhile.

How would I go about putting a panel over an container. I’m trying by it keeps moving other children…

What is it that you are trying to do? If it’s just catching the mouse events then I wouldn’t use a panel but a transparent quad component added to the top of the panel’s component stack. You might have to add your own component class, though, as I can’t remember if the background quad component tries to always move things to the back or not.

Still, if this is trying to solve the focus problem then it seems like the sledge-hammer approach.

If you really want to use an overlay Panel then you will have to attach it as a regular JME child and not a container child. attachChild() versus addChild(). Else it will be a part of the layout and mess up the other children.

I ended up just add a mouse listener to children when theyre added through addChild

Feels dirty but I couldn’t find a better way.

The focus listener worked but wasn’t completely what I needed.

Well, focus management is the “right answer” so I’d be interested to know what wasn’t working about it.