AgtComposites — menus + modal dialogs
AgtComposites — multi-widget compositions
The composite module hosts widgets whose internal structure is a TREE of other widgets, not a single rendering element. Examples shipped here:
<agt/agt-message-box.hpp>—AgtMessageBoxmodal confirmation (Dialog + Label + 1-2 Buttons). Its card is anAgtMovableFrame, so the box is draggable by its title bar.<agt/agt-prompt-dialog.hpp>—AgtPromptDialogmodal input (Dialog + Label + EditField + 2 Buttons); also a draggable card.<agt/agt-theme-dialog.hpp>—AgtThemeDialogmodal theme picker (Dialog + Label +AgtListBoxof the built-in theme names + OK/Cancel). Live preview: selecting a row applies the theme toAgtPalette::current()andrestyle_tree()s both the target window and the dialog; OK keeps the pick, Cancel/Escape reverts. Persistence is the caller’s job (AgtSettings), likeAgtPromptDialogreturns its text.<agt/agt-progress-dialog.hpp>—AgtProgressDialoga “working…” dialog (Dialog + Label +AgtProgressBarorAgtSpinner+ optional Cancel). Determinate (set_progress) or indeterminate (spinner, self-animated viastart()aftershow(), or stepped withadvance()); modal (run) or modeless (show).<agt/agt-menu-bar.hpp>—AgtMenuBar(HBox of top-level MenuItem titles; owns the popups).<agt/agt-menu.hpp>—AgtMenu(Frame with MenuItem rows as DIRECT children, laid out eagerly — see note below). Supports cascading submenus (add_submenu→ an owned child popup): opening one transfers mouse-capture + keyboard focus to the child and hands it back up the parent chain on dismiss, so an arbitrary-depth menu tree drives off the single window capture slot. Casts a soft drop shadow so it reads as floating over the content it covers.<agt/agt-scroll-frame.hpp>—AgtScrollFrameclipped scrolling viewport (: AgtFrame). Owns acontent()child positioned at(inner_offset - scroll)plus a vertical (+ optional horizontal)AgtScrollBar; overrideswants_child_clipso the render walk + hit-test confine children to the viewport (scrolled-out content is neither painted nor clickable). The first consumer of the coordinate-model primitives — it scrolls via the render-walk translate, not bespoke math.<agt/agt-combo-box.hpp>—AgtComboBoxnon-editable selection combo (: AgtFrame). A collapsed field + a dropdown that REUSESAgtListBox, shown with theAgtMenureparent / capture / dismiss model: on open the list reparents to the window (paints on top) and the combo grabs the capture; arrow keys browse, a click / Enter commits (emits the combo’sAGT_SEL_COMMAND), an outside click or Escape dismisses.<agt/agt-tab-view.hpp>—AgtTabViewnotebook tab container (: AgtFrame): a strip of tab buttons + a stack of page widgets, only the active one shown viaAgtWidget::set_visible(no reparenting — pages stay children, so ownership is the plain cascade).add_tab(label)returns the page to populate; a tab button’s command (message_id == tab index) swaps the active page.<agt/agt-tool-bar.hpp>—AgtToolBarcommand-button strip (: AgtHBox):add_button(label, target, id)(flat button) /add_separator()(vertical divider); HBox does the left-to-right layout.<agt/agt-spin-box.hpp>—AgtSpinBoxinteger field (: AgtFrame) with owned+/-stepper buttons that adjust the value bystep, clamped to[min, max]; emits SEL_COMMAND on a real change.<agt/agt-form.hpp>+<agt/agt-form-browser.hpp>— the declarative settings-forms framework (an HII-style “BIOS setup” builder for AGT’s own apps).AgtFormFieldis a POD descriptor (SECTION / BOOL / INT / ENUM / TEXT / PASSWORD / ACTION, with a typed-pointer or get/set binding, per-type constraints, defaults, and optionalvisible_if/enabled_ifpredicates — the C++ replacement for HIIsuppressif/grayoutif, no expression VM); a consumer declaresstatic const AgtFormField fields[] = {…}.AgtFormwraps the array + a fixed-capacity working copy and the commit model (save/discard/load_defaults; bindings change only on Save).AgtFormBrowser(: AgtFrame) maps each field to its editor widget (checkbox / spin box / combo / edit / password / button), lays them out as labeled rows inside an ownedAgtScrollFrame, drives the working copy, and carries the Restore-Defaults / Discard / Save button row. The logic/render split mirrors EDK2’sSetupBrowserDxe/DisplayEngineDxe.<agt/agt-expander.hpp>—AgtExpandercollapsible section (: AgtFrame): a clickable header (disclosure triangle + title) toggles an ownedcontent()viaset_visibleAND shrinks the expander to header height (re-flows in anAgtVBox).<agt/agt-tree-view.hpp>—AgtTreeViewvirtualized collapsible tree (: AgtFrame) over an ownedAxlNTreemodel (per-node payload atnode->data): flattens the visible nodes each change and paints only the viewport window (O(page)); indented rows + disclosure triangles; click / keyboard select + expand/collapse; reusesAgtScrollBar.<agt/agt-file-dialog.hpp>—AgtFileDialogmodal filesystem browser (: AgtDialog): a draggable card with a path label, “Up” button, anAgtListBoxof entries read via axl-fs ([name]for dirs), and Open / Cancel. Open navigates into a dir or picks a file (path()afterRESULT_OK). The list / Open / Up commands use ids the dialog’s own map catches so they don’t hitAgtDialog’s dismiss range; Cancel (id 0) falls through todismiss(0).<agt/agt-main-window.hpp>—AgtMainWindow(: AgtWindow) the docking-chrome window: reserves edge bands for a menu bar (top), tool bar (top), and status bar (bottom), with the remaining region as the content area — the FOXFXMainWindowshape (axeditis built on it)<agt/agt-search-entry.hpp>—AgtSearchEntry(: AgtEditField) an edit field with a magnifier glyph + a clear (✕) gutter button; the find-bar input idiom<agt/agt-password-field.hpp>—AgtPasswordField(: AgtEditField) a masked field (starts inAGT_ECHO_PASSWORD, secret kept off the clipboard) with an optional reveal (eye) gutter button that toggles the masking; the credentials-form input idiom<agt/agt-table-base.hpp>—AgtTableBase(: AgtFrame) the shared substrate for theAgtTable*family: the column schema + per-cell-text row model, the virtualized viewport geometry (paints only the visible window, O(page)), the ownedAgtScrollBar, the command-target plumbing, and the draw skeleton (frame + header stripper-column clipped text). Three protected hooks (
paint_selection_bg_/cell_text_color_/paint_header_extra_) carry the per-subclass variation. Not instantiated directly
<agt/agt-table-view.hpp>—AgtTableView(: AgtTableBase) the read-only DATA grid: whole-row selection + sortable columns (text / numeric, click-to-sort with a caret). The tabular counterpart toAgtTreeView<agt/agt-table-edit.hpp>—AgtTableEdit(: AgtTableBase) the EDITABLE cell grid: a single ACTIVE CELL(row,col)with arrow / Tab navigation and in-cell editing via a single overlaidAgtEditField(F2 / Enter / double-click / type-to-edit; Enter commits + moves down, Tab commits + moves right, Escape cancels, focus-out commits); per-columneditablegate. EmitsAGT_SEL_COMMANDon a cell commit<agt/agt-tooltip.hpp>—AgtTooltip(: AgtFrame) the hover-hint bubble: a small bordered caption on its own per-pixel-alpha child surface with a soft drop shadow; the shared instance is driven byAgtWindow’s hover-delay timer (consumers set per-widget tip text, not the widget directly)
Both dialog composites inherit AgtDialog’s frosted modal veil —
the parent frame is snapshotted, blurred, and dimmed behind the
card (see the render module), and Tab / Shift+Tab cycle focus over
the card’s buttons + fields.
Distinction from src/widgets/: an atomic widget renders
multiple visual elements inside ONE draw() (e.g. AgtCheckBox’s
indicator + label both come from the checkbox’s own draw method,
not from sub-widgets). A composite OWNS sub-widget AgtObject
nodes that participate in the parent-child tree, get
hit-tested, and dispatch their own events.
Conventions
Composites in this module follow five rules — these are the shape every new composite should adopt:
1. Construct children eagerly in the ctor
The composite’s constructor instantiates every internal sub-
widget via new, parented to the composite (or to an internal
container that’s parented to the composite). After the
constructor returns, the widget tree is complete; consumers
don’t add more children.
AgtMessageBox::AgtMessageBox(const char *title, const char *msg)
: AgtDialog()
{
auto *vbox = new AgtVBox(this, ...);
new AgtLabel(vbox, ..., title);
new AgtLabel(vbox, ..., msg);
auto *hbox = new AgtHBox(vbox, ...);
ok_button_ = new AgtButton(hbox, ..., "OK", &trampoline_, ...);
set_default_button(ok_button_);
}
2. Children are heap-allocated
The composite ctor allocates sub-widgets via new, parented to
the composite tree. When the composite is destroyed, the
AgtObject parent-cascade dtor frees the children.
Composites MUST NOT stack-allocate sub-widgets — a stack-
allocated child would be double-freed when the cascade runs and
again when the stack frame unwinds.
3. Don’t expose sub-widgets via public API
Consumers manipulate composite state through the composite’s
own setters; they don’t reach into the internal tree. Example:
messagebox.set_message("...") updates the internal label;
messagebox.message_label() is NOT exposed. This keeps the
composition an implementation detail — the composite’s
internal structure can change without breaking consumers.
4. Route SEL_COMMAND to the composite itself (self-target)
When the composite owns interactive sub-widgets (buttons), the
buttons target the composite — or a base class of it — directly,
and that class hosts the AGT_SEL_COMMAND handler in its own
message map. The composite already has an AGT_DECLARE_MAP
(it’s an AgtObject), so no separate forwarder object is needed.
Two shipped examples:
AgtDialogmaps the full command-id range toon_command_dismiss, which callsdismiss(message_id()).AgtMessageBox/AgtPromptDialogwire their OK / Cancel buttons to targetthiswithtarget_id == the result code— so a click routes up the metaclass chain to the inheritedAgtDialoghandler and dismisses, with zero per-composite glue:// In AgtDialog's map (inherited by every dialog composite): AGT_MAP_COMMAND_RANGE(0, 65535, &AgtDialog::on_command_dismiss) // In the composite ctor — button id IS the dismiss code: ok_button_ = new AgtButton(button_row, ..., "OK", this, RESULT_OK);
AgtMenuBarmaps its menu-index range toon_title_command; each titleAgtMenuItemtargets the bar withtarget_id == menu index.
When you genuinely need a separate forwarder — the button’s
id space must differ from the composite’s handler ids, or a
button must NOT trigger the composite’s default command behavior
(e.g. an “Apply” button on a dialog that otherwise dismisses on
every command) — give that button a different target: either a
small file-local AgtObject subclass parented to the composite
via add_child (so the dtor cascade frees it), or another
already-existing object. The earlier dialog trampolines were
removed once the self-target pattern subsumed them; reach for a
forwarder only when self-target can’t express the wiring.
5. Inherit from existing widget bases, NOT from a composite base class
Composites that are top-level windows (AgtMessageBox,
AgtPromptDialog) inherit AgtDialog. Composites that are
in-window children (AgtMenuBar, AgtMenu) inherit
AgtFrame (or AgtHBox / AgtVBox for layout-shaped
composites). There is no AgtComposite base type — composition
is a SOURCE-TREE convention and a documentation grouping, not a
type-system distinction. See the discussion in the AGT-Design
document.
Menu composites — deviations from the generic shape
AgtMenuBar + AgtMenu follow the SEL_COMMAND-trampoline
spirit but differ from the MessageBox/PromptDialog shape in three
documented ways (full rationale in the class headers):
Popup rows are populated post-ctor, not in the ctor. The menu bar’s
add_menureturns an emptyAgtMenu; the consumer fills it viaadd_item. So convention #1 (“construct children eagerly in the ctor”) is relaxed: rows are eager but added by the owner, not the ctor.Popup rows are DIRECT children, laid out eagerly — no
AgtVBox. A shown popup grabs the window mouse capture, so positional events bypass the window hit-test and the popup must hit-route them to rows itself. That requires valid row geometry the instant the popup is shown (before any render), so the popup positions rows inadd_item/show_atrather than relying on a render-time layout pass.Cross-object ownership with a teardown rule. The menu bar owns popups by raw pointer; a shown popup is reparented to the window (for paint-on-top z-order) and detached on dismiss.
~AgtMenuBarfrees only DETACHED popups (and dismisses+frees shown ones when deleted independently of a live window); a popup still parented to the window during the window’s own teardown cascade is freed by that cascade. This avoids a double-free / dangling iterator inAgtObject’s child-cascade.
Testing patterns
Composite tests assert on EXTERNAL state + sub-widget interaction outcomes, not on the internal tree structure:
“After
messagebox.run()and a synthetic OK click, the result isRESULT_OK” — observable contract.NOT: “messagebox owns exactly one VBox containing exactly two labels” — internal structure that could change.
Tests use the Scenario DSL where the input flow is the
interesting axis (click sequences, key presses through
dispatch_input). For state-machine isolation tests, direct
handler invocation matches the existing widgets/-side
pattern.
API Reference
AgtMenuBar
-
class AgtMenuBar : public AgtHBox
Public Functions
-
~AgtMenuBar() noexcept override
Free owned popups (see the ownership note above).
-
AgtMenuBar(const AgtMenuBar&) = delete
-
AgtMenuBar(AgtMenuBar&&) = delete
-
AgtMenuBar &operator=(const AgtMenuBar&) = delete
-
AgtMenuBar &operator=(AgtMenuBar&&) = delete
-
AgtMenu *add_menu(const char *label) noexcept
Add a top-level title and return its (empty) popup for the caller to populate via
AgtMenu::add_item. Returns NULL if the bar is full or allocation failed.
-
inline int menu_count() const noexcept
Number of top-level titles.
-
AgtMenuItem *title_at(int index) const noexcept
Title row / popup by index, or NULL if out of range.
-
inline int active_menu() const noexcept
Index of the currently-open menu, or -1 if none.
-
inline AxlSurface *session_surface() const noexcept
The bar’s session surface (C7 P4) while a menu session is active, else NULL. On activation the bar lifts to this surface (rendering its titles); the open menu parents its surface under it, so ONE seat pointer-grab on the bar surface confines the whole {bar → menu → submenu} subtree. A top-level
AgtMenureads this to parent under.
-
inline int highlighted_title() const noexcept
Index of the keyboard-highlighted top-level title (Phase 2 cursor), or -1 when menu mode is inactive. Stays set to the open menu’s index while a popup is up (Phase 3) so the title reads as active. Exposed for the nav state machine + tests.
-
inline bool menu_active() const noexcept
True while the menu bar is in keyboard menu mode — either a title is highlighted (Phase 2) or a popup is open (Phase 3).
-
void activate() noexcept
Enter keyboard menu mode: highlight the first title and take keyboard focus so subsequent arrows route here (Phase 2). This is the action F10 performs; exposed so a consumer can wire F10 (or any “open menu” accelerator) from a window-level key handler, since AGT routes keys only to the focused widget.
-
void deactivate() noexcept
Leave keyboard menu mode entirely: dismiss any open popup, clear the title highlight, release keyboard focus (Phase 1).
-
void open_menu(int index) noexcept
Open menu index (dismissing any currently-open popup), positioning its popup flush under the title. No-op for an out-of-range index. Exposed for the keyboard navigation state machine (2.7f-B) + tests.
-
void close_active() noexcept
Dismiss the open popup (if any) and clear
active_menu_.
-
bool activate_mnemonic(char letter) noexcept
Open the menu whose title mnemonic matches letter — the
Alt+<letter>access path (case-insensitive). Mirrors a title click (open_menu). A window-level key handler calls this on an Alt+letter press. Returns true if a title matched (menu opened), false if none did (so the caller can let the key fall through).
-
void menu_back_to_bar() noexcept
Popup Escape: close the popup and return to Phase 2 with the same title highlighted + the bar re-focused.
-
void menu_nav_adjacent(int dir) noexcept
Popup Left/Right: close the current popup and open the adjacent title’s popup (dir is -1 / +1, wrapping), staying in Phase 3.
-
void on_menu_dismissed(AgtMenu *p) noexcept
Called by an owned
AgtMenuwhen it dismisses itself (e.g. click-away). Clearsactive_menu_if p is the active popup so the bar’s state tracks the popup’s.
-
long on_title_command(AgtObject *sender, AgtEvent *ev)
Command handler — title clicks land here (each title targets the bar with
target_id == index).
-
long on_key_press(AgtObject *sender, AgtEvent *ev)
Phase-2 keyboard handler — F10 (toggle menu mode), Left / Right (move title highlight), Down / Enter (open the highlighted title’s popup), Escape (leave menu mode). Only fires while the bar holds keyboard focus; once a popup is open it owns focus and handles its own keys.
-
bool handle_event(AgtEvent &ev) override
Title router for the menu session (C7 P4). While a session is active the bar’s session surface delivers TITLE-area pointer events here (the open menu’s rows go to the menu’s own surface); this hover-switches / click-toggles among the bar titles. A press OUTSIDE the {bar + menu} subtree is the bar surface’s grab dismiss →
deactivate. Outside a session it defers to the base AgtHBox (keys / closed-title clicks).
Public Static Functions
-
~AgtMenuBar() noexcept override
AgtMenu
-
class AgtMenu : public AgtFrame
Public Functions
-
AgtMenu(AgtMenuBar *bar, AgtWindow *window) noexcept
Construct a detached popup owned by bar. window is the root window it attaches to when shown (the menu bar resolves + refreshes it at open time, so passing the known root here is an optimization, not a requirement — NULL is fine).
-
~AgtMenu() noexcept override
Tear down the open surface (if any) and
deleteowned submenu popups. A menu is never a window child, so a delete-while-shown path explicitly notifies the window to drop any focused/hovered back-pointer (UAF guard).
-
AgtMenuItem *add_item(const char *label, const char *shortcut = nullptr, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
Append a menu row. target / target_id are forwarded to the row’s
AgtMenuItem— a selection emitsAGT_SEL_COMMANDto target exactly like a button click. Returns the new row, or NULL if the popup is full. Re-lays-out the popup so geometry stays valid before show.
-
AgtMenu *add_submenu(const char *label, const char *shortcut = nullptr) noexcept
Append a submenu row: a row that, instead of emitting a command, opens a nested child popup cascading to its right. Returns the (empty) child
AgtMenufor the caller to populate viaadd_item/add_submenu; the child is OWNED by this popup. The row renders a right-pointing arrow indicator. Returns NULL if the popup is full or allocation failed.
-
AgtMenuItem *add_check_item(const char *label, AgtObject *target = nullptr, uint16_t target_id = 0, bool checked = false) noexcept
Append a checkable command row — a checkbox showing a checkmark while checked. Otherwise identical to
add_item. Once any markable row is added the popup reserves a left glyph gutter for EVERY row, so labels stay column-aligned. Returns the row, or NULL if full.
-
AgtMenuItem *add_radio_item(const char *label, uint16_t group, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
Append a radio row in group (must be non-zero). Rows sharing a group are mutually exclusive: activating one — by click or Enter — checks it and clears the rest (and
set_radio_selecteddoes the same programmatically). Returns the row, or NULL if full.
-
void set_radio_selected(uint16_t group, uint16_t target_id) noexcept
Check the row in radio group whose
target_idmatches target_id and clear every other row in that group. The consumer calls this to reflect state it owns (e.g. the current encoding) — at startup and whenever that state changes by any path. No-op if no row matches.
-
inline AgtMenu *active_submenu() const noexcept
Currently-open child submenu, or NULL. Exposed for tests + the nav state machine.
-
inline bool is_submenu() const noexcept
True when this popup is itself a submenu (has a parent popup), false for a top-level popup owned by a menu bar. Controls Left/Right key semantics: a submenu’s Left closes back to its parent, a top-level popup’s Left switches menu- bar menus.
-
void on_submenu_dismissed(AgtMenu *child) noexcept
Called by an owned child submenu when it dismisses itself, so this popup drops its
active_submenu_tracking.
-
inline int item_count() const noexcept
Number of item rows added.
-
AgtMenuItem *item_at(int index) const noexcept
Item row by index, or NULL if out of range.
-
void show_at(int screen_x, int screen_y) noexcept
Open the popup’s child surface at output position (screen_x, screen_y) — nested under the bar’s session surface (top-level) or the parent menu’s surface (submenu) — take AGT-side keyboard focus, and highlight the first row. No-op if already shown or there is no window to attach to.
-
void dismiss() noexcept
Tear down: close any child submenu, clear highlights, close this popup’s surface, and hand keyboard focus back up the chain. Safe to call when not shown (no-op). Idempotent.
-
inline bool shown() const noexcept
-
inline AxlSurface *surface() const noexcept
This menu’s own child
AxlSurface(C7 P4), or NULL when closed / headless. A submenu parents its surface under this one (so one grab on the bar surface confines the whole chain).
-
inline void set_window(AgtWindow *w) noexcept
Set the root window this popup attaches to when shown. Called by the owning
AgtMenuBarat open time so the popup always targets the live root even if the tree was assembled after the popup was created.
-
inline AgtWindow *window() const noexcept
The window this popup targets. Intentionally shadows
AgtWidget::window(): the popup caches the window pointer so it’s valid even before the popup is attached to the tree (the menu bar sets it at open time), whereas the inherited walk would return NULL for a popup that is never a window child.
-
inline int highlight_index() const noexcept
Index of the currently keyboard-highlighted row, or -1 for none. Exposed for the menu navigation state machine + tests.
-
void set_highlight_index(int index) noexcept
Move the keyboard highlight to index (clamped/ignored if out of range; pass -1 to clear). Repaints the affected rows. Used by
show_at(highlight first) and the 2.7f-B arrow-key handler.
-
bool handle_event(AgtEvent &ev) override
Route seat-delivered positional events (content-local) to the row under the pointer with hover bookkeeping; a leaf release selects + tears down the chain. Everything else (keys, focus) defers to the base map dispatch.
-
void draw(AgtDrawContext &ctx) override
Paint a soft drop shadow under the popup (depth cue for the floating menu), then delegate to
AgtFrame::drawfor the panel background + border. Items paint on top via the render-walk.
-
int dirty_margin() const noexcept override
Over-draw margin covering the drop shadow (incremental present).
-
long on_key_press(AgtObject *sender, AgtEvent *ev)
Keyboard handler (the popup holds focus while shown) — see the “Keyboard model” note above: Up / Down move the row highlight (wrapping); Enter selects a leaf or opens a submenu row; Escape / Left close one level; Right opens a submenu or (top-level only) switches menu-bar menus; F10 exits menu mode.
Public Static Attributes
-
static constexpr int MAX_ITEMS = 32
Maximum item rows per popup. Fixed inline array — pre-boot menus are short (a dozen commands is already a lot); past this,
add_itemreturns NULL.
-
AgtMenu(AgtMenuBar *bar, AgtWindow *window) noexcept
AgtMessageBox
-
class AgtMessageBox : public AgtDialog
Public Types
-
enum Buttons
Which buttons appear at the bottom of the dialog.
Values:
-
enumerator OK
Single “OK” button.
-
enumerator OK_CANCEL
“OK” (default) + “Cancel”.
-
enumerator YES_NO
“Yes” (default) + “No”.
-
enumerator SAVE_DISCARD_CANCEL
Three-way “save changes?” prompt: “Cancel” (Escape) / “Don’t Save” / “Save” (default, left → right).
-
enumerator OK
Public Functions
Public Static Functions
-
static int info(AgtApp *app, const char *title, const char *message) noexcept
Show an informational MessageBox (single OK button). Stack-allocates internally, runs the modal, returns the result code (always
RESULT_OKon dismissal — Enter or click on OK both produce it). Reads as the typical “info dialog” call site: AgtMessageBox::info(&app, “Done”, “Operation complete.”);
-
static int question(AgtApp *app, const char *title, const char *message) noexcept
Show a yes/no question MessageBox. Default button is Yes; Cancel button (also Escape) is No. Returns
RESULT_YESorRESULT_NO.
-
static int save_changes(AgtApp *app, const char *title, const char *message) noexcept
Show a three-way “save changes?” prompt: “Save” (default / Enter), “Don’t Save”, and “Cancel” (also Escape). Returns
RESULT_SAVE,RESULT_DISCARD, orRESULT_CANCEL— the standard editor close-with-unsaved-edits gesture.
-
enum Buttons
AgtPromptDialog
-
class AgtPromptDialog : public AgtDialog
Public Functions
-
AgtPromptDialog(const char *title, const char *prompt, const char *default_text = "") noexcept
Construct.
default_textpre-populates the edit field and is selected so the user can type to replace. Typical use: AgtPromptDialog dlg(“Name”, “Enter device name:”); if (dlg.run(&app) == AgtPromptDialog::RESULT_OK) { use(dlg.text()); // valid while dlg is in scope } No static convenience helper because returning the entered string past the dialog’s lifetime would require an output buffer or heap copy at every call site — direct construction reads cleaner.
-
const char *text() const noexcept
Current text in the input field. Valid throughout the dialog’s lifetime; reads the latest committed buffer on dismissal.
-
AgtPromptDialog(const char *title, const char *prompt, const char *default_text = "") noexcept
AgtScrollFrame
-
class AgtScrollFrame : public AgtFrame
Public Types
Public Functions
-
AgtScrollFrame(AgtWidget *parent, int x, int y, int w, int h, bool horizontal = false) noexcept
Construct a scroll frame.
horizontaladds a bottom horizontal scrollbar (default vertical-only).
-
inline AgtWidget *content() const noexcept
The scrollable content container. Parent your widgets to this (their coordinates are content-relative). Owned by the frame (cascade-freed).
-
void set_content_size(int w, int h) noexcept
Set the logical content extent. Sizes the content child, updates the scroll ranges, and re-clamps the current offset. No command.
-
inline int content_width() const noexcept
-
inline int content_height() const noexcept
-
inline int scroll_x() const noexcept
-
inline int scroll_y() const noexcept
-
void scroll_to(int x, int y) noexcept
Scroll so content offset (x, y) sits at the viewport top-left. Clamps to
[0, max]; repositions the content child; silently syncs the bars. No SEL_COMMAND.
-
int viewport_width() const noexcept
Visible content area (inner rect minus the scrollbar strips).
-
int viewport_height() const noexcept
-
bool wants_child_clip(AxlGfxClip &out) const noexcept override
Confine children to the viewport (the frame’s inner rect, local coords) — render + hit-test both honor it.
-
inline uint32_t focus_policy() const noexcept override
The viewport itself is not a Tab target — the focusable widgets live inside
content().
-
void draw(AgtDrawContext &ctx) override
-
AgtScrollFrame(AgtWidget *parent, int x, int y, int w, int h, bool horizontal = false) noexcept
AgtComboBox
-
class AgtComboBox : public AgtFrame
Public Types
Public Functions
-
AgtComboBox(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
-
~AgtComboBox() noexcept override
-
int add_item(const char *text) noexcept
Append an option (text copied). Returns the new index or -1.
-
void clear() noexcept
-
int item_count() const noexcept
-
const char *item_text(int index) const noexcept
-
int current() const noexcept
Selected option index, or -1 if none.
-
void set_current(int index) noexcept
Set the selection programmatically (clamped; -1 clears). Updates the field. No SEL_COMMAND.
-
const char *current_text() const noexcept
Text of the current selection, or “” when nothing is selected.
-
inline bool is_open() const noexcept
-
inline uint16_t target_id() const noexcept
-
inline uint32_t focus_policy() const noexcept override
-
void draw(AgtDrawContext &ctx) override
-
long on_list_command(AgtObject *sender, AgtEvent *ev)
SEL_COMMAND from the owned list (id == ID_LIST) — a browse update.
-
void request_deferred_commit(int index) noexcept
Commit index, but DEFERRED to the next loop tick. The dropdown’s seat-activation path calls this:
commit_tears down the dropdown surface the seat is mid-dispatch through (and the emit may delete this combo), so the work must run after the dispatch unwinds. Scheduled viaaxl_defer; cancelled on close / destroy. Falls back to a synchronous commit only when no loop is reachable. Public for direct testing.
-
AgtComboBox(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtTabView
-
class AgtTabView : public AgtFrame
Public Functions
-
AgtTabView(AgtWidget *parent, int x, int y, int w, int h, BorderStyle border = AGT_FRAME_LINE, int border_width = 1) noexcept
-
~AgtTabView() noexcept override
Free the owned tab labels. The tab buttons + page containers are children, freed by the
AgtObjectcascade.
-
AgtWidget *add_tab(const char *label)
Add a tab titled label; returns its (initially empty) page container for the caller to populate, or NULL if
MAX_TABSis exceeded. The label is copied. The first tab added is active.
-
inline int tab_count() const noexcept
-
inline int active() const noexcept
-
void set_active(int index) noexcept
Make tab index active (clamped to a valid tab); shows its page, hides the rest. No-op when there are no tabs. When the active tab actually changes and a change target is registered (
set_change_target), emitsAGT_SEL_COMMANDto it so the consumer can react (e.g. move keyboard focus into the new page) — the view itself does NOT move focus.
-
bool remove_tab(int index) noexcept
Remove tab index: deletes its page (cascading the hosted content) and tab button, frees the label, and compacts the remaining tabs so their ids stay equal to their index. Refuses to remove the LAST tab (a tab view always shows a page; the caller decides last-tab policy) and any out-of-range index — returns false in those cases, true on a successful removal. Adjusts
active()to stay in range; does NOT emit the change signal (the caller drives any post-remove refresh / focus, since index semantics shift under compaction).
-
inline void set_change_target(AgtObject *target, uint16_t id) noexcept
Register the object notified when the active tab changes (via click, Ctrl+Tab, arrows, or a programmatic
set_activethat moves it). The target receivesAGT_SEL_COMMANDwithmessage_id == id; it readsactive()for the new index. NULL target (the default) disables the signal. MirrorsAgtButton::set_target.
-
void set_tab_label(int index, const char *label) noexcept
Replace tab index’s title with a copy of label (NULL = empty). No-op for an out-of-range index. The view owns the copy; the tab button points at it. For a consumer that renames a tab as its content changes (a file’s basename, a dirty
*marker).
-
AgtWidget *page(int index) const noexcept
The page container for tab index, or NULL if out of range.
-
inline int tab_strip_height() const noexcept
-
void set_tab_strip_height(int h) noexcept
-
inline int tab_width() const noexcept
-
void set_tab_width(int w) noexcept
-
void draw(AgtDrawContext &ctx) override
-
long on_tab(AgtObject *sender, AgtEvent *ev)
Tab-button command handler —
message_idis the tab index.
-
long on_key_press(AgtObject *sender, AgtEvent *ev)
Ctrl+Tab / Ctrl+Shift+Tab cycle tabs (wrap); Left/Right switch when a tab button has focus (so page-content arrows are never hijacked). The view itself isn’t a tab stop — these reach it by bubbling up from a focused tab button or page-content widget.
Public Static Functions
Public Static Attributes
-
static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST
FOX-style message-ID chain (see
AgtFrame::ID_LAST). Tab buttons target the view withmessage_idin[0, MAX_TABS), matched by anAGT_MAP_COMMAND_RANGE— so the view reserves that low-id range.
-
static constexpr int MAX_TABS = 12
Upper bound on tabs (fixed-capacity, no heap array — mirrors
AgtMenuBar::MAX_MENUS).
-
static constexpr int DEFAULT_TAB_WIDTH = 110
Default per-tab button width (overridable via
set_tab_width).
-
AgtTabView(AgtWidget *parent, int x, int y, int w, int h, BorderStyle border = AGT_FRAME_LINE, int border_width = 1) noexcept
AgtToolBar
-
class AgtToolBar : public AgtHBox
Public Functions
-
AgtToolBar(AgtWidget *parent, int x, int y, int w, int h, int spacing = DEFAULT_SPACING, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
-
AgtButton *add_button(const char *label, AgtObject *target, uint16_t id, int width = DEFAULT_BUTTON_WIDTH)
Append a flat command button titled label that emits SEL_COMMAND to target with id on click. Sized width by the bar’s inner height; returns it (for further styling).
-
AgtBitmapButton *add_icon_button(AgtStyle::StockIcon icon, const char *tooltip, AgtObject *target, uint16_t id)
Append a flat, square icon button (icon-only
AgtBitmapButton) sized to the bar height, emitting SEL_COMMAND to target with id on click, with a hover tooltip. Two icon sources:a built-in themeable stock icon (
AgtStyle::StockIcon), ora consumer bitmap (bytes / len, decoded like
set_icon). Returns the button (for further styling).
-
AgtBitmapButton *add_icon_button(const uint8_t *bytes, size_t len, const char *tooltip, AgtObject *target, uint16_t id)
-
AgtSeparator *add_separator()
Append a vertical separator (a group divider). Returns it.
-
void draw(AgtDrawContext &ctx) override
Base fill (header tone) + a 1px bottom divider before the content.
Public Static Functions
-
AgtToolBar(AgtWidget *parent, int x, int y, int w, int h, int spacing = DEFAULT_SPACING, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept
AgtSpinBox
-
class AgtSpinBox : public AgtFrame
Public Types
Public Functions
-
AgtSpinBox(AgtWidget *parent, int x, int y, int w, int h, int value, int min_value, int max_value, int step = 1, AgtObject *target = nullptr, uint16_t target_id = 0, BorderStyle border = AGT_FRAME_SUNKEN, int border_width = 1) noexcept
-
inline int value() const noexcept
-
inline int min_value() const noexcept
-
inline int max_value() const noexcept
-
inline int step() const noexcept
-
void set_value(int v) noexcept
Set the value, clamped to
[min, max]. No SEL_COMMAND — only a stepper press emits.
-
void set_range(int lo, int hi) noexcept
Set the inclusive range (swapped if
lo > hi); re-clamps value.
-
void set_step(int s) noexcept
-
void draw(AgtDrawContext &ctx) override
-
AgtSpinBox(AgtWidget *parent, int x, int y, int w, int h, int value, int min_value, int max_value, int step = 1, AgtObject *target = nullptr, uint16_t target_id = 0, BorderStyle border = AGT_FRAME_SUNKEN, int border_width = 1) noexcept
AgtExpander
-
class AgtExpander : public AgtFrame
Public Functions
-
AgtExpander(AgtWidget *parent, int x, int y, int w, int h, const char *title, bool expanded = true, BorderStyle border = AGT_FRAME_LINE, int border_width = 1) noexcept
-
~AgtExpander() noexcept override
-
inline AgtWidget *content() const noexcept
The content container — parent your section widgets here.
-
inline bool expanded() const noexcept
-
void set_expanded(bool expanded) noexcept
Expand / collapse: toggles
content()visibility and the expander’s own height (header-only when collapsed).
-
inline int header_height() const noexcept
-
void set_title(const char *title)
The title text (copied).
-
inline const char *title() const noexcept
-
inline uint32_t focus_policy() const noexcept override
The header is keyboard-focusable so Space / Enter can toggle it.
-
void draw(AgtDrawContext &ctx) override
Public Static Functions
-
AgtExpander(AgtWidget *parent, int x, int y, int w, int h, const char *title, bool expanded = true, BorderStyle border = AGT_FRAME_LINE, int border_width = 1) noexcept
AgtTreeView
-
class AgtTreeView : public AgtFrame
Public Types
Public Functions
-
AgtTreeView(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
-
~AgtTreeView() noexcept override
-
AxlNTree *add_item(AxlNTree *parent, const char *label) noexcept
Add a child item under parent (NULL = a top-level item); the label is copied. Returns the node handle (owned by the view), or NULL on allocation failure.
-
void clear() noexcept
Remove every item (frees labels + nodes); resets selection/scroll.
-
const char *item_label(const AxlNTree *item) const noexcept
The item’s label, or NULL. Borrowed — the view owns it.
-
int visible_count() const noexcept
Number of currently-visible (flattened) rows.
-
bool is_expanded(const AxlNTree *item) const noexcept
-
void set_expanded(AxlNTree *item, bool expanded) noexcept
-
inline AxlNTree *current() const noexcept
-
void set_current(AxlNTree *item) noexcept
Select item (NULL clears); scrolls it into view. No command.
-
inline int top() const noexcept
-
int visible_rows() const noexcept
-
inline int row_height() const noexcept
-
void set_row_height(int px) noexcept
-
inline AxlGfxPixel text_color() const noexcept
-
void set_text_color(AxlGfxPixel c) noexcept
-
void set_selection_colors(AxlGfxPixel bg, AxlGfxPixel text) noexcept
-
inline uint16_t target_id() const noexcept
-
inline uint32_t focus_policy() const noexcept override
-
void draw(AgtDrawContext &ctx) override
-
AgtTreeView(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtFileDialog
-
class AgtFileDialog : public AgtDialog
Public Functions
-
AgtFileDialog(const char *title, const char *initial_dir) noexcept
initial_dir is the directory to open at (e.g.
"fs0:\\").
-
~AgtFileDialog() noexcept override
-
inline const char *directory() const noexcept
The directory currently shown.
-
inline const char *path() const noexcept
The picked file path after
RESULT_OK, else empty.
-
void set_directory(const char *dir) noexcept
Show dir (copied) and read its entries (best-effort: an unreadable directory just shows an empty list).
-
void navigate_up() noexcept
Navigate to the parent directory (clamped at the volume root).
-
int entry_count() const noexcept
-
AgtFileDialog(const char *title, const char *initial_dir) noexcept
AgtMainWindow
-
class AgtMainWindow : public AgtWindow
Public Functions
-
AgtMainWindow() noexcept = default
-
explicit AgtMainWindow(AgtApp &app) noexcept
Convenience: construct + bind the app’s input seat (see
AgtWindow(AgtApp&)).
-
inline void set_menu_bar(AgtMenuBar *bar) noexcept
Register a chrome / content widget for a dock slot. Each must be a child of this window (so the render walk reaches it);
set_*only records the pointer for layout. Passing NULL clears the slot.
-
inline void set_tool_bar(AgtToolBar *bar) noexcept
-
inline void set_status_bar(AgtStatusBar *bar) noexcept
-
inline AgtMenuBar *menu_bar() const noexcept
-
inline AgtToolBar *tool_bar() const noexcept
-
inline AgtStatusBar *status_bar() const noexcept
-
void draw(AgtDrawContext &ctx) override
Lay out the dock slots (top→down for menu/tool bars, bottom for the status bar, content fills the rest), then clear the background. Runs before the render walk descends, exactly like a box’s
draw().
-
AgtMainWindow() noexcept = default
AgtSearchEntry
-
class AgtSearchEntry : public AgtEditField
Public Functions
-
AgtSearchEntry(AgtWidget *parent, int x, int y, int w, int h, const char *text = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
-
inline bool show_clear() const noexcept
Whether the clear (×) button appears when the field has text. Default true. Turning it off mid-text hides the button.
-
void set_show_clear(bool on) noexcept
-
inline AxlGfxPixel icon_color() const noexcept
Tint of the magnifier + clear glyphs. Defaults to the muted disabled-text grey so the chrome stays subordinate to content.
-
void set_icon_color(AxlGfxPixel c) noexcept
-
inline bool clear_visible() const noexcept
True when the clear button is currently visible — i.e.
show_clear()is on and the field holds at least one byte.
-
void draw(AgtDrawContext &ctx) override
Paint the field (AgtEditField) then the magnifier and, when visible, the clear glyph on top of the gutters.
Public Static Functions
Public Static Attributes
-
static constexpr int ICON_SIZE = 12
Side length (px) of the magnifier / clear glyph boxes.
-
static constexpr uint16_t ID_LAST = AgtEditField::ID_LAST
FOX-style message-ID chain (see
AgtEditField::ID_LAST).
-
AgtSearchEntry(AgtWidget *parent, int x, int y, int w, int h, const char *text = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtPasswordField
-
class AgtPasswordField : public AgtEditField
Public Functions
-
AgtPasswordField(AgtWidget *parent, int x, int y, int w, int h, const char *text = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
-
inline bool show_reveal() const noexcept
Whether the reveal (eye) button is shown. Default true; turning it off hides the eye AND re-masks the field (a hidden eye must not leave a secret revealed).
-
void set_show_reveal(bool on) noexcept
-
inline bool revealed() const noexcept
True when the secret is currently shown (echo mode == NORMAL).
-
void set_revealed(bool on) noexcept
Show / hide the secret (flip the base echo mode). A no-op toward an already-matching state.
-
inline void toggle_reveal() noexcept
Flip the reveal state — the eye-button action.
-
inline bool eye_visible() const noexcept
True when the eye button is currently on screen (i.e.
show_reveal()).
-
inline AxlGfxPixel icon_color() const noexcept
Tint of the eye glyph. Defaults to the muted disabled-text grey so the chrome stays subordinate to content (matches
AgtSearchEntry).
-
void set_icon_color(AxlGfxPixel c) noexcept
-
void draw(AgtDrawContext &ctx) override
Paint the field (AgtEditField) then the eye glyph in the right gutter.
-
long on_left_button_press(AgtObject *sender, AgtEvent *ev)
Intercept a press on a visible eye button (toggle reveal); otherwise defer to
AgtEditFieldfor cursor positioning + focus.
-
void restyle() noexcept override
Re-snapshot the eye tint on a theme swap.
Public Static Functions
Public Static Attributes
-
static constexpr int ICON_SIZE = 12
Side length (px) of the eye glyph box.
-
static constexpr uint16_t ID_LAST = AgtEditField::ID_LAST
FOX-style message-ID chain (see
AgtEditField::ID_LAST).
-
AgtPasswordField(AgtWidget *parent, int x, int y, int w, int h, const char *text = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtProgressDialog
-
class AgtProgressDialog : public AgtDialog
Public Types
Public Functions
-
AgtProgressDialog(const char *title, const char *message, Mode mode = DETERMINATE, bool cancelable = true) noexcept
Construct. title is the draggable card bar; message the caption above the indicator. cancelable adds a Cancel button (also Escape) that dismisses with
RESULT_CANCEL. Strings are held by pointer — caller storage must outlive the dialog.
-
inline bool cancelable() const noexcept
-
void set_progress(int value) noexcept
Set the current value (clamped to the range) and repaint.
-
int progress() const noexcept
-
void set_range(int min, int max) noexcept
Replace the inclusive range and repaint.
-
int min() const noexcept
-
int max() const noexcept
-
void start() noexcept
Start the spinner self-animating. Call after
show()(a shown dialog reaches the app loop viawindow()->loop(), so the spinner’s timer arms on it). No-op for a determinate dialog. For a MODALrun()(which blocks before you could call this), drive the spinner withadvance()from your work instead.
-
void stop() noexcept
Stop the spinner’s self-animation. Safe any time. The Cancel route stops it automatically; if you
close()an indeterminate dialog programmatically AND keep it alive, call this first so the spinner timer doesn’t free-run (harmless no-op redraws) until destruction.
-
void advance() noexcept
Step the spinner one frame + repaint — the manual drive for blocking work that doesn’t return to the loop. No-op for a determinate dialog.
-
void set_message(const char *message) noexcept
Replace the caption text and repaint.
-
inline bool was_canceled() const noexcept
True only after the user dismissed the dialog via Cancel / Escape — distinct from a programmatic
close(code)and false before any dismissal (unlike a bareresult() == RESULT_CANCEL, which a never-dismissed dialog also satisfies).
-
long on_cancel(AgtObject *sender, AgtEvent *ev)
Cancel route (Cancel button click / Escape): record the cancellation, stop the spinner, then dismiss with
RESULT_CANCEL. Public so a test can invoke it directly. Shadows the base rangeon_command_dismissforRESULT_CANCELonly (the dialog’s sole command).
-
~AgtProgressDialog() noexcept override
Stop the spinner before teardown (belt-and-suspenders; the child spinner’s own dtor also stops it).
-
AgtProgressDialog(const char *title, const char *message, Mode mode = DETERMINATE, bool cancelable = true) noexcept
AgtForm
-
struct AgtFormField
A single form field. POD aggregate — no user-declared constructors, no virtuals; the static factory helpers below just return aggregate-initialized values, and the
with_*/visible_if/enabled_ifchain helpers return a modified COPY (so they compose in an array initializer:AgtFormField::boolean("Advanced", &adv).visible_if(show_adv)).Per-type field usage:
SECTION—label(+ optionalhelp).BOOL—bool_ptr(orget_fn/set_fn);default_int(0/1).INT—int_ptr(orget_fn/set_fn);min/max/step;default_int.ENUM—int_ptr(selected index) (orget_fn/set_fn);choices/choice_count;default_int.TEXT—text_ptr+text_cap(buffer);default_text.PASSWORD—text_ptr+text_cap;default_text.ACTION—action_fn(+user).
Public Functions
-
inline AgtFormField with_help(const char *h) const noexcept
Attach a hover-help string (row tooltip).
-
inline AgtFormField with_user(void *u) const noexcept
Attach a
userpointer (passed to predicates / get / set).
-
inline AgtFormField with_getset(AgtFormGetFn g, AgtFormSetFn s, void *u = nullptr) const noexcept
Attach a computed get/set binding (overrides the typed pointer). Only int-like fields (BOOL/INT/ENUM) support a computed binding — TEXT/ PASSWORD are always buffer-bound. u updates
userONLY when non-null, so a prior.with_user(p)is preserved if you omit it.
-
inline AgtFormField visible_if(AgtFormPredicate p) const noexcept
Attach a
suppressif-style visibility predicate.
-
inline AgtFormField enabled_if(AgtFormPredicate p) const noexcept
Attach a
grayoutif-style enable predicate.
-
inline bool is_int_like() const noexcept
True for the value-bearing field kinds (BOOL/INT/ENUM use the int slot).
-
inline bool is_text_like() const noexcept
True for the text-bearing field kinds.
Public Members
-
AgtFieldType type = AGT_FIELD_SECTION
-
const char *label = ""
borrowed; row caption / button text
-
const char *help = nullptr
borrowed; optional row tooltip
-
bool *bool_ptr = nullptr
BOOL direct binding.
-
int *int_ptr = nullptr
INT / ENUM direct binding.
-
char *text_ptr = nullptr
TEXT / PASSWORD buffer.
-
int text_cap = 0
capacity of
text_ptr(incl. NUL)
-
AgtFormGetFn get_fn = nullptr
computed int-like read (overrides *_ptr)
-
AgtFormSetFn set_fn = nullptr
computed int-like write (overrides *_ptr)
-
void *user = nullptr
passed to get/set/action/predicates
-
int min = 0
INT lower bound (inclusive)
-
int max = 0
INT upper bound (inclusive)
-
int step = 1
INT spinner step.
-
const char *const *choices = nullptr
ENUM option labels (borrowed)
-
int choice_count = 0
ENUM option count.
-
AgtFormActionFn action_fn = nullptr
-
AgtFormPredicate visible_pred = nullptr
-
AgtFormPredicate enabled_pred = nullptr
-
int default_int = 0
BOOL/INT/ENUM default working value.
-
const char *default_text = ""
TEXT/PASSWORD default working value.
Public Static Functions
-
static inline AgtFormField section(const char *label) noexcept
A group header / divider row.
-
static inline AgtFormField boolean(const char *label, bool *binding, bool deflt = false) noexcept
An on/off field bound to binding. deflt seeds Load-Defaults.
-
static inline AgtFormField integer(const char *label, int *binding, int min, int max, int step = 1, int deflt = 0) noexcept
An integer field bound to binding, clamped to
[min, max], stepped by step.
-
static inline AgtFormField choice(const char *label, int *binding, const char *const *choices, int count, int deflt = 0) noexcept
A one-of-N field; binding holds the selected index into choices.
-
static inline AgtFormField text(const char *label, char *buf, int cap, const char *deflt = "") noexcept
A free-text field over the caller’s buf (cap bytes incl. NUL).
-
static inline AgtFormField password(const char *label, char *buf, int cap, const char *deflt = "") noexcept
A masked-text field over the caller’s buf.
-
static inline AgtFormField action(const char *label, AgtFormActionFn fn, void *user = nullptr) noexcept
A button that fires fn(user) when pressed.
-
class AgtForm
The form model: a borrowed field array + a fixed-capacity working copy and the Save / Discard / Load-Defaults commit model. The working copy is read from the bindings at construction (
load()); editors mutate it;save()commits it back to the bindings.Public Functions
-
AgtForm(const AgtFormField *fields, int count) noexcept
Construct over fields (count count, clamped to
MAX_FIELDS). The array is BORROWED (caller keeps it alive for the form’s lifetime — it is typicallystatic const). Immediatelyload()s the working copy from the bindings.
-
inline int count() const noexcept
-
inline const AgtFormField &field(int i) const noexcept
The descriptor for field i. i MUST be in
[0, count())— the browser indexes by a valid field number; out-of-range is UB.
-
bool field_visible(int i) const noexcept
True if field i has no
visible_ifor its predicate returns true.
-
bool field_enabled(int i) const noexcept
True if field i has no
enabled_ifor its predicate returns true.
-
int work_int(int i) const noexcept
-
inline bool work_bool(int i) const noexcept
-
const char *work_text(int i) const noexcept
-
void set_work_int(int i, int v) noexcept
Set the int slot, clamped to the field’s constraints (INT range, ENUM index range, BOOL 0/1).
-
inline void set_work_bool(int i, bool v) noexcept
-
void set_work_text(int i, const char *v) noexcept
Set the text slot (bounded copy into the working buffer).
-
int read_binding_int(int i) const noexcept
-
void write_binding_int(int i, int v) const noexcept
-
const char *read_binding_text(int i) const noexcept
-
void write_binding_text(int i, const char *v) const noexcept
-
void load() noexcept
Working ← bindings (called by the ctor; the Discard path). Copies the binding value RAW — an out-of-range binding is preserved, NOT clamped (so a no-op
save()writes it back unchanged); a value is normalized only when the user actually edits it viaset_work_int.
-
void save() noexcept
Bindings ← working — the commit. SECTION / ACTION fields are skipped. Ends with a
load()resync so the working copy reflects what the bindings now hold (a get/set binding may transform the value, a text buffer may truncate it), leavingis_dirty()clean right after Save.
-
inline void discard() noexcept
Working ← bindings — revert in-progress edits.
-
void load_defaults() noexcept
Working ← schema defaults (
default_int/default_text).
-
bool is_dirty() const noexcept
True if any field’s working copy differs from its live binding.
-
void invoke_action(int i) const noexcept
Invoke field i’s ACTION callback (no-op if not an ACTION field).
Public Static Attributes
-
static constexpr int MAX_FIELDS = 64
Maximum fields in one form (one settings screen / page). Fixed-cap so the working copy needs no allocation. Excess fields are dropped (the count is clamped) —
count()reports the honored number.
-
static constexpr int TEXT_CAP = 264
Working-copy text capacity per field, in bytes incl. NUL. Comfortably over
AgtEditField::MAX_TEXT_LEN(256) so a full-length edit-field value round-trips without loss; the consumer’s owntext_capbounds the final commit.
-
AgtForm(const AgtFormField *fields, int count) noexcept
AgtFormBrowser
-
class AgtFormBrowser : public AgtFrame
Public Types
Message-ID chain.
ID_FIELD_BASE + iis the SEL_COMMAND id fieldi’s editor targets the browser with; Save/Discard/Defaults (Phase D) take the three slots before it. Consumer ids start atID_LAST.Values:
-
enumerator ID_SAVE
-
enumerator ID_DISCARD
-
enumerator ID_DEFAULTS
-
enumerator ID_FIELD_BASE
-
enumerator ID_LAST
-
enumerator ID_SAVE
Public Functions
-
AgtFormBrowser(AgtWidget *parent, int x, int y, int w, int h, const AgtFormField *fields, int count) noexcept
Construct a browser over fields (count count). The array is BORROWED by the owned
AgtForm(keep it alive for the browser’s lifetime — typicallystatic const). Builds one editor per field, seeded from the working copy, and lays the rows out.
-
inline AgtForm &form() noexcept
The owned form model (working copy + commit). Read it for Save state (
is_dirty()), or drivesave()/discard()/load_defaults()directly.
-
inline int field_count() const noexcept
Number of fields (==
form().count()).
-
AgtWidget *editor_widget(int i) const noexcept
The editor widget for field i (NULL for a SECTION row). Exposed for focus management + testing; the concrete type follows the field type (BOOL→AgtCheckBox, INT→AgtSpinBox, …).
-
void refresh() noexcept
Re-seed editors from the working copy (after a programmatic
form().discard()/load_defaults()), then re-evaluate every field’svisible_if/enabled_ifand re-lay-out — rows hide / show / grey to match. Call after any change to the working copy made outside the editors.
-
inline int content_height() const noexcept
Total laid-out height of the rows (content extent) — what the owned scroll frame sizes its content child to.
-
void commit() noexcept
Commit the working copy to the live bindings (Save), re-seed the editors from the resynced working copy, update the button enablement, and fire the
set_on_savenotification (if any).
-
void discard() noexcept
Revert the working copy to the live bindings (Discard) and re-seed.
-
void load_defaults() noexcept
Reset the working copy to the schema defaults (Restore Defaults) and re-seed. Does NOT commit — the bindings change only on a later Save.
-
inline void set_on_save(AgtObject *target, uint16_t target_id) noexcept
Optionally notify target (with
AGT_SEL_COMMAND, id target_id) right after a successfulcommit()— the “settings applied” hook.
-
void draw(AgtDrawContext &ctx) override
-
long on_field_command(AgtObject *sender, AgtEvent *ev)
SEL_COMMAND from a field editor (id in
[ID_FIELD_BASE, …)): sync that editor’s value into the working copy (or fire an ACTION), then refresh. Public for direct testing.
AgtTableBase
-
class AgtTableBase : public AgtFrame
Subclassed by AgtTableEdit, AgtTableView
Public Types
Public Functions
-
~AgtTableBase() noexcept override
-
int add_column(const char *header, int width) noexcept
Append a column with header (copied) and pixel width. Returns the column index, or -1 when full / after rows exist (define the schema first).
-
inline int column_count() const noexcept
-
const char *column_header(int col) const noexcept
-
int column_width(int col) const noexcept
-
void set_column_width(int col, int width) noexcept
-
virtual int add_row() noexcept
Append an empty row. Returns the row index, or -1 (no columns yet / allocation failure). Virtual so a subclass can react (e.g. seed the active cell).
-
int row_count() const noexcept
-
void set_cell(int row, int col, const char *text) noexcept
Set the (row, col) cell text (copied; NULL clears). No-op out of range.
-
const char *cell(int row, int col) const noexcept
The cell text, or NULL. Borrowed — the table owns it.
-
virtual void clear_rows() noexcept
Remove every row (frees cell text); keeps the column schema. Virtual so a subclass can also reset its selection / sort state.
-
inline int top() const noexcept
-
int visible_rows() const noexcept
Fully-visible data rows in the viewport (below the header).
-
inline int row_height() const noexcept
-
void set_row_height(int px) noexcept
-
inline int header_height() const noexcept
-
void set_header_height(int px) noexcept
-
inline AxlGfxPixel text_color() const noexcept
-
void set_text_color(AxlGfxPixel c) noexcept
-
void set_header_colors(AxlGfxPixel bg, AxlGfxPixel text) noexcept
-
inline uint16_t target_id() const noexcept
-
inline uint32_t focus_policy() const noexcept override
-
void draw(AgtDrawContext &ctx) override
Public Static Attributes
-
static constexpr int MAX_COLS = 16
Maximum columns. Fixed inline per-row cell array — diagnostic tables are a handful of columns wide; past this,
add_columnreturns -1.
-
~AgtTableBase() noexcept override
AgtTableView
-
class AgtTableView : public AgtTableBase
Public Types
Public Functions
-
AgtTableView(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
-
~AgtTableView() noexcept override
-
SortMode column_sort_mode(int col) const noexcept
How column col compares when sorted (default
AGT_SORT_TEXT).
-
inline bool sortable() const noexcept
Enable/disable clickable, sorting column headers (default off). When off, header clicks do nothing (the v0.1 behavior).
-
void set_sortable(bool on) noexcept
-
void sort_by(int col, bool ascending) noexcept
Sort the rows by column col, ascending or descending. The selected row follows the data (selection is preserved by identity, not index). Works regardless of
sortable()(that flag gates only the header-click affordance). No-op for an out-of-range column.
-
inline int sorted_column() const noexcept
The column the rows are currently sorted by, or -1 if never sorted.
-
inline bool sort_ascending() const noexcept
The current sort direction (meaningful only when
sorted_column() >= 0).
-
void clear_rows() noexcept override
-
inline int current_row() const noexcept
Selected row index, or -1 for none.
-
void set_current_row(int row) noexcept
Select row (-1 clears); scrolls it into view. No command.
-
void set_selection_colors(AxlGfxPixel bg, AxlGfxPixel text) noexcept
-
AgtTableView(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtTableEdit
-
class AgtTableEdit : public AgtTableBase
Public Types
Public Functions
-
AgtTableEdit(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
-
~AgtTableEdit() noexcept override
-
int add_column(const char *header, int width, bool editable = true) noexcept
Append a column with header (copied), pixel width, and an editable flag (default true) gating in-cell editing for the whole column. Returns the column index, or -1 (full / after rows exist). Shadows
AgtTableBase::add_columnto carry the per-column flag.
-
bool column_editable(int col) const noexcept
-
void set_column_editable(int col, bool editable) noexcept
-
int add_row() noexcept override
-
void clear_rows() noexcept override
-
inline int current_row() const noexcept
Active cell row / column, or -1 for none.
-
inline int current_col() const noexcept
-
void set_current_cell(int row, int col) noexcept
Make (row, col) the active cell; clamps to the grid (or clears to -1,-1 if the grid is empty), scrolls the row into view. No command.
-
void set_active_colors(AxlGfxPixel bg, AxlGfxPixel text) noexcept
-
inline bool editing() const noexcept
True while the cell editor is open.
-
void begin_edit() noexcept
Open the editor over the active cell — loads the cell text and selects all (F2 / Enter / double-click semantics). No-op when there is no active cell or its column is read-only (
column_editable(col) == false).
-
void commit_edit() noexcept
Write the editor text into the active cell and close the editor; emits
AGT_SEL_COMMAND. Does NOT move the active cell. No-op when not editing.
-
void cancel_edit() noexcept
Close the editor WITHOUT writing (discard the edit). No-op when not editing.
-
bool editor_cursor_blink() const noexcept
Whether the in-cell editor’s cursor blinks (default true). Turn off for a steady cursor — accessibility, or a deterministic screenshot.
-
void set_editor_cursor_blink(bool on) noexcept
-
AgtTableEdit(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtTooltip
-
class AgtTooltip : public AgtFrame
Public Functions
-
AgtTooltip(AgtWidget *parent, const char *text = "") noexcept
Construct a tooltip parented to parent (normally the window). Sized to text immediately; reposition with
move_to. Starts at (0, 0) — the owner hides + places it.
-
inline const char *text() const noexcept
-
void set_text(const char *text) noexcept
Replace the (borrowed) text and re-fit the widget to it.
-
inline AxlGfxPixel text_color() const noexcept
-
void set_text_color(AxlGfxPixel c) noexcept
-
inline float text_size() const noexcept
-
void set_text_size(float px) noexcept
-
void move_to(int x, int y) noexcept
Move the top-left to (x, y) in parent-local coordinates, clamped so the tooltip stays fully inside the parent (if the parent is a widget). Width / height are unchanged.
-
void draw(AgtDrawContext &ctx) override
Paint the drop shadow, then the frame (bg + border), then the tip text inside the inner rect.
-
int dirty_margin() const noexcept override
Over-draw margin covering the drop shadow (so a move / hide damages the shadowed footprint for incremental present).
-
AgtTooltip(AgtWidget *parent, const char *text = "") noexcept