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/alfcombobox.cpp | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 alf/alfcombobox.cpp (limited to 'alf/alfcombobox.cpp') 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); +} + -- cgit v1.2.3