mabe02 / lanterna

Java library for creating text-based GUIs
GNU Lesser General Public License v3.0
2.25k stars 242 forks source link

LinearLayout always uses northwest gravity #349

Open ToxicFrog opened 6 years ago

ToxicFrog commented 6 years ago

Specifically, it's often convenient to do something like this:

Panel [LinearLayout VERTICAL]
┣Panel [LinearLayout HORIZONTAL] [FILL]
┃┣Label
┃┣Label
┃┗...
┗Panel [LinearLayout HORIZONTAL] [FILL]
  ┗...

To create a vertical list of horizontal lines of labels that can all be independently themed, etc.

The problem is that, in each line, all of the individual labels will be left aligned, because LinearLayout starts placing its children from the upper left.

If you want right justification, you can change that FILL to END, but then the horizontal Panels don't span the entire line, which means you can no longer theme the entire line, just the part covered by the Panel.

Swap HORIZONTAL and VERTICAL and this describes the behaviour when you have a row-of-columns rather than a column-of-rows too.

avl42 commented 6 years ago

I think, the first obstacle is that a Linearlayout doesn't consume more space along its orientation than it needs for the preferred sizes of the components. Anything you see right of the rightmost item in a line is really the rest of the row-panel shining through.

What seems to be really wished for is another "LinearLayoutData" to decide on what to do with extra space along the orientation axis. And that's certainly not trivial, because admittedly the current system - of components only specifying a single preferredSize() - doesn't really help in the decision of how to best share the extra space among the components in the case of "FILL".

A simpler approach could be to just add a "reverseGravity" flag to the LinearLayout, and if that is set, it would layout the components right-to-left (or bottom-to-top).

ToxicFrog commented 6 years ago

A simpler approach could be to just add a "reverseGravity" flag to the LinearLayout, and if that is set, it would layout the components right-to-left (or bottom-to-top).

Yeah, that would be sufficient for my use case -- at the moment I have a workaround by creating a two-cell GridLayout where one cell contains the LinearLayout and the other cell contains a blank label set to FILL and with both h-expand and v-expand set, but that's pretty ugly.

avl42 commented 6 years ago

I've thrown together something, but it might still need some polishing. At least it will need some usage in the test-suite (LinearLayoutTest.java -- that is imho pretty in need of some improvement, anyway, even apart from this new feature... at least, the window should use Hint.EXPANDED, so one sees what happens with excessive available space, and the labels should be distinguishable).

URL: https://github.com/avl42/lanterna/blob/llrev/src/main/java/com/googlecode/lanterna/gui2/LinearLayout.java

mabe02 commented 6 years ago

You should be able to use GridLayout to achieve this as it has more sofisticated layout constaints, but I agree it would be nice if LinearLayout could do it too. I'll review the code again.

mabe02 commented 6 years ago

@ToxicFrog are you just looking to right-align the whole horizontal label row or is it more about picking which of the labels that eats the leftover space rather than always the last one?

avl42 commented 6 years ago

"about picking which of the labels that eats the leftover space rather than always the last one?"

That isn't accurate! If a Panel is sized 30 columns in width, and I add to it a horizontal LinearLayout and two labels of 10 chars each, then the remaining 10 columns will not be eaten by any of the nested labels. The sought option is rather: will uncovered part of the Panel shine through on right side or on left side?

Given that the usecase seems rare (even if real) and that a workaround with GridLayout seems to exist, I'm not likely to push my patches any further (that means: not turning them into a PR). If @ToxicFrog convinces us some more about usefulness of space left on left side, I'm all open to it.

ToxicFrog commented 6 years ago

You should be able to use GridLayout to achieve this as it has more sofisticated layout constaints, but I agree it would be nice if LinearLayout could do it too. I'll review the code again.

Yeah, I mentioned that I'm doing that. GridLayout with a blank label at the top/left set to fill its cell and claim all available space. It's not pretty, but it works.

are you just looking to right-align the whole horizontal label row or is it more about picking which of the labels that eats the leftover space rather than always the last one?

Thank you for pointing that out; it's actually the latter -- just right/top aligning isn't enough because that just changes which part of the underlying panel "shines through", as @avl42 points out. So yeah, what I'm really after is some equivalent to the GridLayout's vfill/hfill and vexpand/hexpand settings, that let me say "the LinearLayout should expand to fill the entire space horizontally or vertically, and any "bonus" space acquired this way should be split between the following components: ..."

I'm not actually sure I have a use case where I would want to right or top justify, without filling the available space, but where I'm not already using a GridLayout for other reasons. If I do think of one I'll post it here. :P

avl42 commented 6 years ago

Ben Kelly notifications@github.com schrieb am Mo., 12. März 2018, 02:14:

So yeah, what I'm really after is some equivalent to the GridLayout's vfill/hfill and vexpand/hexpand settings, that let me say "the LinearLayout should expand to fill the entire space horizontally or vertically, and any "bonus" space acquired this way should be split between the following components: ..."

So, you want a LinearLayout with the power of the GridLayout. Why is using the GridLayout not the straightforward thing, but a workaround? What's the downside of GridLayout, that an enhanced LinearLayout would not have?