Many Little Lemur Questions

That is the key I was missing thanks. Worked perfectly now I’m cookin with grease.

1 Like

This is probably more of a general JME question but with this menu how can I consume mouse clicks so that they don’t reach other click handlers? The buttons consume the event but other elements in the menu don’t. I tried to investigate how the Button/ColorChooser do it but couldn’t figure it out.

I think all handlers get the event, but the event also brings an isConsumed Boolean to check (or similar name).

I assume you want that “Counter Menu” to be “modal”… in that when it’s open you don’t want anything else on the screen to get events.

If so…

I would like to introduce you to PopupState. You can grab the instance here:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/GuiGlobals.html#getPopupState()

Then show your window with:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/event/PopupState.html#showModalPopup(com.jme3.scene.Spatial)

If you only want it to consume events that are over the window itself… then PopupState is not what you want. Instead, just add:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/event/ConsumingMouseListener.html
…as a listener to the container window.

1 Like

Just want to tell you guys that this thread here is gold :slight_smile: it did answer a couple of questions I had and also gives me a lot of pointers to solve issues I faced and till now ignored as I had other stuff to do.
keep the good work up.

2 Likes

Modal doesn’t seem like the approach I need because I’d like clicks outside of the panel to still be processed.

I added this line of code to the Container:

addMouseListener(ConsumingMouseListener.INSTANCE);

Now a Listbox on the container with a click command registered like this IS NOT responding:

hostGameScreen.gamesListBox.addClickCommands(new Command<ListBox>() {
	@Override
	public void execute(ListBox source) {
		...
	}
});

A Button on the Container with a click command registered like this IS responding though:

hostGameScreen.hostBtn.addClickCommands(new Command<Button>() {
	@Override
	public void execute(Button source) {
		...
	}
});

Any idea?

It sounds like the event is getting to the outer container first for some reason. Does selection still work?

Maybe the Z was flattened in your size setup or something?

I am very worried about that frankly because I did have to set some sizes and translations manually. The ListBox is selecting the item in the list still but the click command is not firing when it does. In the example the Rows and Columns text fields should populate with text when an item in the ListBox is clicked.

Here is the CustomContainer I am using:

public class CustomContainer extends Container {

private Container innerContainer;

public CustomContainer(String label) {
	super(new BorderLayout());
	Label l = new Label(label, getElementId().child("title.label"), "custom");
	l.setFontSize(18);
	addChild(l, BorderLayout.Position.North);
	innerContainer = new Container(new ElementId("menu.container"));
	addChild(innerContainer, BorderLayout.Position.Center);
	addMouseListener(ConsumingMouseListener.INSTANCE);
}
...

Here is how I add the ListBox to the Container, I don’t touch the ListBox after this:

gamesListBox = panel.getInner().addChild(new ListBox<String>());

Here is how I translate the Container:

public static void positionGUIElements(ClientModel cm, Panel backBtn, Container panel) {
	...
	x = windowWidth / 2 - containerWidth / 2;
	y = windowHeight / 2 + containerHeight / 2;
	panel.setLocalTranslation(x, y, panel.getLocalTranslation().z);
}

That’s veeeery strange. The same code that handles the click event sending is also managing selection:

Anyway, another thing to check is what the preferred size if for the outer container, list box, etc… see if any of them are saying z=0 for preferred size.

It could be that some background panel somewhere needs a larger z-offset or something.

If it were me debugging this, I’d probably traverse the whole hiearchy to dump their actual sizes to see if something looks strange there. Mouse clicks/events are processed in z-order.

The Container getPreferredSize() is at z=4. The ListBox getPreferredSize() is at z=1. I included the relevant code to maybe determine why, will keep looking.

Rather than answering in-thread, you should consider making a wiki page on Lemur, then just link that page as the answer…

However, I’ve modified my code from:

    ((SimpleApplication) getApplication()).getGuiNode().attachChild(loadWindow);

to

    GuiGlobals.getInstance().getPopupState().showPopup(loadWindow);

The result is the opposite of what I expect: the loadWindow don’t receive any event at all, but the rest of the gui does… a reverse popup? :slight_smile:

If I raise the barrier of entry event a hair higher then I won’t have time to answer at all. And Lemur has its own wiki which is 1000000000x easier to update than JME’s. But still takes more time.

Creates a popup. Not a modal popup. To get a modal popup you would have to use the one for modal popups.

The regular showPopup() method will attach your child to the guiNode just like you’ve done before. The only difference is that it also installs a panel underneath it to catch events to auto-close the dialog (by default).

The only additional logic is how it positions the event catcher panel relative to the guiNode and the popup relative to that.

I think I need to know more about how selection occurs because I’m not sorting this out. Is it just localTranslation z + preferredSize z that come into play? Is it just localTranslation z as long as preferredSize z is greater than 0? Or is it something else? How much does addChild() play into setting things up properly? If I manually translate an element will the picking system still work properly? I manually translated the listbox to 100z and not having an effect. Would prefer not to manually translate anyway but expected it to work. Here is the outputs for my components, let me know if there are other relevant outputs to look at.

hostGameScreen.gamesListBox.getPreferredSize() => (17.421764, 112.58823, 1.0)
hostGameScreen.gamesListBox.getLocalTranslation() => (0.0, 0.0, 100.0)
hostGameScreen.panel.getPreferredSize() => (324.0, 323.94116, 4.0)
hostGameScreen.panel.getLocalTranslation() => (350.0, 545.9706, 0.0)

This is the behavior without:

addMouseListener(ConsumingMouseListener.INSTANCE);

This is the behavior with:

The “Host” Button doesn’t have the same problem with these outputs so it’s hard to tell what gives:

hostGameScreen.hostBtn.getLocalTranslation() => (0.0, 0.0, 0.0)
hostGameScreen.hostBtn.getPreferredSize() => (260.0, 57.705883, 2.0)

Lemur is just using regular JME scene picking into the guiNode (basically). It processes the collision results it gets in nearest to farthest order. The first hit it finds that has a listener gets the event if it’s consumed. (The logic is a bit weird here and I have on my to-do list to refactor but that’s how it’s supposed to work).

So you can try constructing a ray and seeing what the results of collideWith() are yourself. Maybe it sheds some light on why the back object gets hit first?

Your positions/sizes sort of look ok from the output but really it’s the world translation that would be most interesting.

addChild() is not special except that it makes your spatial part of the layout. At the end of the day, these are all just JME spatials in a regular attach/detach style hierarchy and conform to all normal JME transformation stuff. getWorldBound(), getWorldTranslation(), etc. should all be sensible with respect to the children.

Barring all of that, if you can put together a simple test case that illustrates the issue then I might be able to try it myself. Or you can try the lemur demos to see if the popups work properly there and modify it to break.

Sent a Ray down not sure what attribute of Geometry would be useful seems like something can be gleaned from parent. The component attachments are done in this fashion: ListBox (gamesListBox) > Container (innerContainer) > CustomContainer which extends Container (panel) > Node. The ListBox parent is an innerContainer in my CustomContainer which has the elementId “menu.container” so it seems like the ListBox is the first collision. The second collision seems to be the innerContainer whose parent is my CustomContainer which would be as expected.

First collision’s parent was:

com.simsilica.lemur.Container[layout=com.simsilica.lemur.component.SpringGridLayout[mainAxis=Y, minorAxis=X, mainFill=Even, minorFill=Even], elementId=ElementId[menu.container]]

Second collision’s parent was:

view.component.lemur.CustomContainer[layout=com.simsilica.lemur.component.BorderLayout@173d1801, elementId=ElementId[container]]

This is the world translations:

hostGameScreen.gamesListBox.getPreferredSize() => (17.421764, 112.58823, 1.0)
hostGameScreen.gamesListBox.getWorldTranslation() => (350.0, 545.9706, 100.0)
hostGameScreen.panel.getPreferredSize() => (324.0, 323.94116, 4.0)
hostGameScreen.panel.getWorldTranslation() => (350.0, 545.9706, 0.0)
hostGameScreen.hostBtn.getPreferredSize() => (260.0, 57.705883, 2.0)
hostGameScreen.hostBtn.getWorldTranslation() => (350.0, 545.9706, 0.0)

There should be more than 2 geometries in the collision results though…I would expect it to atleast hit ListBox > innerContainer > CustomContainer

The Button (hostBtn) has the exact same collision results.

Creating a test environment now.

Might have gained some insight…Here is a demonstration of a similar effect in the test. When the click command is fired for the ListBox the textfield shows LISTBOX, when the click command is fired for the Button the textfield shows BUTTON. When clicks are made on the right hand side of the ListBox nothing is fired. When made on the left hand side they are fired. I feel like a much more drastic version of this is happening in my app. I just tested my app and confirmed that if I click at the VERY far left hand side infrequently and not reproducibly the click command will actually fire.

It starts becoming an issue when something like this is done to the Container:

Vector3f v = panel.getPreferredSize();
panel.setPreferredSize(new Vector3f(v.x + 100, v.y, v.z));

Here is my testing code and appreciate you offering to take a look:

import com.jme3.app.SimpleApplication;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.ListBox;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.event.ConsumingMouseListener;
import com.simsilica.lemur.style.BaseStyles;

public class SimpleTest extends SimpleApplication {

public static void main(String[] args) {
	SimpleTest test = new SimpleTest();
	test.start();
}

@Override
public void simpleInitApp() {
	this.setDisplayFps(false);
	this.setDisplayStatView(false);
	this.setPauseOnLostFocus(false);

	this.getFlyByCamera().setEnabled(false);
	this.getInputManager().clearMappings();

	GuiGlobals.initialize(this);
	BaseStyles.loadGlassStyle();
	GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");

	MenuHostGameNode node = new MenuHostGameNode();
	getGuiNode().attachChild(node);
}

private class MenuHostGameNode extends Node {

	public TextField rowsTextField;
	public ListBox<String> gamesListBox;
	public Button hostBtn;
	public Container panel;

	public MenuHostGameNode() {
		panel = new Container();

		ListBox<String> listbox = new ListBox<String>();
		gamesListBox = panel.addChild(listbox);

		rowsTextField = panel.addChild(new TextField("Rows"));

		hostBtn = panel.addChild(new Button("Host"));

		this.attachChild(panel);
		Vector3f v = panel.getPreferredSize();
		panel.setPreferredSize(new Vector3f(v.x + 100, v.y, v.z));

		panel.addMouseListener(ConsumingMouseListener.INSTANCE);

		addGamesToListBox(gamesListBox);
		position();
		addBehavior();
	}

	private void addGamesToListBox(ListBox listbox) {
		// Populate listbox
		for (int i = 0; i < 5; ++i) {
			listbox.getModel().add("test" + i);
		}

		listbox.setVisibleItems(listbox.getModel().size());
	}

	private void addBehavior() {
		gamesListBox.addClickCommands(new Command<ListBox>() {
			@Override
			public void execute(ListBox source) {
				rowsTextField.setText("LISTBOX");
			}
		});
		hostBtn.addClickCommands(new Command<Button>() {
			@Override
			public void execute(Button source) {
				rowsTextField.setText("BUTTON");
			}
		});
	}

	private void position() {
		float windowWidth = settings.getWidth();
		float windowHeight = settings.getHeight();

		float containerWidth = panel.getPreferredSize().x;
		float containerHeight = panel.getPreferredSize().y;
		float x = windowWidth / 2 - containerWidth / 2;
		float y = windowHeight / 2 + containerHeight / 2;
		panel.setLocalTranslation(x, y, panel.getLocalTranslation().z);
	}
}

}

Edit: Don’t know why library version didn’t occur to me but I’m using:

	<!--================== Lemur dependencies =================== -->
	<dependency>
		<groupId>com.simsilica</groupId>
		<artifactId>lemur</artifactId>
		<version>1.9.1</version>
	</dependency>
	<dependency>
		<groupId>com.simsilica</groupId>
		<artifactId>lemur-proto</artifactId>
		<version>1.8.1</version>
	</dependency>

Any version changes you’d recommend?

Edit2: Switched to these versions same issue. Actually working worse with new versions in my app. The ListBox doesn’t even fire the event without panel.addMouseListener(ConsumingMouseListener.INSTANCE); with the new version.

	<!--================== Lemur dependencies =================== -->
	<dependency>
		<groupId>com.simsilica</groupId>
		<artifactId>lemur</artifactId>
		<version>1.10.1</version>
	</dependency>
	<dependency>
		<groupId>com.simsilica</groupId>
		<artifactId>lemur-proto</artifactId>
		<version>1.9.1</version>
	</dependency>