#include "alfpriv.h" static void ALF_Layout_CalcSizes(ALFLayout *layout, HWND window); static void ALF_Layout_EnsureRowExists(ALFLayout *layout, int rowno); static void ALF_Layout_EnsureColumnExists(ALFLayout *layout, int colno); static BOOL ALF_Layout_GetControlPosPriv(ALFLayout *layout, HWND window, HWND needle, POINT *p); static BOOL ALF_Layout_SetControlPosPriv(ALFLayout *layout, HWND window, HWND needle, POINT *p); static BOOL ALF_Layout_GetControlSizePriv(ALFLayout *layout, HWND window, HWND needle, SIZE *s); static BOOL ALF_Layout_SetControlSizePriv(ALFLayout *layout, HWND window, HWND needle, SIZE *s); static DWORD ALF_Layout_GetControlFlagsPriv(ALFLayout *layout, HWND window, HWND needle); static BOOL ALF_Layout_SetControlFlagsPriv(ALFLayout *layout, HWND window, HWND needle, DWORD flags); static int ALF_Layout_GetColumnSizePriv(ALFLayout *layout, HWND window, int colno); static BOOL ALF_Layout_SetColumnSizePriv(ALFLayout *layout, HWND window, int colno, int size); static int ALF_Layout_GetColumnExpandPriv(ALFLayout *layout, HWND window, int colno); static BOOL ALF_Layout_SetColumnExpandPriv(ALFLayout *layout, HWND window, int colno, int expand); static DWORD ALF_Layout_GetColumnFlagsPriv(ALFLayout *layout, HWND window, int colno); static BOOL ALF_Layout_SetColumnFlagsPriv(ALFLayout *layout, HWND window, int colno, DWORD flags); static int ALF_Layout_GetRowSizePriv(ALFLayout *layout, HWND window, int rowno); static BOOL ALF_Layout_SetRowSizePriv(ALFLayout *layout, HWND window, int rowno, int size); static int ALF_Layout_GetRowExpandPriv(ALFLayout *layout, HWND window, int rowno); static BOOL ALF_Layout_SetRowExpandPriv(ALFLayout *layout, HWND window, int rowno, int expand); static DWORD ALF_Layout_GetRowFlagsPriv(ALFLayout *layout, HWND window, int rowno); static BOOL ALF_Layout_SetRowFlagsPriv(ALFLayout *layout, HWND window, int rowno, DWORD flags); static void ALF_Layout_HandleBackgroundChange(ALFLayout *layout, HWND window); void ALF_Layout_Init(ALFLayout *layout) { ZeroMemory(layout, sizeof(*layout)); ALF_ListInit(&layout->controls); layout->nRows = 1; layout->rows = ALF_New(ALFLayoutRowOrColumn, (SIZE_T)layout->nRows); layout->nColumns = 1; layout->columns = ALF_New(ALFLayoutRowOrColumn, (SIZE_T)layout->nColumns); layout->dpi = 96; layout->bgcolor = ALF_COLOR_TRANSPARENT; } void ALF_Layout_Clear(ALFLayout *layout) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { ALF_Free(w); } ALF_ListInit(&layout->controls); ALF_Free(layout->columns); ALF_Free(layout->rows); layout->columns = NULL; layout->rows = NULL; layout->nColumns = 0; layout->nRows = 0; } static void ALF_Layout_ForwardFontToControl(ALFLayout *layout, HWND window, ALFControlPriv *control, HFONT font, LPARAM redraw) { if (control->flags & ALF_LAYOUT_INHERITFONT) { SendMessage(control->hwnd, WM_SETFONT, (WPARAM)font, redraw); switch (control->flags & ALF_LAYOUT_SIZETYPE_MASK) { case ALF_LAYOUT_SIZE_EDIT: case ALF_LAYOUT_SIZE_CHECKBOX: case ALF_LAYOUT_SIZE_PUSHBUTTON: ALF_Layout_Invalidate(layout, window); break; default: // do nothing break; } } } static void ALF_Layout_ForwardFont(ALFLayout *layout, HWND window, HFONT font, LPARAM redraw) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, i) { ALF_Layout_ForwardFontToControl(layout, window, i, font, redraw); } } static void ALF_Layout_ForwardBgColor(ALFLayout *layout, HWND window, WPARAM wparam, LPARAM lparam) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, i) { if (i->flags & ALF_LAYOUT_INHERITBGCOLOR) { SendMessage(i->hwnd, ALF_WM_SETBGCOLOR, wparam, lparam); } } } static void ALF_Layout_ForwardDpiChange(ALFLayout *layout, HWND window, WPARAM wparam, LPARAM lparam) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, i) { if (i->flags & ALF_LAYOUT_SENDDPICHANGE) { SendMessage(i->hwnd, ALF_WM_DPICHANGE, wparam, lparam); } if (i->flags & ALF_LAYOUT_SIZE_EDIT) { ALF_Layout_Invalidate(layout, window); } } } static void ALF_Layout_HandleBackgroundChange(ALFLayout *layout, HWND window) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, i) { if (i->flags & ALF_LAYOUT_TRANSPARENTBG) { InvalidateRect(i->hwnd, NULL, TRUE); } if (i->flags & ALF_LAYOUT_SENDBGCHANGE) { ALF_InvalidateBackground(i->hwnd); } } } void ALF_Layout_EnsureRowExists(ALFLayout *layout, int rowno) { while (rowno >= layout->nRows) { layout->nRows *= 2; layout->rows = ALF_ReNew(layout->rows, ALFLayoutRowOrColumn, (SIZE_T)layout->nRows); } } void ALF_Layout_EnsureColumnExists(ALFLayout *layout, int colno) { while (colno >= layout->nColumns) { layout->nColumns *= 2; layout->columns = ALF_ReNew(layout->columns, ALFLayoutRowOrColumn, (SIZE_T)layout->nColumns); } } static void ALF_Layout_CalcEditSize(HWND hwndWindow, ALFLayout *layout, HWND hwndEdit, SIZE *ps) { (void)hwndWindow; HDC hDc = GetDC(hwndEdit); HFONT font = (HFONT)SendMessage(hwndEdit, WM_GETFONT, 0, 0); HFONT oldfont = SelectFont(hDc, font); TEXTMETRIC tm; ZeroMemory(&tm, sizeof(tm)); if (GetTextMetrics(hDc, &tm)) { int cy = tm.tmHeight + 2*ALF_Compat_GetSystemMetricsForDpi( SM_CYEDGE, (UINT)layout->dpi) + 4 /* padding internal to the edit control */; if (ps->cy < cy) ps->cy = cy; } SelectFont(hDc, oldfont); ReleaseDC(hwndEdit, hDc); } static void ALF_Layout_CalcCheckboxSize(HWND hwndWindow, ALFLayout *layout, HWND hwndCheckbox, SIZE *ps) { (void)hwndWindow; HDC hDC = GetDC(hwndCheckbox); HFONT font = (HFONT)SendMessage(hwndCheckbox, WM_GETFONT, 0, 0); HFONT oldfont = SelectFont(hDC, font); RECT r = { 0, 0, 10, 10 }; TCHAR *textbuf = ALF_Text(hwndCheckbox); DrawText(hDC, textbuf, -1, &r, DT_CALCRECT); ALF_Free(textbuf); // lol int cx = 0; GetCharWidth(hDC, '0', '0', &cx); int checkwidth = 12 * layout->dpi / 96 + 1; int checkheight = checkwidth; if (!ALF_Compat_Is40()) { // 3.x-style checkboxes have the checkmark baseline-aligned TEXTMETRIC tm; ZeroMemory(&tm, sizeof(tm)); if (GetTextMetrics(hDC, &tm)) { checkheight += tm.tmDescent; } } cx += checkwidth + r.right - r.left; int cy = r.bottom - r.top; if (checkheight > cy) cy = checkheight; if (ps->cx < cx) ps->cx = cx; if (ps->cy < cy) ps->cy = cy; SelectFont(hDC, oldfont); ReleaseDC(hwndCheckbox, hDC); } static void ALF_Layout_CalcButtonSize(HWND hwndWindow, ALFLayout *layout, HWND hwndButton, SIZE *pSize) { (void)hwndWindow; HDC hdc = GetDC(hwndButton); HFONT oldFont = SelectFont(hdc, (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0)); // calc drawtext style UINT format = DT_LEFT | DT_EXPANDTABS | DT_CALCRECT; if (!(GetWindowLong(hwndButton, GWL_STYLE) & BS_MULTILINE)) format |= DT_SINGLELINE; RECT r = { 0, 0, 0x7FFFFFFF, 100 }; TCHAR *textbuf = ALF_Text(hwndButton); DrawText(hdc, textbuf, -1, &r, format); ALF_Free(textbuf); int xpadding = ALF_Compat_GetSystemMetricsForDpi(SM_CXEDGE, (UINT)layout->dpi) * 2 + 6; int ypadding = ALF_Compat_GetSystemMetricsForDpi(SM_CYEDGE, (UINT)layout->dpi) * 2 + 4; if (pSize->cx < r.right - r.left + xpadding) { pSize->cx = r.right - r.left + xpadding; } if (pSize->cy < r.bottom - r.top + ypadding) { pSize->cy = r.bottom - r.top + ypadding; } if (pSize->cx < pSize->cy) { pSize->cx = pSize->cy; } SelectFont(hdc, oldFont); ReleaseDC(hwndButton, hdc); } static void ALF_Layout_CalcMinControlSize(ALFLayout *layout, ALFControlPriv *c, HWND window) { if (c->flags & ALF_LAYOUT_SIZE_PX) { c->calculatedSize.cx = c->width; c->calculatedSize.cy = c->height; } else { c->calculatedSize.cx = ALF_CentipointsToPixels(c->width, layout->dpi); c->calculatedSize.cy = ALF_CentipointsToPixels(c->height, layout->dpi); } switch (c->flags & ALF_LAYOUT_SIZETYPE_MASK) { case ALF_LAYOUT_SIZE_FIXED: // already done break; case ALF_LAYOUT_SIZE_QUERY: SendMessage(c->hwnd, ALF_WM_QUERYSIZE, 0, (LPARAM)&c->calculatedSize); break; case ALF_LAYOUT_SIZE_EDIT: ALF_Layout_CalcEditSize(window, layout, c->hwnd, &c->calculatedSize); break; case ALF_LAYOUT_SIZE_CHECKBOX: ALF_Layout_CalcCheckboxSize(window, layout, c->hwnd, &c->calculatedSize); break; case ALF_LAYOUT_SIZE_PUSHBUTTON: ALF_Layout_CalcButtonSize(window, layout, c->hwnd, &c->calculatedSize); break; default: // FIXME! unimplemented break; } } void ALF_Layout_CalcSizes(ALFLayout* layout, HWND window) { if (layout->beginCalcSizes) { layout->beginCalcSizes(layout, window); } layout->biggestColumnNo = 0; layout->columnExpandDenominator = 0; for (int i = 0; i < layout->nColumns; ++i) { if (layout->columns[i].requestedFlags & ALF_LAYOUT_SIZE_PX) { layout->columns[i].calculatedMinWidth = layout->columns[i].requestedMinWidth; } else { layout->columns[i].calculatedMinWidth = ALF_CentipointsToPixels(layout->columns[i].requestedMinWidth, layout->dpi); } layout->columns[i].calculatedExpandNumerator = layout->columns[i].requestedExpandNumerator; layout->columnExpandDenominator += layout->columns[i].requestedExpandNumerator; if (layout->columns[i].requestedExpandNumerator >= layout->columns[layout->biggestColumnNo].requestedExpandNumerator) layout->biggestColumnNo = i; } layout->biggestRowNo = 0; layout->rowExpandDenominator = 0; for (int i = 0; i < layout->nRows; ++i) { if (layout->rows[i].requestedFlags & ALF_LAYOUT_SIZE_PX) { layout->rows[i].calculatedMinWidth = layout->rows[i].requestedMinWidth; } else { layout->rows[i].calculatedMinWidth = ALF_CentipointsToPixels(layout->rows[i].requestedMinWidth, layout->dpi); } layout->rows[i].calculatedExpandNumerator = layout->rows[i].requestedExpandNumerator; layout->rowExpandDenominator += layout->rows[i].requestedExpandNumerator; if (layout->rows[i].requestedExpandNumerator >= layout->rows[layout->biggestRowNo].requestedExpandNumerator) layout->biggestRowNo = i; } ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, c) { ALF_Layout_CalcMinControlSize(layout, c, window); if (c->x < 0 || c->y < 0) { if (layout->handleContainerControlSize) layout->handleContainerControlSize(layout, window, c->hwnd, &c->calculatedSize); } else { if (c->xspan <= 1) { ALF_Layout_EnsureColumnExists(layout, c->x); if (c->calculatedSize.cx > layout->columns[c->x].calculatedMinWidth) layout->columns[c->x].calculatedMinWidth = c->calculatedSize.cx; } if (c->yspan <= 1) { ALF_Layout_EnsureRowExists(layout, c->y); if (c->calculatedSize.cy > layout->rows[c->y].calculatedMinWidth) layout->rows[c->y].calculatedMinWidth = c->calculatedSize.cy; } } } // second pass for spanning cells ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, c) { if (c->xspan > 1) { ALF_Layout_EnsureColumnExists(layout, c->x + c->xspan - 1); // check for extra size we need to distribute int xextra = c->calculatedSize.cx; int xexpandosum = 0; for (int i = c->x; i < c->x + c->xspan; ++i) { xextra -= layout->columns[i].calculatedMinWidth; xexpandosum += layout->columns[i].calculatedExpandNumerator; } if (xextra > 0) { // distribute the extra size based on expand flags int xextraleft = xextra; int biggestColumnNo = c->x; for (int i = c->x; i < c->x + c->xspan; ++i) { int a; if (xexpandosum > 0) a = MulDiv(xextra, layout->columns[i].calculatedExpandNumerator, xexpandosum); else a = MulDiv(xextra, 1, c->xspan); if (a > xextraleft) a = xextraleft; layout->columns[i].calculatedMinWidth += a; xextraleft -= a; if (layout->columns[i].calculatedMinWidth > layout->columns[biggestColumnNo].calculatedMinWidth) biggestColumnNo = i; } if (xextraleft > 0) layout->columns[biggestColumnNo].calculatedMinWidth += xextraleft; } } if (c->yspan > 1) { ALF_Layout_EnsureRowExists(layout, c->y + c->yspan - 1); int yextra = c->calculatedSize.cy; int yexpandosum = 0; for (int i = c->y; i < c->y + c->yspan; ++i) { yextra -= layout->rows[i].calculatedMinWidth; yexpandosum += layout->rows[i].calculatedExpandNumerator; } if (yextra > 0) { // distribute the extra size based on expand flags int yextraleft = yextra; int biggestRowNo = c->y; for (int i = c->y; i < c->y + c->yspan; ++i) { int a; if (yexpandosum > 0) a = MulDiv(yextra, layout->rows[i].calculatedExpandNumerator, yexpandosum); else a = MulDiv(yextra, 1, c->yspan); if (a > yextraleft) a = yextraleft; layout->rows[i].calculatedMinWidth += a; yextraleft -= a; if (layout->rows[i].calculatedMinWidth > layout->rows[biggestRowNo].calculatedMinWidth) biggestRowNo = i; } if (yextraleft > 0) layout->rows[biggestRowNo].calculatedMinWidth += yextraleft; } } } // total minimum bookkeeping layout->totalMinWidth = 0; for (int i = 0; i < layout->nColumns; ++i) { layout->totalMinWidth += layout->columns[i].calculatedMinWidth; } layout->totalMinHeight = 0; for (int i = 0; i < layout->nRows; ++i) { layout->totalMinHeight += layout->rows[i].calculatedMinWidth; } // expando bookkeeping if no expand numerators specified if (layout->columnExpandDenominator == 0) { for (int i = 0; i < layout->nColumns; ++i) { if (layout->columns[i].calculatedMinWidth > 0) { layout->columnExpandDenominator += 1; layout->columns[i].calculatedExpandNumerator = 1; layout->biggestColumnNo = i; } } // all columns empty? expand first one then if (layout->columnExpandDenominator == 0) { layout->columnExpandDenominator = 1; layout->columns[0].calculatedExpandNumerator = 1; layout->biggestColumnNo = 0; } } if (layout->rowExpandDenominator == 0) { for (int i = 0; i < layout->nRows; ++i) { if (layout->rows[i].calculatedMinWidth > 0) { layout->rowExpandDenominator += 1; layout->rows[i].calculatedExpandNumerator = 1; layout->biggestRowNo = i; } } // all rows empty? expand first one then if (layout->rowExpandDenominator == 0) { layout->rowExpandDenominator = 1; layout->rows[0].calculatedExpandNumerator = 1; layout->biggestRowNo = 0; } } SIZE containerMinSize = { 0,0 }; if (layout->endCalcSizes) layout->endCalcSizes(layout, window, &containerMinSize); if (layout->totalMinWidth < containerMinSize.cx - layout->containerMargins.left - layout->containerMargins.right) layout->totalMinWidth = containerMinSize.cx - layout->containerMargins.left - layout->containerMargins.right; if (layout->totalMinHeight < containerMinSize.cy - layout->containerMargins.top - layout->containerMargins.bottom) layout->totalMinHeight = containerMinSize.cy - layout->containerMargins.top - layout->containerMargins.bottom; layout->layoutValididityFlags &= ~ALF_LAYOUT_NEED_RECALC; } void ALF_Layout_Apply(ALFLayout* layout, HWND window) { if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) ALF_Layout_CalcSizes(layout, window); GetClientRect(window, &layout->allocatedControlRect); layout->allocatedControlRect.left += layout->containerMargins.left; layout->allocatedControlRect.right -= layout->containerMargins.right; layout->allocatedControlRect.top += layout->containerMargins.top; layout->allocatedControlRect.bottom -= layout->containerMargins.bottom; // FIXME! can that happen? if (layout->allocatedControlRect.right < layout->allocatedControlRect.left) layout->allocatedControlRect.right = layout->allocatedControlRect.left; if (layout->allocatedControlRect.bottom < layout->allocatedControlRect.top) layout->allocatedControlRect.bottom = layout->allocatedControlRect.top; // distribute extra space int extraWidth = 0; int extraHeight = 0; if (layout->allocatedControlRect.right - layout->allocatedControlRect.left > layout->totalMinWidth) extraWidth = layout->allocatedControlRect.right - layout->allocatedControlRect.left - layout->totalMinWidth; if (layout->allocatedControlRect.bottom - layout->allocatedControlRect.top > layout->totalMinHeight) extraHeight = layout->allocatedControlRect.bottom - layout->allocatedControlRect.top - layout->totalMinHeight; int extraWidthLeft = extraWidth; for (int i = 0; i < layout->nColumns; ++i) { if (i == layout->biggestColumnNo) continue; int extraHere = MulDiv(extraWidth, layout->columns[i].calculatedExpandNumerator, layout->columnExpandDenominator); if (extraHere > extraWidthLeft) extraHere = extraWidthLeft; layout->columns[i].allocatedWidth = layout->columns[i].calculatedMinWidth + extraHere; extraWidthLeft -= extraHere; } layout->columns[layout->biggestColumnNo].allocatedWidth = layout->columns[layout->biggestColumnNo].calculatedMinWidth + extraWidthLeft; int extraHeightLeft = extraHeight; for (int i = 0; i < layout->nRows; ++i) { if (i == layout->biggestRowNo) continue; int extraHere = MulDiv(extraHeight, layout->rows[i].calculatedExpandNumerator, layout->rowExpandDenominator); if (extraHere > extraHeightLeft) extraHere = extraHeightLeft; layout->rows[i].allocatedWidth = layout->rows[i].calculatedMinWidth + extraHere; extraHeightLeft -= extraHere; } layout->rows[layout->biggestRowNo].allocatedWidth = layout->rows[layout->biggestRowNo].calculatedMinWidth + extraHeightLeft; // set row/column positions int x = layout->allocatedControlRect.left; for (int i = 0; i < layout->nColumns; ++i) { layout->columns[i].allocatedPosition = x; x += layout->columns[i].allocatedWidth; } int y = layout->allocatedControlRect.top; for (int i = 0; i < layout->nRows; ++i) { layout->rows[i].allocatedPosition = y; y += layout->rows[i].allocatedWidth; } // now apply positions to controls HDWP hdwp = BeginDeferWindowPos(layout->nColumns * layout->nRows); ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, c) { int col = c->x; int row = c->y; int colspan = c->xspan; int rowspan = c->yspan; if (colspan < 1) colspan = 1; if (rowspan < 1) rowspan = 1; if (col+colspan > layout->nColumns || row+rowspan > layout->nRows) continue; // FIXME: wat? RECT r = { 0,0,0,0 }; if (row < 0 || col < 0) { if (layout->preApplyLayoutToContainerControl) layout->preApplyLayoutToContainerControl(layout, window, c->hwnd, &r); } else { r.left = layout->columns[col].allocatedPosition; r.right = layout->columns[col+colspan-1].allocatedPosition + layout->columns[col+colspan-1].allocatedWidth; r.top = layout->rows[row].allocatedPosition; r.bottom = layout->rows[row+rowspan-1].allocatedPosition + layout->rows[row+rowspan-1].allocatedWidth; } if (c->flags & ALF_LAYOUT_CUSTOMPOS) { hdwp = (HDWP)SendMessage(c->hwnd, ALF_WM_APPLYSIZE, (WPARAM)hdwp, (LPARAM)&r); } else { UINT flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER; // NT 3.51 and Win32s have so many invalidation bugs it's not even funny if (!ALF_Compat_Is40()) flags |= SWP_NOCOPYBITS; RECT oldR; GetWindowRect(c->hwnd, &oldR); MapWindowRect(NULL, window, &oldR); // transparent background: invalidate if control moved if (layout->bgcolor == ALF_COLOR_TRANSPARENT) { if (oldR.left != r.left || oldR.top != r.top) { if (c->flags & ALF_LAYOUT_SENDBGCHANGE) { ALF_InvalidateBackground(c->hwnd); } if (c->flags & ALF_LAYOUT_TRANSPARENTBG) { flags |= SWP_NOCOPYBITS; } } } if (!EqualRect(&r, &oldR)) { hdwp = DeferWindowPos(hdwp, c->hwnd, 0, r.left, r.top, r.right - r.left, r.bottom - r.top, flags); } } } EndDeferWindowPos(hdwp); layout->layoutValididityFlags &= ~ALF_LAYOUT_NEED_REAPPLY; } void ALF_Layout_AddControl(ALFLayout* layout, HWND window, const ALFAddControlParams* params) { ALFControlPriv *w = ALF_New(ALFControlPriv, 1); w->hwnd = params->hwnd; w->x = params->x; w->y = params->y; w->width = params->width; w->height = params->height; w->flags = params->flags; if (GetParent(w->hwnd) != window) SetParent(w->hwnd, window); if (w->flags & ALF_LAYOUT_INHERITFONT) { ALF_Layout_ForwardFontToControl(layout, window, w, layout->font, 0); } if (w->flags & ALF_LAYOUT_INHERITBGCOLOR) { SendMessage(w->hwnd, ALF_WM_SETBGCOLOR, 0, (LPARAM)layout->bgcolor); } if (w->flags & ALF_LAYOUT_SENDDPICHANGE) { SendMessage(w->hwnd, ALF_WM_DPICHANGE, 0, (LPARAM)layout->dpi); } ALF_ListInsert(layout->controls.prev, &w->list); ALF_Layout_Invalidate(layout, window); } HWND ALF_Layout_ControlAtPosPriv(ALFLayout* layout, int x, int y) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->x == x && w->y == y) { return w->hwnd; } } return NULL; } void ALF_Layout_Invalidate(ALFLayout *layout, HWND window) { if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) return; layout->layoutValididityFlags |= ALF_LAYOUT_NEED_RECALC | ALF_LAYOUT_NEED_REAPPLY; HWND parent = GetParent(window); if (parent) { ALF_InvalidateLayout(parent); } PostMessage(window, ALF_WM_VALIDATELAYOUT, 0, 0); } BOOL ALF_Layout_Validate(ALFLayout *layout, HWND window) { BOOL ret = FALSE; if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) { ALF_Layout_CalcSizes(layout, window); ret = TRUE; } if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_REAPPLY) { ALF_Layout_Apply(layout, window); ret = TRUE; } return ret; } void ALF_Layout_GetMinSize(ALFLayout *layout, HWND window, SIZE *size) { if (layout->layoutValididityFlags & ALF_LAYOUT_NEED_RECALC) { ALF_Layout_CalcSizes(layout, window); } size->cx = layout->totalMinWidth + layout->containerMargins.left + layout->containerMargins.right; size->cy = layout->totalMinHeight + layout->containerMargins.top + layout->containerMargins.bottom; } BOOL ALF_Layout_GetControlPosPriv(ALFLayout *layout, HWND window, HWND needle, POINT *p) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { p->x = w->x; p->y = w->y; return TRUE; } } return FALSE; } static BOOL ALF_Layout_SetControlPosPriv(ALFLayout *layout, HWND window, HWND needle, POINT *p) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { w->x = p->x; w->y = p->y; ALF_Layout_Invalidate(layout, window); return TRUE; } } return FALSE; } BOOL ALF_Layout_GetControlSpanPriv(ALFLayout *layout, HWND window, HWND needle, POINT *p) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { p->x = w->xspan; p->y = w->yspan; return TRUE; } } return FALSE; } static BOOL ALF_Layout_SetControlSpanPriv(ALFLayout *layout, HWND window, HWND needle, POINT *p) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { w->xspan = p->x; w->yspan = p->y; ALF_Layout_Invalidate(layout, window); return TRUE; } } return FALSE; } static BOOL ALF_Layout_GetControlSizePriv(ALFLayout *layout, HWND window, HWND needle, SIZE *s) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { s->cx = w->width; s->cy = w->height; return TRUE; } } return FALSE; } static BOOL ALF_Layout_SetControlSizePriv(ALFLayout *layout, HWND window, HWND needle, SIZE *s) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { w->width = s->cx; w->height = s->cy; ALF_Layout_Invalidate(layout, window); return TRUE; } } return FALSE; } static DWORD ALF_Layout_GetControlFlagsPriv(ALFLayout *layout, HWND window, HWND needle) { (void)window; ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { return w->flags; } } return 0; } static BOOL ALF_Layout_SetControlFlagsPriv(ALFLayout *layout, HWND window, HWND needle, DWORD flags) { ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, w) { if (w->hwnd == needle) { w->flags = flags; if (flags & ALF_LAYOUT_INHERITFONT) ALF_Layout_ForwardFontToControl(layout, window, w, layout->font, 0); if (flags & ALF_LAYOUT_INHERITBGCOLOR) SendMessage(w->hwnd, ALF_WM_SETBGCOLOR, 0, (LPARAM)layout->bgcolor); if (flags & ALF_LAYOUT_SENDDPICHANGE) SendMessage(w->hwnd, ALF_WM_DPICHANGE, 0, (LPARAM)layout->dpi); ALF_Layout_Invalidate(layout, window); return TRUE; } } return FALSE; } int ALF_Layout_GetColumnSizePriv(ALFLayout *layout, HWND window, int colno) { (void)window; if (colno >= layout->nColumns) return 0; return layout->columns[colno].requestedMinWidth; } BOOL ALF_Layout_SetColumnSizePriv(ALFLayout *layout, HWND window, int colno, int size) { ALF_Layout_EnsureColumnExists(layout, colno); layout->columns[colno].requestedMinWidth = size; ALF_Layout_Invalidate(layout, window); return TRUE; } int ALF_Layout_GetColumnExpandPriv(ALFLayout *layout, HWND window, int colno) { (void)window; if (colno >= layout->nColumns) return 0; return layout->columns[colno].requestedExpandNumerator; } static BOOL ALF_Layout_SetColumnExpandPriv(ALFLayout *layout, HWND window, int colno, int expand) { ALF_Layout_EnsureColumnExists(layout, colno); layout->columns[colno].requestedExpandNumerator = expand; ALF_Layout_Invalidate(layout, window); return TRUE; } static DWORD ALF_Layout_GetColumnFlagsPriv(ALFLayout *layout, HWND window, int colno) { (void)window; if (colno >= layout->nColumns) return 0; return layout->columns[colno].requestedFlags; } static BOOL ALF_Layout_SetColumnFlagsPriv(ALFLayout *layout, HWND window, int colno, DWORD flags) { ALF_Layout_EnsureColumnExists(layout, colno); layout->columns[colno].requestedFlags = flags; ALF_Layout_Invalidate(layout, window); return TRUE; } static int ALF_Layout_GetRowSizePriv(ALFLayout *layout, HWND window, int rowno) { (void)window; if (rowno >= layout->nRows) return 0; return layout->rows[rowno].requestedMinWidth; } static BOOL ALF_Layout_SetRowSizePriv(ALFLayout *layout, HWND window, int rowno, int size) { ALF_Layout_EnsureRowExists(layout, rowno); layout->rows[rowno].requestedMinWidth = size; ALF_Layout_Invalidate(layout, window); return TRUE; } static int ALF_Layout_GetRowExpandPriv(ALFLayout *layout, HWND window, int rowno) { (void)window; if (rowno >= layout->nRows) return 0; return layout->rows[rowno].requestedExpandNumerator; } static BOOL ALF_Layout_SetRowExpandPriv(ALFLayout *layout, HWND window, int rowno, int expand) { ALF_Layout_EnsureRowExists(layout, rowno); layout->rows[rowno].requestedExpandNumerator = expand; ALF_Layout_Invalidate(layout, window); return TRUE; } static DWORD ALF_Layout_GetRowFlagsPriv(ALFLayout *layout, HWND window, int rowno) { (void)window; if (rowno >= layout->nRows) return 0; return layout->rows[rowno].requestedFlags; } static BOOL ALF_Layout_SetRowFlagsPriv(ALFLayout *layout, HWND window, int rowno, DWORD flags) { ALF_Layout_EnsureRowExists(layout, rowno); layout->rows[rowno].requestedFlags = flags; ALF_Layout_Invalidate(layout, window); return TRUE; } BOOL ALF_Layout_HandleMessage(ALFLayout *layout, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *pRet) { *pRet = 0; if (msg == ALF_WM_QUERYSIZE) { SIZE s = { 0, 0 }; ALF_Layout_GetMinSize(layout, hwnd, &s); if (((SIZE*)lparam)->cx < s.cx) ((SIZE*)lparam)->cx = s.cx; if (((SIZE*)lparam)->cy < s.cy) ((SIZE*)lparam)->cy = s.cy; return TRUE; } if (msg == ALF_WM_ADDCONTROL) { ALF_Layout_AddControl(layout, hwnd, (ALFAddControlParams *)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCTLATPOS) { *pRet = (LRESULT)ALF_Layout_ControlAtPosPriv(layout, ((int*)lparam)[0], ((int*)lparam)[1]); return TRUE; } if (msg == WM_SETFONT) { *pRet = 1; if (layout->font == (HFONT)wparam) return TRUE; layout->font = (HFONT)wparam; ALF_Layout_ForwardFont(layout, hwnd, (HFONT)wparam, lparam); return TRUE; } if (msg == WM_GETFONT) { *pRet = (LRESULT)layout->font; return TRUE; } if (msg == WM_SYSCOLORCHANGE) { // forward to children ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, c) { SendMessage(c->hwnd, WM_SYSCOLORCHANGE, wparam, lparam); } return FALSE; } if (msg == WM_SETTINGCHANGE) { // Controls could technically figure out if their layout parameters // have changed, but that is not easy so nobody would bother anyway. ALF_Layout_Invalidate(layout, hwnd); // forward to children ALF_FOR_LIST(ALFControlPriv, list, &layout->controls, c) { SendMessage(c->hwnd, WM_SETTINGCHANGE, wparam, lparam); } return FALSE; } if (msg == ALF_WM_SETBGCOLOR) { *pRet = 1; if (layout->bgcolor == (ALFColor)lparam) return TRUE; layout->bgcolor = (ALFColor)lparam; ALF_Layout_ForwardBgColor(layout, hwnd, wparam, lparam); ALF_Layout_HandleBackgroundChange(layout, hwnd); InvalidateRect(hwnd, NULL, TRUE); return TRUE; } if (msg == ALF_WM_GETBGCOLOR) { *pRet = (LRESULT)layout->bgcolor; return TRUE; } if (msg == ALF_WM_BACKGROUNDCHANGE) { if (layout->bgcolor != ALF_COLOR_TRANSPARENT) return TRUE; // solid bg -> nothing to do InvalidateRect(hwnd, NULL, TRUE); ALF_Layout_HandleBackgroundChange(layout, hwnd); return TRUE; } if (msg == ALF_WM_DPICHANGE) { int dpi = (int)lparam; if (dpi != layout->dpi) { layout->dpi = dpi; ALF_Layout_ForwardDpiChange(layout, hwnd, wparam, lparam); ALF_Layout_Invalidate(layout, hwnd); } return TRUE; } if (msg == ALF_WM_GETDPI) { *pRet = (LRESULT)layout->dpi; return TRUE; } if (msg == ALF_WM_INVALIDATELAYOUT) { ALF_Layout_Invalidate(layout, hwnd); return TRUE; } if (msg == ALF_WM_VALIDATELAYOUT) { ALF_Layout_Validate(layout, hwnd); return TRUE; } if (msg == ALF_WM_LYT_GETCTLPOS) { *pRet = (LRESULT)ALF_Layout_GetControlPosPriv(layout, hwnd, (HWND)wparam, (POINT *)lparam); return TRUE; } if (msg == ALF_WM_LYT_SETCTLPOS) { *pRet = (LRESULT)ALF_Layout_SetControlPosPriv(layout, hwnd, (HWND)wparam, (POINT *)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCTLSPAN) { *pRet = (LRESULT)ALF_Layout_GetControlSpanPriv(layout, hwnd, (HWND)wparam, (POINT *)lparam); return TRUE; } if (msg == ALF_WM_LYT_SETCTLSPAN) { *pRet = (LRESULT)ALF_Layout_SetControlSpanPriv(layout, hwnd, (HWND)wparam, (POINT *)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCTLSIZE) { *pRet = (LRESULT)ALF_Layout_GetControlSizePriv(layout, hwnd, (HWND)wparam, (SIZE *)lparam); return TRUE; } if (msg == ALF_WM_LYT_SETCTLSIZE) { *pRet = (LRESULT)ALF_Layout_SetControlSizePriv(layout, hwnd, (HWND)wparam, (SIZE *)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCTLFLAGS) { *pRet = (LRESULT)ALF_Layout_GetControlFlagsPriv(layout, hwnd, (HWND)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETCTLFLAGS) { *pRet = (LRESULT)ALF_Layout_SetControlFlagsPriv(layout, hwnd, (HWND)wparam, (DWORD)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCOLSIZE) { *pRet = (LRESULT)ALF_Layout_GetColumnSizePriv(layout, hwnd, (int)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETCOLSIZE) { *pRet = (LRESULT)ALF_Layout_SetColumnSizePriv(layout, hwnd, (int)wparam, (int)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCOLEXPAND) { *pRet = (LRESULT)ALF_Layout_GetColumnExpandPriv(layout, hwnd, (int)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETCOLEXPAND) { *pRet = (LRESULT)ALF_Layout_SetColumnExpandPriv(layout, hwnd, (int)wparam, (int)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETCOLFLAGS) { *pRet = (LRESULT)ALF_Layout_GetColumnFlagsPriv(layout, hwnd, (int)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETCOLFLAGS) { *pRet = (LRESULT)ALF_Layout_SetColumnFlagsPriv(layout, hwnd, (int)wparam, (DWORD)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETROWSIZE) { *pRet = (LRESULT)ALF_Layout_GetRowSizePriv(layout, hwnd, (int)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETROWSIZE) { *pRet = (LRESULT)ALF_Layout_SetRowSizePriv(layout, hwnd, (int)wparam, (int)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETROWEXPAND) { *pRet = (LRESULT)ALF_Layout_GetRowExpandPriv(layout, hwnd, (int)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETROWEXPAND) { *pRet = (LRESULT)ALF_Layout_SetRowExpandPriv(layout, hwnd, (int)wparam, (int)lparam); return TRUE; } if (msg == ALF_WM_LYT_GETROWFLAGS) { *pRet = (LRESULT)ALF_Layout_GetRowFlagsPriv(layout, hwnd, (int)wparam); return TRUE; } if (msg == ALF_WM_LYT_SETROWFLAGS) { *pRet = (LRESULT)ALF_Layout_SetRowFlagsPriv(layout, hwnd, (int)wparam, (DWORD)lparam); return TRUE; } return FALSE; } // public api DWORD ALF_Layout_ControlFlags(HWND parent, HWND control) { return (DWORD)SendMessage(parent, ALF_WM_LYT_GETCTLFLAGS, (WPARAM)control, 0); } BOOL ALF_Layout_SetControlFlags(HWND parent, HWND control, DWORD flags) { return (BOOL)SendMessage(parent, ALF_WM_LYT_SETCTLFLAGS, (WPARAM)control, (LPARAM)flags); } BOOL ALF_Layout_ControlPosition(HWND parent, HWND control, int *pX, int *pY) { POINT p = { 0, 0 }; LRESULT r = SendMessage(parent, ALF_WM_LYT_GETCTLPOS, (WPARAM)control, (LPARAM)&p); *pX = p.x; *pY = p.y; return (BOOL)r; } BOOL ALF_Layout_SetControlPosition(HWND parent, HWND control, int x, int y) { POINT p = { x, y }; return (BOOL)SendMessage(parent, ALF_WM_LYT_SETCTLPOS, (WPARAM)control, (LPARAM)&p); } BOOL ALF_Layout_ControlSpan(HWND parent, HWND control, int *pXspan, int *pYspan) { POINT p = { 0, 0 }; LRESULT r = SendMessage(parent, ALF_WM_LYT_GETCTLSPAN, (WPARAM)control, (LPARAM)&p); *pXspan = p.x; *pYspan = p.y; return (BOOL)r; } BOOL ALF_Layout_SetControlSpan(HWND parent, HWND control, int xspan, int yspan) { POINT p = { xspan, yspan }; return (BOOL)SendMessage(parent, ALF_WM_LYT_SETCTLSPAN, (WPARAM)control, (LPARAM)&p); } BOOL ALF_Layout_ControlMinSize(HWND parent, HWND control, int *pWidth, int *pHeight) { SIZE s = { 0, 0 }; LRESULT r = SendMessage(parent, ALF_WM_LYT_GETCTLSIZE, (WPARAM)control, (LPARAM)&s); *pWidth = s.cx; *pHeight = s.cy; return (BOOL)r; } BOOL ALF_Layout_SetControlMinSize(HWND parent, HWND control, int width, int height) { SIZE s = { width, height }; return (BOOL)SendMessage(parent, ALF_WM_LYT_SETCTLSIZE, (WPARAM)control, (LPARAM)&s); } HWND ALF_Layout_ControlAtPosition(HWND parent, int x, int y) { int xy[2] = { x, y }; return (HWND)SendMessage(parent, ALF_WM_LYT_GETCTLATPOS, 0, (LPARAM)&xy); } int ALF_Layout_RowMinSize(HWND parent, int rowno) { return (int)SendMessage(parent, ALF_WM_LYT_GETROWSIZE, (WPARAM)rowno, 0); } BOOL ALF_Layout_SetRowMinSize(HWND parent, int rowno, int rowsize) { return (BOOL)SendMessage(parent, ALF_WM_LYT_SETROWSIZE, (WPARAM)rowno, (LPARAM)rowsize); } int ALF_Layout_ColumnMinSize(HWND parent, int colno) { return (int)SendMessage(parent, ALF_WM_LYT_GETCOLSIZE, (WPARAM)colno, 0); } BOOL ALF_Layout_SetColumnMinSize(HWND parent, int colno, int colsize) { return (BOOL)SendMessage(parent, ALF_WM_LYT_SETCOLSIZE, (WPARAM)colno, (LPARAM)colsize); } DWORD ALF_Layout_RowFlags(HWND parent, int rowno) { return (DWORD)SendMessage(parent, ALF_WM_LYT_GETROWFLAGS, (WPARAM)rowno, 0); } BOOL ALF_Layout_SetRowFlags(HWND parent, int rowno, DWORD flags) { return (BOOL)SendMessage(parent, ALF_WM_LYT_SETROWFLAGS, (WPARAM)rowno, (LPARAM)flags); } DWORD ALF_Layout_ColumnFlags(HWND parent, int colno) { return (DWORD)SendMessage(parent, ALF_WM_LYT_GETCOLFLAGS, (WPARAM)colno, 0); } BOOL ALF_Layout_SetColumnFlags(HWND parent, int colno, DWORD flags) { return (BOOL)SendMessage(parent, ALF_WM_LYT_SETCOLFLAGS, (WPARAM)colno, (LPARAM)flags); } int ALF_Layout_RowExpandNumerator(HWND parent, int rowno) { return (int)SendMessage(parent, ALF_WM_LYT_GETROWEXPAND, (WPARAM)rowno, 0); } BOOL ALF_Layout_SetRowExpandNumerator(HWND parent, int rowno, int expand) { return (BOOL)SendMessage(parent, ALF_WM_LYT_SETROWEXPAND, (WPARAM)rowno, (LPARAM)expand); } int ALF_Layout_ColumnExpandNumerator(HWND parent, int colno) { return (int)SendMessage(parent, ALF_WM_LYT_GETCOLEXPAND, (WPARAM)colno, 0); } BOOL ALF_Layout_SetColumnExpandNumerator(HWND parent, int colno, int expand) { return (int)SendMessage(parent, ALF_WM_LYT_SETCOLEXPAND, (WPARAM)colno, (LPARAM)expand); } void ALF_InvalidateLayout(HWND hwnd) { SendMessage(hwnd, ALF_WM_INVALIDATELAYOUT, 0, 0); } void ALF_AddControl(HWND win, int x, int y, HWND control, int minWidth, int minHeight, DWORD flags) { ALFAddControlParams params; ZeroMemory(¶ms, sizeof(params)); params.hwnd = control; params.x = x; params.y = y; params.width = minWidth; params.height = minHeight; params.flags = flags; SendMessage(win, ALF_WM_ADDCONTROL, 0, (LPARAM)¶ms); }