DVX_GUI/widgets/widgetSeparator.c

148 lines
4.2 KiB
C

#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 = {
.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
};
void wgtRegister(void) {
sTypeId = wgtRegisterClass(&sClassSeparator);
wgtRegisterApi("separator", &sApi);
}