From 5cbb664bd49cd2b9c035ccfc3024aa436d28c36c Mon Sep 17 00:00:00 2001 From: Jonas Kümmerlin Date: Thu, 16 Apr 2020 13:12:32 +0200 Subject: layout: invalidate and recalculate, automatically --- alf/alf.cpp | 35 ++++++++++++++++------- alf/alf.h | 10 +++---- alf/alfbutton.cpp | 4 +++ alf/alfcombobox.cpp | 3 ++ alf/alfedit.cpp | 2 ++ alf/alflabel.cpp | 52 +++++++++++++++++---------------- alf/alflayout.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++----- alf/alflayout.h | 14 +++++++-- alf/alfnotebook.cpp | 7 +++++ widgetfactory.cpp | 1 - 10 files changed, 159 insertions(+), 51 deletions(-) diff --git a/alf/alf.cpp b/alf/alf.cpp index 77fe344..f914e55 100644 --- a/alf/alf.cpp +++ b/alf/alf.cpp @@ -75,11 +75,9 @@ ALF_UpdateFontsPriv(HWND win, ALFWindowPriv *priv) } void -ALF_RecalculateLayout(HWND hwnd) +ALF_InvalidateLayout(HWND hwnd) { - SIZE dummy; - SendMessage(hwnd, ALF_WM_QUERYSIZE, 0, (LPARAM)&dummy); - SendMessage(hwnd, ALF_WM_APPLYLAYOUT, 0, 0); + SendMessage(hwnd, ALF_WM_INVALIDATELAYOUT, 0, 0); } static void @@ -230,11 +228,21 @@ ALF_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 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)); - tmp.right = priv->layout.totalMinWidth; - tmp.bottom = priv->layout.totalMinHeight; + + 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, @@ -259,7 +267,6 @@ ALF_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) SendMessage(hwnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0); } ALF_UpdateFontsPriv(hwnd, priv); - ALF_Layout_CalcSizes(&priv->layout, hwnd); ALF_Layout_Apply(&priv->layout, hwnd); } @@ -282,15 +289,14 @@ ALF_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) if (msg == WM_DPICHANGED) { ALF_UpdateFontsPriv(hwnd, priv); - ALF_Layout_CalcSizes(&priv->layout, hwnd); + ALF_Layout_Invalidate(&priv->layout, hwnd); 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_CalcSizes(&priv->layout, hwnd); - ALF_Layout_Apply(&priv->layout, hwnd); + ALF_Layout_Invalidate(&priv->layout, hwnd); } if (msg == DM_GETDEFID) { @@ -318,6 +324,15 @@ ALF_DefWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM 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 + // windows will call WM_GETMINMAXINFO and fix it + if (msg == ALF_WM_VALIDATELAYOUT) { + RECT r; + if (GetWindowRect(hwnd, &r)) { + SetWindowPos(hwnd, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); + } + } + return ret; } diff --git a/alf/alf.h b/alf/alf.h index d98dfef..a429472 100644 --- a/alf/alf.h +++ b/alf/alf.h @@ -61,6 +61,8 @@ typedef struct { #define ALF_WM_SETLAYOUTPARAMS (ALF_WM__BASE + 14) #define ALF_WM_GETWIDGETATPOS (ALF_WM__BASE + 15) #define ALF_WM_PRETRANSLATEMSG (ALF_WM__BASE + 16) +#define ALF_WM_INVALIDATELAYOUT (ALF_WM__BASE + 17) +#define ALF_WM_VALIDATELAYOUT (ALF_WM__BASE + 18) #define ALF_WM_LBL_GETSTYLE (ALF_WM__BASE + 201) #define ALF_WM_LBL_SETSTYLE (ALF_WM__BASE + 202) @@ -155,15 +157,11 @@ HWND ALF_WidgetHwndById(HWND win, WORD id); void -ALF_RecalculateLayout(HWND win); +ALF_InvalidateLayout(HWND win); // Recalculates the window's DPI and all fonts and applies them to child widgets. // ALF does this automatically on a DPI or settings change so you shouldn't have -// to call ALF_UpdateFonts(). If you have added new widgets and want to set their -// fonts, call ALF_ApplyFonts() instead. -// -// Since widget sizes depend on the assigned font, you should probably -// call ALF_RecalculateLayout() afterwards. +// to call ALF_UpdateFonts(). void ALF_UpdateFonts(HWND win); diff --git a/alf/alfbutton.cpp b/alf/alfbutton.cpp index 582a90c..be9a276 100644 --- a/alf/alfbutton.cpp +++ b/alf/alfbutton.cpp @@ -475,6 +475,10 @@ ALF__ButtonSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT ALF_Compat_RemoveWindowSubclass(hwnd, ALF__ButtonSubclassProc, 0); } else if (uMsg == WM_ERASEBKGND) { return TRUE; + } else if (uMsg == WM_SETTEXT) { + ALF_InvalidateLayout(GetParent(hwnd)); + } else if (uMsg == WM_SETFONT) { + ALF_InvalidateLayout(GetParent(hwnd)); } return ALF_Compat_DefSubclassProc(hwnd, uMsg, wParam, lParam); diff --git a/alf/alfcombobox.cpp b/alf/alfcombobox.cpp index 097e2a4..288e3a8 100644 --- a/alf/alfcombobox.cpp +++ b/alf/alfcombobox.cpp @@ -74,6 +74,7 @@ ALF__ComboWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } if (uMsg == WM_SETTEXT && hwndChild) { SetWindowText(hwndChild, (TCHAR*)lParam); + ALF_InvalidateLayout(GetParent(hwnd)); } if (uMsg == WM_GETTEXTLENGTH && hwndChild) { return (LRESULT)GetWindowTextLength(hwndChild); @@ -88,6 +89,8 @@ ALF__ComboWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (h) SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)0, h); + ALF_InvalidateLayout(GetParent(hwnd)); + return 0; } if (uMsg == WM_GETFONT && hwndChild) { diff --git a/alf/alfedit.cpp b/alf/alfedit.cpp index c352a8b..ac12e38 100644 --- a/alf/alfedit.cpp +++ b/alf/alfedit.cpp @@ -44,6 +44,8 @@ ALF__EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_P SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS); } else if (uMsg == WM_DESTROY) { ALF_Compat_RemoveWindowSubclass(hwnd, ALF__EditSubclassProc, 0); + } else if (uMsg == WM_SETFONT) { + ALF_InvalidateLayout(GetParent(hwnd)); } return ALF_Compat_DefSubclassProc(hwnd, uMsg, wParam, lParam); diff --git a/alf/alflabel.cpp b/alf/alflabel.cpp index fd2cab7..f400f4f 100644 --- a/alf/alflabel.cpp +++ b/alf/alflabel.cpp @@ -69,6 +69,7 @@ ALF__LabelWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return TRUE; } else if (uMsg == WM_SETTEXT) { InvalidateRect(hwnd, NULL, TRUE); + ALF_InvalidateLayout(GetParent(hwnd)); } else if (uMsg == WM_GETDLGCODE) { return DLGC_STATIC; } else if (uMsg == WM_SETFONT) { @@ -76,6 +77,8 @@ ALF__LabelWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (LOWORD(lParam) != 0) InvalidateRect(hwnd, NULL, TRUE); + ALF_InvalidateLayout(GetParent(hwnd)); + return 0; } else if (uMsg == WM_GETFONT) { return (LRESULT)priv->font; @@ -167,40 +170,39 @@ ALF__LabelWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return TRUE; } else if (uMsg == ALF_WM_QUERYSIZE) { - HDC hdcLabel = GetDC(hwnd); - HFONT oldFont = 0; - if (priv->font) - oldFont = SelectFont(hdcLabel, priv->font); + int textlen = GetWindowTextLength(hwnd); + SIZE *pSize = (SIZE*)(void*)lParam; + if (textlen) { + HDC hdcLabel = GetDC(hwnd); + HFONT oldFont = SelectFont(hdcLabel, priv->font); - // calc drawtext style - UINT format = DT_LEFT | DT_EXPANDTABS | DT_CALCRECT; + // calc drawtext style + UINT format = DT_LEFT | DT_EXPANDTABS | DT_CALCRECT; - RECT r = { 0, 0, 100, 100 }; + RECT r = { 0, 0, 100, 100 }; - int textlen = GetWindowTextLength(hwnd); - TCHAR *textbuf = ALF_New(TCHAR, textlen + 1); - GetWindowText(hwnd, textbuf, textlen+1); + TCHAR *textbuf = ALF_New(TCHAR, textlen + 1); + GetWindowText(hwnd, textbuf, textlen+1); - DrawText(hdcLabel, textbuf, -1, &r, format); + DrawText(hdcLabel, textbuf, -1, &r, format); - ALF_Free(textbuf); + ALF_Free(textbuf); - SIZE *pSize = (SIZE*)(void*)lParam; - if (pSize->cx == 0) { - pSize->cx = r.right - r.left; - if ((priv->style & ALF_LABEL_HALIGN_MASK) == ALF_LABEL_ALIGN_LEFT_LIKE_EDIT) - pSize->cx += ALF__LabelLeftPadding(hwnd, hdcLabel); - } - if (pSize->cy == 0) { - pSize->cy = r.bottom - r.top; - if ((priv->style & ALF_LABEL_VALIGN_MASK) == ALF_LABEL_ALIGN_TOP_LIKE_EDIT) - pSize->cy += ALF__LabelTopPadding(hwnd); - } + if (pSize->cx == 0) { + pSize->cx = r.right - r.left; + if ((priv->style & ALF_LABEL_HALIGN_MASK) == ALF_LABEL_ALIGN_LEFT_LIKE_EDIT) + pSize->cx += ALF__LabelLeftPadding(hwnd, hdcLabel); + } + if (pSize->cy == 0) { + pSize->cy = r.bottom - r.top; + if ((priv->style & ALF_LABEL_VALIGN_MASK) == ALF_LABEL_ALIGN_TOP_LIKE_EDIT) + pSize->cy += ALF__LabelTopPadding(hwnd); + } - if (oldFont) SelectFont(hdcLabel, oldFont); - ReleaseDC(hwnd, hdcLabel); + ReleaseDC(hwnd, hdcLabel); + } } else if (uMsg == WM_DESTROY) { ALF_Free(priv); SetWindowLongPtr(hwnd, 0, 0); diff --git a/alf/alflayout.cpp b/alf/alflayout.cpp index 0cc6cdf..b98ae93 100644 --- a/alf/alflayout.cpp +++ b/alf/alflayout.cpp @@ -1,5 +1,8 @@ #include "alfpriv.h" +static void +ALF_Layout_CalcSizes(ALFLayout *layout, HWND window); + void ALF_Layout_Init(ALFLayout *layout) { @@ -131,11 +134,16 @@ ALF_Layout_CalcSizes(ALFLayout* layout, HWND window) } } } + + layout->layoutValididityFlags &= ~ALF_LAYOUT_NEED_RECALC; } void ALF_Layout_Apply(ALFLayout* layout, HWND window) { + if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) + ALF_Layout_CalcSizes(layout, window); + // allocate cell positions // distribute free space int extraWidth = 0; @@ -229,6 +237,8 @@ ALF_Layout_Apply(ALFLayout* layout, HWND window) } EndDeferWindowPos(hdwp); + + layout->layoutValididityFlags &= ~ALF_LAYOUT_NEED_REAPPLY; } void @@ -254,10 +264,12 @@ ALF_Layout_AddWidget(ALFLayout* layout, HWND window, const ALFWidgetLayoutParams } ALF_ListInsert(layout->widgets.prev, &w->list); + + ALF_InvalidateLayout(window); } BOOL -ALF_Layout_SetWidgetParams(ALFLayout* layout, const ALFWidgetLayoutParams* params, HWND widget) +ALF_Layout_SetWidgetParams(ALFLayout* layout, HWND window, const ALFWidgetLayoutParams* params, HWND widget) { ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, w) { if (w->hwnd == widget) { @@ -272,6 +284,11 @@ ALF_Layout_SetWidgetParams(ALFLayout* layout, const ALFWidgetLayoutParams* param w->cptMarginBottom = params->margins[2]; w->cptMarginLeft = params->margins[3]; + if (w->flags & ALF_INHERITFONT) + SendMessage(w->hwnd, WM_SETFONT, (WPARAM)SendMessage(window, WM_GETFONT, 0, 0), 0); + + ALF_InvalidateLayout(window); + return TRUE; } } @@ -314,6 +331,51 @@ ALF_Layout_WidgetAtPos(ALFLayout* layout, UINT x, UINT y) return NULL; } +void +ALF_Layout_Invalidate(ALFLayout *layout, HWND window) +{ + if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) + return; + + layout->layoutValididityFlags |= ALF_LAYOUT_NEED_RECALC | ALF_LAYOUT_NEED_REAPPLY; + + HWND parent = GetParent(window); + if (parent) { + ALF_InvalidateLayout(parent); + } else { + PostMessage(window, ALF_WM_VALIDATELAYOUT, 0, 0); + } +} + + +BOOL +ALF_Layout_Validate(ALFLayout *layout, HWND window) +{ + BOOL ret = FALSE; + + if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) { + ALF_Layout_CalcSizes(layout, window); + ret = TRUE; + } + + if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_REAPPLY) { + ALF_Layout_Apply(layout, window); + ret = TRUE; + } + + return ret; +} + +void +ALF_Layout_GetMinSize(ALFLayout *layout, HWND window, SIZE *size) +{ + if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) { + ALF_Layout_CalcSizes(layout, window); + } + + size->cx = layout->totalMinWidth; + size->cy = layout->totalMinHeight; +} BOOL ALF_Layout_HandleMessage(ALFLayout *layout, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *pRet) @@ -321,10 +383,7 @@ ALF_Layout_HandleMessage(ALFLayout *layout, HWND hwnd, UINT msg, WPARAM wparam, *pRet = 0; if (msg == ALF_WM_QUERYSIZE) { - ALF_Layout_CalcSizes(layout, hwnd); - SIZE *ps = (SIZE*)lparam; - ps->cx = layout->totalMinWidth; - ps->cy = layout->totalMinHeight; + ALF_Layout_GetMinSize(layout, hwnd, (SIZE*)lparam); return TRUE; } @@ -344,7 +403,7 @@ ALF_Layout_HandleMessage(ALFLayout *layout, HWND hwnd, UINT msg, WPARAM wparam, } if (msg == ALF_WM_SETLAYOUTPARAMS) { - *pRet = (LRESULT)ALF_Layout_SetWidgetParams(layout, (const ALFWidgetLayoutParams *)lparam, (HWND)wparam); + *pRet = (LRESULT)ALF_Layout_SetWidgetParams(layout, hwnd, (const ALFWidgetLayoutParams *)lparam, (HWND)wparam); return TRUE; } @@ -355,7 +414,16 @@ ALF_Layout_HandleMessage(ALFLayout *layout, HWND hwnd, UINT msg, WPARAM wparam, if (msg == WM_SETFONT) { ALF_Layout_ForwardFont(layout, hwnd, (HFONT)wparam, lparam); - *pRet = 0; + return TRUE; + } + + if (msg == ALF_WM_INVALIDATELAYOUT) { + ALF_Layout_Invalidate(layout, hwnd); + return TRUE; + } + + if (msg == ALF_WM_VALIDATELAYOUT) { + ALF_Layout_Validate(layout, hwnd); return TRUE; } diff --git a/alf/alflayout.h b/alf/alflayout.h index c279afa..e326490 100644 --- a/alf/alflayout.h +++ b/alf/alflayout.h @@ -24,6 +24,9 @@ typedef struct { int expand : 1; } ALFLayoutRowOrColumn; +#define ALF_LAYOUT_NEED_RECALC ((DWORD)1) +#define ALF_LAYOUT_NEED_REAPPLY ((DWORD)2) + typedef struct { ALFListHeader widgets; ALFLayoutRowOrColumn *columns; @@ -34,6 +37,7 @@ typedef struct { int totalMinHeight; int expandoColumnCount; int expandoRowCount; + DWORD layoutValididityFlags; } ALFLayout; void @@ -43,7 +47,7 @@ void ALF_Layout_Clear(ALFLayout *layout); void -ALF_Layout_CalcSizes(ALFLayout *layout, HWND window); +ALF_Layout_GetMinSize(ALFLayout *layout, HWND window, SIZE *size); void ALF_Layout_Apply(ALFLayout *layout, HWND window); @@ -55,7 +59,7 @@ BOOL ALF_Layout_RemoveWidget(ALFLayout *layout, HWND window, HWND widget, BOOL destroy); BOOL -ALF_Layout_SetWidgetParams(ALFLayout *layout, const ALFWidgetLayoutParams *params, HWND widget); +ALF_Layout_SetWidgetParams(ALFLayout *layout, HWND window, const ALFWidgetLayoutParams *params, HWND widget); BOOL ALF_Layout_GetWidgetParams(ALFLayout *layout, ALFWidgetLayoutParams *params, HWND widget); @@ -65,3 +69,9 @@ ALF_Layout_HandleMessage(ALFLayout *layout, HWND window, UINT msg, WPARAM wparam HWND ALF_Layout_WidgetAtPos(ALFLayout *layout, UINT x, UINT y); + +void +ALF_Layout_Invalidate(ALFLayout *layout, HWND window); + +BOOL +ALF_Layout_Validate(ALFLayout *layout, HWND window); diff --git a/alf/alfnotebook.cpp b/alf/alfnotebook.cpp index 3b60ba5..25e2904 100644 --- a/alf/alfnotebook.cpp +++ b/alf/alfnotebook.cpp @@ -89,6 +89,7 @@ ALF_Notebook_InternalHandleThemeChange(HWND hwndNotebook, ALFNotebookPriv *priv) priv->hTheme = ALF_Compat_OpenThemeData(hwndNotebook, L"TAB"); InvalidateRect(hwndNotebook, NULL, TRUE); + ALF_InvalidateLayout(hwndNotebook); } @@ -115,6 +116,8 @@ ALF_Notebook_InternalAddTab(HWND notebook, HWND tabControl, const TCHAR *title) TabCtrl_InsertItem(tabControl, ALF_Notebook_InternalTabCount(notebook, tabControl), &tie); ALF_Notebook_InternalHandleTabChange(notebook, tabControl); + ALF_InvalidateLayout(notebook); + return hwndPanel; } @@ -208,6 +211,8 @@ ALF__NotebookWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) HWND p = ALF_Notebook_InternalTabPanel(hwnd, priv->hwndTabCtrl, i); SendMessage(p, WM_SETFONT, wParam, lParam); } + + ALF_InvalidateLayout(hwnd); } else if (uMsg == WM_GETFONT) { return SendMessage(priv->hwndTabCtrl, WM_GETFONT, wParam, lParam); } else if (uMsg == ALF_WM_QUERYSIZE) { @@ -286,6 +291,8 @@ ALF__NotebookWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) EndPaint(hwnd, &ps); return 0; + } else if (uMsg == ALF_WM_INVALIDATELAYOUT) { + ALF_InvalidateLayout(GetParent(hwnd)); } return DefWindowProc(hwnd, uMsg, wParam, lParam); diff --git a/widgetfactory.cpp b/widgetfactory.cpp index 4de9936..eec3192 100644 --- a/widgetfactory.cpp +++ b/widgetfactory.cpp @@ -206,7 +206,6 @@ WinMain ALF_SetText(hwndTabPanel2, TEXT("Panel Text Demo Test Test Test")); - ALF_RecalculateLayout(win); ALF_SetDefaultButton(win, ID_B2); ALF_ComboBoxSetText(hwndCombo2, TEXT("Goodbye World!")); -- cgit v1.2.3