// dvxWidget.h — Widget system for DVX GUI // // A retained-mode widget toolkit layered on top of the DVX window manager. // Widgets form a tree (parent-child via firstChild/lastChild/nextSibling // pointers) rooted at a per-window VBox container. Layout is automatic: // the engine measures minimum sizes bottom-up, then allocates space top-down // using a flexbox-like algorithm with weights for extra-space distribution. // // Design decisions and rationale: // // - Single WidgetT struct with a tagged union: avoids the overhead of a // class hierarchy and virtual dispatch (no C++ vtable indirection). // The wclass pointer provides vtable-style dispatch only for the few // operations that genuinely differ per widget type (paint, layout, events). // Type-specific data lives in the `as` union to keep the struct compact. // // - Tagged size values (wgtPixels/wgtChars/wgtPercent): encoding the unit // in the high bits of a single int32_t avoids extra struct fields for // unit type and lets size hints be passed as plain integers. The 30-bit // value range (up to ~1 billion) is more than sufficient for pixel counts. // // - Tree linkage uses firstChild/lastChild/nextSibling (no prevSibling): // this halves the pointer overhead per widget and insertion/removal is // still O(n) in the worst case, which is acceptable given typical tree // depths of 5-10 nodes. // // - Large widget data (AnsiTermDataT, ListViewDataT) is separately // allocated and stored as a pointer in the union rather than inlined, // because these structures are hundreds of bytes and would bloat every // WidgetT even for simple labels and buttons. #ifndef DVX_WIDGET_H #define DVX_WIDGET_H #include "dvxTypes.h" #include // Forward declarations struct AppContextT; struct WidgetClassT; // ============================================================ // Size specifications // ============================================================ // // Tagged size values encode both a unit type and a numeric value in a // single int32_t. The top 2 bits select the unit (pixels, character widths, // or percentage of parent), and the low 30 bits hold the numeric value. // A raw 0 means "auto" (use the widget's natural/minimum size). // // This encoding avoids a separate enum field for the unit type, keeping // size hints as simple scalar assignments: w->minW = wgtChars(40); // The wgtResolveSize() function in the layout engine decodes these tagged // values back into pixel counts using the font metrics and parent dimensions. #define WGT_SIZE_TYPE_MASK 0xC0000000 #define WGT_SIZE_VAL_MASK 0x3FFFFFFF #define WGT_SIZE_PIXELS 0x00000000 #define WGT_SIZE_CHARS 0x40000000 #define WGT_SIZE_PERCENT 0x80000000 #define wgtPixels(v) ((int32_t)(WGT_SIZE_PIXELS | ((uint32_t)(v) & WGT_SIZE_VAL_MASK))) #define wgtChars(v) ((int32_t)(WGT_SIZE_CHARS | ((uint32_t)(v) & WGT_SIZE_VAL_MASK))) #define wgtPercent(v) ((int32_t)(WGT_SIZE_PERCENT | ((uint32_t)(v) & WGT_SIZE_VAL_MASK))) // ============================================================ // Widget type enum // ============================================================ // // Used as the index into widgetClassTable[] (in widgetInternal.h) to // look up the vtable for each widget type. Adding a new widget type // requires adding an enum value here, a corresponding union member in // WidgetT, and a WidgetClassT entry in widgetClassTable[]. typedef enum { WidgetVBoxE, WidgetHBoxE, WidgetLabelE, WidgetButtonE, WidgetCheckboxE, WidgetRadioGroupE, WidgetRadioE, WidgetTextInputE, WidgetTextAreaE, WidgetListBoxE, WidgetSpacerE, WidgetSeparatorE, WidgetFrameE, WidgetDropdownE, WidgetComboBoxE, WidgetProgressBarE, WidgetSliderE, WidgetTabControlE, WidgetTabPageE, WidgetStatusBarE, WidgetToolbarE, WidgetTreeViewE, WidgetTreeItemE, WidgetImageE, WidgetImageButtonE, WidgetCanvasE, WidgetAnsiTermE, WidgetListViewE, WidgetSpinnerE, WidgetScrollPaneE, WidgetSplitterE, WidgetTimerE } WidgetTypeE; // ============================================================ // ListView types // ============================================================ #define LISTVIEW_MAX_COLS 16 typedef enum { ListViewAlignLeftE, ListViewAlignCenterE, ListViewAlignRightE } ListViewAlignE; typedef enum { ListViewSortNoneE, ListViewSortAscE, ListViewSortDescE } ListViewSortE; typedef struct { const char *title; int32_t width; // tagged size (wgtPixels/wgtChars/wgtPercent, 0 = auto) ListViewAlignE align; } ListViewColT; // ============================================================ // Alignment enum // ============================================================ // // Controls main-axis alignment of children within a container. // HBox: AlignStartE=left, AlignCenterE=center, AlignEndE=right // VBox: AlignStartE=top, AlignCenterE=center, AlignEndE=bottom typedef enum { AlignStartE, AlignCenterE, AlignEndE } WidgetAlignE; // ============================================================ // Frame style enum // ============================================================ typedef enum { FrameInE, // beveled inward (sunken) — default FrameOutE, // beveled outward (raised) FrameFlatE // solid color line } FrameStyleE; // ============================================================ // Text input mode enum // ============================================================ typedef enum { InputNormalE, // default free-form text InputPasswordE, // displays bullets, no copy InputMaskedE // format mask (e.g. "(###) ###-####") } InputModeE; // ============================================================ // Large widget data (separately allocated to reduce WidgetT size) // ============================================================ // // AnsiTermDataT and ListViewDataT are heap-allocated and stored as // pointers in the WidgetT union. Inlining them would add hundreds of // bytes to every WidgetT instance (even labels and buttons), wasting // memory on the 30+ simple widgets that don't need terminal or listview // state. The pointer indirection adds one dereference but saves // significant memory across a typical widget tree. // AnsiTermDataT — full VT100/ANSI terminal emulator state. // Implements a subset of DEC VT100 escape sequences sufficient for BBS // and DOS ANSI art rendering: cursor movement, color attributes (16-color // with bold-as-bright), scrolling regions, and blink. The parser is a // simple state machine (normal -> ESC -> CSI) with parameter accumulation. typedef struct { uint8_t *cells; // character cells: (ch, attr) pairs, cols*rows*2 bytes int32_t cols; // columns (default 80) int32_t rows; // rows (default 25) int32_t cursorRow; // 0-based cursor row int32_t cursorCol; // 0-based cursor column bool cursorVisible; bool wrapMode; // auto-wrap at right margin bool bold; // SGR bold flag (brightens foreground) bool originMode; // cursor positioning relative to scroll region bool csiPrivate; // '?' prefix in CSI sequence uint8_t curAttr; // current text attribute (fg | bg<<4) uint8_t parseState; // 0=normal, 1=ESC, 2=CSI int32_t params[8]; // CSI parameter accumulator int32_t paramCount; // number of CSI params collected int32_t savedRow; // saved cursor position (SCP) int32_t savedCol; // Scrolling region (0-based, inclusive) int32_t scrollTop; // top row of scroll region int32_t scrollBot; // bottom row of scroll region // Scrollback — circular buffer so old lines age out naturally without // memmove. Each line is cols*2 bytes (same ch/attr format as cells). // scrollPos tracks the view offset: when equal to scrollbackCount, // the user sees the live screen; when less, they're viewing history. uint8_t *scrollback; // circular buffer of scrollback lines int32_t scrollbackMax; // max lines in scrollback buffer int32_t scrollbackCount; // current number of lines stored int32_t scrollbackHead; // write position (circular index) int32_t scrollPos; // view position (scrollbackCount = live) // Blink support bool blinkVisible; // current blink phase (true = text visible) clock_t blinkTime; // timestamp of last blink toggle // Cursor blink bool cursorOn; // current cursor blink phase clock_t cursorTime; // timestamp of last cursor toggle // Dirty tracking — a 32-bit bitmask where each bit corresponds to one // terminal row. Only dirty rows are repainted, which is critical because // the ANSI terminal can receive data every frame (at 9600+ baud) and // re-rendering all 25 rows of 80 columns each frame would dominate the // CPU budget. Limits terminal height to 32 rows, which is fine for the // 25-row default target. uint32_t dirtyRows; // bitmask of rows needing repaint int32_t lastCursorRow; // cursor row at last repaint int32_t lastCursorCol; // cursor col at last repaint // Pre-packed 16-color palette avoids calling packColor() (which involves // shift/mask arithmetic) 80*25 = 2000 times per full repaint. uint32_t packedPalette[16]; bool paletteValid; // Selection (line indices in scrollback+screen space) int32_t selStartLine; int32_t selStartCol; int32_t selEndLine; int32_t selEndCol; bool selecting; // Communications interface — abstracted so the terminal can connect to // different backends (serial port, secLink channel, local pipe) without // knowing the transport details. When all are NULL, the terminal is in // offline/disconnected mode (useful for viewing .ANS files). void *commCtx; int32_t (*commRead)(void *ctx, uint8_t *buf, int32_t maxLen); int32_t (*commWrite)(void *ctx, const uint8_t *buf, int32_t len); } AnsiTermDataT; // ListViewDataT — multi-column list with sortable headers and optional // multi-select. cellData is a flat array of strings indexed as // cellData[row * colCount + col]. The sortIndex is an indirection array // that maps displayed row numbers to data row numbers, allowing sort // without rearranging the actual data. resolvedColW[] caches pixel widths // after resolving tagged column sizes, avoiding re-resolution on every paint. typedef struct { const ListViewColT *cols; int32_t colCount; const char **cellData; int32_t rowCount; int32_t selectedIdx; int32_t scrollPos; int32_t scrollPosH; int32_t sortCol; ListViewSortE sortDir; int32_t resolvedColW[LISTVIEW_MAX_COLS]; int32_t totalColW; int32_t *sortIndex; bool multiSelect; int32_t anchorIdx; uint8_t *selBits; bool reorderable; int32_t dragIdx; int32_t dropIdx; void (*onHeaderClick)(struct WidgetT *w, int32_t col, ListViewSortE dir); } ListViewDataT; // ============================================================ // Widget structure // ============================================================ #define MAX_WIDGET_NAME 32 typedef struct WidgetT { WidgetTypeE type; // wclass points to the vtable for this widget type. Looked up once at // creation from widgetClassTable[type]. This avoids a switch on type // in every paint/layout/event dispatch — the cost is one pointer per // widget, which is negligible. const struct WidgetClassT *wclass; char name[MAX_WIDGET_NAME]; // djb2 hash of the name string, computed at wgtSetName() time. // wgtFind() compares hashes before strcmp, making name lookups fast // for the common no-match case in a tree with many unnamed widgets. uint32_t nameHash; // djb2 hash of name, 0 if unnamed // Tree linkage struct WidgetT *parent; struct WidgetT *firstChild; struct WidgetT *lastChild; struct WidgetT *nextSibling; WindowT *window; // Computed geometry (relative to window content area) int32_t x; int32_t y; int32_t w; int32_t h; // Computed minimum size — set bottom-up by calcMinSize during layout. // These represent the smallest possible size for this widget (including // its children if it's a container). The layout engine uses these as // the starting point for space allocation. int32_t calcMinW; int32_t calcMinH; // Size hints (tagged: wgtPixels/wgtChars/wgtPercent, 0 = auto). // These are set by the application and influence the layout engine: // minW/minH override calcMinW/H if larger, maxW/maxH clamp the final // size, and prefW/prefH request a specific size (layout may override). int32_t minW; int32_t minH; int32_t maxW; // 0 = no limit int32_t maxH; int32_t prefW; // preferred size, 0 = auto int32_t prefH; // weight controls how extra space beyond minimum is distributed among // siblings in a VBox/HBox. weight=0 means fixed size (no stretching), // weight=100 is the default for flexible widgets. A widget with // weight=200 gets twice as much extra space as one with weight=100. int32_t weight; // extra-space distribution (0 = fixed, 100 = normal) // Container properties WidgetAlignE align; // main-axis alignment for children int32_t spacing; // tagged size for spacing between children (0 = default) int32_t padding; // tagged size for internal padding (0 = default) // Colors (0 = use color scheme defaults) uint32_t fgColor; uint32_t bgColor; // State bool visible; bool enabled; bool readOnly; bool focused; char accelKey; // lowercase accelerator character, 0 if none // User data and callbacks. These fire for ALL widget types from the // central event dispatcher, not from individual widget handlers. // Type-specific handlers (e.g. button press animation, listbox // selection) run first, then these universal callbacks fire. void *userData; const char *tooltip; // tooltip text (NULL = none, caller owns string) MenuT *contextMenu; // right-click context menu (NULL = none, caller owns) void (*onClick)(struct WidgetT *w); void (*onDblClick)(struct WidgetT *w); void (*onChange)(struct WidgetT *w); void (*onFocus)(struct WidgetT *w); void (*onBlur)(struct WidgetT *w); // Type-specific data — tagged union keyed by the `type` field. // Only the member corresponding to `type` is valid. This is the C // equivalent of a discriminated union / variant type. Using a union // instead of separate structs per widget type keeps all widget data // in a single allocation, which simplifies memory management and // avoids pointer chasing during layout/paint traversal. union { struct { const char *text; } label; struct { const char *text; bool pressed; } button; struct { const char *text; bool checked; } checkbox; struct { int32_t selectedIdx; } radioGroup; struct { const char *text; int32_t index; } radio; // Text input has its own edit buffer (not a pointer to external // storage) so the widget fully owns its text lifecycle. The undo // buffer holds a single-level snapshot taken before each edit // operation — Ctrl+Z restores to the snapshot. This is simpler // than a full undo stack but sufficient for single-line fields. struct { char *buf; int32_t bufSize; int32_t len; int32_t cursorPos; int32_t scrollOff; int32_t selStart; // selection anchor (-1 = none) int32_t selEnd; // selection end (-1 = none) char *undoBuf; int32_t undoLen; int32_t undoCursor; InputModeE inputMode; const char *mask; // format mask for InputMaskedE } textInput; // Multi-line text editor. desiredCol implements the "sticky column" // behavior where pressing Up/Down tries to return to the column // the cursor was at before traversing shorter lines (standard // text editor UX). cachedLines/cachedMaxLL cache values that // require full-buffer scans, invalidated to -1 on any text change. struct { char *buf; int32_t bufSize; int32_t len; int32_t cursorRow; int32_t cursorCol; int32_t scrollRow; int32_t scrollCol; int32_t desiredCol; // sticky column for up/down movement int32_t selAnchor; // selection anchor byte offset (-1 = none) int32_t selCursor; // selection cursor byte offset (-1 = none) char *undoBuf; int32_t undoLen; int32_t undoCursor; // byte offset at time of snapshot int32_t cachedLines; // cached line count (-1 = dirty) int32_t cachedMaxLL; // cached max line length (-1 = dirty) } textArea; struct { const char **items; int32_t itemCount; int32_t selectedIdx; // cursor position (always valid); also sole selection in single-select int32_t scrollPos; int32_t maxItemLen; // cached max strlen of items bool multiSelect; int32_t anchorIdx; // anchor for shift+click range selection uint8_t *selBits; // per-item selection flags (multi-select only) bool reorderable; // allow drag-reorder of items int32_t dragIdx; // item being dragged (-1 = none) int32_t dropIdx; // insertion point (-1 = none) } listBox; struct { bool vertical; } separator; struct { const char *title; FrameStyleE style; // FrameInE (default), FrameOutE, FrameFlatE uint32_t color; // border color for FrameFlatE (0 = use windowShadow) } frame; struct { const char **items; int32_t itemCount; int32_t selectedIdx; bool open; int32_t hoverIdx; int32_t scrollPos; int32_t maxItemLen; // cached max strlen of items } dropdown; struct { char *buf; int32_t bufSize; int32_t len; int32_t cursorPos; int32_t scrollOff; int32_t selStart; // selection anchor (-1 = none) int32_t selEnd; // selection end (-1 = none) char *undoBuf; int32_t undoLen; int32_t undoCursor; const char **items; int32_t itemCount; int32_t selectedIdx; bool open; int32_t hoverIdx; int32_t listScrollPos; int32_t maxItemLen; // cached max strlen of items } comboBox; struct { int32_t value; int32_t maxValue; bool vertical; } progressBar; struct { int32_t value; int32_t minValue; int32_t maxValue; bool vertical; } slider; struct { int32_t activeTab; int32_t scrollOffset; // horizontal scroll of tab headers } tabControl; struct { const char *title; } tabPage; // TreeView uses widget children (WidgetTreeItemE) as its items, // unlike ListBox which uses a string array. This allows nested // hierarchies with expand/collapse state per item. The tree is // rendered by flattening visible items during paint, with TREE_INDENT // pixels of indentation per nesting level. struct { int32_t scrollPos; int32_t scrollPosH; struct WidgetT *selectedItem; struct WidgetT *anchorItem; // anchor for shift+click range selection bool multiSelect; bool reorderable; // allow drag-reorder of items struct WidgetT *dragItem; // item being dragged (NULL = none) struct WidgetT *dropTarget; // insertion target (NULL = none) bool dropAfter; // true = insert after target, false = before } treeView; struct { const char *text; bool expanded; bool selected; // per-item flag for multi-select } treeItem; struct { uint8_t *data; // pixel buffer in display format int32_t imgW; int32_t imgH; int32_t imgPitch; bool pressed; } image; struct { uint8_t *data; // pixel buffer in display format int32_t imgW; int32_t imgH; int32_t imgPitch; bool pressed; } imageButton; struct { uint8_t *data; // pixel buffer in display format int32_t canvasW; int32_t canvasH; int32_t canvasPitch; int32_t canvasBpp; // cached bytes per pixel (avoids pitch/w division) uint32_t penColor; int32_t penSize; int32_t lastX; int32_t lastY; void (*onMouse)(struct WidgetT *w, int32_t cx, int32_t cy, bool drag); } canvas; AnsiTermDataT *ansiTerm; ListViewDataT *listView; struct { int32_t value; int32_t minValue; int32_t maxValue; int32_t step; char buf[16]; // formatted value text int32_t len; int32_t cursorPos; int32_t scrollOff; int32_t selStart; // selection anchor (-1 = none) int32_t selEnd; // selection end (-1 = none) char undoBuf[16]; // undo snapshot int32_t undoLen; int32_t undoCursor; bool editing; // true when user is typing } spinner; struct { int32_t scrollPosV; int32_t scrollPosH; } scrollPane; struct { int32_t dividerPos; // pixels from left/top edge bool vertical; // true = vertical divider (left|right panes) } splitter; struct { int32_t intervalMs; // timer period in milliseconds bool repeat; // true = repeating, false = one-shot bool running; // true = active clock_t lastFire; // clock() value at last fire (or start) } timer; } as; } WidgetT; // ============================================================ // Window integration // ============================================================ // Initialize the widget system for a window. Creates a root VBox container // that fills the window's content area, and installs callback handlers // (onPaint, onMouse, onKey, onResize) that dispatch events to the widget // tree. After this call, the window is fully managed by the widget system // — the application builds its UI by adding child widgets to the returned // root container. The window's userData is set to the AppContextT pointer. WidgetT *wgtInitWindow(struct AppContextT *ctx, WindowT *win); // ============================================================ // Container creation // ============================================================ // // VBox and HBox are the primary layout containers, analogous to CSS // flexbox with column/row direction. Children are laid out sequentially // along the main axis (vertical for VBox, horizontal for HBox) with // spacing between them. Extra space is distributed according to each // child's weight. Frame is a titled groupbox container with a bevel border. WidgetT *wgtVBox(WidgetT *parent); WidgetT *wgtHBox(WidgetT *parent); WidgetT *wgtFrame(WidgetT *parent, const char *title); // ============================================================ // Basic widgets // ============================================================ WidgetT *wgtLabel(WidgetT *parent, const char *text); WidgetT *wgtButton(WidgetT *parent, const char *text); WidgetT *wgtCheckbox(WidgetT *parent, const char *text); WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen); WidgetT *wgtPasswordInput(WidgetT *parent, int32_t maxLen); WidgetT *wgtMaskedInput(WidgetT *parent, const char *mask); // ============================================================ // Radio buttons // ============================================================ // // Radio buttons must be children of a RadioGroup container. The group // tracks which child is selected (by index). Clicking a radio button // automatically deselects the previously selected sibling. This parent- // tracking approach avoids explicit "radio group ID" parameters. WidgetT *wgtRadioGroup(WidgetT *parent); WidgetT *wgtRadio(WidgetT *parent, const char *text); // ============================================================ // Spacing and visual dividers // ============================================================ // // Spacer is an invisible flexible widget (weight=100) that absorbs // extra space — useful for pushing subsequent siblings to the end of // a container (like CSS flex: 1 auto). Separators are thin beveled // lines for visual grouping. WidgetT *wgtSpacer(WidgetT *parent); WidgetT *wgtHSeparator(WidgetT *parent); WidgetT *wgtVSeparator(WidgetT *parent); // ============================================================ // Complex widgets // ============================================================ WidgetT *wgtListBox(WidgetT *parent); WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen); // ============================================================ // Dropdown and ComboBox // ============================================================ WidgetT *wgtDropdown(WidgetT *parent); void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count); int32_t wgtDropdownGetSelected(const WidgetT *w); void wgtDropdownSetSelected(WidgetT *w, int32_t idx); WidgetT *wgtComboBox(WidgetT *parent, int32_t maxLen); void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count); int32_t wgtComboBoxGetSelected(const WidgetT *w); void wgtComboBoxSetSelected(WidgetT *w, int32_t idx); // ============================================================ // ProgressBar // ============================================================ WidgetT *wgtProgressBar(WidgetT *parent); WidgetT *wgtProgressBarV(WidgetT *parent); void wgtProgressBarSetValue(WidgetT *w, int32_t value); int32_t wgtProgressBarGetValue(const WidgetT *w); // ============================================================ // Slider (TrackBar) // ============================================================ WidgetT *wgtSlider(WidgetT *parent, int32_t minVal, int32_t maxVal); void wgtSliderSetValue(WidgetT *w, int32_t value); int32_t wgtSliderGetValue(const WidgetT *w); // ============================================================ // Spinner (numeric input) // ============================================================ WidgetT *wgtSpinner(WidgetT *parent, int32_t minVal, int32_t maxVal, int32_t step); void wgtSpinnerSetValue(WidgetT *w, int32_t value); int32_t wgtSpinnerGetValue(const WidgetT *w); void wgtSpinnerSetRange(WidgetT *w, int32_t minVal, int32_t maxVal); void wgtSpinnerSetStep(WidgetT *w, int32_t step); // ============================================================ // TabControl // ============================================================ WidgetT *wgtTabControl(WidgetT *parent); WidgetT *wgtTabPage(WidgetT *parent, const char *title); void wgtTabControlSetActive(WidgetT *w, int32_t idx); int32_t wgtTabControlGetActive(const WidgetT *w); // ============================================================ // StatusBar and Toolbar // ============================================================ WidgetT *wgtStatusBar(WidgetT *parent); WidgetT *wgtToolbar(WidgetT *parent); // ============================================================ // TreeView // ============================================================ WidgetT *wgtTreeView(WidgetT *parent); WidgetT *wgtTreeViewGetSelected(const WidgetT *w); void wgtTreeViewSetSelected(WidgetT *w, WidgetT *item); void wgtTreeViewSetMultiSelect(WidgetT *w, bool multi); void wgtTreeViewSetReorderable(WidgetT *w, bool reorderable); WidgetT *wgtTreeItem(WidgetT *parent, const char *text); void wgtTreeItemSetExpanded(WidgetT *w, bool expanded); bool wgtTreeItemIsExpanded(const WidgetT *w); bool wgtTreeItemIsSelected(const WidgetT *w); void wgtTreeItemSetSelected(WidgetT *w, bool selected); // ============================================================ // ListView (multi-column list) // ============================================================ WidgetT *wgtListView(WidgetT *parent); void wgtListViewSetColumns(WidgetT *w, const ListViewColT *cols, int32_t count); void wgtListViewSetData(WidgetT *w, const char **cellData, int32_t rowCount); int32_t wgtListViewGetSelected(const WidgetT *w); void wgtListViewSetSelected(WidgetT *w, int32_t idx); void wgtListViewSetSort(WidgetT *w, int32_t col, ListViewSortE dir); void wgtListViewSetHeaderClickCallback(WidgetT *w, void (*cb)(WidgetT *w, int32_t col, ListViewSortE dir)); void wgtListViewSetMultiSelect(WidgetT *w, bool multi); bool wgtListViewIsItemSelected(const WidgetT *w, int32_t idx); void wgtListViewSetItemSelected(WidgetT *w, int32_t idx, bool selected); void wgtListViewSelectAll(WidgetT *w); void wgtListViewClearSelection(WidgetT *w); void wgtListViewSetReorderable(WidgetT *w, bool reorderable); // ============================================================ // ScrollPane // ============================================================ WidgetT *wgtScrollPane(WidgetT *parent); // ============================================================ // Splitter (draggable divider between two child regions) // ============================================================ // Create a splitter. If vertical is true, children are arranged left|right; // if false, top|bottom. Add exactly two children. WidgetT *wgtSplitter(WidgetT *parent, bool vertical); void wgtSplitterSetPos(WidgetT *w, int32_t pos); int32_t wgtSplitterGetPos(const WidgetT *w); // ============================================================ // Timer (invisible, callback-driven) // ============================================================ // Create a timer widget. The timer is invisible and takes no space in // layout. When running, it calls onChange at the specified interval. // intervalMs: period in milliseconds. repeat: true for repeating, // false for one-shot (auto-stops after first fire). WidgetT *wgtTimer(WidgetT *parent, int32_t intervalMs, bool repeat); // Start/restart the timer. Resets the elapsed time. void wgtTimerStart(WidgetT *w); // Stop the timer. void wgtTimerStop(WidgetT *w); // Change the interval (takes effect on next fire). void wgtTimerSetInterval(WidgetT *w, int32_t intervalMs); // Check if the timer is currently running. bool wgtTimerIsRunning(const WidgetT *w); // Called once per frame by dvxUpdate. Checks all active timers. void wgtUpdateTimers(void); // ============================================================ // ImageButton // ============================================================ // Create an image button from raw pixel data (display format). // Takes ownership of the data buffer (freed on destroy). WidgetT *wgtImageButton(WidgetT *parent, uint8_t *data, int32_t w, int32_t h, int32_t pitch); // Load an image button from a file (BMP, PNG, JPEG, GIF). // Returns NULL on load failure; falls through gracefully. WidgetT *wgtImageButtonFromFile(WidgetT *parent, const char *path); // Replace the image data. Takes ownership of the new buffer. void wgtImageButtonSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch); // ============================================================ // Image // ============================================================ // Create an image widget from raw pixel data (display format). // Takes ownership of the data buffer (freed on destroy). WidgetT *wgtImage(WidgetT *parent, uint8_t *data, int32_t w, int32_t h, int32_t pitch); // Load an image widget from a file (BMP, PNG, JPEG, GIF). // Returns NULL on load failure. WidgetT *wgtImageFromFile(WidgetT *parent, const char *path); // Replace the image data. Takes ownership of the new buffer. void wgtImageSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch); // ============================================================ // Canvas // ============================================================ // Create a drawable canvas widget with the given dimensions. WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h); // Clear the canvas to the specified color. void wgtCanvasClear(WidgetT *w, uint32_t color); // Set the pen color (in display pixel format). void wgtCanvasSetPenColor(WidgetT *w, uint32_t color); // Set the pen size in pixels (diameter). void wgtCanvasSetPenSize(WidgetT *w, int32_t size); // Set the mouse callback. Called on click (drag=false) and drag (drag=true) // with canvas-relative coordinates. If NULL (default), mouse events are ignored. void wgtCanvasSetMouseCallback(WidgetT *w, void (*cb)(WidgetT *w, int32_t cx, int32_t cy, bool drag)); // Save the canvas to a PNG file. Returns 0 on success, -1 on failure. int32_t wgtCanvasSave(WidgetT *w, const char *path); // Load a PNG file onto the canvas. Returns 0 on success, -1 on failure. int32_t wgtCanvasLoad(WidgetT *w, const char *path); // Programmatic drawing (coordinates are in canvas space) void wgtCanvasDrawLine(WidgetT *w, int32_t x0, int32_t y0, int32_t x1, int32_t y1); void wgtCanvasDrawRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height); void wgtCanvasFillRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height); void wgtCanvasFillCircle(WidgetT *w, int32_t cx, int32_t cy, int32_t radius); void wgtCanvasSetPixel(WidgetT *w, int32_t x, int32_t y, uint32_t color); uint32_t wgtCanvasGetPixel(const WidgetT *w, int32_t x, int32_t y); // ============================================================ // ANSI Terminal // ============================================================ // // A VT100/ANSI terminal emulator widget. Supports the subset of escape // sequences needed for DOS BBS programs and ANSI art: cursor positioning, // SGR color/attribute codes, scrolling regions, and text blink. Pairs // with the secLink communications layer for remote serial connections // or can be used standalone for viewing .ANS files. // Create an ANSI terminal widget (0 for cols/rows = 80x25 default) WidgetT *wgtAnsiTerm(WidgetT *parent, int32_t cols, int32_t rows); // Write raw data through the ANSI escape sequence parser. Used for // loading .ANS files or feeding data from a source that isn't using // the comm interface. void wgtAnsiTermWrite(WidgetT *w, const uint8_t *data, int32_t len); // Clear the terminal screen and reset cursor to home void wgtAnsiTermClear(WidgetT *w); // Set the communications interface (NULL function pointers = disconnected) void wgtAnsiTermSetComm(WidgetT *w, void *ctx, int32_t (*readFn)(void *, uint8_t *, int32_t), int32_t (*writeFn)(void *, const uint8_t *, int32_t)); // Set the scrollback buffer size in lines (default 500). Clears existing scrollback. void wgtAnsiTermSetScrollback(WidgetT *w, int32_t maxLines); // Poll the comm interface for incoming data and process it. Returns bytes processed. int32_t wgtAnsiTermPoll(WidgetT *w); // Fast-path repaint for the terminal widget. Instead of going through the // full widget paint pipeline (which would repaint the entire widget), this // renders only the dirty rows (tracked via the dirtyRows bitmask) directly // into the window's content buffer. This is essential for responsive terminal // output — incoming serial data can dirty a few rows per frame, and // repainting only those rows keeps the cost proportional to the actual // change rather than the full 80x25 grid. Returns the number of rows // repainted; outY/outH report the affected region for dirty-rect tracking. int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH); // ============================================================ // Operations // ============================================================ // Walk from any widget up the tree to the root, then retrieve the // AppContextT stored in the window's userData. This lets any widget // access the full application context without passing it through every // function call. struct AppContextT *wgtGetContext(const WidgetT *w); // Update text cursor blink state. Call once per frame from dvxUpdate. // Toggles the cursor visibility at 250ms intervals, matching the ANSI // terminal cursor rate. void wgtUpdateCursorBlink(void); // Mark a widget as needing both re-layout (measure + position) and // repaint. Propagates upward to ancestors since a child's size change // can affect parent layout. Use this after structural changes (adding/ // removing children, changing text that affects size). void wgtInvalidate(WidgetT *w); // Mark a widget as needing repaint only, without re-layout. Use this // for visual-only changes that don't affect geometry (e.g. checkbox // toggle, selection highlight change, cursor blink). void wgtInvalidatePaint(WidgetT *w); // Set/get widget text (label, button, textInput, etc.) void wgtSetText(WidgetT *w, const char *text); const char *wgtGetText(const WidgetT *w); // Enable/disable a widget void wgtSetEnabled(WidgetT *w, bool enabled); // Set read-only mode (allows scrolling/selection but blocks editing) void wgtSetReadOnly(WidgetT *w, bool readOnly); // Set/get keyboard focus void wgtSetFocused(WidgetT *w); WidgetT *wgtGetFocused(void); // Show/hide a widget void wgtSetVisible(WidgetT *w, bool visible); // Set a widget's name (for lookup via wgtFind) void wgtSetName(WidgetT *w, const char *name); // Find a widget by name (searches the subtree rooted at root) WidgetT *wgtFind(WidgetT *root, const char *name); // Destroy a widget and all its children (removes from parent) void wgtDestroy(WidgetT *w); // ============================================================ // List box operations // ============================================================ void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count); int32_t wgtListBoxGetSelected(const WidgetT *w); void wgtListBoxSetSelected(WidgetT *w, int32_t idx); void wgtListBoxSetMultiSelect(WidgetT *w, bool multi); bool wgtListBoxIsItemSelected(const WidgetT *w, int32_t idx); void wgtListBoxSetItemSelected(WidgetT *w, int32_t idx, bool selected); void wgtListBoxSelectAll(WidgetT *w); void wgtListBoxClearSelection(WidgetT *w); void wgtListBoxSetReorderable(WidgetT *w, bool reorderable); // ============================================================ // Tooltip // ============================================================ // Set tooltip text for a widget (NULL to remove). // Caller owns the string — it must outlive the widget. void wgtSetTooltip(WidgetT *w, const char *text); // ============================================================ // Debug // ============================================================ // Draw borders around layout containers in ugly colors void wgtSetDebugLayout(struct AppContextT *ctx, bool enabled); // ============================================================ // Layout (called internally; available for manual trigger) // ============================================================ // Decode a tagged size value (WGT_SIZE_PIXELS/CHARS/PERCENT) into a // concrete pixel count. For CHARS, multiplies by charWidth; for PERCENT, // computes the fraction of parentSize. Returns 0 for a raw 0 input // (meaning "auto"). int32_t wgtResolveSize(int32_t taggedSize, int32_t parentSize, int32_t charWidth); // Execute the full two-pass layout algorithm on the widget tree: // Pass 1 (bottom-up): calcMinSize on every widget to compute minimum sizes. // Pass 2 (top-down): allocate space within availW/availH, distributing // extra space according to weights and respecting min/max constraints. // Normally called automatically by the paint handler; exposed here for // cases where layout must be forced before the next paint (e.g. dvxFitWindow). void wgtLayout(WidgetT *root, int32_t availW, int32_t availH, const BitmapFontT *font); // Paint the entire widget tree by depth-first traversal. Each widget's // clip rect is set to its bounds before calling its paint function. // Overlays (dropdown popups, tooltips) are painted in a second pass // after the main tree so they render on top of everything. void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors); #endif // DVX_WIDGET_H