// widgetOps.c — Paint dispatcher and public widget operations #include "widgetInternal.h" // ============================================================ // debugContainerBorder // ============================================================ // // Draw a 1px border in a garish neon color derived from the widget // pointer so every container gets a distinct, ugly color. static void debugContainerBorder(WidgetT *w, DisplayT *d, const BlitOpsT *ops) { static const uint8_t palette[][3] = { {255, 0, 255}, // magenta { 0, 255, 0}, // lime {255, 255, 0}, // yellow { 0, 255, 255}, // cyan {255, 128, 0}, // orange {128, 0, 255}, // purple {255, 0, 128}, // hot pink { 0, 128, 255}, // sky blue {128, 255, 0}, // chartreuse {255, 64, 64}, // salmon { 64, 255, 128}, // mint {255, 128, 255}, // orchid }; uint32_t h = (uint32_t)(uintptr_t)w * 2654435761u; int32_t idx = (int32_t)((h >> 16) % 12); uint32_t color = packColor(d, palette[idx][0], palette[idx][1], palette[idx][2]); drawHLine(d, ops, w->x, w->y, w->w, color); drawHLine(d, ops, w->x, w->y + w->h - 1, w->w, color); drawVLine(d, ops, w->x, w->y, w->h, color); drawVLine(d, ops, w->x + w->w - 1, w->y, w->h, color); } // ============================================================ // widgetPaintOne // ============================================================ // // Paint a single widget and its children. Dispatches to per-widget // paint functions defined in their respective files. void widgetPaintOne(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) { if (!w->visible) { return; } // Paint this widget via vtable if (w->wclass && w->wclass->paint) { w->wclass->paint(w, d, ops, font, colors); } // Widgets that paint their own children return early if (w->wclass && (w->wclass->flags & WCLASS_PAINTS_CHILDREN)) { if (sDebugLayout) { debugContainerBorder(w, d, ops); } return; } // Paint children for (WidgetT *c = w->firstChild; c; c = c->nextSibling) { widgetPaintOne(c, d, ops, font, colors); } // Debug: draw container borders on top of children if (sDebugLayout && w->firstChild) { debugContainerBorder(w, d, ops); } } // ============================================================ // widgetPaintOverlays // ============================================================ // // Paints popup overlays (open dropdowns/comboboxes) on top of // the widget tree. Called after the main paint pass. void widgetPaintOverlays(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) { if (!sOpenPopup) { return; } // Verify the popup belongs to this widget tree WidgetT *check = sOpenPopup; while (check->parent) { check = check->parent; } if (check != root) { return; } if (sOpenPopup->wclass && sOpenPopup->wclass->paintOverlay) { sOpenPopup->wclass->paintOverlay(sOpenPopup, d, ops, font, colors); } } // ============================================================ // wgtDestroy // ============================================================ void wgtDestroy(WidgetT *w) { if (!w) { return; } if (w->parent) { widgetRemoveChild(w->parent, w); } widgetDestroyChildren(w); if (w->wclass && w->wclass->destroy) { w->wclass->destroy(w); } // Clear static references if (sFocusedWidget == w) { sFocusedWidget = NULL; } if (sOpenPopup == w) { sOpenPopup = NULL; } if (sDragSlider == w) { sDragSlider = NULL; } if (sDrawingCanvas == w) { sDrawingCanvas = NULL; } // If this is the root, clear the window's reference if (w->window && w->window->widgetRoot == w) { w->window->widgetRoot = NULL; } free(w); } // ============================================================ // wgtFind // ============================================================ WidgetT *wgtFind(WidgetT *root, const char *name) { if (!root || !name) { return NULL; } if (root->name[0] && strcmp(root->name, name) == 0) { return root; } for (WidgetT *c = root->firstChild; c; c = c->nextSibling) { WidgetT *found = wgtFind(c, name); if (found) { return found; } } return NULL; } // ============================================================ // wgtGetText // ============================================================ const char *wgtGetText(const WidgetT *w) { if (!w) { return ""; } if (w->wclass && w->wclass->getText) { return w->wclass->getText(w); } return ""; } // ============================================================ // wgtInitWindow // ============================================================ WidgetT *wgtInitWindow(AppContextT *ctx, WindowT *win) { WidgetT *root = widgetAlloc(NULL, WidgetVBoxE); if (!root) { return NULL; } root->window = win; root->userData = ctx; win->widgetRoot = root; win->onPaint = widgetOnPaint; win->onMouse = widgetOnMouse; win->onKey = widgetOnKey; win->onResize = widgetOnResize; return root; } // ============================================================ // wgtInvalidate // ============================================================ void wgtInvalidate(WidgetT *w) { if (!w || !w->window) { return; } // Find the root WidgetT *root = w; while (root->parent) { root = root->parent; } AppContextT *ctx = (AppContextT *)root->userData; if (!ctx) { return; } // Manage scrollbars (measures, adds/removes scrollbars, relayouts) widgetManageScrollbars(w->window, ctx); // Repaint RectT fullRect = {0, 0, w->window->contentW, w->window->contentH}; widgetOnPaint(w->window, &fullRect); w->window->contentDirty = true; // Dirty the window on screen dvxInvalidateWindow(ctx, w->window); } // ============================================================ // wgtInvalidatePaint // ============================================================ // // Lightweight repaint — skips measure/layout/scrollbar management. // Use when only visual state changed (slider value, cursor blink, // selection highlight, checkbox toggle) but widget sizes are stable. void wgtInvalidatePaint(WidgetT *w) { if (!w || !w->window) { return; } WidgetT *root = w; while (root->parent) { root = root->parent; } AppContextT *ctx = (AppContextT *)root->userData; if (!ctx) { return; } // Repaint without measure/layout RectT fullRect = {0, 0, w->window->contentW, w->window->contentH}; widgetOnPaint(w->window, &fullRect); w->window->contentDirty = true; dvxInvalidateWindow(ctx, w->window); } // ============================================================ // wgtPaint // ============================================================ void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) { if (!root) { return; } widgetPaintOne(root, d, ops, font, colors); } // ============================================================ // wgtSetDebugLayout // ============================================================ void wgtSetDebugLayout(bool enabled) { sDebugLayout = enabled; } // ============================================================ // wgtSetEnabled // ============================================================ void wgtSetEnabled(WidgetT *w, bool enabled) { if (w) { w->enabled = enabled; } } // ============================================================ // wgtSetText // ============================================================ void wgtSetText(WidgetT *w, const char *text) { if (!w) { return; } if (w->wclass && w->wclass->setText) { w->wclass->setText(w, text); } } // ============================================================ // wgtSetVisible // ============================================================ void wgtSetVisible(WidgetT *w, bool visible) { if (w) { w->visible = visible; } }