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>AgtVBox vertical stacking container

  • <agt/agt-hbox.hpp>AgtHBox horizontal stacking container

  • <agt/agt-grid.hpp>AgtGrid row-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 gets split_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 (FOX LAYOUT_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.

  • spacing is the only between-children parameter. Default 0 (children touch). set_spacing(int) updates it and marks dirty. Negative values clamp to 0.

  • Non-widget AgtObject children 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 stacking

  • agt-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 (or AgtWindow centering) 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::draw to 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

static inline AgtVBoxBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

FOX-style message-ID chain.

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

static inline AgtHBoxBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

FOX-style message-ID chain.

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. index past MAX_TRACKS is 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 calls AgtFrame::draw.

Public Static Functions

static inline AgtGridBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

AgtPaned

class AgtPaned : public AgtFrame

Public Types

enum Orientation

Split-axis orientation. Stable values; new variants append.

Values:

enumerator AGT_PANED_HORIZONTAL

side-by-side panes, vertical sash

enumerator AGT_PANED_VERTICAL

stacked panes, horizontal sash

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_pos and 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.

long on_left_button_press(AgtObject *sender, AgtEvent *ev)
long on_mouse_motion(AgtObject *sender, AgtEvent *ev)
long on_left_button_release(AgtObject *sender, AgtEvent *ev)

Public Static Functions

static inline AgtPanedBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

FOX-style message-ID chain (see AgtFrame::ID_LAST).