#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) { ALF_Free(w); } ALF_ListInit(&layout->widgets); ALF_Free(layout->columns); ALF_Free(layout->rows); layout->columns = NULL; layout->rows = NULL; layout->nColumns = 0; layout->nRows = 0; } static void ALF_ApplyFontForWidget(ALFWidgetPriv *widget, const ALFWindowFonts *fonts) { if (widget->hwnd) { if (widget->flags & 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); } if (widget->flags & ALF_SENDAPPLYFONTS) { SendMessage(widget->hwnd, ALF_WM_APPLYFONTS, 0, (LPARAM)fonts); } } } void ALF_Layout_ApplyFonts(ALFLayout *layout, HWND window, const ALFWindowFonts *fonts) { (void)window; ALF_FOR_LIST(ALFWidgetPriv, list, &layout->widgets, i) { ALF_ApplyFontForWidget(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 = ALF_New(ALFLayoutRowOrColumn, layout->nColumns); } else { layout->nColumns *= 2; layout->columns = ALF_ReNew(layout->columns, ALFLayoutRowOrColumn, layout->nColumns); } } while ((int)c->y >= layout->nRows) { // FIXME! overflow, use reallocarray(2) equivalent if (layout->nRows == 0) { layout->nRows = 1; layout->rows = ALF_New(ALFLayoutRowOrColumn, layout->nRows); } else { layout->nRows *= 2; layout->rows = ALF_ReNew(layout->rows, ALFLayoutRowOrColumn, layout->nRows); } } // 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 = ALF_New(ALFWidgetPriv, 1); 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); } 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; } BOOL ALF_Layout_HandleMessage(ALFLayout *layout, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *pRet) { *pRet = 0; if (msg == ALF_WM_QUERYSIZE) { ALF_Layout_CalcSizes(layout, hwnd); SIZE *ps = (SIZE*)lparam; ps->cx = layout->totalMinWidth; ps->cy = layout->totalMinHeight; return TRUE; } if (msg == ALF_WM_APPLYFONTS && lparam != 0) { ALF_Layout_ApplyFonts(layout, hwnd, (const ALFWindowFonts *)lparam); return TRUE; } if (msg == ALF_WM_APPLYLAYOUT) { ALF_Layout_Apply(layout, hwnd); return TRUE; } if (msg == ALF_WM_ADDWIDGET) { ALF_Layout_AddWidget(layout, hwnd, (ALFWidgetLayoutParams *)lparam); return TRUE; } if (msg == ALF_WM_GETLAYOUTPARAMS) { *pRet = (LRESULT)ALF_Layout_GetWidgetParams(layout, (ALFWidgetLayoutParams *)lparam, (HWND)wparam); return TRUE; } if (msg == ALF_WM_SETLAYOUTPARAMS) { *pRet = (LRESULT)ALF_Layout_SetWidgetParams(layout, (const ALFWidgetLayoutParams *)lparam, (HWND)wparam); return TRUE; } if (msg == ALF_WM_GETWIDGETATPOS) { *pRet = (LRESULT)ALF_Layout_WidgetAtPos(layout, ((UINT*)lparam)[0], ((UINT*)lparam)[1]); return TRUE; } return FALSE; }