// 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); } } }