From 379e9fd3a72fbf5f136443284bc6a2d81bb828fa Mon Sep 17 00:00:00 2001 From: Jonas Kümmerlin Date: Sat, 30 May 2020 16:16:50 +0200 Subject: toplevel: rename and reorganize functions --- alf/alftoplevel.cpp | 505 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 505 insertions(+) create mode 100644 alf/alftoplevel.cpp (limited to 'alf/alftoplevel.cpp') diff --git a/alf/alftoplevel.cpp b/alf/alftoplevel.cpp new file mode 100644 index 0000000..99f78e5 --- /dev/null +++ b/alf/alftoplevel.cpp @@ -0,0 +1,505 @@ +#include "alfpriv.h" + +typedef struct { + ALFToplevelVTable *vtbl; + void *closure; + LPARAM modalResult; + ALFLayout layout; + HWND hwndFocus; + HFONT hMessageFont; +} ALFToplevelPriv; + +struct ALFToplevel_CreateParams{ + ALFToplevelVTable *vtbl; + void *closure; +}; + +static TCHAR _alf_toplevelClass[28] = {0}; + +static void +ALF_InitializeToplevelPriv(HWND hwnd, ALFToplevelPriv *priv) +{ + (void)hwnd; + + ALF_Layout_Init(&priv->layout); + priv->layout.bgcolor = ALF_COLOR_SYS(COLOR_BTNFACE); +} + +static void +ALF_DestroyToplevelPriv(ALFToplevelPriv *priv) +{ + if (priv->vtbl && priv->vtbl->postdestroy) + priv->vtbl->postdestroy(priv->closure); + + ALF_Layout_Clear(&priv->layout); + + if (priv->hMessageFont) + DeleteObject(priv->hMessageFont); + + ALF_Free(priv); +} + +static void +ALF_UpdateFontsPriv(HWND win, ALFToplevelPriv *priv) +{ + // XXX: SystemParametersInfoForDpi is Unicode-only and needs Vista+ NONCLIENTMETRICS, + ALF_NONCLIENTMETRICSW_VISTA ncm; + ZeroMemory(&ncm, sizeof(ncm)); + ncm.cbSize = sizeof(ncm); + + LOGFONT lfMessageFont; + ZeroMemory(&lfMessageFont, sizeof(lfMessageFont)); + + if (ALF_Compat_SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, (UINT)priv->layout.dpi)) { +#ifdef UNICODE + lfMessageFont = ncm.lfMessageFont; +#else + ALF_Compat_LogFontWtoA(&ncm.lfMessageFont, &lfMessageFont); +#endif + } else { + // FIXME! fallback to default font, 8pt MS Shell Dlg + ZeroMemory(&lfMessageFont, sizeof(lfMessageFont)); + + lfMessageFont.lfHeight = -MulDiv(8, priv->layout.dpi, 72); + lstrcpy(lfMessageFont.lfFaceName, TEXT("MS Shell Dlg")); + } + + if (priv->hMessageFont) { + DeleteObject(priv->hMessageFont); + } + + priv->hMessageFont = CreateFontIndirect(&lfMessageFont); + + SendMessage(win, WM_SETFONT, (WPARAM)priv->hMessageFont, (LPARAM)1); +} + + +/*static BOOL +ALF_PreTranslateMessagePriv(HWND win, ALFToplevelPriv *priv, MSG *message) +{ + BOOL ret = FALSE; + + if (priv->vtbl->pretranslatemessage) + ret = priv->vtbl->pretranslatemessage(priv->closure, win, message); + + if (!ret) + ret = IsDialogMessage(win, message); + + return ret; +}*/ + +void +ALF_Toplevel_EnsureBigEnough(HWND toplevel) +{ + MINMAXINFO i; + ZeroMemory(&i, sizeof(i)); + SendMessage(toplevel, WM_GETMINMAXINFO, 0, (LPARAM)&i); + + WINDOWPLACEMENT wp; + ZeroMemory(&wp, sizeof(wp)); + wp.length = sizeof(wp); + + GetWindowPlacement(toplevel, &wp); + + if (wp.rcNormalPosition.right - wp.rcNormalPosition.left < i.ptMinTrackSize.x || + wp.rcNormalPosition.bottom - wp.rcNormalPosition.top < i.ptMinTrackSize.y) { + if (wp.rcNormalPosition.right - wp.rcNormalPosition.left < i.ptMinTrackSize.x) + wp.rcNormalPosition.right = wp.rcNormalPosition.left + i.ptMinTrackSize.x; + if (wp.rcNormalPosition.bottom - wp.rcNormalPosition.top < i.ptMinTrackSize.y) + wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + i.ptMinTrackSize.y; + + SetWindowPlacement(toplevel, &wp); + } +} + +static INT_PTR CALLBACK +ALF_Toplevel_DlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + (void)hwnd; + (void)msg; + (void)wparam; + (void)lparam; + + return FALSE; +} + + +static LRESULT CALLBACK +ALF_Toplevel_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (msg == WM_CREATE) { + ALFToplevelPriv *priv = ALF_New(ALFToplevelPriv, 1); + SetWindowLongPtr(hwnd, DLGWINDOWEXTRA, (LONG_PTR)priv); + ALF_InitializeToplevelPriv(hwnd, priv); + + int dpi = (int)ALF_Compat_GetDpiForWindow(hwnd); + SendMessage(hwnd, ALF_WM_DPICHANGE, 0, (LPARAM)dpi); // will also update fonts + } + + ALFToplevelPriv *priv = (ALFToplevelPriv*)(void*)GetWindowLongPtr(hwnd, DLGWINDOWEXTRA); + if (priv != NULL) { + if (priv->vtbl && priv->vtbl->message) { + return priv->vtbl->message(priv->closure, hwnd, msg, wparam, lparam); + } else { + return ALF_Toplevel_DefWindowProc(hwnd, msg, wparam, lparam); + } + } else { + return DefDlgProc(hwnd, msg, wparam, lparam); + } +} + +static void +ALF_Toplevel_Paint(ALFToplevelPriv *priv, HWND hwnd, HDC dc, RECT *r) +{ + if (priv->vtbl && priv->vtbl->paint) { + priv->vtbl->paint(priv->closure, hwnd, dc, r); + } else { + ALF_FillRect(dc, r, priv->layout.bgcolor); + } +} + +LRESULT +ALF_Toplevel_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + ALFToplevelPriv *priv = (ALFToplevelPriv*)GetWindowLongPtr(hwnd, DLGWINDOWEXTRA); + + if (msg == WM_INITDIALOG) { + struct ALFToplevel_CreateParams *params = (struct ALFToplevel_CreateParams *)lparam; + + priv->vtbl = params->vtbl; + priv->closure = params->closure; + + BOOL retval = TRUE; + if (priv->vtbl && priv->vtbl->initialize) { + retval = priv->vtbl->initialize(priv->closure, hwnd); + } + + ALF_Toplevel_EnsureBigEnough(hwnd); + + return retval; + } + + if (msg == ALF_WM_GETMODALRESULT) { + return (LRESULT)priv->modalResult; + } + if (msg == ALF_WM_SETMODALRESULT) { + priv->modalResult = lparam; + return 0; + } + + if (msg == ALF_WM_GETDPI) { + return (LRESULT)priv->layout.dpi; + } + + if (msg == ALF_WM_UPDATEFONTS) { + ALF_UpdateFontsPriv(hwnd, priv); + return 0; + } + + if (msg == WM_ERASEBKGND) { + return TRUE; // handled in WM_PAINT + } + + if (msg == WM_PRINTCLIENT) { + RECT r = { 0, 0, 0, 0 }; + GetClientRect(hwnd, &r); + + ALF_Toplevel_Paint(priv, hwnd, (HDC)wparam, &r); + return TRUE; + } + + if (msg == WM_PAINT) { + PAINTSTRUCT ps; + HDC dc = BeginPaint(hwnd, &ps); + + ALF_Toplevel_Paint(priv, hwnd, dc, &ps.rcPaint); + + EndPaint(hwnd, &ps); + + return 0; + } + + if (msg == WM_COMMAND) { + HWND source = (HWND)lparam; + WORD code = HIWORD(wparam); + WORD id = LOWORD(wparam); + LRESULT ret = 0; + if (source != 0) + ret = SendMessage(source, 0x2000 + WM_COMMAND, wparam, lparam); + + if (ret == 0 && priv->vtbl && priv->vtbl->command) + ret = priv->vtbl->command(priv->closure, hwnd, code, id, source); + + return ret; + } + + if (msg == WM_NOTIFY) { + NMHDR *nmhdr = (NMHDR *)lparam; + LRESULT ret = 0; + + if (nmhdr->hwndFrom) + ret = SendMessage(nmhdr->hwndFrom, 0x2000 + WM_NOTIFY, wparam, lparam); + + if (ret == 0 && priv->vtbl && priv->vtbl->notify) + ret = priv->vtbl->notify(priv->closure, hwnd, wparam, nmhdr); + + return ret; + } + + if (msg == WM_DRAWITEM) { + LPDRAWITEMSTRUCT dis = (DRAWITEMSTRUCT *)lparam; + LRESULT ret = 0; + if (wparam && dis->hwndItem) { + ret = SendMessage(dis->hwndItem, 0x2000 + WM_DRAWITEM, wparam, lparam); + } + + if (ret) + return ret; + } + + if (msg == WM_CTLCOLORSTATIC) { + LRESULT ret = SendMessage((HWND)lparam, 0x2000 + WM_CTLCOLORSTATIC, wparam, lparam); + + if (ret) { + return ret; + } else { + SetBkColor((HDC)wparam, GetSysColor(COLOR_BTNFACE)); + SetTextColor((HDC)wparam, GetSysColor(COLOR_BTNTEXT)); + return (LRESULT)GetSysColorBrush(COLOR_BTNFACE); + } + } + + if (msg == WM_CTLCOLORBTN) { + LRESULT ret = SendMessage((HWND)lparam, 0x2000 + WM_CTLCOLORBTN, wparam, lparam); + + if (ret) { + return ret; + } else { + SetBkColor((HDC)wparam, GetSysColor(COLOR_BTNFACE)); + SetTextColor((HDC)wparam, GetSysColor(COLOR_BTNTEXT)); + return (LRESULT)GetSysColorBrush(COLOR_BTNFACE); + } + } + + if (msg == WM_SIZE) { + ALF_Layout_Apply(&priv->layout, hwnd); + } + + if (msg == WM_SHOWWINDOW) { + if (wparam) + ALF_Layout_Validate(&priv->layout, hwnd); + } + + if (msg == WM_GETMINMAXINFO) { + RECT tmp; + ZeroMemory(&tmp, sizeof(tmp)); + + SIZE s; + ZeroMemory(&s, sizeof(s)); + ALF_Layout_GetMinSize(&priv->layout, hwnd, &s); + + tmp.right = s.cx; + tmp.bottom = s.cy; + + if (ALF_Compat_AdjustWindowRectExForDpi( + &tmp, + (DWORD)GetWindowLong(hwnd, GWL_STYLE), + GetMenu(hwnd) != NULL, + (DWORD)GetWindowLong(hwnd, GWL_EXSTYLE), + (UINT)priv->layout.dpi)) { + MINMAXINFO *i = (MINMAXINFO *)lparam; + i->ptMinTrackSize.x = tmp.right - tmp.left; + i->ptMinTrackSize.y = tmp.bottom - tmp.top; + return 0; + } + } + + if (msg == WM_DESTROY && priv->vtbl && priv->vtbl->destroy) { + priv->vtbl->destroy(priv->closure, hwnd); + } + + if (msg == WM_CLOSE) { + if (!priv->vtbl || !priv->vtbl->close || !priv->vtbl->close(priv->closure, hwnd)) { + priv->modalResult = 2; + ShowWindow(hwnd, FALSE); + } + return TRUE; + } + + if (msg == WM_NCDESTROY) { + SetWindowLongPtr(hwnd, DLGWINDOWEXTRA, 0); + ALF_DestroyToplevelPriv(priv); + } + + if (msg == WM_DPICHANGED) { + int dpi = LOWORD(wparam); + SendMessage(hwnd, ALF_WM_DPICHANGE, 0, (LPARAM)dpi); // will also update fonts and invalidate layout + + RECT *r = (RECT*)lparam; + SetWindowPos(hwnd, NULL, r->left, r->top, r->right-r->left, r->bottom-r->top, SWP_NOACTIVATE|SWP_NOZORDER); + } + + if (msg == WM_THEMECHANGED || msg == WM_SETTINGCHANGE) { + ALF_UpdateFontsPriv(hwnd, priv); + ALF_Layout_Invalidate(&priv->layout, hwnd); + ALF_InvalidateBackground(hwnd); + InvalidateRect(hwnd, NULL, TRUE); + } + + if (msg == WM_WINDOWPOSCHANGED) { + if (priv->vtbl && priv->vtbl->windowposchanged) { + priv->vtbl->windowposchanged(priv->closure, hwnd, (WINDOWPOS *)lparam); + } + } + + LRESULT ret = 0; + if (ALF_Layout_HandleMessage(&priv->layout, hwnd, msg, wparam, lparam, &ret)) { + // if the layout was changed, our current size might be too small + if (msg == ALF_WM_VALIDATELAYOUT) { + ALF_Toplevel_EnsureBigEnough(hwnd); + } + + if (msg == ALF_WM_DPICHANGE) { + ALF_UpdateFontsPriv(hwnd, priv); + } + + return ret; + } + + return DefDlgProc(hwnd, msg, wparam, lparam); +} + +void +ALF_RegisterToplevelClass(void) +{ + WNDCLASS cls; + ZeroMemory(&cls, sizeof(cls)); + + wsprintf(_alf_toplevelClass, TEXT("ALFWindow.%p"), (ULONG_PTR)&_alf_toplevelClass[0]); + + cls.style = 0; + cls.hInstance = ALF_HINSTANCE; + cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW); + cls.hbrBackground = NULL; + cls.lpszClassName = _alf_toplevelClass; + cls.cbWndExtra = DLGWINDOWEXTRA + sizeof(void*); + cls.cbClsExtra = 0; + cls.lpfnWndProc = ALF_Toplevel_WindowProc; + + ATOM classatom = RegisterClass(&cls); + if (!classatom) { + MessageBox(NULL, TEXT("FATAL: Couldn't register toplevel window class"), TEXT("DEBUG"), MB_ICONHAND|MB_OK); + } +} + +void +ALF_UnregisterToplevelClass(void) +{ + UnregisterClass(_alf_toplevelClass, ALF_HINSTANCE); +} + +HWND +ALF_CreateToplevelWindow(HWND hwndParent, ALFToplevelVTable *vtbl, void *closure) +{ +#pragma pack(push, 1) + struct { + DLGTEMPLATE t; + WCHAR s[32]; + } t; +#pragma pack(pop) + ZeroMemory(&t, sizeof(t)); + t.t.style = WS_OVERLAPPEDWINDOW; + t.t.dwExtendedStyle = 0; + t.t.x = 0; + t.t.y = 0; + t.t.cx = 10; + t.t.cy = 10; + t.t.cdit = 0; + + // NOTE: this only works because we know the window class name is all ascii + for (int i = 0; _alf_toplevelClass[i]; ++i) { + t.s[i+1] = _alf_toplevelClass[i]; + } + + struct ALFToplevel_CreateParams params; + params.vtbl = vtbl; + params.closure = closure; + return CreateDialogIndirectParam(ALF_HINSTANCE, &t.t, hwndParent, ALF_Toplevel_DlgProc, (LPARAM)¶ms); +} + +void +ALF_Toplevel_SetDefaultButton(HWND win, WORD id) +{ + SendMessage(win, DM_SETDEFID, (WPARAM)id, 0); +} + +LPARAM +ALF_Toplevel_ShowModal(HWND win) +{ + MSG msg; + + ALF_Toplevel_SetModalResult(win, 0); + + // TODO: disable parent window + ShowWindow(win, SW_SHOW); + + while (GetMessage(&msg, NULL, 0, 0) > 0) { + // TODO: call application message hooks + // TODO: call preprocess message hook + + if (!IsDialogMessage(win, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + int mr = ALF_Toplevel_GetModalResult(win); + if (mr) + return mr; + } + + return 0; +} + + +void +ALF_Toplevel_SetModalResult(HWND win, LPARAM result) +{ + SendMessage(win, ALF_WM_SETMODALRESULT, 0, result); +} + +LPARAM +ALF_Toplevel_GetModalResult(HWND win) +{ + return (LPARAM)SendMessage(win, ALF_WM_GETMODALRESULT, 0, 0); +} + +void +ALF_Toplevel_Resize(HWND win, int cptWidth, int cptHeight) +{ + int dpi = ALF_GetDpi(win); + + int pxwidth = ALF_CentipointsToPixels(cptWidth, dpi); + int pxheight = ALF_CentipointsToPixels(cptHeight, dpi); + + ALF_Toplevel_ResizePx(win, pxwidth, pxheight); +} + +void +ALF_Toplevel_ResizePx(HWND win, int pxwidth, int pxheight) +{ + MINMAXINFO tmp = { + { 0, 0 }, + { pxwidth, pxheight }, + { 0, 0 }, + { pxwidth, pxheight }, + { pxwidth, pxheight } + }; + SendMessage(win, WM_GETMINMAXINFO, 0, (LPARAM)&tmp); + + if (tmp.ptMinTrackSize.x > pxwidth) + pxwidth = tmp.ptMinTrackSize.x; + if (tmp.ptMinTrackSize.y > pxheight) + pxheight = tmp.ptMinTrackSize.y; + + SetWindowPos(win, NULL, 0, 0, pxwidth, pxheight, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); +} -- cgit v1.2.3