From 261902fcae887249ffa03114e49afdef85fb4442 Mon Sep 17 00:00:00 2001 From: Jonas Kümmerlin Date: Tue, 1 Jan 2019 22:12:51 +0100 Subject: first try at combobox implementation --- alf/alf.cpp | 36 ++++--- alf/alf.h | 31 ++++++ alf/alfcombobox.cpp | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++ alf/alfcompat.cpp | 11 +++ alf/alfcompat.h | 2 + alf/alfpriv.h | 7 ++ 6 files changed, 343 insertions(+), 14 deletions(-) create mode 100644 alf/alfcombobox.cpp (limited to 'alf') diff --git a/alf/alf.cpp b/alf/alf.cpp index ae6c8f9..9d1379c 100644 --- a/alf/alf.cpp +++ b/alf/alf.cpp @@ -519,10 +519,31 @@ ALF_CreateApplication(HINSTANCE hInstance) InitCommonControlsEx(&icc); app->compatFn = ALF_CreateCompatFuncTable(); + ALF_RegisterComboClass(app); return app; } + +void +ALF_BuildRandomClassName(const TCHAR *prefix, TCHAR *buf) +{ + UUID uuid; + UuidCreate(&uuid); + +#ifdef UNICODE + unsigned short *uuidstr = NULL; +#else + unsigned char *uuidstr = NULL; +#endif + UuidToString(&uuid, &uuidstr); + + lstrcpy(buf, prefix); + lstrcat(buf, (LPCTSTR)uuidstr); + + RpcStringFree(&uuidstr); +} + LPTSTR ALF_RegisterWindowClass(ALFAPP app, const ALFWindowClassParams *params) { @@ -535,20 +556,7 @@ ALF_RegisterWindowClass(ALFAPP app, const ALFWindowClassParams *params) ZeroMemory(classNameBuf, sizeof(classNameBuf)); classNamePtr = classNameBuf; - UUID uuid; - UuidCreate(&uuid); - -#ifdef UNICODE - unsigned short *uuidstr = NULL; -#else - unsigned char *uuidstr = NULL; -#endif - UuidToString(&uuid, &uuidstr); - - lstrcpy(classNameBuf, TEXT("ALFWindow.")); - lstrcat(classNameBuf, (LPCTSTR)uuidstr); - - RpcStringFree(&uuidstr); + ALF_BuildRandomClassName(TEXT("ALFWindow."), classNameBuf); } cls.style = params->classStyle; diff --git a/alf/alf.h b/alf/alf.h index 6d36aff..c6a7b77 100644 --- a/alf/alf.h +++ b/alf/alf.h @@ -142,6 +142,37 @@ ALF_SetModalResult(HWND win, int result); int ALF_GetModalResult(HWND win); +// combo box + +HWND +ALF_AddEditableComboBox(HWND win, WORD id, UINT x, UINT y, const TCHAR *defaultText); + +HWND +ALF_AddSelectionOnlyComboBox(HWND win, WORD id, UINT x, UINT y); + +int /* index */ +ALF_ComboBoxAddString(HWND combo, const TCHAR *text); + +void +ALF_ComboBoxInsertString(HWND combo, int index, const TCHAR *text); + +void +ALF_ComboBoxRemoveString(HWND combo, int index); + +int +ALF_ComboBoxCurrentIndex(HWND combo); + +void +ALF_ComboBoxSetCurrentIndex(HWND combo); + +// NOTE: call HeapFree +TCHAR * +ALF_ComboBoxCurrentText(HWND combo); + +void +ALF_ComboBoxSetText(HWND combo, const TCHAR *text); + + #ifdef __cplusplus } // extern C #endif diff --git a/alf/alfcombobox.cpp b/alf/alfcombobox.cpp new file mode 100644 index 0000000..43c547a --- /dev/null +++ b/alf/alfcombobox.cpp @@ -0,0 +1,270 @@ +#include "alfpriv.h" + +typedef struct { + ALFAPP app; + DWORD comboStyle; +} ALFComboCreateParams; + +static int +ALF__ComboItemHeight(HWND hwnd) +{ + int height = 0; + HDC hDc = GetDC(hwnd); + HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); + if (font) { + HFONT oldfont = SelectFont(hDc, font); + + TEXTMETRIC tm; + ZeroMemory(&tm, sizeof(tm)); + + if (GetTextMetrics(hDc, &tm)) { + height = tm.tmHeight; + } + + SelectFont(hDc, oldfont); + } + + ReleaseDC(hwnd, hDc); + return height; +} + +static LRESULT CALLBACK +ALF__ComboWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_NCCREATE) { + ALFComboCreateParams *params = (ALFComboCreateParams*)((CREATESTRUCT*)lParam)->lpCreateParams; + + SetWindowLongPtr(hwnd, 0, (LONG_PTR)params->app); + } + + ALFAPP app = (ALFAPP)GetWindowLongPtr(hwnd, 0); + HWND hwndChild = (HWND)GetWindowLongPtr(hwnd, sizeof(void*)); + + if (uMsg == WM_CREATE) { + ALFComboCreateParams *params = (ALFComboCreateParams*)((CREATESTRUCT*)lParam)->lpCreateParams; + HWND hwndCombo = CreateWindowEx(0, + TEXT("COMBOBOX"), + TEXT(""), + WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | params->comboStyle, + 0, 0, 0, 200 /* FIXME needed for commctl32 v5, what is the best value here? */, + hwnd, + (HMENU) GetWindowLongPtrW(hwnd, GWLP_ID), + ((CREATESTRUCT*)lParam)->hInstance, + NULL); + + SetWindowLongPtr(hwnd, sizeof(void*), (LONG_PTR)hwndCombo); + } + if (uMsg == WM_ENABLE && hwndChild) { + EnableWindow(hwndChild, (BOOL)wParam); + return 0; + } + if (uMsg == WM_SETTEXT && hwndChild) { + return SendMessage(hwndChild, WM_SETTEXT, wParam, lParam); + } + if (uMsg == WM_GETTEXTLENGTH && hwndChild) { + return SendMessage(hwndChild, WM_GETTEXTLENGTH, wParam, lParam); + } + if (uMsg == WM_GETTEXT && hwndChild) { + return SendMessage(hwndChild, WM_GETTEXT, wParam, lParam); + } + if (uMsg == WM_SETFONT && hwndChild) { + SendMessage(hwndChild, WM_SETFONT, wParam, lParam); + + int h = ALF__ComboItemHeight(hwnd); + if (h) + SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)0, h); + + return 0; + } + if (uMsg == WM_GETFONT && hwndChild) { + return SendMessage(hwndChild, WM_GETFONT, wParam, lParam); + } + if (uMsg == CB_ADDSTRING && hwndChild) { + return SendMessage(hwndChild, CB_ADDSTRING, wParam, lParam); + } + + if (uMsg == ALF_WM_QUERYSIZE) { + HDC hDc = GetDC(hwnd); + HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); + + if (font) { + HFONT oldfont = SelectFont(hDc, font); + + TEXTMETRIC tm; + ZeroMemory(&tm, sizeof(tm)); + + if (GetTextMetrics(hDc, &tm)) { + SIZE *ps = (SIZE*)lParam; + if (!ps->cx) { + ps->cx = ALF_CentipointsToPixels(GetParent(hwnd), 12000); + } + + if (!ps->cy) { + ps->cy = tm.tmHeight + 2*app->compatFn->GetSystemMetricsForDpi( + SM_CYEDGE, ALF_CentipointsToPixels(GetParent(hwnd), 7200)) + + 4 /* padding internal to the edit control */ + + 2 /* external padding to line up with themed button */; + } + } + + SelectFont(hDc, oldfont); + } + + ReleaseDC(hwnd, hDc); + return 0; + } + + if (uMsg == WM_MEASUREITEM) { + PMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam; + + int h = ALF__ComboItemHeight(hwnd); + if (h) + lpmis->itemHeight = h; + + return TRUE; + } + + if (uMsg == WM_DRAWITEM) { + LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam; + + if (lpdis->itemID != (UINT)-1) { // Empty item + DWORD clrForeground = SetTextColor(lpdis->hDC, + GetSysColor(lpdis->itemState & (ODS_SELECTED | ODS_FOCUS) ? + COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); + + DWORD clrBackground = SetBkColor(lpdis->hDC, + GetSysColor(lpdis->itemState & (ODS_SELECTED | ODS_FOCUS) ? + COLOR_HIGHLIGHT : COLOR_WINDOW)); + + LONG y = lpdis->rcItem.top; + LONG x = lpdis->rcItem.left; + + // add left padding like an edit control + TEXTMETRIC tm; + ZeroMemory(&tm, sizeof(tm)); + if (GetTextMetrics(lpdis->hDC, &tm)) { + if (tm.tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE)) + x += ALF_GetAveCharWidth(lpdis->hDC) / 2; + } + + // Get and display the text for the list item. + int len = (int)SendMessage(lpdis->hwndItem, CB_GETLBTEXTLEN, (WPARAM)lpdis->itemID, 0); + if (len != CB_ERR) { + TCHAR *buf = (TCHAR*)LocalAlloc(LPTR, (len+1) * sizeof(TCHAR)); + + SendMessage(lpdis->hwndItem, CB_GETLBTEXT, + lpdis->itemID, (LPARAM)buf); + + ExtTextOut(lpdis->hDC, x, y, + ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem, + buf, lstrlen(buf), NULL); + + LocalFree(buf); + } + + // Restore the previous colors. + SetTextColor(lpdis->hDC, clrForeground); + SetBkColor(lpdis->hDC, clrBackground); + } + + // If the item has the focus, draw the focus rectangle. + if ((lpdis->itemState & ODS_FOCUS) && !(lpdis->itemState & ODS_NOFOCUSRECT)) + DrawFocusRect(lpdis->hDC, &lpdis->rcItem); + + return TRUE; + } + + if (uMsg == WM_WINDOWPOSCHANGED && hwndChild) { + WINDOWPOS *pos = (WINDOWPOS *)lParam; + if (!(pos->flags & SWP_NOSIZE)) { + RECT r; + GetWindowRect(hwndChild, &r); + SetWindowPos(hwndChild, NULL, 0, 1, pos->cx, r.bottom - r.top, SWP_NOZORDER|SWP_NOACTIVATE); + + SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)-1, pos->cy - 8); + + int h = ALF__ComboItemHeight(hwnd); + if (h) + SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)0, h); + } + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + + +void +ALF_RegisterComboClass(ALFAPP app) +{ + WNDCLASS cls; + ZeroMemory(&cls, sizeof(cls)); + + TCHAR classNameBuf[256]; + ALF_BuildRandomClassName(TEXT("ALFComboBox."), classNameBuf); + + cls.hInstance = app->hInstance; + cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); + cls.lpszClassName = classNameBuf; + cls.cbWndExtra = sizeof(void*)*2; + cls.lpfnWndProc = ALF__ComboWindowProc; + + ATOM classatom = RegisterClass(&cls); + if (!classatom) + MessageBox(NULL, TEXT("FATAL: Could not register Combo class"), NULL, MB_OK); + + app->comboClass = MAKEINTATOM(classatom); +} + +static HWND +ALF_InternalAddComboBox(HWND win, WORD id, UINT x, UINT y, DWORD style, const TCHAR *defaultText) +{ + ALFComboCreateParams cp; + cp.app = ALF_ApplicationFromWindow(win); + cp.comboStyle = style; + + HWND hwndCombo = CreateWindowEx(WS_EX_CONTROLPARENT, + cp.app->comboClass, + TEXT(""), + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, + 0, 0, 0, 0, + win, + (HMENU)(int)id, + (HINSTANCE)GetWindowLongPtr(win, GWLP_HINSTANCE), + &cp); + + if (defaultText) + SetWindowText(hwndCombo, defaultText); + + ALFAddWidgetParams p; + ZeroMemory(&p, sizeof(p)); + p.hwnd = hwndCombo; + p.x = x; + p.y = y; + p.width = 0; + p.height = 0; + p.flags = ALF_QUERYSIZE | ALF_MESSAGEFONT; + + ALF_AddWidgetEx(win, &p); + + return hwndCombo; +} + +HWND +ALF_AddEditableComboBox(HWND win, WORD id, UINT x, UINT y, const TCHAR *defaultText) +{ + return ALF_InternalAddComboBox(win, id, x, y, CBS_DROPDOWN, defaultText); +} + +HWND +ALF_AddSelectionOnlyComboBox(HWND win, WORD id, UINT x, UINT y) +{ + return ALF_InternalAddComboBox(win, id, x, y, CBS_DROPDOWNLIST, NULL); +} + +int /* index */ +ALF_ComboBoxAddString(HWND combo, const TCHAR *text) +{ + return (int)SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)text); +} + diff --git a/alf/alfcompat.cpp b/alf/alfcompat.cpp index e6a9851..269b9f4 100644 --- a/alf/alfcompat.cpp +++ b/alf/alfcompat.cpp @@ -123,3 +123,14 @@ ALF_CreateCompatFuncTable(void) return compatfn; } + +long +ALF_GetAveCharWidth(HDC hdc) +{ + // see: HOWTO: Calculate Dialog Units When Not Using the System Font + + SIZE s; + GetTextExtentPoint32A(hdc, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &s); + + return (s.cx / 26 + 1) / 2; +} diff --git a/alf/alfcompat.h b/alf/alfcompat.h index 8319dac..846590c 100644 --- a/alf/alfcompat.h +++ b/alf/alfcompat.h @@ -20,3 +20,5 @@ typedef struct { ALFCompatFunctions * ALF_CreateCompatFuncTable(void); +long +ALF_GetAveCharWidth(HDC hdc); diff --git a/alf/alfpriv.h b/alf/alfpriv.h index a398127..0d5be2f 100644 --- a/alf/alfpriv.h +++ b/alf/alfpriv.h @@ -61,7 +61,14 @@ typedef struct { struct ALFAppPriv { HINSTANCE hInstance; ALFCompatFunctions *compatFn; + TCHAR *comboClass; }; void ALF_UpdateFontsPriv(HWND hwnd, ALFWindowPriv *priv); + +void +ALF_RegisterComboClass(ALFAPP app); + +void +ALF_BuildRandomClassName(const TCHAR *prefix, TCHAR *buf); -- cgit v1.2.3