AgtCore — Object + Widget + Event + Meta

AgtCore — Object tree, widget base, event payload, message map

The foundation every other AGT module builds on. Defines the parent/child ownership tree, the visible-widget base class, the input event payload, and the FOX-style message-map dispatch machinery.

Headers:

  • <agt/agt-object.hpp>AgtObject parent/child tree root

  • <agt/agt-widget.hpp>AgtWidget visible-node base (bounds, dirty flag, enabled state, draw / handle_event virtuals)

  • <agt/agt-event.hpp>AgtEvent input + dispatch payload

  • <agt/agt-meta.hpp>AgtMetaClass + AgtMessageMap + the AGT_MAP_* macros for declaring per-class message tables

  • <agt/agt-palette.hpp>AgtPalette, the color/metric DATA (Qt QPalette analog): tokens + nested per-widget sections + the JSON5 theme-file loader (agt_palette_load / agt_palette_parse).

  • <agt/agt-style.hpp>AgtStyle, the polymorphic RENDERER (Qt QStyle analog): draw_* per control. Both axes resolve down the widget tree and swap live; see the design doc’s Styling policy.

The Qt/GTK-shape coordinate model is backed by axl-sdk’s AxlTransform (from <axl/axl-math.h>), not an AGT type: AgtDrawContext carries one and maps widgets’ local coordinates to device pixels through it. See the design doc’s Coordinate model section.

This module is paradigm-agnostic across the rest of AGT — every container, control, and dialog in the upcoming widget set inherits from these four building blocks. No knowledge of rendering, layout, or specific widget semantics leaks into core.

AgtObject — the ownership tree

AgtObject is the root of every AGT class hierarchy. It carries the parent/child ownership tree shared by all widgets, containers, and dialogs — an intrusive doubly-linked sibling list with first and last child pointers, no ref counting and no type registry.

Memory model: every node has at most one parent; the parent’s destructor cascades into every descendant in depth-first order. The application’s top-level node (an AgtWindow from the render module) owns the tree’s lifetime.

AgtObject also defines:

  • is_widget() virtual — false by default, overridden to true by AgtWidget. Lets walks that need to safely static_cast<AgtWidget *> an AgtObject * (the render walk, hit-test, mark_dirty propagation) gate on the virtual rather than RTTI.

  • on_widget_destroyed(AgtWidget *) virtual hook — called by AgtWidget’s destructor on every ancestor before unhook, letting ancestors clear pointers (AgtWindow::hovered_) that match the dying widget. Pre-condition: only inspect the pointer as an identity comparison; do not dereference (the widget is mid-destruction).

  • handle_message(sender, selector, id, ev) — the dispatch root. Walks this object’s metaclass chain (see Message maps below) to find a matching handler.

AgtWidget — the visible-node base

AgtWidget extends AgtObject with the parts a visible node needs: a bounding rect in parent-local coordinates, a dirty flag for the render pipeline, an enabled flag for input gating, and the two virtuals (draw, handle_event) that the render loop and event loop dispatch through.

The geometry is stored in parent-local coordinates(x, y) is the offset from the parent’s top-left, (w, h) is the widget’s size. AgtWindow (the only top-level widget) is the exception: its geometry is in screen space. Every other widget transitively resolves through its ancestor AgtWindow via the render walk’s origin accumulator in AgtDrawContext (render module).

The dirty flag drives the render pipeline: setting it schedules a redraw of this widget on the next render pass, and propagates up the parent chain so the owning window knows to flip its back buffer. mark_dirty() is the entry point — it walks the parent chain (skipping non-widget ancestors via is_widget()) and sets the bit on every widget along the way. Default state is dirty so a freshly-constructed widget renders on its first frame.

Enabled state lives on AgtWidget so every future interactive widget inherits the same set_enabled(bool) shape — matches FOX FXWindow::isEnabled placement. Subclasses override set_enabled to hook the transition (AgtButton clears hover_/pressed_ on a was-enabled-now-not edge so a re-enabled button doesn’t re-emit a click).

handle_event(AgtEvent &) delegates to handle_message() so the per-class AGT_MAP_* table drives input routing; the return value mirrors ev.consumed(). Subclasses override only when they want custom routing (e.g. AgtWindow does its own hit-testing before dispatching to children).

AgtEvent — the input + dispatch payload

AgtEvent is the tagged-union view over an axl-input AxlInputEvent plus AGT-specific dispatch metadata. The selector (AGT_SEL_*) identifies the event class; positional fields hold pointer / touch coordinates (translated to widget-local for hit-tested events); button + modifier bitfields mirror axl-input.

Dispatch metadata:

  • sender — the widget that originated the event (e.g. the AgtButton (widgets module) that emitted AGT_SEL_COMMAND when clicked). NULL for raw input events that haven’t been re-emitted by a widget yet.

  • message_id — the consumer-defined command id passed through AGT_MAP_COMMAND. Zero for non-command events.

  • consumed — handler sets this true to stop propagation; the dispatcher respects it. An unconsumed event bubbles up the parent chain.

Selectors

Framework selectors live in the AGT_SEL_* constant block (0 through AGT_SEL_LAST_RESERVED reserved for AGT). Consumer code that defines its own synthetic events allocates selectors at or above AGT_SEL_LAST_RESERVED + 1.

Message maps

AGT widgets receive input events through a FOX-style message map: each class declares a compile-time table of (selector, id-range, handler) tuples linked by a base_class pointer that mirrors C++ inheritance. Dispatch walks the map of the dynamic type first, then the base’s, then the base’s base, until either a match is found or the chain reaches AgtObject (whose base_class is NULL).

Storage shape per class:

  • static const AgtMessageMap CLASS::map_entries_[] — packed, sentinel-terminated array

  • static const AgtMetaClass CLASS::metaclass — name, base pointer, entries pointer, count

  • virtual const AgtMetaClass *get_metaclass() const — returns the dynamic type’s metaclass

Type safety: handlers are stored as plain free function pointers (the trampoline), not pointer-to-members. The trampoline template captures the derived class type from the member-pointer’s type and emits a safe static_cast<Class *> before invoking the member function. No FOX-style reinterpret_cast on pointer-to-member; everything is well-defined C++.

Declaring a class with a message map

class MyDialog : public AgtDialog {
public:
    long on_ok_clicked    (AgtObject *sender, AgtEvent *ev);
    long on_cancel_clicked(AgtObject *sender, AgtEvent *ev);

    AGT_DECLARE_MAP(MyDialog)
};

AGT_MAP_BEGIN(MyDialog, AgtDialog)
    AGT_MAP_COMMAND(ID_OK,     &MyDialog::on_ok_clicked)
    AGT_MAP_COMMAND(ID_CANCEL, &MyDialog::on_cancel_clicked)
AGT_MAP_END(MyDialog, AgtDialog)

Available entry macros

  • AGT_MAP_COMMAND(ID, &Class::method) — bind a single AGT_SEL_COMMAND id

  • AGT_MAP_COMMAND_RANGE(LO, HI, &Class::method) — bind a contiguous block of command ids to one handler (FOX’s FXMAPFUNCS analog)

  • AGT_MAP_EVENT(SEL, &Class::method) — bind a selector (e.g. AGT_SEL_LEFTBUTTONPRESS) where the id isn’t meaningful

Dispatch

AgtObject::handle_message walks the metaclass chain from the dynamic type’s map upward, calling the first handler whose (selector, id_low..id_high) matches. Returns the handler’s return value, or 0 if no handler matched.

For AgtWidget and subclasses, the default AgtWidget::handle_event delegates to handle_message so the per-class map drives input handling. Override handle_event only for custom routing (e.g. a composite widget that does its own hit-testing before dispatching to children — AgtWindow does exactly this).

FOX-style ID chain

Every concrete class declares static constexpr uint16_t ID_LAST chained from its base — AgtObject::ID_LAST = 0, AgtWidget::ID_LAST = AgtObject::ID_LAST, etc. Consumer code following the same chain pattern guarantees command IDs across the inheritance tree never collide:

enum {
    ID_OK = AgtDialog::ID_LAST,
    ID_CANCEL,
    ID_LAST
};

See the API Reference section for the full surface.

API Reference

AgtObject

class AgtObject

Subclassed by AgtApp, AgtWidget

Public Functions

AgtObject() = default
virtual ~AgtObject() noexcept

Cascade-delete all children depth-first, then unhook from our parent’s sibling list if we still have one. Walking the children before unhooking keeps the parent’s iteration pointer stable when a child destructor runs.

AgtObject(const AgtObject&) = delete
AgtObject(AgtObject&&) = delete
AgtObject &operator=(const AgtObject&) = delete
AgtObject &operator=(AgtObject&&) = delete
inline AgtObject *parent() const noexcept

Owning parent, or NULL for a detached root.

inline AgtObject *first_child() const noexcept

First child in document order (the child added earliest with add_child). Iterate via child->next_sibling().

inline AgtObject *last_child() const noexcept

Last child in document order — the most-recently-added child. FOX maintains both endpoints so add-to-tail is O(1) and the iterate-in-reverse pattern (typical for hit-testing top-most child first) is also O(1) per step.

inline AgtObject *next_sibling() const noexcept
inline AgtObject *prev_sibling() const noexcept
void add_child(AgtObject *child) noexcept

Append child to our child list at the tail. child must currently be detached (child->parent() == NULL); if it is attached to a different parent, the implementation reparents it (detach + re-attach). No-op when child is NULL, already our child, or this (cycle guard). Ownership transfers to the parent: the parent’s destructor will eventually free child.

void remove_child(AgtObject *child) noexcept

Detach child from our child list without deleting it. After this returns, the caller owns child again and is responsible for either re-parenting or deleting it. No-op if child is not actually one of our children.

uint32_t child_count() const noexcept

Count of direct children (does not recurse). O(n) in the number of children — intentional, since most use sites already have a pointer-walk loop and don’t need the count. uint32_t rather than size_t to match the rest of the AGT API (axl-sdk’s count idiom).

inline virtual bool is_widget() const noexcept

True if this object is an AgtWidget (or subclass). Default false; AgtWidget overrides to true. Used by the render walk, hit-test, and mark_dirty parent walk to safely distinguish “renderable” objects from bare AgtObjects without RTTI. A mixed tree (widgets under a non-widget root, or a widget whose ancestor is a non-widget) is legal and remains coherent because these paths gate on is_widget() before any static_cast<AgtWidget *>.

inline virtual void on_widget_destroyed(AgtWidget *w) noexcept

Notification hook called by AgtWidget’s destructor on every ancestor before the widget unhooks from its parent. AgtWindow overrides this to clear any hovered_ / focus pointer that matches the destroyed widget, preventing the use-after-free that would otherwise hit on the next mouse event. Default no-op. Implementations MUST only inspect w as a pointer value (do not deref — the widget is mid-destruction; only AgtObject’s subobject is intact).

inline virtual void on_tree_mutated() noexcept

Notification hook called on a parent (this) right after a real add_child / remove_child mutates its child list. The damage-tracked renderer uses it to force a full present on the frame a widget appears / disappears / reparents (the incremental damage path can’t know a newly-attached subtree’s footprint). AgtWidget overrides it to flag its owning AgtWindow; AgtWindow flags itself. Default no-op.

long handle_message(AgtObject *sender, uint16_t selector, uint16_t id, AgtEvent *ev)

Walk this object’s metaclass chain looking for a handler matching (selector, id). Calls the first match found (most-derived class wins). Returns the handler’s return value, or 0 if no handler matched.

Most consumer code goes through AgtWidget::handle_event instead of calling this directly; this is the dispatch engine that handle_event delegates to.

Public Static Attributes

static constexpr uint16_t ID_NONE = 0

FOX-style message-ID chain anchor. Every class that defines its own AGT_MAP_COMMAND ids should declare an enum { ID_X = Base::ID_LAST, ID_Y, ..., ID_LAST } block — the chain guarantees IDs never collide across the class hierarchy. AgtObject::ID_LAST is the chain root.

**ID_NONE == 0 is reserved** (FOX FXWindow::ID_NONE shape): it is the “no command” sentinel — a widget’s default / not-yet- wired target_id is 0 — and is intentionally never mapped, so emitting it is always a benign no-op. The chain therefore starts at ID_LAST == 1; the first real command in any branch is Base::ID_LAST, never 0.

static constexpr rather than an anonymous enum because anonymous-enum-of-base-class-reference confuses doxygen/breathe; the visible behavior is identical (Class::ID_LAST is a constant expression usable in a derived enum).

Example:

class MyDialog : public AgtDialog {
public:
    enum {
        ID_OK = AgtDialog::ID_LAST,
        ID_CANCEL,
        ID_LAST
    };
    ...
};

static constexpr uint16_t ID_LAST = 1

AgtWidget

class AgtWidget : public AgtObject

Subclassed by AgtFrame, AgtSeparator, AgtSpacer, AgtSpinner, AgtWindow

Public Types

FOX-style message-ID chain (see AgtObject::ID_LAST). The base AgtWidget ships the standard widget-state commands (FOX FXWindow shape): target ANY widget with one of these to change its state with no custom handler — e.g. a button wired to &other, AgtWidget::ID_DISABLE greys other out. Derived classes chain their own ids from ID_LAST.

Values:

enumerator ID_HIDE

set_visible(false)

enumerator ID_SHOW

set_visible(true)

enumerator ID_TOGGLESHOWN

set_visible(!is_visible())

enumerator ID_DISABLE

set_enabled(false)

enumerator ID_ENABLE

set_enabled(true)

enumerator ID_TOGGLEENABLED

set_enabled(!enabled())

enumerator ID_LAST

Public Functions

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

Construct a widget at the given parent-local bounds. Pass NULL parent for a detached widget (AgtWindow is the canonical detached root).

~AgtWidget() noexcept override

Notify ancestors via on_widget_destroyed, then chain to AgtObject::~AgtObject. The notification lets the owning AgtWindow clear hovered_ / focus pointers that match this widget before the parent unhooks it — fixing the UAF that would otherwise hit on the next mouse event. Only fires when this widget has a live parent (parent_ is NULL during cascade-delete, which is exactly when no notification is needed because the window is also dying).

virtual void draw(AgtDrawContext &ctx)

Draw this widget into ctx. Default no-op — concrete widget subclasses override to paint themselves.

Pass-by-reference signature matches FOX (FXWindow::onPaint passes an FXDCWindow &). The forward-declared AgtDrawContext is enough at the call-site declaration; concrete subclasses pull in the header where they need to touch context members.

virtual bool handle_event(AgtEvent &ev)

Handle an input event. Default implementation delegates to handle_message() so the per-class AGT_MAP_* table gets walked; the consumed flag on ev controls propagation. Subclasses override only when they want custom routing (e.g. an AgtComposite that does its own hit-testing before dispatching to children).

Returns true if the event was consumed (don’t propagate further), false to let the event bubble to the parent. The return value mirrors ev.consumed() — either is usable at the call site.

const AgtStyle &style() const noexcept

The renderer that paints this widget — its own set_style, else the nearest widget ancestor’s, else AgtStyle::current().

const AgtPalette &palette() const noexcept

The color/metric data this widget reads — its own set_palette, else the nearest widget ancestor’s, else AgtPalette::current().

void set_style(const AgtStyle *style) noexcept

Pin a renderer on this widget (and, by ancestor resolution, its subtree). NULL clears the pin (back to inherited/global). Marks the subtree dirty. After swapping the GLOBAL style instead, call restyle_tree() is unnecessary (the renderer is read live) — just invalidate_all() + redraw().

void set_palette(const AgtPalette *palette) noexcept

Pin palette data on this widget (and its subtree). NULL clears the pin. Re-snapshots this subtree’s palette-derived colors (restyle_tree()) and marks it dirty.

inline virtual void restyle() noexcept

Re-read this widget’s palette-derived colors from palette() (Qt changeEvent(PaletteChange) shape). Default no-op; chrome widgets override to re-snapshot the tokens they cached at construction (skipping any the consumer overrode explicitly). Called by restyle_tree() after a palette swap.

void restyle_tree() noexcept

Re-snapshot palette colors across this widget’s whole subtree (depth-first restyle()), then mark it dirty. Call after mutating AgtPalette::current() to re-color already-built widgets, then redraw() the window.

virtual uint32_t state_flags() const noexcept

The widget’s current interaction state as AgtWidgetState flags — the renderer’s state seam (Qt QStyleOption::state). The base reports ENABLED (when enabled) + FOCUSED (when it holds the window focus); interactive subclasses OR in HOVERED/PRESSED/ CHECKED/SELECTED.

bool has_focus() const noexcept

True when this widget holds its window’s keyboard focus.

inline constexpr int x() const noexcept
inline constexpr int y() const noexcept
inline constexpr int width() const noexcept
inline constexpr int height() const noexcept
void set_bounds(int x, int y, int w, int h) noexcept

Replace the widget’s bounding rect. Marks dirty so the next render frame repaints the affected region. No-op (no dirty mark, no membery write) when the new bounds equal the current bounds — set_bounds in an animation hot path doesn’t waste a frame on identical values.

This is the consumer path: it PINS the size (width_fixed() / height_fixed() become true), so a container respects the pinned w/h instead of the widget’s natural_*() size (FOX LAYOUT_FIX_*). A layout manager writes child geometry via the internal place() instead, which leaves the fixed flags alone.

inline virtual int natural_width() const noexcept

The width/height the widget’s content wants, in pixels. Base returns the current width()/height(); content widgets (button, label, …) override to measure (e.g. axl_ttf_measure of the label + insets — pure, no GOP, so it works headless). Read by stretching containers for an AUTO child.

inline virtual int natural_height() const noexcept
inline bool width_fixed() const noexcept

True when the width/height is PINNED by the consumer (explicit-geometry ctor / set_bounds / set_fixed_*) — a container then honors the fixed size rather than natural_*(). Default true (so every existing explicit-geometry call site keeps its exact size); a geometry-free ctor clears it.

inline bool height_fixed() const noexcept
void set_fixed_width(bool fixed) noexcept

Pin / unpin the natural-size override per axis (or both). Pinning false lets a container size this widget to its natural_*().

void set_fixed_height(bool fixed) noexcept
void set_fixed_size(bool fixed) noexcept
inline int stretch() const noexcept

Main-axis stretch weight (Qt stretch-factor shape). 0 (default) = keep natural size; >0 = share the container’s leftover main-axis space in proportion to weight. An AgtSpacer with a weight is a flexible spring. (Boxes; AgtGrid uses per-row/col weights.)

void set_stretch(int weight) noexcept
inline bool fill_cross() const noexcept

Fill the container’s CROSS axis (FOX LAYOUT_FILL_*): in an AgtVBox stretch to the inner width, in an AgtHBox to the inner height; in an AgtGrid, fill the cell. Default false (natural cross size, positioned at the cross origin).

void set_fill_cross(bool fill) noexcept
inline constexpr bool dirty() const noexcept
inline constexpr bool enabled() const noexcept

Enabled state — disabled widgets render greyed (subclass responsibility, e.g. AgtLabel selects disabled_label_color) and ignore input (interactive subclasses gate handlers on enabled()). Default true. Lives on AgtWidget rather than any interactive subclass so every future control inherits the same set_enabled(false) shape — matches FOX’s FXWindow::isEnabled placement.

virtual void set_enabled(bool enabled) noexcept

Set enabled state and mark dirty (palette change repaints). Virtual so interactive subclasses can hook the transition — e.g. AgtButton::set_enabled(false) also clears hover/ pressed so a disabled button doesn’t re-emit a click when re-enabled mid-press.

inline constexpr bool is_visible() const noexcept

Visibility — a hidden widget (and its whole subtree) is neither drawn by AgtWindow::render_subtree nor returned by widget_at / hit_test_recursive (so it’s non-clickable too). Default true. Unlike the reparent-to-hide trick the composites use for popups, this keeps the widget parented in place — the model a tab container (show one stacked page) or a collapsible section needs. NOTE (v0.1): box layouts still reserve cell space for a hidden child; widgets that stack children in one rect (AgtTabView) position them manually, so they’re unaffected.

bool effectively_visible() const noexcept

True only if this widget AND every widget ancestor up the parent chain are visible — i.e. it would actually be rendered. Unlike is_visible() (which reads only this widget’s own flag), this catches a hidden ancestor — e.g. a widget on an INACTIVE AgtTabView page (the page is set_visible(false), the child’s own flag stays true). Animated widgets gate their self-driven repaints on this so an unseen animation doesn’t burn CPU (and, behind a modal, doesn’t thrash the veil’s full-screen blur).

void set_visible(bool visible) noexcept

Set visibility; marks dirty on a real change so the next frame repaints the revealed/vacated region.

long on_cmd_hide(AgtObject *sender, AgtEvent *ev)
long on_cmd_show(AgtObject *sender, AgtEvent *ev)
long on_cmd_toggleshown(AgtObject *sender, AgtEvent *ev)
long on_cmd_disable(AgtObject *sender, AgtEvent *ev)
long on_cmd_enable(AgtObject *sender, AgtEvent *ev)
long on_cmd_toggleenabled(AgtObject *sender, AgtEvent *ev)
inline const char *tooltip() const noexcept

Hover-tooltip text, or NULL for “no tooltip” (the default). The string is BORROWED — the caller owns the storage and must keep it alive for the widget’s lifetime (same convention as AgtLabel’s label / AgtMovableFrame’s title). When set, AgtWindow pops a floating AgtTooltip after the pointer rests over this widget for the hover delay; an empty or NULL string suppresses it. Lives on AgtWidget so every control can carry a tip with no per-class plumbing (matches FOX FXWindow::tipText).

inline void set_tooltip(const char *text) noexcept
inline bool has_tooltip() const noexcept

True when this widget has a non-empty tooltip string.

inline virtual uint32_t focus_policy() const noexcept

Keyboard-focus policy — HOW this widget can acquire focus (Tab / click / wheel; see AgtFocusPolicy). Default AGT_FOCUS_NONE: display-only widgets (Label, Frame, ProgressBar, Image) opt out. Interactive widgets (Button, CheckBox, RadioButton, EditField, Slider, ListBox, …) override to AGT_FOCUS_STRONG. See docs/AGT-Input-Focus-Design.md §2.1.

inline bool can_focus() const noexcept

True if this widget participates in Tab focus cycling (e.g. AgtDialog’s Tab handler) — derived from focus_policy(). Note: SEPARATE from the actual focus state in AgtWindow::focused() — focus can be programmatically directed at any widget via set_focus(); can_focus() only controls Tab-navigation inclusion.

inline virtual bool is_focus_scope() const noexcept

True if this widget is a focus scope — Tab traversal is confined to (and wraps within) its subtree, never escaping to siblings. Default false (the owning AgtWindow is the implicit root scope). A modal dialog body or a future docked panel that must trap Tab overrides this. See docs/AGT-Input-Focus-Design.md §2.3.

inline virtual bool same_focus_group(const AgtWidget &other) const noexcept

Focus group membership: true if other is in the SAME focus group as this widget (radio buttons sharing a parent + target_id). A group is ONE Tab stop — traversal keeps only the group-selected member (or, if none, the first) — while arrow keys move focus + selection WITHIN it. Default false (each widget independent). See docs/AGT-Input-Focus-Design.md §2.4.

inline virtual bool is_group_selected() const noexcept

True if this widget is the SELECTED member of its focus group — the representative that Tab traversal lands on. Default false.

virtual bool wants_child_clip(AxlGfxClip &out) const noexcept

Opt into clipping this widget’s children to a sub-rectangle during both the render walk and hit-testing. Default returns false (children draw + hit-test unclipped, as every widget did before). A container that confines its children to a viewport — a scroll frame, a clipped list — overrides this to return true and fill out with the clip in WIDGET-LOCAL coordinates (its own frame, (0, 0) = its top-left, the same space draw() authors in). AgtWindow::render_subtree pushes the clip before recursing into the children and pops it after; hit_test_recursive honors the same clip so a child masked out of view is also non-clickable (input matches what is painted).

AgtWindow *window() noexcept

The AgtWindow this widget belongs to, or NULL when the widget is detached (no AgtWindow ancestor). Walks the parent chain and, at each ancestor, the metaclass base_class chain looking for AgtWindow — so a consumer subclass of AgtWindow (with its own AGT_DECLARE_MAP, the common case) is still recognized, where an exact-type or topmost-root check would miss it. Used by widgets that need the window for mouse capture or focus (Slider, EditField, MenuBar, MovableFrame). (AgtMenu deliberately shadows this with a cached pointer that’s valid even before it’s attached — see its window().)

void absolute_origin(int *out_x, int *out_y) const noexcept

This widget’s top-left in window/screen coordinates — the sum of x() / y() for this widget and every widget ancestor (stopping at a non-widget ancestor, which carries no offset). The canonical local→absolute query, mirroring Qt’s mapToGlobal / GTK’s gtk_widget_translate_coordinates; use it instead of re-implementing the parent-walk per widget.

void map_to_window(int lx, int ly, int *out_x, int *out_y) const noexcept

Map a widget-local point to window/screen coordinates (absolute_origin() plus the local offset).

virtual void mark_dirty() noexcept

Mark this widget (and its widget ancestors transitively) dirty. Walking the parent chain lets the render loop find the owning window via a single bit-check at the root. Non-widget ancestors (is_widget() == false) are skipped safely. Also unions this widget’s painted footprint into the owning window’s damage region for incremental present. Virtual so AgtWindow can force a full present when it invalidates itself (a window-wide change has no boundable footprint).

inline virtual int dirty_margin() const noexcept

Pixels this widget paints BEYOND its own bounds, in any direction (a symmetric over-draw margin). Default 0 — a widget paints only inside its bounds. Shadow-casting widgets (AgtMovableFrame, AgtTooltip, AgtMenu) override it to return their drop-shadow extent so the damage region covers the shadow — otherwise a dragged/hidden shadowed widget would leave a shadow trail on the incremental-present path.

void on_tree_mutated() noexcept override

AgtObject hook: a child was attached to / detached from this widget. Flags the owning window to full-present the next frame (a structural change the damage path can’t bound).

inline void clear_dirty() noexcept

Called by the render loop after a draw completes. Does NOT propagate — only the widget that was drawn clears its own flag. The owning AgtWindow clears its own dirty bit separately after presenting the back buffer. Default state is dirty == true so a freshly-constructed widget renders on its first frame (tests that want to observe transitions clear_dirty() first, then trigger the change).

inline bool is_widget() const noexcept override

AgtWidget

is the type-system anchor for “renderable

AgtObject”. Used by walks that need to safely

static_cast<AgtWidget *> an AgtObject (see agt-window.cpp:hit_test_recursive / render_subtree / absolute_origin_of, and AgtWidget::mark_dirty).

AgtEvent

class AgtEvent

Public Functions

AgtEvent() = default

Default-constructed event has AGT_SEL_NONE

as its selector — distinguishable from “real event with a

non-zero selector” via

ev.selector() != AGT_SEL_NONE.

inline explicit AgtEvent(uint16_t selector) noexcept
inline uint16_t selector() const noexcept

Selector (event class — AGT_SEL_*).

inline void set_selector(uint16_t s) noexcept
inline uint16_t message_id() const noexcept

Command id (for AGT_SEL_COMMAND). Zero otherwise.

inline void set_message_id(uint16_t id) noexcept
inline AgtObject *sender() const noexcept

Originating widget — the button that emitted the command, the source of a propagated event, etc. NULL for raw input events not yet re-emitted by a widget. Stored as a raw pointer: handlers MUST NOT keep sender() past the handler’s return — once dispatch unwinds, the sender’s lifetime is no longer guaranteed.

inline void set_sender(AgtObject *s) noexcept
inline int x() const noexcept

Pointer / touch position. For events delivered through the widget tree, already translated to widget-local coordinates by the dispatcher. Raw events from axl-input are in screen coordinates.

inline int y() const noexcept
inline void set_position(int x, int y) noexcept
inline int wheel_dx() const noexcept

Wheel deltas (notch ticks; positive = up/right). Zero for non-wheel events.

inline int wheel_dy() const noexcept
inline void set_wheel(int dx, int dy) noexcept
inline uint32_t buttons() const noexcept

Mouse-button bitfield (AGT_BUTTON_*).

inline void set_buttons(uint32_t b) noexcept
inline uint32_t click_count() const noexcept

Click count for button events (axl-input’s gesture recognizer): 1 = single, 2 = double, 3 = triple within the multi-click window; 0 for non-button events. Drives double / triple-click selection.

inline void set_click_count(uint32_t c) noexcept
inline uint32_t modifiers() const noexcept

Modifier bitfield (AGT_MOD_*).

inline void set_modifiers(uint32_t m) noexcept
inline uint32_t keycode() const noexcept

Raw scancode (key events).

inline uint32_t unicode() const noexcept

Translated unicode codepoint (0 if none / non-printable).

inline void set_key(uint32_t scancode, uint32_t codepoint) noexcept
inline bool consumed() const noexcept

Has a handler taken responsibility for this event? When true, the dispatcher stops propagation up the parent chain.

inline void set_consumed(bool c = true) noexcept

AgtMetaClass + AgtMessageMap

struct AgtMetaClass

Per-class metadata. Static instance per AGT class; linked up the inheritance chain by base_class.

Public Members

const char *class_name

C-string class name (debug aid)

const AgtMetaClass *base_class

parent class’s metaclass, or NULL for AgtObject

const AgtMessageMap *entries

pointer to map_entries_[] (sentinel-terminated)

uint32_t entry_count

number of non-sentinel entries

struct AgtMessageMap

One entry in a class’s message map. id_low == id_high for single-id entries; ranges are useful when a dialog dispatches a contiguous block of button ids to one handler (FOX’s FXMAPFUNCS macro). Sentinel-end is selector == 0.

Layout: 2 + 2 + 2 + 8 = 14 bytes on x64 (padded to 16 with alignment). Storage cost is ~16 B per AGT_MAP_* entry — a dialog with 20 commands costs 320 bytes of static data.

Public Members

uint16_t selector

event class (AGT_SEL_*)

uint16_t id_low

inclusive id range start

uint16_t id_high

inclusive id range end (often == id_low)

AgtMessageFunc handler

trampoline that dispatches to a member fn

AgtStyle

Centralized default palette + metrics every widget snapshots at construction; plus the JSON5 theme-file loader that overlays tokens onto a style (see the Styling policy section of the design doc).

Warning

doxygenstruct: Cannot find class “AgtStyle” in doxygen xml output for project “agt” from directory: /home/runner/work/agt/agt/docs/sphinx/../../out/docs/doxygen-xml

Warning

doxygenfunction: Cannot find function “agt_style_parse” in doxygen xml output for project “agt” from directory: /home/runner/work/agt/agt/docs/sphinx/../../out/docs/doxygen-xml

Warning

doxygenfunction: Cannot find function “agt_style_load” in doxygen xml output for project “agt” from directory: /home/runner/work/agt/agt/docs/sphinx/../../out/docs/doxygen-xml

The Qt/GTK-shape coordinate model is backed by axl-sdk’s AxlTransform (from <axl/axl-math.h>), not an AGT type — AgtDrawContext carries one and maps widgets’ local coordinates to device pixels through it (see the Coordinate model section of the design doc).