libui/windows/box.c

301 lines
7.1 KiB
C

// 7 april 2015
#include "uipriv_windows.h"
struct uiBox {
uiWindowsControl c;
HWND hwnd;
struct ptrArray *controls;
int vertical;
int padded;
};
#define ctrlStretchy(child) childFlag(child)
#define ctrlSetStretchy(child, stretchy) childSetFlag(child, stretchy)
// both used by resize(); preallocated to save time and reduce risk of failure
#define ctrlWidth(child) childIntmax(child, 0)
#define ctrlSetWidth(child, w) childSetIntmax(child, 0, w)
#define ctrlHeight(child) childIntmax(child, 1)
#define ctrlSetHeight(child, h) childSetIntmax(child, 1, h)
static void onDestroy(uiBox *);
uiWindowsDefineControlWithOnDestroy(
uiBox, // type name
uiBoxType, // type function
onDestroy(this); // on destroy
)
static void onDestroy(uiBox *b)
{
struct child *bc;
while (b->controls->len != 0) {
bc = ptrArrayIndex(b->controls, struct child *, 0);
ptrArrayDelete(b->controls, 0);
childDestroy(bc);
}
ptrArrayDestroy(b->controls);
}
static void minimumSize(uiWindowsControl *c, uiWindowsSizing *d, intmax_t *width, intmax_t *height)
{
uiBox *b = uiBox(c);
struct child *bc;
int xpadding, ypadding;
uintmax_t nStretchy;
// these two contain the largest minimum width and height of all stretchy controls in the box
// all stretchy controls will use this value to determine the final minimum size
intmax_t maxStretchyWidth, maxStretchyHeight;
uintmax_t i;
intmax_t minimumWidth, minimumHeight;
uiWindowsSizing *dself;
*width = 0;
*height = 0;
if (b->controls->len == 0)
return;
dself = uiWindowsNewSizing(b->hwnd);
// 0) get this Box's padding
xpadding = 0;
ypadding = 0;
if (b->padded) {
xpadding = d->XPadding;
ypadding = d->YPadding;
}
// 1) initialize the desired rect with the needed padding
// TODO this is wrong if any controls are hidden
if (b->vertical)
*height = (b->controls->len - 1) * ypadding;
else
*width = (b->controls->len - 1) * xpadding;
// 2) add in the size of non-stretchy controls and get (but not add in) the largest widths and heights of stretchy controls
// we still add in like direction of stretchy controls
nStretchy = 0;
maxStretchyWidth = 0;
maxStretchyHeight = 0;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
if (!childVisible(bc))
continue;
childMinimumSize(bc, dself, &minimumWidth, &minimumHeight);
if (ctrlStretchy(bc)) {
nStretchy++;
if (maxStretchyWidth < minimumWidth)
maxStretchyWidth = minimumWidth;
if (maxStretchyHeight < minimumHeight)
maxStretchyHeight = minimumHeight;
}
if (b->vertical) {
if (*width < minimumWidth)
*width = minimumWidth;
if (!ctrlStretchy(bc))
*height += minimumHeight;
} else {
if (!ctrlStretchy(bc))
*width += minimumWidth;
if (*height < minimumHeight)
*height = minimumHeight;
}
}
// 3) and now we can add in stretchy controls
if (b->vertical)
*height += nStretchy * maxStretchyHeight;
else
*width += nStretchy * maxStretchyWidth;
uiWindowsFreeSizing(dself);
}
static void boxRelayout(uiWindowsControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height)
{
uiBox *b = uiBox(c);
struct child *bc;
int xpadding, ypadding;
uintmax_t nStretchy;
intmax_t stretchywid, stretchyht;
uintmax_t i;
intmax_t minimumWidth, minimumHeight;
uiWindowsSizing *d;
uiWindowsEnsureMoveWindow(b->hwnd, x, y, width, height);
if (b->controls->len == 0)
return;
d = uiWindowsNewSizing(b->hwnd);
// -1) get this Box's padding
xpadding = 0;
ypadding = 0;
if (b->padded) {
xpadding = d->XPadding;
ypadding = d->YPadding;
}
// 0) inset the available rect by the needed padding
// TODO this is incorrect if any controls are hidden
if (b->vertical)
height -= (b->controls->len - 1) * ypadding;
else
width -= (b->controls->len - 1) * xpadding;
// 1) get width and height of non-stretchy controls
// this will tell us how much space will be left for stretchy controls
stretchywid = width;
stretchyht = height;
nStretchy = 0;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
if (!childVisible(bc))
continue;
if (ctrlStretchy(bc)) {
nStretchy++;
continue;
}
childMinimumSize(bc, d, &minimumWidth, &minimumHeight);
if (b->vertical) { // all controls have same width
ctrlSetWidth(bc, width);
ctrlSetHeight(bc, minimumHeight);
stretchyht -= minimumHeight;
} else { // all controls have same height
ctrlSetWidth(bc, minimumWidth);
ctrlSetHeight(bc, height);
stretchywid -= minimumWidth;
}
}
// 2) now get the size of stretchy controls
if (nStretchy != 0)
if (b->vertical)
stretchyht /= nStretchy;
else
stretchywid /= nStretchy;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
if (!childVisible(bc))
continue;
if (ctrlStretchy(bc)) {
ctrlSetWidth(bc, stretchywid);
ctrlSetHeight(bc, stretchyht);
}
}
// 3) now we can position controls
// first, make relative to the top-left corner of the container
x = 0;
y = 0;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
if (!childVisible(bc))
continue;
childRelayout(bc, x, y, ctrlWidth(bc), ctrlHeight(bc));
if (b->vertical)
y += ctrlHeight(bc) + ypadding;
else
x += ctrlWidth(bc) + xpadding;
}
uiWindowsFreeSizing(d);
}
static void boxContainerUpdateState(uiControl *c)
{
uiBox *b = uiBox(c);
struct child *bc;
uintmax_t i;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
childUpdateState(bc);
}
}
static void redoControlIDsZOrder(uiBox *b)
{
struct child *bc;
LONG_PTR controlID;
HWND insertAfter;
uintmax_t i;
controlID = 100;
insertAfter = NULL;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
childAssignControlIDZOrder(bc, &controlID, &insertAfter);
}
}
static void boxArrangeChildrenControlIDsZOrder(uiWindowsControl *c)
{
uiBox *b = uiBox(c);
redoControlIDsZOrder(b);
}
void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
{
struct child *bc;
bc = newChild(c, uiControl(b), b->hwnd);
ctrlSetStretchy(bc, stretchy);
ptrArrayAppend(b->controls, bc);
redoControlIDsZOrder(b);
uiWindowsControlQueueRelayout(uiWindowsControl(b));
}
void uiBoxDelete(uiBox *b, uintmax_t index)
{
struct child *bc;
bc = ptrArrayIndex(b->controls, struct child *, index);
ptrArrayDelete(b->controls, index);
childRemove(bc);
redoControlIDsZOrder(b);
uiWindowsControlQueueRelayout(uiWindowsControl(b));
}
int uiBoxPadded(uiBox *b)
{
return b->padded;
}
void uiBoxSetPadded(uiBox *b, int padded)
{
b->padded = padded;
uiWindowsControlQueueRelayout(uiWindowsControl(b));
}
static uiBox *finishNewBox(int vertical)
{
uiBox *b;
b = (uiBox *) uiNewControl(uiBoxType());
b->hwnd = newContainer();
b->vertical = vertical;
b->controls = newPtrArray();
uiWindowsFinishNewControl(b, uiBox);
uiControl(b)->ContainerUpdateState = boxContainerUpdateState;
uiWindowsControl(b)->Relayout = boxRelayout;
uiWindowsControl(b)->ArrangeChildrenControlIDsZOrder = boxArrangeChildrenControlIDsZOrder;
return b;
}
uiBox *uiNewHorizontalBox(void)
{
return finishNewBox(0);
}
uiBox *uiNewVerticalBox(void)
{
return finishNewBox(1);
}