Is there an 'absolute' GuiLayout? [RESOLVED]

My question is whether anyone has written a GuiLayout that just uses the children’s absolute positioning?

I realise this sounds contradictory so I’ll explain what I’m trying to do. It’s quite likely that I’m going about things the wrong way altogether.

I’m developing a tree component for lemur (To be used for displaying a substantial hierarchy in a game). Originally I developed the tree component as an extension of Panel and then laid out the text using manual positioning. I then realised I would have to do quite a bit of work to get the close/open buttons working which would be just replicating Button functionality. But I can’t use a standard button because the panel won’t lay it out with icons, borders etc.

So I thought the right approach is probably to define the tree as an extension of Container and then develop my own GuiLayout to position the elements of the tree. But easier would be if I could tell the Container to not lay out the children at all and then place them manually in the correct hierarchy. However I can’t see an easy way to do that.

Am I going about this all the wrong way?

I dont know if this can help but i have a really simple layout that uses a percentage from 0 to 100 and from that it calculates the absolute position, it also sets the size of the childs by a percentage

package com.Capuchin.Layouts;

import com.Capuchin.Map.MapCloneFunction;
import com.Capuchin.Utils.ScreenGuiUtils;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.util.SafeArrayList;
import com.jme3.util.clone.Cloner;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.component.AbstractGuiComponent;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.core.GuiLayout;
import java.util.Collection;
import java.util.HashMap;

/**
 *
 * @author Pankey
 */
public class SimpleLayout extends AbstractGuiComponent
                              implements GuiLayout, Cloneable{

    private Node menu;
    private SafeArrayList<Node> nodes;
    private HashMap<Integer,Float> posX;
    private HashMap<Integer,Float> posY;
    private HashMap<Integer,Float> posZ;
    private HashMap<Integer,Float> sizeX;
    private HashMap<Integer,Float> sizeY;
    private HashMap<Integer,Float> sizeZ;
    private ScreenGuiUtils screen;
    private int nSize=0;

    public SimpleLayout(){
        this.menu = new Node();
        this.nodes = new SafeArrayList<Node>(Node.class);
        this.posX = new HashMap<>();
        this.posY = new HashMap<>();
        this.posZ = new HashMap<>();
        this.sizeX = new HashMap<>();
        this.sizeY = new HashMap<>();
        this.sizeZ = new HashMap<>();
        this.screen = ScreenGuiUtils.getScreenGuiUtils();
    }

    @Override
    public void calculatePreferredSize(Vector3f size) {

    }

    @Override
    public void reshape(Vector3f pos, Vector3f size) {
        for(int x =0; x < this.nSize; x++){
            if(this.nodes.get(x) instanceof Panel){
                if(this.posX.get(x) != null && this.posY.get(x) != null){
                    this.nodes.get(x).setLocalTranslation(this.screen.setLeftTopSize(size.x,size.y,this.posX.get(x), this.posY.get(x), this.posZ.get(x)));
                }
                if(this.sizeX.get(x) != null && this.sizeY.get(x) != null){
                    ((Panel) this.nodes.get(x)).setPreferredSize(this.screen.setSize(size.x,size.y,this.sizeX.get(x), this.sizeY.get(x), this.sizeZ.get(x)));
                }
            }
        }
    }

    @Override
    public <T extends Node> T addChild(T n, Object... constraints) {
        if(constraints != null){
            if(constraints.length >= 2){
                if(constraints[0] instanceof Float){
                    this.posX.put(this.nSize, (Float) constraints[0]);
                }
                if(constraints[1] instanceof Float){
                    this.posY.put(this.nSize, (Float) constraints[1]);
                }
                this.posZ.put(this.nSize, 1f);
            }
            if(constraints.length == 3 || constraints.length >= 5){
                if(constraints[2] instanceof Float){
                    this.posZ.put(this.nSize, (Float) constraints[2]);
                }
            }
            if(constraints.length == 4){
                if(constraints[2] instanceof Float){
                    this.sizeX.put(this.nSize, (Float) constraints[2]);
                }
                if(constraints[3] instanceof Float){
                    this.sizeY.put(this.nSize, (Float) constraints[3]);
                }
                this.sizeZ.put(this.nSize, 1f);
            }
            if(constraints.length >= 5){
                if(constraints[3] instanceof Float){
                    this.sizeX.put(this.nSize, (Float) constraints[3]);
                }
                if(constraints[4] instanceof Float){
                    this.sizeY.put(this.nSize, (Float) constraints[4]);
                }
                this.sizeZ.put(this.nSize, 1f);
            }
            if(constraints.length >= 6){
                if(constraints[5] instanceof Float){
                    this.sizeZ.put(this.nSize, (Float) constraints[5]);
                }
            }
        }
        if(n != null){
            this.menu.attachChild(n);
            this.nodes.add(this.nSize,n);
            this.nSize++;
        }        
        return n;
    }

    @Override
    public void removeChild(Node n) {
        this.nodes.remove(n);
        this.menu.detachChild(n);
    }

    @Override
    public Collection<Node> getChildren() {
        return this.nodes;
    }

    @Override
    public void clearChildren() {
        this.nodes.clear();
        this.menu.detachAllChildren();
    }

    @Override
    public GuiLayout clone() {
        Cloner cloner = new Cloner();
        cloner.setCloneFunction(java.util.Map.class, new MapCloneFunction());
        SimpleLayout clone = new SimpleLayout();
        clone.nodes = cloner.clone(this.nodes);
        clone.posX = cloner.clone(this.posX);
        clone.posY = cloner.clone(this.posY);
        clone.sizeX = cloner.clone(this.sizeX);
        clone.sizeY = cloner.clone(this.sizeY);
        clone.screen = this.screen;
        return clone;
    }

    @Override
    public void attach( GuiControl parent ) {
        super.attach(parent);
        parent.getNode().attachChild(this.menu);
    }

    @Override
    public void detach( GuiControl parent ) {
        super.detach(parent);
        parent.getNode().detachChild(this.menu);
    }

}

it also need this class

package com.Capuchin.Utils;

import com.jme3.app.Application;
import com.jme3.asset.AssetManager;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.system.AppSettings;

/**
 *
 * @author Pankey
 */

public class ScreenGuiUtils {

    private final AppSettings settings;
    private final AssetManager assets;
    private static ScreenGuiUtils guiUtils = null;

    public ScreenGuiUtils(Application app){
        this.assets = app.getAssetManager();
        this.settings = app.getContext().getSettings();
    }

    public static void initialize(Application app){
        ScreenGuiUtils.guiUtils = new ScreenGuiUtils(app);
    }

    public static ScreenGuiUtils getScreenGuiUtils(){
        if(ScreenGuiUtils.guiUtils == null){
            return null;
        }
        return ScreenGuiUtils.guiUtils;
    }

    public Vector2f getScreenSize(){        
        return new Vector2f(settings.getWidth(),settings.getHeight());
    }

    public float QuadX(float x){        
        return setSize(new Vector2f(x,0)).getX();
    }
    public float QuadY(float y){        
        return setSize(new Vector2f(0,y)).getY();
    }
    public Vector3f setSize(float x,float y,float z){        
        return new Vector3f(QuadX(x),QuadY(y),z);
    }
    public Vector2f setSize(float x,float y){        
        return setSize(new Vector2f(x,y));
    }
    public Vector2f setSize(Vector2f vec){
        return setSize(vec, 100);
    }
    public Vector2f setSize(Vector2f vec,float rel){
        float x = ((settings.getWidth()/rel)*vec.getX());
        float y = ((settings.getHeight()/rel)*vec.getY());
        return new Vector2f(x,y);
    }
    public Vector2f setSize(float width,float height,float x,float y){
        return this.setSize(width, height, new Vector2f(x,y),100);
    }
    public Vector2f setSize(float width,float height,Vector2f vec,float rel){
        float x = ((width/rel)*vec.getX());
        float y = ((height/rel)*vec.getY());
        return new Vector2f(x,y);
    }
    public Vector3f setSize(float width,float height,float x,float y,float z){
        return this.setSize(width, height, new Vector3f(x,y,z),100);
    }
    public Vector3f setSize(float width,float height,Vector3f vec,float rel){
        float x = ((width/rel)*vec.getX());
        float y = ((height/rel)*vec.getY());
        return new Vector3f(x,y,vec.z);
    }
    public Vector3f setLeftTopSize(float width,float height,float x,float y,float z){
        return this.setLeftTopSize(width, height, new Vector3f(x,y,z),100);
    }
    public Vector3f setLeftTopSize(float width,float height,Vector3f vec,float rel){
        float x = ((width/rel)*vec.getX());
        float y = ((height/rel)*vec.getY());
        return new Vector3f(x,-y,vec.z);
    }
    public Vector3f setLeftTopSize(Vector3f size,Vector3f vec,float rel){
        float x = ((size.x/rel)*vec.getX());
        float y = ((size.y/rel)*vec.getY());
        return new Vector3f(x,-y,vec.z);
    }

    public float setMiddlePosX(float x){
        return setMiddlePos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public float setMiddlePosY(float y){
        return setMiddlePos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setMiddlePosZ(float z){
        return setMiddlePos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public Vector2f setMiddlePos(float x,float y){
        return new Vector2f(setMiddlePosX(x),setMiddlePosY(y));
    }
    public Vector3f setMiddlePos(float x,float y,float z){
        return setMiddlePos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setMiddlePos(float x,float y,float z,float width,float height){
        return setMiddlePos(new Vector3f(x,y,z),width,height,100);
    }
    public Vector3f setMiddlePos(Vector3f vec){
        return setMiddlePos(vec,0,0,100);
    }
    public Vector3f setMiddlePos(Vector3f vec,float width,float height){
        return setMiddlePos(vec,width,height,100);
    }
    public Vector3f setMiddlePos(Vector3f vec,float width,float height,float rel){
        float x = ((settings.getWidth()/2)-(width/2))+(((settings.getWidth()/2)/(rel))*vec.getX());
        float y = ((settings.getHeight()/2)-(height/2))+(((settings.getHeight()/2)/(rel))*vec.getY());
        return new Vector3f(x,y,vec.getZ());
    }
    public float setCenter_From_RawPosZ(float z){
        return setCenter_From_RawPos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public float setCenter_From_RawPosY(float y){
        return setCenter_From_RawPos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setCenter_From_RawPosX(float x){
        return setCenter_From_RawPos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public Vector3f setCenter_From_RawPos(float x,float y,float z){
        return setCenter_From_RawPos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setCenter_From_RawPos(Vector3f vec,float width,float height){
        return setCenter_From_RawPos(vec,width,height,100);
    }
    public Vector3f setCenter_From_RawPos(Vector3f vec,float width,float height,float rel){
        float x = (vec.getX()-((settings.getWidth()/2)-(width/2)))/((settings.getWidth()/2)/(rel));
        float y = (vec.getY()-((settings.getHeight()/2)-(height/2)))/((settings.getHeight()/2)/(rel));
        return new Vector3f(x,y,vec.getZ());
    }

    public float setLeftTopPosX(float x){
        return setLeftTopPos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public float setLeftTopPosY(float y){
        return setLeftTopPos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setLeftTopPosZ(float z){
        return setLeftTopPos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public Vector2f setLeftTopPos(float x,float y){
        return new Vector2f(setLeftTopPosX(x),setLeftTopPosY(y));
    }
    public Vector3f setLeftTopPos(float x,float y,float z){
        return setLeftTopPos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setLeftTopPos(Vector3f vec){
        return setLeftTopPos(vec,0,0,100);
    }
    public Vector3f setLeftTopPos(Vector3f vec,float width,float height){
        return setLeftTopPos(vec,width,height,100);
    }
    public Vector3f setLeftTopPos(Vector3f vec,float width,float height,float rel){
        float x = -(width/2) + (((settings.getWidth())/(rel))*vec.getX());
        float y = ((settings.getHeight())-(height/2))-(((settings.getHeight())/(rel))*vec.getY());
        return new Vector3f(x,y,vec.getZ());
    }

    public float setLeftTop_From_RawPosZ(float z){
        return setLeftTop_From_RawPos(new Vector3f(0,0,z),0,0,100).getZ();
    }
    public float setLeftTop_From_RawPosY(float y){
        return setLeftTop_From_RawPos(new Vector3f(0,y,0),0,0,100).getY();
    }
    public float setLeftTop_From_RawPosX(float x){
        return setLeftTop_From_RawPos(new Vector3f(x,0,0),0,0,100).getX();
    }
    public Vector3f setLeftTop_From_RawPos(float x,float y,float z){
        return setLeftTop_From_RawPos(new Vector3f(x,y,z),0,0,100);
    }
    public Vector3f setLeftTop_From_RawPos(Vector3f vec,float width,float height){
        return setLeftTop_From_RawPos(vec,width,height,100);
    }
    public Vector3f setLeftTop_From_RawPos(Vector3f vec,float width,float height,float rel){
        float x = (vec.getX()-(-(width/2)))/((settings.getWidth())/(rel));
        float y = (vec.getY()-((settings.getHeight())-(height/2)))/((settings.getHeight())/(rel));
        return new Vector3f(x,y,vec.getZ());
    }

    public boolean isInsideViewPort(Vector4f viewport , Vector2f cursor){
        return  ( viewport.x <= cursor.x )  &&
                ( viewport.y >= cursor.x )  &&
                ( viewport.z <= cursor.y )  &&
                ( viewport.w >= cursor.y );
    }

    public boolean isViewPorFullSize(Vector4f viewport){
        return  viewport.x==0.00f && 
                viewport.y==1.00f &&
                viewport.z==0.00f &&
                viewport.w==1.00f;
    }

    public Vector2f getViewPortPosition(Vector2f cursor){
        float x = cursor.getX()/settings.getWidth();
        float y = cursor.getY()/settings.getHeight();
        return new Vector2f(x,y);
    }

    public AppSettings getSettings(){
        return settings;
    }

    public int getScreenHeight(){
        return settings.getHeight();
    }

    public int getScreenWidth(){
        return settings.getWidth();
    }


}

and the map clone funtion

package com.Capuchin.Map;

import com.jme3.util.clone.CloneFunction;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.util.Map;


/**
 *
 * @author Pankey
 */

public class MapCloneFunction<T extends Map> implements CloneFunction<T> {


    public T cloneObject( Cloner cloner, T object ) {         
        try {
            T clone = cloner.javaClone(object);         
            return clone;
        } catch( CloneNotSupportedException e ) {
            throw new IllegalArgumentException("Clone not supported for type:" + object.getClass(), e);
        }
    }

    /**
     *  Clones the elements of the map.
     */    
    @SuppressWarnings("unchecked")
    public void cloneFields( Cloner cloner, T clone, T object ) {
        for(Object mapp : object.entrySet()){
            Map.Entry entry = (Map.Entry) mapp;
            if(entry.getKey().equals(entry.getValue().getClass()) ||
               entry.getKey() instanceof String ||
               entry.getKey() instanceof Integer ){
                ValueClone( cloner,  clone,  object ,entry);
            }else{
                KeyValueClone( cloner,  clone,  object ,entry);
            }
        }

    }

    /**
     *  It only clones the value of the map if it's part of the jme-clonning system 
     */  
    public void ValueClone(Cloner cloner, T clone, T object ,Map.Entry entry){
        CloneFunction f = cloner.getCloneFunction(entry.getValue().getClass());
        if(f != null ||
           entry.getValue() instanceof Cloneable || 
           entry.getValue() instanceof JmeCloneable ||
           entry.getValue().getClass().isArray() ){
            clone.put(entry.getKey(), cloner.clone(entry.getValue()));
        }
    }

    /**
     *  It clones the key and value of the map if they are part of the jme-clonning system 
     */  
    public void KeyValueClone(Cloner cloner, T clone, T object ,Map.Entry entry){
        CloneFunction f = cloner.getCloneFunction(entry.getKey().getClass());
        CloneFunction f2 = cloner.getCloneFunction(entry.getValue().getClass());

        if(f != null ||
           entry.getKey() instanceof Cloneable || 
           entry.getKey() instanceof JmeCloneable ||
           entry.getKey().getClass().isArray() 
           &&
           f2 != null ||
           entry.getValue() instanceof Cloneable || 
           entry.getValue() instanceof JmeCloneable ||
           entry.getValue().getClass().isArray()){

            clone.put(cloner.clone(entry.getKey()), cloner.clone(entry.getValue()));

        }else{
        if(f != null ||
           entry.getKey() instanceof Cloneable || 
           entry.getKey() instanceof JmeCloneable ||
           entry.getKey().getClass().isArray() ){

            clone.put(cloner.clone(entry.getKey()), entry.getValue());

        }else{
        if(f2 != null ||
           entry.getValue() instanceof Cloneable || 
           entry.getValue() instanceof JmeCloneable ||
           entry.getValue().getClass().isArray()){

            clone.put(entry.getKey(), cloner.clone(entry.getValue()));

        }else{  
            clone.put(entry.getKey(), entry.getValue());
        }
        }
        }
    }
}

this would be an example of the layout on a class that extends Panel

package com.TCUProyect;

import com.Capuchin.Layouts.SimpleLayout;
import com.Capuchin.Utils.ScreenGuiUtils;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.core.GuiLayout;
import com.simsilica.lemur.style.Attributes;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.style.StyleAttribute;
import com.simsilica.lemur.style.StyleDefaults;
import com.simsilica.lemur.style.Styles;

/**
 *
 * @author Pankey
 */
public class SimplePanel extends Panel{

    public static final String ELEMENT_ID = "simplepanel";
    protected SimpleLayout layout;
    protected Container photo;
    protected Label loadPhotoLabel;
    protected TextField loadPhotoText;
    protected Button loadPhotoButton;


    public SimplePanel(String style) {
        this(true,new ElementId(ELEMENT_ID),style);
    }

    public SimplePanel( boolean applyStyles,ElementId elementId,String style) {
        super(false,elementId,style);
        this.layout = new SimpleLayout();
        this.setLayout(this.layout);

        if( applyStyles ) {
            Styles styles = GuiGlobals.getInstance().getStyles();
            styles.applyStyles(this, elementId.getId(), style);
        }
        this.photo = new Container(elementId.child("container"),style);
        this.loadPhotoLabel = new Label("simple label",elementId.child("label"),style);
        this.loadPhotoText = new TextField("",elementId.child("textfield"),style);
        this.loadPhotoButton = new Button("load photo",elementId.child("button"),style);


        this.addChild(this.loadPhotoLabel,      0,  0,  100,  10);
        this.addChild(this.photo,               0, 10,  100,  70);
        this.addChild(this.loadPhotoText,       0, 80,  100,  10);
        this.addChild(this.loadPhotoButton,     0, 90,  100,  10);

        this.loadPhotoButton.addClickCommands(new Command<Button>() {
            @Override
            public void execute(Button source) {

            }
        });
    }

    @StyleDefaults(ELEMENT_ID)
    public static void initializeDefaultStyles( Attributes attrs ) {
        attrs.set("layout", new SimpleLayout(), false);
    }

    public <T extends Node> T addChild( T child, Object... constraints ) {
        getLayout().addChild(child, constraints);
        return child;
    }

    public void removeChild( Node child ) {
        getLayout().removeChild(child);
    }

    public void clearChildren() {
        getLayout().clearChildren();   
    }

    @Override
    public Spatial detachChildAt( int index ) {
        Spatial child = getChild(index);

        // See if this child is managed by the layout
        if( child instanceof Node && getLayout().getChildren().contains((Node)child) ) {
            removeChild((Node)child);
            return child;
        } else {
            // Just let the superclass do its thing with the 
            // unmanaged child
            return super.detachChildAt(index);
        }        
    }

    @StyleAttribute(value="layout", lookupDefault=false)
    public void setLayout( GuiLayout layout ) {
        getControl(GuiControl.class).setLayout(layout);
    }

    public GuiLayout getLayout() {
        return getControl(GuiControl.class).getLayout();
    }

    public <T extends Node> T addChild( T child, int posX,int poxY ) {
        getLayout().addChild(child, posX, poxY);
        return child;
    }

    public <T extends Node> T addChild( T child, float posX,float poxY, float sizeX,float sizeY ) {
        getLayout().addChild(child, posX, poxY, sizeX, sizeY);
        return child;
    }

    public <T extends Node> T addChild( T child, float posX,float poxY, float poxZ, float sizeX,float sizeY ) {
        getLayout().addChild(child, posX, poxY, poxZ, sizeX, sizeY);
        return child;
    }

    public <T extends Node> T addChild( T child, float posX,float poxY, float poxZ, float sizeX,float sizeY,float sizeZ ) {
        getLayout().addChild(child, posX, poxY, poxZ, sizeX, sizeY, sizeZ);
        return child;
    }

    public void setLocalGuiTranslation(Vector3f localTranslation) {
        if(ScreenGuiUtils.getScreenGuiUtils() == null){
            return;
        }
        if(this.parent != null){
            if(this.parent instanceof Panel){
                Vector3f size = ((Panel)this.parent).getPreferredSize();
                this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopSize(size,localTranslation,100));
            }else{
                this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(localTranslation));
            }
        }else{
            this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(localTranslation));
        }
    }

    public void setLocalGuiTranslation(float x,float y,float z) {
        if(ScreenGuiUtils.getScreenGuiUtils() == null){
            return;
        }
        if(this.parent != null){
            if(this.parent instanceof Panel){
                Vector3f size = ((Panel)this.parent).getPreferredSize();
                this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopSize(size.x,size.y,x,y,z));
            }else{
                this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(x,y,z));
            }
        }else{
            this.setLocalTranslation(ScreenGuiUtils.getScreenGuiUtils().setLeftTopPos(x,y,z));
        }

    }

    @Override
    public String toString() {
        return getClass().getName() + "[layout=" + getLayout() + ", elementId=" + getElementId() + "]";
    }

    //helper class to return the largest value in the output array
    public static double arrayMaximum(double[] arr) {
        double max = Double.NEGATIVE_INFINITY;
        for(double cur: arr)
            max = Math.max(max, cur);
        return max;
    }

    // helper class to find the index (and therefore numerical value) of the largest confidence score
    public int getIndexOfLargestValue( double[] array )
    {
        if ( array == null || array.length == 0 ){
            return -1;
        }
        int largest = 0;
        for ( int i = 1; i < array.length; i++ ){
            if ( array[i] > array[largest] ){
                largest = i;
            }
        }
        return largest;
    }
}

and this is the style of that panel

package com.TCUProyect;

import com.Capuchin.Utils.ScreenGuiUtils;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.texture.Texture;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.Attributes;
import com.simsilica.lemur.style.Styles;

/**
 *
 * @author Pankey
 */
public class SimpleStyle {
    public SimpleStyle(String style){
        Styles styles =GuiGlobals.getInstance().getStyles();
        Attributes attrs;

        Vector2f fullSize = ScreenGuiUtils.getScreenGuiUtils().setSize(100, 100);
        Vector2f halfHeight = ScreenGuiUtils.getScreenGuiUtils().setSize(100, 50);
        Vector2f halfWidth = ScreenGuiUtils.getScreenGuiUtils().setSize(50, 100);
        Vector2f one4th = ScreenGuiUtils.getScreenGuiUtils().setSize(50, 50);
        Vector2f portraitSmall = ScreenGuiUtils.getScreenGuiUtils().setSize(50, 80);
        Vector2f portrait = ScreenGuiUtils.getScreenGuiUtils().setSize(25, 40);
        Vector2f landScapeSmall = ScreenGuiUtils.getScreenGuiUtils().setSize(40, 25);
        Vector2f landScape = ScreenGuiUtils.getScreenGuiUtils().setSize(80, 50);

        Texture monkey = GuiGlobals.getInstance().loadTexture("Textures/Monkey.png",false,false);

        attrs = styles.getSelector("simplepanel", style);
        attrs.set("preferredSize", new Vector3f(fullSize.x,fullSize.y,1));

        attrs = styles.getSelector("simplepanel.label", style);
        attrs.set("background", new QuadBackgroundComponent (monkey));

        attrs = styles.getSelector("simplepanel.container", style);
        attrs.set("background", new QuadBackgroundComponent (monkey));

        attrs = styles.getSelector("simplepanel.textfield", style);
        attrs.set("background", new QuadBackgroundComponent (monkey));

        attrs = styles.getSelector("simplepanel.button", style);
        attrs.set("background", new QuadBackgroundComponent (monkey));

    }
}

and this is the main class

package com.TCUProyect;

import com.Capuchin.Utils.ScreenGuiUtils;
import com.jme3.app.SimpleApplication;
import com.simsilica.lemur.GuiGlobals;

/**
 *
 *
 *  @author    Pankey
 */
public class Main extends SimpleApplication {

    public static void main( String... args ) throws Exception {
        Main main = new Main();
        main.start();
    }

    @Override
    public void simpleInitApp() {        
        this.setShowSettings(false);
      //  CapuchinWindows.initialize(this);
      //  MonkeyLearning.initialize(this,CapuchinWindows.getInstance().getMonkeyFile());

        GuiGlobals.initialize(this);
        ScreenGuiUtils.initialize(this);
        GuiGlobals.getInstance().getStyles().setDefault(new SimpleStyle("simplestyle"));

        SimplePanel rec = new SimplePanel("simplestyle");
        rec.setLocalGuiTranslation(0, 0, 1);
        this.guiNode.attachChild(rec);
    } 

    @Override
    public void simpleUpdate(float tpf) {

    }
}

You mean like a tree for a file explorer, etc… strict hierarchy, expandable/collapsible nodes, etc.?

Not sure why you wouldn’t want a layout for that since you’d want all of the items to be stacked.

Im still fighting a bit with my own Gui Layout (see WiP threat) and im on the phone…

Check the options of Springgridlayout.
Container.addchild(child, row,column)
And use the Fillmode. (None, Last etc).
You may say Container main has 2 child
Subcontainer 1 and subcontainer 2.
They can be in 1 line or one row.
Each of those subcontainers may hold any element you need. E.g. buttons, text, whatever. For the right position you may add “empty” containers to the subcontainers taking the empty space space or you try working with inlets.

Edit:look to that old post Lemur style and attribute question (Container layout)

If you maybe can make a sketch it may help to understand.
I have made a menu that is using the approach above. All sizes are calculated by display size and depending on size of some elements…

Yes that’s what I mean - a strict hierarchy with the ability to open and close sub hierarchies.

I’ll work on a layout for it.

A ‘best practice’ question for you: it feels like it would be more elegant for the addChild of the layout to just take the node that will be displayed and leave the layout to create the expand / collapse buttons itself. But I can’t see any examples of layouts that create their own elements as they go. Do you have design advice on this?

Thanks for that suggestion - I’m pretty familiar with SpringGridLayout and I suspect it’s not flexible enough for my needs but I will give it a go.

With respect to the layout I’m aiming for, as pspeed mentioned in his reply it’s really just the same as any file explorer interface - a strict hierarchy with the ability to expand and collapse subhierarchies with icons next to each node.

Personally, I’d extend the GridPanel and keep track of which rows were displayed in the model. After all, only the visible rows need worrying. Then it’s just a matter of rendering them with the proper indent, vertical bars, tree branches, whatever.

You are trying to mix view with model here.

The panel is for displaying what’s in your tree model. You do have a tree model, don’t you? (If not then there is problem number 1.)

Yes I have a tree model. I’ll take your advice and keep the layout to be pure layout logic and leave the generation of expand/collapse buttons to the tree.

thanks

I ended up doing this with a BoxLayout within a BoxLayout and using insets to indent the children. That seems to work fine and is substantially simpler than a custom layout.

Thanks everyone for your help.

2 Likes