summaryrefslogtreecommitdiff
path: root/alf/alftoplevel.cpp
diff options
context:
space:
mode:
authorJonas Kümmerlin <jonas@kuemmerlin.eu>2020-05-30 16:16:50 +0200
committerJonas Kümmerlin <jonas@kuemmerlin.eu>2020-05-30 16:17:38 +0200
commit379e9fd3a72fbf5f136443284bc6a2d81bb828fa (patch)
tree74c0874be87dd342e7baf36a60b60b1c8f49f9ce /alf/alftoplevel.cpp
parentf9cfbb8870a023478d52fd03c90abcbed9c49304 (diff)
toplevel: rename and reorganize functions
Diffstat (limited to 'alf/alftoplevel.cpp')
-rw-r--r--alf/alftoplevel.cpp505
1 files changed, 505 insertions, 0 deletions
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)&params);
+}
+
+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);
+}