230 lines
6.6 KiB
C
230 lines
6.6 KiB
C
#define DVX_WIDGET_IMPL
|
|
// widgetProgressBar.c -- ProgressBar widget
|
|
//
|
|
// A non-interactive fill-bar widget for displaying bounded progress.
|
|
// Supports both horizontal and vertical orientations via separate
|
|
// constructors rather than a runtime flag, because orientation is
|
|
// typically fixed at UI construction time and the two constructors
|
|
// read more clearly at the call site.
|
|
//
|
|
// Rendering: sunken 2px bevel border (inverted highlight/shadow to
|
|
// look recessed) with a solid fill rect proportional to value/maxValue.
|
|
// Uses integer division for fill size -- no floating point, which would
|
|
// be prohibitively expensive on a 486 without an FPU.
|
|
//
|
|
// The widget has no mouse or keyboard handlers -- it is purely display.
|
|
// Value is controlled externally through the set/get API.
|
|
|
|
#include "dvxWidgetPlugin.h"
|
|
|
|
static int32_t sTypeId = -1;
|
|
|
|
typedef struct {
|
|
int32_t value;
|
|
int32_t maxValue;
|
|
bool vertical;
|
|
} ProgressBarDataT;
|
|
|
|
|
|
// ============================================================
|
|
// widgetProgressBarCalcMinSize
|
|
// ============================================================
|
|
|
|
// Min size is based on font metrics to keep the bar proportional to
|
|
// surrounding text. The cross-axis uses charHeight+4 (room for the
|
|
// bar plus 2px bevel on each side). The main axis uses 12 char widths
|
|
// so the bar is wide enough to show meaningful progress granularity.
|
|
void widgetProgressBarCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|
ProgressBarDataT *d = (ProgressBarDataT *)w->data;
|
|
|
|
if (d->vertical) {
|
|
w->calcMinW = font->charHeight + 4;
|
|
w->calcMinH = font->charWidth * 12;
|
|
} else {
|
|
w->calcMinW = font->charWidth * 12;
|
|
w->calcMinH = font->charHeight + 4;
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetProgressBarPaint
|
|
// ============================================================
|
|
|
|
// Paint uses a two-pass approach: first the sunken bevel border, then
|
|
// the fill rect. The fill color defaults to activeTitleBg (the window
|
|
// title bar color) to provide strong visual contrast inside the
|
|
// recessed trough -- matching classic Win3.1/Motif progress bar style.
|
|
// When disabled, the fill becomes windowShadow to look grayed out.
|
|
//
|
|
// Vertical bars fill from bottom-up (natural "filling" metaphor),
|
|
// which requires computing the Y offset as (innerH - fillH).
|
|
void widgetProgressBarPaint(WidgetT *w, DisplayT *disp, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
|
(void)font;
|
|
ProgressBarDataT *d = (ProgressBarDataT *)w->data;
|
|
uint32_t fg = w->enabled ? (w->fgColor ? w->fgColor : colors->activeTitleBg) : colors->windowShadow;
|
|
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
|
|
|
// Sunken border -- highlight/shadow are swapped vs raised bevel
|
|
// to create the "pressed in" trough appearance
|
|
BevelStyleT bevel;
|
|
bevel.highlight = colors->windowShadow;
|
|
bevel.shadow = colors->windowHighlight;
|
|
bevel.face = bg;
|
|
bevel.width = 2;
|
|
drawBevel(disp, ops, w->x, w->y, w->w, w->h, &bevel);
|
|
|
|
// Fill bar
|
|
int32_t maxVal = d->maxValue;
|
|
|
|
if (maxVal <= 0) {
|
|
maxVal = 100;
|
|
}
|
|
|
|
int32_t innerW = w->w - 4;
|
|
int32_t innerH = w->h - 4;
|
|
|
|
if (d->vertical) {
|
|
int32_t fillH = (innerH * d->value) / maxVal;
|
|
|
|
if (fillH > innerH) {
|
|
fillH = innerH;
|
|
}
|
|
|
|
if (fillH > 0) {
|
|
rectFill(disp, ops, w->x + 2, w->y + 2 + innerH - fillH, innerW, fillH, fg);
|
|
}
|
|
} else {
|
|
int32_t fillW = (innerW * d->value) / maxVal;
|
|
|
|
if (fillW > innerW) {
|
|
fillW = innerW;
|
|
}
|
|
|
|
if (fillW > 0) {
|
|
rectFill(disp, ops, w->x + 2, w->y + 2, fillW, innerH, fg);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetProgressBarDestroy
|
|
// ============================================================
|
|
|
|
void widgetProgressBarDestroy(WidgetT *w) {
|
|
free(w->data);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// DXE registration
|
|
// ============================================================
|
|
|
|
static const WidgetClassT sClassProgressBar = {
|
|
.version = WGT_CLASS_VERSION,
|
|
.flags = 0,
|
|
.handlers = {
|
|
[WGT_METHOD_PAINT] = (void *)widgetProgressBarPaint,
|
|
[WGT_METHOD_CALC_MIN_SIZE] = (void *)widgetProgressBarCalcMinSize,
|
|
[WGT_METHOD_DESTROY] = (void *)widgetProgressBarDestroy,
|
|
}
|
|
};
|
|
|
|
|
|
// ============================================================
|
|
// Widget creation functions
|
|
// ============================================================
|
|
|
|
|
|
WidgetT *wgtProgressBar(WidgetT *parent) {
|
|
WidgetT *w = widgetAlloc(parent, sTypeId);
|
|
|
|
if (w) {
|
|
ProgressBarDataT *d = calloc(1, sizeof(ProgressBarDataT));
|
|
d->value = 0;
|
|
d->maxValue = 100;
|
|
d->vertical = false;
|
|
w->data = d;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
WidgetT *wgtProgressBarV(WidgetT *parent) {
|
|
WidgetT *w = widgetAlloc(parent, sTypeId);
|
|
|
|
if (w) {
|
|
ProgressBarDataT *d = calloc(1, sizeof(ProgressBarDataT));
|
|
d->value = 0;
|
|
d->maxValue = 100;
|
|
d->vertical = true;
|
|
w->data = d;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
int32_t wgtProgressBarGetValue(const WidgetT *w) {
|
|
VALIDATE_WIDGET(w, sTypeId, 0);
|
|
ProgressBarDataT *d = (ProgressBarDataT *)w->data;
|
|
|
|
return d->value;
|
|
}
|
|
|
|
|
|
void wgtProgressBarSetValue(WidgetT *w, int32_t value) {
|
|
VALIDATE_WIDGET_VOID(w, sTypeId);
|
|
ProgressBarDataT *d = (ProgressBarDataT *)w->data;
|
|
|
|
if (value < 0) {
|
|
value = 0;
|
|
}
|
|
|
|
if (value > d->maxValue) {
|
|
value = d->maxValue;
|
|
}
|
|
|
|
d->value = value;
|
|
wgtInvalidatePaint(w);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// DXE registration
|
|
// ============================================================
|
|
|
|
|
|
static const struct {
|
|
WidgetT *(*create)(WidgetT *parent);
|
|
WidgetT *(*createV)(WidgetT *parent);
|
|
void (*setValue)(WidgetT *w, int32_t value);
|
|
int32_t (*getValue)(const WidgetT *w);
|
|
} sApi = {
|
|
.create = wgtProgressBar,
|
|
.createV = wgtProgressBarV,
|
|
.setValue = wgtProgressBarSetValue,
|
|
.getValue = wgtProgressBarGetValue
|
|
};
|
|
|
|
static const WgtPropDescT sProps[] = {
|
|
{ "Value", WGT_IFACE_INT, (void *)wgtProgressBarGetValue, (void *)wgtProgressBarSetValue }
|
|
};
|
|
|
|
static const WgtIfaceT sIface = {
|
|
.basName = "ProgressBar",
|
|
.props = sProps,
|
|
.propCount = 1,
|
|
.methods = NULL,
|
|
.methodCount = 0,
|
|
.events = NULL,
|
|
.eventCount = 0
|
|
};
|
|
|
|
void wgtRegister(void) {
|
|
sTypeId = wgtRegisterClass(&sClassProgressBar);
|
|
wgtRegisterApi("progressbar", &sApi);
|
|
wgtRegisterIface("progressbar", &sIface);
|
|
}
|