[Lemur] Best practices for layout?

Just a quick request for comments. I got it to work, but I don’t know whether that’s the best way to do it; any feedback about better ways to set this up appreciated.

This is what I want (HTML terminology):

backgroundPanel; 640x480; internal padding: 5x5x5x5
* table row 1: title string, large font, centered, some padding below
* table row 2: button "start game"; width: 75%; top padding: 20 pixels
* table row 3: button "configure"; width: 75%; top padding: 20 pixels
* table row 4: button "quit"; width: 75%; top padding: 20 pixels

Here is how I did it:

Container backgroundPanel
  SpringGridLayout (Axis.Y, Axis.X, FillMode.NONE, FillMode.EVEN)
  preferred size: w = 640, h = 480
* Label("My Super Game", "main-title")
  setTextHAlignment(HAlignment.CENTER)
  style "main-title" has "font" set as GuiGlobals.loadFont("Interface/fonts/gui-large.fnt")
* Container
  setInsets(new Insets3f(topPadding = 20f, hPadding = 640 / 8, 0f, hPadding = 640 / 8))
  * Button ("start game")
(more Containers for the remaining buttons)

I’m not sure if they are equivalent since the insets for the container are for the whole container and not the children in it… if you know what I mean.

Other than that, I might have rolled the center alignment into the main title style.

I guess it would be interesting to have a version of the InsetsComponent that used percentages.

Centered alignment: Done.
That was just a remains of my attempt to work without styles (some other configuration - font, probably - wouldn’t work if done through setters but would work through styles).
I have a strong dislike for specifying property names as strings. They make it harder to nail down the data flow. Styles as defined here are exactly that, so I tried to avoid them, but… well, if they must be used, I won’t fight the framework. I’d do things more to my taste if I had the time, but I don’t, so I’m just moving forward and making a mental note for the unlikely case that I’ll ever find the time to roll my own GUI library.

Percentages… not sure that they’re very useful in this context. Button width is calculated in Java code as Math.round (w * 0.125f). If I wanted to expressly specify “75%, centered, rounded to pixel boundaries”, I’d probably write a static function to do exactly that; right now, it’s “12.5% of w on each side, rounded to pixel boundaries”, which is close enough for me. If I needed that to be closer, I’d write a function like “center(button1, 0.75f)” that wrapped the button in a Container.

Yes, I added the subcontainers just to have insets. Insets in the Buttons would not inset the button bounds.
I’m wondering whether I can get rid of the subontainers.
Without making the grid multicolumn, anyway. I abhor multicolumn layouts; split a column to accommodate something in a single row, and suddenly you have to add colspan attributes to every other row. That is about as antimodular as it gets.
The alternative would be alternating between rows with controls and rows with spacer Panels. I’m not sure that that’s a better approach.

@toolforger said: Centered alignment: Done. That was just a remains of my attempt to work without styles (some other configuration - font, probably - wouldn't work if done through setters but would work through styles). I have a strong dislike for specifying property names as strings. They make it harder to nail down the data flow. Styles as defined here are exactly that, so I tried to avoid them, but... well, if they must be used, I won't fight the framework. I'd do things more to my taste if I had the time, but I don't, so I'm just moving forward and making a mental note for the unlikely case that I'll ever find the time to roll my own GUI library.

It is a bit freewheeling even for my taste but it does have a long and illustrious tradition in everything from Swing to CSS. Though CSS will check the actual attributes there are still a hundred ways to mistype something and have a style completely missed.

Anyway, there are few alternatives, really.

@toolforger said: Percentages... not sure that they're very useful in this context. Button width is calculated in Java code as `Math.round (w * 0.125f)`. If I wanted to expressly specify "75%, centered, rounded to pixel boundaries", I'd probably write a static function to do exactly that; right now, it's "12.5% of w on each side, rounded to pixel boundaries", which is close enough for me. If I needed that to be closer, I'd write a function like "center(button1, 0.75f)" that wrapped the button in a Container.

I’d forgotten insets where “intra-border” but percentages would be useful for the case where you wanted to use it in a style definition. You don’t necessarily know the size up front. That being said, it hasn’t come up for me either. I prefer fixed sizing in most cases.

@toolforger said: Yes, I added the subcontainers just to have insets. Insets in the Buttons would not inset the button bounds. I'm wondering whether I can get rid of the subontainers. Without making the grid multicolumn, anyway. I abhor multicolumn layouts; split a column to accommodate something in a single row, and suddenly you have to add colspan attributes to every other row. That is about as antimodular as it gets. The alternative would be alternating between rows with controls and rows with spacer Panels. I'm not sure that that's a better approach.

Ah, I misunderstood the code block.

So, the interesting thing is that every Lemur GUI element (like Button, Label, etc.) is really just a wrapper around a component stack. Calling a component “background” or “insets” just makes it easier for a Label or Button user to reset parts of this stack. BUT, you can add whatever components you want to this stack… wherever. If you haven’t done a deep dive yet then I’ll explain why that’s interesting.

The component stack is setup so that each component determines how the next component will be positioned and sized.

When determining preferred size, Lemur starts at the top component and asks it for its preferred size. It then passes this down to the next component in the stack, then the next, and so on. With each component adding it’s own bit based on the previous until we reach the bottom.

When actually reshaping the element, Lemur starts at the bottom… passing in the full size for the element and position 0,0,0. That component will subtract what it wants from the size and adjust the position accordingly. This is passed on to the next component and so on, each chipping their bit away and moving the location of the next component.

So, the reason that the standard insets is “inside the background” is because setInsets() puts the insets component after the background component.

Booo… I just looked an insets is supposed to work the other way. I thought there was something wonky about this conversation. Insets tries its best to always be the first component… everything should be sized with respect to insets. (If I’d wanted the other behavior then I would have called it “margin”, by the way.)

I’ll leave the description above because I think it’s useful… but putting insets on the buttons should have given you the behavior you desired without the extra containers.