From 4b1817b432c95f19041902ce877e1a25869ac16a Mon Sep 17 00:00:00 2001 From: Jonas Kümmerlin Date: Sun, 31 May 2020 12:28:51 +0200 Subject: implement modality in ALFApplication --- alf/alf.cpp | 19 +++++++++++++++++++ alf/alf.h | 48 +++++++++++++++++++++++++++++++++++++++++++----- alf/alftoplevel.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 109 insertions(+), 11 deletions(-) (limited to 'alf') diff --git a/alf/alf.cpp b/alf/alf.cpp index 84ba71d..a4abca6 100644 --- a/alf/alf.cpp +++ b/alf/alf.cpp @@ -411,3 +411,22 @@ ALF_SetFocus(HWND target) else SendMessage(target, WM_NEXTDLGCTL, (WPARAM)target, TRUE); } + +int +ALF_MessageBox(HWND hwnd, const TCHAR *text, const TCHAR *caption, UINT type) +{ + ALFApplication *app = NULL; + + if (GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId()) + app = ALF_Toplevel_Application(hwnd); + + if (app) + ALF_Application_DisableWindowsForModal(app, hwnd); + + int retval = MessageBox(hwnd, text, caption, type); + + if (app) + ALF_Application_ReenableWindowsForModal(app, hwnd); + + return retval; +} diff --git a/alf/alf.h b/alf/alf.h index cc1d936..2399569 100644 --- a/alf/alf.h +++ b/alf/alf.h @@ -302,13 +302,21 @@ ALF_FillRect(HDC dc, const RECT *rc, ALFColor color); void ALF_DestroyWidget(HWND win, WORD id); +// wrapper for MessageBox() that handles modality for multiple toplevels with ALFApplication +int +ALF_MessageBox(HWND hwnd, const TCHAR *text, const TCHAR *caption, UINT type); + // application -// an application is a container for multiple top-level windows +// ALFApplication is a container for multiple top-level windows. +// +// Having an ALFApplication is not required, but it enables you to get modality +// right in the face of multiple top-level windows. +// // NOTE: ALFApplication is not thread-safe. It must be accessed only from the // thread that created it, and all toplevels associated with the application -// must live on that same thread. +// must live on that same thread. There must be at most one application per thread. typedef struct tagALFApplication ALFApplication; @@ -335,12 +343,41 @@ ALF_Application_SetPreTranslateMessageHandler(ALFApplication *app, BOOL(*handler BOOL ALF_Application_PreTranslateMessage(ALFApplication *app, MSG *msg, HWND modalToplevel); -// TODO: implement modal stuff -/*void +// Modality with multiple toplevels +// -------------------------------- +// +// How it works: +// * A "modal disable" counter is managed for each ALF toplevel +// * Calling ALF_Application_DisableWindowsForModal() will increase the counter +// for each toplevel of the application, except for the HWND skip +// * Calling ALF_Application_ReenableWindowsForModal() will decrease the counter +// for each toplevel of the application, except for the HWND skip +// +// How to show a modal ALF toplevel: +// (1) It is assumed that we're running on the thread that owns the modal toplevel +// (2) Show the modal toplevel +// (3) If the modal has an ALFApplication: +// (3.1) Call DisableWindowsForModal on that app with skip=modal +// (4) Additionally, disable the owner with EnableWindow() +// The owner might be a non-ALF window or live on another thread, in which +// case it is not being disabled in step 3.1. +// (5) Run the modal message loop +// (6) Undo (4) via EnableWindow +// (7) Undo (3.1) via EnableWindowsForModal with skip=modal +// (8) Hide the modal window +// --> see ALF_Toplevel_ShowModal() for an example of how to do this +// +// How to show a modal window that adheres to Win32 modality conventions: +// (1) If the owner window lives on the same thread and has an ALFApplication: +// (1.1) Call DisableWindowsForModal on that app with skip=owner +// (2) Run the modal window +// (3) undo 1.1 via EnableWindowsForModal with skip=owner +// --> see ALF_MessageBox() for an example of how to do this +void ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip); void -ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip);*/ +ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip); // also destroys all toplevels associated with the application void @@ -349,6 +386,7 @@ ALF_DestroyApplication(ALFApplication *app); // toplevel window +// NOTE about app: If app==NULL, the owner's app will be copied if the owner lives on the same thread HWND ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFApplication *app, ALFToplevelVTable *vtbl, void *closure); diff --git a/alf/alftoplevel.cpp b/alf/alftoplevel.cpp index b9f2e7b..d54d4b3 100644 --- a/alf/alftoplevel.cpp +++ b/alf/alftoplevel.cpp @@ -8,6 +8,7 @@ typedef struct { void *closure; ALFListHeader toplevelList; ALFApplication *app; + DWORD modalDisableDepth; DWORD flags; LPARAM modalResult; ALFLayout layout; @@ -461,6 +462,10 @@ ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFApplicat if (hwndOwner && GetWindowLong(hwndOwner, GWL_STYLE) & WS_CHILD) hwndOwner = GetParent(hwndOwner); + // copy app of owner, but only if we're in the same thread + if (!app && hwndOwner && GetWindowThreadProcessId(hwndOwner, NULL) == GetCurrentThreadId()) + app = ALF_Toplevel_Application(hwndOwner); + struct ALFToplevel_CreateParams params; params.vtbl = vtbl; params.closure = closure; @@ -487,6 +492,9 @@ ALF_Toplevel_ShowModal(HWND toplevel) ShowWindow(toplevel, SW_SHOW); + if (priv->app) + ALF_Application_DisableWindowsForModal(priv->app, toplevel); + if (owner) ownerEnabled = !EnableWindow(owner, FALSE); @@ -504,8 +512,11 @@ ALF_Toplevel_ShowModal(HWND toplevel) } } - if (owner) - EnableWindow(owner, ownerEnabled); + if (owner && ownerEnabled) + EnableWindow(owner, TRUE); + + if (priv->app) + ALF_Application_ReenableWindowsForModal(priv->app, toplevel); ShowWindow(toplevel, SW_HIDE); @@ -636,12 +647,42 @@ ALF_Application_PreTranslateMessage(ALFApplication *app, MSG *msg, HWND modalTop || (app->activeToplevel && app->activeToplevel->hwnd != modalToplevel && ALF_Toplevel_PreTranslateMessage(app->activeToplevel->hwnd, msg)); } -// TODO: implement modal stuff -/*void -ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip); +void +ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip) +{ + ALF_FOR_LIST(ALFToplevelPriv, toplevelList, &app->toplevelList, t) { + if (t->hwnd == skip) + continue; + + DWORD c = t->modalDisableDepth & 0x7fffffff; + if (c == 0) { + t->modalDisableDepth = 1; + if (EnableWindow(t->hwnd, FALSE)) + t->modalDisableDepth |= 0x80000000; // flag: was already disabled + } else { + t->modalDisableDepth++; + } + } +} void -ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip);*/ +ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip) +{ + ALF_FOR_LIST(ALFToplevelPriv, toplevelList, &app->toplevelList, t) { + if (t->hwnd == skip) + continue; + + DWORD c = t->modalDisableDepth & 0x7fffffff; + if (c == 1) { + if (!(t->modalDisableDepth & 0x80000000)) + EnableWindow(t->hwnd, TRUE); + + t->modalDisableDepth = 0; + } else { + t->modalDisableDepth--; + } + } +} void ALF_DestroyApplication(ALFApplication *app) -- cgit v1.2.3