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); }
static void uiFormMinimumSize(uiWindowsControl *c, int *width, int *height) { uiForm *f = uiForm(c); int xpadding, ypadding; int nStretchy; // these two contain the largest minimum width and height of all stretchy controls in the form // all stretchy controls will use this value to determine the final minimum size int maxLabelWidth, maxControlWidth; int maxStretchyHeight; int labelwid; int i; int minimumWidth, minimumHeight; int nVisible; uiWindowsSizing sizing; *width = 0; *height = 0; if (f->controls->size() == 0) return; // 0) get this Form's padding formPadding(f, &xpadding, &ypadding); // 1) determine the longest width of all controls and labels; add in the height of non-stretchy controls and get (but not add in) the largest heights of stretchy controls // we still add in like direction of stretchy controls nStretchy = 0; maxLabelWidth = 0; maxControlWidth = 0; maxStretchyHeight = 0; nVisible = 0; for (const struct formChild &fc : *(f->controls)) { if (!uiControlVisible(fc.c)) continue; nVisible++; labelwid = uiWindowsWindowTextWidth(fc.label); if (maxLabelWidth < labelwid) maxLabelWidth = labelwid; uiWindowsControlMinimumSize(uiWindowsControl(fc.c), &minimumWidth, &minimumHeight); if (fc.stretchy) { nStretchy++; if (maxStretchyHeight < minimumHeight) maxStretchyHeight = minimumHeight; } if (maxControlWidth < minimumWidth) maxControlWidth = minimumWidth; if (!fc.stretchy) *height += minimumHeight; } if (nVisible == 0) // nothing to show; return 0x0 return; *width += maxLabelWidth + maxControlWidth; // 2) outset the desired rect with the needed padding *width += xpadding; *height += (nVisible - 1) * ypadding; // 3) and now we can add in stretchy controls *height += nStretchy * maxStretchyHeight; }
static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LONG_PTR ww; uiWindow *w; CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; WINDOWPOS *wp = (WINDOWPOS *) lParam; MINMAXINFO *mmi = (MINMAXINFO *) lParam; intmax_t width, height; LRESULT lResult; ww = GetWindowLongPtrW(hwnd, GWLP_USERDATA); if (ww == 0) { if (uMsg == WM_CREATE) SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams)); // fall through to DefWindowProc() anyway return DefWindowProcW(hwnd, uMsg, wParam, lParam); } w = uiWindow((void *) ww); if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) return lResult; switch (uMsg) { case WM_COMMAND: // not a menu if (lParam != 0) break; if (HIWORD(wParam) != 0) break; runMenuEvent(LOWORD(wParam), uiWindow(w)); return 0; case WM_WINDOWPOSCHANGED: if ((wp->flags & SWP_NOSIZE) != 0) break; windowRelayout(w); return 0; case WM_GETMINMAXINFO: // ensure the user cannot resize the window smaller than its minimum size lResult = DefWindowProcW(hwnd, uMsg, wParam, lParam); uiWindowsControlMinimumSize(uiWindowsControl(w), &width, &height); // width and height are in client coordinates; ptMinTrackSize is in window coordinates clientSizeToWindowSize(w->hwnd, &width, &height, w->hasMenubar); mmi->ptMinTrackSize.x = width; mmi->ptMinTrackSize.y = height; return lResult; case WM_PRINTCLIENT: // we do no special painting; just erase the background // don't worry about the return value; we let DefWindowProcW() handle this message SendMessageW(hwnd, WM_ERASEBKGND, wParam, lParam); return 0; case WM_CLOSE: if ((*(w->onClosing))(w, w->onClosingData)) uiControlDestroy(uiControl(w)); return 0; // we destroyed it already } return DefWindowProcW(hwnd, uMsg, wParam, lParam); }
BOOL uiWindowsControlTooSmall(uiWindowsControl *c) { RECT r; int width, height; uiWindowsControlLayoutRect(c, &r); uiWindowsControlMinimumSize(c, &width, &height); if ((r.right - r.left) < width) return TRUE; if ((r.bottom - r.top) < height) return TRUE; return FALSE; }
static void uiWindowMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) { uiWindow *w = uiWindow(c); uiWindowsSizing sizing; int mx, my; *width = 0; *height = 0; if (w->child != NULL) uiWindowsControlMinimumSize(uiWindowsControl(w->child), width, height); windowMargins(w, &mx, &my); *width += 2 * mx; *height += 2 * my; }
// this cannot queue a resize because it's called by the resize handler void ensureMinimumWindowSize(uiWindow *w) { intmax_t width, height; RECT r; uiWindowsControlMinimumSize(uiWindowsControl(w), &width, &height); uiWindowsEnsureGetClientRect(w->hwnd, &r); if (width < (r.right - r.left)) // preserve width if larger width = r.right - r.left; if (height < (r.bottom - r.top)) // preserve height if larger height = r.bottom - r.top; clientSizeToWindowSize(w->hwnd, &width, &height, w->hasMenubar); if (SetWindowPos(w->hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0) logLastError(L"error resizing window"); }
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 formRelayout(uiForm *f) { RECT r; int x, y, width, height; int xpadding, ypadding; int nStretchy; int labelwid, stretchyht; int thiswid; int i; int minimumWidth, minimumHeight; uiWindowsSizing sizing; int labelht, labelyoff; int nVisible; if (f->controls->size() == 0) return; uiWindowsEnsureGetClientRect(f->hwnd, &r); x = r.left; y = r.top; width = r.right - r.left; height = r.bottom - r.top; // 0) get this Form's padding formPadding(f, &xpadding, &ypadding); // 1) get width of labels and height of non-stretchy controls // this will tell us how much space will be left for controls labelwid = 0; stretchyht = height; nStretchy = 0; nVisible = 0; for (struct formChild &fc : *(f->controls)) { if (!uiControlVisible(fc.c)) { ShowWindow(fc.label, SW_HIDE); continue; } ShowWindow(fc.label, SW_SHOW); nVisible++; thiswid = uiWindowsWindowTextWidth(fc.label); if (labelwid < thiswid) labelwid = thiswid; if (fc.stretchy) { nStretchy++; continue; } uiWindowsControlMinimumSize(uiWindowsControl(fc.c), &minimumWidth, &minimumHeight); fc.height = minimumHeight; stretchyht -= minimumHeight; } if (nVisible == 0) // nothing to do return; // 2) inset the available rect by the needed padding width -= xpadding; height -= (nVisible - 1) * ypadding; stretchyht -= (nVisible - 1) * ypadding; // 3) now get the width of controls and the height of stretchy controls width -= labelwid; if (nStretchy != 0) { stretchyht /= nStretchy; for (struct formChild &fc : *(f->controls)) { if (!uiControlVisible(fc.c)) continue; if (fc.stretchy) fc.height = stretchyht; } } // 4) get the y offset labelyoff = labelYOffset; uiWindowsGetSizing(f->hwnd, &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &labelyoff); // 5) now we can position controls // first, make relative to the top-left corner of the container // also prefer left alignment on Windows x = labelwid + xpadding; y = 0; for (const struct formChild &fc : *(f->controls)) { if (!uiControlVisible(fc.c)) continue; labelht = labelHeight; uiWindowsGetSizing(f->hwnd, &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &labelht); uiWindowsEnsureMoveWindowDuringResize(fc.label, 0, y + labelyoff - sizing.InternalLeading, labelwid, labelht); uiWindowsEnsureMoveWindowDuringResize((HWND) uiControlHandle(fc.c), x, y, width, fc.height); y += fc.height + ypadding; } }