[Lemur] Centering?

Yet another silly question - what’s the best way to center a control right inside its Container?
I checked the GuiLayout descendants but came up empty. I could probably whip something up using a GuiControl, but I thought I’d ask rather than reinvent the wheel.

Oh, and by the way, the generic on AbstractNodeControl is unnecessary.

I’m on my way to another state so that my wife can have brain surgery. Will answer this when I get settled at the hotel.

Good luck for your wife! (I’m repeating myself on purpose. There’s never too much luck with surgery.)

I’m starting work on a CenteredLayout.
Places all children at its center (let them sort out Z order as they wish).
Won’t resize its children at all.
Preferred size = largest size of all children.

Will let you know what came of it.

UPDATE: I’m finding it simpler to extend SpringGridLayout. I’d have to replicate a ton of off-topic code from SpringGridLayout otherwise (or refactor that into a common base class).

@pspeed okay, I’m ready to provide patches.

Two patches for now, just to get preliminary stuff out of the way:

Added Javadoc to TbtQuadComponent.
Please review whether the part about “all coordinates are relative to the lower-left border” is correct.

Moved axis-specific operations into Axis.java.
The switch construction inside Axis.java is kind of hilarious, but it was the best way to achieve that goal, so whatever :wink:

The centering code that I wrote is based on the Axis patch, so reviewing that doesn’t make sense until the Axis patch is either rejected (in which case I’ll have to prepare a different patch) or accepted (in which case you should have the Axis patch applied to make sense of the patch).

<cite>@toolforger said:</cite> @pspeed okay, I'm ready to provide patches.

Two patches for now, just to get preliminary stuff out of the way:

Added Javadoc to TbtQuadComponent.
Please review whether the part about “all coordinates are relative to the lower-left border” is correct.

Moved axis-specific operations into Axis.java.
The switch construction inside Axis.java is kind of hilarious, but it was the best way to achieve that goal, so whatever :wink:

The centering code that I wrote is based on the Axis patch, so reviewing that doesn’t make sense until the Axis patch is either rejected (in which case I’ll have to prepare a different patch) or accepted (in which case you should have the Axis patch applied to make sense of the patch).

We just got back home… I will try to take a look this weekend.

I may have a different solution in mind for the axis range stuff. That may affect your centering code. We’ll see I guess. :slight_smile:

Bummer… that’s going to hold me up.
Do you have a one-line description of your idea? It might be faster for me to simply implement it than wait for it.

I have something else in the pipeline:

[java]class RootContainer extends Container {
/**

  • Defines the size of the root container.
  • <p>
  • Each coordinate and size is specified as a base value plus a fraction of
  • the maximum.<br>
  • For w and h, maximum is display width resp. display height (minus base).<br>
  • For x and y, maximum is display w/h minus size (minus base.<br>
  • Position and size are adjusted whenever the display size changes.
    */
    public RootContainer (
    float xBase, float xFactor,
    float yBase, float yFactor,
    float wBase, float wFactor,
    float hBase, float hFactor);
    }[/java]

The idea is to hook this thing up with AppState or a Camera and let it auto-resize with any changes to the display resolution.
You can put any Layout inside that container and have an automatically resizing GUI that way.

My current code works but isn’t properly factored out and modularized yet, so if you’re interested in pulling that in I have something to work on until you can deal with the Axis stuff.

<cite>@toolforger said:</cite> Bummer... that's going to hold me up. Do you have a one-line description of your idea? It might be faster for me to simply implement it than wait for it.

I don’t like all of the switch-cases and I don’t like putting logic into enums… but there is a missing class… some kind of AxisRange or a utility class that will handle the distribution logic or something. I have notes on it but I haven’t dug them out yet. I have a few subclasses that override the standard distribution and I think my notes cover that somehow.

<cite>@toolforger said:</cite> I have something else in the pipeline:

[java]class RootContainer extends Container {
/**

  • Defines the size of the root container.
  • <p>
  • Each coordinate and size is specified as a base value plus a fraction of
  • the maximum.<br>
  • For w and h, maximum is display width resp. display height (minus base).<br>
  • For x and y, maximum is display w/h minus size (minus base.<br>
  • Position and size are adjusted whenever the display size changes.
    */
    public RootContainer (
    float xBase, float xFactor,
    float yBase, float yFactor,
    float wBase, float wFactor,
    float hBase, float hFactor);
    }[/java]

The idea is to hook this thing up with AppState or a Camera and let it auto-resize with any changes to the display resolution.
You can put any Layout inside that container and have an automatically resizing GUI that way.

My current code works but isn’t properly factored out and modularized yet, so if you’re interested in pulling that in I have something to work on until you can deal with the Axis stuff.

I wouldn’t create a custom container for this. Just change the size of the regular container when the display size changes.

Note: the fact that there is no easy way to hook the JME/lwjgl resize events is something I plan to fix soon. I have a cobbled together thing muddying up my main class right now and I hate it that I can’t put this in an app state.

A note about my specific “resize” use-case.

…I have a general global HUD app state that has its own Container that uses the BorderLayout with SpringGridLayout containers in each of the positions. The app state then provides access to these containers.

Back to the original topic, can you describe the UI that you are using centering? It’s been on my to do list to add this sort of anchoring generally but I don’t have any use-cases of my own yet.

In Java, logic does belong into enums.
They’re essentially classes that just happen to have a fixed number of singleton instances. (Java’s restrictions on enums are there to ensure that property.)
I don’t like the switch statements myself, but to eliminate them, the enum would have to turn into a superclass and X, Y, Z would need to become singleton subclasses - not worth it. (That’s where a prototype-based language like Javascript and Python shine.) I don’t know of another way to eliminate the switches, and in the end, I don’t see any serious problem with them. Case switching is totally okay while it’s all inside the same source code, the OO approach to eliminate switches was motivated by getting rid of switches strewn throughout an entire application.

Back to the use case, it’s simple: I’m using a JFrame that the user can resize arbitrarily, and since I can’t resize the fonts, I want to keep the GUI stuff centered in a 640x480 area.
The base/factor construction is there to cover stuff like top/bottom/left/right alignment and any variation thereof that anybody might come up with.

In theory, any Node would do, but I need something that has a width and height so the centering code knows the proper offsets.
I also want to put a SpringGridLayout in it so that if I decide on a different GUI size, I don’t have to do touch every single item when redoing the layout.
So… this all looked like a use case for Container. RootContainer only because size and preferredSize must not deviate for it - size is governed by parameters and screen size, preferredSize does not come into play and in fact there should not be a Layout on the parent Node at all.
Essentially, it’s supposed to be the root node where the pixel resolution gets injected into the layout engine for the component stack.
It’s also supposed to allow multiple stacks without requiring them to interact via a common parent single SpringGridLayout. I.e. the various stacks can be shown or hidden via separate AppStates, or even use different GUI engines.

That said, it’s just the easiest/most modular way I could come up with to wrap that base/factor construction as.
I’m open to alternative suggestions.

<cite>@toolforger said:</cite> Back to the use case, it's simple: I'm using a JFrame that the user can resize arbitrarily, and since I can't resize the fonts, I want to keep the GUI stuff centered in a 640x480 area. The base/factor construction is there to cover stuff like top/bottom/left/right alignment and any variation thereof that anybody might come up with.

In theory, any Node would do, but I need something that has a width and height so the centering code knows the proper offsets.
I also want to put a SpringGridLayout in it so that if I decide on a different GUI size, I don’t have to do touch every single item when redoing the layout.
So… this all looked like a use case for Container. RootContainer only because size and preferredSize must not deviate for it - size is governed by parameters and screen size, preferredSize does not come into play and in fact there should not be a Layout on the parent Node at all.
Essentially, it’s supposed to be the root node where the pixel resolution gets injected into the layout engine for the component stack.
It’s also supposed to allow multiple stacks without requiring them to interact via a common parent single SpringGridLayout. I.e. the various stacks can be shown or hidden via separate AppStates, or even use different GUI engines.

That said, it’s just the easiest/most modular way I could come up with to wrap that base/factor construction as.
I’m open to alternative suggestions.

So your use-case for a CenterLayout and the RootContainer is the same?

Let me see if I understand. You want a container of a fixed size that centers another container using its preferred size? Or the centering might be adjustable?

Just trying to make sure I understand the use-case properly.

That’s certainly a use case I’m having, but want RootContainer and layout to extend in different directions.

RootContainer is supposed to dynamically adapt to changing resolution (fullscreen) or changing size (windowed mode). This needs to be 100% automatic since the available estate can change at runtime.

I didn’t do a CenterLayout actually - I extended SpringGridLayout so that it not only center cell contents but also place it at the left, right, top, or bottom border of the cell, or stretch the contents to fill the cell.
This is just a 90% solution. It cannot do stuff like “fill two thirds of the cell, leaving 20% free at the right side” like RootContainer can. However, since layouts are pixel-based anyway (because you can’t properly scale bitmap fonts, they get block when scaling up and ugly/hard-to-read when scaling down), the programmer will have to redo parts of the layout anyway and a 90% solution is good enough (until somebody comes up with a way to make AWT create bitmap fonts with outline borders on the fly… and creates an equivalent mechanism for Android).

Maybe I could do without RootContainer. Just fixate a Node at the screen center and center the GUI Container on top of it.
The screen-center node will automatically move around and carry the GUI container with it… centering is easy because the GUI container has a fixed size (I have settled on a minimum resolution of 640x480 and don’t scale the GUI, so I have a fixed pixel offset I can use).
Unless I’m overlooking something, this could be the route to go.

Curious… since I’m trying to fix this deficiency in JME… how are you hooking the resize?

[java]
@Override
public void update(float tpf) {
Camera camera = app.getCamera();
if (camera.isViewportChanged()) {
updateDialogPosition (camera.getWidth(), camera.getHeight());
}
super.update(tpf);
}
[/java]

Simple and effective.
One could modularize that better by adding a Swing-style event handler hook to Camera#onViewPortChange. That way, the resizing code doesn’t need to know about Cameras or Applications.

I haven’t thought much about how this would work for multi-Camera set-ups.
Stereoscopic ones would probably have two Cameras with a mostly-overlapping frustum, giving two almost-identical centers and borders. I guess any repositioning code would have to take that into account.
Not sure whether there are any other use cases for having multiple Cameras on the same scenegraph.

Oh, btw: FillMode is ambiguous: it can be misunderstood as “how grid cells are being filled”.
It might be better to describe it as StretchMode (how rows/columns are stretched). Or as ExcessSpaceDistributionMode.

<cite>@toolforger said:</cite> [java] @Override public void update(float tpf) { Camera camera = app.getCamera(); if (camera.isViewportChanged()) { updateDialogPosition (camera.getWidth(), camera.getHeight()); } super.update(tpf); } [/java]

Simple and effective.
One could modularize that better by adding a Swing-style event handler hook to Camera#onViewPortChange. That way, the resizing code doesn’t need to know about Cameras or Applications.

I haven’t thought much about how this would work for multi-Camera set-ups.
Stereoscopic ones would probably have two Cameras with a mostly-overlapping frustum, giving two almost-identical centers and borders. I guess any repositioning code would have to take that into account.
Not sure whether there are any other use cases for having multiple Cameras on the same scenegraph.

Nice.

I want to add general listener support to the context itself. Sort of like the current system listener stuff but with a few less methods. That way apps can detect show/hide and stuff like that also without having to override Application methods. (ie: could do it in an app state)

Your solution is nice because it would presumably still work if the viewport were resized. If not then that’s another interesting area of thought.

Yes, viewport resizing is very important for me since I’m running in windowed mode.
Just tested it. I had a RootContainer with 5-pixel insets on each side and a TbTBackgroundQuad. Kept its distance from the canvas borders, under all resizing operations.
It’s nice when things “just work” :slight_smile:

I’ve applied your javadoc patch. Still looking at the Axis patch as per offline messages.