[Lemur] Layout-related NPE

This works:
[java] backgroundPanel = new Container();
getGuiNode().attachChild(backgroundPanel);
backgroundPanel.setPreferredSize(new Vector3f(w, h, 0));[/java]
This gives me a null pointer exception:
[java] GuiLayout layout = new BoxLayout(Axis.Y, FillMode.NONE);
backgroundPanel = new Container(layout);
getGuiNode().attachChild(backgroundPanel);
backgroundPanel.setPreferredSize(new Vector3f(w, h, 0));[/java]
Exception trace:

10702 [LWJGL Renderer Thread] ERROR com.jme3.app.Application - Uncaught exception thrown in Thread[LWJGL Renderer Thread,6,main] java.lang.NullPointerException at com.simsilica.lemur.component.BoxLayout.reshape(BoxLayout.java:148) at com.simsilica.lemur.core.GuiControl.setSize(GuiControl.java:141) at com.simsilica.lemur.core.GuiControl.revalidate(GuiControl.java:307) at com.simsilica.lemur.core.GuiControl.controlUpdate(GuiControl.java:273) at com.jme3.scene.control.AbstractControl.update(AbstractControl.java:112) at com.jme3.scene.Spatial.runControlUpdate(Spatial.java:570) at com.jme3.scene.Spatial.updateLogicalState(Spatial.java:688) at com.jme3.scene.Node.updateLogicalState(Node.java:145) at com.jme3.scene.Node.updateLogicalState(Node.java:152) at org.durchholz.jme3lib.ClientApplication.update(ClientApplication.java:311) at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151) at com.jme3.system.lwjgl.LwjglCanvas.runLoop(LwjglCanvas.java:229) at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228) at java.lang.Thread.run(Thread.java:722)
What's wrong?

[EDIT] Replacing BoxLayout with a SpringGridLayout works.

BoxLayout seems to have an issue when it hasn’t been laid out before and resizing is triggered. I may deprecate this class, though, because I don’t think it does anything different than SpringGridLayout with one column now.

Anyway, I fixed this in SVN (I think)… even though I recommend SpringGridLayout instead as it’s ultimately a little more robust and a lot more powerful if you need to go further.

1 Like

Works!
Thanks.

What I like about BoxLayout is that I don’t have to keep track of the row/column number for the next addChild call.
To save on maintenance effort, it might be possible to rewrite BoxLayout as a subclass of SpringGridLayout. Maybe something like this:
[java]
public class BoxLayout extends SpringGridLayout {
public BoxLayout() {
this( Axis.Y, FillMode.EVEN );
}
public BoxLayout( Axis axis, FillMode fill ) {
super(axis, minor(axis), fill, FillMode.EVEN);
this.axis = axis;
this.fill = fill;
}
private static minor(Axis axis) {
switch(axis) {
case X: return Axis.Y; case Y: return Axis.Z; case Z: return Axis.X;
}
}
private int nextRowIndex;
public T addChild( T n, Object… constraints ) {
if( n.getControl( GuiControl.class ) == null ) {
throw new IllegalArgumentException( “Child is not GUI element.” );
}
if( constraints != null && constraints.length > 0 ) { // BTW the null check is gratuitious
throw new IllegalArgumentException( “Box layout does not take constraints.” );
}
super.addChild(nextRowIndex++, 0, n);
}
}
[/java]
I haven’t tested this at all, and it might cry for cleanup to eliminate public members that don’t make sense for a BoxLayout.

If you don’t specify a row or column for spring grid layout then it already works like box layout.

@toolforger said: if( constraints != null && constraints.length > 0 ) { // BTW the null check is gratuitious [/java]
Only slightly gratuitous since the caller could force a null to be passed.
@pspeed said: If you don't specify a row or column for spring grid layout then it already works like box layout.

Ah okay.
I thought it was hardwired to always fill up rows; at that time, I hadn’t discovered that SpringGridLayout’s definition of “row” depends on what axis is major.
When I found that out, I didn’t revisit my previous finding that without constraints, I’d get the children placed left-to-right instead of top-to-bottom.
Just as a heads-up for the kind of trap people can fall to (and which you might want to document or have me help document), I’m fine now.

@toolforger said: Ah okay. I thought it was hardwired to always fill up rows; at that time, I hadn't discovered that SpringGridLayout's definition of "row" depends on what axis is major. When I found that out, I didn't revisit my previous finding that without constraints, I'd get the children placed left-to-right instead of top-to-bottom. Just as a heads-up for the kind of trap people can fall to (and which you might want to document or have me help document), I'm fine now.

By default, SpringGridLayout is row major… so row, ie: y-axis is the major axis.

If row or column is not specified on add then row = rowCount and col = 0… so each new item gets added to the end of the major axis.

…and yes, this needs to be documented.

@pspeed said: Only slightly gratuitous since the caller could force a null to be passed.

Ah… I thought I knew everything about varargs, but I wasn’t aware that Java assumes the array case when calling varargFunction(null).
That’s the second corner case I’m seeing from this ambiguity. Ah well…

@toolforger said: Ah... I thought I knew everything about varargs, but I wasn't aware that Java assumes the array case when calling varargFunction(null). That's the second corner case I'm seeing from this ambiguity. Ah well...

It doesn’t… but you can cast the argument to an array and force the null to be passed. It’s rare but callers might get confused… and often times I do support null for internal overloading with special conditions and so I get used to having the check.

Never underestimate the users ability to misunderstand something their IDE is telling them.