summaryrefslogtreecommitdiff
path: root/alf/alflayout.cpp
diff options
context:
space:
mode:
authorJonas Kümmerlin <jonas@kuemmerlin.eu>2019-01-05 22:07:51 +0100
committerJonas Kümmerlin <jonas@kuemmerlin.eu>2019-01-05 22:07:51 +0100
commitdb2f529599cea4c86481ed6ca650a5b069c003b0 (patch)
tree7f7f6c3449d8811fdf68f28f761efcc9fa4e9b9d /alf/alflayout.cpp
parentcd7552ae1ce7f6c92d03b7d0a83fe4aec073aeff (diff)
move layout into own file, implement expand flag
Diffstat (limited to 'alf/alflayout.cpp')
-rw-r--r--alf/alflayout.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/alf/alflayout.cpp b/alf/alflayout.cpp
new file mode 100644
index 0000000..8d03a6b
--- /dev/null
+++ b/alf/alflayout.cpp
@@ -0,0 +1,324 @@
+#include "alfpriv.h"
+
+void
+ALF_Layout_Init(ALFLayout *layout)
+{
+ ZeroMemory(layout, sizeof(*layout));
+ ALF_ListInit(&layout->widgets);
+}
+
+void
+ALF_Layout_Clear(ALFLayout *layout)
+{
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, w) {
+ HeapFree(GetProcessHeap(), 0, w);
+ }
+ ALF_ListInit(&layout->widgets);
+}
+
+
+static void
+ALF_UpdateFontForWidget(ALFWidgetPriv *widget, const ALFWindowFonts *fonts)
+{
+ if (widget->hwnd && (widget->flags & ALF_MESSAGEFONT) == ALF_MESSAGEFONT) {
+ SendMessage(widget->hwnd, WM_SETFONT, (WPARAM)fonts->hMessageFont, (LPARAM)NULL);
+
+ // XXX: Invalidating should IMHO be the decision of the control, but at
+ // least the commctl32 V5 static control doesn't do it.
+ InvalidateRect(widget->hwnd, NULL, TRUE);
+ }
+}
+
+void
+ALF_Layout_UpdateFonts(ALFLayout *layout, HWND window, const ALFWindowFonts *fonts)
+{
+ (void)window;
+
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, i) {
+ ALF_UpdateFontForWidget(i, fonts);
+ }
+}
+
+void
+ALF_Layout_CalcSizes(ALFLayout* layout, HWND window)
+{
+ for (int i = 0; i < layout->nColumns; ++i) {
+ ZeroMemory(&layout->columns[i], sizeof(layout->columns[i]));
+ }
+ for (int i = 0; i < layout->nRows; ++i) {
+ ZeroMemory(&layout->rows[i], sizeof(layout->rows[i]));
+ }
+
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, c) {
+ while ((int)c->x >= layout->nColumns) {
+ // FIXME! overflow, use reallocarray(2) equivalent
+ if (layout->nColumns == 0) {
+ layout->nColumns = 1;
+ layout->columns = (ALFLayoutRowOrColumn*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, layout->nColumns*sizeof(layout->columns[0]));
+ } else {
+ layout->nColumns *= 2;
+ layout->columns = (ALFLayoutRowOrColumn*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, layout->columns, layout->nColumns*sizeof(layout->columns[0]));
+ }
+ }
+ while ((int)c->y >= layout->nRows) {
+ // FIXME! overflow, use reallocarray(2) equivalent
+ if (layout->nRows == 0) {
+ layout->nRows = 1;
+ layout->rows = (ALFLayoutRowOrColumn*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, layout->nRows*sizeof(layout->rows[0]));
+ } else {
+ layout->nRows *= 2;
+ layout->rows = (ALFLayoutRowOrColumn*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS, layout->rows, layout->nRows*sizeof(layout->rows[0]));
+ }
+ }
+
+ // TODO: ignore spanning cell
+
+ SIZE qs = { ALF_CentipointsToPixels(window, c->cptWidth),
+ ALF_CentipointsToPixels(window, c->cptHeight) };
+ if ((c->flags & ALF_QUERYSIZE) == ALF_QUERYSIZE) {
+ SendMessage(c->hwnd, ALF_WM_QUERYSIZE, 0, (LPARAM)&qs);
+ }
+
+ qs.cx += ALF_CentipointsToPixels(window, c->cptMarginLeft);
+ qs.cx += ALF_CentipointsToPixels(window, c->cptMarginRight);
+ qs.cy += ALF_CentipointsToPixels(window, c->cptMarginTop);
+ qs.cy += ALF_CentipointsToPixels(window, c->cptMarginBottom);
+
+ if (qs.cx > layout->columns[c->x].minWidth)
+ layout->columns[c->x].minWidth = qs.cx;
+ if (qs.cy > layout->rows[c->y].minWidth)
+ layout->rows[c->y].minWidth = qs.cy;
+
+ // expand flag
+ if (c->flags & ALF_HEXPAND)
+ layout->columns[c->x].expand = 1;
+ if (c->flags & ALF_VEXPAND)
+ layout->rows[c->y].expand = 1;
+ }
+
+ // TODO: second pass for spanning cells
+
+ // update min width bookkeeping
+ layout->totalMinWidth = 0;
+ layout->expandoColumnCount = 0;
+ for (int i = 0; i < layout->nColumns; ++i) {
+ layout->totalMinWidth += layout->columns[i].minWidth;
+
+ if (layout->columns[i].expand)
+ layout->expandoColumnCount++;
+ }
+
+ if (layout->expandoColumnCount == 0) {
+ // mark all columns as expanding
+ for (int i = 0; i < layout->nColumns; ++i) {
+ if (layout->columns[i].minWidth > 0) {
+ layout->columns[i].expand = 1;
+ layout->expandoColumnCount++;
+ }
+ }
+ }
+
+ layout->totalMinHeight = 0;
+ layout->expandoRowCount = 0;
+ for (int i = 0; i < layout->nRows; ++i) {
+ layout->totalMinHeight += layout->rows[i].minWidth;
+
+ if (layout->rows[i].expand)
+ layout->expandoRowCount++;
+ }
+
+ if (layout->expandoRowCount == 0) {
+ // mark all rows as expanding
+ for (int i = 0; i < layout->nRows; ++i) {
+ if (layout->rows[i].minWidth > 0) {
+ layout->rows[i].expand = 1;
+ layout->expandoRowCount++;
+ }
+ }
+ }
+}
+
+void
+ALF_Layout_Apply(ALFLayout* layout, HWND window)
+{
+ // allocate cell positions
+ // distribute free space
+ int extraWidth = 0;
+ int extraHeight = 0;
+ RECT client;
+ if (GetClientRect(window, &client)) {
+ if (client.right - client.left > layout->totalMinWidth)
+ extraWidth = client.right - client.left - layout->totalMinWidth;
+ if (client.bottom - client.top > layout->totalMinHeight)
+ extraHeight = client.bottom - client.top - layout->totalMinHeight;
+ }
+
+ int extraWidthPart = 0;
+ int extraWidthError = 0;
+ if (layout->expandoColumnCount) {
+ extraWidthPart = extraWidth / layout->expandoColumnCount;
+ extraWidthError = extraWidth - extraWidthPart * layout->expandoColumnCount;
+ }
+
+ for (int i = 0; i < layout->nColumns; ++i) {
+ layout->columns[i].allocatedWidth = layout->columns[i].minWidth;
+
+ if (layout->columns[i].expand) {
+ layout->columns[i].allocatedWidth += extraWidthPart;
+ if (extraWidthError) {
+ layout->columns[i].allocatedWidth++;
+ extraWidthError--;
+ }
+ }
+
+ if (i == 0) {
+ layout->columns[i].allocatedPosition = 0;
+ } else {
+ layout->columns[i].allocatedPosition =
+ layout->columns[i-1].allocatedPosition + layout->columns[i-1].allocatedWidth;
+ }
+ }
+
+
+ int extraHeightPart = 0;
+ int extraHeightError = 0;
+ if (layout->expandoRowCount) {
+ extraHeightPart = extraHeight / layout->expandoRowCount;
+ extraHeightError = extraHeight - extraHeightPart * layout->expandoRowCount;
+ }
+
+ for (int i = 0; i < layout->nRows; ++i) {
+ layout->rows[i].allocatedWidth = layout->rows[i].minWidth;
+
+ if (layout->rows[i].expand) {
+ layout->rows[i].allocatedWidth += extraHeightPart;
+ if (extraHeightError) {
+ layout->rows[i].allocatedWidth++;
+ extraHeightError--;
+ }
+ }
+
+ if (i == 0) {
+ layout->rows[i].allocatedPosition = 0;
+ } else {
+ layout->rows[i].allocatedPosition =
+ layout->rows[i-1].allocatedPosition + layout->rows[i-1].allocatedWidth;
+ }
+ }
+
+ HDWP hdwp = BeginDeferWindowPos(layout->nColumns * layout->nRows);
+
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, c) {
+ if ((int)c->x >= layout->nColumns || (int)c->y >= layout->nRows)
+ continue;
+
+ int marginleft = ALF_CentipointsToPixels(window, c->cptMarginLeft);
+ int marginright = ALF_CentipointsToPixels(window, c->cptMarginRight);
+ int margintop = ALF_CentipointsToPixels(window, c->cptMarginTop);
+ int marginbottom = ALF_CentipointsToPixels(window, c->cptMarginBottom);
+
+ RECT r = { 0,0,0,0 };
+ r.left = layout->columns[c->x].allocatedPosition + marginleft;
+ r.right = r.left + layout->columns[c->x].allocatedWidth - marginleft - marginright;
+ r.top = layout->rows[c->y].allocatedPosition + margintop;
+ r.bottom = r.top + layout->rows[c->y].allocatedWidth - margintop - marginbottom;
+
+ if (c->flags & ALF_APPLYSIZE) {
+ hdwp = (HDWP)SendMessage(c->hwnd, ALF_WM_APPLYSIZE, (WPARAM)hdwp, (LPARAM)&r);
+ } else {
+ hdwp = DeferWindowPos(hdwp,
+ c->hwnd, 0,
+ r.left, r.top, r.right - r.left, r.bottom - r.top,
+ SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ }
+
+ EndDeferWindowPos(hdwp);
+}
+
+void
+ALF_Layout_AddWidget(ALFLayout* layout, HWND window, const ALFWidgetLayoutParams* params)
+{
+ ALFWidgetPriv *w = (ALFWidgetPriv*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY|HEAP_GENERATE_EXCEPTIONS, sizeof(ALFWidgetPriv));
+ w->hwnd = params->hwnd;
+ w->x = params->x;
+ w->y = params->y;
+ w->cptWidth = params->width;
+ w->cptHeight = params->height;
+ w->flags = params->flags;
+ w->cptMarginTop = params->margins[0];
+ w->cptMarginRight = params->margins[1];
+ w->cptMarginBottom = params->margins[2];
+ w->cptMarginLeft = params->margins[3];
+
+ if (GetParent(w->hwnd) != window)
+ SetParent(w->hwnd, window);
+
+ ALF_ListInsert(layout->widgets.prev, &w->list);
+
+ if (w->flags & ALF_MESSAGEFONT) {
+ ALFWindowFonts fonts;
+ ZeroMemory(&fonts, sizeof(fonts));
+ ALF_WindowFonts(window, &fonts);
+ ALF_UpdateFontForWidget(w, &fonts);
+ }
+}
+
+BOOL
+ALF_Layout_SetWidgetParams(ALFLayout* layout, const ALFWidgetLayoutParams* params, HWND widget)
+{
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, w) {
+ if (w->hwnd == widget) {
+ w->hwnd = params->hwnd;
+ w->x = params->x;
+ w->y = params->y;
+ w->cptWidth = params->width;
+ w->cptHeight = params->height;
+ w->flags = params->flags;
+ w->cptMarginTop = params->margins[0];
+ w->cptMarginRight = params->margins[1];
+ w->cptMarginBottom = params->margins[2];
+ w->cptMarginLeft = params->margins[3];
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL
+ALF_Layout_GetWidgetParams(ALFLayout* layout, ALFWidgetLayoutParams* params, HWND widget)
+{
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, w) {
+ if (w->hwnd == widget) {
+ params->hwnd = w->hwnd;
+ params->x = w->x;
+ params->y = w->y;
+ params->width = w->cptWidth;
+ params->height = w->cptHeight;
+ params->flags = w->flags;
+ params->margins[0] = w->cptMarginTop;
+ params->margins[1] = w->cptMarginRight;
+ params->margins[2] = w->cptMarginBottom;
+ params->margins[3] = w->cptMarginLeft;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+HWND
+ALF_Layout_WidgetAtPos(ALFLayout* layout, UINT x, UINT y)
+{
+ ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, w) {
+ if (w->x == x && w->y == y) {
+ return w->hwnd;
+ }
+ }
+
+ return NULL;
+}
+