Many Little Lemur Questions

Let me start by saying that compared to the absolute nightmare that was trying to use Nifty, Lemur is a breath of fresh air. It is hard finding documentation about certain aspects of using the library though and I’d appreciate some help.

  1. How do you set width on certain components like Button and Label? On TextField there is setPreferredWidth() and on others you can use setPreferredSize() but seems overly cumbersome for just wanting to change one dimension and seemingly having to know specifics of the library like what z position the element should have etc.

  2. How do you set font size on more complex components like ListBox?

  3. Is there any components in the library or that others have created that functions like a JComboBox? The ListBox provided gets the job done but the ux of the JComboBox is preferable. I understand that the point of Lemur was to be extensible but don’t want to reinvent the wheel either.

  4. Is there a way to make all elements in a container lose focus? When I switch app states when the use hits or it still activates components that had focus in the last app state.

  5. How do you control stylistic elements like background or border of a Container?

  6. For certain components there is an addClickCommands(), is there a way to handle hover commands as well?

  7. How do you disable or hide elements? There is an animation utility in the library that seems like could be applied in some respect but disabling elements seems like a universal behavior that might have a more canonical way to do.

  1. To achieve this look i applied insets to each component in the Container. Is there a way in Lemur to apply container level spacing for a simpler implementation?
1 Like

Well, I’ll give you some of the information that is in my hand at the moment:


You must go the cumbersome way, but you can always create a set of utilities where you can “ease” all of this things.


I have to admit that I always have a bad time with font sizes/labels and lemur/resizing. I can’t say much more about this.


Well, you are talking about another library here (Swing) so, although the wheel mustn’t be reinvented it must be re-implemented.



You have multiple ways


You have the addCommands (if talking about a button) or MouseEventControl if you are with “no-button” panels.


You can do it with animations or without them. You can make them transparent or just detach them.

1 Like
  1. I generally use getprefferred/setpreffered but only usually to avoid being too small. It’s more of a situational thing than a programmatic failure.

  2. All text is a label, so you get the label and resize that.

  3. Yes I believe so but it escapes me as to what the library was. It was quite cool and has 3d stuff also.

  4. I believe you set the focus to null in guiglobals somewhere. There is a trick to this issue.

  5. Styles are written in groovy or in Java code directly if you are on Android. Take a look at the default glass style.

  6. Yes, there is also a spatial hook of sorts to have that kind of activity on any spatial, 2d or 3d. I’m not on my pc right now to give the exact name of the static methods though.

  7. You set the alpha if you want to animate it out, and remove them or cull them just like a regular geometry with a material.

  8. Yes. Use styles.

Sorry for being vague, I am on my phone, just wanted to clarify some things.

2 Likes

Ok, let’s roll up our sleeves and get to this! :slight_smile:

It’s often rare to want to do this as the layouts take care of a lot of the stuff. For root level windows it’s quite common, though. Best approach, ask the window what it’s preferred size is, Math.min() whatever values you want against your minimum preferred size, then set it back.

Either in styling (the easy way) or directly with code by setting your own cell renderer.

You can see how the ListBox is setup in the glass style. You’d set a fontSize in the list.item styling:

In it’s current configuration, it inherits the default from the root glass style.

It is on my to-do list. I think a few others have cobbled some things together.

Only recently has this become particularly easy since there is now a built in way to handle popups. So now it’s just a matter of combining a Label or TextField with a button and a pop-up list box.

GuiGlobals.getInstance().requestFocus(null); …if you have no better place to go.

http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/GuiGlobals.html#requestFocus(com.jme3.scene.Spatial)

Either set them directly:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/Panel.html#setBackground(com.simsilica.lemur.core.GuiComponent)
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/Panel.html#setBorder(com.simsilica.lemur.core.GuiComponent)

Or in styling:

addClickCommands() is a convenience function that essentially calls:
addCommands(ButtonAction.Click…)
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/Button.html#addCommands(com.simsilica.lemur.Button.ButtonAction,%20com.simsilica.lemur.Command...)

The available hooks are here:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/Button.ButtonAction.html

Hide but still participate in layout… JME’s regular cull hint.

Hide from even the layout… I think only removing the child.

But if you just mean like to show/hide a root window, then that’s regular JME stuff. Just remember that all Lemur GUI elements are just spatials.

Or did you mean something else?

You can set the margin on the background component and it will achieve the same effect… assuming the background component supports a margin (in this case it does).

But either way, you can add whatever you want to the component stack for a GUI element. Insets are applied before background only because that’s the order they are in the stack.

Still, 99% of the time (like in the glass style) you will have a QuadBackgroundComponent or a TbtBackgroundComponent… and both of those support setting a margin (they pretty much have to).

Hope that helped a little.

Edit: P.S.: I wish that discourse would show better links for the wiki links above… that last link jumps right into the component stack documentation but you’d never know it from that view.

3 Likes

What are those spatial hooks I’m talking abiut, @pspeed - the ones that provide collision results. It’s bugging me. They are useful.

1 Like

CursorEvents… they work even on regular JME Spatials.

http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/event/CursorEventControl.html

Works just like MouseEventControl except the events contain the collision information, too. Example of MouseEventControl:

3 Likes

I use these in my new advanced crafting system! They are very useful.

2 Likes

Yeah, totally.

I use CursorEvents almost exclusively now because even when I don’t need the information for normal processing, it can be nice to have for debugging.

1 Like

Have a look at this:

Actually, more a “dropdown menu” than a real “combobox”.

I’m not currently updating it, but PR are welcome. :wink:

1 Like
  1. Paul mentioned using layouts to achieve this. I’d be happy to do that but haven’t found great documentation on using the layouts frankly. I got a SpringGridLayout to actually create a grid which required a few forums posts to figure out but that’s about it so far.

As per other people’s suggestions I tried the using setPreferredSize() /getPreferredSize() for setting width of the buttons and it seems to work.

2,5,8) I tried to add a custom .groovy file as described in the recent work in progress thread:

Eek… the easiest and best long-term way is to cut-paste the style files into your own style and just change the textures, fonts, whatever, there. Most of them inherit the background image from the same one or two definitions.

Then just load that style and set it as the default instead of the build in glass style.

GuiGlobals.initialize(this);
BaseStyles.loadStyleResources("custom-styles.groovy");
GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");

But not seeing style changes appear when run so don’t know what to make of that. Either it’s not reading the file properly cause I have the wrong path or I can’t use glass name or something.

  1. The suggestions for this worked perfectly.

Also Paul I would like to achieve this effect that you did in your math game with the panel title bar. How was this achieved?
http://i.imgur.com/J30z1QP.png

1 Like

Yes, groovy is just a convenience wrapper over the Java support. There is nothing it’s doing that isn’t ultimately calling Java. So you can at least be confident in that.

The above code is setting things in the “root” style the is inherited by all other styles… but if that style (such as “glass”) overrides that then you won’t see it.

Probably you want to modify the glass style itself.
Attributes attrs = styles.getSelector(“container”, “glass”);

setDefaultStyle(“glass”) does not affect the selectors only the actual retrieval of values. A null style passed to the selectors means you are editing the root style… which is bit different than the “default” style.

I edited my post heavily due to the whinging nature of it and further discoveries. Can you take another look over?

Did you cut-paste the whole glass style? By naming yours something different, you will not be loading the normal glass style… just your custom one.

Lemur is setup to load every one of the loadStyleResources(“foo”) it finds on the classpath. Which is why I get away with having a glass style defined in Lemur core and another add-on one defined in lemur-proto.

I did it in other things, too… I’m surprised it isn’t part of the default styling.

…let me check…

Ah, I believe it is part of the regular style:

…just need to use “title” in your element ID.

You can see it also used in the IsoSurfaceDemo:

Below is a near copy of the glass style written programatically. I do intend to contribute it to lemur at some point. It saves a lot of pain when you can’t use groovy or don’t want to.


package com.jayfella.motorunner.lemur;

import com.jme3.asset.AssetManager;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.TbtQuadBackgroundComponent;
import com.simsilica.lemur.geom.TbtQuad;
import com.simsilica.lemur.style.Attributes;
import com.simsilica.lemur.style.Styles;

/**
 * Created by James on 16/01/2018.
 */

public class LemurGuiStyle {

    public LemurGuiStyle(AssetManager assetManager) {

        float guiScale = 10.0f;
        float guiScaleHalf = guiScale * .5f;

        Styles styles = GuiGlobals.getInstance().getStyles();

        Attributes attrs;

        /*
        QuadBackgroundComponent bg = new QuadBackgroundComponent(new ColorRGBA(0.207f, 0.518f, 0.89f, 0.85f));

        TbtQuadBackgroundComponent gradient = TbtQuadBackgroundComponent.create(
                "Textures/Gui/white-rounded.png",
                1, 1, 1, 126, 126,
                1f,false);
        */

        QuadBackgroundComponent bg = new QuadBackgroundComponent(new ColorRGBA(0.207f, 0.207f, 0.207f, 0.85f));

        TbtQuadBackgroundComponent gradient = TbtQuadBackgroundComponent.create(
                "/com/simsilica/lemur/icons/bordered-gradient.png",
                // "Textures/Gui/radial-gradient.png",
                1, 1, 1, 126, 126,
                1f,false);

        TbtQuadBackgroundComponent double_gradient = TbtQuadBackgroundComponent.create(
                "/com/simsilica/lemur/icons/double-gradient-128.png",
                // "Textures/Gui/radial-gradient.png",
                1, 1, 1, 126, 126,
                1f,false);
        double_gradient.setColor(new ColorRGBA(0.5f, 0.75f, 0.85f, 0.5f));

        attrs = styles.getSelector("glass");
        attrs.set("fontSize", 4 * guiScale);

        // label
        attrs = styles.getSelector("label", "glass");
        attrs.set("insets", new Insets3f(2 * guiScaleHalf, 2 * guiScaleHalf, 2 * guiScaleHalf, 2 * guiScaleHalf));
        attrs.set("color", new ColorRGBA(0.5f, 0.75f, 0.75f, 0.85f));

        // title
        attrs = styles.getSelector("title", "glass");
        attrs.set("color", new ColorRGBA(0.8f, 0.9f, 1.0f, 0.85f));
        attrs.set("highlightColor", new ColorRGBA(1.0f, 0.8f, 1.0f, 0.85f));
        attrs.set("shadowColor", new ColorRGBA(0.0f, 0.0f, 0.0f, 0.75f));
        attrs.set("shadowOffset", new Vector3f(2, -2, -1));
        attrs.set("background", double_gradient.clone());
        attrs.set("insets", new Insets3f(2 * guiScale,2 * guiScale,2 * guiScale,2 * guiScale));

        // button
        attrs = styles.getSelector("button", "glass");
        attrs.set("color", new ColorRGBA(0.8f, 0.9f, 1.0f, 0.85f));
        attrs.set("background", gradient.clone());
        ((TbtQuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.0f, 0.75f, 0.75f, 0.5f));
        attrs.set("insets", new Insets3f(2 * guiScale,2 * guiScale,2 * guiScale,2 * guiScale));

        // container
        attrs = styles.getSelector("container", "glass");
        attrs.set("background", gradient.clone());
        ((TbtQuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.25f, 0.5f, 0.5f, 0.5f));

        // slider
        attrs = styles.getSelector("slider", "glass");
        attrs.set("insets", new Insets3f(2,2,2,2));
        attrs.set("background", bg.clone());
        ((QuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.25f, 0.5f, 0.5f, 0.5f));

        attrs = styles.getSelector("slider", "button", "glass");
        attrs.set("background", bg.clone());
        ((QuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.5f, 0.75f, 0.75f, 0.5f));
        attrs.set("insets", new Insets3f(0,0,0,0));

        attrs = styles.getSelector("slider", "slider.thumb.button", "glass");
        attrs.set("text", "[]");
        attrs.set("color", new ColorRGBA(0.6f, 0.8f, 0.8f, 0.85f));

        attrs = styles.getSelector("slider", "slider.left.button", "glass");
        attrs.set("text", "-");
        attrs.set("background", bg.clone());
        ((QuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.5f, 0.75f, 0.75f, 0.5f));
        ((QuadBackgroundComponent)attrs.get("background")).setMargin(5, 0);
        attrs.set("color", new ColorRGBA(0.6f, 0.8f, 0.8f, 0.85f));

        attrs = styles.getSelector("slider", "slider.right.button", "glass");
        attrs.set("text", "+");
        attrs.set("background", bg.clone());
        ((QuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.5f, 0.75f, 0.75f, 0.5f));
        ((QuadBackgroundComponent)attrs.get("background")).setMargin(4, 0);
        attrs.set("color", new ColorRGBA(0.6f, 0.8f, 0.8f, 0.85f));

        // Tabbed Panel
        attrs = styles.getSelector("tabbedPanel", "glass");
        attrs.set("activationColor", new ColorRGBA(0.8f, 0.9f, 1.0f, 0.85f));

        attrs = styles.getSelector("tabbedPanel.container", "glass");
        attrs.set("background", null);

        attrs = styles.getSelector("tab.button", "glass");
        attrs.set("background", gradient.clone());
        ((TbtQuadBackgroundComponent)attrs.get("background")).setColor(new ColorRGBA(0.25f, 0.5f, 0.5f, 0.5f));
        ((TbtQuadBackgroundComponent)attrs.get("background")).setMargin(12, 6);
        attrs.set("color", new ColorRGBA(0.4f, 0.45f, 0.5f, 0.85f));
        attrs.set("insets", new Insets3f(12,6,0,6));

    }

}


2 Likes

Can I just say you guys are freaking amazing. I got it working. Yes i did cut and paste the whole glass style and proto glass style trying to combine them in the way you described in the other thread because I use some proto elements as well.

I put my custom .groovy file in the assets folder.

Here is my custom .groovy file:

Here is my custom loading code:

BaseStyles.loadStyleResources("custom-styles.groovy");
GuiGlobals.getInstance().getStyles().setDefaultStyle("custom");

Are there any resources to see all of the available settings you can set for a selector in the .groovy files?

1 Like

It’s up to the classes as they can define what the want. You can search for the StyleAttribute annotations, though.

For example:

Otherwise, it’s using reflection so any class can define whatever styleable attributes it likes.

Knowing about that StyleAttribute annotation helped a lot . So I’m having success changing existing stylings of components globally. I’m still struggling with changing certain styles on components at a non-global level.

You mentioned before:

You can set the margin on the background component and it will achieve the same effect… assuming the background component supports a margin (in this case it does).

I am just not seeing how…how would you set margins on an individual Container?

Container panel = new Container();

I mean, usually you’d make it part of the style you use for that container. But if you want to set it directly, you will either need to set the background component directly… or know/hope that it is one that supports a margin.

QuadBackgroundComponent qbc = new QuadBackgroundComponent(ColorRGBA.Red, 10, 10);
panel.setBackground(qbc);

Or if you already know/hope what kind of component it is:
((QuadBackgroundComponent)panel.getBackground()).setMargin(10, 10);

That worked like a charm but I don’t feel like I can apply that to other stylings for individual components going forward but at least it’s a pathway.

If you usually wouldn’t do that and would make it part of the style you use for that container can you give an example of that without changing the style for all Containers? I didn’t see anything like setStyle().

As a separate question if you wanted to have 2 global classes of stylings for Containers, lets call them A and B, so that you can apply A to some containers and B to others in your app how would you approach that?

selector(“mySpecial.container”) {
background = whatever you want
}

Container panel = new Container(new ElementId(“mySpecial.container”));

…should pick up your styling and any of container’s other styling that you haven’t specifically overridden.

See this part of the wiki for more details on how precedence is resolved: