DVX_GUI/dvx/widgets/widgetProgressBar.c

161 lines
5.1 KiB
C

// 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 "widgetInternal.h"
// ============================================================
// wgtProgressBar
// ============================================================
WidgetT *wgtProgressBar(WidgetT *parent) {
WidgetT *w = widgetAlloc(parent, WidgetProgressBarE);
if (w) {
w->as.progressBar.value = 0;
w->as.progressBar.maxValue = 100;
w->as.progressBar.vertical = false;
}
return w;
}
// ============================================================
// wgtProgressBarV
// ============================================================
WidgetT *wgtProgressBarV(WidgetT *parent) {
WidgetT *w = widgetAlloc(parent, WidgetProgressBarE);
if (w) {
w->as.progressBar.value = 0;
w->as.progressBar.maxValue = 100;
w->as.progressBar.vertical = true;
}
return w;
}
// ============================================================
// wgtProgressBarGetValue
// ============================================================
int32_t wgtProgressBarGetValue(const WidgetT *w) {
VALIDATE_WIDGET(w, WidgetProgressBarE, 0);
return w->as.progressBar.value;
}
// ============================================================
// wgtProgressBarSetValue
// ============================================================
void wgtProgressBarSetValue(WidgetT *w, int32_t value) {
VALIDATE_WIDGET_VOID(w, WidgetProgressBarE);
if (value < 0) {
value = 0;
}
if (value > w->as.progressBar.maxValue) {
value = w->as.progressBar.maxValue;
}
w->as.progressBar.value = value;
wgtInvalidatePaint(w);
}
// ============================================================
// 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) {
if (w->as.progressBar.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 *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
(void)font;
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(d, ops, w->x, w->y, w->w, w->h, &bevel);
// Fill bar
int32_t maxVal = w->as.progressBar.maxValue;
if (maxVal <= 0) {
maxVal = 100;
}
int32_t innerW = w->w - 4;
int32_t innerH = w->h - 4;
if (w->as.progressBar.vertical) {
int32_t fillH = (innerH * w->as.progressBar.value) / maxVal;
if (fillH > innerH) {
fillH = innerH;
}
if (fillH > 0) {
rectFill(d, ops, w->x + 2, w->y + 2 + innerH - fillH, innerW, fillH, fg);
}
} else {
int32_t fillW = (innerW * w->as.progressBar.value) / maxVal;
if (fillW > innerW) {
fillW = innerW;
}
if (fillW > 0) {
rectFill(d, ops, w->x + 2, w->y + 2, fillW, innerH, fg);
}
}
}