#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 "dvxWgtP.h" #include "stb_ds_wrap.h" #include #include // 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; }