#include "alfpriv.h" typedef struct { HWND hwndEdit; HWND hwndUpdown; int dpi; } ALFSpinBoxPriv; static void ALF_SpinBox_Initialize(ALFSpinBoxPriv *priv, HWND hwnd) { priv->dpi = 96; RECT rcClient; GetClientRect(hwnd, &rcClient); priv->hwndEdit = CreateWindowEx(ALF_Compat_Is40() ? WS_EX_CLIENTEDGE : 0, TEXT("EDIT"), TEXT("0"), WS_CHILD | WS_VISIBLE | WS_TABSTOP | (ALF_Compat_Is40() ? 0 : WS_BORDER), 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hwnd, NULL, ALF_HINSTANCE, NULL); priv->hwndUpdown = CreateWindowEx(0, TEXT("msctls_updown32"), NULL, WS_CHILD | WS_VISIBLE | (ALF_Compat_Is40() ? UDS_ALIGNRIGHT : WS_BORDER) | UDS_AUTOBUDDY | UDS_SETBUDDYINT | UDS_ARROWKEYS | UDS_NOTHOUSANDS, 0, 0, 0, 0, hwnd, NULL, ALF_HINSTANCE, NULL); } static void ALF_SpinBox_Destroy(ALFSpinBoxPriv *priv, HWND hwnd) { (void)priv; (void)hwnd; // nothing to do here (yet) // child windows are destroyed automatically } static void ALF_SpinBox_Resize(ALFSpinBoxPriv *priv, HWND hwnd) { (void)hwnd; RECT rcClient; GetClientRect(hwnd, &rcClient); int overlap = GetWindowLong(priv->hwndEdit, GWL_EXSTYLE) & WS_EX_CLIENTEDGE ? ALF_Compat_GetSystemMetricsForDpi(SM_CXBORDER, (UINT)priv->dpi) * 2 : ALF_Compat_GetSystemMetricsForDpi(SM_CXBORDER, (UINT)priv->dpi); int updownwidth = ALF_Compat_GetSystemMetricsForDpi(SM_CXVSCROLL, (UINT)priv->dpi) - ALF_Compat_GetSystemMetricsForDpi(SM_CXBORDER, (UINT)priv->dpi); if (updownwidth > rcClient.bottom - rcClient.top) updownwidth = rcClient.bottom - rcClient.top; RECT rcEdit = { 0, 0, rcClient.right - rcClient.left - updownwidth, rcClient.bottom - rcClient.top }; RECT rcUpdown = { rcEdit.right - overlap, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top }; HDWP hdwp = BeginDeferWindowPos(2); hdwp = DeferWindowPos(hdwp, priv->hwndEdit, NULL, rcEdit.left, rcEdit.top, rcEdit.right - rcEdit.left, rcEdit.bottom - rcEdit.top, SWP_NOACTIVATE|SWP_NOZORDER); hdwp = DeferWindowPos(hdwp, priv->hwndUpdown, NULL, rcUpdown.left, rcUpdown.top, rcUpdown.right - rcUpdown.left, rcUpdown.bottom - rcUpdown.top, SWP_NOACTIVATE|SWP_NOZORDER); EndDeferWindowPos(hdwp); } static void ALF_SpinBox_SetFont(ALFSpinBoxPriv *priv, HFONT font, LPARAM lparam) { SendMessage(priv->hwndEdit, WM_SETFONT, (WPARAM)font, lparam); } static LRESULT ALF_SpinBox_GetFont(ALFSpinBoxPriv *priv, WPARAM wp, LPARAM lp) { return SendMessage(priv->hwndEdit, WM_GETFONT, wp, lp); } static void ALF_SpinBox_HandleDpiChange(ALFSpinBoxPriv *priv, HWND hwnd, int newdpi) { if (priv->dpi == newdpi) return; priv->dpi = newdpi; ALF_SpinBox_Resize(priv, hwnd); } static LRESULT ALF_SpinBox_HandleGetRange(ALFSpinBoxPriv *priv, HWND hwnd) { (void)hwnd; return SendMessage(priv->hwndUpdown, UDM_GETRANGE, 0, 0); } static LRESULT ALF_SpinBox_HandleSetRange(ALFSpinBoxPriv *priv, HWND hwnd, LPARAM lparam) { (void)hwnd; SendMessage(priv->hwndUpdown, UDM_SETRANGE, 0, lparam); return 1; } static LRESULT ALF_SpinBox_HandleGetPos(ALFSpinBoxPriv *priv, HWND hwnd) { (void)hwnd; return SendMessage(priv->hwndUpdown, UDM_GETPOS, 0, 0); } static LRESULT ALF_SpinBox_HandleSetPos(ALFSpinBoxPriv *priv, HWND hwnd, LPARAM lparam) { (void)hwnd; SendMessage(priv->hwndUpdown, UDM_SETPOS, 0, lparam); return 1; } static LRESULT ALF_SpinBox_HandleCommand(ALFSpinBoxPriv *priv, HWND hwnd, WORD code, WORD id, HWND control) { if (control == priv->hwndEdit) { return SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLong(hwnd, GWL_ID), code), (LPARAM)hwnd); } else { return SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, code), (LPARAM)control); } } static LRESULT ALF_SpinBox_HandleNotify(ALFSpinBoxPriv *priv, HWND hwnd, WPARAM wparam, const NMHDR *pnmh) { if (pnmh->hwndFrom == priv->hwndUpdown && pnmh->code == UDN_DELTAPOS) { NMUPDOWN nmud = *(const NMUPDOWN *)pnmh; nmud.hdr.hwndFrom = hwnd; nmud.hdr.idFrom = (UINT_PTR)GetWindowLongPtr(hwnd, GWLP_ID); return SendMessage(GetParent(hwnd), WM_NOTIFY, nmud.hdr.idFrom, (LPARAM)&nmud); } else { return SendMessage(GetParent(hwnd), WM_NOTIFY, wparam, (LPARAM)pnmh); } } static LRESULT ALF_SpinBox_HandleGetEditControl(ALFSpinBoxPriv *priv) { return (LRESULT)priv->hwndEdit; } static LRESULT ALF_SpinBox_HandleGetUpDownControl(ALFSpinBoxPriv *priv) { return (LRESULT)priv->hwndUpdown; } static LRESULT ALF_SpinBox_HandlePaint(ALFSpinBoxPriv *priv, HWND hwnd) { (void)priv; PAINTSTRUCT ps; HDC dc = BeginPaint(hwnd, &ps); FillRect(dc, &ps.rcPaint, GetSysColorBrush(COLOR_BTNFACE)); EndPaint(hwnd, &ps); return 0; } static LRESULT WINAPI ALF_SpinBox_WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { ALFSpinBoxPriv *priv = (ALFSpinBoxPriv *)GetWindowLongPtr(hwnd, 0); if (!priv && msg == WM_NCCREATE) { priv = ALF_New(ALFSpinBoxPriv, 1); SetWindowLongPtr(hwnd, 0, (LONG_PTR)priv); } if (!priv) return DefWindowProc(hwnd, msg, wparam, lparam); switch (msg) { case WM_CREATE: ALF_SpinBox_Initialize(priv, hwnd); break; case WM_DESTROY: ALF_SpinBox_Destroy(priv, hwnd); SetWindowLongPtr(hwnd, 0, 0); ALF_Free(priv); priv = NULL; break; case WM_SETFONT: ALF_SpinBox_SetFont(priv, (HFONT)wparam, lparam); break; case WM_GETFONT: return ALF_SpinBox_GetFont(priv, wparam, lparam); case WM_SIZE: ALF_SpinBox_Resize(priv, hwnd); break; case ALF_WM_DPICHANGE: ALF_SpinBox_HandleDpiChange(priv, hwnd, (int)lparam); return 0; case ALF_WM_SPINBOX_GETRANGE: return ALF_SpinBox_HandleGetRange(priv, hwnd); case ALF_WM_SPINBOX_SETRANGE: return ALF_SpinBox_HandleSetRange(priv, hwnd, lparam); case ALF_WM_SPINBOX_GETPOS: return ALF_SpinBox_HandleGetPos(priv, hwnd); case ALF_WM_SPINBOX_SETPOS: return ALF_SpinBox_HandleSetPos(priv, hwnd, lparam); case ALF_WM_SPINBOX_GETEDIT: return ALF_SpinBox_HandleGetEditControl(priv); case ALF_WM_SPINBOX_GETUDCTL: return ALF_SpinBox_HandleGetUpDownControl(priv); case WM_GETTEXT: return (LRESULT)GetWindowText(priv->hwndEdit, (TCHAR *)lparam, (int)wparam); case WM_GETTEXTLENGTH: return (LRESULT)GetWindowTextLength(priv->hwndEdit); case WM_SETTEXT: return (LRESULT)SetWindowText(priv->hwndEdit, (const TCHAR *)lparam); case WM_COMMAND: return ALF_SpinBox_HandleCommand(priv, hwnd, HIWORD(wparam), LOWORD(wparam), (HWND)lparam); case WM_NOTIFY: return ALF_SpinBox_HandleNotify(priv, hwnd, wparam, (const NMHDR *)lparam); case WM_PAINT: return ALF_SpinBox_HandlePaint(priv, hwnd); case WM_ERASEBKGND: return 1; } if (ALF_ShouldMessageBubble(hwnd, msg, wparam, lparam)) return SendMessage(GetParent(hwnd), msg, wparam, lparam); return DefWindowProc(hwnd, msg, wparam, lparam); } HWND ALF_AddNumericSpinBox(HWND parent, WORD id, int x, int y, short val, short min, short max) { HWND hwnd = ALF_CreateControlWindow(WS_EX_CONTROLPARENT, TEXT(""), WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0, 0, 0, 0, parent, (HMENU)(ULONG_PTR)id, ALF_SpinBox_WndProc, NULL); ALF_AddControl(parent, x, y, hwnd, 0, 0, ALF_LAYOUT_SIZE_EDIT | ALF_LAYOUT_INHERITFONT | ALF_LAYOUT_SENDDPICHANGE); ALF_SpinBox_SetRange(hwnd, min, max); ALF_SpinBox_SetPos(hwnd, val); return hwnd; } BOOL ALF_SpinBox_Range(HWND spinbox, short *pmin, short *pmax) { LRESULT r = SendMessage(spinbox, ALF_WM_SPINBOX_GETRANGE, 0, 0); *pmin = GET_X_LPARAM(r); *pmax = GET_Y_LPARAM(r); return TRUE; } BOOL ALF_SpinBox_SetRange(HWND spinbox, short min, short max) { return (BOOL)SendMessage(spinbox, ALF_WM_SPINBOX_SETRANGE, 0, MAKELPARAM((WORD)max, (WORD)min)); } short ALF_SpinBox_Pos(HWND spinbox) { return (short)SendMessage(spinbox, ALF_WM_SPINBOX_GETPOS, 0, 0); } BOOL ALF_SpinBox_SetPos(HWND spinbox, short pos) { return (BOOL)SendMessage(spinbox, ALF_WM_SPINBOX_SETPOS, 0, (LPARAM)pos); } HWND ALF_SpinBox_EditControl(HWND spinbox) { return (HWND)SendMessage(spinbox, ALF_WM_SPINBOX_GETEDIT, 0, 0); } HWND ALF_SpinBox_UpDownControl(HWND spinbox) { return (HWND)SendMessage(spinbox, ALF_WM_SPINBOX_GETUDCTL, 0, 0); }