320 lines
8.0 KiB
C++
320 lines
8.0 KiB
C++
|
// 8 june 2016
|
||
|
#include "uipriv_windows.hpp"
|
||
|
|
||
|
struct formChild {
|
||
|
uiControl *c;
|
||
|
HWND label;
|
||
|
int stretchy;
|
||
|
int height;
|
||
|
};
|
||
|
|
||
|
struct uiForm {
|
||
|
uiWindowsControl c;
|
||
|
HWND hwnd;
|
||
|
std::vector<struct formChild> *controls;
|
||
|
int padded;
|
||
|
};
|
||
|
|
||
|
static void formPadding(uiForm *f, int *xpadding, int *ypadding)
|
||
|
{
|
||
|
uiWindowsSizing sizing;
|
||
|
|
||
|
*xpadding = 0;
|
||
|
*ypadding = 0;
|
||
|
if (f->padded) {
|
||
|
uiWindowsGetSizing(f->hwnd, &sizing);
|
||
|
uiWindowsSizingStandardPadding(&sizing, xpadding, ypadding);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// via http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing
|
||
|
#define labelHeight 8
|
||
|
#define labelYOffset 3
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void uiFormDestroy(uiControl *c)
|
||
|
{
|
||
|
uiForm *f = uiForm(c);
|
||
|
|
||
|
for (const struct formChild &fc : *(f->controls)) {
|
||
|
uiControlSetParent(fc.c, NULL);
|
||
|
uiControlDestroy(fc.c);
|
||
|
uiWindowsEnsureDestroyWindow(fc.label);
|
||
|
}
|
||
|
delete f->controls;
|
||
|
uiWindowsEnsureDestroyWindow(f->hwnd);
|
||
|
uiFreeControl(uiControl(f));
|
||
|
}
|
||
|
|
||
|
uiWindowsControlDefaultHandle(uiForm)
|
||
|
uiWindowsControlDefaultParent(uiForm)
|
||
|
uiWindowsControlDefaultSetParent(uiForm)
|
||
|
uiWindowsControlDefaultToplevel(uiForm)
|
||
|
uiWindowsControlDefaultVisible(uiForm)
|
||
|
uiWindowsControlDefaultShow(uiForm)
|
||
|
uiWindowsControlDefaultHide(uiForm)
|
||
|
uiWindowsControlDefaultEnabled(uiForm)
|
||
|
uiWindowsControlDefaultEnable(uiForm)
|
||
|
uiWindowsControlDefaultDisable(uiForm)
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
uiWindowsControlDefaultSetParentHWND(uiForm)
|
||
|
|
||
|
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 void uiFormMinimumSizeChanged(uiWindowsControl *c)
|
||
|
{
|
||
|
uiForm *f = uiForm(c);
|
||
|
|
||
|
if (uiWindowsControlTooSmall(uiWindowsControl(f))) {
|
||
|
uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(f));
|
||
|
return;
|
||
|
}
|
||
|
formRelayout(f);
|
||
|
}
|
||
|
|
||
|
uiWindowsControlDefaultLayoutRect(uiForm)
|
||
|
uiWindowsControlDefaultAssignControlIDZOrder(uiForm)
|
||
|
|
||
|
static void uiFormChildVisibilityChanged(uiWindowsControl *c)
|
||
|
{
|
||
|
// TODO eliminate the redundancy
|
||
|
uiWindowsControlMinimumSizeChanged(c);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
uiprivFree(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));
|
||
|
}
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
int uiFormPadded(uiForm *f)
|
||
|
{
|
||
|
return f->padded;
|
||
|
}
|
||
|
|
||
|
void uiFormSetPadded(uiForm *f, int padded)
|
||
|
{
|
||
|
f->padded = padded;
|
||
|
uiWindowsControlMinimumSizeChanged(uiWindowsControl(f));
|
||
|
}
|
||
|
|
||
|
static void onResize(uiWindowsControl *c)
|
||
|
{
|
||
|
formRelayout(uiForm(c));
|
||
|
}
|
||
|
|
||
|
uiForm *uiNewForm(void)
|
||
|
{
|
||
|
uiForm *f;
|
||
|
|
||
|
uiWindowsNewControl(uiForm, f);
|
||
|
|
||
|
f->hwnd = uiWindowsMakeContainer(uiWindowsControl(f), onResize);
|
||
|
|
||
|
f->controls = new std::vector<struct formChild>;
|
||
|
|
||
|
return f;
|
||
|
}
|