libui/windows/box.cpp

302 lines
7.1 KiB
C++
Raw Normal View History

2015-04-22 16:04:30 -05:00
// 7 april 2015
#include "uipriv_windows.hpp"
// TODO C++-ize
2015-04-22 16:04:30 -05:00
struct uiBox {
uiWindowsControl c;
HWND hwnd;
struct ptrArray *controls;
2015-04-22 16:04:30 -05:00
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 *);
2015-04-22 16:04:30 -05:00
uiWindowsDefineControlWithOnDestroy(
uiBox, // type name
onDestroy(me); // on destroy
)
static void onDestroy(uiBox *b)
2015-04-22 16:04:30 -05:00
{
struct child *bc;
2015-04-22 16:04:30 -05:00
while (b->controls->len != 0) {
bc = ptrArrayIndex(b->controls, struct child *, 0);
ptrArrayDelete(b->controls, 0);
childDestroy(bc);
2015-04-22 16:54:05 -05:00
}
ptrArrayDestroy(b->controls);
}
static void minimumSize(uiWindowsControl *c, uiWindowsSizing *d, intmax_t *width, intmax_t *height)
{
uiBox *b = uiBox(c);
struct child *bc;
2015-04-22 16:04:30 -05:00
int xpadding, ypadding;
uintmax_t nStretchy;
2015-09-01 08:18:37 -05:00
// 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
2015-04-22 16:04:30 -05:00
intmax_t maxStretchyWidth, maxStretchyHeight;
uintmax_t i;
2015-09-01 08:18:37 -05:00
intmax_t minimumWidth, minimumHeight;
uiWindowsSizing *dself;
2015-04-22 16:04:30 -05:00
*width = 0;
*height = 0;
if (b->controls->len == 0)
2015-04-22 16:04:30 -05:00
return;
2015-09-01 08:18:37 -05:00
dself = uiWindowsNewSizing(b->hwnd);
2015-04-22 16:04:30 -05:00
// 0) get this Box's padding
xpadding = 0;
ypadding = 0;
if (b->padded) {
2015-05-07 13:33:46 -05:00
xpadding = d->XPadding;
ypadding = d->YPadding;
2015-04-22 16:04:30 -05:00
}
// 1) initialize the desired rect with the needed padding
// TODO this is wrong if any controls are hidden
2015-04-22 16:04:30 -05:00
if (b->vertical)
*height = (b->controls->len - 1) * ypadding;
2015-04-22 16:04:30 -05:00
else
*width = (b->controls->len - 1) * xpadding;
2015-04-22 16:04:30 -05:00
// 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);
2015-09-01 15:44:09 -05:00
if (!childVisible(bc))
continue;
2015-09-01 08:18:37 -05:00
childMinimumSize(bc, dself, &minimumWidth, &minimumHeight);
if (ctrlStretchy(bc)) {
2015-04-22 16:04:30 -05:00
nStretchy++;
2015-09-01 08:18:37 -05:00
if (maxStretchyWidth < minimumWidth)
maxStretchyWidth = minimumWidth;
if (maxStretchyHeight < minimumHeight)
maxStretchyHeight = minimumHeight;
2015-04-22 16:04:30 -05:00
}
if (b->vertical) {
2015-09-01 08:18:37 -05:00
if (*width < minimumWidth)
*width = minimumWidth;
if (!ctrlStretchy(bc))
*height += minimumHeight;
2015-04-22 16:04:30 -05:00
} else {
2015-09-01 08:18:37 -05:00
if (!ctrlStretchy(bc))
*width += minimumWidth;
if (*height < minimumHeight)
*height = minimumHeight;
2015-04-22 16:04:30 -05:00
}
}
// 3) and now we can add in stretchy controls
if (b->vertical)
*height += nStretchy * maxStretchyHeight;
else
*width += nStretchy * maxStretchyWidth;
2015-09-01 08:18:37 -05:00
uiWindowsFreeSizing(dself);
2015-04-22 16:04:30 -05:00
}
2015-09-01 06:21:18 -05:00
static void boxRelayout(uiWindowsControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height)
2015-04-22 16:04:30 -05:00
{
2015-09-01 08:18:37 -05:00
uiBox *b = uiBox(c);
struct child *bc;
2015-04-22 16:04:30 -05:00
int xpadding, ypadding;
uintmax_t nStretchy;
intmax_t stretchywid, stretchyht;
uintmax_t i;
2015-09-01 08:18:37 -05:00
intmax_t minimumWidth, minimumHeight;
uiWindowsSizing *d;
2015-04-22 16:04:30 -05:00
uiWindowsEnsureMoveWindowDuringResize(b->hwnd, x, y, width, height);
2015-05-15 23:44:24 -05:00
if (b->controls->len == 0)
2015-04-22 16:04:30 -05:00
return;
2015-09-01 08:18:37 -05:00
d = uiWindowsNewSizing(b->hwnd);
2015-04-22 16:04:30 -05:00
// -1) get this Box's padding
xpadding = 0;
ypadding = 0;
if (b->padded) {
2015-05-07 13:33:46 -05:00
xpadding = d->XPadding;
ypadding = d->YPadding;
2015-04-22 16:04:30 -05:00
}
// 0) inset the available rect by the needed padding
// TODO this is incorrect if any controls are hidden
2015-04-22 16:04:30 -05:00
if (b->vertical)
height -= (b->controls->len - 1) * ypadding;
2015-04-22 16:04:30 -05:00
else
width -= (b->controls->len - 1) * xpadding;
2015-04-22 16:04:30 -05:00
// 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);
2015-09-01 15:44:09 -05:00
if (!childVisible(bc))
continue;
2015-09-01 08:18:37 -05:00
if (ctrlStretchy(bc)) {
2015-04-22 16:04:30 -05:00
nStretchy++;
continue;
}
2015-09-01 08:18:37 -05:00
childMinimumSize(bc, d, &minimumWidth, &minimumHeight);
2015-04-22 16:04:30 -05:00
if (b->vertical) { // all controls have same width
2015-09-01 08:18:37 -05:00
ctrlSetWidth(bc, width);
ctrlSetHeight(bc, minimumHeight);
stretchyht -= minimumHeight;
2015-04-22 16:04:30 -05:00
} else { // all controls have same height
2015-09-01 08:18:37 -05:00
ctrlSetWidth(bc, minimumWidth);
ctrlSetHeight(bc, height);
stretchywid -= minimumWidth;
2015-04-22 16:04:30 -05:00
}
}
// 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);
2015-09-01 15:44:09 -05:00
if (!childVisible(bc))
continue;
2015-09-01 08:18:37 -05:00
if (ctrlStretchy(bc)) {
ctrlSetWidth(bc, stretchywid);
ctrlSetHeight(bc, stretchyht);
2015-04-22 16:04:30 -05:00
}
}
// 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);
2015-09-01 15:44:09 -05:00
if (!childVisible(bc))
continue;
2015-09-01 08:39:25 -05:00
childRelayout(bc, x, y, ctrlWidth(bc), ctrlHeight(bc));
2015-04-22 16:04:30 -05:00
if (b->vertical)
2015-09-01 08:18:37 -05:00
y += ctrlHeight(bc) + ypadding;
2015-04-22 16:04:30 -05:00
else
2015-09-01 08:18:37 -05:00
x += ctrlWidth(bc) + xpadding;
2015-04-22 16:04:30 -05:00
}
2015-09-01 08:18:37 -05:00
uiWindowsFreeSizing(d);
2015-04-22 16:04:30 -05:00
}
static void boxContainerUpdateState(uiControl *c)
2015-05-15 23:44:24 -05:00
{
uiBox *b = uiBox(c);
struct child *bc;
2015-05-15 23:44:24 -05:00
uintmax_t i;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
childUpdateState(bc);
2015-05-15 23:44:24 -05:00
}
}
2015-09-02 08:36:53 -05:00
static void redoControlIDsZOrder(uiBox *b)
2015-04-22 16:04:30 -05:00
{
struct child *bc;
2015-09-02 08:36:53 -05:00
LONG_PTR controlID;
HWND insertAfter;
uintmax_t i;
2015-09-02 08:36:53 -05:00
controlID = 100;
insertAfter = NULL;
for (i = 0; i < b->controls->len; i++) {
bc = ptrArrayIndex(b->controls, struct child *, i);
childAssignControlIDZOrder(bc, &controlID, &insertAfter);
}
2015-09-02 08:36:53 -05:00
}
static void boxArrangeChildrenControlIDsZOrder(uiWindowsControl *c)
{
uiBox *b = uiBox(c);
redoControlIDsZOrder(b);
}
2015-09-02 08:36:53 -05:00
void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
{
struct child *bc;
2015-04-22 16:04:30 -05:00
bc = newChild(c, uiControl(b), b->hwnd);
ctrlSetStretchy(bc, stretchy);
ptrArrayAppend(b->controls, bc);
2015-09-02 08:36:53 -05:00
redoControlIDsZOrder(b);
2015-09-01 06:21:18 -05:00
uiWindowsControlQueueRelayout(uiWindowsControl(b));
2015-04-22 16:04:30 -05:00
}
void uiBoxDelete(uiBox *b, uintmax_t index)
2015-04-22 16:04:30 -05:00
{
struct child *bc;
2015-04-22 16:04:30 -05:00
bc = ptrArrayIndex(b->controls, struct child *, index);
ptrArrayDelete(b->controls, index);
childRemove(bc);
2015-09-02 08:36:53 -05:00
redoControlIDsZOrder(b);
2015-09-01 06:21:18 -05:00
uiWindowsControlQueueRelayout(uiWindowsControl(b));
2015-04-22 16:04:30 -05:00
}
int uiBoxPadded(uiBox *b)
2015-04-22 16:04:30 -05:00
{
return b->padded;
}
void uiBoxSetPadded(uiBox *b, int padded)
2015-04-22 16:04:30 -05:00
{
b->padded = padded;
2015-09-01 06:21:18 -05:00
uiWindowsControlQueueRelayout(uiWindowsControl(b));
2015-04-22 16:04:30 -05:00
}
static uiBox *finishNewBox(int vertical)
2015-04-22 16:04:30 -05:00
{
uiBox *b;
2015-04-22 16:04:30 -05:00
b = (uiBox *) uiNewControl(uiBoxType());
2015-04-22 16:04:30 -05:00
b->hwnd = newContainer();
b->vertical = vertical;
b->controls = newPtrArray();
uiWindowsFinishNewControl(b, uiBox);
2015-09-01 06:21:18 -05:00
uiControl(b)->ContainerUpdateState = boxContainerUpdateState;
uiWindowsControl(b)->Relayout = boxRelayout;
uiWindowsControl(b)->ArrangeChildrenControlIDsZOrder = boxArrangeChildrenControlIDsZOrder;
return b;
2015-04-22 16:04:30 -05:00
}
uiBox *uiNewHorizontalBox(void)
2015-04-22 16:04:30 -05:00
{
return finishNewBox(0);
}
2015-04-22 16:04:30 -05:00
uiBox *uiNewVerticalBox(void)
{
return finishNewBox(1);
2015-04-22 16:04:30 -05:00
}