static void uiFormSyncEnableState(uiWindowsControl *c, int enabled) { uiForm *f = uiForm(c); if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(f), enabled)) return; for (const struct formChild &fc : *(f->controls)) uiWindowsControlSyncEnableState(uiWindowsControl(fc.c), enabled); }
static void uiFormMinimumSizeChanged(uiWindowsControl *c) { uiForm *f = uiForm(c); if (uiWindowsControlTooSmall(uiWindowsControl(f))) { uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(f)); return; } formRelayout(f); }
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); }
static void uiGroupMinimumSizeChanged(uiWindowsControl *c) { uiGroup *g = uiGroup(c); if (uiWindowsControlTooSmall(uiWindowsControl(g))) { uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(g)); return; } groupRelayout(g); }
void uiFormDelete(uiForm *f, int index) { struct formChild fc; fc = (*(f->controls))[index]; uiControlSetParent(fc.c, NULL); uiWindowsControlSetParentHWND(uiWindowsControl(fc.c), NULL); uiWindowsEnsureDestroyWindow(fc.label); f->controls->erase(f->controls->begin() + index); formArrangeChildren(f); uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); }
void uiWindowSetChild(uiWindow *w, uiControl *child) { if (w->child != NULL) { uiControlSetParent(w->child, NULL); uiWindowsControlSetParentHWND(uiWindowsControl(w->child), NULL); } w->child = child; if (w->child != NULL) { uiControlSetParent(w->child, uiControl(w)); uiWindowsControlSetParentHWND(uiWindowsControl(w->child), w->hwnd); uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl(w->child)); windowRelayout(w); } }
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)); } }
uiSpinbox *uiNewSpinbox(int min, int max) { uiSpinbox *s; int temp; if (min >= max) { temp = min; min = max; max = temp; } uiWindowsNewControl(uiSpinbox, s); s->hwnd = uiWindowsMakeContainer(uiWindowsControl(s), onResize); s->edit = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, L"edit", L"", // don't use ES_NUMBER; it doesn't allow typing in a leading - ES_AUTOHSCROLL | ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, hInstance, NULL, TRUE); uiWindowsEnsureSetParentHWND(s->edit, s->hwnd); uiWindowsRegisterWM_COMMANDHandler(s->edit, onWM_COMMAND, uiControl(s)); uiSpinboxOnChanged(s, defaultOnChanged, NULL); recreateUpDown(s); s->inhibitChanged = TRUE; SendMessageW(s->updown, UDM_SETRANGE32, (WPARAM) min, (LPARAM) max); SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) min); s->inhibitChanged = FALSE; return s; }
void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) { HWND hwnd; WCHAR *wtext; DWORD groupTabStop; // the first radio button gets both WS_GROUP and WS_TABSTOP // successive radio buttons get *neither* groupTabStop = 0; if (r->hwnds->size() == 0) groupTabStop = WS_GROUP | WS_TABSTOP; wtext = toUTF16(text); hwnd = uiWindowsEnsureCreateControlHWND(0, L"button", wtext, BS_RADIOBUTTON | groupTabStop, hInstance, NULL, TRUE); uiFree(wtext); uiWindowsEnsureSetParentHWND(hwnd, r->hwnd); uiWindowsRegisterWM_COMMANDHandler(hwnd, onWM_COMMAND, uiControl(r)); r->hwnds->push_back(hwnd); radiobuttonsArrangeChildren(r); uiWindowsControlMinimumSizeChanged(uiWindowsControl(r)); }
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; }
void uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl *c) { uiControl *parent; parent = uiControlParent(uiControl(c)); if (parent != NULL) uiWindowsControlMinimumSizeChanged(uiWindowsControl(parent)); }
void uiWindowsRearrangeControlIDsZOrder(uiControl *c) { uiWindowsControl *wc; c = uiControlParent(c); if (c == NULL) return; wc = uiWindowsControl(c); (*(wc->ArrangeChildrenControlIDsZOrder))(wc); }
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); }
void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) { struct formChild fc; WCHAR *wlabel; fc.c = c; wlabel = toUTF16(label); fc.label = uiWindowsEnsureCreateControlHWND(0, L"STATIC", wlabel, SS_LEFT | SS_NOPREFIX, hInstance, NULL, TRUE); uiFree(wlabel); uiWindowsEnsureSetParentHWND(fc.label, f->hwnd); fc.stretchy = stretchy; uiControlSetParent(fc.c, uiControl(f)); uiWindowsControlSetParentHWND(uiWindowsControl(fc.c), f->hwnd); f->controls->push_back(fc); formArrangeChildren(f); uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); }
uiForm *uiNewForm(void) { uiForm *f; uiWindowsNewControl(uiForm, f); f->hwnd = uiWindowsMakeContainer(uiWindowsControl(f), onResize); f->controls = new std::vector<struct formChild>; return f; }
uiRadioButtons *uiNewRadioButtons(void) { uiRadioButtons *r; uiWindowsNewControl(uiRadioButtons, r); r->hwnd = uiWindowsMakeContainer(uiWindowsControl(r), onResize); r->hwnds = new std::vector<HWND>; return r; }
static void uiWindowMinimumSizeChanged(uiWindowsControl *c) { uiWindow *w = uiWindow(c); if (uiWindowsControlTooSmall(uiWindowsControl(w))) { // TODO figure out what to do with this function // maybe split it into two so WM_GETMINMAXINFO can use it? ensureMinimumWindowSize(w); return; } // otherwise we only need to re-layout everything windowRelayout(w); }
static void formArrangeChildren(uiForm *f) { LONG_PTR controlID; HWND insertAfter; int i; controlID = 100; insertAfter = NULL; for (const struct formChild &fc : *(f->controls)) { // TODO assign label ID and z-order uiWindowsControlAssignControlIDZOrder(uiWindowsControl(fc.c), &controlID, &insertAfter); } }
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 uiWindowShow(uiControl *c) { uiWindow *w = uiWindow(c); w->visible = 1; // just in case the window's minimum size wasn't recalculated already ensureMinimumWindowSize(w); if (w->shownOnce) { ShowWindow(w->hwnd, SW_SHOW); return; } w->shownOnce = TRUE; // make sure the child is the correct size uiWindowsControlMinimumSizeChanged(uiWindowsControl(w)); ShowWindow(w->hwnd, nCmdShow); if (UpdateWindow(w->hwnd) == 0) logLastError(L"error calling UpdateWindow() after showing uiWindow for the first time"); }
void uiWindowsControlQueueRelayout(uiWindowsControl *c) { uiControl *cc; uintmax_t i; uiWindowsControl *d; // resizing a control requires us to reocmpute the sizes of everything in the top-level window cc = toplevelOwning(uiControl(c)); if (cc == NULL) return; c = uiWindowsControl(cc); // make sure we're only queued once for (i = 0 ; i < resizes->len; i++) { d = ptrArrayIndex(resizes, uiWindowsControl *, i); if (c == d) return; } ptrArrayAppend(resizes, c); }
void uiImageBoxSetImage(uiImageBox *i, uiImage *image) { HBITMAP bmp = uiImageGetHBITMAP(image); // it is a responsibility of the programmer to delete the old bitmap HBITMAP old = (HBITMAP) SendMessage(i->hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) bmp); if (old && old != bmp) { DeleteObject(old); } // sometimes an internal copy is made and bmp must be deleted HBITMAP copy = (HBITMAP) SendMessage(i->hwnd, STM_GETIMAGE, IMAGE_BITMAP, 0); if (copy && bmp && copy != bmp) { DeleteObject(bmp); } // changing the bitmap might necessitate a change in size uiWindowsControlMinimumSizeChanged(uiWindowsControl(i)); }
uiWindowsControl *uiWindowsAllocControl(size_t n, uint32_t typesig, const char *typenamestr) { return uiWindowsControl(uiAllocControl(n, uiWindowsControlSignature, typesig, typenamestr)); }
void uiCheckboxSetText(uiCheckbox *c, const char *text) { uiWindowsSetWindowText(c->hwnd, text); // changing the text might necessitate a change in the checkbox's size uiWindowsControlMinimumSizeChanged(uiWindowsControl(c)); }
static void uiFormSetMinSize(uiControl *c, int w, int h) { // checkme uiFormMinimumSizeChanged(uiWindowsControl(c)); }
void uiFormSetPadded(uiForm *f, int padded) { f->padded = padded; uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); }
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; } }