DVX_GUI/core/widgetClass.c

255 lines
6.5 KiB
C

#define DVX_WIDGET_IMPL
// widgetClass.c -- Widget class table, API registry, and dynamic registration
//
// widgetClassTable is a stb_ds dynamic array. Widget DXEs call
// wgtRegisterClass() during wgtRegister() to append their class
// definition and receive a runtime type ID. No widget types are
// known at compile time.
//
// The API registry uses an stb_ds string hashmap for O(1) lookup.
// Each widget DXE calls wgtRegisterApi("name", &sApi) during
// wgtRegister(). App/core code calls wgtGetApi("name") to get
// the API pointer, then casts and calls through it.
#include "dvxWidgetPlugin.h"
#include "stb_ds_wrap.h"
#include <string.h>
#include <strings.h>
// stb_ds dynamic array of class pointers. Grows on each
// wgtRegisterClass() call. Index = type ID.
const WidgetClassT **widgetClassTable = NULL;
// stb_ds string hashmap: key = widget name, value = API pointer
typedef struct {
char *key; // stb_ds string key (heap-allocated by shput)
const void *value;
} ApiMapEntryT;
static ApiMapEntryT *sApiMap = NULL;
// stb_ds string hashmap: key = widget name, value = iface + path
typedef struct {
const WgtIfaceT *iface;
char path[DVX_MAX_PATH];
int32_t pathIndex; // 1-based: 1 = first widget from this file, 2 = second, etc.
} IfaceEntryT;
typedef struct {
char *key;
IfaceEntryT value;
} IfaceMapEntryT;
static IfaceMapEntryT *sIfaceMap = NULL;
// ============================================================
// wgtGetApi
// ============================================================
//
// Look up a widget API by name. O(1) via stb_ds string hashmap.
// Returns NULL if the widget is not loaded. Callers should still
// cache the result in a static local to skip even the hash.
const void *wgtGetApi(const char *name) {
if (!name) {
return NULL;
}
int32_t idx = shgeti(sApiMap, name);
if (idx < 0) {
return NULL;
}
return sApiMap[idx].value;
}
// ============================================================
// wgtFindByBasName
// ============================================================
//
// Scan all registered interfaces for one whose basName matches
// (case-insensitive). Returns the widget type name, or NULL.
const char *wgtFindByBasName(const char *basName) {
if (!basName || !sIfaceMap) {
return NULL;
}
for (size_t i = 0; i < shlenu(sIfaceMap); i++) {
if (sIfaceMap[i].value.iface && sIfaceMap[i].value.iface->basName) {
if (strcasecmp(sIfaceMap[i].value.iface->basName, basName) == 0) {
return sIfaceMap[i].key;
}
}
}
return NULL;
}
// ============================================================
// wgtGetIface
// ============================================================
//
// Look up a widget interface descriptor by type name.
const WgtIfaceT *wgtGetIface(const char *name) {
if (!name) {
return NULL;
}
int32_t idx = shgeti(sIfaceMap, name);
if (idx < 0) {
return NULL;
}
return sIfaceMap[idx].value.iface;
}
// ============================================================
// wgtIfaceCount / wgtIfaceAt
// ============================================================
//
// Enumerate all registered widget interfaces.
int32_t wgtIfaceCount(void) {
return sIfaceMap ? (int32_t)shlenu(sIfaceMap) : 0;
}
const WgtIfaceT *wgtIfaceAt(int32_t idx, const char **outName) {
if (!sIfaceMap || idx < 0 || idx >= (int32_t)shlenu(sIfaceMap)) {
return NULL;
}
if (outName) {
*outName = sIfaceMap[idx].key;
}
return sIfaceMap[idx].value.iface;
}
// ============================================================
// wgtIfaceGetPath
// ============================================================
const char *wgtIfaceGetPath(const char *name) {
if (!name || !sIfaceMap) {
return NULL;
}
int32_t idx = shgeti(sIfaceMap, name);
if (idx < 0) {
return NULL;
}
return sIfaceMap[idx].value.path[0] ? sIfaceMap[idx].value.path : NULL;
}
// ============================================================
// wgtIfaceGetPathIndex
// ============================================================
int32_t wgtIfaceGetPathIndex(const char *name) {
if (!name || !sIfaceMap) {
return 1;
}
int32_t idx = shgeti(sIfaceMap, name);
if (idx < 0) {
return 1;
}
return sIfaceMap[idx].value.pathIndex > 0 ? sIfaceMap[idx].value.pathIndex : 1;
}
// ============================================================
// wgtIfaceSetPath
// ============================================================
void wgtIfaceSetPath(const char *name, const char *path) {
if (!name || !path || !sIfaceMap) {
return;
}
int32_t idx = shgeti(sIfaceMap, name);
if (idx >= 0) {
snprintf(sIfaceMap[idx].value.path, sizeof(sIfaceMap[idx].value.path), "%s", path);
// Count how many ifaces already have this path to assign a 1-based index
int32_t count = 0;
for (size_t i = 0; i < shlenu(sIfaceMap); i++) {
if (sIfaceMap[i].value.path[0] && strcmp(sIfaceMap[i].value.path, path) == 0) {
count++;
}
}
sIfaceMap[idx].value.pathIndex = count;
}
}
// ============================================================
// wgtRegisterApi
// ============================================================
//
// Register a widget's public API struct under a name. Called by
// each widget DXE during wgtRegister().
void wgtRegisterApi(const char *name, const void *api) {
if (!name || !api) {
return;
}
shput(sApiMap, name, api);
}
// ============================================================
// wgtRegisterIface
// ============================================================
//
// Register a widget's interface descriptor under its type name.
// Called by widget DXEs during wgtRegister().
void wgtRegisterIface(const char *name, const WgtIfaceT *iface) {
if (!name || !iface) {
return;
}
IfaceEntryT entry;
memset(&entry, 0, sizeof(entry));
entry.iface = iface;
shput(sIfaceMap, name, entry);
}
// ============================================================
// wgtRegisterClass
// ============================================================
//
// Appends a class to the table and returns the assigned type ID.
// The ID is simply the array index.
int32_t wgtRegisterClass(const WidgetClassT *wclass) {
if (!wclass) {
return -1;
}
int32_t id = arrlen(widgetClassTable);
arrput(widgetClassTable, wclass);
return id;
}