// 16 may 2015 #include "uipriv_windows.hpp" struct uiGroup { uiWindowsControl c; HWND hwnd; struct uiControl *child; int margined; }; // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define groupXMargin 6 #define groupYMarginTop 11 /* note this value /includes/ the groupbox label */ #define groupYMarginBottom 7 // unfortunately because the client area of a groupbox includes the frame and caption text, we have to apply some margins ourselves, even if we don't want "any" // these were deduced by hand based on the standard DLU conversions; the X and Y top margins are the width and height, respectively, of one character cell // they can be fine-tuned later #define groupUnmarginedXMargin 4 #define groupUnmarginedYMarginTop 8 #define groupUnmarginedYMarginBottom 3 static void groupMargins(uiGroup *g, int *mx, int *mtop, int *mbottom) { uiWindowsSizing sizing; *mx = groupUnmarginedXMargin; *mtop = groupUnmarginedYMarginTop; *mbottom = groupUnmarginedYMarginBottom; if (g->margined) { *mx = groupXMargin; *mtop = groupYMarginTop; *mbottom = groupYMarginBottom; } uiWindowsGetSizing(g->hwnd, &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, mx, mtop); uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, mbottom); } static void groupRelayout(uiGroup *g) { RECT r; int mx, mtop, mbottom; if (g->child == NULL) return; uiWindowsEnsureGetClientRect(g->hwnd, &r); groupMargins(g, &mx, &mtop, &mbottom); r.left += mx; r.top += mtop; r.right -= mx; r.bottom -= mbottom; uiWindowsEnsureMoveWindowDuringResize((HWND) uiControlHandle(g->child), r.left, r.top, r.right - r.left, r.bottom - r.top); } static void uiGroupDestroy(uiControl *c) { uiGroup *g = uiGroup(c); if (g->child != NULL) { uiControlSetParent(g->child, NULL); uiControlDestroy(g->child); } uiWindowsEnsureDestroyWindow(g->hwnd); uiFreeControl(uiControl(g)); } uiWindowsControlDefaultHandle(uiGroup) uiWindowsControlDefaultParent(uiGroup) uiWindowsControlDefaultSetParent(uiGroup) uiWindowsControlDefaultToplevel(uiGroup) uiWindowsControlDefaultVisible(uiGroup) uiWindowsControlDefaultShow(uiGroup) uiWindowsControlDefaultHide(uiGroup) uiWindowsControlDefaultEnabled(uiGroup) uiWindowsControlDefaultEnable(uiGroup) uiWindowsControlDefaultDisable(uiGroup) uiWindowsControlDefaultSetFocus(uiGroup) static void uiGroupSyncEnableState(uiWindowsControl *c, int enabled) { uiGroup *g = uiGroup(c); if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(g), enabled)) return; EnableWindow(g->hwnd, enabled); if (g->child != NULL) uiWindowsControlSyncEnableState(uiWindowsControl(g->child), enabled); } uiWindowsControlDefaultSetParentHWND(uiGroup) static void uiGroupMinimumSize(uiWindowsControl *c, int *width, int *height) { uiGroup *g = uiGroup(c); int mx, mtop, mbottom; int labelWidth; *width = 0; *height = 0; if (g->child != NULL) uiWindowsControlMinimumSize(uiWindowsControl(g->child), width, height); labelWidth = uiWindowsWindowTextWidth(g->hwnd); if (*width < labelWidth) // don't clip the label; it doesn't ellipsize *width = labelWidth; groupMargins(g, &mx, &mtop, &mbottom); *width += 2 * mx; *height += mtop + mbottom; } static void uiGroupMinimumSizeChanged(uiWindowsControl *c) { uiGroup *g = uiGroup(c); if (uiWindowsControlTooSmall(uiWindowsControl(g))) { uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(g)); return; } groupRelayout(g); } static void uiGroupSetMinSize(uiControl *c, int w, int h) { // checkme uiGroupMinimumSizeChanged(uiWindowsControl(c)); } uiWindowsControlDefaultLayoutRect(uiGroup) uiWindowsControlDefaultAssignControlIDZOrder(uiGroup) static void uiGroupChildVisibilityChanged(uiWindowsControl *c) { // TODO eliminate the redundancy uiWindowsControlMinimumSizeChanged(c); } char *uiGroupTitle(uiGroup *g) { return uiWindowsWindowText(g->hwnd); } void uiGroupSetTitle(uiGroup *g, const char *text) { uiWindowsSetWindowText(g->hwnd, text); // changing the text might necessitate a change in the groupbox's size uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); } void uiGroupSetChild(uiGroup *g, uiControl *child) { if (g->child != NULL) { uiControlSetParent(g->child, NULL); uiWindowsControlSetParentHWND(uiWindowsControl(g->child), NULL); } g->child = child; if (g->child != NULL) { uiControlSetParent(g->child, uiControl(g)); uiWindowsControlSetParentHWND(uiWindowsControl(g->child), g->hwnd); uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl(g->child)); uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); } } int uiGroupMargined(uiGroup *g) { return g->margined; } void uiGroupSetMargined(uiGroup *g, int margined) { g->margined = margined; uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); } static LRESULT CALLBACK groupSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { uiGroup *g = uiGroup(dwRefData); WINDOWPOS *wp = (WINDOWPOS *) lParam; MINMAXINFO *mmi = (MINMAXINFO *) lParam; int minwid, minht; LRESULT lResult; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) return lResult; switch (uMsg) { case WM_WINDOWPOSCHANGED: if ((wp->flags & SWP_NOSIZE) != 0) break; groupRelayout(g); return 0; case WM_GETMINMAXINFO: lResult = DefWindowProcW(hwnd, uMsg, wParam, lParam); uiWindowsControlMinimumSize(uiWindowsControl(g), &minwid, &minht); mmi->ptMinTrackSize.x = minwid; mmi->ptMinTrackSize.y = minht; return lResult; case WM_NCDESTROY: if (RemoveWindowSubclass(hwnd, groupSubProc, uIdSubclass) == FALSE) logLastError(L"error removing groupbox subclass"); break; } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } uiGroup *uiNewGroup(const char *text) { uiGroup *g; WCHAR *wtext; uiWindowsNewControl(uiGroup, g); wtext = toUTF16(text); g->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CONTROLPARENT, L"button", wtext, BS_GROUPBOX, hInstance, NULL, TRUE); uiFree(wtext); if (SetWindowSubclass(g->hwnd, groupSubProc, 0, (DWORD_PTR) g) == FALSE) logLastError(L"error subclassing groupbox to handle parent messages"); return g; }