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>—AgtObjectparent/child tree root<agt/agt-widget.hpp>—AgtWidgetvisible-node base (bounds, dirty flag, enabled state,draw/handle_eventvirtuals)<agt/agt-event.hpp>—AgtEventinput + dispatch payload<agt/agt-meta.hpp>—AgtMetaClass+AgtMessageMap+ theAGT_MAP_*macros for declaring per-class message tables<agt/agt-palette.hpp>—AgtPalette, the color/metric DATA (QtQPaletteanalog): tokens + nested per-widget sections + the JSON5 theme-file loader (agt_palette_load/agt_palette_parse).<agt/agt-style.hpp>—AgtStyle, the polymorphic RENDERER (QtQStyleanalog):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 —falseby default, overridden totruebyAgtWidget. Lets walks that need to safelystatic_cast<AgtWidget *>anAgtObject *(the render walk, hit-test,mark_dirtypropagation) gate on the virtual rather than RTTI.on_widget_destroyed(AgtWidget *)virtual hook — called byAgtWidget’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. theAgtButton(widgets module) that emittedAGT_SEL_COMMANDwhen clicked). NULL for raw input events that haven’t been re-emitted by a widget yet.message_id— the consumer-defined command id passed throughAGT_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 arraystatic const AgtMetaClass CLASS::metaclass— name, base pointer, entries pointer, countvirtual 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 singleAGT_SEL_COMMANDidAGT_MAP_COMMAND_RANGE(LO, HI, &Class::method)— bind a contiguous block of command ids to one handler (FOX’sFXMAPFUNCSanalog)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.
-
inline AgtObject *first_child() const noexcept
First child in document order (the child added earliest with
add_child). Iterate viachild->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.
-
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, orthis(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_trather thansize_tto 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;AgtWidgetoverrides to true. Used by the render walk, hit-test, andmark_dirtyparent walk to safely distinguish “renderable” objects from bareAgtObjects 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 onis_widget()before anystatic_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.AgtWindowoverrides this to clear anyhovered_/ 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 inspectwas a pointer value (do not deref — the widget is mid-destruction; onlyAgtObject’s subobject is intact).
-
inline virtual void on_tree_mutated() noexcept
Notification hook called on a parent (
this) right after a realadd_child/remove_childmutates 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).AgtWidgetoverrides it to flag its owningAgtWindow;AgtWindowflags 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_eventinstead of calling this directly; this is the dispatch engine thathandle_eventdelegates 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_COMMANDids should declare anenum { ID_X = Base::ID_LAST, ID_Y, ..., ID_LAST }block — the chain guarantees IDs never collide across the class hierarchy.AgtObject::ID_LASTis the chain root.**
ID_NONE == 0is reserved** (FOXFXWindow::ID_NONEshape): it is the “no command” sentinel — a widget’s default / not-yet- wiredtarget_idis 0 — and is intentionally never mapped, so emitting it is always a benign no-op. The chain therefore starts atID_LAST == 1; the first real command in any branch isBase::ID_LAST, never 0.static constexprrather than an anonymousenumbecause anonymous-enum-of-base-class-reference confuses doxygen/breathe; the visible behavior is identical (Class::ID_LASTis a constant expression usable in a derivedenum).Example:
class MyDialog : public AgtDialog { public: enum { ID_OK = AgtDialog::ID_LAST, ID_CANCEL, ID_LAST }; ... };
-
static constexpr uint16_t ID_LAST = 1
-
AgtObject() = default
AgtWidget
-
class AgtWidget : public AgtObject
Subclassed by AgtFrame, AgtSeparator, AgtSpacer, AgtSpinner, AgtWindow
Public Types
FOX-style message-ID chain (see
AgtObject::ID_LAST). The baseAgtWidgetships the standard widget-state commands (FOXFXWindowshape): target ANY widget with one of these to change its state with no custom handler — e.g. a button wired to&other, AgtWidget::ID_DISABLEgreysotherout. Derived classes chain their own ids fromID_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
-
enumerator ID_HIDE
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 (
AgtWindowis the canonical detached root).
-
~AgtWidget() noexcept override
Notify ancestors via
on_widget_destroyed, then chain toAgtObject::~AgtObject. The notification lets the owningAgtWindowclearhovered_/ 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::onPaintpasses anFXDCWindow &). The forward-declaredAgtDrawContextis 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-classAGT_MAP_*table gets walked; the consumed flag onevcontrols propagation. Subclasses override only when they want custom routing (e.g. anAgtCompositethat does its own hit-testing before dispatching to children).Returns
trueif the event was consumed (don’t propagate further),falseto let the event bubble to the parent. The return value mirrorsev.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, elseAgtStyle::current().
-
const AgtPalette &palette() const noexcept
The color/metric data this widget reads — its own
set_palette, else the nearest widget ancestor’s, elseAgtPalette::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) — justinvalidate_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()(QtchangeEvent(PaletteChange)shape). Default no-op; chrome widgets override to re-snapshot the tokens they cached at construction (skipping any the consumer overrode explicitly). Called byrestyle_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 mutatingAgtPalette::current()to re-color already-built widgets, thenredraw()the window.
-
virtual uint32_t state_flags() const noexcept
The widget’s current interaction state as
AgtWidgetStateflags — the renderer’s state seam (QtQStyleOption::state). The base reportsENABLED(when enabled) +FOCUSED(when it holds the window focus); interactive subclasses OR inHOVERED/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’snatural_*()size (FOXLAYOUT_FIX_*). A layout manager writes child geometry via the internalplace()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_measureof 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 thannatural_*(). 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
falselets a container size this widget to itsnatural_*().
-
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
AgtSpacerwith 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 onenabled()). Default true. Lives on AgtWidget rather than any interactive subclass so every future control inherits the sameset_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_subtreenor returned bywidget_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 INACTIVEAgtTabViewpage (the page isset_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.
-
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,AgtWindowpops a floatingAgtTooltipafter 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 FOXFXWindow::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). DefaultAGT_FOCUS_NONE: display-only widgets (Label, Frame, ProgressBar, Image) opt out. Interactive widgets (Button, CheckBox, RadioButton, EditField, Slider, ListBox, …) override toAGT_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 inAgtWindow::focused()— focus can be programmatically directed at any widget viaset_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
AgtWindowis 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 spacedraw()authors in).AgtWindow::render_subtreepushes the clip before recursing into the children and pops it after;hit_test_recursivehonors the same clip so a child masked out of view is also non-clickable (input matches what is painted).
-
AgtWindow *window() noexcept
The
AgtWindowthis widget belongs to, or NULL when the widget is detached (noAgtWindowancestor). Walks the parent chain and, at each ancestor, the metaclassbase_classchain looking forAgtWindow— so a consumer subclass ofAgtWindow(with its ownAGT_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 itswindow().)
-
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’smapToGlobal/ GTK’sgtk_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 soAgtWindowcan 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
AgtWindowclears its own dirty bit separately after presenting the back buffer. Default state isdirty == trueso a freshly-constructed widget renders on its first frame (tests that want to observe transitionsclear_dirty()first, then trigger the change).
-
inline bool is_widget() const noexcept override
AgtWidgetis the type-system anchor for “renderable
AgtObject”. Used by walks that need to safely
static_cast<AgtWidget *>anAgtObject(seeagt-window.cpp:hit_test_recursive/render_subtree/absolute_origin_of, andAgtWidget::mark_dirty).
AgtEvent
-
class AgtEvent
Public Functions
-
AgtEvent() = default
Default-constructed event has
AGT_SEL_NONEas 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 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
-
AgtEvent() = default
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
-
const char *class_name
-
struct AgtMessageMap
One entry in a class’s message map.
id_low == id_highfor single-id entries; ranges are useful when a dialog dispatches a contiguous block of button ids to one handler (FOX’sFXMAPFUNCSmacro). Sentinel-end isselector == 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.
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).