diff options
| author | Jonas Kümmerlin <jonas@kuemmerlin.eu> | 2020-05-31 01:49:11 +0200 |
|---|---|---|
| committer | Jonas Kümmerlin <jonas@kuemmerlin.eu> | 2020-05-31 01:49:11 +0200 |
| commit | 9251e89359ce46d08c0924b09d48fedbdf2c95fb (patch) | |
| tree | 4adb0418223a07834f8e55a457c07ceda1bf29b3 | |
| parent | 8d31086aed4aa91232cbaad9cf88d720e6329bcf (diff) | |
ALFApplication and multiple toplevels support
| -rw-r--r-- | alf/alf.h | 51 | ||||
| -rw-r--r-- | alf/alflist.h | 4 | ||||
| -rw-r--r-- | alf/alftoplevel.cpp | 148 | ||||
| -rw-r--r-- | widgetfactory.cpp | 79 |
4 files changed, 268 insertions, 14 deletions
@@ -80,6 +80,7 @@ typedef struct { #define ALF_WM_SETMODALRESULT (ALF_WM__BASE + 7) #define ALF_WM_GETMODALRESULT (ALF_WM__BASE + 8) #define ALF_WM_GETDPI (ALF_WM__BASE + 9) +#define ALF_WM_GETAPPLICATION (ALF_WM__BASE + 10) #define ALF_WM_BACKGROUNDCHANGE (ALF_WM__BASE + 11) #define ALF_WM_APPLYSIZE (ALF_WM__BASE + 12) #define ALF_WM_SETBGCOLOR (ALF_WM__BASE + 13) @@ -301,10 +302,58 @@ ALF_FillRect(HDC dc, const RECT *rc, ALFColor color); void ALF_DestroyWidget(HWND win, WORD id); + +// application + +// an application is a container for 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. + +typedef struct tagALFApplication ALFApplication; + +ALFApplication * +ALF_CreateApplication(void); + +void +ALF_Application_ProcessMessages(ALFApplication *app); + +// continuously processes messages until a quit message is posted +// or all windows have been destroyed +void +ALF_Application_Run(ALFApplication *app); + +BOOL +ALF_Application_EnumToplevels(ALFApplication *app, WNDENUMPROC proc, LPARAM param); + +HWND +ALF_Application_ActiveToplevel(ALFApplication *app); + +void +ALF_Application_SetPreTranslateMessageHandler(ALFApplication *app, BOOL(*handler)(void *,MSG *), void *closure); + +BOOL +ALF_Application_PreTranslateMessage(ALFApplication *app, MSG *msg, HWND modalToplevel); + +// TODO: implement modal stuff +/*void +ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip); + +void +ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip);*/ + +// also destroys all toplevels associated with the application +void +ALF_DestroyApplication(ALFApplication *app); + + // toplevel window HWND -ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFToplevelVTable *vtbl, void *closure); +ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFApplication *app, ALFToplevelVTable *vtbl, void *closure); + +ALFApplication * +ALF_Toplevel_Application(HWND toplevel); void ALF_Toplevel_Resize(HWND win, int cptWidth, int cptHeight); diff --git a/alf/alflist.h b/alf/alflist.h index 7b4ca66..8bbfda8 100644 --- a/alf/alflist.h +++ b/alf/alflist.h @@ -38,8 +38,8 @@ ALF_ListRemove(ALFListHeader *member) { member->prev->next = member->next; member->next->prev = member->prev; - member->next = NULL; - member->prev = NULL; + member->next = member; + member->prev = member; } static inline void diff --git a/alf/alftoplevel.cpp b/alf/alftoplevel.cpp index 161a738..b9f2e7b 100644 --- a/alf/alftoplevel.cpp +++ b/alf/alftoplevel.cpp @@ -3,8 +3,11 @@ #define ALF_TOPLEVEL_FLAG_MODALEND ((DWORD)1) typedef struct { + HWND hwnd; ALFToplevelVTable *vtbl; void *closure; + ALFListHeader toplevelList; + ALFApplication *app; DWORD flags; LPARAM modalResult; ALFLayout layout; @@ -12,9 +15,17 @@ typedef struct { HFONT hMessageFont; } ALFToplevelPriv; +struct tagALFApplication { + ALFListHeader toplevelList; + ALFToplevelPriv *activeToplevel; + BOOL (*pretranslatemessage)(void *,MSG *); + void *pretranslatemessage_closure; +}; + struct ALFToplevel_CreateParams{ ALFToplevelVTable *vtbl; void *closure; + ALFApplication *app; }; static TCHAR _alf_toplevelClass[28] = {0}; @@ -22,8 +33,8 @@ static TCHAR _alf_toplevelClass[28] = {0}; static void ALF_InitializeToplevelPriv(HWND hwnd, ALFToplevelPriv *priv) { - (void)hwnd; - + priv->hwnd = hwnd; + ALF_ListInit(&priv->toplevelList); ALF_Layout_Init(&priv->layout); priv->layout.bgcolor = ALF_COLOR_SYS(COLOR_BTNFACE); } @@ -34,6 +45,10 @@ ALF_DestroyToplevelPriv(ALFToplevelPriv *priv) if (priv->vtbl && priv->vtbl->postdestroy) priv->vtbl->postdestroy(priv->closure); + if (priv->app && priv->app->activeToplevel == priv) + priv->app->activeToplevel = NULL; + + ALF_ListRemove(&priv->toplevelList); ALF_Layout_Clear(&priv->layout); if (priv->hMessageFont) @@ -166,6 +181,11 @@ ALF_Toplevel_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) priv->vtbl = params->vtbl; priv->closure = params->closure; + priv->app = params->app; + + if (priv->app) { + ALF_ListInsert(&priv->app->toplevelList, &priv->toplevelList); + } BOOL retval = TRUE; if (priv->vtbl && priv->vtbl->initialize) { @@ -195,6 +215,18 @@ ALF_Toplevel_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) return 0; } + if (msg == WM_ACTIVATE) { + if (wparam) { + if (priv->app) { + priv->app->activeToplevel = priv; + } + } else { + if (priv->app && priv->app->activeToplevel == priv) { + priv->app->activeToplevel = NULL; + } + } + } + if (msg == WM_ERASEBKGND) { return TRUE; // handled in WM_PAINT } @@ -350,6 +382,10 @@ ALF_Toplevel_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) } } + if (msg == ALF_WM_GETAPPLICATION) { + return (LRESULT)priv->app; + } + 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 @@ -397,7 +433,7 @@ ALF_UnregisterToplevelClass(void) } HWND -ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFToplevelVTable *vtbl, void *closure) +ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFApplication *app, ALFToplevelVTable *vtbl, void *closure) { #pragma pack(push, 1) struct { @@ -428,6 +464,7 @@ ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFToplevel struct ALFToplevel_CreateParams params; params.vtbl = vtbl; params.closure = closure; + params.app = app; return CreateDialogIndirectParam(ALF_HINSTANCE, &t.t, hwndOwner, ALF_Toplevel_DlgProc, (LPARAM)¶ms); } @@ -456,8 +493,8 @@ ALF_Toplevel_ShowModal(HWND toplevel) while (!(priv->flags & ALF_TOPLEVEL_FLAG_MODALEND)) { MSG msg; if (GetMessage(&msg, NULL, 0, 0)) { - // TODO: call app-level message filter - if (!ALF_Toplevel_PreTranslateMessage(toplevel, &msg)) { + if (!(priv->app && ALF_Application_PreTranslateMessage(priv->app, &msg, toplevel)) + && !ALF_Toplevel_PreTranslateMessage(toplevel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -518,3 +555,104 @@ ALF_Toplevel_ResizePx(HWND win, int pxwidth, int pxheight) SetWindowPos(win, NULL, 0, 0, pxwidth, pxheight, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); } + +ALFApplication * +ALF_Toplevel_Application(HWND toplevel) +{ + return (ALFApplication *)SendMessage(toplevel, ALF_WM_GETAPPLICATION, 0, 0); +} + +ALFApplication * +ALF_CreateApplication(void) +{ + ALFApplication *app = ALF_New(ALFApplication, 1); + ALF_ListInit(&app->toplevelList); + + return app; +} + +void +ALF_Application_ProcessMessages(ALFApplication *app) +{ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + PostQuitMessage((int)msg.wParam); + return; + } else { + if (!ALF_Application_PreTranslateMessage(app, &msg, NULL)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +} + +void +ALF_Application_Run(ALFApplication *app) +{ + while (!ALF_ListIsEmpty(&app->toplevelList)) { + MSG msg; + if (GetMessage(&msg, NULL, 0, 0)) { + if (!ALF_Application_PreTranslateMessage(app, &msg, NULL)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } else { + PostQuitMessage((int)msg.wParam); + return; + } + } +} + +BOOL +ALF_Application_EnumToplevels(ALFApplication *app, WNDENUMPROC proc, LPARAM param) +{ + ALF_FOR_LIST(ALFToplevelPriv, toplevelList, &app->toplevelList, t) { + if (!proc(t->hwnd, param)) + return FALSE; + } + + return TRUE; +} + +HWND +ALF_Application_ActiveToplevel(ALFApplication *app) +{ + return app->activeToplevel->hwnd; +} + +void +ALF_Application_SetPreTranslateMessageHandler(ALFApplication *app, BOOL(*handler)(void *,MSG *), void *closure) +{ + app->pretranslatemessage = handler; + app->pretranslatemessage_closure = closure; +} + +BOOL +ALF_Application_PreTranslateMessage(ALFApplication *app, MSG *msg, HWND modalToplevel) +{ + return (app->pretranslatemessage && app->pretranslatemessage(app->pretranslatemessage_closure, msg)) + || (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_ReenableWindowsForModal(ALFApplication *app, HWND skip);*/ + +void +ALF_DestroyApplication(ALFApplication *app) +{ + // don't use ALF_FOR_LIST here, since ALF_FOR_LIST can only handle destroying + // the current element. But the window might to anything in WM_DESTROY, + // including destroying various other windows. + while (!ALF_ListIsEmpty(&app->toplevelList)) { + HWND w = ALF_LIST_CONTAINER(ALFToplevelPriv, toplevelList, app->toplevelList.next)->hwnd; + DestroyWindow(w); + } + + ALF_Free(app); +} diff --git a/widgetfactory.cpp b/widgetfactory.cpp index 060544b..f761d2b 100644 --- a/widgetfactory.cpp +++ b/widgetfactory.cpp @@ -40,11 +40,15 @@ enum { IDM_PANES, IDM_PANES__MAX = IDM_PANES + PANE__MAX, IDM_MODALDIALOG_PANES, - IDM_MODALDIALOG_PANES__MAX = IDM_MODALDIALOG_PANES + PANE__MAX + IDM_MODALDIALOG_PANES__MAX = IDM_MODALDIALOG_PANES + PANE__MAX, + IDM_MODELESSDIALOG_PANES, + IDM_MODELESSDIALOG_PANES__MAX = IDM_MODELESSDIALOG_PANES + PANE__MAX }; BOOL (WINAPI *fnGradientFill)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG) = NULL; +static ALFApplication *g_application = NULL; + enum PanelBackgroundMode { BG_INHERIT, BG_GRADIENT, @@ -55,6 +59,7 @@ enum PanelBackgroundMode { #define FM_PANEL_SET_BGMODE (ALF_WM_USER + 1) static void wfModalDialogRun(HWND owner, int pane); +static void wfModelessDialogStart(int pane); /* common stuff */ typedef struct { @@ -749,7 +754,8 @@ handleClose(void *closure, HWND window) { (void)closure; - ALF_Toplevel_SetModalResult(window, 0); + //ALF_Toplevel_SetModalResult(window, 0); + DestroyWindow(window); } static LRESULT @@ -819,6 +825,11 @@ handleCommand(void *closure, HWND window, WORD notificationcode, WORD sourceid, wfModalDialogRun(window, paneId); } + if (sourceid >= IDM_MODELESSDIALOG_PANES && sourceid < IDM_MODELESSDIALOG_PANES__MAX) { + int paneId = sourceid - IDM_MODELESSDIALOG_PANES; + wfModelessDialogStart(paneId); + } + return 0; } @@ -868,13 +879,62 @@ wfModalDialogRun(HWND owner, int pane) { HWND dlg = ALF_CreateToplevelWindow(WS_EX_DLGMODALFRAME, WS_POPUPWINDOW | WS_CAPTION | WS_THICKFRAME, - owner, + owner, g_application, &g_wfModalDialogVtbl, (void*)(LONG_PTR)pane); ALF_Toplevel_ShowModal(dlg); DestroyWindow(dlg); } +static BOOL +wfModelessDialogInit(void *closure, HWND dialog) +{ + HWND panel = ALF_AddPanel(dialog, (WORD)-1, 1, 1); + int paneid = (int)(LONG_PTR)closure; + g_paneInitTable[paneid](panel); + + ALF_SetText(dialog, g_paneLabels[paneid]); + + ALF_LayoutSetColumnExpandNumerator(dialog, 1, 1); + ALF_LayoutSetRowExpandNumerator(dialog, 1, 1); + ALF_LayoutSetRowMinSize(dialog, 0, 525); + ALF_LayoutSetRowMinSize(dialog, 2, 525); + ALF_LayoutSetColumnMinSize(dialog, 0, 525); + ALF_LayoutSetColumnMinSize(dialog, 2, 525); + + return TRUE; +} + +static void +wfModelessDialogClose(void *closure, HWND dialog) +{ + (void)closure; + + DestroyWindow(dialog); +} + +static ALFToplevelVTable g_wfModelessDialogVtbl; + +static void +wfModelessDialogPrepareVtbl(void) +{ + ZeroMemory(&g_wfModelessDialogVtbl, sizeof(g_wfModelessDialogVtbl)); + g_wfModelessDialogVtbl.close = wfModelessDialogClose; + g_wfModelessDialogVtbl.initialize = wfModelessDialogInit; +} + +static void +wfModelessDialogStart(int pane) +{ + HWND dlg = ALF_CreateToplevelWindow(0, + WS_OVERLAPPEDWINDOW, + NULL, g_application, + &g_wfModelessDialogVtbl, + (void*)(LONG_PTR)pane); + ShowWindow(dlg, SW_SHOW); +} + + int CALLBACK #ifdef UNICODE wWinMain @@ -898,6 +958,7 @@ WinMain } wfModalDialogPrepareVtbl(); + wfModelessDialogPrepareVtbl(); ALFToplevelVTable vtbl; ZeroMemory(&vtbl, sizeof(vtbl)); @@ -907,7 +968,9 @@ WinMain ALF_Initialize(); - HWND win = ALF_CreateToplevelWindow(0, WS_OVERLAPPEDWINDOW, NULL, &vtbl, NULL); + g_application = ALF_CreateApplication(); + + HWND win = ALF_CreateToplevelWindow(0, WS_OVERLAPPEDWINDOW, NULL, g_application, &vtbl, NULL); if (!win) { MessageBox(0, TEXT("couldn't create main window!"), 0, MB_ICONHAND|MB_OK); return 1; @@ -944,6 +1007,7 @@ WinMain HMENU filemenu = CreateMenu(); HMENU newtabmn = CreateMenu(); HMENU modwndmn = CreateMenu(); + HMENU mlwndmn = CreateMenu(); HMENU helpmenu = CreateMenu(); HMENU bgmenu = CreateMenu(); @@ -951,6 +1015,7 @@ WinMain AppendMenu(filemenu, MF_SEPARATOR, 0, 0); AppendMenu(filemenu, MF_POPUP, (UINT_PTR)newtabmn, TEXT("New Tab")); AppendMenu(filemenu, MF_POPUP, (UINT_PTR)modwndmn, TEXT("Show Modal")); + AppendMenu(filemenu, MF_POPUP, (UINT_PTR)mlwndmn, TEXT("Show Modeless")); AppendMenu(filemenu, MF_SEPARATOR, 0, 0); AppendMenu(filemenu, MF_STRING, IDM_FILE_CLOSE, TEXT("&Close\tAlt+F4")); @@ -968,14 +1033,16 @@ WinMain for (int i = 0; i < PANE__MAX; ++i) { AppendMenu(newtabmn, MF_STRING, IDM_PANES + (UINT)i, g_paneLabels[i]); AppendMenu(modwndmn, MF_STRING, IDM_MODALDIALOG_PANES + (UINT)i, g_paneLabels[i]); + AppendMenu(mlwndmn, MF_STRING, IDM_MODELESSDIALOG_PANES + (UINT)i, g_paneLabels[i]); } SetMenu(win, mainmenu); ALF_Toplevel_EnsureBigEnough(win); - ALF_Toplevel_ShowModal(win); - DestroyWindow(win); + ALF_Application_Run(g_application); + ALF_DestroyApplication(g_application); + ALF_UnInitialize(); return 0; |
