Y-axis distribution in SpringGridLayout seems to be wrong

I’m playing with layouts and it is very common to have the exception:

java.lang.IllegalArgumentException: Size cannot be negative:(9.0, -0.5, 0.0)
	at com.simsilica.lemur.core.GuiControl.setSize(GuiControl.java:299)
	at com.simsilica.lemur.component.SpringGridLayout$Entry.setSize(SpringGridLayout.java:537)

After some tries I could verify that when a SpringGridLayout (with defaults configs) has some childrens there is a size where the owner container can’t resize smaller without throwing the exception. The x-axis seems to be behaving well but the minimum y axis size is always (when having 2 children) the difference between both children + 2 (what seems to be some size set by the present components, eg: backgrounds adds +0.5).

Is this normal?, if so, why?

Edit: If I remember well, the layout always uses the index to get the axis values based on the major/minor axis so I suppose that it will give the same issues if they are switched (with x instead of y).

Some test code:

public class TestLemurLayouts extends SimpleApplication {

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

    Container container;

    public void simpleInitApp() {
        GuiGlobals.initialize(stateManager.getApplication());
        BaseStyles.loadGlassStyle();
        GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");

        container = new Container(new SpringGridLayout(Axis.Y, Axis.X, FillMode.Even, FillMode.Even));
        container.setPreferredSize(new Vector3f(100, 100, 1));

        test1();
    }

    private void test1() {

        Panel a = container.addChild(new Panel("glass"));
        Panel b = container.addChild(new Panel("glass"));

        a.setPreferredSize(new Vector3f(10, 10, 1));
        b.setPreferredSize(new Vector3f(20, 20, 1));

        // If set, crashes starts at 9 instead of 11.
//        container.setBackground(null);

        // Doesn't crash
//        container.setPreferredSize(new Vector3f(12, 12, 1));
        // Crashes: java.lang.IllegalArgumentException: Size cannot be negative:(9.0, -0.5, 0.0)
        container.setPreferredSize(new Vector3f(11, 11, 1));

        setCommons(container);
    }

    private void test2() {

        container.addChild(new Panel()).setPreferredSize(new Vector3f(20, 20, 1));
        container.addChild(new Panel()).setPreferredSize(new Vector3f(20, 20, 1));
        container.addChild(new Panel()).setPreferredSize(new Vector3f(20, 20, 1));

        setCommons(container);
    }

    private void test3() {
        container.addChild(new Panel()).setPreferredSize(new Vector3f(80, 80, 1));
        container.addChild(new Panel()).setPreferredSize(new Vector3f(100, 100, 1));


        // Crashes: java.lang.IllegalArgumentException: Size cannot be negative:(19.0, -0.5, 0.0)
        container.setPreferredSize(new Vector3f(21, 21, 1));

        setCommons(container);
    }

    private void setCommons(Panel container) {
        container.setLocalTranslation(new Vector3f(500, 500, 0));
        guiNode.attachChild(container);
    }
}
1 Like

My guess is that you are setting it too small for the components + borders/backgrounds/etc in it. You can’t do that with the spring layout. It must always be at least as big as its natural preferred size.

It’s not swing that will iteratively layout your stuff 100 times until it settles on a value. There is one pass.

1 Like

But it seems to work with the minor-axis. It also fully works if the children are all the same size. Why to limit it to that minimal “natural” size?. And in the case of that limitation (what I repeat I can’t understand because of the minor axis seems to be correctly calculated), wouldn’t it be better to Just set the size to 0 (if size < 0) instead of throwing an exception? (the problem would be more visual and less abstract, it’s really Hard to find where is the issue when you have a complex interface. The point is to create visual things, if something is wrong it will be visually perceived (if not it isn’t an issue) and it tracking woulf be easier.

1 Like

size < 0 is a layout error on your part. You’ve asked things to be smaller than they can be.

If I allow negative size then I’d get a bunch of bug reports about “my quads are invisible”… because negative size would invert the triangles and you’d be looking at the backs of them.

Without having time to run your code myself, I cannot say why one axis works and the other doesn’t except to say that by default maybe the layout flow is different?

But a negative size 100% means that you’ve asked the panel to be smaller than it should be.

If you want absolute positioning then maybe don’t use a layout at all, I guess.

1 Like

What do you mean with different flow?[quote=“NemesisMate, post:1, topic:39028”]
new SpringGridLayout(Axis.Y, Axis.X, FillMode.Even, FillMode.Even));
[/quote]

I’m always setting the same size for x and y in the tests. The container layout is the said above. However, the exception thrown is always in the form: Size cannot be negative:(19.0, -0.5, 0.0). Where x is always >= 0 and y < 0 (it makes no sense, they both should always be the same size).

I don’t want absolute positioning, I want the springridlayout functionality but allowing to set the container any size without crashing, just resizing the children to be smaller (even if any of them must go to 0 size an disappear).

Yes, it could not be the better idea, maybe the springgridlayout could set panels to 0 size instead if the calculations says that they must be negative? (it would be ideal for me xD, however, if it still not be the common desired behavior I have no problem with extending it)

1 Like

If you are ok with having your preferred sizes as 0 then just force set them all to 0.

I can’t shrink elements less than their preferred size because I don’t know what that will do. How do you shrink fixed size text for example?

Anyway, something in your layout must be different vertically versus horizontally. I didn’t look in detail but if you use any Labels/Buttons then they will have text, etc. which definitely changes the major/minor sizing and so on.

Something in your layout is asymmetrical. It is quite literally the exact same lines of code that calculate both.

1 Like

It’s not much code, I’m sure they are both with the same size and the same component structure (just a panel).

When setting expansion to “Even” and you expand the container, making it bigger than the child’s preferred size this child gets expanded evenly. When it gets smaller, this child shrinks. If something has a fixed size bigger and can’t shrink, it just overflows (what is visually obvious) without any crash. All this is fine and good and it does work when children are all the same size. So this is currently happening and I’m happy with it.
The problem isn’t that it doesn’t shrink, the problem is when the children sizes are different and it tries to set a negative size. I’m repeating myself now but it doesn’t seems to be a good behavior:

  1. If children are all the same size → it shrinks till 0 size (what can’t be shrank just overflows. It happens with labels).
  2. If children are different size → it crashes (with a negative size equals to the difference in the major axis between the 2 childs).

In 2 it shouldn’t crash, just do like 1. The container’s size should be evenly distributed shrinking the childs when needed. No negative size should be trying to be set.

1 Like

Yes, but one is above the other… so naturally the layout of y will take more space than the layout of x.

I mean, I could do it. I hope you stay around to answer all of the “I can’t see my stuff” questions because there will be more of them for sure… and they are much harder to diagnose than the exception is.

In the end the net result is the same: you will end up writing code to keep the user from resizing the window too small.

1 Like

I also hope to be here. However, if you are convinced is more a problem than a solution then just stay with it. It’s not hard to me to just have my version.

Not in my case. I’m currently doing some tweens with layouts. I want some animations where the layout sizes changes (scales are not suitable for the effect I look for) and I came up to this issue. However, I’ve experienced that exception a lot and I think it would be more friendly if it weren’t an exception (I can be wrong).

1 Like

@pspeed, after trying to make some UI on android and use them on different devices I could see a bigger problem with the “exception approach”. To make good-looking UIs the density must be taken in account to many things to be compatible with all the screens, so an UI that is working perfectly on a device may crash on another (if it is perfectly done it won’t crash on any, of course, but it is really hard to ensure it will). To an end-user it is better to have a visual issue than a crash so this can be a real problem when working in android.

So, I came to a solution that support both: adding a global value that can be set to enable/disable this kind of crash.

Another area where there is need to 0-crashes would be a lemur-editor (it would be really horrifying if every time you try to resize a panel it crashes).

No imposing anything here, I just wanted to share my thoughts and current solution.

Another way to do it is to put all of your UI under a root node and sample the screen “size” at the beginning of the app so that you can give your UI a workable scale at the beginning.

For my desktop apps, I usually pick a “max” screen size that I want my UI to fill and then don’t scale up but scale down if the screen is smaller.

I don’t have a problem removing the exception support when I get time. I’m not going to start polluting the code with a bunch of odd global variables if I can avoid it, though.