#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 "dvxWgtP.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 = { .version = WGT_CLASS_VERSION, .flags = 0, .handlers = { [WGT_METHOD_PAINT] = (void *)widgetSeparatorPaint, [WGT_METHOD_CALC_MIN_SIZE] = (void *)widgetSeparatorCalcMinSize, [WGT_METHOD_DESTROY] = (void *)widgetSeparatorDestroy, } }; // ============================================================ // 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 }; static const WgtIfaceT sIface = { .basName = "Line", .props = NULL, .propCount = 0, .methods = NULL, .methodCount = 0, .events = NULL, .eventCount = 0, .createSig = WGT_CREATE_PARENT }; void wgtRegister(void) { sTypeId = wgtRegisterClass(&sClassSeparator); wgtRegisterApi("separator", &sApi); wgtRegisterIface("separator", &sIface); }