summaryrefslogtreecommitdiff
path: root/alf
diff options
context:
space:
mode:
authorJonas Kümmerlin <jonas@kuemmerlin.eu>2020-07-08 21:03:41 +0200
committerJonas Kümmerlin <jonas@kuemmerlin.eu>2020-07-08 21:03:41 +0200
commitc8a5d9cc2dedf3a8573659fa70b14f86e899bac4 (patch)
tree396a26f622c5064734c44197b2dc8d39008e7410 /alf
parent960f0cef228a64ea598c4531d0a7d159dfb7ed0d (diff)
progress bar
Diffstat (limited to 'alf')
-rw-r--r--alf/alf.h38
-rw-r--r--alf/alfprogress.cpp476
2 files changed, 514 insertions, 0 deletions
diff --git a/alf/alf.h b/alf/alf.h
index f6ef21d..e64cac7 100644
--- a/alf/alf.h
+++ b/alf/alf.h
@@ -685,6 +685,44 @@ ALF_SpinBox_EditControl(HWND spinbox);
HWND
ALF_SpinBox_UpDownControl(HWND spinbox);
+// progress
+enum ALFProgressState {
+ ALF_PROGRESS_STATE_NORMAL = 1, //==PBST_NORMAL
+ ALF_PROGRESS_STATE_ERROR = 2, //==PBST_ERROR FIXME! only supported in native commctl v6 progress bar on Vista+
+ ALF_PROGRESS_STATE_PAUSED = 3, //==PBST_PAUSED FIXME! only supported in native commctl v6 progress bar on Vista+
+ ALF_PROGRESS_STATE_INDETERMINATE = 4,
+ ALF_PROGRESS_STATE_HIDDEN = 5 // TODO: define semantics and implement
+};
+
+HWND
+ALF_AddNativeProgressBar(HWND parent, WORD id, int x, int y);
+
+HWND
+ALF_AddProgressGauge(HWND parent, WORD id, int x, int y);
+
+// creates a native progress bar on commctlv6 (XP+), a selfmade progress gauge otherwise
+HWND
+ALF_AddProgressBar(HWND parent, WORD id, int x, int y);
+
+void
+ALF_Progress_Range(HWND progress, int *pMin, int *pMax);
+
+void
+ALF_Progress_SetRange(HWND progress, int min, int max);
+
+int
+ALF_Progress_Value(HWND progress);
+
+void
+ALF_Progress_SetValue(HWND progress, int value);
+
+enum ALFProgressState
+ALF_Progress_State(HWND progress);
+
+void
+ALF_Progress_SetState(HWND progress, enum ALFProgressState state);
+
+
#ifdef __cplusplus
} // extern C
#endif
diff --git a/alf/alfprogress.cpp b/alf/alfprogress.cpp
new file mode 100644
index 0000000..3a5c4fc
--- /dev/null
+++ b/alf/alfprogress.cpp
@@ -0,0 +1,476 @@
+#include "alfpriv.h"
+
+#define ALF_PROGRESS_MARQUEE_WIDTH 5
+#define ALF_PROGRESS_MARQUEE_TIMER_ID 42
+
+// need special messages for gauge on Win32s because Win32s only keeps the
+// low word of WPARAM where we occasionally need to pass 32bit LONGs
+#define ALF_WM_PROGRESS_SETRANGE (ALF_WM__BASE + 201)
+#define ALF_WM_PROGRESS_SETPOS (ALF_WM__BASE + 202)
+
+typedef struct {
+ int min;
+ int max;
+ int pos;
+ int marquee;
+ HFONT font;
+} ALFProgressGaugePriv;
+
+static void
+ALF_ProgressGauge_Init(ALFProgressGaugePriv *priv)
+{
+ priv->min = 0;
+ priv->max = 100;
+ priv->pos = 0;
+ priv->marquee = 0;
+}
+
+static LRESULT
+ALF_ProgressGauge_GetRange(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM retminormax, PBRANGE *pRange)
+{
+ (void)hwnd;
+
+ if (pRange) {
+ pRange->iLow = priv->min;
+ pRange->iHigh = priv->max;
+ }
+
+ if (retminormax)
+ return priv->min;
+ else
+ return priv->max;
+}
+
+static LRESULT
+ALF_ProgressGauge_SetRange(ALFProgressGaugePriv *priv, HWND hwnd, int min, int max)
+{
+ priv->min = min;
+ priv->max = max;
+
+ if (max < min)
+ priv->max = min;
+
+ if (priv->pos < min)
+ priv->pos = min;
+
+ if (priv->pos > max)
+ priv->pos = max;
+
+ InvalidateRect(hwnd, NULL, TRUE);
+ return 1;
+}
+
+static LRESULT
+ALF_ProgressGauge_GetPos(ALFProgressGaugePriv *priv, HWND hwnd)
+{
+ (void)hwnd;
+
+ return (LRESULT)priv->pos;
+}
+
+static LRESULT
+ALF_ProgressGauge_SetPos(ALFProgressGaugePriv *priv, HWND hwnd, int pos)
+{
+ priv->pos = pos;
+
+ if (priv->pos < priv->min)
+ priv->pos = priv->min;
+
+ if (priv->pos > priv->max)
+ priv->pos = priv->max;
+
+ InvalidateRect(hwnd, NULL, TRUE);
+ return 1;
+}
+
+static LRESULT
+ALF_ProgressGauge_Size(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM wparam, LPARAM lparam)
+{
+ (void)priv;
+
+ InvalidateRect(hwnd, NULL, TRUE);
+
+ return DefWindowProc(hwnd, WM_SIZE, wparam, lparam);
+}
+
+static void
+ALF_ProgressGauge_DoPaint(ALFProgressGaugePriv *priv, HWND hwnd, HDC dc, RECT *rcClip)
+{
+ (void)rcClip;
+
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+ OffsetRect(&rc, -rc.left, -rc.top);
+
+ DrawEdge(dc, &rc, BDR_SUNKENOUTER, BF_RECT | BF_ADJUST);
+
+ FrameRect(dc, &rc, GetSysColorBrush(COLOR_BTNFACE));
+ InflateRect(&rc, -1, -1);
+
+ TCHAR textbuf[64]; // FIXME! make this variable length?
+ ZeroMemory(textbuf, sizeof(textbuf));
+
+ int len = GetWindowText(hwnd, textbuf, sizeof(textbuf)/sizeof(textbuf[0]));
+
+ HFONT oldfont = SelectFont(dc, (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0));
+
+ TEXTMETRIC tm;
+ ZeroMemory(&tm, sizeof(tm));
+
+ GetTextMetrics(dc, &tm);
+
+ int refx = rc.left + (rc.right - rc.left) / 2;
+ int refy = rc.top + (rc.bottom - rc.top + tm.tmAscent - tm.tmDescent) / 2;
+
+ SetTextAlign(dc, TA_CENTER | TA_BASELINE);
+
+ COLORREF clrHilight = GetSysColor(COLOR_HIGHLIGHT);
+ COLORREF clrHilightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ COLORREF clrBtnFace = GetSysColor(COLOR_BTNFACE);
+ COLORREF clrBtnText = GetSysColor(COLOR_BTNTEXT);
+
+ if (GetWindowLong(hwnd, GWL_STYLE) & PBS_MARQUEE) {
+ RECT rcMarquee = { rc.left + priv->marquee,
+ rc.top,
+ rc.left + priv->marquee + ALF_PROGRESS_MARQUEE_WIDTH,
+ rc.bottom };
+ if (rcMarquee.left < rc.left)
+ rcMarquee.left = rc.left;
+ if (rcMarquee.right > rc.right)
+ rcMarquee.right = rc.right;
+
+ RECT rcMarqueeWrap = { rc.left,
+ rc.top,
+ rc.left + priv->marquee + ALF_PROGRESS_MARQUEE_WIDTH - rc.right + rc.left,
+ rc.bottom };
+ if (rcMarqueeWrap.right < rcMarqueeWrap.left)
+ rcMarqueeWrap.right = rcMarqueeWrap.left;
+
+ RECT rcLeftSpace = { rcMarqueeWrap.right, rc.top, rcMarquee.left, rc.bottom };
+ RECT rcRightSpace = { rcMarquee.right, rc.top, rc.right, rc.bottom };
+
+ SetBkColor(dc, clrHilight);
+ SetTextColor(dc, clrHilightText);
+ ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcMarquee, textbuf, (UINT)len, NULL);
+ ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcMarqueeWrap, textbuf, (UINT)len, NULL);
+
+ SetBkColor(dc, clrBtnFace);
+ SetTextColor(dc, clrBtnText);
+ ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcLeftSpace, textbuf, (UINT)len, NULL);
+ ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcRightSpace, textbuf, (UINT)len, NULL);
+ } else {
+ int barwidth = MulDiv(priv->pos - priv->min, rc.right - rc.left, priv->max - priv->min);
+
+ // XXX: make the painting pixel-compatible to the original progress bar
+ // IMHO this seems to be some kind of off-by-one error in the microsoft code
+ if (barwidth >= rc.right - rc.left)
+ barwidth -= 1;
+
+ RECT rcLeft = { rc.left, rc.top, rc.left + barwidth, rc.bottom };
+ RECT rcRight = { rc.left + barwidth, rc.top, rc.right, rc.bottom };
+
+ SetBkColor(dc, clrHilight);
+ SetTextColor(dc, clrHilightText);
+ ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcLeft, textbuf, (UINT)len, NULL);
+
+ SetBkColor(dc, clrBtnFace);
+ SetTextColor(dc, clrBtnText);
+ ExtTextOut(dc, refx, refy, ETO_OPAQUE|ETO_CLIPPED, &rcRight, textbuf, (UINT)len, NULL);
+ }
+
+ SelectFont(dc, oldfont);
+}
+
+static LRESULT
+ALF_ProgressGauge_Paint(ALFProgressGaugePriv *priv, HWND hwnd)
+{
+ PAINTSTRUCT ps;
+ HDC dc = BeginPaint(hwnd, &ps);
+
+ ALF_ProgressGauge_DoPaint(priv, hwnd, dc, &ps.rcPaint);
+
+ EndPaint(hwnd, &ps);
+ return 0;
+}
+
+static LRESULT
+ALF_ProgressGauge_PrintClient(ALFProgressGaugePriv *priv, HWND hwnd, HDC dc)
+{
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+ OffsetRect(&rc, -rc.left, -rc.top);
+
+ ALF_ProgressGauge_DoPaint(priv, hwnd, dc, &rc);
+ return 0;
+}
+
+static LRESULT
+ALF_ProgressGauge_Timer(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM id, LPARAM lparam)
+{
+ if (id == ALF_PROGRESS_MARQUEE_TIMER_ID) {
+ if (GetWindowLong(hwnd, GWL_STYLE) & PBS_MARQUEE) {
+ RECT rcClient;
+ GetClientRect(hwnd, &rcClient);
+
+ priv->marquee += 1;
+ if (priv->marquee >= rcClient.right - rcClient.left - 4) {
+ priv->marquee = 0;
+ }
+
+ InvalidateRect(hwnd, NULL, TRUE);
+ } else {
+ KillTimer(hwnd, id);
+ }
+
+ return 0;
+ } else {
+ return DefWindowProc(hwnd, WM_TIMER, id, lparam);
+ }
+}
+
+static LRESULT
+ALF_ProgressGauge_SetMarquee(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM onoff, UINT milliseconds)
+{
+ if (GetWindowLong(hwnd, GWL_STYLE) & PBS_MARQUEE) {
+ if (onoff) {
+ priv->pos = priv->min;
+ priv->marquee = -2;
+ InvalidateRect(hwnd, NULL, TRUE);
+
+ if (milliseconds == 0)
+ milliseconds = 30;
+
+ SetTimer(hwnd, ALF_PROGRESS_MARQUEE_TIMER_ID, milliseconds, NULL);
+ } else {
+ KillTimer(hwnd, ALF_PROGRESS_MARQUEE_TIMER_ID);
+ }
+ }
+
+ return 0;
+}
+
+static LRESULT
+ALF_ProgressGauge_StyleChanged(ALFProgressGaugePriv *priv, HWND hwnd, WPARAM wparam, LPARAM lparam)
+{
+ (void)priv;
+ InvalidateRect(hwnd, NULL, TRUE);
+ return DefWindowProc(hwnd, WM_STYLECHANGED, wparam, lparam);
+}
+
+static LRESULT
+ALF_ProgressGauge_SetFont(ALFProgressGaugePriv *priv, HWND hwnd, HFONT font)
+{
+ priv->font = font;
+ InvalidateRect(hwnd, NULL, TRUE);
+ return 0;
+}
+
+static LRESULT
+ALF_ProgressGauge_GetFont(ALFProgressGaugePriv *priv, HWND hwnd)
+{
+ (void)hwnd;
+ return (LRESULT)priv->font;
+}
+
+static LRESULT
+ALF_ProgressGauge_SetText(ALFProgressGaugePriv *priv, HWND hwnd, const TCHAR *text)
+{
+ (void)priv;
+
+ InvalidateRect(hwnd, NULL, TRUE);
+ return DefWindowProc(hwnd, WM_SETTEXT, 0, (LPARAM)text);
+}
+
+static LRESULT
+ALF_ProgressGauge_CalcSize(ALFProgressGaugePriv *priv, HWND hwnd, SIZE *pSize)
+{
+ if (GetWindowTextLength(hwnd) > 0) {
+ HDC dc = GetDC(hwnd);
+ HFONT oldfont = SelectFont(dc, priv->font);
+
+ TEXTMETRIC tm;
+ ZeroMemory(&tm, sizeof(tm));
+ GetTextMetrics(dc, &tm);
+
+ int cy = tm.tmHeight + 8;
+ if (pSize->cy < cy)
+ pSize->cy = cy;
+
+ SelectFont(dc, oldfont);
+ ReleaseDC(hwnd, dc);
+ }
+
+ return 0;
+}
+
+static LRESULT WINAPI
+ALF_ProgressGauge_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ ALFProgressGaugePriv *priv = (ALFProgressGaugePriv *)GetWindowLongPtr(hwnd, 0);
+
+ if (!priv) {
+ if (msg == WM_CREATE) {
+ priv = ALF_New(ALFProgressGaugePriv, 1);
+ ALF_ProgressGauge_Init(priv);
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)priv);
+ } else {
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+ }
+ }
+
+ switch (msg) {
+ case WM_DESTROY:
+ SetWindowLongPtr(hwnd, 0, 0);
+ ALF_Free(priv);
+ break;
+ case PBM_GETPOS:
+ return ALF_ProgressGauge_GetPos(priv, hwnd);
+ case PBM_SETPOS:
+ return ALF_ProgressGauge_SetPos(priv, hwnd, (int)wparam);
+ case ALF_WM_PROGRESS_SETPOS:
+ return ALF_ProgressGauge_SetPos(priv, hwnd, (int)lparam);
+ case PBM_GETRANGE:
+ return ALF_ProgressGauge_GetRange(priv, hwnd, wparam, (PBRANGE *)lparam);
+ case PBM_SETRANGE32:
+ return ALF_ProgressGauge_SetRange(priv, hwnd, (int)wparam, (int)lparam);
+ case ALF_WM_PROGRESS_SETRANGE:
+ return ALF_ProgressGauge_SetRange(priv, hwnd, ((PBRANGE *)lparam)->iLow, ((PBRANGE *)lparam)->iHigh);
+ case WM_PAINT:
+ return ALF_ProgressGauge_Paint(priv, hwnd);
+ case WM_ERASEBKGND:
+ return TRUE;
+ case WM_PRINTCLIENT:
+ return ALF_ProgressGauge_PrintClient(priv, hwnd, (HDC)wparam);
+ case WM_TIMER:
+ return ALF_ProgressGauge_Timer(priv, hwnd, wparam, lparam);
+ case PBM_SETMARQUEE:
+ return ALF_ProgressGauge_SetMarquee(priv, hwnd, wparam, (UINT)lparam);
+ case WM_STYLECHANGED:
+ return ALF_ProgressGauge_StyleChanged(priv, hwnd, wparam, lparam);
+ case WM_SIZE:
+ return ALF_ProgressGauge_Size(priv, hwnd, wparam, lparam);
+ case WM_SETFONT:
+ return ALF_ProgressGauge_SetFont(priv, hwnd, (HFONT)wparam);
+ case WM_GETFONT:
+ return ALF_ProgressGauge_GetFont(priv, hwnd);
+ case WM_SETTEXT:
+ return ALF_ProgressGauge_SetText(priv, hwnd, (const TCHAR *)lparam);
+ case ALF_WM_QUERYSIZE:
+ return ALF_ProgressGauge_CalcSize(priv, hwnd, (SIZE *)lparam);
+ }
+
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+HWND
+ALF_AddNativeProgressBar(HWND parent, WORD id, int x, int y)
+{
+ HWND hwnd = CreateWindowEx(0,
+ PROGRESS_CLASS,
+ NULL,
+ WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
+ 0, 0, 10, 10,
+ parent,
+ (HMENU)(ULONG_PTR)id,
+ ALF_HINSTANCE,
+ NULL);
+
+ ALF_AddControl(parent, x, y, hwnd, 12000, 1125, ALF_LAYOUT_TRANSPARENTBG);
+
+ return hwnd;
+}
+
+HWND
+ALF_AddProgressGauge(HWND parent, WORD id, int x, int y)
+{
+ HWND hwnd = ALF_CreateControlWindow(0,
+ TEXT(""),
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, 10, 10,
+ parent,
+ (HMENU)(ULONG_PTR)id,
+ ALF_ProgressGauge_WindowProc,
+ NULL);
+
+ ALF_AddControl(parent, x, y, hwnd, 12000, 1125, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT);
+
+ return hwnd;
+}
+
+HWND
+ALF_AddProgressBar(HWND parent, WORD id, int x, int y)
+{
+ if (ALF_Compat_IsComCtlV6())
+ return ALF_AddNativeProgressBar(parent, id, x, y);
+ else
+ return ALF_AddProgressGauge(parent, id, x, y);
+}
+
+void
+ALF_Progress_Range(HWND progress, int *pMin, int *pMax)
+{
+ PBRANGE r = { 0, 0 };
+ SendMessage(progress, PBM_GETRANGE, 0, (LPARAM)&r);
+
+ if (pMin) *pMin = r.iLow;
+ if (pMax) *pMax = r.iHigh;
+}
+
+void
+ALF_Progress_SetRange(HWND progress, int min, int max)
+{
+ PBRANGE r = { min, max };
+ if (!SendMessage(progress, ALF_WM_PROGRESS_SETRANGE, 0, (LPARAM)&r)) {
+ SendMessage(progress, PBM_SETRANGE32, (WPARAM)min, (LPARAM)max);
+ }
+}
+
+int
+ALF_Progress_Value(HWND progress)
+{
+ return (int)SendMessage(progress, PBM_GETPOS, 0, 0);
+}
+
+void
+ALF_Progress_SetValue(HWND progress, int value)
+{
+ if (!SendMessage(progress, ALF_WM_PROGRESS_SETPOS, 0, (LPARAM)value))
+ SendMessage(progress, PBM_SETPOS, (WPARAM)value, 0);
+}
+
+enum ALFProgressState
+ALF_Progress_State(HWND progress)
+{
+ LRESULT state = SendMessage(progress, /*PBM_GETSTATE*/(WM_USER + 17), 0, 0);
+ BOOL marquee = GetWindowLong(progress, GWL_STYLE) & PBS_MARQUEE;
+
+ if (marquee) {
+ return ALF_PROGRESS_STATE_INDETERMINATE;
+ } else if (state == 0) {
+ // state not supported by this progress bar
+ return ALF_PROGRESS_STATE_NORMAL;
+ } else {
+ // enum values have been chosen to be equal to PBST_*
+ return (enum ALFProgressState)state;
+ }
+}
+
+void
+ALF_Progress_SetState(HWND progress, enum ALFProgressState state)
+{
+ switch (state) {
+ case ALF_PROGRESS_STATE_INDETERMINATE:
+ SetWindowLong(progress, GWL_STYLE, GetWindowLong(progress, GWL_STYLE) | PBS_MARQUEE);
+ SendMessage(progress, WM_USER+16/*PBM_SETSTATE*/, 1/*PBST_NORMAL*/, 0);
+ SendMessage(progress, PBM_SETMARQUEE, TRUE, 0);
+ break;
+ default:
+ if (GetWindowLong(progress, GWL_STYLE) & PBS_MARQUEE) {
+ SendMessage(progress, PBM_SETMARQUEE, FALSE, 0);
+ SetWindowLong(progress, GWL_STYLE, GetWindowLong(progress, GWL_STYLE) & ~PBS_MARQUEE);
+ }
+ // enum values have been chosen to equal to PBST_*
+ SendMessage(progress, WM_USER+16/*PBM_SETSTATE*/, (WPARAM)state, 0);
+ break;
+ }
+}