AgtLayout — VBox + HBox
AgtLayout — Vertical + horizontal stacking containers
Layout containers that position widget children inside the
inner rect of an AgtFrame-backed pane. FOX FXVerticalFrame
/ FXHorizontalFrame analog.
Headers:
<agt/agt-vbox.hpp>—AgtVBoxvertical stacking container<agt/agt-hbox.hpp>—AgtHBoxhorizontal stacking container<agt/agt-grid.hpp>—AgtGridrow-major equal-cell layout: widget children flow into a fixed column count; like the boxes it only writes each child’s (x, y) — children keep their size, placed at the cell top-left<agt/agt-paned.hpp>—AgtPaned(: AgtFrame) a two-pane split container with a draggable sash: the first pane getssplit_pos(a negative value centers it), the second fills the rest; dragging the sash re-apportions. Horizontal or vertical orientation
The boxes/grid inherit AgtFrame (widgets module), so a bordered group
container is just AgtVBox::build(parent).bounds(x, y, w, h) .spacing(spacing).border(AgtFrame::AGT_FRAME_LINE).border_width(1) .create(). Padding and corner_radius are inherited too.
v0.1 layout contract
The current containers ship a deliberately minimal model:
Children retain their own width / height. The container only writes each widget child’s
(x, y). Auto-stretch (FOXLAYOUT_FILL_X-style flags) is deferred — sized children compose cleanly without it (a button declares its width at construction, VBox just positions it). Real alignment / fill flags will land alongside interactive controls (slider / edit field / scroll panel) that ask for them.spacingis the only between-children parameter. Default 0 (children touch).set_spacing(int)updates it and marks dirty. Negative values clamp to 0.Non-widget
AgtObjectchildren are skipped. A mixed tree is legal; non-widget siblings contribute no advance to the stack.
Re-flow timing
Children are positioned at the start of AgtVBox::draw /
AgtHBox::draw — BEFORE AgtFrame::draw(ctx) paints the bg +
border, and BEFORE the render walk descends into the children.
Layout mutations to each child’s set_bounds are picked up by
the walk’s translate step on the next iteration of the loop in
AgtWindow::render_subtree.
This means the bg + border paint on top of the children’s
final positions — important for AGT_FRAME_LINE borders that
need to render visually above the children rather than under
them.
set_bounds marks each child dirty via
AgtWidget::mark_dirty, which propagates up the entire parent
chain. Today (no incremental-render path) this is harmless —
mark_dirty only sets flags, doesn’t trigger an additional
redraw, and the whole tree is being repainted this frame anyway.
Forward-looking caveat. A future incremental-render
optimization that skips clean subtrees on the next frame would
be defeated by this in-frame re-dirty: every ancestor gets its
dirty_ re-set after render_subtree already cleared it. When
that optimization lands, AgtVBox/AgtHBox will need a
layout-mode set_bounds_during_layout variant that suppresses
the propagation walk. Documented as a TODO in
src/layout/agt-vbox.cpp::draw.
Nested composition
Containers compose without special-case code — each level positions its widget children inside its own inner rect:
auto *outer = new AgtHBox(&window, 100, 100, 800, 500,
/*spacing=*/16);
auto *col1 = new AgtVBox(outer, 0, 0, 200, 480);
auto *col2 = new AgtVBox(outer, 0, 0, 200, 480);
new AgtLabel(col1, 0, 0, 180, 80, "Col 1 row 1");
new AgtLabel(col1, 0, 0, 180, 80, "Col 1 row 2");
new AgtButton(col2, 0, 0, 180, 60, "Col 2 row 1");
The render walk visits outer → col1 → its children → col2 → its children, with each container’s draw running just before its own children are visited.
Module layout
agt-vbox.hpp/agt-vbox.cpp— vertical stackingagt-hbox.hpp/agt-hbox.cpp— horizontal stacking
Future additions to this module: a AgtGrid for two-axis
layouts, layout flags for stretch / alignment, and possibly a
AgtFlexBox once a real consumer needs CSS-flex semantics.
See the API Reference section for the full surface.
API Reference
AgtVBox
-
class AgtVBox : public AgtFrame
Subclassed by AgtMovableFrame
Public Functions
-
AgtVBox(AgtWidget *parent, int x, int y, int w, int h, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
Construct a vertical-stacking container at the given parent-local bounds. spacing is the gap (in pixels) inserted between adjacent widget children; default 0 (children touch). border and border_width are forwarded to AgtFrame, defaulting to NONE / 0 (a bare VBox is invisible — useful as a pure layout container; opt into a border by passing one).
-
explicit AgtVBox(AgtWidget *parent, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
Geometry-free ctor: no x/y/w/h — the box sizes to its children via
natural_*()and an outer container (orAgtWindowcentering) places it. Both axes start AUTO. Unambiguous with the explicit ctor (that one needs >= 5 args);new AgtVBox(parent, 12)is a spacing-12 auto box.
-
int natural_width() const noexcept override
Natural size: tall enough to stack the children’s natural heights + gaps, wide enough for the widest child, plus the frame chrome.
-
int natural_height() const noexcept override
-
inline int spacing() const noexcept
-
void set_spacing(int s) noexcept
-
void draw(AgtDrawContext &ctx) override
Position widget children top-to-bottom inside the inner rect, then call
AgtFrame::drawto paint the bg + border. Layout-before-paint ordering keeps the bg / border on top of the children’s final positions when the render walk reaches them.
Public Static Functions
-
AgtVBox(AgtWidget *parent, int x, int y, int w, int h, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
AgtHBox
-
class AgtHBox : public AgtFrame
Subclassed by AgtMenuBar, AgtToolBar
Public Functions
-
AgtHBox(AgtWidget *parent, int x, int y, int w, int h, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
Construct a horizontal-stacking container at the given parent-local bounds. spacing is the gap (in pixels) inserted between adjacent widget children. Other args mirror AgtVBox.
-
explicit AgtHBox(AgtWidget *parent, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
Geometry-free ctor (mirrors AgtVBox): no x/y/w/h — sizes to children via
natural_*(). Both axes start AUTO.
-
int natural_width() const noexcept override
Natural size: wide enough to lay the children’s natural widths + gaps, tall enough for the tallest child, plus the frame chrome.
-
int natural_height() const noexcept override
-
inline int spacing() const noexcept
-
void set_spacing(int s) noexcept
-
void draw(AgtDrawContext &ctx) override
Position widget children left-to-right inside the inner rect, then call
AgtFrame::draw.
Public Static Functions
-
AgtHBox(AgtWidget *parent, int x, int y, int w, int h, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
AgtGrid
-
class AgtGrid : public AgtFrame
Public Functions
-
AgtGrid(AgtWidget *parent, int x, int y, int w, int h, int columns = 1, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
-
inline int columns() const noexcept
-
void set_columns(int n) noexcept
-
inline int spacing() const noexcept
-
void set_spacing(int s) noexcept
-
void set_col_weight(int col, int weight) noexcept
Per-track stretch weights (Qt
setColumnStretch/setRowStretch). A track’s extent ∝ its weight; all-equal (the default, weight 1) is the original equal-cell grid.indexpastMAX_TRACKSis ignored (that track stays weight 1). A weight < 0 clamps to 0.
-
void set_row_weight(int row, int weight) noexcept
-
void draw(AgtDrawContext &ctx) override
Position widget children row-major into weighted cells; a child with
set_fill_cross(true)fills its cell, otherwise it keeps its size at the cell’s top-left. Then callsAgtFrame::draw.
Public Static Functions
-
AgtGrid(AgtWidget *parent, int x, int y, int w, int h, int columns = 1, int spacing = 0, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
AgtPaned
-
class AgtPaned : public AgtFrame
Public Types
Public Functions
-
AgtPaned(AgtWidget *parent, int x, int y, int w, int h, Orientation orient = AGT_PANED_HORIZONTAL, int split_pos = -1) noexcept
Construct a split container at the given parent-local bounds. orient picks the split axis. split_pos is the initial size of the first pane along that axis; a negative value centers the sash for the constructed bounds. The paned has no border/padding by default so the panes fill it edge-to-edge.
-
inline Orientation orientation() const noexcept
-
void set_orientation(Orientation o) noexcept
-
int split_pos() const noexcept
Size of the first pane along the split axis (pixels). Always the clamped, in-bounds value.
-
void set_split_pos(int pos) noexcept
-
inline int sash_width() const noexcept
Thickness of the sash strip along the split axis. Default 6.
-
void set_sash_width(int w) noexcept
-
inline int min_pane_size() const noexcept
Smallest size either pane may be squeezed to by the sash. Default 20. Clamps both
set_split_posand drags.
-
void set_min_pane_size(int m) noexcept
-
inline bool resizable() const noexcept
Enable / disable sash dragging (default enabled). Disabling mid-drag also ends any in-progress drag + releases capture.
-
void set_resizable(bool on) noexcept
-
inline bool dragging() const noexcept
True between a sash press and its release.
-
void draw(AgtDrawContext &ctx) override
Position the two pane children around the sash, paint the background, then the sash strip + grip on top.
Public Static Functions
-
AgtPaned(AgtWidget *parent, int x, int y, int w, int h, Orientation orient = AGT_PANED_HORIZONTAL, int split_pos = -1) noexcept