summaryrefslogtreecommitdiff
path: root/alf/alfcombobox.cpp
diff options
context:
space:
mode:
authorJonas Kümmerlin <jonas@kuemmerlin.eu>2019-01-01 22:12:51 +0100
committerJonas Kümmerlin <jonas@kuemmerlin.eu>2019-01-01 22:12:51 +0100
commit261902fcae887249ffa03114e49afdef85fb4442 (patch)
treefbc81a4581dc762d7194f7ee5b9b7b2790319dbf /alf/alfcombobox.cpp
parentdbd2fb78dec385f0abfe07fd4609dc8f444bdefd (diff)
first try at combobox implementation
Diffstat (limited to 'alf/alfcombobox.cpp')
-rw-r--r--alf/alfcombobox.cpp270
1 files changed, 270 insertions, 0 deletions
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);
+}
+