BoxLayout with FillMode.Last causes overlapping layout

I have some UI that shows the ships under the players command and some details about them

image

I’m trying to add rows under each fighter showing the fighters that ship carries. But I’m finding those rows aren’t laying out correctly (and are in fact over the top of later ships). I’ve created a simplified version of this UI

public class LemurTest extends SimpleApplication{

    @Override
    public void simpleInitApp(){

        // Initialize the globals access so that the default
        // components can find what they need.
        GuiGlobals.initialize(this);
        BaseStyles.loadGlassStyle();
        GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");

        Container myWindow = new Container();
        guiNode.attachChild(myWindow);

        BoxLayout topLayout = new BoxLayout(Axis.Y, FillMode.Last);
        myWindow.setLayout(topLayout);
        myWindow.setLocalTranslation(300, 600, 0);

        for(int ship=0;ship<2;ship++){

            Container overallShipContainer = new Container();
            BoxLayout overallShipLayout = new BoxLayout(Axis.Y, FillMode.Last);
            overallShipContainer.setLayout(overallShipLayout);
            myWindow.addChild(overallShipContainer);

            Container mainShipContainer = new Container();
            BorderLayout mainShipLayout = new BorderLayout();
            mainShipContainer.setLayout(mainShipLayout);
            overallShipContainer.addChild(mainShipContainer);

            mainShipContainer.addChild(new Label("___________Silhouette_____________"), BorderLayout.Position.Center);
            mainShipContainer.addChild(new Label("Select |"), BorderLayout.Position.West);
            mainShipContainer.addChild(new Label("| Command buttons"), BorderLayout.Position.East);
            mainShipContainer.addChild(new Label("Ship name " + ship), BorderLayout.Position.South);

            for(int fighter = 0; fighter<2;fighter++){
                overallShipContainer.addChild(new Label("Fighter " + fighter +" of ship " + ship));
            }
        }
    }

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

The layout ends up wrong, with the ship section too big and the fighters of each ship over the top of later ships.

I realised while writing this question that if I change the FillMode from Last to None the problem goes away and it looks the way I wanted it to look

image

This sorts my problem but I still don’t really understand what’s going on. Isn’t FillMode.Last just supposed to give spare space to the last element? (and I didn’t expect to have spare space, so didn’t really worry about it)

(I’m on lemur 1.16.0)

It looks like BoxLayout does not support all of the fill modes:

As I recall, BoxLayout was one of the first layouts added to Lemur and when SpringGridLayout was written, for me it completely took over any BoxLayout use-cases I had. Consequently, it got less love than the other layouts.

These days, I personally only use BorderLayout and SpringGridLayout for everything. I’ve considered deprecating and removing BoxLayout but I know some users like it and I’ve never really drilled into why. From my perspective, a properly setup SpringGridLayout will do the same as a BoxLayout… but I may be missing something. (And it could be the fact that it’s non-obvious is the real issue.)

I can see how SpringGridLayout could be used to just produce a row, or a column, but I think when I originally looked at it it seemed too complicated for that (with major and minor columns). The documentation even says “SpringGridLayout is a little more complicated.” Maybe if the documentation started with a simple “here is how to do a column” and “here is how to do a row” people wouldn’t be so quick to look for something simpler. (It does look like anyone can edit the documentation so I’d be happy to do that if you’re happy?).

What do you think to the BoxLayout throwing an IllegalStateException when the fill mode is not in one of those modes, rather than the current getting in a weird state behaviour

I’d rather have it support all of the modes, really. But if someone wants to submit a PR either way, I’ll be happy to look at it.

Yeah, it’s non-obvious which is what I always suspected the issue is. It’s a “little more complicated” because it will do a LOT more than the other layouts.

new BoxLayout(Axis.Y, FillMode.Even)
...equivalent to:
new SpringGridLayout(Axis.Y, Axis.X, FillMode.Even, FillMode.*)
...from a simple use perspective:
addChild(row1)
addChild(row2)
...and so on.

Those should produce the same layout results. Switch the Axis parameters for horizontal layouts.

The different is that if you decide you want to add some additional columns/rows then it is simple. Like in the above example, if you decide you want to add labels to your rows:

addChild(new Label("Row 1"));
addChild(row1, 1); // second column
addChild(new Label("Row 2"));
addChild(row2, 1); // second column

…and that’s why I’ve all but abandoned BoxLayout in my own projects.

But the “this makes more sense for this” feature of BoxLayout should not be discounted, I guess.

Just as a thought (given people like the simplicity of thinking about BoxLayouts). I think the whole of BoxLayout could just be swapped out for this:

public class BoxLayout extends SpringGridLayout{

    /**
     * Just for clone, could alternatively make protected in SpringGridLayout
     */
    Axis axis;
    FillMode fill;

    public BoxLayout() {
        this(Axis.Y, FillMode.Even);
    }

    public BoxLayout(Axis axis, FillMode fill) {
        super(axis, axis == Axis.X?Axis.Y:Axis.X,fill, FillMode.None);
        this.axis = axis;
        this.fill = fill;
    }

    @Override
    public BoxLayout clone() {
        return new BoxLayout(axis,fill);
    }
}

Or possibly this could be static methods in SpringGridLayout; SpringGridLayout#rowLayout and SpringGridLayout#columnLayout. Just to avoid having to think about major and minor axes and if the rows and columns have swapped round

Yeah, it’s an idea.

These classes all could use a little standardization love… for example, SpringGridLayout should probably at least expose the current axis and fill mode with getters. I also think these classes predate JmeCloner which they could definitely take advantage of to do more correct cloning, etc…