#define DVX_WIDGET_IMPL // widgetSeparator.c -- Separator widget (horizontal and vertical) // // A purely decorative widget that draws a 2px etched line (shadow + // highlight pair) to visually divide groups of widgets. The etched // line technique (dark line followed by light line offset by 1px) // creates a subtle 3D groove that matches the Motif/Win3.1 aesthetic. // // Separate constructors (wgtHSeparator/wgtVSeparator) rather than a // runtime orientation parameter because separators are always fixed // orientation at construction time. // // The min size in the cross-axis is SEPARATOR_THICKNESS (2px), while // the main axis min is 0 -- the separator stretches to fill the // available width/height in its parent layout. This makes separators // work correctly in both HBox and VBox containers without explicit // sizing. // // No mouse/key handlers -- purely decorative, non-focusable. #include "dvxWidgetPlugin.h" #define SEPARATOR_THICKNESS 2 static int32_t sTypeId = -1; typedef struct { bool vertical; } SeparatorDataT; // ============================================================ // Prototypes // ============================================================ static void widgetSeparatorDestroy(WidgetT *w); // ============================================================ // widgetSeparatorCalcMinSize // ============================================================ void widgetSeparatorCalcMinSize(WidgetT *w, const BitmapFontT *font) { (void)font; SeparatorDataT *d = (SeparatorDataT *)w->data; if (d->vertical) { w->calcMinW = SEPARATOR_THICKNESS; w->calcMinH = 0; } else { w->calcMinW = 0; w->calcMinH = SEPARATOR_THICKNESS; } } // ============================================================ // widgetSeparatorDestroy // ============================================================ static void widgetSeparatorDestroy(WidgetT *w) { free(w->data); w->data = NULL; } // ============================================================ // widgetSeparatorPaint // ============================================================ void widgetSeparatorPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) { (void)font; SeparatorDataT *sd = (SeparatorDataT *)w->data; if (sd->vertical) { int32_t cx = w->x + w->w / 2; drawVLine(d, ops, cx, w->y, w->h, colors->windowShadow); drawVLine(d, ops, cx + 1, w->y, w->h, colors->windowHighlight); } else { int32_t cy = w->y + w->h / 2; drawHLine(d, ops, w->x, cy, w->w, colors->windowShadow); drawHLine(d, ops, w->x, cy + 1, w->w, colors->windowHighlight); } } // ============================================================ // DXE registration // ============================================================ static const WidgetClassT sClassSeparator = { .flags = 0, .paint = widgetSeparatorPaint, .paintOverlay = NULL, .calcMinSize = widgetSeparatorCalcMinSize, .layout = NULL, .onMouse = NULL, .onKey = NULL, .destroy = widgetSeparatorDestroy, .getText = NULL, .setText = NULL }; // ============================================================ // Widget creation functions // ============================================================ WidgetT *wgtHSeparator(WidgetT *parent) { WidgetT *w = widgetAlloc(parent, sTypeId); if (w) { SeparatorDataT *d = calloc(1, sizeof(SeparatorDataT)); w->data = d; } return w; } WidgetT *wgtVSeparator(WidgetT *parent) { WidgetT *w = widgetAlloc(parent, sTypeId); if (w) { SeparatorDataT *d = calloc(1, sizeof(SeparatorDataT)); w->data = d; d->vertical = true; } return w; } // ============================================================ // DXE registration // ============================================================ static const struct { WidgetT *(*hSeparator)(WidgetT *parent); WidgetT *(*vSeparator)(WidgetT *parent); } sApi = { .hSeparator = wgtHSeparator, .vSeparator = wgtVSeparator }; void wgtRegister(void) { sTypeId = wgtRegisterClass(&sClassSeparator); wgtRegisterApi("separator", &sApi); }