Is there a way, either regular or a dirty hack, to force Lemur elements to maintain a specific size and stay at a specific position?
The current way of placing stuff allows for too little control.
Is there a way, either regular or a dirty hack, to force Lemur elements to maintain a specific size and stay at a specific position?
The current way of placing stuff allows for too little control.
Can you be more specific?
Normally UIs use a layout that helps organize the components. There is quite a lot of flexibility in how this works. You are of course free to not use a container and just place things manually wherever you want⌠then deal with the cascade of issues whenever you want to make something slightly wider, a little to the left, wider border, etcâŚ
Itâs almost always better to figure out how to get the container + layout to work when possible.
You are largely right, containerisation is almost very helpful. But what is missing is the ability to set either max and min sizes.
That whole idea requires an iterative layout⌠which in itself requires almost 3x the code to handle than what is there now, along with complicated tree locking, and so on. (Take a look inside Java Swing sometime, easily half the code is dedicated to sorting out the tricky issues with iterative layouts.) With min/max sizing, itâs impossible to calculate sizing in one pass.
It also turns out to be rarely required with a little forethought and organization. Swing had to support dynamically resizable windows, fortunately Lemur does not⌠so often times just a few small tweaks can get a layout to work as desired.
I think in all of my Lemur UIs (for which there are many), Iâve only had one case where I wanted to know what size a window would be if a contained child was a certain size and I ultimately just ended up doing that a different (in hindsight: more correct) way.
So if we can talk about specific examples then I might be able to offer advice and/or give you ways you could abuse the current layout support to get something like min/max when iteration would not be necessary.
Or if itâs unclear why min/max implies iterative layout, I can explain that, too.
That is a good thing. When playing a game, I am hardly interested in resizing windows at all.
I think I should start with some more insight of what is happening. But somehow I even struggle to get a border around each container for debugging purposes. This code in the stylesheet does not seem to work:
def border = TbtQuadBackgroundComponent.create(
texture( name:"/com/simsilica/lemur/icons/border.png",
generateMips:false ),
1, 1, 1, 6, 6,
1f, false );
// Only for debugging. Put red border around everything.
selector( "ungamunga"){
border = border.clone();
border.color=color(1,0,0,1);
}
This is my specific example:
(I managed to color a lot of lables and containers with a random color. The coder who created the randomColor-method for ColorRGBA needs to get a Nobel Prize)
The most urgent problem is that it is to wide, I want it to be smaller. And I would like to loose the space above every text.
void makeEvolutionMenu(){
Label l;
Container window = createWindow(0,"Training","evolution.png");
Container row;
row=createRow("Running:",window);
row.addChild(new Label("Round: "),0,0);
qaRounds = row.addChild(new Label("0"),1,0);
row.addChild(new Label("Time: "),2,0);
qaTime = row.addChild(new Label("0"),3,0);
// ---------------------------------------------------------------------
row.addChild(new Label("Surv: "),0,1);
qaSurvivors= row.addChild(new Label("0"),1,1);
qaTotalGuys= row.addChild(new Label("/ 200"),2,1);
row=createRow("",window);
row.addChild(new Label("Survivors",idSmallLabel),0,0);
row.addChild(new Label("Actions",idSmallLabel),1,0);
row.addChild(new Label("Fitness",idSmallLabel),2,0);
l= row.addChild(new Label("","ungaGraphHolder"),0,1);
survivalGraph=new BarGraph(app, l, 1,app.stats.totalsPerRound, StatsData.COUNTERS.Survivors);
l= row.addChild(new Label("","ungaGraphHolder"),1,1);
actionGraph=new BarGraph(app, l, 1,app.stats.totalsPerRound, StatsData.COUNTERS.Action_Executed);
l= row.addChild(new Label("","ungaGraphHolder"),2,1);
fitnessGraph=new BarGraph(app, l, 1,app.stats.totalsPerRound, app.fittnesCriteria);
row=createRow("Fittness: ",window);
VersionedList<String> testList = enumToVList(StatsData.COUNTERS.class);
Selector lb=new Selector(testList, "ungamunga");
lb.setSelectedItem(app.fittnesCriteria.toString());
controls.put("EVO_Fittness", (Panel)lb);
row.addChild(lb,0,1);
row=createRow("",window);
qaRounds.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
qaTime.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
qaSurvivors.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
qaTotalGuys.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
}
Container createWindow(int _idx,String _title, String _icon){
Container window = new Container(new SpringGridLayout(Axis.X, Axis.Y,FillMode.Last,FillMode.Last),idWindow);
Label title=window.addChild(new Label(_title,idTitle),0);
title.setIcon(new IconComponent("Interface/Icons/"+_icon,new Vector2f(.8f,.8f),2,2,1,false ));
Container content=new Container(new SpringGridLayout(Axis.X, Axis.Y,FillMode.Last,FillMode.Last));
window.addChild(content,1);
if (GUIDBG){
title.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
content.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
}
positionWindow(window,cubeFacePos[_idx][0],cubeFacePos[_idx][1]);
guiNode.attachChild(window);
rowCount=0;
return content;
}
Container createRow(String _title,Container _window){
if (_title!=""){
Label l=new Label(_title,idRowTitle);
_window.addChild(l, 0, rowCount++ );
if (GUIDBG) l.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
}
Container row = new Container(new SpringGridLayout(Axis.X, Axis.Y,FillMode.ForcedEven,FillMode.Last),idRow);
_window.addChild(row, 0, rowCount++ );
if (GUIDBG){
row.setBorder(new QuadBackgroundComponent(ColorRGBA.randomColor()));
}
//rowCount++;
return row;
}
This the most relevant part of the stylesheet:
selector( "ungamunga" ) {
fontSize = 20
font = font("Interface/Fonts/DelinquentRegular.fnt")
color = color(0,0,0, 1.0)
}
selector( "_window", "ungamunga" ) {
insets = new Insets3f( 5, 5, 5, 10 );
background = windowBG.clone()
background.setColor(color(1.0, 0.878 ,0.816 , 1.0))
preferredSize = new com.jme3.math.Vector3f(280, 320, 1);
}
selector( "_title", "ungamunga" ) {
insets = new Insets3f( 10, 10, 20, 10 );
font = titleFont
fontSize = 24
}
selector( "_rowTitle", "ungamunga" ) {
insets = new Insets3f( 5, 10, 0, 0 );
color = color(0.102,0.078,0.078, 1.0)
font = titleFont
fontSize = 18
}
selector( "_row", "ungamunga" ) {
insets = new Insets3f( 5, 10, 0, 0 );
}
selector( "_smallLabel", "ungamunga" ) {
insets = new Insets3f( 0, 0, 0, 0 );
fontSize = 16
}
selector( "label", "ungamunga" ) {
textHAlignment = HAlignment.Left
textVAlignment = VAlignment.Top
border = border.clone();
border.color=color(1,0,0,1);
}
selector( "container", "ungamunga" ) {
textHAlignment = HAlignment.Left
textVAlignment = VAlignment.Top
}
selector( "label", "ungaGraphHolder" ) {
preferredSize = new com.jme3.math.Vector3f(90, 40, 1);
background = graphBG.clone()
}
But it will only be as wide as the things in it. Some child is forcing it to be that wide because itâs preferred size says it should be that wide. Maybe the graphs?
This may require some experimenting to figure out where the space is coming from. It could even be coming from the font itself which will be hard for Lemur to overcome. But from a brief glance through your style code and make some guesses, it seems like all of your âtext with space aboveâ might be picking up an insets with a top value > 0. âAction Executedâ seems ok. All other text seems like it has insets top=someValue, bottom=9
If it were me and randomly poking around, Iâd set insets to different values to see what happens.
That code will only put a red border around anything without a border already, I think. And if itâs just for testing, a regular colored QuadBackgroundComponent with a margin might be a better test.
I got rid of the FillMode.Last and Evens, that helped with the width of the entire interface. I also got rid of all insets and added just a few that were really necessary.
It is getting better, but still some little things that I need to change. Have a look at this picture:
The spinner controls take the size of their contents. No matter my preferredSize settings, they keep changing size.
I also can not get the numbers in the spinner to vertical align in the center.
And there is a margin - or padding in css terms - inside the selector and spinner controls that I donât know how to change.
I donât have time at the moment to look into it too deeply but my recollection is that a lot of times the spinners and selectors are slaves to the font because they use font characters for the âVâ and the +/-. I canât remember whether switching to an icon let me tighten these up for my own UIs or if using negative insets was how I did it. (Edit: to clarify, fonts in general have a ton of space above/below their characters⌠especially in the case of +/-)
âŚeither way, itâs styling on the selector/spinner buttons. You might try just making those butonsâ font size really tiny to see if thatâs it before going to more extreme measures.
Icons for the buttons of the controls would be my next question. How do I do that in the style sheet? Canât find any example with icons.
âŚand I am have a look at the font to see if there is any useless spacing.
icon = YourComponentHere
Itâs not necessarily âuselessâ spacing but fonts by their nature will have spacing. A font line has to be able to accommodate the tallest and lowest hanging characters. And for a â-â or a â+â that will look like a ton of space.
Found it outâŚ
def thumbIcon =new IconComponent( "/Interface/Icons/thumb.png", 1.0f,
0, 0, 1f, false );
selector("spinner.down.button", "ungamunga"){
insets = new Insets3f( 0, 0, 0, 0 );
PreferredSize = new com.jme3.math.Vector3f(6, 6, 1)
fontSize = 6
icon= minusIcon
text=""
}
The fontSize is necessary, otherwise the buttons will still be big.