AgtWidgets — leaf controls, media, editors + dialog

AgtWidgets — leaf controls, media, editors + dialog

The concrete leaf widgets AGT ships. The text-bearing controls are organized as a FOX-style inheritance chain: AgtButton : AgtLabel : AgtFrame : AgtWidget. Each link adds one focused responsibility on top of the previous: AgtFrame contributes a configurable border + padded background, AgtLabel adds a text caption inside the inner rect, AgtButton adds the state machine + click contract. Beyond the chain, the module also ships media widgets (AgtIcon / AgtImage / AgtSprite / AgtAnimatedImage / AgtCanvas / AgtSpinner) and the editor widgets (AgtEditBox for text, AgtHexBox + the AgtHexSource byte-source family for hex/memory/IO) — all : AgtFrame.

Headers:

  • <agt/agt-frame.hpp>AgtFrame border + bg + padding (NONE / LINE / RAISED / SUNKEN / ROUNDED variants; per-side padding; virtual effective_bg_color hook)

  • <agt/agt-label.hpp>AgtLabel single-line text caption with horizontal alignment (LEFT / CENTER / RIGHT)

  • <agt/agt-button.hpp>AgtButton hover + pressed state, AGT_SEL_COMMAND-emitting click contract, state-dependent bg palette, virtual on_clicked hook for state-bearing subclasses (CheckBox / RadioButton)

  • <agt/agt-checkbox.hpp>AgtCheckBox binary toggle with square indicator- <agt/agt-radio-button.hpp>AgtRadioButton mutually- exclusive selection with circle indicator + group via shared parent + target_id- <agt/agt-progress-bar.hpp>AgtProgressBar non-interactive value-fill display, horizontal or vertical

  • <agt/agt-scale.hpp>AgtScale (: AgtFrame) linear analog meter: the AgtProgressBar value/range model plus tick marks, colored threshold bands (green/amber/red zones), and a FILL (band-colored bar) or NEEDLE (zones + marker) indicator — a thermometer / VU-meter / bar gauge

  • <agt/agt-dial.hpp>AgtDial (: AgtFrame) circular needle gauge: value→needle-angle over a configurable sweep (default 270°) + tick marks; the instrument-cluster readout (RPM, temp, pressure). Needle/ticks composed from transform-aware dots

  • <agt/agt-chart.hpp>AgtChart (: AgtFrame) single-series line / strip chart: a fixed-capacity ring buffer of samples mapped to a polyline over an auto or fixed value range, with gridlines — the live-trace / history view (voltage ripple, temperature ramp). push() scrolls old samples off the left

  • <agt/agt-slider.hpp>AgtSlider interactive draggable thumb on a track, emitting AGT_SEL_COMMAND on value change

  • <agt/agt-edit-field.hpp>AgtEditField single-line text input with cursor + click-drag selection, UTF-8-aware; an EchoMode masks the display (PASSWORD dots / NO_ECHO) and suppresses clipboard copy of a masked secret

  • <agt/agt-image.hpp>AgtImage non-interactive widget that decodes PNG/JPEG/GIF/BMP via axl_pixmap_decode and renders centered inside its frame

  • <agt/agt-scrollbar.hpp>AgtScrollBar proportional-thumb scrollbar; range is (total, page), position [0, total-page], thumb extent ∝ page/total; track-click pages, thumb drags. Sibling to AgtSlider; emits AGT_SEL_COMMAND only on user-driven change (set_position is silent)

  • <agt/agt-listbox.hpp>AgtListBox scrolling single-selection list; rows are an axl-array of axl_strdup-owned char* (the data-structure policy’s first consumer), owns an AgtScrollBar child; click / keyboard / wheel; ensure_visible(int) forces a row into view

  • <agt/agt-separator.hpp>AgtSeparator thin divider line (horizontal or vertical), display-only; paints a thickness-px rule centered on its minor axis, colour from AgtPalette::separator

  • <agt/agt-spacer.hpp>AgtSpacer invisible fixed-size layout strut (a bare AgtWidget, header-only); occupies its bounds in an AgtHBox / AgtVBox but draws nothing

  • <agt/agt-toggle-button.hpp>AgtToggleButton latching button (: AgtButton); a click flips a sticky checked state and the button renders sunken + active-filled while checked (state flipped in on_clicked before the SEL_COMMAND, à la AgtCheckBox)

  • <agt/agt-group-box.hpp>AgtGroupBox titled bordered container (: AgtFrame); a line border with a caption in a top-edge notch, reserving a top padding band so child content clears the title

  • <agt/agt-status-bar.hpp>AgtStatusBar chrome strip (: AgtFrame) with a left message + optional right-aligned field; unlike AgtLabel it COPIES its text (status text is usually transient), freed in the destructor

  • <agt/agt-switch.hpp>AgtSwitch sliding on/off toggle (: AgtCheckBox); inherits the latched state + click, overrides draw to paint a rounded pill track with a knob (left=off, right=on) plus the caption. Green “on” / grey “off”

  • <agt/agt-menu-item.hpp>AgtMenuItem selectable menu row (a borderless AgtButton with left label + right-anchored shortcut + a keyboard-cursor highlighted_ state separate from mouse hover_); used by the AgtMenuBar / AgtMenu composites

  • <agt/agt-dialog.hpp>AgtDialog dialog base (: AgtWindow), modal or modeless. Modal (run(app)) blocks on the AgtApp modal stack, grabs the seat, saves/restores focus, cycles Tab / Shift+Tab over can_focus() children, and paints a frosted veil behind the content; returns a result code. Modeless (show(app)) floats over a fully-interactive parent — non-blocking, no grab, no veil; the surface is transparent with its input region clipped to the card so outside-card clicks fall through, and the outcome arrives via set_on_close(target, id). AgtMessageBox / AgtPromptDialog (composite module) build on the modal path. See the design doc’s “Dialogs — modal and modeless” section

  • <agt/agt-movable-frame.hpp>AgtMovableFrame (: AgtVBox) a draggable container with a title-bar grip strip: a press on the bar grabs the window mouse-capture and repositions the frame with per-event delta math (drift-free), clamped inside its parent, and casts a soft drop shadow so it reads as floating. The draggable dialog cards are built on it

  • <agt/agt-icon.hpp>AgtIcon (: AgtFrame) decodes a small image (PNG/JPEG/GIF/BMP via axl_pixmap_decode) and renders it at its natural size; the still, fixed-size counterpart to AgtImage’s scaled fill

  • <agt/agt-bitmap-button.hpp>AgtBitmapButton (: AgtButton) a button whose face is an icon instead of (or beside) a caption — a decoded bitmap or a procedural stock icon (AgtStyle::StockIcon); the toolbar-button building block

  • <agt/agt-spinner.hpp>AgtSpinner (: AgtWidget) an animated busy indicator; a phase()-driven rotating arc on an axl_loop timer, with a deterministic phase for visual baselines

  • <agt/agt-light.hpp>AgtLight (: AgtLabel) a status-light / LED indicator: a bezelled disc in a discrete state color (OFF/OK/WARN/ERROR/BUSY, themed via AgtPalette::light), optionally blinking on an axl_loop timer (deterministic phase for baselines), with an optional trailing caption — a column of these is a status panel

  • <agt/agt-canvas.hpp>AgtCanvas (: AgtFrame) a custom-paint surface that hands the consumer an AgtDrawContext callback to draw arbitrary content inside the frame (charts, diagrams, custom gauges)

  • <agt/agt-sprite.hpp>AgtSprite (: AgtFrame) a sprite-sheet widget: animates a sub-rectangle of a borrowed sheet bitmap, frame cell by frame cell, on a loop timer (no-copy blit_rect)

  • <agt/agt-animated-image.hpp>AgtAnimatedImage (: AgtFrame) a flip-book of decoded frames cycled on a timer; the moving counterpart to AgtImage, the still analogue of AgtSpinner’s motion

  • <agt/agt-edit-box.hpp>AgtEditBox (: AgtFrame) the multi-line, out-of-core text editor widget over an AxlPieceTree store (undo/redo, find, soft word-wrap, style buffer); the core of the axedit tool

  • Hex editor subsystem (the hexview tool + axedit’s hex view):

    • <agt/agt-hex-box.hpp>AgtHexBox (: AgtFrame) an offset | hex | ASCII editor widget over an AgtHexSource; nibble editing, selection, masked find, copy/paste, undo

    • <agt/agt-hex-data-panel.hpp>AgtHexDataPanel (: AgtFrame) a data inspector that decodes the bytes at the caret as int/float types (follows the hex box’s caret signal)

    • <agt/agt-hex-source.hpp>AgtHexSource the byte-source abstraction the hex widgets read/write through, plus its concrete backends: AgtPieceTreeHexSource (a file/text tree, read-only shared view), AgtRangeHexSource (base for fixed ranges), AgtMemoryHexSource (physical RAM) and AgtIoHexSource (x86 I/O ports), via axl-sdk axl_mem_phys_* / axl_io_*

The chain rationale: every text-bearing widget benefits from AgtFrame’s border + bg machinery; every interactive widget benefits from AgtLabel’s text rendering. Future widgets (checkboxes, radio buttons, edit fields) slot into this chain at the appropriate level — none need to duplicate the bg-fill / border-stroke / text-centering code that AgtFrame + AgtLabel already own.

AgtFrame — bordered, padded background

AgtFrame is the second link in the chain. It contributes three things every text-bearing or interactive widget benefits from:

  1. A background filleffective_bg_color() returns the color used by draw()’s background fill_rect. The method is virtual so subclasses with state-dependent backgrounds (notably AgtButton::effective_bg_color selecting among normal / hover / pressed / disabled) plug in without duplicating the bg-fill code.

  2. A configurable borderset_border_style picks between AGT_FRAME_NONE (flat), AGT_FRAME_LINE (single-color outline), AGT_FRAME_RAISED (2D bevel: hilite top-left, shadow bottom-right), AGT_FRAME_SUNKEN (inverted bevel), and AGT_FRAME_ROUNDED (rounded-corner fill via axl-sdk’s G3 path API — set_corner_radius controls the radius). set_border_width controls thickness for the rectangular styles. THICK / GROOVE / RIDGE variants are reserved further along the enum for additive future work.

    ROUNDED in v0.1 ships fill-only — RAISED/SUNKEN bevels on rounded corners need anti-aliased stroke on a rounded- rect path with hilite/shadow halves and are deferred as a polish item. border_width is accepted by the ctor for API uniformity but no border strokes are emitted in this style.

  3. Per-side paddingset_padding(top, right, bottom, left) (CSS shorthand order; or a single all value) shrinks the interior content rect that subclasses use for text / icon placement. inner_offset_x and friends return the inset from the widget’s top-left; inner_width / inner_height return the interior dimensions (clamped to 0 when the frame is too small).

Subclasses that paint on top of the frame (AgtLabel, AgtButton) call AgtFrame::draw(ctx) first to render the background + border, then paint their own content inside the inner rect.

Default palette

  • bg_color_ defaults to a mid-grey (AXL_GFX_RGB(0x60, 0x60, 0x60)).

  • hilite_color_ defaults to bg + 0x30 per channel (clamped at 0xFF) — lighter shade for the top-left of a raised bevel.

  • shadow_color_ defaults to bg - 0x30 per channel (clamped at 0) — darker shade for the bottom-right.

  • border_color_ defaults to bg - 0x40 per channel — used by the single-color AGT_FRAME_LINE outline.

set_bg_color recomputes the accents unless the caller has individually pinned them via set_hilite_color and friends. Set bg first, then customize accents, so the accent setters take effect against the right bg.

AgtLabel — text caption on a frame

AgtLabel is the third link in the chain. It adds a single- line text caption to AgtFrame’s bordered + padded background. The text renders inside the inner rect (post-border, post-padding) honoring AgtLabel::TextAlign for horizontal placement; vertical is always center for now.

Composition

AgtLabel::draw calls AgtFrame::draw(ctx) first to paint the bg + border, then positions ctx.draw_text_ttf(...) within inner_offset_x() / inner_offset_y() / inner_width() / inner_height(). Empty and NULL labels suppress the draw_text_ttf op — the frame itself still paints, so an AgtLabel with label = "" is a labelled-by-omission bg tile.

Text rendering — TTF

AgtLabel renders text via anti-aliased vector glyphs using axl_ttf_default() (axl-sdk’s built-in DejaVu Sans subset covering ASCII + Latin-1 + typographic punctuation). Pixel height is configurable per instance via set_text_size(float), default 16.0f.

AgtCheckBox and AgtRadioButton inherit the text_size() getter from AgtLabel and use the same TTF path for their labels. Bitmap text is still available via the ctx.draw_text(...) forwarder for callers that want pixel-grid console glyphs (e.g. future debug overlays); see the render module for both APIs.

Alignment

  • AGT_ALIGN_CENTER (default) — text centered horizontally within the inner rect.

  • AGT_ALIGN_LEFT — text anchored to inner_offset_x.

  • AGT_ALIGN_RIGHT — text right-anchored to inner_offset_x + inner_width - text_width.

Vertical alignment is always center (single-line labels have no other meaningful default). A future multi-line variant will introduce vertical-alignment flags.

Word-wrap (opt-in)

set_wrap(true) switches the label to a multi-line renderer: the text greedily wraps to inner_width() on space boundaries, embedded \n forces a break, and a single word wider than the inner rect overflows on its own line rather than being split mid-word. The wrapped block is vertically centered in the inner rect; each line is horizontally aligned per text_align(). Default is OFF — the single-line path (the original behavior) is untouched, so the wrap code never runs for Button / CheckBox / RadioButton.

wrapped_height(avail_width) reports the pixel height the text needs when wrapped to avail_width, so a container can size itself to fit before laying out — AgtMessageBox uses it to grow its card to the message body. Measurement uses axl_ttf_measure_prefix (no per-glyph advance arrays); each line is sliced into a stack buffer only at draw time for the null-terminated axl_ttf_draw.

When the measured text width (axl_ttf_measure at text_size()) exceeds inner_width() (a narrow widget with a long caption), all alignments fall back to inner_offset_x + LABEL_INSET so leading characters stay visible instead of vanishing off the inner rect’s left edge.

Color

  • set_label_color — used when enabled() == true. Default AXL_GFX_WHITE.

  • set_disabled_label_color — used when enabled() == false. Default AXL_GFX_RGB(0xA0, 0xA0, 0xA0).

Enabled state is inherited from AgtWidget (every widget can opt into disabling — see the core module).

Defaults

AgtLabel deliberately defaults to border_style = AGT_FRAME_NONE and border_width = 0 (diverging from AgtFrame’s default border_width = 1). Pairing NONE with a non-zero border_width would inset inner_offset_x by a wasted pixel without drawing a visible border; the AgtLabel default keeps inner_offset_x == padding_left_ for the borderless case. Bordered labels (status panels, group headers) opt in via the ctor’s border and border_width arguments.

AgtButton — interactive clickable widget

AgtButton is the leaf of the chain. It inherits text caption

  • alignment + label color from AgtLabel and border + per-side padding + accent-color machinery from AgtFrame, and contributes the hover/pressed state machine, the AGT_SEL_COMMAND-emitting click contract, and a state-dependent background palette layered onto the inherited draw pipeline via the virtual AgtFrame::effective_bg_color hook.

By default an AgtButton ships with an AGT_FRAME_RAISED border (border_width = 1) so it visually reads as interactive without any extra wiring. Drop to AGT_FRAME_NONE via the inherited AgtFrame::set_border_style for a flat button.

Click semantics

A “successful click” is the FOX / Win32 definition: button-press inside the widget bounds, followed by button-release ALSO inside the bounds. Releasing outside cancels the click — the universal “I changed my mind” affordance. This is implemented in AgtButton::on_leave, which clears both hover_ and pressed_ when the pointer leaves: a subsequent release fires no command because the dispatcher has moved on to whatever widget the pointer is now over.

Target wiring

Common case — pass target + id as trailing constructor args:

new AgtButton(&window, 100, 600, 220, 60, "Quit",
              &window, HelloWindow::ID_QUIT);

This matches FOX’s FXButton(parent, label, icon, target, sel) shape. target defaults to the button’s parent (so an un-targeted button still has a reasonable receiver) and target_id defaults to 0; set_target(...) is available for late-bound wiring.

IDs follow the FOX-style ID chain convention documented in the core module’s FOX-style ID chain section. The receiving target is any AgtObject with a matching AGT_MAP_COMMAND(ID_QUIT, ...) entry:

class MyWindow : public AgtWindow {
public:
    long on_quit(AgtObject *sender, AgtEvent *ev) {
        app_->quit();
        return 1;
    }
    AGT_DECLARE_MAP(MyWindow)
private:
    AgtApp *app_;
};

AGT_MAP_BEGIN(MyWindow, AgtWindow)
    AGT_MAP_COMMAND(ID_QUIT, &MyWindow::on_quit)
AGT_MAP_END(MyWindow, AgtWindow)

Visual states

The default palette renders well over the dark AgtWindow background (the bundled hello.efi demo uses that combo); override per-state colors via AgtButton::set_normal_color and friends to match a different theme.

  • NormalAXL_GFX_RGB(0x40, 0x60, 0x90)

  • HoverAXL_GFX_RGB(0x50, 0x80, 0xC0) (lighter blue)

  • PressedAXL_GFX_RGB(0x20, 0x40, 0x70) (darker blue)

  • DisabledAXL_GFX_RGB(0x50, 0x50, 0x50) (neutral grey)

set_normal_color also re-syncs the AgtFrame bg accents (hilite / shadow) so the raised bevel stays calibrated to the new surface tone. Pre-1.0 regression caught by the trace test test_set_normal_color_resyncs_bevel_accents.

on_clicked virtual hook

AgtButton::on_clicked() is a no-op virtual called from on_left_button_release immediately BEFORE the SEL_COMMAND emit, on a successful click (press + release both inside, target wired). State-bearing subclasses (AgtCheckBox / AgtRadioButton) override the hook to mutate their state (toggle checked, do group mutual-exclusion) so the command target’s handler sees the NEW state when it queries the sender — e.g. static_cast<AgtCheckBox *>(sender) ->checked() returns the post-toggle value, not the pre-toggle one.

Cancelled clicks (release-after-leave) skip BOTH the hook AND the SEL_COMMAND emit, so a cancelled toggle is never partially applied. Pinned by test_agt_button_on_clicked_skipped_on_cancelled_click.

AgtCheckBox — binary toggle

AgtCheckBox inherits AgtButton for the click contract, target wiring, and hover/pressed state machine. Adds a checked_ state field, a left-side square indicator (outline

  • inner check when checked), and a left-anchored label to the right of the indicator.

The ctor drops AgtButton’s raised-border default (back to AGT_FRAME_NONE) so the indicator — not the surrounding frame — is what reads as interactive. Consumers who want a bordered checkbox re-enable via set_border_style().

effective_bg_color() overrides AgtButton’s state palette to return a neutral surface (no hover/pressed tint, that’s button-y not checkbox-y). Disabled state still reads visually via AgtButton’s disabled_color.

on_clicked() toggles checked_ and marks dirty. Combined with set_checked(bool) for programmatic state changes (doesn’t emit SEL_COMMAND — only click-driven transitions fire commands, matching FOX FXCheckButton’s contract).

Default indicator size: 14 px square, sized for a 16 px font line height. set_indicator_size, set_indicator_color, set_check_color cover theming.

AgtRadioButton — mutually-exclusive selection

Same shape as AgtCheckBox, with three differences: (1) the indicator is a circle (drawn via ctx.fill_rounded_rect with radius = size/2) with a dot when selected, (2) the state field is selected_ (semantically named), and (3) clicking triggers group mutual-exclusion.

Grouping convention — radios that share the SAME parent AND the SAME target_id form a group. No separate AgtRadioGroup class in v0.1; the parent+target_id convention covers the common case (a row/column of radios in the same container). Cross-parent grouping isn’t supported.

on_clicked() walks the parent’s siblings via metaclass comparison (no RTTI), clears any sister AgtRadioButton with the same target_id, then sets self selected_. Re-click on an already-selected radio re-emits SEL_COMMAND but doesn’t change state (matches FOX FXRadioButton: a click is a click).

AgtProgressBar — proportional value-fill display

AgtProgressBar is a non-interactive, AgtFrame-derived widget that paints a proportional fill rectangle inside the inner rect. The fill width (HORIZONTAL) or height (VERTICAL) tracks (value - min) / (max - min); horizontal fills left-to-right, vertical fills bottom-to-top — matching FOX FXProgressBar and Win32 progress bar conventions.

No AGT_SEL_COMMAND emission, no input handlers; drive via set_value from whoever owns the underlying state (a long- running task, a stream reader, a download manager). set_value clamps to [min, max] so callers can pass raw streaming values without their own clamp.

Defaults

  • value = 0, min = 0, max = 100 — reads as a percentage gauge out of the box.

  • Orientation defaults to AGT_ORIENT_HORIZONTAL.

  • Border defaults to AGT_FRAME_SUNKEN with border_width = 1 — matches Win32 / FOX where the progress bar sits in a sunken well so the filled portion reads as content inside a recess.

  • Fill color defaults to AXL_GFX_RGB(0x40, 0x90, 0x40) (green that reads as progress over the SUNKEN bg). Override via set_fill_color.

Range degeneracy

Both max <= min and value == min emit no fill rectangle — only the AgtFrame chrome paints. This is deliberate: a misconfigured range (someone passed min and max in the wrong order) reads as “no progress” rather than crashing or wrapping around with negative widths. set_range(min > max) is rejected outright — the range stays unchanged so a fix-up at the caller isn’t masked by silently swapping.

Overflow safety

The proportional-fill math is done in 64-bit: (value - min) * inner_w / (max - min). A 32-bit multiply would overflow for wide ranges (e.g. max = INT32_MAX, value = INT32_MAX / 2); the explicit int64_t widens before the multiply so the proportional fill is correct at the boundaries of the int range.

AgtSlider — draggable thumb on a track

AgtSlider is the interactive sibling of AgtProgressBar. Same FOX-shape inheritance off AgtFrame for the bg + border + padding, plus a draggable thumb fill rectangle that the user can grab and move along the inner rect. HORIZONTAL slides the thumb left- to-right with value increasing along the way; VERTICAL slides top-to-bottom with value increasing toward the TOP (FOX FXSlider convention).

Drag model

Pressing inside the widget snaps the thumb to the click pixel (matching FOX / Win32 trackbar shape — clicking the bare track relocates the thumb immediately, not just clicks-to-start-drag). Subsequent motion updates the value proportionally; release ends the drag.

The slider calls the owning AgtWindow’s capture_mouse while dragging, so the pointer wandering off-bounds still delivers motion + release events to the slider. A detached slider (no window ancestor) skips the capture call and the drag stays contained inside the slider’s own bounds — useful for unit-test fixtures.

Commands

AGT_SEL_COMMAND fires on every value change during the drag — FOX semantics, giving the handler real-time feedback for live previews (the widgets-demo wires a slider’s SEL_COMMAND to a paired AgtProgressBar::set_value). Drag motion that doesn’t quantize to a new integer value emits nothing.

set_value (programmatic) does NOT emit SEL_COMMAND — only user-driven changes dispatch, mirroring FOX FXSlider’s contract.

Defaults

  • value=0, min=0, max=100.

  • Orientation defaults to AGT_ORIENT_HORIZONTAL.

  • Border defaults to AGT_FRAME_RAISED with border_width=1 (matches AgtButton — reads as interactive).

  • Thumb extent along the orient axis defaults to 20 pixels; set_thumb_size clamps at the lower edge to 4 px (anything narrower stops reading as a thumb) and at the upper edge to the inner extent at draw time.

  • Thumb color defaults to AXL_GFX_RGB(0xC0, 0xC0, 0xC0) (a neutral light grey that reads over the AgtFrame bg).

Disabled mid-drag

set_enabled(false) while a drag is in progress clears the dragging flag AND releases the mouse capture — without this, re-enabling later would leave the slider thinking it still owns the mouse.

AgtEditField — single-line text input

AgtEditField is the first widget that consumes keyboard focus. It owns a fixed-size 256-byte text buffer (UTF-8), a byte-offset cursor, and an anchor byte-offset for click-drag selection. Inherits AgtFrame for the chrome (sunken border default, matching Win32 / Motif input-field shape).

Interaction

  • Click anywhere inside the widget grabs the keyboard focus (AgtWindow::set_focus(this)) and positions the cursor at the clicked pixel, snapped to the nearest UTF-8 codepoint boundary. The pixel-to-byte binary search uses axl_ttf_measure_prefix for O(log n · measure) without materializing per-codepoint advance arrays.

  • Click + drag selects a range. The press grabs the mouse capture (AgtWindow::capture_mouse(this)) so the pointer leaving the widget bounds during a drag-select doesn’t abort the selection.

  • Backspace deletes one codepoint before the cursor (or the selection if any). Delete removes one codepoint after the cursor. Both step in full UTF-8 codepoints — never slicing a multi-byte sequence in half.

  • Left / Right arrows move the cursor one codepoint at a time, clearing the selection.

  • Printable Unicode characters insert at the cursor, replacing the selection if any. Unicode arrives via axl-input’s unicode field (UCS-2 codepoint); the widget encodes back to UTF-8 before insertion.

  • Enter (CR or LF unicode) emits AGT_SEL_COMMAND — matches FOX FXTextField’s “user finished editing” event. After the emit, this may be destroyed by the target’s handler (same UAF guard convention as AgtButton / AgtSlider).

Not in v0.1: clipboard, Home / End, Shift+arrow selection, word-by-word navigation, multi-line. These slot in cleanly later — the surface is deliberately minimal so the primary use case (entering device names, IP addresses, passwords in pre-boot diagnostics) ships without expanding the test surface beyond what’s strictly justified.

State

  • text() / length() — current buffer contents.

  • cursor() / anchor() — byte offsets. has_selection() is anchor() != cursor(); selection_start() / selection_end() return the low / high bounds regardless of which side anchored first.

  • set_cursor(byte_offset) / set_selection(anchor, cursor) — programmatic positioning. Each clamps to [0, length] and snaps DOWN to the nearest UTF-8 boundary, defending against callers passing mid-codepoint offsets.

  • focused() — true between FOCUSIN and FOCUSOUT. The cursor paints only while focused; the selection highlight paints regardless of focus state (matches Win32 — you can see what’s selected even while another widget has the focus).

Defaults

  • value = "" (empty buffer).

  • Border AGT_FRAME_SUNKEN, border_width = 1.

  • Background AXL_GFX_RGB(0x20, 0x20, 0x28) (dark slate that reads white text + blue selection clearly).

  • Text color AXL_GFX_WHITE; selection bg AXL_GFX_RGB(0x40, 0x60, 0x90) (muted blue); cursor color AXL_GFX_WHITE. Override via set_text_color, set_selection_bg_color, set_cursor_color.

  • MAX_TEXT_LEN = 256 bytes. Insertions that would overflow are rejected at the edge; set_text truncates with a codepoint-boundary snap so a multi-byte glyph never gets sliced.

AgtImage — decoded pixmap display

AgtImage is a non-interactive AgtFrame-derived widget that decodes a PNG / JPEG / GIF / BMP byte buffer via axl-sdk’s axl_pixmap_decode and renders the result centered inside the frame’s inner rect. No input handlers; drive via set_image when the underlying bytes change (e.g. a remote logo arrives mid-session).

Ownership

The widget owns the decoded AxlGfxBuffer * for its lifetime and frees it via axl_gfx_buffer_free in the destructor. Input bytes are NOT referenced after the ctor / set_image call — axl_pixmap_decode copies the decoded pixels into a fresh buffer. Callers can free the source bytes immediately.

Failure handling

If axl_pixmap_decode returns NULL (bad bytes, unsupported format, allocation failure), the widget stays in the “empty placeholder” state: image_buffer() returns NULL and draw paints only the frame chrome. No exception; no abort. A failed set_image replacement always frees the OLD buffer first, so there’s no partial state where stale pixels keep rendering after a failed replace.

Centering, not scaling

Images smaller than the inner rect render centered (integer math: (inner_w - image_w) / 2). Images larger than the inner rect are CLIPPED — a push_clip rect matching inner bounds restricts the blit, and the centered position becomes negative so the visible portion is the image’s middle. Scaling is deferred until a consumer asks; pre-boot UI use cases (vendor logos at fixed asset sizes, small status icons) don’t need it.

Draw trace shape

[0] FillRect      (AgtFrame chrome bg)
[1..N] FillRect   (AgtFrame border strips if border_width > 0)
[N+1] PushClip    (inner rect)
[N+2] Blit        (image pixels at centered position)
[N+3] PopClip

When the widget is empty (no decoded buffer), only the AgtFrame chrome paints — no PushClip, no Blit, no PopClip.

See the API Reference section for the full surface.

AgtMenuItem — selectable menu row

AgtMenuItem inherits AgtButton so it reuses the press/release/hover state machine and the AGT_SEL_COMMAND-on- click contract verbatim — a menu item IS a button that renders like a menu row. It is consumed by the menu composites (AgtMenuBar for top-level titles, AgtMenu for the drop-down rows; both in the composite module).

Two visual departures from AgtButton:

  1. No raised bevel — the ctor drops to AGT_FRAME_NONE. A menu row’s affordance is its selection background, not a 3D edge (matches Win32 / GTK menus).

  2. Left label + optional right-anchored shortcut — the caption left-aligns; an optional shortcut hint (“Ctrl+S”) right-aligns in a dimmer color. The shortcut is display-only in v0.1 (AGT has no accelerator binding yet); the consumer wires the actual key handling.

Highlight vs hover

AgtMenuItem carries a highlighted_ flag SEPARATE from AgtButton’s hover_. highlighted_ is the keyboard-navigation cursor (driven by the menu’s arrow-key handling in Phase 2.7f-B); hover_ is the mouse-pointer state. Both paint the same selection color via effective_bg_color() so the two cursors look identical — but keeping them on separate fields means moving the keyboard cursor never clobbers the mouse hover state and vice-versa.

effective_bg_color() returns the selection color when hover() || highlighted(), the menu surface otherwise, and delegates to AgtButton::effective_bg_color() for the disabled tone (same trick AgtCheckBox uses).

Row insets

LABEL_INSET / SHORTCUT_INSET / LABEL_SHORTCUT_GAP are public static constexpr so AgtMenu can size its auto-width from the same numbers — single source of truth, so tuning the row layout re-sizes the popup in lock-step.

API Reference

AgtFrame

class AgtFrame : public AgtWidget

Subclassed by AgtAnimatedImage, AgtCanvas, AgtChart, AgtComboBox, AgtDial, AgtEditBox, AgtEditField, AgtExpander, AgtFormBrowser, AgtGrid, AgtGroupBox, AgtHBox, AgtHexBox, AgtHexDataPanel, AgtIcon, AgtImage, AgtLabel, AgtListBox, AgtLogView, AgtMenu, AgtPaned, AgtProgressBar, AgtScale, AgtScrollBar, AgtScrollFrame, AgtSlider, AgtSpinBox, AgtSprite, AgtStatusBar, AgtTabView, AgtTableBase, AgtTerminal, AgtTooltip, AgtTreeView, AgtVBox

Public Types

enum BorderStyle

Border decoration style. Values are stable — new variants (THICK / GROOVE / RIDGE) append at the end so existing callers stay binary-compatible.

Values:

enumerator AGT_FRAME_NONE

no border drawn

enumerator AGT_FRAME_LINE

single-color rectangular outline

enumerator AGT_FRAME_RAISED

2D bevel: hilite TL, shadow BR

enumerator AGT_FRAME_SUNKEN

inverted bevel: shadow TL, hilite BR

enumerator AGT_FRAME_ROUNDED

Rounded-corner fill via ctx.fill_rounded_rect. Border stroke for ROUNDED is deferred (RAISED/SUNKEN bevels don’t compose with rounded corners in this batch — fill- only is the v0.1 shape). Use set_corner_radius to control the corner radius; default 0 produces a plain rect (same as AGT_FRAME_NONE visually, but still routed through axl_gfx_fill_rounded_rect).

Public Functions

AgtFrame(AgtWidget *parent, int x, int y, int w, int h, BorderStyle border = AGT_FRAME_NONE, int border_width = 1) noexcept

Construct a frame at the given parent-local bounds. border defaults to AGT_FRAME_NONE so a bare AgtFrame is just a colored rectangle until the caller opts in.

inline BorderStyle border_style() const noexcept
void set_border_style(BorderStyle s) noexcept
inline int border_width() const noexcept
void set_border_width(int w) noexcept
inline int padding_top() const noexcept
inline int padding_right() const noexcept
inline int padding_bottom() const noexcept
inline int padding_left() const noexcept
void set_padding(int top, int right, int bottom, int left) noexcept

Set all four paddings. Argument order matches CSS shorthand: top, right, bottom, left. Marks dirty if any value changed.

inline void set_padding(int all) noexcept

Convenience: same value for all four sides.

inline int inner_offset_x() const noexcept

Interior content area in widget-local coordinates. offset_* is the inset from the widget’s (x, y) — i.e. add it to ctx.origin_x() / origin_y() at draw time to get the absolute content top-left. inner_width / inner_height are the content dimensions, clamped to 0 (never negative) when the frame is too small to hold any inner area.

corner_radius does NOT shrink the inner rect — rounded corners only affect the visible bg silhouette, not the usable interior bounds. Subclasses (AgtLabel) lay out content inside the same rect regardless of border_style.

inline int inner_offset_y() const noexcept
int inner_width() const noexcept
int inner_height() const noexcept
inline float corner_radius() const noexcept

Corner radius for AGT_FRAME_ROUNDED. Ignored by other border styles. Clamped by axl-gfx to min(w, h) / 2 at draw time, so values larger than half the smaller side produce a pill (capsule) shape. Default 0.

void set_corner_radius(float r) noexcept
inline AxlGfxPixel bg_color() const noexcept
inline AxlGfxPixel hilite_color() const noexcept
inline AxlGfxPixel shadow_color() const noexcept
inline AxlGfxPixel border_color() const noexcept
void set_bg_color(AxlGfxPixel c) noexcept

Change the background color and (unless the caller has already overridden them individually) recompute the hilite / shadow / border accents from it. To customize accents: set bg first, then set each accent individually.

void set_themed_bg(AxlGfxPixel AgtPalette::* role) noexcept

Set the widget’s THEMED-default background by palette role (a pointer-to-AgtPalette-member, e.g. &AgtPalette::well_bg) rather than a fixed color. Unlike set_bg_color, this is NOT a consumer pin: restyle() re-reads the live palette through the role on a theme swap, so a widget that picks its surface token this way recolors live. Widgets call this in their constructor instead of set_bg_color(AgtPalette::current().token); accents derive from the resolved color exactly as set_bg_color.

void set_hilite_color(AxlGfxPixel c) noexcept
void set_shadow_color(AxlGfxPixel c) noexcept
void set_border_color(AxlGfxPixel c) noexcept
virtual AxlGfxPixel effective_bg_color() const noexcept

Color used for draw()’s background fill_rect. Default returns bg_color(); subclasses (notably AgtButton) override to swap palettes based on state — letting AgtFrame stay agnostic about WHY the color is what it is, just painting whatever the subclass declares.

void set_bg_gradient(AxlGfxPixel top, AxlGfxPixel bottom) noexcept

Fill the background with a vertical gradient (top at the top edge → bottom at the bottom) instead of the flat effective_bg_color(). A glossy/raised look for buttons, panels, etc.

Overrides state coloring: the gradient is drawn as-is in every state, so a subclass whose effective_bg_color() varies by state (e.g. AgtButton’s normal/hover/pressed) loses that feedback while a gradient is set. Honored by both the square and AGT_FRAME_ROUNDED fills (the rounded path uses the AA rounded-gradient primitive).

void set_bg_gradient_auto(bool on) noexcept

Enable a STATE-AWARE background gradient: at draw time the top is effective_bg_color() (so it tracks normal/hover/pressed) and the bottom is that color darkened by AgtPalette::button.gradient_sheen. The glossy default for buttons; unlike set_bg_gradient it doesn’t freeze the state coloring.

void clear_bg_gradient() noexcept

Drop the background gradient (explicit or auto) — flat fill again.

inline bool has_bg_gradient() const noexcept

True while a background gradient is active.

inline bool bg_gradient_is_auto() const noexcept

Gradient introspection for the renderer (AgtStyle::draw_frame). In AUTO mode the endpoints are derived at draw time from the live effective_bg_color() + the themed sheen; in explicit mode they are the stored set_bg_gradient colors.

inline AxlGfxPixel bg_gradient_top() const noexcept
inline AxlGfxPixel bg_gradient_bottom() const noexcept
void draw(AgtDrawContext &ctx) override

Paints background then border on top. Override only if the whole compositing order needs to change; subclasses that just want to paint content inside the inner rect should call AgtFrame::draw(ctx) then do their own work.

void restyle() noexcept override

Re-snapshot the background + derived accents from the live palette() (Qt changeEvent shape) unless the consumer pinned them via set_bg_color / set_*_color. Called by restyle_tree() after a palette swap.

Public Static Functions

static AxlGfxPixel shade(AxlGfxPixel c, int delta) noexcept

Channel-wise lighten (+delta) / darken (−delta) with saturation clamping; alpha preserved. The shared bevel/gloss shading helper — used by AgtFrame’s accent derivation and the renderer’s auto-gradient ramp.

static inline AgtFrameBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtWidget::ID_LAST

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

AgtLabel

class AgtLabel : public AgtFrame

Subclassed by AgtButton, AgtLight

Public Types

enum TextAlign

Horizontal alignment of the label within the inner rect. Vertical alignment is always center. Stable enum values (CENTER == 0 = default) so callers using the default don’t need to import the enum just to construct an AgtLabel.

Values:

enumerator AGT_ALIGN_CENTER
enumerator AGT_ALIGN_LEFT
enumerator AGT_ALIGN_RIGHT

Public Functions

AgtLabel(AgtWidget *parent, int x, int y, int w, int h, const char *label, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept

Construct a label. border and border_width

default to NONE / 0 — a bare AgtLabel renders text on a solid bg rectangle with no border, matching the “just a piece of

text” use case. Bordered labels (status panels, group headers) opt in by passing a non-NONE style.

Deliberate divergence from AgtFrame’s default border_width = 1: pairing AGT_FRAME_NONE with border_width = 1 would inset inner_offset_x by a wasted pixel even though no border is drawn. AgtLabel’s border-disabled default mirrors that visually.

AgtLabel(AgtWidget *parent, const char *label, BorderStyle border = AGT_FRAME_NONE, int border_width = 0) noexcept

Geometry-free ctor (FOX new FXLabel(p,"text")): no x/y/w/h — the label reports its measured text size via natural_*() and a layout container sizes/places it. Both axes start AUTO (un-pinned); a later set_bounds / set_fixed_* pins them.

int natural_width() const noexcept override

Natural size = measured text + the frame’s border/padding chrome. With wrap() ON, the natural size measures the **explicit-newline (\n) block** — the widest \n-delimited line for the width, the line count for the height — which is width-INDEPENDENT, so a multi-line caption (and AgtButton, which inherits this) auto-sizes to its block. (Pure word-wrap to a narrower width than the natural block still needs a fixed width — that wrap target the single-pass box layout can’t negotiate.)

int natural_height() const noexcept override
inline const char *label() const noexcept
void set_label(const char *label) noexcept
inline int mnemonic_index() const noexcept

Index (into label()) of a single character to underline — the keyboard MNEMONIC cue (e.g. the “F” of “File”). -1 (default) draws no underline. Honored only on the single-line caption path; the wrapped path ignores it. AgtMenuItem::set_mnemonic derives this from a letter; most widgets never set it.

void set_mnemonic_index(int index) noexcept
inline TextAlign text_align() const noexcept
void set_text_align(TextAlign a) noexcept
inline AxlGfxPixel label_color() const noexcept
inline AxlGfxPixel disabled_label_color() const noexcept
void set_label_color(AxlGfxPixel c) noexcept
void set_disabled_label_color(AxlGfxPixel c) noexcept
inline float text_size() const noexcept

Font pixel height passed to axl_ttf_draw / axl_ttf_measure. Default 16.0f — close to the bitmap LaffStd height the pre-TTF AgtLabel used, so a label that fits in its inner rect at the bitmap path keeps fitting after TTF adoption. Subclasses (AgtCheckBox / AgtRadioButton) inherit and reuse this setter for their own label rendering.

void set_text_size(float px_size) noexcept
inline bool wrap() const noexcept

Word-wrap mode. Default OFF — the label renders a single line (vertically centered), the original behavior. When ON, the text greedily wraps to the inner width on word (space) boundaries, honoring embedded \n as forced breaks; the wrapped block is vertically centered in the inner rect and each line is horizontally aligned per text_align(). A single word wider than the inner rect overflows on its own line rather than being split mid-word. Used by AgtMessageBox for multi-line message bodies; reusable for any flowed text (help blurbs, hints).

void set_wrap(bool on) noexcept
int wrapped_height(int avail_width) const noexcept

Pixel height needed to render the current label wrapped to avail_width (the content width, i.e. what inner_width() would be). Returns one line-height for empty / NULL text. Lets a container (e.g. AgtMessageBox) size itself to fit the wrapped body before laying out. Independent of the wrap() flag — it always measures as if wrapping.

void draw(AgtDrawContext &ctx) override

Delegates to the renderer (AgtStyle::draw_label): bg + border then the caption. Suppressed for NULL / empty labels — the frame still paints.

void draw_caption(AgtDrawContext &ctx) const

Paint just the caption (single-line aligned, or wrapped) inside the inner rect — the text half of the label, with the frame already painted. Called by AgtStyle::draw_label after the frame chrome; the intricate wrap layout stays widget-owned (Qt keeps complex text layout in the widget, not the style).

void restyle() noexcept override

Re-snapshot the label/disabled-label colors + text size from the live palette after a theme swap.

Public Static Functions

static inline AgtLabelBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtLabel::build(p).label("Status").create() (omit geometry for a size-to-text label a layout places). See agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

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

AgtButton

class AgtButton : public AgtLabel

Subclassed by AgtBitmapButton, AgtCheckBox, AgtMenuItem, AgtRadioButton, AgtToggleButton

Public Functions

AgtButton(AgtWidget *parent, int x, int y, int w, int h, const char *label, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept

Construct a button with the common shape (parent, geometry, label, target, target_id) — the SEL_COMMAND emitted on click hits target with message_id == target_id. Default target = parent widget; default target_id = 0 (override via the trailing args or post-construction set_target).

7 constructor args is at the edge of “too many”; if a future revision adds an icon and/or hint string, lift these into an AgtButtonOpts struct value passed by const ref. For v0.1 the flat arg list matches FOX’s FXButton(parent, label, icon, target, sel) shape.

AgtButton(AgtWidget *parent, const char *label, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept

Geometry-free ctor (FOX new FXButton(p,"label",…,target,sel)): no x/y/w/h — the button sizes to its label via natural_*() and a layout container places it. Both axes start AUTO; set_bounds/set_fixed_* pins them.

int natural_width() const noexcept override

Natural size = the label’s natural size + the button’s comfort padding.

int natural_height() const noexcept override
void set_target(AgtObject *target, uint16_t target_id) noexcept

Target widget that receives AGT_SEL_COMMAND with message_id == target_id on each successful click. NULL target = no command emitted (button still tracks hover/press state visually).

inline AgtObject *target() const noexcept
inline uint16_t target_id() const noexcept
void set_enabled(bool enabled) noexcept override

Override AgtWidget::set_enabled to also clear hover_/ pressed_ on a disable transition — a button disabled mid-press would otherwise re-emit a click on re-enable (the LEAVE handler that normally clears the state never fired).

inline bool pressed() const noexcept

Visual feedback state (for tests + inspection). pressed is true between a button-press inside the widget and the matching release (cleared if the pointer leaves first). hover is true while the pointer is inside the bounds.

inline bool hover() const noexcept
inline uint32_t focus_policy() const noexcept override

AgtButton is the Tab-cycle focus base for the state-bearing subclasses (CheckBox, RadioButton) too — inheriting buttons all opt in to focus cycling.

void set_normal_color(AxlGfxPixel c) noexcept

Override state-specific bg colors. Pixel values come from axl-gfx’s AXL_GFX_RGB. Default palette renders well over the dark AgtWindow background; consumers can tune to their theme. Label and disabled-label colors are inherited from AgtLabel (set_label_color, set_disabled_label_color).

void set_hover_color(AxlGfxPixel c) noexcept
void set_pressed_color(AxlGfxPixel c) noexcept
void set_disabled_color(AxlGfxPixel c) noexcept
void set_flat(bool flat) noexcept

Flat (toolbar) mode: at rest the button paints in the header tone (surface_title_bg) so the icon floats on the bar with no tile; a hover / press still shows an accent pill. Disables the gloss and the border, and rounds the corners for the highlight. The standard toolbar-button idiom — used by AgtToolBar.

inline bool flat() const noexcept
AxlGfxPixel effective_bg_color() const noexcept override

State-dependent background color. The renderer’s draw_frame calls this virtual to pick the bg fill color, so AgtButton composes onto the frame+label rendering without reimplementing the bg+border+text pipeline.

void draw(AgtDrawContext &ctx) override

Delegates to the renderer (AgtStyle::draw_button).

void restyle() noexcept override

Re-snapshot the state palette (accent / hover / pressed / disabled) + re-apply the themed surface tone after a theme swap.

uint32_t state_flags() const noexcept override

Adds HOVERED / PRESSED to the base flags.

virtual void on_clicked() noexcept

Called from on_left_button_release immediately BEFORE the SEL_COMMAND is emitted on a successful click. Default no-op; state-bearing subclasses (AgtCheckBox / AgtRadioButton) override to mutate their state (toggle checked, do group mutual-exclusion) so the command target’s handler sees the NEW state when it queries the sender (e.g. cb->checked() returns the post-toggle value, not the pre-toggle one).

The hook fires only on a successful click — i.e. when the press-then-release-both-inside condition holds AND target_ is set AND not self. Cancelled clicks (release-after-leave) skip the hook AND skip the command, so a cancelled toggle is not partially applied.

long on_left_button_press(AgtObject *sender, AgtEvent *ev)
long on_left_button_release(AgtObject *sender, AgtEvent *ev)
long on_enter(AgtObject *sender, AgtEvent *ev)
long on_leave(AgtObject *sender, AgtEvent *ev)
long on_key_press(AgtObject *sender, AgtEvent *ev)

Space / Enter activate a focused button — same path as a click (on_clicked() then emit the command). Inherited by CheckBox / ToggleButton / Switch / BitmapButton so they activate by keyboard.

Public Static Functions

static inline AgtButtonBuilder build(AgtWidget *parent) noexcept

Fluent builder entry point: AgtButton::build(parent).label("OK") .size(100,36).target(&t,id).create(). An alternative to the positional ctors that names every field at the call site (kills the “which arg is which” problem in the 7-arg form). See AgtButtonBuilder

  • agt-builder.hpp. The positional ctors stay — build() is additive.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtLabel::ID_LAST

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

static constexpr int NATURAL_PAD_X = 18

Comfortable padding added around the measured label so the caption doesn’t hug the button’s bevel (FOX FXButton default margins).

static constexpr int NATURAL_PAD_Y = 8

AgtCheckBox

class AgtCheckBox : public AgtButton

Subclassed by AgtSwitch

Public Types

FOX-style message-ID chain. Ships the standard check commands (FOX FXWindow shape) — target a checkbox with ID_CHECK / ID_UNCHECK to set its state with no custom handler.

Values:

enumerator ID_CHECK

set_checked(true)

enumerator ID_UNCHECK

set_checked(false)

enumerator ID_LAST

Public Functions

AgtCheckBox(AgtWidget *parent, int x, int y, int w, int h, const char *label, AgtObject *target = nullptr, uint16_t target_id = 0, bool checked = false) noexcept
AgtCheckBox(AgtWidget *parent, const char *label, AgtObject *target = nullptr, uint16_t target_id = 0, bool checked = false) noexcept

Geometry-free ctor (FOX new FXCheckButton(p,"label")): no x/y/w/h — the checkbox sizes to indicator + gap + label via natural_*() and a layout container places it. Both axes start AUTO.

int natural_width() const noexcept override

Natural size = indicator + gap + label (not the button-face padding AgtButton uses), tall enough for the larger of indicator / text.

int natural_height() const noexcept override
inline bool checked() const noexcept
void set_checked(bool c) noexcept

Set checked state and mark dirty. No-op when unchanged. Does NOT emit SEL_COMMAND — only click-driven transitions fire commands (matches FOX FXCheckButton’s contract).

long on_cmd_check(AgtObject *sender, AgtEvent *ev)
long on_cmd_uncheck(AgtObject *sender, AgtEvent *ev)
inline int indicator_size() const noexcept

Indicator size in pixels (square outline + inner check when checked). Default 14; sized for a 16px label.

void set_indicator_size(int px) noexcept
inline AxlGfxPixel indicator_color() const noexcept

Indicator outline color. Default WHITE.

void set_indicator_color(AxlGfxPixel c) noexcept
inline AxlGfxPixel check_color() const noexcept

Inner fill color when checked. Default WHITE.

void set_check_color(AxlGfxPixel c) noexcept
AxlGfxPixel effective_bg_color() const noexcept override

Neutral surface color — no hover/pressed tint (that’s button-y). Returns disabled_color() when !enabled() so disabled state still reads visually; otherwise the inherited AgtFrame bg_color().

void on_clicked() noexcept override

Toggle checked_ and mark dirty. Called from AgtButton::on_left_button_release just before the SEL_COMMAND emit, so the target’s handler sees the new state via sender->checked().

void draw(AgtDrawContext &ctx) override

Delegates to the renderer (AgtStyle::draw_checkbox): bg, the indicator (outline + optional inner check), then the trailing label.

void restyle() noexcept override

Re-snapshot indicator/check colors + size and re-assert the flat, borderless surface after a theme swap.

uint32_t state_flags() const noexcept override

Adds CHECKED when ticked.

Public Static Functions

static inline AgtCheckBoxBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

AgtRadioButton

class AgtRadioButton : public AgtButton

Public Functions

AgtRadioButton(AgtWidget *parent, int x, int y, int w, int h, const char *label, AgtObject *target = nullptr, uint16_t target_id = 0, bool selected = false) noexcept
inline bool selected() const noexcept
void set_selected(bool s) noexcept

Set selected state and mark dirty. No-op when unchanged. Does NOT trigger group mutual-exclusion — only click-driven selection walks siblings. Use this to pre-set an initial selection (e.g. in demo / dialog init code).

inline int indicator_size() const noexcept

Indicator size in pixels (circle outline + inner dot when selected). Default 14.

void set_indicator_size(int px) noexcept
inline AxlGfxPixel indicator_color() const noexcept
void set_indicator_color(AxlGfxPixel c) noexcept
inline AxlGfxPixel dot_color() const noexcept

Inner dot color when selected. Default WHITE.

void set_dot_color(AxlGfxPixel c) noexcept
AxlGfxPixel effective_bg_color() const noexcept override
void on_clicked() noexcept override

Walk parent’s children — for each AgtRadioButton sibling with the same target_id, clear its selected_ and mark it dirty. Then set self selected. Called from AgtButton’s click contract just before SEL_COMMAND fires, so the target handler sees the post-exclusion state.

void draw(AgtDrawContext &ctx) override

Delegates to the renderer (AgtStyle::draw_radio): bg, a circle indicator (outline + optional inner dot), then a trailing label.

void restyle() noexcept override

Re-snapshot indicator/dot colors + size and re-assert the flat, borderless surface after a theme swap.

uint32_t state_flags() const noexcept override

Adds CHECKED when selected.

bool same_focus_group(const AgtWidget &other) const noexcept override

True if other is a radio in this radio’s group (same concrete class + parent + target_id — matches on_clicked’s rule), so the focus traversal collapses the group to one Tab stop.

inline bool is_group_selected() const noexcept override

The selected radio is the group’s Tab-traversal representative.

long on_key_press(AgtObject *sender, AgtEvent *ev)

Arrow keys (Up/Left = previous, Down/Right = next, wrapping) move focus + selection to the adjacent radio in the group and emit its command — Qt/GTK radio-group keyboard behavior. Non-arrows fall through (Tab leaves the group, Space/Enter via AgtButton).

Public Static Functions

static inline AgtRadioButtonBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtButton::ID_LAST

FOX-style message-ID chain.

AgtProgressBar

class AgtProgressBar : public AgtFrame

Public Types

enum Orientation

Fill direction. Stable enum values (HORIZONTAL == 0 = default) so callers using the default don’t need to import the enum just to construct an AgtProgressBar.

Values:

enumerator AGT_ORIENT_HORIZONTAL
enumerator AGT_ORIENT_VERTICAL

Public Functions

AgtProgressBar(AgtWidget *parent, int x, int y, int w, int h, int value = 0, int min = 0, int max = 100, Orientation orient = AGT_ORIENT_HORIZONTAL) noexcept

Construct a progress bar at x, y of size (w, h). value is clamped to [min, max] at construction; if max <= min the range is degenerate and the fill stays zero-width regardless of value (so a misconfigured range reads as “no progress” rather than crashing or wrapping). Default border is AGT_FRAME_SUNKEN with border_width = 1 — matches Win32 / FOX where progress bars sit in a sunken well so the filled portion reads as content inside a recess.

inline int value() const noexcept

Current value, clamped to [min, max].

void set_value(int v) noexcept

Set the current value, clamped to [min, max]. Mark-dirties only on actual change (no spurious redraw for set-to-same). Caller does not need to clamp themselves — pass a raw streaming value and the widget tames it.

inline int min() const noexcept
inline int max() const noexcept
void set_range(int min, int max) noexcept

Replace the range. Rejects min > max (range stays unchanged in that case — same pattern as AgtLabel::set_text_size rejecting non-positive sizes). Re-clamps the current value to the new range.

inline Orientation orientation() const noexcept
void set_orientation(Orientation o) noexcept
inline AxlGfxPixel fill_color() const noexcept

Color of the proportional fill. Default AXL_GFX_RGB(0x40, 0x90, 0x40) — a green that reads as progress over the default sunken AgtFrame bg. Override to theme.

void set_fill_color(AxlGfxPixel c) noexcept
void set_fill_gradient(AxlGfxPixel top, AxlGfxPixel bottom) noexcept

Fill the progress bar with a vertical gradient (topbottom) instead of the flat fill_color() — a glossy bar.

void clear_fill_gradient() noexcept

Drop the fill gradient, back to a flat fill_color().

inline bool has_fill_gradient() const noexcept
inline AxlGfxPixel fill_gradient_top() const noexcept

Fill-gradient endpoints (valid when has_fill_gradient()) — read by the renderer (AgtStyle::draw_progress).

inline AxlGfxPixel fill_gradient_bottom() const noexcept
void draw(AgtDrawContext &ctx) override

Paints AgtFrame::draw (bg + border) then the proportional fill rectangle inside the inner rect. Degenerate ranges (max <= min) or zero progress (value == min) emit no fill — only the AgtFrame chrome. Full progress (value == max) fills the entire inner rect.

void restyle() noexcept override

Re-snapshot the fill color from the live palette on a theme swap.

Public Static Functions

static inline AgtProgressBarBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtProgressBar::build(p).bounds(...).range(0,N) .value(k).create(). 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).

AgtScale

class AgtScale : public AgtFrame

Public Types

enum Orientation

Track direction. Stable values (HORIZONTAL == 0 = default) so a caller using the default needn’t import the enum.

Values:

enumerator AGT_ORIENT_HORIZONTAL
enumerator AGT_ORIENT_VERTICAL
enum Indicator

Indicator style. Stable values (FILL == 0 = default).

Values:

enumerator AGT_SCALE_FILL

proportional bar min→value (band-colored)

enumerator AGT_SCALE_NEEDLE

band zones + a thin marker at the value

Public Functions

AgtScale(AgtWidget *parent, int x, int y, int w, int h, int value = 0, int min = 0, int max = 100, Orientation orient = AGT_ORIENT_HORIZONTAL) noexcept

Construct a scale at x, y of size (w, h). value is clamped to [min, max]; a degenerate range (max <= min) reads as “at min”. Default border is AGT_FRAME_SUNKEN width 1 — the track sits in a recessed well (matching AgtProgressBar).

inline int value() const noexcept
void set_value(int v) noexcept

Set the value, clamped to [min, max]. Mark-dirties only on change.

inline int min() const noexcept
inline int max() const noexcept
void set_range(int min, int max) noexcept

Replace the range (rejects min > max; re-clamps the value).

inline Orientation orientation() const noexcept
void set_orientation(Orientation o) noexcept
inline Indicator indicator() const noexcept
void set_indicator(Indicator i) noexcept
inline int tick_interval() const noexcept

Major tick spacing in value units (0 = no ticks).

inline int minor_divisions() const noexcept

Sub-divisions drawn between each major tick (1 = none).

void set_ticks(int major_step, int minor_div = 1) noexcept

Set the major tick spacing (value units) and the minor sub-division count per major interval. major_step <= 0 disables ticks; minor_div is clamped to >= 1.

inline int band_count() const noexcept
const Band &band(int i) const noexcept
void set_bands(const Band *bands, int count) noexcept

Copy up to MAX_BANDS bands (extra are dropped). Marks dirty.

void clear_bands() noexcept
int band_at(int v) const noexcept

Index of the first band whose [lo, hi] contains v, or -1.

inline AxlGfxPixel fill_color() const noexcept
inline AxlGfxPixel tick_color() const noexcept
inline AxlGfxPixel needle_color() const noexcept
void set_fill_color(AxlGfxPixel c) noexcept

Pin a color (consumer override that survives a theme swap).

void set_tick_color(AxlGfxPixel c) noexcept
void set_needle_color(AxlGfxPixel c) noexcept
AxlGfxPixel active_fill_color() const noexcept

The fill color used RIGHT NOW: the color of the band containing the current value (FILL mode’s zone coloring), or fill_color() when no band applies. The renderer reads this so band-coloring stays widget-owned.

int track_length() const noexcept

Length of the track in pixels along the orient axis (inner width for horizontal, inner height for vertical).

int value_position(int v) const noexcept

Pixel offset from the MIN end of the track for value v (0 at min, track_length() at max). v is clamped to the range; a degenerate range maps to 0. Orientation-independent — the renderer maps the offset to the correct physical end.

void draw(AgtDrawContext &ctx) override

Paints the well chrome then the bands / indicator / ticks.

void restyle() noexcept override

Re-snapshot the fill / tick / needle colors from the live palette, unless a consumer pinned them.

Public Static Functions

static inline AgtScaleBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtScale::build(p).bounds(...).range(0,N).value(k) .ticks(10).create(). See agt-builder.hpp.

Public Static Attributes

static constexpr int MAX_BANDS = 8

Maximum threshold bands stored (copied, fixed array — no alloc).

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

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

struct Band

A colored threshold zone over [lo, hi] (inclusive), in value units. Bands are app-specific (safe/warn/critical limits), so the color is given explicitly rather than themed.

Public Members

int lo
int hi
AxlGfxPixel color

AgtDial

class AgtDial : public AgtFrame

Public Functions

AgtDial(AgtWidget *parent, int x, int y, int w, int h, int value = 0, int min = 0, int max = 100) noexcept

Construct a dial at x, y of size (w, h). value is clamped to [min, max]; a degenerate range (max <= min) parks the needle at min.

inline int value() const noexcept
void set_value(int v) noexcept
inline int min() const noexcept
inline int max() const noexcept
void set_range(int min, int max) noexcept
inline double sweep_deg() const noexcept

Total angular sweep in degrees (the arc the needle travels from min to max). Default 270. Clamped to (0, 360]. At a full 360° sweep min and max land on the SAME point (bottom / 6 o’clock), so a tick at each coincides — expected for a full-circle gauge.

void set_sweep_deg(double deg) noexcept
double value_angle(int v) const noexcept

Compass angle (degrees, 0 = up, clockwise) the needle points at for value v (clamped to the range). Monotonic in v — not wrapped to [0, 360) — so a 270° gauge yields min=225°, mid=360°, max=495°.

inline double start_angle() const noexcept

Needle angle at min (= 360 - sweep/2) and at max (= start+sweep).

inline double end_angle() const noexcept
inline int tick_interval() const noexcept
inline int minor_divisions() const noexcept
void set_ticks(int major_step, int minor_div = 1) noexcept

Major tick spacing (value units; 0 = none) + minor sub-divisions per major interval (1 = none).

inline AxlGfxPixel needle_color() const noexcept
inline AxlGfxPixel tick_color() const noexcept
void set_needle_color(AxlGfxPixel c) noexcept
void set_tick_color(AxlGfxPixel c) noexcept
void draw(AgtDrawContext &ctx) override

Paints the face, tick marks, the needle, and the hub.

void restyle() noexcept override

Re-snapshot the needle / tick colors from the live palette unless a consumer pinned them.

Public Static Functions

static inline AgtDialBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtDial::build(p).bounds(...).range(0,N).value(k) .ticks(10).create(). See agt-builder.hpp.

Public Static Attributes

static constexpr double DEFAULT_SWEEP_DEG = 270.0

Default angular sweep — a 270° gauge (90° gap at the bottom).

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

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

AgtChart

class AgtChart : public AgtFrame

Public Functions

AgtChart(AgtWidget *parent, int x, int y, int w, int h, int capacity = DEFAULT_CAPACITY) noexcept

Construct a chart. capacity is the ring-buffer size (clamped to [1, MAX_CAPACITY]). Default border is AGT_FRAME_SUNKEN width 1 — the plot sits in a recessed well (matching AgtProgressBar / AgtScale).

inline int capacity() const noexcept
inline int count() const noexcept
double sample(int i) const noexcept

Sample at index i in [0, count()) — 0 is the OLDEST, count()-1 the newest. Out-of-range returns 0.

void push(double v) noexcept

Append a sample; once full, the oldest is dropped (scroll). Marks dirty.

void clear() noexcept

Drop all samples. Marks dirty.

inline bool auto_range() const noexcept
void set_auto_range(bool on) noexcept

Turn auto-ranging on (range tracks the data min/max) or off (keeps the last set_y_range).

void set_y_range(double lo, double hi) noexcept

Pin a fixed value range (turns auto-ranging OFF). Rejects lo >= hi.

double active_y_min() const noexcept

The value range currently in effect: the fixed range, or — in auto mode — the data’s min/max (a unit window around a single level, or [0, 1] when empty).

double active_y_max() const noexcept
inline int grid_divisions() const noexcept
void set_grid_divisions(int n) noexcept

Number of horizontal grid INTERVALS (0 = no gridlines); draws grid_divisions + 1 lines spanning the plot. Clamped to >= 0.

int sample_x(int i) const noexcept

Widget-local x pixel for sample index i (oldest at the left edge, newest at the right). A single sample sits at the left.

int value_y(double v) const noexcept

Widget-local y pixel for value v through the active range (clamped; larger values map UP, i.e. to a smaller y).

inline AxlGfxPixel line_color() const noexcept
inline AxlGfxPixel grid_color() const noexcept
void set_line_color(AxlGfxPixel c) noexcept
void set_grid_color(AxlGfxPixel c) noexcept
void draw(AgtDrawContext &ctx) override

Paints the well chrome, the gridlines, then the series polyline.

void restyle() noexcept override

Re-snapshot the line / grid colors from the live palette unless pinned.

Public Static Functions

static inline AgtChartBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr int DEFAULT_CAPACITY = 128

Default ring-buffer capacity (samples kept) and the fixed storage cap.

static constexpr int MAX_CAPACITY = 512
static constexpr int DEFAULT_GRID = 4

Default number of grid INTERVALS (so DEFAULT_GRID + 1 lines).

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

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

AgtSlider

class AgtSlider : public AgtFrame

Public Types

enum Orientation

Drag direction. HORIZONTAL = thumb moves left-right across inner width; VERTICAL = thumb moves up-down with the value increasing toward the TOP (matches FOX and most vertical scroll-style interactions).

Values:

enumerator AGT_ORIENT_HORIZONTAL
enumerator AGT_ORIENT_VERTICAL

Public Functions

AgtSlider(AgtWidget *parent, int x, int y, int w, int h, int value = 0, int min = 0, int max = 100, Orientation orient = AGT_ORIENT_HORIZONTAL, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
inline int value() const noexcept
void set_value(int v) noexcept

Set the value programmatically. Clamps to [min, max]; no SEL_COMMAND is emitted (only drag-driven changes dispatch — matches FOX FXSlider’s setValue contract).

inline int min() const noexcept
inline int max() const noexcept
void set_range(int min, int max) noexcept

Rejects min > max; re-clamps current value.

inline Orientation orientation() const noexcept
void set_orientation(Orientation o) noexcept
inline int thumb_size() const noexcept

Thumb extent along the orient axis, in pixels. Default 20. Clamped at the lower edge to 4 px (any narrower stops reading as a thumb); upper bound is enforced at draw time against the inner rect.

void set_thumb_size(int s) noexcept
inline AxlGfxPixel thumb_color() const noexcept
void set_thumb_color(AxlGfxPixel c) noexcept
int thumb_origin() const noexcept

Thumb’s widget-local top-left along the orient axis (local x for HORIZONTAL, local y for VERTICAL) — the value-to-pixel mapping the renderer (AgtStyle::draw_slider) needs to place the thumb.

void set_target(AgtObject *target, uint16_t target_id) noexcept
inline AgtObject *target() const noexcept
inline uint16_t target_id() const noexcept
void set_enabled(bool enabled) noexcept override

Override AgtWidget::set_enabled to also clear dragging_ on a disable transition — a slider disabled mid-drag would otherwise keep capturing the mouse silently.

inline bool dragging() const noexcept

Inspection (for tests). True between a successful press and the matching release (clearing on either release or set_enabled(false)).

inline uint32_t focus_policy() const noexcept override

Sliders accept keyboard focus so future arrow-key adjustment lands cleanly; currently they only emit on drag, but the focus inclusion is the right shape.

void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot the thumb color / size from the live palette on a theme swap.

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

Arrow keys step the value (Up/Right +1, Down/Left -1); PageUp/Down step by ~1/10 of the range; Home/End jump to min/max. Emits SEL_COMMAND on change, like a drag.

Public Static Functions

static inline AgtSliderBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtSlider::build(p).bounds(...).range(0,255) .value(128).target(&t,id).create(). 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).

AgtEditField

class AgtEditField : public AgtFrame

Subclassed by AgtPasswordField, AgtSearchEntry

Public Types

enum EchoMode

How the buffer is DISPLAYED (the value is always stored in full and readable via text()). The FOX TEXTFIELD_PASSWD / Qt QLineEdit::EchoMode shape — a property, not a separate widget, so AgtPasswordField and the forms framework reuse the same field.

Values:

enumerator AGT_ECHO_NORMAL

show the real glyphs (default)

enumerator AGT_ECHO_PASSWORD

one masking dot per codepoint

enumerator AGT_ECHO_NO_ECHO

draw nothing (PIN / sudo style)

Public Functions

AgtEditField(AgtWidget *parent, int x, int y, int w, int h, const char *text = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
AgtEditField(AgtWidget *parent, const char *text = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept

Geometry-free ctor (FOX new FXTextField(p, ncols)): no x/y/w/h — the field takes a default visible width (NATURAL_COLUMNS) + text height via natural_*() and a layout container places it. Both axes AUTO.

int natural_width() const noexcept override

Natural size: NATURAL_COLUMNS digit-widths + text inset, one text line + a little vertical breathing room, plus the frame chrome.

int natural_height() const noexcept override
inline const char *text() const noexcept

Current buffer (NUL-terminated UTF-8). The pointer stays valid until the next set_text or edit operation.

inline int length() const noexcept
void set_text(const char *text) noexcept

Replace the buffer contents. Truncates at MAX_TEXT_LEN (at a UTF-8 codepoint boundary — won’t slice a multi-byte glyph in half). Resets cursor to the end, clears selection, marks dirty. Emits no AGT_SEL_COMMAND (programmatic set; only user-driven Enter dispatches).

inline int cursor() const noexcept

Cursor byte offset into the buffer. Always falls on a UTF-8 codepoint boundary (the setters enforce this).

inline int anchor() const noexcept

Anchor byte offset. Equal to cursor when there’s no selection; otherwise the selection spans [min(anchor, cursor), max(anchor, cursor)).

inline int selection_start() const noexcept
inline int selection_end() const noexcept
inline bool has_selection() const noexcept
void set_cursor(int byte_offset) noexcept

Move the cursor to byte_offset (clamped to [0, length], then snapped DOWN to the nearest codepoint boundary). Clears any selection.

void set_selection(int anchor, int cursor) noexcept

Set both anchor and cursor directly. Each is independently clamped + boundary-snapped. Anchor != cursor produces a selection.

void select_word_at(int byte_offset) noexcept

Select the run of like-classed characters (word chars, whitespace, or other punctuation) containing the byte at byte_offset — the double-click gesture, and a menu-callable “Select Word”. At a word/non-word boundary the word side wins.

void select_all() noexcept

Select the whole field (anchor 0, cursor at the end) — the triple-click gesture / “Select All”.

void copy() noexcept

Copy the current selection to the process clipboard as UTF-8 text (axl_clipboard_set). No-op (clipboard untouched) with no selection.

void cut() noexcept

Copy the selection, then delete it — as one action. No-op with no selection; the delete only runs if the copy succeeded (a failed capture never loses the text).

void paste() noexcept

Insert the clipboard’s text at the cursor, replacing any selection. A non-text payload or empty clipboard is ignored. Single-line: pasted text is truncated at the first line break.

inline EchoMode echo_mode() const noexcept

Current echo mode (default AGT_ECHO_NORMAL).

void set_echo_mode(EchoMode m) noexcept

Set how the buffer is displayed. The stored value is unchanged and still readable via text(); only the rendering + clipboard policy change. Marks dirty. In a masked mode (PASSWORD / NO_ECHO) copy() / cut() are suppressed so the secret can’t be exfiltrated to the clipboard (paste() still works — you can paste a secret in).

inline bool echo_masked() const noexcept

True while the displayed text is masked (echo mode != NORMAL).

inline uint32_t password_char() const noexcept

Codepoint drawn (as a filled dot) per character in PASSWORD mode. Stored as a Unicode scalar but RENDERED as a procedural disc, so the value is font-independent — it exists mainly so a consumer/theme can reason about the mask; the default is U+00B7 MIDDLE DOT.

void set_password_char(uint32_t codepoint) noexcept
inline bool cursor_blink() const noexcept

Whether the cursor blinks while focused (default true). Pauses solid for one interval after cursor activity. Disable for a steady cursor (accessibility, or deterministic screenshots).

inline void set_cursor_blink(bool on) noexcept

True when the blink phase shows the cursor now (always true while disabled). The draw gate ANDs this with focus.

One blink half-cycle (the loop-timer trampoline + direct testing).

inline AxlGfxPixel text_color() const noexcept
inline AxlGfxPixel selection_bg_color() const noexcept
inline AxlGfxPixel cursor_color() const noexcept
void set_text_color(AxlGfxPixel c) noexcept
void set_selection_bg_color(AxlGfxPixel c) noexcept
void set_cursor_color(AxlGfxPixel c) noexcept
inline float text_size() const noexcept

TTF text height (px). Defaults from AgtPalette::current().text_size.

void set_text_size(float px) noexcept
void set_target(AgtObject *target, uint16_t target_id) noexcept
inline AgtObject *target() const noexcept
inline uint16_t target_id() const noexcept
inline bool focused() const noexcept

True between AGT_SEL_FOCUSIN and AGT_SEL_FOCUSOUT. The cursor only paints while focused; the selection highlight paints regardless (matches Win32 — losing focus keeps the selection visible so you can see what was selected).

inline uint32_t focus_policy() const noexcept override

Editable text fields are always Tab-cycle focusable.

void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot text/selection/cursor colors + size + edit bg on a theme swap.

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

Public Static Functions

static inline AgtEditFieldBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtEditField::build(p).text("eth0").create() (omit geometry for a size-to-content field a layout places). See agt-builder.hpp.

Public Static Attributes

static constexpr int MAX_TEXT_LEN = 256

Inline buffer length. 256 bytes covers most practical inputs (path component, device name, URL fragment) plus some Unicode headroom. Inputs longer than this are rejected at the insertion edge — the field never truncates after the fact.

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST

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

static constexpr int NATURAL_COLUMNS = 12

Default visible width in “0”-glyph columns when auto-sized (FOX ncols).

AgtImage

class AgtImage : public AgtFrame

Public Types

enum ScaleMode

How the decoded image is fitted into the inner rect.

Values:

enumerator AGT_IMAGE_CENTER

1:1 pixels, centered; an image larger than the inner rect is clipped. The default — no resampling, exact pixels (right for fixed-size logos / icons).

enumerator AGT_IMAGE_FIT

Scale preserving aspect ratio to the largest size that fits inside the inner rect, then center. Nearest- neighbor resample (cached).

enumerator AGT_IMAGE_STRETCH

Stretch to fill the inner rect exactly, ignoring aspect ratio. Nearest-neighbor resample (cached).

Public Functions

AgtImage(AgtWidget *parent, int x, int y, int w, int h, const uint8_t *bytes = nullptr, size_t len = 0) noexcept

Construct an image widget and decode bytes immediately. Pass NULL / 0 for an empty placeholder (just the frame chrome; set image later via set_image).

~AgtImage() noexcept override

Free the decoded buffer. Cascades through AgtFrame’s children (this widget has none of its own).

AgtImage(const AgtImage&) = delete
AgtImage(AgtImage&&) = delete
AgtImage &operator=(const AgtImage&) = delete
AgtImage &operator=(AgtImage&&) = delete
void set_image(const uint8_t *bytes, size_t len) noexcept

Replace the current image. Frees the previous decoded buffer, decodes bytes via axl_pixmap_decode. Pass NULL / 0 to clear back to the empty placeholder state. On decode failure, the widget ends up in the empty state (previous image still freed — no half-state). Mark-dirty fires either way so the consumer’s next redraw paints the new state.

inline AxlGfxBuffer *image_buffer() const noexcept

Decoded buffer or NULL. Pointer is stable until the next set_image or destruction. Exposed for diagnostics + tests; production code shouldn’t need direct access. Returned non-const because axl_gfx_buffer_pixels / axl_gfx_buffer_get_info take non-const buffer handles — holding the const-correctness line here would force a const_cast at every consumer.

inline int image_width() const noexcept

Decoded image dimensions in pixels. Both report 0 when the widget is in the empty state (no buffer). Stable for the lifetime of the current buffer.

inline int image_height() const noexcept
inline ScaleMode scale_mode() const noexcept

Fit mode (default AGT_IMAGE_CENTER). FIT / STRETCH resample via nearest-neighbor into a cached scaled buffer that is rebuilt only when the target size or source image changes — redraws at a steady size pay no resample cost.

void set_scale_mode(ScaleMode m) noexcept
void draw(AgtDrawContext &ctx) override

Public Static Functions

static inline AgtImageBuilder 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).

AgtScrollBar

class AgtScrollBar : public AgtFrame

Public Types

enum Orientation

Scroll axis. HORIZONTAL = thumb moves left↔right; VERTICAL = thumb moves top↔bottom. position 0 is the start either way.

Values:

enumerator AGT_ORIENT_HORIZONTAL
enumerator AGT_ORIENT_VERTICAL

Public Functions

AgtScrollBar(AgtWidget *parent, int x, int y, int w, int h, Orientation orient = AGT_ORIENT_VERTICAL, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
inline int total() const noexcept
inline int page() const noexcept
void set_range(int total, int page) noexcept

Set the content extent + visible page (both clamped to >= 0). Re-clamps position into the new [0, total - page] range. No SEL_COMMAND (programmatic).

inline int position() const noexcept
int max_position() const noexcept

The largest valid position, max(0, total - page).

void set_position(int p) noexcept

Set the scroll position programmatically; clamps to [0, max_position()]. No SEL_COMMAND (matches AgtSlider::set_value — only user drag/paging dispatches).

inline Orientation orientation() const noexcept
void set_orientation(Orientation o) noexcept
inline int min_thumb() const noexcept

Minimum thumb extent along the orient axis, in pixels — the thumb never shrinks below this however small page / total gets, so it stays grabbable. Default 16.

void set_min_thumb(int px) noexcept
inline AxlGfxPixel thumb_color() const noexcept
void set_thumb_color(AxlGfxPixel c) noexcept
void set_target(AgtObject *target, uint16_t target_id) noexcept
inline AgtObject *target() const noexcept
inline uint16_t target_id() const noexcept
void set_enabled(bool enabled) noexcept override

Clears an in-flight drag (+ releases capture) on a disable transition — same guard as AgtSlider.

inline bool dragging() const noexcept

True between a successful thumb press and its release.

inline uint32_t focus_policy() const noexcept override

A scrollbar is not a Tab-focus target — the widget it scrolls (listbox, scrollframe) owns the keyboard; the bar is a mouse affordance. (The default is already AGT_FOCUS_NONE; stated explicitly for intent.)

void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot thumb color/metrics + well bg on a theme swap.

long on_left_button_press(AgtObject *sender, AgtEvent *ev)
long on_left_button_release(AgtObject *sender, AgtEvent *ev)
long on_mouse_motion(AgtObject *sender, AgtEvent *ev)
int thumb_extent() const noexcept

Thumb extent along the orient axis, in pixels (proportional to page / total, clamped to [min_thumb, track]).

int thumb_offset() const noexcept

Thumb top-left offset along the orient axis, in pixels measured from the inner-rect start (inner_offset_*). 0 at position 0.

Public Static Functions

static inline AgtScrollBarBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtScrollBar::build(p).bounds(...).range(total,page) .target(&t,id).create(). 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).

AgtListBox

class AgtListBox : public AgtFrame

Public Types

Message-ID chain. ID_SCROLLBAR is the id the owned scrollbar targets the list with (internal); consumer command ids start at ID_LAST.

Values:

enumerator ID_SCROLLBAR
enumerator ID_LAST
enum Orientation

Layout axis. VERTICAL (default) stacks rows top-to-bottom with a right-edge scrollbar; HORIZONTAL lays items left-to-right — a filmstrip / strip of choices — with a bottom-edge scrollbar. The item model, selection, and command wiring are identical either way; only the layout

  • scrollbar axis flip.

Values:

enumerator AGT_ORIENT_VERTICAL
enumerator AGT_ORIENT_HORIZONTAL

Public Functions

AgtListBox(AgtWidget *parent, int x, int y, int w, int h, AgtObject *target = nullptr, uint16_t target_id = 0, Orientation orient = AGT_ORIENT_VERTICAL) noexcept
~AgtListBox() noexcept override
int add_item(const char *text) noexcept

Append a row (the text is copied). Returns the new row index, or -1 on allocation failure / NULL text.

int add_item(const char *text, AgtStyle::StockIcon icon) noexcept

Append a row with a leading built-in stock icon (drawn left of the text in the row color). Same copy/return contract as add_item.

void clear() noexcept

Remove all rows, freeing their text. Resets selection + scroll.

int item_count() const noexcept
const char *item_text(int index) const noexcept

Row text at index, or NULL if out of range. Borrowed — the list owns it.

bool item_has_icon(int index) const noexcept

Whether row index carries a leading stock icon.

AgtStyle::StockIcon item_icon(int index) const noexcept

Row index’s stock icon (meaningful only when item_has_icon).

inline int current() const noexcept
void set_current(int index) noexcept

Set the selection programmatically (clamped to [-1, count-1]; -1 clears). Scrolls it into view. No SEL_COMMAND.

inline Orientation orientation() const noexcept

Layout axis (default VERTICAL).

void set_orientation(Orientation o) noexcept

Switch the layout axis: moves the scrollbar to the right (VERTICAL) or bottom (HORIZONTAL) edge, re-lays out, and re-clamps the scroll.

inline int item_width() const noexcept

Per-item extent along the MAIN axis: row_height() is the height of a row in VERTICAL mode; item_width() is the width of an item cell in HORIZONTAL mode. Each is used only in its own orientation.

void set_item_width(int px) noexcept
inline int top() const noexcept

First visible item index (scroll position in item units).

int visible_rows() const noexcept

Number of fully-visible items in the content area — rows in VERTICAL mode, columns in HORIZONTAL mode.

void ensure_visible(int index) noexcept

Scroll index into view (unconditionally — unlike set_current, which short-circuits when the selection is unchanged). Needed after a resize when the selection already equals index but the new viewport height changed which rows are visible (e.g. a combo dropdown opening at its computed height). No SEL_COMMAND.

inline int row_height() const noexcept
void set_row_height(int px) noexcept
inline float text_size() const noexcept

Row TTF text height (px). Defaults from AgtPalette::current().text_size.

void set_text_size(float px) noexcept
inline AxlGfxPixel text_color() const noexcept
void set_text_color(AxlGfxPixel c) noexcept
void set_selection_colors(AxlGfxPixel bg, AxlGfxPixel text) noexcept
void set_target(AgtObject *target, uint16_t target_id) noexcept
inline AgtObject *target() const noexcept
inline uint16_t target_id() const noexcept
inline void set_activate_command(uint16_t id) noexcept

Activation command (FOX SEL_DOUBLECLICKED / Qt activated): a SEL_COMMAND with this id fires on double-click or Enter — distinct from the selection-changed target_id of a single click / arrow nav. Sent to the same target. Default 0 = no activation signal (double-click then behaves like a single click). A file list wires this to “open the row” (enter a folder / pick a file).

inline uint16_t activate_command() const noexcept
inline uint32_t focus_policy() const noexcept override
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot row/text/selection colors + metrics + well bg on a theme swap.

long on_left_button_press(AgtObject *sender, AgtEvent *ev)
long on_key_press(AgtObject *sender, AgtEvent *ev)
long on_mouse_wheel(AgtObject *sender, AgtEvent *ev)
long on_focus_in(AgtObject *sender, AgtEvent *ev)
long on_focus_out(AgtObject *sender, AgtEvent *ev)
long on_scroll(AgtObject *sender, AgtEvent *ev)

SEL_COMMAND from the owned scrollbar (id == ID_SCROLLBAR).

Public Static Functions

static inline AgtListBoxBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

AgtSeparator

class AgtSeparator : public AgtWidget

Public Types

enum Orientation

Values:

enumerator AGT_ORIENT_HORIZONTAL

line runs left↔right

enumerator AGT_ORIENT_VERTICAL

line runs top↔bottom

Public Functions

AgtSeparator(AgtWidget *parent, int x, int y, int w, int h, Orientation orient = AGT_ORIENT_HORIZONTAL) noexcept
inline Orientation orientation() const noexcept
void set_orientation(Orientation o) noexcept
inline int thickness() const noexcept

Line thickness in pixels along the minor axis (default 1).

void set_thickness(int px) noexcept
inline AxlGfxPixel color() const noexcept
void set_color(AxlGfxPixel c) noexcept
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot the line color from the live palette on a theme swap.

Public Static Functions

static inline AgtSeparatorBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtWidget::ID_LAST

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

AgtLogView

class AgtLogView : public AgtFrame

Public Types

Message-ID chain. ID_SCROLLBAR is the id the owned scrollbar targets the log view with (internal); consumer ids start at ID_LAST.

Values:

enumerator ID_SCROLLBAR
enumerator ID_LAST

Public Functions

AgtLogView(AgtWidget *parent, int x, int y, int w, int h, int scrollback = DEFAULT_SCROLLBACK) noexcept
~AgtLogView() noexcept override
void append(const char *text) noexcept

Append a byte stream (interpreting \n / \r / \t as above). NULL is ignored. Follow-tail keeps the newest output in view.

void append_line(const char *text) noexcept

Append text followed by a newline (the common “log a line” call).

void clear() noexcept

Drop all lines (ring + current), reset the scroll, and re-engage follow-tail — a cleared console behaves like a fresh one.

int line_count() const noexcept

Number of DISPLAYED lines — completed ring lines plus the in-progress current line when it is non-empty.

const char *line_text(int i) const noexcept

Text of displayed line i (borrowed; NULL if out of range). The log view owns ring lines; the current line points at the inline buffer.

inline int scrollback() const noexcept

Scrollback capacity (completed lines). set_scrollback evicts the oldest lines if the new cap is below the current line count.

void set_scrollback(int lines) noexcept
inline bool follow_tail() const noexcept
void set_follow_tail(bool on) noexcept

Enable/disable auto-scroll to the newest line. Enabling snaps the view to the bottom immediately.

inline int top() const noexcept

First visible line index (scroll position in line units).

int visible_rows() const noexcept

Number of fully-visible text rows in the content area.

void scroll_to(int top_line) noexcept

Scroll so top_line is the first visible line (clamped). Re-evaluates follow-tail (engaged only when scrolled to the very bottom).

inline int row_height() const noexcept

Line-row height (px). Independent of text_size (matches AgtListBox): shrinking the text does NOT shrink the row, and a larger text may need a matching set_row_height. Defaults from AgtPalette::list_row_height.

void set_row_height(int px) noexcept
inline float text_size() const noexcept

Monospace text height (px). Defaults from AgtPalette::current().text_size. Does not change row_height() — set that too for very large text.

void set_text_size(float px) noexcept
inline AxlGfxPixel text_color() const noexcept
void set_text_color(AxlGfxPixel c) noexcept
inline uint32_t focus_policy() const noexcept override
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot text color + metrics + well bg on a theme swap.

long on_key_press(AgtObject *sender, AgtEvent *ev)
long on_mouse_wheel(AgtObject *sender, AgtEvent *ev)
long on_scroll(AgtObject *sender, AgtEvent *ev)

SEL_COMMAND from the owned scrollbar (id == ID_SCROLLBAR).

Public Static Functions

static inline AgtLogViewBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr int DEFAULT_SCROLLBACK = 1000

Default scrollback (completed lines retained).

static constexpr int MAX_LINE = 2048

Max bytes in one line before it hard-wraps to the next.

AgtTerminal

class AgtTerminal : public AgtFrame

Public Types

ID_SCROLLBAR is the id the owned scrollbar targets the terminal with; consumer command ids start at ID_LAST.

Values:

enumerator ID_SCROLLBAR
enumerator ID_LAST
enum Attr

Cell attribute bits (Cell::attr).

Values:

enumerator AGT_TERM_BOLD
enumerator AGT_TERM_UNDERLINE
enumerator AGT_TERM_INVERSE
using InputFn = void (*)(void *user, const char *bytes, int len)

Sink for bytes the terminal emits from keystrokes (the WRITE side).

Public Functions

AgtTerminal(AgtWidget *parent, int x, int y, int w, int h, int cols = DEFAULT_COLS, int rows = DEFAULT_ROWS) noexcept
~AgtTerminal() noexcept override
inline int cols() const noexcept
inline int rows() const noexcept
inline int cursor_row() const noexcept
inline int cursor_col() const noexcept
const Cell &cell_at(int row, int col) const noexcept

The visible-grid cell at (row, col). Out-of-range returns a reference to a static blank cell (never a bad read).

int scrollback_len() const noexcept

Lines currently held in the scrollback ring (0 .. scrollback cap).

const Cell &scrollback_cell(int n, int col) const noexcept

A scrollback line, n counted from the OLDEST (0) toward the newest. col indexes within the line. Out-of-range returns the blank cell.

void feed(const char *bytes, int len) noexcept

Feed len bytes of terminal output through the escape parser.

void feed_str(const char *s) noexcept

Convenience: feed a NUL-terminated string.

void reset() noexcept

Clear the grid + scrollback, home the cursor, reset the pen (the ESC c effect).

void set_grid_size(int cols, int rows) noexcept

Resize the grid (clamped to [1, MAX]). Clears the grid + scrollback and homes the cursor (no reflow in v0.1).

inline int scrollback_cap() const noexcept
void set_scrollback_cap(int lines) noexcept
inline void set_on_input(InputFn fn, void *user) noexcept

Install the sink for emitted input bytes (replaces any previous).

bool emit_key(const AgtEvent &ev) noexcept

Translate one key event to bytes and emit them via on_input_. Public for direct testing. Returns whether anything was emitted.

inline AxlGfxPixel default_fg() const noexcept
inline AxlGfxPixel default_bg() const noexcept
void set_default_colors(AxlGfxPixel fg, AxlGfxPixel bg) noexcept

Colors for cells whose fg/bg is COLOR_DEFAULT (the terminal’s own foreground / background). Default: a light-grey-on-near-black console.

inline float text_size() const noexcept
void set_text_size(float px) noexcept
inline int top_line() const noexcept

First visible content line (0 = oldest scrollback line). The viewport shows rows() lines from [scrollback ++ live grid].

inline bool follow_tail() const noexcept

When true (default) new output snaps the view to the bottom (the live grid); scrolling up releases the glue, scrolling back re-engages it.

void set_follow_tail(bool on) noexcept
inline uint32_t focus_policy() const noexcept override
inline bool focused() const noexcept
void draw(AgtDrawContext &ctx) override
long on_key_press(AgtObject *sender, AgtEvent *ev)
long on_mouse_wheel(AgtObject *sender, AgtEvent *ev)
long on_focus_in(AgtObject *sender, AgtEvent *ev)
long on_focus_out(AgtObject *sender, AgtEvent *ev)
long on_scroll(AgtObject *sender, AgtEvent *ev)

Public Static Functions

static inline AgtTerminalBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

static AxlGfxPixel ansi_pixel(uint8_t index) noexcept

Map an ANSI/256 color index to a pixel (0–15 base + bright, 16–231 the 6×6×6 cube, 232–255 the grayscale ramp). COLOR_DEFAULT is NOT valid here — callers resolve that to default_fg/bg first.

Public Static Attributes

static constexpr int MAX_COLS = 256

Hard bounds on the grid (a cell is 8 bytes; 256×128 caps one terminal’s grid at ~256 KB). set_grid_size clamps to these.

static constexpr int MAX_ROWS = 128
static constexpr int DEFAULT_COLS = 80
static constexpr int DEFAULT_ROWS = 24
static constexpr int DEFAULT_SCROLLBACK = 1000

Lines retained above the top of the screen (bounded ring).

static constexpr uint8_t COLOR_DEFAULT = 0xFFu

Cell::fg / Cell::bg sentinel: use the widget’s default fg/bg rather than a palette index. Real colors are 0–15 (8 normal + 8 bright) or, via 38;5;n, an extended 0–255 index.

struct Cell

One screen cell. cp is a Unicode scalar ( ‘` for blank).

Public Members

uint32_t cp

codepoint (0x20 = blank)

uint8_t fg

palette index, or COLOR_DEFAULT

uint8_t bg

palette index, or COLOR_DEFAULT

uint8_t attr

Attr bitmask.

AgtSpacer

class AgtSpacer : public AgtWidget

AgtToggleButton

class AgtToggleButton : public AgtButton

Public Functions

AgtToggleButton(AgtWidget *parent, int x, int y, int w, int h, const char *label = "", AgtObject *target = nullptr, uint16_t target_id = 0) noexcept
inline bool checked() const noexcept
void set_checked(bool checked) noexcept

Set the latched state programmatically (updates the bevel). No SEL_COMMAND — only a user click emits.

inline AxlGfxPixel active_color() const noexcept
void set_active_color(AxlGfxPixel c) noexcept
AxlGfxPixel effective_bg_color() const noexcept override

Active fill while checked; otherwise the inherited button palette.

void restyle() noexcept override

Re-snapshot the active (checked) color on a theme swap.

uint32_t state_flags() const noexcept override

Adds CHECKED when latched on.

Public Static Functions

static inline AgtToggleButtonBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtButton::ID_LAST

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

AgtGroupBox

class AgtGroupBox : public AgtFrame

Public Functions

AgtGroupBox(AgtWidget *parent, int x, int y, int w, int h, const char *title = "") noexcept
inline const char *title() const noexcept
void set_title(const char *title) noexcept
inline AxlGfxPixel title_color() const noexcept
void set_title_color(AxlGfxPixel c) noexcept
inline float title_size() const noexcept
void set_title_size(float px) noexcept
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot title color/size + surface bg on a theme swap.

Public Static Functions

static inline AgtGroupBoxBuilder 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).

AgtMenuItem

class AgtMenuItem : public AgtButton

Public Types

enum class Mark : uint8_t

Optional state glyph drawn in a left gutter (see set_mark). CHECK is a checkmark for a toggleable item; RADIO is a filled dot for one-of-many selection. Both render only while checked().

Values:

enumerator NONE

plain command row (default) — no gutter

enumerator CHECK

checkbox: a checkmark when checked

enumerator RADIO

radio: a filled dot when checked

Public Functions

AgtMenuItem(AgtWidget *parent, int x, int y, int w, int h, const char *label, const char *shortcut = nullptr, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept

Construct a menu item. Same trailing (target, target_id) shape as AgtButton — a release-inside emits AGT_SEL_COMMAND to target with target_id. shortcut is an optional right-anchored hint string (caller owns the storage, like the label).

inline const char *shortcut() const noexcept

Optional right-anchored shortcut hint. Display-only. NULL or empty renders no shortcut.

void set_shortcut(const char *s) noexcept
inline bool highlighted() const noexcept

Keyboard-navigation cursor state, separate from hover(). Set by the owning menu’s arrow-key handling. Painted with the same selection color as hover (see effective_bg_color).

void set_highlighted(bool h) noexcept
inline AgtMenu *submenu() const noexcept

Submenu this row opens, or NULL for a plain command row. A submenu row emits no command on click — the owning AgtMenu opens the child popup instead — and renders a right-pointing arrow indicator in place of a shortcut. The pointer is a non-owning back-reference; the child popup is owned by the parent AgtMenu (see add_submenu).

inline bool has_submenu() const noexcept
void set_submenu(AgtMenu *child) noexcept
inline Mark mark() const noexcept

State-glyph mode. CHECK/RADIO reserve a left gutter so the label aligns with sibling rows; the glyph paints only while checked(). Setting a non-NONE mark also reserves the gutter (see set_gutter). Default NONE.

void set_mark(Mark m) noexcept
inline bool checked() const noexcept

Whether the state glyph is shown (the row’s check/radio is “on”). Meaningful only when mark() != NONE. Default false.

void set_checked(bool c) noexcept
inline uint16_t radio_group() const noexcept

Radio-group id (0 = none). Rows sharing a non-zero group are mutually exclusive — AgtMenu::set_radio_selected checks one and clears the rest. Set implicitly by AgtMenu::add_radio_item.

inline void set_radio_group(uint16_t g) noexcept
inline bool gutter() const noexcept

Reserve the left check-gutter even when this row has no mark, so a menu that mixes markable and plain rows keeps every label aligned. set_mark(non-NONE) turns it on automatically; the owning AgtMenu turns it on for every row once any row is markable.

void set_gutter(bool on) noexcept
inline char mnemonic() const noexcept

Keyboard mnemonic letter for this row / title (0 = none). On a menu-bar title, Alt+<letter> opens its menu; the same letter is underlined in the label as the access cue. Stored lowercased and matched case-insensitively against the label’s first occurrence; setting it also resolves the label’s underline index. Resolve order matters: call AFTER the label is set (it reads the current label()); re-set_label then re-set_mnemonic if the label changes.

void set_mnemonic(char c) noexcept
inline uint32_t focus_policy() const noexcept override

Menu items ARE focusable (inherited from AgtButton), but in practice the OWNING menu holds keyboard focus and drives the item highlight; the override is explicit here for clarity.

AxlGfxPixel effective_bg_color() const noexcept override

Selection color when hovered OR highlighted; menu background otherwise; disabled tone when disabled. Overrides AgtButton’s normal/hover/pressed palette entirely (a menu row has no pressed-vs-hover distinction).

void set_menu_bg_color(AxlGfxPixel c) noexcept

Menu-row background (un-highlighted) + selection color. Defaults: dark menu surface + blue selection, tuned to read over the AgtWindow background.

void set_selection_color(AxlGfxPixel c) noexcept
inline AxlGfxPixel shortcut_color() const noexcept

Color of the right-anchored shortcut text. Default a dimmed grey relative to the label color.

void set_shortcut_color(AxlGfxPixel c) noexcept
void draw(AgtDrawContext &ctx) override

Paint bg (via effective_bg_color) + left-anchored label (AgtLabel::draw), then the right-anchored shortcut hint.

void restyle() noexcept override

Re-snapshot menu bg / selection / shortcut colors on a theme swap.

Public Static Functions

static char parse_mnemonic_label(const char *label, char *out, size_t out_cap, int *out_index) noexcept

Parse a FOX/Qt-style &-marked label into its display text + mnemonic. & before a character marks that character as the mnemonic and is stripped from the display ("&File" → “File”, mnemonic f, underline index 0; "E&xit" → “Exit”, x, index 1). "&&" is a literal &. A trailing & is dropped. Only the first marker sets the mnemonic; the index is the EXACT marked position (not a first-occurrence search), so a repeated letter underlines the marked one. out receives the (NUL-terminated, out_cap-bounded) display text; out_index receives the underline index (-1 if none).

Returns:

the lowercased mnemonic letter/digit, or 0 if the label has no & marker. AgtMenuBar::add_menu / AgtMenu::add_item use this; a label with no & keeps the auto-first-letter mnemonic.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtButton::ID_LAST

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

static constexpr int CHECK_GUTTER = 18

Left gutter (px) reserved for the check/radio glyph on a markable row. The label inset shifts right by this so a column of rows aligns whether or not each is currently checked. Wide enough for the ~10px glyph plus breathing room.

static constexpr int LABEL_INSET = 10

Row text insets (px), exposed so the owning AgtMenu can size its auto-width to leave the label + shortcut fully visible without re-hard-coding these numbers (single source of truth — tuning them here re-sizes the popup too). LABEL_INSET is the left gap before the caption, SHORTCUT_INSET the right gap after the shortcut, and LABEL_SHORTCUT_GAP the minimum space kept between the two.

static constexpr int SHORTCUT_INSET = 10
static constexpr int LABEL_SHORTCUT_GAP = 16

AgtDialog

class AgtDialog : public AgtWindow

Subclassed by AgtFileDialog, AgtMessageBox, AgtProgressDialog, AgtPromptDialog, AgtThemeDialog

Public Functions

AgtDialog() noexcept
~AgtDialog() noexcept override
int run(AgtApp *app) noexcept

Block until dismiss() is called. Pushes self onto the app’s modal stack, saves the currently-focused widget, then drives axl_loop_iterate_until until dismissed. On return, pops the modal and restores focus.

Returns:

result code set by dismiss(), or RESULT_RUN_FAILED if the app’s modal stack was already full.

void dismiss(int result_code) noexcept

Signal the dialog to dismiss with result_code. For a MODAL dialog (run()) this signals the nested loop to exit; for a MODELESS dialog (show()) it routes to close() — non-blocking teardown + the on-close command. Safe to call from any AGT handler dispatched while the dialog is up (button click, key press, etc.); the OK/Cancel buttons and the Enter/Escape shortcuts all funnel here, so they close a modeless dialog exactly as they dismiss a modal one.

void show(AgtApp *app) noexcept

Show the dialog modelessly (non-blocking). Hosts a transparent fullscreen child surface of app’s window, clips its input region to the card, gives the dialog keyboard focus, presents, and returns immediately. No-op if already open or app has no window. The dialog must outlive this call (see the lifetime note above).

void close(int result_code) noexcept

Tear down a modeless dialog: destroy its surface, restore keyboard focus + repaint the parent, and emit the on-close command (see set_on_close) with result_code as the message id. Idempotent — a second close() (or a close() on a never-shown dialog) is a no-op. dismiss() routes here while modeless, so a button/Escape closes too.

inline bool is_open() const noexcept

True between show() and close() (modeless dialog currently up).

inline bool is_modeless() const noexcept

True if this dialog is running modelessly (show() was used rather than run()). Stays set after close() so a reused instance keeps its mode until the next run()/show().

inline void set_on_close(AgtObject *target, uint16_t id) noexcept

Set the completion callback for a modeless dialog: on close(code) the dialog emits AGT_SEL_COMMAND(code) to target (its message_id() is the result code; the target reads result() too). A non-blocking dialog can’t return a code, so this is how the caller learns the outcome. Pass a NULL target to disable. Mirrors the button/target emit pattern.

inline int result() const noexcept

Last dismiss() code, or 0 if never dismissed.

void draw(AgtDrawContext &ctx) override

Paint the modal veil (C7 P6): a translucent dark tint written into the surface buffer (raw axl_gfx_buffer_clear — a fill_rect would force the result opaque and defeat the veil). The dialog renders on a per-pixel-alpha child surface the compositor frosts at composite time (axl_surface_set_backdrop_blur), so the tint blends over the LIVE frosted parent surface beneath — no parent-frame readback. The dialog’s own widgets (the card) render opaque on top via the normal child walk. Headless (no surface buffer) falls back to an opaque dark fill.

PresentMode present_mode() const noexcept override

Coalesce PRESENT_DAMAGE up to PRESENT_FULL: the veil is a whole-surface write (buffer_clear ignores the damage clip) and a backdrop-blur surface recomposites its full rect regardless, so a partial present would leave the card stale outside the damage bbox.

inline void set_veil_blur(bool on) noexcept

Enable/disable the frosted backdrop blur behind the modal veil for THIS dialog. When off, the veil still dims (the translucent tint) but the parent surface beneath shows through un-blurred. Blur is a per-present full-surface cost, so turning it off is the cheap path on slow targets / when content animates behind the modal. Defaults to the global default (see set_default_veil_blur).

inline bool veil_blur() const noexcept
inline void set_default_button(AgtButton *b) noexcept

Set the button that should activate on Enter. Pass NULL to disable the default-button shortcut. AGT_SEL_COMMAND is emitted to the button’s target with its target_id on Enter — the same dispatch the button would emit on click.

inline AgtButton *default_button() const noexcept
inline void set_cancel_button(AgtButton *b) noexcept

Set the button that should activate on Escape. Pass NULL to fall back to dismiss(0) on Escape.

inline AgtButton *cancel_button() const noexcept
inline AxlEvent *done_event() const noexcept

Currently-active dialog event, valid only between run() entry and dismiss(). Exposed so the parent test infrastructure can short-circuit modals via Scenario’s .dismiss_modal(code) helper.

long on_key_press(AgtObject *sender, AgtEvent *ev)
long on_command_dismiss(AgtObject *sender, AgtEvent *ev)

Default AGT_SEL_COMMAND handler: dismiss the dialog with the command’s message_id() as the result code. Dialog composites wire their OK/Cancel buttons to target the dialog itself with target_id == result code — so a button click routes here and dismisses, with no per-composite forwarding trampoline. Mapped over the full id range; a dialog with a button that should NOT dismiss simply targets a different object instead of this.

Public Static Functions

static inline void set_default_veil_blur(bool on) noexcept

Set the GLOBAL default for the modal backdrop blur — the value every subsequently-constructed dialog (including AgtMessageBox / AgtFileDialog, which are built + run internally and so can’t be configured per-instance) starts with. Default: true (frosted). Call once at startup to opt the whole app out of the blur.

static inline bool default_veil_blur() noexcept

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtWindow::ID_LAST
static constexpr uint16_t ID_CANCEL = 0

dismiss(0) — negative result

Standard dialog result codes (FOX FXDialogBox::ID_ACCEPT / ID_CANCEL shape). A button targeting the dialog with one of these dismisses run() with that value — wire an OK button to &dlg, AgtDialog::ID_ACCEPT, a Cancel button to AgtDialog::ID_CANCEL, with no per-dialog handler. These are RESULT CODES (the value run() returns), not message-map chain ids: on_command_dismiss maps the whole command range and dismisses with the command id AS the result, so it shadows the inherited AgtWidget state commands on a dialog. Values match AgtMessageBox::RESULT_OK / RESULT_CANCEL (1 / 0).

static constexpr uint16_t ID_ACCEPT = 1

dismiss(1) — affirmative

static constexpr int RESULT_RUN_FAILED = -1

Sentinel returned by run() on any non-dismissal failure path — NULL app, allocation failure for the done event, or modal-stack overflow (AgtApp::MODAL_STACK_MAX already- pushed dialogs). Callers compare against this before inspecting a real result code; the dialog ran no event loop in this case.

AgtMovableFrame

class AgtMovableFrame : public AgtVBox

Public Functions

AgtMovableFrame(AgtWidget *parent, int x, int y, int w, int h, const char *title, int spacing = 8) noexcept

Construct a movable frame. title is shown in the bar (caller owns the storage). spacing is the gap between stacked content children. Defaults to a raised 1px border so it reads as a floating panel.

inline const char *title() const noexcept
void set_title(const char *title) noexcept
inline int title_height() const noexcept

Height of the title-bar grip strip (px). Content stacks below it. Default 30.

void set_title_height(int h) noexcept
void set_content_padding(int pad) noexcept

Inset applied to the content area on the left / right / bottom (the top inset is the title bar). Re-applied as the underlying frame padding. Default 0.

inline AxlGfxPixel title_bg_color() const noexcept

Title-bar colors + text size. Defaults derive from the frame background (a lighter bar over the content).

inline AxlGfxPixel title_text_color() const noexcept
inline float title_text_size() const noexcept
void set_title_bg_color(AxlGfxPixel c) noexcept
void set_title_text_color(AxlGfxPixel c) noexcept
void set_title_text_size(float px) noexcept
inline bool movable() const noexcept

Enable / disable dragging (default enabled). Disabling mid-drag also ends any in-progress drag + releases capture.

void set_movable(bool on) noexcept
inline bool dragging() const noexcept

True between a title-bar press and its release.

void draw(AgtDrawContext &ctx) override

Paint the VBox (bg + border + stacked content) then the title bar strip + title text on top of the bar area.

void restyle() noexcept override

Re-snapshot title-bar colors/metrics + surface bg on a theme swap.

int dirty_margin() const noexcept override

Over-draw margin covering the drop shadow, so a drag / hide damages the shadowed footprint (no trail on incremental present).

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 AgtMovableFrameBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtVBox::ID_LAST

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

AgtStatusBar

class AgtStatusBar : public AgtFrame

Public Functions

AgtStatusBar(AgtWidget *parent, int x, int y, int w, int h, BorderStyle border = AGT_FRAME_LINE, int border_width = 1) noexcept
~AgtStatusBar() noexcept override
void set_text(const char *text)

Set the left status text (copied). NULL clears it.

void set_right_text(const char *text)

Set the right-aligned field (copied). NULL clears it.

inline const char *text() const noexcept
inline const char *right_text() const noexcept
int add_segment(AgtObject *target, uint16_t id) noexcept
void set_segment_text(int index, const char *text) noexcept
inline int segment_count() const noexcept
int segment_at(int local_x, int local_y) noexcept

Public for direct testing: hit-test a content-local x/y against the laid-out segments; returns the segment index hit, or -1.

inline AxlGfxPixel text_color() const noexcept
inline float text_size() const noexcept
void set_text_color(AxlGfxPixel c) noexcept
void set_text_size(float px_size) noexcept
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot text color/size + title-bar bg on a theme swap.

long on_left_button_press(AgtObject *sender, AgtEvent *ev)

Left-click → dispatch the hit segment’s command (public for tests).

Public Static Functions

static inline AgtStatusBarBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtFrame::ID_LAST
static constexpr int MAX_SEGMENTS = 4

Right-aligned clickable segments (cells), laid out left-to-right in add order with the group flush to the right edge. A left-click on a cell emits AGT_SEL_COMMAND (its id) to target — e.g. an editor’s encoding / line-ending indicators that cycle on click. Returns the segment index, or -1 if full (MAX_SEGMENTS). Segments own the right side; don’t also use set_right_text when segments are present.

AgtSwitch

class AgtSwitch : public AgtCheckBox

Public Functions

AgtSwitch(AgtWidget *parent, int x, int y, int w, int h, const char *label = "", AgtObject *target = nullptr, uint16_t target_id = 0, bool checked = false) noexcept
inline AxlGfxPixel on_color() const noexcept
void set_on_color(AxlGfxPixel c) noexcept
inline AxlGfxPixel off_color() const noexcept
void set_off_color(AxlGfxPixel c) noexcept
inline AxlGfxPixel knob_color() const noexcept
void set_knob_color(AxlGfxPixel c) noexcept
inline int track_width() const noexcept
inline int track_height() const noexcept
void set_track_size(int w, int h) noexcept
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot on/off/knob colors on a theme swap.

Public Static Functions

static inline AgtSwitchBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtCheckBox::ID_LAST
static constexpr int DEFAULT_TRACK_WIDTH = 44
static constexpr int DEFAULT_TRACK_HEIGHT = 20

AgtIcon

class AgtIcon : public AgtFrame

Public Functions

AgtIcon(AgtWidget *parent, int x, int y, int w = 0, int h = 0, const uint8_t *bytes = nullptr, size_t len = 0) noexcept

Construct an icon. Argument order matches AgtImage (parent, x, y, w, h, bytes, len) so the sister widgets read the same. w / h

default to 0 = “size to the decoded

image” per axis (see “Natural sizing”); pass explicit extents to fix the box.

bytes / len default to empty (set the glyph later via set_icon).

~AgtIcon() noexcept override = default

Decoded buffer is freed by the AgtPixmap member; AgtFrame’s dtor cascades into children (this widget has none of its own).

AgtIcon(const AgtIcon&) = delete
AgtIcon(AgtIcon&&) = delete
AgtIcon &operator=(const AgtIcon&) = delete
AgtIcon &operator=(AgtIcon&&) = delete
void set_icon(const uint8_t *bytes, size_t len) noexcept

Replace the current glyph. Frees the previous decoded buffer, decodes bytes. Pass NULL / 0 to clear back to the empty state. Does NOT resize the widget (the box layout gave it is kept). Marks dirty either way.

inline AxlGfxBuffer *icon_buffer() const noexcept

Decoded buffer or NULL. Non-const for the same reason as AgtPixmap::buffer (the axl-gfx pixel/info accessors take a non-const handle).

inline int icon_width() const noexcept

Decoded glyph dimensions in pixels; both 0 in the empty state.

inline int icon_height() const noexcept
void draw(AgtDrawContext &ctx) override

Public Static Functions

static inline AgtIconBuilder 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).

AgtBitmapButton

class AgtBitmapButton : public AgtButton

Public Types

enum IconPlacement

Where the icon sits relative to the caption (group centered in the inner rect). AGT_ICON_LEFT == 0 is the default.

Values:

enumerator AGT_ICON_LEFT

icon left of caption (default)

enumerator AGT_ICON_RIGHT

icon right of caption

enumerator AGT_ICON_ABOVE

icon above caption

enumerator AGT_ICON_BELOW

icon below caption

enumerator AGT_ICON_ONLY

icon only; caption ignored

Public Functions

AgtBitmapButton(AgtWidget *parent, int x, int y, int w, int h, const char *label, const uint8_t *bytes = nullptr, size_t len = 0, AgtObject *target = nullptr, uint16_t target_id = 0) noexcept

Construct a bitmap button. label may be NULL / "" for an icon-only face; bytes / len decode the icon face (pass NULL / 0 to set it later via set_icon). target / target_id route the click SEL_COMMAND exactly as AgtButton (default target = parent).

~AgtBitmapButton() noexcept override = default
AgtBitmapButton(const AgtBitmapButton&) = delete
AgtBitmapButton(AgtBitmapButton&&) = delete
AgtBitmapButton &operator=(const AgtBitmapButton&) = delete
AgtBitmapButton &operator=(AgtBitmapButton&&) = delete
void set_icon(const uint8_t *bytes, size_t len) noexcept

Replace the icon face. Frees the previous decoded buffer, decodes bytes. NULL / 0 clears to no icon. Marks dirty.

void set_stock_icon(AgtStyle::StockIcon id) noexcept

Use a built-in themeable stock icon (drawn procedurally by AgtStyle::draw_stock_icon in the button’s foreground color, dimmed when disabled) instead of a decoded bitmap. Takes precedence over any set_icon buffer until clear_stock_icon(). Marks dirty.

void clear_stock_icon() noexcept

Drop the stock icon (falls back to the decoded bitmap, if any).

inline bool has_stock_icon() const noexcept
inline AgtStyle::StockIcon stock_icon() const noexcept
inline AxlGfxBuffer *icon_buffer() const noexcept

Decoded icon buffer or NULL. Non-const for the same reason as AgtPixmap::buffer.

inline int icon_width() const noexcept
inline int icon_height() const noexcept
inline IconPlacement icon_placement() const noexcept
void set_icon_placement(IconPlacement p) noexcept
void draw(AgtDrawContext &ctx) override

Public Static Functions

static inline AgtBitmapButtonBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr int ICON_TEXT_GAP = 6

Pixels between the icon and the caption when both are shown. Public so the renderer + layout-aware callers read one source (the AgtMenuItem row-inset precedent).

static constexpr uint16_t ID_LAST = AgtButton::ID_LAST

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

AgtSpinner

class AgtSpinner : public AgtWidget

Public Functions

AgtSpinner(AgtWidget *parent, int x, int y, int w, int h, int steps = DEFAULT_STEPS) noexcept

Construct a spinner. steps is the dot count (clamped to a minimum of 2). Starts stopped at phase 0; call start() to self-animate or drive advance() from your own clock.

~AgtSpinner() noexcept override

Stops the animation timer (if any) before teardown.

inline int steps() const noexcept
inline int phase() const noexcept

Index of the brightest dot, in [0, steps()).

void set_phase(int p) noexcept

Set the brightest dot (wrapped into range). Marks dirty.

void advance() noexcept

Advance the brightest dot by one (wraps). This is one animation tick; the self-running timer calls it.

inline AxlGfxPixel active_color() const noexcept

Brightest-dot color and the color the trail fades toward.

inline AxlGfxPixel trail_color() const noexcept
void set_active_color(AxlGfxPixel c) noexcept
void set_trail_color(AxlGfxPixel c) noexcept
inline uint32_t interval_ms() const noexcept

Per-tick interval for the self-running animation. Takes effect immediately if already running.

void set_interval_ms(uint32_t ms) noexcept
inline bool running() const noexcept

True while the self-running timer is armed.

void start() noexcept

Begin self-animating: arm a repeating timer on the window’s event loop that calls advance() + redraws each tick. Means “ensure armed” — calling it when already armed is a no-op, and calling it again after the spinner is attached to a window (having first been started while detached) arms the timer then. With no window/loop bound (the headless test harness) it flips running() but arms no timer — drive advance() directly there. The bound AxlLoop must outlive the spinner (declare AgtApp before the window so the loop tears down last).

void stop() noexcept

Stop self-animating and remove the timer. Safe if not running.

void draw(AgtDrawContext &ctx) override

Paint the ring of dots for the current phase.

void restyle() noexcept override

Re-snapshot the active / trail dot colors on a theme swap.

Public Static Functions

static inline AgtSpinnerBuilder build(AgtWidget *parent) noexcept

Fluent builder: AgtSpinner::build(p).bounds(...).steps(8).create(). See agt-builder.hpp.

Public Static Attributes

static constexpr uint16_t ID_LAST = AgtWidget::ID_LAST

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

static constexpr int DEFAULT_STEPS = 12

Dots around the ring by default.

static constexpr uint32_t DEFAULT_INTERVAL_MS = 80

Default per-tick interval when self-animating (~12.5 fps).

AgtLight

class AgtLight : public AgtLabel

Public Types

enum State

Discrete indicator state. Stable values (OFF == 0 = default) so a caller using the default doesn’t need to import the enum. Indexes the per-state color table, so new states append at the end.

Values:

enumerator OFF

neutral / unlit

enumerator OK

green — pass / healthy

enumerator WARN

amber — caution

enumerator ERROR

red — fail

enumerator BUSY

blue — running / in progress

Public Functions

AgtLight(AgtWidget *parent, int x, int y, int w, int h, const char *label = "", State state = OFF) noexcept

Construct a light at fixed parent-local bounds. label defaults to “” (a caption-less disc); state defaults to OFF.

AgtLight(AgtWidget *parent, const char *label = "", State state = OFF) noexcept

Geometry-free ctor (FOX new FXLabel(p,"text") shape): no x/y/w/h — the light sizes to disc + gap + caption via natural_*() and a layout container places it.

~AgtLight() noexcept override

Stops the blink timer (if any) before teardown.

int natural_width() const noexcept override

Natural size = disc + gap + caption (or just the disc when there is no caption), tall enough for the larger of disc / text.

int natural_height() const noexcept override
inline State state() const noexcept
void set_state(State s) noexcept

Set the state and mark dirty. No-op when unchanged.

inline bool blinking() const noexcept

True when blinking is enabled (whether or not its timer is armed).

void set_blink(bool on) noexcept

Enable / disable the pulse. Disabling seats the phase lit (steady). Does NOT arm the timer — call start() once the window loop is bound (the spinner shape); a layout/test can drive advance_blink().

Current blink phase: true = lit half, false = dim half. Pure state, like AgtSpinner::phase() — pin it for a deterministic baseline.

void advance_blink() noexcept

Flip the blink phase (one animation tick). The self-running timer calls this; drive it directly in a headless layout/test.

void start() noexcept

Begin self-animating: arm a repeating timer on the window’s loop that flips the phase + redraws each half-period. “Ensure armed” — a second start() is a no-op. With no window/loop bound (headless) it flips running() but arms no timer. Safe to call when not blinking (the tick is a no-op until set_blink(true)).

void stop() noexcept

Stop self-animating and remove the timer. Safe if not running.

inline bool running() const noexcept

True while the self-running timer is armed.

Per-half-period interval (ms). Takes effect immediately if running.

inline int indicator_size() const noexcept

Disc diameter in pixels. Default seeds from AgtPalette::indicator_size and tracks the live palette metric; pinning a size here makes it a consumer override that survives a theme swap (the AgtFrame explicit-color shape). Clamped to >= 0.

void set_indicator_size(int px) noexcept
AxlGfxPixel state_color(State state) const noexcept

Base color for state — the per-instance override if one was set via set_state_color, otherwise the live AgtPalette::light token.

void set_state_color(State state, AxlGfxPixel c) noexcept

Pin a per-state color override (mark dirty). Survives a palette swap; pass states you never override to keep tracking the theme.

AxlGfxPixel indicator_color() const noexcept

The color the disc renders with RIGHT NOW: state_color(state()), darkened while blinking and in the dim phase. The renderer reads this so the blink presentation stays widget-owned.

void draw(AgtDrawContext &ctx) override

Delegates to the renderer (AgtStyle::draw_light): flat surface + bezelled disc in indicator_color() + the trailing caption.

void restyle() noexcept override

Re-snapshot the indicator size from the live palette on a theme swap (colors resolve live through state_color).

Public Static Functions

static inline AgtLightBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr int STATE_COUNT = 5

One past the last state — the per-state color table size.

Default blink half-period (ms) when self-animating (~1 Hz pulse).

static constexpr uint16_t ID_LAST = AgtLabel::ID_LAST

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

AgtCanvas

class AgtCanvas : public AgtFrame

Public Types

using PaintFn = void (*)(void *user, AgtDrawContext &ctx, int width, int height)

Per-frame paint callback. ctx is clipped + translated to the inner content rect, so paint in local coordinates from (0, 0) to (width, height). user is the context registered with the callback. NULL is allowed (the canvas then shows only its frame chrome).

Public Functions

AgtCanvas(AgtWidget *parent, int x, int y, int w, int h, PaintFn paint = nullptr, void *user = nullptr) noexcept

Construct a canvas. Same (parent, x, y, w, h) shape as every widget; paint / user wire the draw callback (settable later too).

void set_paint(PaintFn fn, void *user) noexcept

Set (or clear, with NULL) the paint callback + its borrowed context. Marks the canvas dirty so the next present repaints.

inline PaintFn paint_fn() const noexcept
inline void *paint_user() const noexcept
void draw(AgtDrawContext &ctx) override

Paint the frame chrome (AgtFrame::draw), then invoke the callback clipped + translated to the inner content rect. No callback → chrome only.

Public Static Functions

static inline AgtCanvasBuilder 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).

AgtSprite

class AgtSprite : public AgtFrame

Public Functions

AgtSprite(AgtWidget *parent, int x, int y, int w, int h) noexcept
~AgtSprite() noexcept override

Stops the animation timer (if any) before teardown.

void set_sheet(const AxlGfxPixel *pixels, int sheet_w, int sheet_h, int cell_w, int cell_h, int cols, int frame_count) noexcept

Bind the sprite sheet (BORROWED): pixels is a row-major BGRX image sheet_w × sheet_h pixels, laid out as a grid of cell_w × cell_h cells, cols cells per row, frame_count cells total (left-to-right, top-to-bottom). Resets to frame 0. Pass pixels = NULL to clear. No-op on a malformed grid (non-positive dims or cols < 1).

inline int frame() const noexcept
inline int frame_count() const noexcept
inline int cell_width() const noexcept
inline int cell_height() const noexcept
void set_frame(int f) noexcept

Set the visible cell (wrapped into [0, frame_count())). Marks dirty.

void advance() noexcept

Advance the cell by one (wraps). One animation tick.

inline uint32_t interval_ms() const noexcept
void set_interval_ms(uint32_t ms) noexcept
inline bool running() const noexcept
void start() noexcept

Begin self-animating: arm a repeating timer on the window’s loop that advance()s + redraws each tick. “Ensure armed” (idempotent). With no window/loop bound (headless) it flips running() but arms nothing — drive advance() directly there. The bound AxlLoop must outlive the sprite.

void stop() noexcept

Stop self-animating + remove the timer. Safe if not running.

void draw(AgtDrawContext &ctx) override

Blit the current cell, centered + clipped to the inner content rect.

Public Static Functions

static inline AgtSpriteBuilder 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).

static constexpr uint32_t DEFAULT_INTERVAL_MS = 100

Default per-tick interval when self-animating (~10 fps).

AgtAnimatedImage

class AgtAnimatedImage : public AgtFrame

Public Functions

AgtAnimatedImage(AgtWidget *parent, int x, int y, int w, int h) noexcept
~AgtAnimatedImage() noexcept override

Stops the animation timer (if any) before teardown.

void set_frames(const AxlGfxPixel *const *frames, int frame_count, int frame_w, int frame_h) noexcept

Bind the frame list (BORROWED): frames is an array of frame_count pointers, each a row-major BGRX image frame_w × frame_h pixels. Both the array and the pixels are borrowed — the caller keeps them alive for the widget’s lifetime. Resets to frame 0. Pass frames = NULL or a non-positive count/size to clear.

inline int frame() const noexcept
inline int frame_count() const noexcept
inline int frame_width() const noexcept
inline int frame_height() const noexcept
void set_frame(int f) noexcept

Set the visible frame (wrapped into [0, frame_count())). Marks dirty.

void advance() noexcept

Advance the frame by one (wraps). One animation tick.

inline uint32_t interval_ms() const noexcept
void set_interval_ms(uint32_t ms) noexcept
inline bool running() const noexcept
void start() noexcept

Begin self-animating: arm a repeating timer on the window’s loop that advance()s + redraws each tick. “Ensure armed” (idempotent). With no window/loop bound (headless) it flips running() but arms nothing — drive advance() directly there. The bound AxlLoop must outlive the widget.

void stop() noexcept

Stop self-animating + remove the timer. Safe if not running.

void draw(AgtDrawContext &ctx) override

Blit the current frame, centered + clipped to the inner content rect.

Public Static Functions

static inline AgtAnimatedImageBuilder 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).

static constexpr uint32_t DEFAULT_INTERVAL_MS = 100

Default per-tick interval when self-animating (~10 fps).

AgtEditBox

class AgtEditBox : public AgtFrame

Public Types

Message-ID chain. ID_SCROLLBAR is the id the owned scrollbar targets the box with (internal); consumer ids start at ID_LAST.

Values:

enumerator ID_SCROLLBAR

vertical bar → on_scroll

enumerator ID_HSCROLLBAR

horizontal bar → on_hscroll

enumerator ID_LAST

Search-option flags for the unified find / replace_all overloads the find bar drives (bitwise-OR).

Values:

enumerator AGT_FIND_REGEX

query is a regex (else literal)

enumerator AGT_FIND_CASELESS

ASCII case-insensitive.

enumerator AGT_FIND_WHOLE_WORD

only word-delimited matches

Public Functions

AgtEditBox(AgtWidget *parent, int x, int y, int w, int h, const char *text = "") noexcept
~AgtEditBox() noexcept override
AgtEditBox(const AgtEditBox&) = delete
AgtEditBox(AgtEditBox&&) = delete
AgtEditBox &operator=(const AgtEditBox&) = delete
AgtEditBox &operator=(AgtEditBox&&) = delete
inline const char *text() const noexcept
inline int length() const noexcept
inline uint64_t revision() const noexcept

Monotonic content revision (bumps on every edit) — a cheap “did the

text change?” signal for content-derived caches like highlighting.

inline AxlPieceTree *tree() const noexcept

The buffer’s backing AxlPieceTree (borrowed; NULL only on a degenerate doc) — for a shared-tree hex view (AgtPieceTreeHexSource::attach) that reads the SAME bytes, including the editor’s unsaved edits.

void set_text(const char *text) noexcept

Replace the contents. Cursor to end, selection cleared, scroll reset.

bool load_file(const char *path) noexcept

Load the file at path into the buffer (encoding + EOL detected; large UTF-8 files stay out-of-core). Resets the view + cursor to the top; returns false (contents unchanged) on failure.

bool save_file(const char *path) noexcept

Write the buffer to path (current encoding / EOL; a save over the out-of-core backing file rebases — see AgtTextDoc::save_file). Returns false on failure.

void set_page_cache(AxlPageCache *cache) noexcept

Borrow a shared AxlPageCache so several editors’ out-of-core loads share ONE bounded frame budget (a multi-buffer editor — call before load_file). Caller-owns the cache; it must outlive every editor that borrows it. Forwards to AgtTextDoc::set_page_cache.

const char *path() const noexcept

The buffer’s associated file (”” when untitled) and whether it has unsaved edits — for the app’s title / status bar.

bool modified() const noexcept
AxlEncoding encoding() const noexcept

Detected/loaded text encoding + line-ending style (for the status bar; default UTF-8 / LF on an untitled buffer).

AxlEol eol() const noexcept
void set_encoding(AxlEncoding enc, bool bom) noexcept

Change the save encoding / line-ending style (status-bar “switch

modes”). set_eol rewrites the buffer’s line endings; set_encoding only changes the on-disk codec. Both mark the buffer modified and repaint.

void set_eol(AxlEol eol) noexcept
int cursor_line() const noexcept

Cursor position as a 0-based logical line and a 0-based column counted in UTF-8 codepoints from the line start (the status bar shows these +1).

int cursor_col() const noexcept
inline int cursor() const noexcept
inline int anchor() const noexcept
inline int selection_start() const noexcept
inline int selection_end() const noexcept
inline bool has_selection() const noexcept
void set_cursor(int byte_offset) noexcept

Move the cursor (clears selection) and scroll it into view.

void set_selection(int anchor, int cursor) noexcept

Set anchor + cursor directly; scrolls the cursor into view.

void go_to_line(int line) noexcept

Place the cursor at the start of (0-based) logical line, clamped to the document, and scroll it into view. For the editor’s Go to Line.

void select_word_at(int byte_offset) noexcept

Select the run of like-classed characters (word chars, whitespace, or other punctuation) containing the byte at byte_offset, clamped to that character’s line so a selection never crosses a newline. An empty line just places the cursor. This is the double-click gesture and a menu-callable “Select Word”.

void select_line_at(int byte_offset) noexcept

Select the text of the line containing byte_offset, excluding the line terminator. The triple-click gesture / “Select Line”.

void undo() noexcept
void redo() noexcept
inline bool can_undo() const noexcept
inline bool can_redo() const noexcept
void cut() noexcept

Cut the selection to the clipboard: copy, then delete, as one atomic undo step. A no-op with no selection; a failed copy (OOM) aborts without deleting, so text it couldn’t capture is never lost.

void copy() noexcept

Copy the selection to the clipboard as UTF-8 text. A no-op (the clipboard is left untouched) when there is no selection.

void paste() noexcept

Insert the clipboard’s text at the cursor, replacing any selection, as one atomic undo step. A non-text payload or empty clipboard is ignored.

void select_all() noexcept

Select the entire buffer (anchor at 0, cursor at the end).

bool find(const char *query, bool forward = true) noexcept

Find the literal substring query and select the next match. Scans from just past the current match (or from the cursor when no selection is active) so repeated calls step through occurrences, and wraps around the document ends. forward = false searches toward the start. On a hit the match is selected (cursor at its end) and scrolled into view, and the method returns true; on no match (or an empty query) the selection is left unchanged and it returns false. Case-sensitive in v0.1.

bool find_regex(const char *pattern, bool forward = true) noexcept

Regex find: compile pattern (AxlRegex syntax) and select the next/previous match, stepping past the current one and wrapping — the regex counterpart of find. Returns false (selection unchanged) on a malformed pattern or no match. The find-options dialog (Phase 4) toggles between this and literal find.

bool find(const char *query, bool forward, uint32_t flags) noexcept

Find with options. flags == 0 is exactly the plain literal find above (case-sensitive, out-of-core). Any flag routes through the regex engine: a non-AGT_FIND_REGEX query is matched literally (its metacharacters escaped); AGT_FIND_CASELESS folds ASCII case; AGT_FIND_WHOLE_WORD keeps only word-delimited matches. Selects the match + scrolls it into view; returns false on no match / malformed regex.

int replace_all(const char *query, const char *replacement) noexcept

Replace every literal match of query with replacement across the buffer, as ONE atomic undo step; returns the replacement count. Scrolls the (collapsed) cursor into view + repaints when any match was replaced.

int replace_all_regex(const char *pattern, const char *replacement) noexcept

Regex replace-all: compile pattern and replace every match with replacement, expanding \0..\9 capture groups and \\, as ONE atomic undo step; returns the count (0 on a malformed pattern, no match, or error).

int replace_all(const char *query, const char *replacement, uint32_t flags) noexcept

Replace-all with the same option flags as the unified find. flags == 0 is the plain literal replace_all above (the replacement is inserted verbatim). With AGT_FIND_REGEX the replacement expands \0..\9 capture groups + \\; for a literal (non-regex) query the replacement stays verbatim even under CASELESS / WHOLE_WORD. Returns the replacement count.

void replace_selection(const char *text) noexcept

Replace the current selection with text as ONE atomic undo step (inserts at the cursor when nothing is selected; an empty text deletes the selection). No-op when read-only or text is NULL. The find bar’s single Replace uses this on the current match.

int set_filter(const char *query, uint32_t flags) noexcept

Enter “filter mode”: render ONLY the logical lines that match query (option flags as for find — regex / caseless / whole-word), as a live grep over a log. The document is NOT copied — a per-match line INDEX is built over the out-of-core doc (one line number per matching line, de-duped when a line matches more than once), and the view, the scrollbar, Up/Down and clicks all map through it (so a match stays clickable back to its source line). Filter mode is read-only and pins soft-wrap off (one match = one row). The caret parks on the first match. Returns the number of matching lines; an empty query or NO matches leaves the full view unchanged and returns 0 (never shows a blank filtered screen). Re-applying replaces the active filter.

void clear_filter() noexcept

Leave filter mode: restore the full view (and the pre-filter wrap / read-only state), keeping the caret on its current source line — so Esc after browsing matches drops you onto that line in the full document. A no-op when not filtering.

inline bool filtering() const noexcept

Whether filter mode is active.

int filter_match_count() const noexcept

Number of matching lines in the active filter (0 when not filtering).

int filter_source_line(int view_row) const noexcept

Source (full-document) line number shown at filtered view-row view_row, or -1 when not filtering / out of range. The click-to- source map; public for tests.

int line_count() const noexcept

Number of logical lines (\n-delimited; always >= 1).

int line_text(int line, char *out, int cap, int *start) const noexcept

Copy logical line line’s content (excluding the terminator) into out (NUL-terminated within cap); returns the byte count and, if start is non-NULL, the line’s start byte offset. The syntax-highlight engine reads visible lines through this.

inline int top_line() const noexcept

First visible line (vertical scroll position, in line units).

inline int left_offset() const noexcept

Horizontal scroll position in pixels (0 = line start at the left inset; grows as a long line scrolls left).

int visible_lines() const noexcept

Number of fully-visible text lines in the content area.

inline int line_height() const noexcept
void set_line_height(int px) noexcept
inline AxlGfxPixel text_color() const noexcept
inline AxlGfxPixel selection_bg_color() const noexcept
inline AxlGfxPixel cursor_color() const noexcept
void set_text_color(AxlGfxPixel c) noexcept
void set_selection_bg_color(AxlGfxPixel c) noexcept
void set_cursor_color(AxlGfxPixel c) noexcept
inline float text_size() const noexcept

TTF text height (px). Defaults from AgtPalette::current().text_size.

void set_text_size(float px) noexcept
inline AxlTtf *font() const noexcept

Text face. NULL (the default) renders with axl_ttf_default() (the proportional UI font); pass a fixed-width face such as axl_ttf_mono_default() for a code editor. Borrowed — the caller keeps it alive for the widget’s lifetime; changing it reflows.

void set_font(AxlTtf *ttf) noexcept
inline bool zoomable() const noexcept

When enabled, Ctrl+wheel and Ctrl++/- (and Ctrl+0 to reset) zoom the text size in-place (set_text_size, clamped to [6, 72] px), keeping the caret in view. Off by default — a plain text input keeps Ctrl+wheel as ordinary scroll.

inline void set_zoomable(bool on) noexcept
void zoom_in() noexcept

grow the text by one step

Programmatic zoom (clamped to [6, 72] px), independent of zoomable_ (which gates only the Ctrl+wheel/key shortcuts) — wire these to toolbar or menu commands. Each keeps the caret in view.

void zoom_out() noexcept

shrink the text by one step

void zoom_reset() noexcept

back to the construction-time size

inline int tab_width() const noexcept

Tab stop width in columns (one column = the advance of a space at the current text_size); a \t renders by advancing to the next stop. Default 4. Render-only — the buffer keeps the literal tab.

void set_tab_width(int columns) noexcept
inline bool line_numbers() const noexcept

Whether a left-column line-number gutter is shown (default off). The gutter displays each logical line’s 1-based number (the SOURCE line when filtering; blank on soft-wrap continuation rows), right-aligned and dimmed with the cursor’s line emphasized. It narrows the usable text column (so wrap / h-scroll / click mapping all account for it); a click inside the gutter places the caret at that line’s start. A persisted editor preference — see docs/AGT-Settings-Design.md.

void set_line_numbers(bool on) noexcept
inline bool word_wrap() const noexcept

When on, long logical lines wrap to the content width across multiple visual rows and the horizontal scrollbar is suppressed; when off (default), a long line is clipped and the h-scrollbar appears. (This commit lands the wrap-layout engine + the toggle; the wrapped render + caret / click / Up-Down remapping are follow-on commits.)

void set_word_wrap(bool on) noexcept
int wrap_line(int line, int width, int *breaks, int maxbreaks) const noexcept

Wrap-layout query: split logical line line into the visual rows that fit width px — tab-aware, greedy word-break, an over-long word hard-breaking at a codepoint boundary. Fills breaks with line-relative byte offsets (breaks[0] == 0, breaks[rows] == line length; visual row r is [breaks[r], breaks[r+1])) and returns the row count (always >= 1, capped at maxbreaks - 1 rows). A line longer than the internal scratch is wrapped up to that bound (the same cap the renderer uses). Public for the renderer + tests.

inline bool focused() const noexcept
inline uint32_t focus_policy() const noexcept override
inline bool cursor_blink() const noexcept

Whether the text cursor blinks while the box is focused (default true). The blink pauses — cursor solid — for one full interval after any cursor activity (typing / move / click), the standard editor feel. Disable for a steady cursor: an accessibility preference, or deterministic screenshots. The blink is driven by a repeating timer on the window’s event loop, so it only ticks once attached + focused.

inline void set_cursor_blink(bool on) noexcept

Blink half-period in ms (default 530, ~ the Win32 default).

True when the blink phase currently shows the cursor (always true while blink is disabled). The draw gate ANDs this with focus.

One blink half-cycle: flip the phase and repaint. Called by the repeating loop timer; public for the trampoline + direct testing.

inline bool overwrite() const noexcept

Whether typing overwrites the character under the cursor instead of inserting (default false = insert). The Insert key toggles it. In overwrite mode the cursor renders as a char-wide underline; at a line end (or with an active selection) typing still inserts / replaces the selection, so the newline is never consumed.

void set_overwrite(bool on) noexcept
inline bool read_only() const noexcept

Whether the buffer rejects edits (default false). A read-only box still moves the caret, extends + copies selections, finds, scrolls, and zooms — only the mutating paths (typing, Enter/Tab, Backspace/ Delete, cut, paste, undo, redo) become no-ops. For a diagnostics viewer that opens a log / dump without risking accidental edits.

inline void set_read_only(bool on) noexcept
void set_hilite_styles(const AgtHiliteStyle *styles, int count) noexcept

Set the style palette (BORROWED — must outlive the widget). Style index 0 is the default (drawn in text_color()); index i in [1, count] maps to styles[i-1]. NULL / count 0 drops it.

void set_style(int pos, int len, uint8_t idx) noexcept

Mark bytes [pos, pos+len) with style index idx. Runs accumulate in document order (the engine clears, then pushes left-to-right); index 0 leaves the span default. Empty / out-of-range spans ignored.

void clear_styles() noexcept

Drop all style runs (the engine calls this before re-highlighting).

uint8_t style_at(int pos) const noexcept

Style index covering byte pos (0 if none) — for the renderer + tests.

void draw(AgtDrawContext &ctx) override
void restyle() noexcept override
long on_left_button_press(AgtObject *sender, AgtEvent *ev)
long on_left_button_release(AgtObject *sender, AgtEvent *ev)
long on_mouse_motion(AgtObject *sender, AgtEvent *ev)
long on_mouse_wheel(AgtObject *sender, AgtEvent *ev)
long on_key_press(AgtObject *sender, AgtEvent *ev)
long on_focus_in(AgtObject *sender, AgtEvent *ev)
long on_focus_out(AgtObject *sender, AgtEvent *ev)
long on_scroll(AgtObject *sender, AgtEvent *ev)

SEL_COMMAND from the owned vertical scrollbar (id == ID_SCROLLBAR).

long on_hscroll(AgtObject *sender, AgtEvent *ev)

SEL_COMMAND from the owned horizontal scrollbar (id == ID_HSCROLLBAR).

Public Static Functions

static inline AgtEditBoxBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

AgtHexBox

class AgtHexBox : public AgtFrame

Public Types

Values:

enumerator ID_SCROLLBAR

owned vertical bar → on_scroll

enumerator ID_LAST
enum class CopyFormat

Clipboard copy formats (H6). HEX_TEXT is the default Ctrl+C format; the others are menu-driven. RAW pastes back verbatim (round-trips with paste()).

Values:

enumerator HEX_TEXT

“DE AD BE EF” — uppercase, space-separated (text/plain)

enumerator C_ARRAY

“0xDE, 0xAD, 0xBE, 0xEF” — C initializer (text/plain)

enumerator RAW

the literal bytes (application/octet-stream)

Public Functions

AgtHexBox(AgtWidget *parent, int x, int y, int w, int h) noexcept
~AgtHexBox() noexcept override
AgtHexBox(const AgtHexBox&) = delete
AgtHexBox &operator=(const AgtHexBox&) = delete
void set_source(AgtHexSource *src) noexcept

Set the byte source (BORROWED — the caller owns it and keeps it alive for as long as the box references it). NULL shows an empty view. Resets the cursor + scroll to the top.

inline AgtHexSource *source() const noexcept
inline int64_t cursor() const noexcept

Selected byte offset (0-based). set_cursor clamps to the source and scrolls it into view.

void set_cursor(int64_t off) noexcept
inline int64_t anchor() const noexcept
inline bool has_selection() const noexcept
inline int64_t selection_start() const noexcept

Inclusive selection bounds (== cursor when there is no selection).

inline int64_t selection_end() const noexcept
void set_selection(int64_t anchor, int64_t cursor) noexcept

Set anchor + cursor directly (both clamped); scrolls the cursor in view.

void select_all() noexcept

Select every byte (anchor 0 .. cursor size()-1).

int copy(CopyFormat fmt = CopyFormat::HEX_TEXT) noexcept

Copy the selection — or the single cursor byte when none — to the clipboard in fmt. Works in a viewer (read-only). Returns the number of bytes copied (0 = nothing / failure); a huge selection is capped.

int paste() noexcept

Paste over the caret. A text/* clipboard is parsed as hex bytes (“DE AD BE EF”, any whitespace / separators ignored); an application/octet-stream clipboard (a RAW copy) is taken verbatim. Either way the bytes OVERWRITE at the caret (does not grow the view; a span past the end is truncated). Editable sources only; one undo step. Returns the number of bytes written.

inline void set_caret_target(AgtObject *target, uint16_t id) noexcept

Emit AGT_SEL_COMMAND(@a id) to target whenever the bytes a reader at the caret would see change — the caret MOVES (nav / click / find) or the byte(s) under it are EDITED in place / undone without the caret moving — so a data-interpreter panel can re-read cursor(). NULL target detaches.

inline bool read_only() const noexcept

Viewer mode: when true (or the source is not writable()), typing is inert and the cursor renders as a plain selection. Default false (an editor): hex digits overwrite the active nibble, the ASCII pane types whole bytes, Tab switches panes, Ctrl+Z / Ctrl+Y undo / redo.

void set_read_only(bool on) noexcept
void set_enabled(bool enabled) noexcept override

Whether typing currently edits (an editable widget over a writable source). Disabling mid-drag drops the mouse grab cleanly (so a re-enable doesn’t resume from a stale drag) — mirrors AgtSlider.

bool editable() const noexcept
bool can_resize() const noexcept

Whether insert/delete editing is available (editable over a resizable source — a file, not memory / I/O). Gates the Insert-key toggle and Backspace / Delete, and lets the caret reach the append (EOF) cell.

inline bool ascii_pane() const noexcept

Which pane the caret edits — false = hex, true = ASCII (Tab toggles).

inline int nibble() const noexcept

Active hex nibble at the caret: 0 = high, 1 = low (hex pane only).

inline bool overwrite_mode() const noexcept

Typing mode (H3b): true (default) overwrites the byte under the caret; false inserts new bytes before it (the view grows). The Insert key toggles it when can_resize(). At the append (EOF) cell typing always inserts regardless of this flag. Inert on a non-resizable source.

void set_overwrite_mode(bool on) noexcept
void undo() noexcept

Ctrl+Z — revert the last edit (delegates to the source)

void redo() noexcept

Ctrl+Y — re-apply the last undone edit.

bool find(const char *query, bool hex_mode, bool caseless, bool backward, bool wrap, bool regex = false) noexcept

Search for query and move the cursor to the match, highlighting it (out-of-core: scans source() read-windows, so it works over a file, memory, or I/O source). hex_mode parses query as a hex byte pattern with nibble wildcards (DE AD ?? EF; D? = byte 0xD0, mask 0xF0); otherwise query’s literal bytes are matched (caseless = ASCII case-insensitive). Repeating the same query + mode steps to the next (backward = previous) match; wrap continues past the start / end. Returns whether a match was found (the caret + highlight only move on success).

regex (mutually exclusive with hex_mode) treats query as an AxlRegex pattern matched over the raw byte stream (so it spans binary too); caseless maps to AXL_REGEX_CASELESS. The match is variable-length (match_length() reflects it). ^/$ anchor to the true source boundaries (the windowing passes NOTBOL/NOTEOL on mid-stream windows); an empty match (a*, x?) is skipped (a pattern that only ever matches empty re-reads a window per byte — fine for files, pathological for a multi-GB source, but degenerate for a find). Out-of-core caveat: a regex match is found only if it fits within one search window (FIND_WINDOW bytes) — longer matches (e.g. an unbounded .* run) are skipped; literal / hex / bounded patterns are unaffected.

inline int64_t match_offset() const noexcept

The active find-match highlight range; match_length() == 0 when there is none (any manual caret move / edit clears it).

inline int match_length() const noexcept
bool replace_match(const char *replacement, bool hex_mode) noexcept

Replace the current find-match (the match_offset() / match_length() highlight) with replacement. hex_mode parses replacement as concrete hex byte pairs (DE AD), otherwise its literal text bytes; wildcards are not allowed in a replacement (?? / D? → no-op). Equal length overwrites in place (any writable source); a different length splices (delete + insert) and so needs a resizable() source — on a fixed memory / I/O source an unequal-length replace is refused. One undo step. On success the match highlight clears and the caret sits just after the replacement. Pure replace — it does NOT auto-find the next match (a “Replace” UI follows it with find(..., forward)). An EMPTY replacement is rejected (no-op) — delete-the-match is not a replace in v0.1 (mirrors find’s empty-query rejection). Returns whether a replacement was written.

int replace_all(const char *query, bool hex_mode, bool caseless, bool regex, const char *replacement) noexcept

Replace EVERY match of query (parsed exactly as in find) from the start of the source with replacement, as ONE undo group. regex matches replace with the literal replacement bytes (no capture-group back-references in v0.1). Stops early (and leaves the prior replacements) if an unequal-length replace hits a fixed source. Returns the number of replacements made.

int add_region(int64_t start, int64_t len, AxlGfxPixel color, const char *label = nullptr) noexcept

Add a colored byte range [start, start + len) with an optional label (copied; NULL = none). Regions paint as the LOWEST background layer — the find-match highlight, the selection, and the cursor draw on top. Where ranges overlap, the FIRST-added region wins. Returns the region index, or -1 (bad args / allocation failure). Out-of-core safe: only the visible window is consulted at paint time, so thousands of regions over a multi-GB source stay cheap to store; a page paints in O(visible-bytes x regions).

void clear_regions() noexcept

Remove every region.

int region_count() const noexcept
int region_at(int64_t off) const noexcept

Index of the FIRST region containing off, or -1 — drives a “field under the caret” status readout.

const char *region_label(int idx) const noexcept
AxlGfxPixel region_color(int idx) const noexcept
int64_t region_start(int idx) const noexcept
int64_t region_length(int idx) const noexcept
int64_t size() const noexcept

Total bytes (0 when no source).

int bytes_per_row() const noexcept

Bytes shown per row. Default 0 = responsive (the largest of 8/16/32 that fits the content width); a non-zero value FIXES it (clamped to [1, 32]). bytes_per_row() returns the effective count either way.

void set_bytes_per_row(int n) noexcept
int offset_digits() const noexcept

Hex digits in the offset gutter (enough for size(), min 8).

inline int top_row() const noexcept

First visible row (vertical scroll position, in rows).

int total_rows() const noexcept

Total rows = ceil(size / bytes_per_row) (>= 0; 0 when empty).

int visible_rows() const noexcept

Fully-visible rows in the content area.

int64_t byte_at_point(int lx, int ly) const noexcept

Byte offset under a widget-local point (the hex OR ASCII pane), or -1 when the point is outside any byte cell. Public for tests + click.

inline int line_height() const noexcept
void set_line_height(int px) noexcept
inline uint32_t focus_policy() const noexcept override
inline bool focused() const noexcept
void draw(AgtDrawContext &ctx) override
void restyle() noexcept override

Re-snapshot the pane colors + metrics on a theme swap (restyle_tree), so a live light/dark switch re-skins the hex view.

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)
long on_mouse_wheel(AgtObject *sender, AgtEvent *ev)
long on_key_press(AgtObject *sender, AgtEvent *ev)
long on_focus_in(AgtObject *sender, AgtEvent *ev)
long on_focus_out(AgtObject *sender, AgtEvent *ev)
long on_scroll(AgtObject *sender, AgtEvent *ev)

Public Static Functions

static inline AgtHexBoxBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

Public Static Attributes

static constexpr int FIND_MAX_PATTERN = 256

Max search-pattern length, in bytes (hex or text), for find.

AgtHexDataPanel

class AgtHexDataPanel : public AgtFrame

Public Functions

AgtHexDataPanel(AgtWidget *parent, int x, int y, int w, int h) noexcept
~AgtHexDataPanel() noexcept override
AgtHexDataPanel(const AgtHexDataPanel&) = delete
AgtHexDataPanel &operator=(const AgtHexDataPanel&) = delete
void set_source(AgtHexSource *src) noexcept

Byte source to decode (BORROWED — caller keeps it alive). NULL = empty.

void set_offset(int64_t off) noexcept

The offset whose bytes are interpreted (the hex caret). Clamped >= 0.

inline int64_t offset() const noexcept
inline bool little_endian() const noexcept

Endianness of multi-byte decodes (default little-endian, x86-native).

void set_little_endian(bool le) noexcept
inline void toggle_endian() noexcept
int read_cursor_bytes(uint8_t *out8) const noexcept

Read up to 8 bytes at offset() into out8 (caller-sized >= 8), honouring the source’s access_width() alignment: width>1 register sources (whose read() refuses an unaligned offset) are read at an aligned base and the caret’s byte is shifted to out8[0]. Returns the number of bytes available from offset() (0..8). Public for tests + reuse; draw() decodes from exactly these bytes.

void draw(AgtDrawContext &ctx) override
inline int line_height() const noexcept
void set_line_height(int px) noexcept

Public Static Functions

static inline AgtHexDataPanelBuilder build(AgtWidget *parent) noexcept

Fluent builder: see agt-builder.hpp.

AgtHexSource

class AgtHexSource

Subclassed by AgtPieceTreeHexSource, AgtRangeHexSource

Public Functions

virtual ~AgtHexSource() noexcept = default
virtual int64_t size() const noexcept = 0

Total addressable bytes (0 when closed / empty).

virtual int read(int64_t off, int len, uint8_t *buf) const noexcept = 0

Copy [off, off+len) into buf; returns the number of bytes actually read — clamped to size() (0 when off is past the end, len <= 0, or buf is NULL). Reads ONLY the requested span (out-of-core).

inline virtual bool writable() const noexcept

Whether bytes can be written at all (overwrite). False = a viewer.

inline virtual bool resizable() const noexcept

Whether bytes can be inserted / removed (length changes). Memory is fixed-size (false); a file is resizable (true). Only meaningful when writable().

inline virtual bool is_volatile() const noexcept

Whether the bytes can change underneath us (live memory) — the widget re-reads the visible window every repaint instead of caching it.

inline virtual uint32_t access_width() const noexcept

Read/write granularity in bytes (1 for a byte-addressable file or RAM; 2/4/8 for a width-N register space). read() offsets and lengths must be multiples of this, so windowed scanners (find) align to it.

inline virtual bool overwrite(int64_t, const uint8_t*, int) noexcept

Overwrite n bytes at off in place (no length change). All writable sources support this.

inline virtual bool insert(int64_t, const uint8_t*, int) noexcept

Insert n bytes at off (grows the source). Resizable sources only.

inline virtual bool remove(int64_t, int) noexcept

Remove n bytes at off (shrinks the source). Resizable sources only.

inline virtual bool can_undo() const noexcept
inline virtual bool can_redo() const noexcept
inline virtual bool undo() noexcept
inline virtual bool redo() noexcept
inline virtual void begin_edit_group() noexcept

Open / close an atomic undo group: every mutation between a begin_edit_group() and its matching end_edit_group() undoes as ONE step. Nestable (depth-counted) — the widget brackets a two-nibble hex byte so it is a single undo step even though it overwrites/inserts twice. Default no-ops; a source with undo (the file) maps them to its history. Calls must be balanced.

inline virtual void end_edit_group() noexcept
inline virtual const char *label() const noexcept

A short human label for the status bar (a file basename, or a memory region descriptor like “RAM 0x…”). Never NULL; “” when unknown.

AgtRangeHexSource

class AgtRangeHexSource : public AgtHexSource

Subclassed by AgtIoHexSource, AgtMemoryHexSource

Public Functions

AgtRangeHexSource() noexcept = default
void set_window(uintptr_t base, uint64_t len, uint32_t access_width = 1) noexcept

View [base, base+len) of the address space at access_width-byte granularity (1/2/4/8, clamped to 1 on a bad value). base should be aligned to access_width. Rebuilds the label from the region.

inline uintptr_t base() const noexcept
inline uint32_t access_width() const noexcept override
inline int64_t size() const noexcept override
int read(int64_t off, int len, uint8_t *buf) const noexcept override
inline bool resizable() const noexcept override
inline bool is_volatile() const noexcept override
bool writable() const noexcept override
bool overwrite(int64_t off, const uint8_t *bytes, int n) noexcept override
inline const char *label() const noexcept override

AgtMemoryHexSource

class AgtMemoryHexSource : public AgtRangeHexSource

Public Functions

AgtMemoryHexSource() noexcept = default
AxlMemRegionType region_type() const noexcept

The region type base falls in (RAM / MMIO / …) — for the UX banner.

AgtIoHexSource

class AgtIoHexSource : public AgtRangeHexSource

Public Functions

AgtIoHexSource() noexcept = default
AxlIoRegionType region_type() const noexcept

The I/O range type base falls in (IO / RESERVED / UNMAPPED).

AgtPieceTreeHexSource

class AgtPieceTreeHexSource : public AgtHexSource

Public Functions

AgtPieceTreeHexSource() noexcept = default
~AgtPieceTreeHexSource() noexcept override
AgtPieceTreeHexSource(const AgtPieceTreeHexSource&) = delete
AgtPieceTreeHexSource &operator=(const AgtPieceTreeHexSource&) = delete
bool open(const char *path, AxlPageCache *cache = nullptr) noexcept

Open path raw + out-of-core, replacing any currently-open file. When cache is non-NULL the file view borrows that shared page-cache frame budget (an editor with several open buffers); NULL gives the source its own frames. Returns false (source left closed) on failure.

void attach(AxlPieceTree *tree, const char *label = nullptr) noexcept

Read an EXISTING (BORROWED) piece tree — the bytes another widget owns, e.g. an AgtEditBox’s AgtTextDoc tree, for a shared-tree text↔hex view (no reload, no second copy; the hex view even reflects the text view’s unsaved edits). The source does NOT own / free tree, and a borrowed tree is read-only via this source (writable() is false) so a hex edit can never bypass the owner’s caches. Replaces any currently-open file (freeing it). label is the status-bar label (NULL = “”). NULL tree leaves the source empty.

int64_t size() const noexcept override
int read(int64_t off, int len, uint8_t *buf) const noexcept override
inline bool writable() const noexcept override
inline bool resizable() const noexcept override
bool overwrite(int64_t off, const uint8_t *bytes, int n) noexcept override

Overwrite n bytes at off in place (fixed length — refuses a span past the end), as ONE undo step. Returns whether it applied.

bool insert(int64_t off, const uint8_t *bytes, int n) noexcept override

Insert n bytes at off (grows the file); off == size() appends. One undo step. Refused on a borrowed tree. Returns whether it applied.

bool remove(int64_t off, int n) noexcept override

Remove n bytes at off (shrinks the file), clamped to the tail. One undo step. Refused on a borrowed tree. Returns whether it applied.

bool can_undo() const noexcept override
bool can_redo() const noexcept override
bool undo() noexcept override
bool redo() noexcept override
void begin_edit_group() noexcept override
void end_edit_group() noexcept override
inline const char *label() const noexcept override
inline AxlPieceTree *tree() const noexcept

The backing tree (borrowed) — for the future shared-tree text↔hex toggle + the H3 editing surface. NULL when closed.