diff options
Diffstat (limited to 'src/libui_sdl/libui/windows/grid.cpp')
-rw-r--r-- | src/libui_sdl/libui/windows/grid.cpp | 665 |
1 files changed, 0 insertions, 665 deletions
diff --git a/src/libui_sdl/libui/windows/grid.cpp b/src/libui_sdl/libui/windows/grid.cpp deleted file mode 100644 index 0a854c5..0000000 --- a/src/libui_sdl/libui/windows/grid.cpp +++ /dev/null @@ -1,665 +0,0 @@ -// 10 june 2016 -#include "uipriv_windows.hpp" - -// TODO compare with GTK+: -// - what happens if you call InsertAt() twice? -// - what happens if you call Append() twice? - -// TODOs -// - the Assorted page has clipping and repositioning issues - -struct gridChild { - uiControl *c; - int left; - int top; - int xspan; - int yspan; - int hexpand; - uiAlign halign; - int vexpand; - uiAlign valign; - - // have these here so they don't need to be reallocated each relayout - int finalx, finaly; - int finalwidth, finalheight; - int minwidth, minheight; -}; - -struct uiGrid { - uiWindowsControl c; - HWND hwnd; - std::vector<struct gridChild *> *children; - std::map<uiControl *, size_t> *indexof; - int padded; - - int xmin, ymin; - int xmax, ymax; -}; - -static bool gridRecomputeMinMax(uiGrid *g) -{ - bool first = true; - - for (struct gridChild *gc : *(g->children)) { - // this is important; we want g->xmin/g->ymin to satisfy gridLayoutData::visibleRow()/visibleColumn() - if (!uiControlVisible(gc->c)) - continue; - if (first) { - g->xmin = gc->left; - g->ymin = gc->top; - g->xmax = gc->left + gc->xspan; - g->ymax = gc->top + gc->yspan; - first = false; - continue; - } - if (g->xmin > gc->left) - g->xmin = gc->left; - if (g->ymin > gc->top) - g->ymin = gc->top; - if (g->xmax < (gc->left + gc->xspan)) - g->xmax = gc->left + gc->xspan; - if (g->ymax < (gc->top + gc->yspan)) - g->ymax = gc->top + gc->yspan; - } - return first != false; -} - -#define xcount(g) ((g)->xmax - (g)->xmin) -#define ycount(g) ((g)->ymax - (g)->ymin) -#define toxindex(g, x) ((x) - (g)->xmin) -#define toyindex(g, y) ((y) - (g)->ymin) - -class gridLayoutData { - int ycount; -public: - int **gg; // topological map gg[y][x] = control index - int *colwidths; - int *rowheights; - bool *hexpand; - bool *vexpand; - int nVisibleRows; - int nVisibleColumns; - - bool noVisible; - - gridLayoutData(uiGrid *g) - { - size_t i; - int x, y; - - this->noVisible = gridRecomputeMinMax(g); - - this->gg = new int *[ycount(g)]; - for (y = 0; y < ycount(g); y++) { - this->gg[y] = new int[xcount(g)]; - for (x = 0; x < xcount(g); x++) - this->gg[y][x] = -1; - } - - for (i = 0; i < g->children->size(); i++) { - struct gridChild *gc; - - gc = (*(g->children))[i]; - if (!uiControlVisible(gc->c)) - continue; - for (y = gc->top; y < gc->top + gc->yspan; y++) - for (x = gc->left; x < gc->left + gc->xspan; x++) - this->gg[toyindex(g, y)][toxindex(g, x)] = i; - } - - this->colwidths = new int[xcount(g)]; - ZeroMemory(this->colwidths, xcount(g) * sizeof (int)); - this->rowheights = new int[ycount(g)]; - ZeroMemory(this->rowheights, ycount(g) * sizeof (int)); - this->hexpand = new bool[xcount(g)]; - ZeroMemory(this->hexpand, xcount(g) * sizeof (bool)); - this->vexpand = new bool[ycount(g)]; - ZeroMemory(this->vexpand, ycount(g) * sizeof (bool)); - - this->ycount = ycount(g); - - // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, it is invisible and should not be considered for padding amount calculations - // note that the first row and column will always be visible because gridRecomputeMinMax() computed a smallest fitting rectangle - if (this->noVisible) - return; - this->nVisibleRows = 0; - for (y = 0; y < this->ycount; y++) - if (this->visibleRow(g, y)) - this->nVisibleRows++; - this->nVisibleColumns = 0; - for (x = 0; x < xcount(g); x++) - if (this->visibleColumn(g, x)) - this->nVisibleColumns++; - } - - ~gridLayoutData() - { - size_t y; - - delete[] this->hexpand; - delete[] this->vexpand; - delete[] this->colwidths; - delete[] this->rowheights; - for (y = 0; y < this->ycount; y++) - delete[] this->gg[y]; - delete[] this->gg; - } - - bool visibleRow(uiGrid *g, int y) - { - int x; - struct gridChild *gc; - - for (x = 0; x < xcount(g); x++) - if (this->gg[y][x] != -1) { - gc = (*(g->children))[this->gg[y][x]]; - if (gc->yspan == 1 || gc->top - g->ymin == y) - return true; - } - return false; - } - - bool visibleColumn(uiGrid *g, int x) - { - int y; - struct gridChild *gc; - - for (y = 0; y < this->ycount; y++) - if (this->gg[y][x] != -1) { - gc = (*(g->children))[this->gg[y][x]]; - if (gc->xspan == 1 || gc->left - g->xmin == x) - return true; - } - return false; - } -}; - -static void gridPadding(uiGrid *g, int *xpadding, int *ypadding) -{ - uiWindowsSizing sizing; - - *xpadding = 0; - *ypadding = 0; - if (g->padded) { - uiWindowsGetSizing(g->hwnd, &sizing); - uiWindowsSizingStandardPadding(&sizing, xpadding, ypadding); - } -} - -static void gridRelayout(uiGrid *g) -{ - RECT r; - int x, y, width, height; - gridLayoutData *ld; - int xpadding, ypadding; - int ix, iy; - int iwidth, iheight; - int i; - struct gridChild *gc; - int nhexpand, nvexpand; - - if (g->children->size() == 0) - return; // nothing to do - - uiWindowsEnsureGetClientRect(g->hwnd, &r); - x = r.left; - y = r.top; - width = r.right - r.left; - height = r.bottom - r.top; - - gridPadding(g, &xpadding, &ypadding); - ld = new gridLayoutData(g); - if (ld->noVisible) { // nothing to do - delete ld; - return; - } - - // 0) discount padding from width/height - width -= (ld->nVisibleColumns - 1) * xpadding; - height -= (ld->nVisibleRows - 1) * ypadding; - - // 1) compute colwidths and rowheights before handling expansion - // we only count non-spanning controls to avoid weirdness - for (iy = 0; iy < ycount(g); iy++) - for (ix = 0; ix < xcount(g); ix++) { - i = ld->gg[iy][ix]; - if (i == -1) - continue; - gc = (*(g->children))[i]; - uiWindowsControlMinimumSize(uiWindowsControl(gc->c), &iwidth, &iheight); - if (gc->xspan == 1) - if (ld->colwidths[ix] < iwidth) - ld->colwidths[ix] = iwidth; - if (gc->yspan == 1) - if (ld->rowheights[iy] < iheight) - ld->rowheights[iy] = iheight; - // save these for step 6 - gc->minwidth = iwidth; - gc->minheight = iheight; - } - - // 2) figure out which rows/columns expand but not span - // we need to know which expanding rows/columns don't span before we can handle the ones that do - for (i = 0; i < g->children->size(); i++) { - gc = (*(g->children))[i]; - if (!uiControlVisible(gc->c)) - continue; - if (gc->hexpand && gc->xspan == 1) - ld->hexpand[toxindex(g, gc->left)] = true; - if (gc->vexpand && gc->yspan == 1) - ld->vexpand[toyindex(g, gc->top)] = true; - } - - // 3) figure out which rows/columns expand that do span - // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand - for (i = 0; i < g->children->size(); i++) { - gc = (*(g->children))[i]; - if (!uiControlVisible(gc->c)) - continue; - if (gc->hexpand && gc->xspan != 1) { - bool doit = true; - - for (ix = gc->left; ix < gc->left + gc->xspan; ix++) - if (ld->hexpand[toxindex(g, ix)]) { - doit = false; - break; - } - if (doit) - for (ix = gc->left; ix < gc->left + gc->xspan; ix++) - ld->hexpand[toxindex(g, ix)] = true; - } - if (gc->vexpand && gc->yspan != 1) { - bool doit = true; - - for (iy = gc->top; iy < gc->top + gc->yspan; iy++) - if (ld->vexpand[toyindex(g, iy)]) { - doit = false; - break; - } - if (doit) - for (iy = gc->top; iy < gc->top + gc->yspan; iy++) - ld->vexpand[toyindex(g, iy)] = true; - } - } - - // 4) compute and assign expanded widths/heights - nhexpand = 0; - nvexpand = 0; - for (i = 0; i < xcount(g); i++) - if (ld->hexpand[i]) - nhexpand++; - else - width -= ld->colwidths[i]; - for (i = 0; i < ycount(g); i++) - if (ld->vexpand[i]) - nvexpand++; - else - height -= ld->rowheights[i]; - for (i = 0; i < xcount(g); i++) - if (ld->hexpand[i]) - ld->colwidths[i] = width / nhexpand; - for (i = 0; i < ycount(g); i++) - if (ld->vexpand[i]) - ld->rowheights[i] = height / nvexpand; - - // 5) reset the final coordinates for the next step - for (i = 0; i < g->children->size(); i++) { - gc = (*(g->children))[i]; - if (!uiControlVisible(gc->c)) - continue; - gc->finalx = 0; - gc->finaly = 0; - gc->finalwidth = 0; - gc->finalheight = 0; - } - - // 6) compute cell positions and sizes - for (iy = 0; iy < ycount(g); iy++) { - int curx; - int prev; - - curx = 0; - prev = -1; - for (ix = 0; ix < xcount(g); ix++) { - if (!ld->visibleColumn(g, ix)) - continue; - i = ld->gg[iy][ix]; - if (i != -1) { - gc = (*(g->children))[i]; - if (iy == toyindex(g, gc->top)) { // don't repeat this step if the control spans vertically - if (i != prev) - gc->finalx = curx; - else - gc->finalwidth += xpadding; - gc->finalwidth += ld->colwidths[ix]; - } - } - curx += ld->colwidths[ix] + xpadding; - prev = i; - } - } - for (ix = 0; ix < xcount(g); ix++) { - int cury; - int prev; - - cury = 0; - prev = -1; - for (iy = 0; iy < ycount(g); iy++) { - if (!ld->visibleRow(g, iy)) - continue; - i = ld->gg[iy][ix]; - if (i != -1) { - gc = (*(g->children))[i]; - if (ix == toxindex(g, gc->left)) { // don't repeat this step if the control spans horizontally - if (i != prev) - gc->finaly = cury; - else - gc->finalheight += ypadding; - gc->finalheight += ld->rowheights[iy]; - } - } - cury += ld->rowheights[iy] + ypadding; - prev = i; - } - } - - // 7) everything as it stands now is set for xalign == Fill yalign == Fill; set the correct alignments - // this is why we saved minwidth/minheight above - for (i = 0; i < g->children->size(); i++) { - gc = (*(g->children))[i]; - if (!uiControlVisible(gc->c)) - continue; - if (gc->halign != uiAlignFill) { - switch (gc->halign) { - case uiAlignEnd: - gc->finalx += gc->finalwidth - gc->minwidth; - break; - case uiAlignCenter: - gc->finalx += (gc->finalwidth - gc->minwidth) / 2; - break; - } - gc->finalwidth = gc->minwidth; // for all three - } - if (gc->valign != uiAlignFill) { - switch (gc->valign) { - case uiAlignEnd: - gc->finaly += gc->finalheight - gc->minheight; - break; - case uiAlignCenter: - gc->finaly += (gc->finalheight - gc->minheight) / 2; - break; - } - gc->finalheight = gc->minheight; // for all three - } - } - - // 8) and FINALLY we resize - for (iy = 0; iy < ycount(g); iy++) - for (ix = 0; ix < xcount(g); ix++) { - i = ld->gg[iy][ix]; - if (i != -1) { // treat empty cells like spaces - gc = (*(g->children))[i]; - uiWindowsEnsureMoveWindowDuringResize( - (HWND) uiControlHandle(gc->c), - gc->finalx,//TODO + x, - gc->finaly,//TODO + y, - gc->finalwidth, - gc->finalheight); - } - } - - delete ld; -} - -static void uiGridDestroy(uiControl *c) -{ - uiGrid *g = uiGrid(c); - - for (struct gridChild *gc : *(g->children)) { - uiControlSetParent(gc->c, NULL); - uiControlDestroy(gc->c); - uiFree(gc); - } - delete g->indexof; - delete g->children; - uiWindowsEnsureDestroyWindow(g->hwnd); - uiFreeControl(uiControl(g)); -} - -uiWindowsControlDefaultHandle(uiGrid) -uiWindowsControlDefaultParent(uiGrid) -uiWindowsControlDefaultSetParent(uiGrid) -uiWindowsControlDefaultToplevel(uiGrid) -uiWindowsControlDefaultVisible(uiGrid) -uiWindowsControlDefaultShow(uiGrid) -uiWindowsControlDefaultHide(uiGrid) -uiWindowsControlDefaultEnabled(uiGrid) -uiWindowsControlDefaultEnable(uiGrid) -uiWindowsControlDefaultDisable(uiGrid) -uiWindowsControlDefaultSetFocus(uiGrid) - -static void uiGridSyncEnableState(uiWindowsControl *c, int enabled) -{ - uiGrid *g = uiGrid(c); - - if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(g), enabled)) - return; - for (const struct gridChild *gc : *(g->children)) - uiWindowsControlSyncEnableState(uiWindowsControl(gc->c), enabled); -} - -uiWindowsControlDefaultSetParentHWND(uiGrid) - -static void uiGridMinimumSize(uiWindowsControl *c, int *width, int *height) -{ - uiGrid *g = uiGrid(c); - int xpadding, ypadding; - gridLayoutData *ld; - int x, y; - int i; - struct gridChild *gc; - int minwid, minht; - int colwidth, rowheight; - - *width = 0; - *height = 0; - if (g->children->size() == 0) - return; // nothing to do - - gridPadding(g, &xpadding, &ypadding); - ld = new gridLayoutData(g); - if (ld->noVisible) { // nothing to do; return 0x0 - delete ld; - return; - } - - // 1) compute colwidths and rowheights before handling expansion - // TODO put this in its own function (but careful about the spanning calculation in gridRelayout()) - for (y = 0; y < ycount(g); y++) - for (x = 0; x < xcount(g); x++) { - i = ld->gg[y][x]; - if (i == -1) - continue; - gc = (*(g->children))[i]; - uiWindowsControlMinimumSize(uiWindowsControl(gc->c), &minwid, &minht); - // allot equal space in the presence of spanning to keep things sane - if (ld->colwidths[x] < minwid / gc->xspan) - ld->colwidths[x] = minwid / gc->xspan; - if (ld->rowheights[y] < minht / gc->yspan) - ld->rowheights[y] = minht / gc->yspan; - // save these for step 6 - gc->minwidth = minwid; - gc->minheight = minht; - } - - // 2) compute total column width/row height - colwidth = 0; - rowheight = 0; - for (x = 0; x < xcount(g); x++) - colwidth += ld->colwidths[x]; - for (y = 0; y < ycount(g); y++) - rowheight += ld->rowheights[y]; - - // and that's it; just account for padding - *width = colwidth + (ld->nVisibleColumns - 1) * xpadding; - *height = rowheight + (ld->nVisibleRows - 1) * ypadding; -} - -static void uiGridMinimumSizeChanged(uiWindowsControl *c) -{ - uiGrid *g = uiGrid(c); - - if (uiWindowsControlTooSmall(uiWindowsControl(g))) { - uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(g)); - return; - } - gridRelayout(g); -} - -static void uiGridSetMinSize(uiControl *c, int w, int h) -{ - // checkme - uiGridMinimumSizeChanged(uiWindowsControl(c)); -} - -uiWindowsControlDefaultLayoutRect(uiGrid) -uiWindowsControlDefaultAssignControlIDZOrder(uiGrid) - -static void uiGridChildVisibilityChanged(uiWindowsControl *c) -{ - // TODO eliminate the redundancy - uiWindowsControlMinimumSizeChanged(c); -} - -// must have called gridRecomputeMinMax() first -static void gridArrangeChildren(uiGrid *g) -{ - LONG_PTR controlID; - HWND insertAfter; - gridLayoutData *ld; - bool *visited; - int x, y; - int i; - struct gridChild *gc; - - if (g->children->size() == 0) - return; // nothing to do - ld = new gridLayoutData(g); - controlID = 100; - insertAfter = NULL; - visited = new bool[g->children->size()]; - ZeroMemory(visited, g->children->size() * sizeof (bool)); - for (y = 0; y < ycount(g); y++) - for (x = 0; x < xcount(g); x++) { - i = ld->gg[y][x]; - if (i == -1) - continue; - if (visited[i]) - continue; - visited[i] = true; - gc = (*(g->children))[i]; - uiWindowsControlAssignControlIDZOrder(uiWindowsControl(gc->c), &controlID, &insertAfter); - } - delete[] visited; - delete ld; -} - -static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) -{ - struct gridChild *gc; - - if (xspan < 0) - userbug("You cannot have a negative xspan in a uiGrid cell."); - if (yspan < 0) - userbug("You cannot have a negative yspan in a uiGrid cell."); - gc = uiNew(struct gridChild); - gc->c = c; - gc->xspan = xspan; - gc->yspan = yspan; - gc->hexpand = hexpand; - gc->halign = halign; - gc->vexpand = vexpand; - gc->valign = valign; - return gc; -} - -static void add(uiGrid *g, struct gridChild *gc) -{ - uiControlSetParent(gc->c, uiControl(g)); - uiWindowsControlSetParentHWND(uiWindowsControl(gc->c), g->hwnd); - g->children->push_back(gc); - (*(g->indexof))[gc->c] = g->children->size() - 1; - gridRecomputeMinMax(g); - gridArrangeChildren(g); - uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); -} - -void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) -{ - struct gridChild *gc; - - gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); - gc->left = left; - gc->top = top; - add(g, gc); -} - -// TODO decide what happens if existing is NULL -void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) -{ - struct gridChild *gc; - struct gridChild *other; - - gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); - other = (*(g->children))[(*(g->indexof))[existing]]; - switch (at) { - case uiAtLeading: - gc->left = other->left - gc->xspan; - gc->top = other->top; - break; - case uiAtTop: - gc->left = other->left; - gc->top = other->top - gc->yspan; - break; - case uiAtTrailing: - gc->left = other->left + other->xspan; - gc->top = other->top; - break; - case uiAtBottom: - gc->left = other->left; - gc->top = other->top + other->yspan; - break; - // TODO add error checks to ALL enums - } - add(g, gc); -} - -int uiGridPadded(uiGrid *g) -{ - return g->padded; -} - -void uiGridSetPadded(uiGrid *g, int padded) -{ - g->padded = padded; - uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); -} - -static void onResize(uiWindowsControl *c) -{ - gridRelayout(uiGrid(c)); -} - -uiGrid *uiNewGrid(void) -{ - uiGrid *g; - - uiWindowsNewControl(uiGrid, g); - - g->hwnd = uiWindowsMakeContainer(uiWindowsControl(g), onResize); - - g->children = new std::vector<struct gridChild *>; - g->indexof = new std::map<uiControl *, size_t>; - - return g; -} |