Expand/Shrink

gH/Vbox

Definition: include pGUI.e

gdx id = gHbox(gdx children={}, string attributes="", dword_seq args={})
-- or --
gdx id = gVbox(gdx children={}, string attributes="", dword_seq args={})
Description: Creates a container for composing elements horizontally or vertically, without any decorations.

children: list of the elements that will be placed in the box.
An empty list is only useful during development, and cannot be extended later (except of course at the source code level).
For more information on the attributes and args parameters see gSetAttributes().
Note that standard optional parameters behaviour is perfectly sufficient, and no paranormalisation is performed or required on either of these functions.

Returns: the identifier of the created element. The term "gBox" is occasionally used to mean "either".
pwa/p2js: Supported, kinda: but still missing is the all-important layout manager that deals with SIZE/GAP/SPACE/MARGIN/etc.
Notes: The file demo\rosetta\7guis\Booker.exw is probably the simplest/easiest thing to study/try out/resize first.

By default, the box automatically expands to fill its parent (especially when that is a gDialog), and then determines the appropriate expansion/spacing for its children. It is not actually possible to absolutely disable EXPAND on a gH/Vbox: attempts to do so depend on parent/siblings being able to cope.
Attributes:
GAP (non inheritable) Defines a (minimum) horizontal/vertical space in pixels between the children. Default: "0".

See also: MARGIN, EXPAND, SPACE
SPACE Applies independently to both horizontal and vertical expansion for both gHbox and gVbox, albeit differently.
Only has any effect in the primary orientation when EXPAND is set on the gH/Vbox (as it is by default) but not on any of the children, whereas for the cross orientation that would instead be "not on all" children. For instance a gHbox with SPACE=AROUND would:
  • split slack between children that expand horizontally (and otherwise ignore SPACE), but if none
    insert slack/horizontal padding before the first, after the last, and between the children, and
  • expand those children that do so vertically (ditto ignore for them), but add
    vertical padding above and below each of those that do not (ie expand heightwise)
  • note that each child is, obviously, treated as a single child in the cross orientation, and
  • likewise any child that expands leaves 0 for SPACE to subsequently distribute
In other words, MARGIN and GAP can be considered a baseline minimum, with SPACE distributing any excess when no child[ren] gobble that up. Quite often SPACE has no effect when the window is first shown, only swinging into effect when the dialog is resized, unless the box initially expands to some explicitly defined size or to match some sibling element.

Should a gH/Vbox not itself expand in a given direction, then it isn’t passed any slack in that direction that it has to deal with in the first place, unless the standard sole child exception applies. While you can specify fixed GAP/MARGIN, any excess space is then either all passed/split to expandable children, or spread as follows, with no way to half-and-half it, but of course each expandable child can do whatever it likes with the extra space it gets passed, and especially so when it too is also a gH/Vbox.

Set to one or two of the following values, with eg gHbox({..},"SPACE=LEFT") being the neatest code:
String Integer Description
LEFT XPG_SPACE_LEFT (0b100) all space on the left, elements are right-aligned
TOP XPG_SPACE_TOP ("", gVbox) all space at the top, elements are bottom-aligned
RIGHT XPG_SPACE_RIGHT (0b001) (default) all space on the right, elements are left-aligned
BOTTOM XPG_SPACE_BOTTOM ("", gVbox) ("") all space at the bottom, elements are top-aligned
BETWEEN XPG_SPACE_BETWEEN (0b010) space inserted between elements
AROUND XPG_SPACE_AROUND (0b111) "" and before first/after last
CENTRE XPG_SPACE_CENTRE (0b101) space inserted before first/after last only
NONE XPG_SPACE_NONE (0b000) no expansion/spacing (caveat emptor)
 
A single setting such as "SPACE=CENTRE" applies both horizontally and vertically, whereas "SPACE={BETWEEN,CENTRE}" sets the {h,v} for both gHbox and gVbox (rather than {primary,cross} aka {h,v} for the former, but {v,h} for the latter).

Note that NONE is meaningless and officially unsupported, it may for instance thwart expansion or perhaps trigger similar behaviour to sole child, on different backends or in some future release. If it has any meaning, that would be "you forgot to set this", or perhaps this container does not expand, or never has no expanding children in the primary direction, or any non-expanding in the cross. That said, it is the default, albeit behaving as RIGHT|BOTTOM, but should you want a guarantee, be explicit.

Hand on heart, XPG_SPACE_NONE only really exists so the following routines can return something sensible (esp "NONE") when it wasn’t set, presumably because it would never be used anyway. The following edge cases can be skipped on first reading. For the cross orientation and single child in the primary orientation, (BETWEEN|NONE|RIGHT|BOTTOM) behave identically, as do (AROUND|CENTRE), like they fairly obviously should. While we certainly need the RIGHT/BOTTOM bit, (NONE|RIGHT|BOTTOM) [by their lonesome] also actually behave identically even when multiple children are involved (flw). The simple truth of the matter is that the SPACE attribute is only consulted when the container is given space and it does not know what else to do with it: should it not be given excess space in the first place it should have EXPAND=NO, or perhaps RESIZE=NO. That said, when practical the layout manager is at liberty to assume you meant RIGHT|BOTTOM, rather than always/belligerently crash.

Note that gGetInt(id,"SPACE") returns the integer value[s], ie {h,v} or if same an integer, whereas gGetAttribute(id,"SPACE") returns a human-readable string, and both crash if id is not a box.

To achieve, say, three buttons on the left and two on the right, use nested gHbox() with appropriate settings:
    gdx lh = gHbox({l1,l2,l3},"EXPAND=NO"),
        rh = gHbox({r1,r2},"EXPAND=NO"),
      all5 = gHbox({lh,rh},"SPACE=BETWEEN")
-- or gHbox({gHbox({l1,l2,l3},"SPACE=RIGHT"),gHbox({r1,r2},"EXPAND=NO")})
-- or gHbox({gHbox({l1,l2,l3},"EXPAND=NO"),gHbox({r1,r2},"SPACE=LEFT")})
-- or gHbox({gHbox({l1,l2,l3}),gHbox({r1,r2},"EXPAND=NO")})
-- or gHbox({gHbox({l1,l2,l3}),gHbox({r1,r2},"SPACE=LEFT")})
Bear in mind this works on the slack left after any MARGIN and/or GAP have been applied, and note it is sometimes necessary to prevent slack being passed down to children, as well as needing three gHbox() no matter which way you do it. None of those is particularly "best", just use whichever you prefer.

See also: EXPAND, MARGIN (not PADDING), GAP.
There are also a elem.style.left/top, were you looking for them.
ORIENTATION (read-only, non inheritable): Returns "HORIZONTAL" or "VERTICAL" for a gHbox/gVbox respectively.
also EXPAND, FONT, MARGIN, SIZE.