663 lines
30 KiB
C
663 lines
30 KiB
C
// dvxWgt.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:
|
|
//
|
|
// - Generic WidgetT with void *data: core knows nothing about individual
|
|
// widget types. Each widget DXE allocates and manages its own private
|
|
// data struct via w->data. The wclass vtable provides polymorphic
|
|
// dispatch for paint, layout, events, and all type-specific operations.
|
|
// No widget-specific enums, structs, or union members in this header.
|
|
//
|
|
// - Dynamic type IDs: wgtRegisterClass() returns a runtime ID at load
|
|
// time. No compile-time enum. Adding a new widget requires zero core
|
|
// changes -- just drop a .wgt DXE in the widgets directory.
|
|
//
|
|
// - 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.
|
|
#ifndef DVX_WGT_H
|
|
#define DVX_WGT_H
|
|
|
|
#include "dvxTypes.h"
|
|
|
|
#include <time.h>
|
|
|
|
// 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 IDs are assigned dynamically by wgtRegisterClass() at
|
|
// runtime. There is no compile-time enum. Each widget DXE stores
|
|
// its assigned ID(s) in file-scope globals.
|
|
|
|
// ============================================================
|
|
// ListView column types (used by ListView API in widgetListView.h)
|
|
// ============================================================
|
|
|
|
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;
|
|
|
|
// ============================================================
|
|
// Widget-specific enums and types used by the public API
|
|
// ============================================================
|
|
//
|
|
// These types are referenced by application code through the wgtApi
|
|
// function table. Widget-private types (data structs) are defined
|
|
// in their respective widget .c files.
|
|
|
|
// ============================================================
|
|
// Widget class dispatch table
|
|
// ============================================================
|
|
//
|
|
// Each widget type has a WidgetClassT that defines its behavior.
|
|
// Built-in classes are static const; dynamically registered classes
|
|
// (from DXE plugins) are stored in the mutable region of
|
|
// widgetClassTable[].
|
|
//
|
|
// The handlers[] array provides ABI-stable dispatch: method IDs are
|
|
// fixed constants that never change. Adding new methods appends new
|
|
// IDs without shifting existing ones, so widget DXEs compiled against
|
|
// an older DVX version continue to work unmodified. The version field
|
|
// is checked at wgtRegisterClass() time to catch DXEs compiled against
|
|
// an incompatible layout.
|
|
//
|
|
// Flags encode static properties checked by the framework:
|
|
// FOCUSABLE -- can receive keyboard focus (Tab navigation)
|
|
// BOX_CONTAINER -- uses the generic VBox/HBox layout algorithm
|
|
// HORIZ_CONTAINER -- lays out children horizontally (vs. vertical)
|
|
// PAINTS_CHILDREN -- widget handles child rendering itself
|
|
// NO_HIT_RECURSE -- hit testing stops here, no child recursion
|
|
|
|
// Flags encode static properties checked by the framework.
|
|
// Widgets set these in their WidgetClassT definition.
|
|
#define WCLASS_FOCUSABLE 0x00000001
|
|
#define WCLASS_HORIZ_CONTAINER 0x00000004
|
|
#define WCLASS_PAINTS_CHILDREN 0x00000008
|
|
#define WCLASS_NO_HIT_RECURSE 0x00000010
|
|
#define WCLASS_FOCUS_FORWARD 0x00000020 // accel hit forwards focus to next focusable
|
|
#define WCLASS_HAS_POPUP 0x00000040 // has dropdown popup overlay
|
|
#define WCLASS_SCROLLABLE 0x00000080 // accepts mouse wheel events
|
|
#define WCLASS_SCROLL_CONTAINER 0x00000100 // scroll container (ScrollPane)
|
|
#define WCLASS_NEEDS_POLL 0x00000200 // needs periodic polling (AnsiTerm comms)
|
|
#define WCLASS_SWALLOWS_TAB 0x00000400 // Tab key goes to widget, not focus nav
|
|
#define WCLASS_RELAYOUT_ON_SCROLL 0x00000800 // full relayout on scrollbar drag
|
|
#define WCLASS_PRESS_RELEASE 0x00001000 // click = press+release (Button, ImageButton)
|
|
#define WCLASS_ACCEL_WHEN_HIDDEN 0x00002000 // accel matching works even when invisible
|
|
|
|
// Method IDs -- stable ABI, never reorder, never reuse.
|
|
// New methods are appended at the end with the next sequential ID.
|
|
#define WGT_METHOD_PAINT 0 // void (w, d, ops, font, colors)
|
|
#define WGT_METHOD_PAINT_OVERLAY 1 // void (w, d, ops, font, colors)
|
|
#define WGT_METHOD_CALC_MIN_SIZE 2 // void (w, font)
|
|
#define WGT_METHOD_LAYOUT 3 // void (w, font)
|
|
#define WGT_METHOD_GET_LAYOUT_METRICS 4 // void (w, font, pad, gap, extraTop, borderW)
|
|
#define WGT_METHOD_ON_MOUSE 5 // void (w, root, vx, vy)
|
|
#define WGT_METHOD_ON_KEY 6 // void (w, key, mod)
|
|
#define WGT_METHOD_ON_ACCEL_ACTIVATE 7 // void (w, root)
|
|
#define WGT_METHOD_DESTROY 8 // void (w)
|
|
#define WGT_METHOD_ON_CHILD_CHANGED 9 // void (parent, child)
|
|
#define WGT_METHOD_GET_TEXT 10 // const char *(w)
|
|
#define WGT_METHOD_SET_TEXT 11 // void (w, text)
|
|
#define WGT_METHOD_CLEAR_SELECTION 12 // bool (w)
|
|
#define WGT_METHOD_CLOSE_POPUP 13 // void (w)
|
|
#define WGT_METHOD_GET_POPUP_RECT 14 // void (w, font, contentH, popX, popY, popW, popH)
|
|
#define WGT_METHOD_ON_DRAG_UPDATE 15 // void (w, root, x, y)
|
|
#define WGT_METHOD_ON_DRAG_END 16 // void (w, root, x, y)
|
|
#define WGT_METHOD_GET_CURSOR_SHAPE 17 // int32_t (w, vx, vy)
|
|
#define WGT_METHOD_POLL 18 // void (w, win)
|
|
#define WGT_METHOD_QUICK_REPAINT 19 // int32_t (w, outY, outH)
|
|
#define WGT_METHOD_SCROLL_CHILD_INTO_VIEW 20 // void (parent, child)
|
|
#define WGT_METHOD_COUNT 21
|
|
#define WGT_METHOD_MAX 32 // room for future methods
|
|
|
|
#define WGT_CLASS_VERSION 1 // bump on breaking ABI change
|
|
|
|
typedef struct WidgetClassT {
|
|
uint32_t version;
|
|
uint32_t flags;
|
|
void *handlers[WGT_METHOD_MAX];
|
|
} WidgetClassT;
|
|
|
|
// ============================================================
|
|
// Widget structure
|
|
// ============================================================
|
|
|
|
#define MAX_WIDGET_NAME 32
|
|
|
|
typedef struct WidgetT {
|
|
int32_t type; // assigned by wgtRegisterClass()
|
|
// 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];
|
|
|
|
// 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 swallowTab; // Tab key goes to widget, not focus nav
|
|
bool paintDirty; // needs repaint (set by wgtInvalidatePaint)
|
|
char accelKey; // lowercase accelerator character, 0 if none
|
|
|
|
// Content offset: mouse event coordinates are adjusted by this amount
|
|
// so callbacks receive content-relative coords (e.g. canvas subtracts
|
|
// its border so (0,0) = first drawable pixel, matching VB3 behavior).
|
|
int32_t contentOffX;
|
|
int32_t contentOffY;
|
|
|
|
// 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;
|
|
void *data; // widget-private data (allocated by widget DXE)
|
|
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);
|
|
void (*onKeyPress)(struct WidgetT *w, int32_t keyAscii);
|
|
void (*onKeyDown)(struct WidgetT *w, int32_t keyCode, int32_t shift);
|
|
void (*onKeyUp)(struct WidgetT *w, int32_t keyCode, int32_t shift);
|
|
void (*onMouseDown)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
|
|
void (*onMouseUp)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
|
|
void (*onMouseMove)(struct WidgetT *w, int32_t button, int32_t x, int32_t y);
|
|
void (*onScroll)(struct WidgetT *w, int32_t delta);
|
|
bool (*onValidate)(struct WidgetT *w); // return false to cancel write
|
|
|
|
} WidgetT;
|
|
|
|
// ============================================================
|
|
// Typed dispatch helpers
|
|
// ============================================================
|
|
//
|
|
// Each wclsFoo() inline function extracts a handler by stable method ID,
|
|
// casts it to the correct function pointer type, and calls it with a
|
|
// NULL check. This gives callers type-safe dispatch with the same
|
|
// codegen as a direct struct field call.
|
|
|
|
static inline bool wclsHas(const WidgetT *w, int32_t methodId) {
|
|
return w->wclass && w->wclass->handlers[methodId] != NULL;
|
|
}
|
|
|
|
static inline void wclsPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
|
typedef void (*FnT)(WidgetT *, DisplayT *, const BlitOpsT *, const BitmapFontT *, const ColorSchemeT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_PAINT] : NULL;
|
|
if (fn) { fn(w, d, ops, font, colors); }
|
|
}
|
|
|
|
static inline void wclsPaintOverlay(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
|
typedef void (*FnT)(WidgetT *, DisplayT *, const BlitOpsT *, const BitmapFontT *, const ColorSchemeT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_PAINT_OVERLAY] : NULL;
|
|
if (fn) { fn(w, d, ops, font, colors); }
|
|
}
|
|
|
|
static inline void wclsCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|
typedef void (*FnT)(WidgetT *, const BitmapFontT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_CALC_MIN_SIZE] : NULL;
|
|
if (fn) { fn(w, font); }
|
|
}
|
|
|
|
static inline void wclsLayout(WidgetT *w, const BitmapFontT *font) {
|
|
typedef void (*FnT)(WidgetT *, const BitmapFontT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_LAYOUT] : NULL;
|
|
if (fn) { fn(w, font); }
|
|
}
|
|
|
|
static inline void wclsGetLayoutMetrics(const WidgetT *w, const BitmapFontT *font, int32_t *pad, int32_t *gap, int32_t *extraTop, int32_t *borderW) {
|
|
typedef void (*FnT)(const WidgetT *, const BitmapFontT *, int32_t *, int32_t *, int32_t *, int32_t *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_LAYOUT_METRICS] : NULL;
|
|
if (fn) { fn(w, font, pad, gap, extraTop, borderW); }
|
|
}
|
|
|
|
static inline void wclsOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
|
|
typedef void (*FnT)(WidgetT *, WidgetT *, int32_t, int32_t);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_MOUSE] : NULL;
|
|
if (fn) { fn(w, root, vx, vy); }
|
|
}
|
|
|
|
static inline void wclsOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|
typedef void (*FnT)(WidgetT *, int32_t, int32_t);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_KEY] : NULL;
|
|
if (fn) { fn(w, key, mod); }
|
|
}
|
|
|
|
static inline void wclsOnAccelActivate(WidgetT *w, WidgetT *root) {
|
|
typedef void (*FnT)(WidgetT *, WidgetT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_ACCEL_ACTIVATE] : NULL;
|
|
if (fn) { fn(w, root); }
|
|
}
|
|
|
|
static inline void wclsDestroy(WidgetT *w) {
|
|
typedef void (*FnT)(WidgetT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_DESTROY] : NULL;
|
|
if (fn) { fn(w); }
|
|
}
|
|
|
|
static inline void wclsOnChildChanged(WidgetT *parent, WidgetT *child) {
|
|
typedef void (*FnT)(WidgetT *, WidgetT *);
|
|
FnT fn = parent->wclass ? (FnT)parent->wclass->handlers[WGT_METHOD_ON_CHILD_CHANGED] : NULL;
|
|
if (fn) { fn(parent, child); }
|
|
}
|
|
|
|
static inline const char *wclsGetText(const WidgetT *w) {
|
|
typedef const char *(*FnT)(const WidgetT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_TEXT] : NULL;
|
|
return fn ? fn(w) : "";
|
|
}
|
|
|
|
static inline void wclsSetText(WidgetT *w, const char *text) {
|
|
typedef void (*FnT)(WidgetT *, const char *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_SET_TEXT] : NULL;
|
|
if (fn) { fn(w, text); }
|
|
}
|
|
|
|
static inline bool wclsClearSelection(WidgetT *w) {
|
|
typedef bool (*FnT)(WidgetT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_CLEAR_SELECTION] : NULL;
|
|
return fn ? fn(w) : false;
|
|
}
|
|
|
|
static inline void wclsClosePopup(WidgetT *w) {
|
|
typedef void (*FnT)(WidgetT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_CLOSE_POPUP] : NULL;
|
|
if (fn) { fn(w); }
|
|
}
|
|
|
|
static inline void wclsGetPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
|
|
typedef void (*FnT)(const WidgetT *, const BitmapFontT *, int32_t, int32_t *, int32_t *, int32_t *, int32_t *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_POPUP_RECT] : NULL;
|
|
if (fn) { fn(w, font, contentH, popX, popY, popW, popH); }
|
|
}
|
|
|
|
static inline void wclsOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
|
|
typedef void (*FnT)(WidgetT *, WidgetT *, int32_t, int32_t);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_DRAG_UPDATE] : NULL;
|
|
if (fn) { fn(w, root, x, y); }
|
|
}
|
|
|
|
static inline void wclsOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
|
|
typedef void (*FnT)(WidgetT *, WidgetT *, int32_t, int32_t);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_ON_DRAG_END] : NULL;
|
|
if (fn) { fn(w, root, x, y); }
|
|
}
|
|
|
|
static inline int32_t wclsGetCursorShape(const WidgetT *w, int32_t vx, int32_t vy) {
|
|
typedef int32_t (*FnT)(const WidgetT *, int32_t, int32_t);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_GET_CURSOR_SHAPE] : NULL;
|
|
return fn ? fn(w, vx, vy) : 0;
|
|
}
|
|
|
|
static inline void wclsPoll(WidgetT *w, WindowT *win) {
|
|
typedef void (*FnT)(WidgetT *, WindowT *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_POLL] : NULL;
|
|
if (fn) { fn(w, win); }
|
|
}
|
|
|
|
static inline int32_t wclsQuickRepaint(WidgetT *w, int32_t *outY, int32_t *outH) {
|
|
typedef int32_t (*FnT)(WidgetT *, int32_t *, int32_t *);
|
|
FnT fn = w->wclass ? (FnT)w->wclass->handlers[WGT_METHOD_QUICK_REPAINT] : NULL;
|
|
return fn ? fn(w, outY, outH) : 0;
|
|
}
|
|
|
|
static inline void wclsScrollChildIntoView(WidgetT *parent, const WidgetT *child) {
|
|
typedef void (*FnT)(WidgetT *, const WidgetT *);
|
|
FnT fn = parent->wclass ? (FnT)parent->wclass->handlers[WGT_METHOD_SCROLL_CHILD_INTO_VIEW] : NULL;
|
|
if (fn) { fn(parent, child); }
|
|
}
|
|
|
|
// ============================================================
|
|
// 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);
|
|
|
|
// ============================================================
|
|
// 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);
|
|
|
|
// 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);
|
|
|
|
// Default window resize handler installed by wgtInitWindow. Re-evaluates
|
|
// scrollbars and relayouts the widget tree. Call this from custom onResize
|
|
// handlers to chain to the widget system before firing application events.
|
|
void widgetOnResize(WindowT *win, int32_t newW, int32_t newH);
|
|
|
|
// ============================================================
|
|
// 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);
|
|
|
|
// ============================================================
|
|
// Dynamic widget registration
|
|
// ============================================================
|
|
|
|
// Register a new widget class at runtime. Returns the assigned type ID
|
|
// Appends wclass to widgetClassTable and returns the assigned type ID.
|
|
// The WidgetClassT must remain valid for the lifetime of the process
|
|
// (typically a static const in the registering DXE).
|
|
int32_t wgtRegisterClass(const WidgetClassT *wclass);
|
|
|
|
// ============================================================
|
|
// 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, bool fullRepaint);
|
|
|
|
// ============================================================
|
|
// Widget API registry
|
|
// ============================================================
|
|
//
|
|
// Each widget DXE registers a small API struct under a name
|
|
// during wgtRegister(). Callers retrieve it via wgtGetApi()
|
|
// and cast to the widget-specific API type. Per-widget headers
|
|
// (e.g. widgetButton.h) provide typed accessors and convenience
|
|
// macros so callers don't need manual casts.
|
|
//
|
|
// This replaces the monolithic WidgetApiT -- adding a new widget
|
|
// requires zero changes to dvxWidget.h.
|
|
|
|
void wgtRegisterApi(const char *name, const void *api);
|
|
const void *wgtGetApi(const char *name);
|
|
|
|
// ============================================================
|
|
// Widget interface descriptors
|
|
// ============================================================
|
|
//
|
|
// Each widget DXE can register an interface descriptor that
|
|
// describes its BASIC-facing properties, methods, and events.
|
|
// The form runtime and IDE use these for generic dispatch and
|
|
// property panel enumeration.
|
|
|
|
// Property data types
|
|
#define WGT_IFACE_STRING 0
|
|
#define WGT_IFACE_INT 1
|
|
#define WGT_IFACE_BOOL 2
|
|
#define WGT_IFACE_FLOAT 3
|
|
#define WGT_IFACE_ENUM 4 // int32_t with named values
|
|
|
|
// Method calling conventions (how the form runtime marshals args)
|
|
#define WGT_SIG_VOID 0 // void fn(WidgetT *)
|
|
#define WGT_SIG_INT 1 // void fn(WidgetT *, int32_t)
|
|
#define WGT_SIG_BOOL 2 // void fn(WidgetT *, bool)
|
|
#define WGT_SIG_STR 3 // void fn(WidgetT *, const char *)
|
|
#define WGT_SIG_INT_INT 4 // void fn(WidgetT *, int32_t, int32_t)
|
|
#define WGT_SIG_INT_BOOL 5 // void fn(WidgetT *, int32_t, bool)
|
|
#define WGT_SIG_RET_INT 6 // int32_t fn(const WidgetT *)
|
|
#define WGT_SIG_RET_BOOL 7 // bool fn(const WidgetT *)
|
|
#define WGT_SIG_RET_BOOL_INT 8 // bool fn(const WidgetT *, int32_t)
|
|
#define WGT_SIG_INT3 9 // void fn(WidgetT *, int32_t, int32_t, int32_t)
|
|
#define WGT_SIG_INT4 10 // void fn(WidgetT *, int32_t, int32_t, int32_t, int32_t)
|
|
#define WGT_SIG_RET_INT_INT_INT 11 // int32_t fn(const WidgetT *, int32_t, int32_t)
|
|
#define WGT_SIG_INT_INT_STR 12 // void fn(WidgetT *, int32_t, int32_t, const char *)
|
|
#define WGT_SIG_INT5 13 // void fn(WidgetT *, int32_t, int32_t, int32_t, int32_t, int32_t)
|
|
#define WGT_SIG_RET_STR_INT 14 // const char *fn(const WidgetT *, int32_t)
|
|
#define WGT_SIG_RET_STR_INT_INT 15 // const char *fn(const WidgetT *, int32_t, int32_t)
|
|
|
|
// Property descriptor
|
|
typedef struct {
|
|
const char *name; // BASIC property name (e.g. "Caption", "Value")
|
|
uint8_t type; // WGT_IFACE_*
|
|
void *getFn; // getter function pointer (NULL if write-only)
|
|
void *setFn; // setter function pointer (NULL if read-only)
|
|
const char **enumNames; // WGT_IFACE_ENUM only: NULL-terminated array of value names
|
|
} WgtPropDescT;
|
|
|
|
// Method descriptor
|
|
typedef struct {
|
|
const char *name; // BASIC method name (e.g. "Clear", "SetFocus")
|
|
uint8_t sig; // WGT_SIG_*
|
|
void *fn; // function pointer
|
|
} WgtMethodDescT;
|
|
|
|
// Event descriptor
|
|
typedef struct {
|
|
const char *name; // event name (e.g. "Click", "Change")
|
|
} WgtEventDescT;
|
|
|
|
// Common events implicitly available on all widgets.
|
|
// The form runtime wires these callbacks on every control.
|
|
// Widget descriptors only need to list EXTRA events beyond these.
|
|
// Click, DblClick, Change, GotFocus, LostFocus
|
|
|
|
// Create function signature types for design-time / runtime instantiation.
|
|
// The create function is always the first slot in the widget API struct.
|
|
typedef enum {
|
|
WGT_CREATE_PARENT = 0, // fn(parent)
|
|
WGT_CREATE_PARENT_TEXT, // fn(parent, const char *text)
|
|
WGT_CREATE_PARENT_INT, // fn(parent, int32_t)
|
|
WGT_CREATE_PARENT_INT_INT, // fn(parent, int32_t, int32_t)
|
|
WGT_CREATE_PARENT_INT_INT_INT, // fn(parent, int32_t, int32_t, int32_t)
|
|
WGT_CREATE_PARENT_INT_BOOL, // fn(parent, int32_t, bool)
|
|
WGT_CREATE_PARENT_BOOL, // fn(parent, bool)
|
|
WGT_CREATE_PARENT_DATA, // fn(parent, data, w, h, pitch) -- not auto-creatable
|
|
} WgtCreateSigE;
|
|
|
|
#define WGT_MAX_CREATE_ARGS 3
|
|
|
|
// Widget interface descriptor (registered by each .wgt)
|
|
typedef struct {
|
|
const char *basName; // VB-style name (e.g. "CommandButton"), or NULL
|
|
const WgtPropDescT *props; // type-specific properties
|
|
int32_t propCount;
|
|
const WgtMethodDescT *methods; // type-specific methods
|
|
int32_t methodCount;
|
|
const WgtEventDescT *events; // extra events beyond common set
|
|
int32_t eventCount;
|
|
uint8_t createSig; // WgtCreateSigE: how to call create fn
|
|
int32_t createArgs[WGT_MAX_CREATE_ARGS]; // default numeric args
|
|
bool isContainer; // can hold child widgets
|
|
const char *defaultEvent; // default event name (e.g. "Click")
|
|
const char *namePrefix; // auto-name prefix (NULL = use basName)
|
|
} WgtIfaceT;
|
|
|
|
// Register/retrieve interface descriptors by widget type name.
|
|
void wgtRegisterIface(const char *name, const WgtIfaceT *iface);
|
|
const WgtIfaceT *wgtGetIface(const char *name);
|
|
|
|
// Find a widget type name by its VB-style name (e.g. "CommandButton" -> "button").
|
|
// Returns NULL if no widget has that basName. Case-insensitive.
|
|
const char *wgtFindByBasName(const char *basName);
|
|
|
|
// Enumerate all registered widget interfaces.
|
|
int32_t wgtIfaceCount(void);
|
|
const WgtIfaceT *wgtIfaceAt(int32_t idx, const char **outName);
|
|
|
|
// Get/set the .wgt file path for a registered widget (set by loader).
|
|
const char *wgtIfaceGetPath(const char *name);
|
|
void wgtIfaceSetPath(const char *name, const char *path);
|
|
|
|
// Get the 1-based index of this widget within its .wgt file.
|
|
// Used to construct suffixed resource names (e.g. "name-2", "icon16-2").
|
|
int32_t wgtIfaceGetPathIndex(const char *name);
|
|
|
|
#endif // DVX_WGT_H
|