#include "alfpriv.h" TCHAR *_alf_groupboxClass = NULL; typedef struct { ALFLayout layout; HTHEME theme; HWND label; SIZE calculatedLabelSize; } ALFGroupBoxPriv; static void ALF_GroupBox_BeginCalcSizes(ALFLayout *layout, HWND hwnd); static void ALF_GroupBox_HandleContainerWidgetSize(ALFLayout *layout, HWND groupbox, HWND child, SIZE *s); static void ALF_GroupBox_EndCalcSizes(ALFLayout *layout, HWND hwnd, SIZE *containerMinSize); static void ALF_GroupBox_PreContainerWidgetApplyPos(ALFLayout *layout, HWND groupbox, HWND child, RECT *r); static void ALF_GroupBox_IntializePriv(ALFGroupBoxPriv *priv, HWND window, const TCHAR *text) { ALF_Layout_Init(&priv->layout); priv->layout.beginCalcSizes = ALF_GroupBox_BeginCalcSizes; priv->layout.handleContainerWidgetSize = ALF_GroupBox_HandleContainerWidgetSize; priv->layout.endCalcSizes = ALF_GroupBox_EndCalcSizes; priv->layout.preApplyLayoutToContainerWidget = ALF_GroupBox_PreContainerWidgetApplyPos; priv->label = ALF_AddLabel(window, (WORD)-1, -1, -1, text); ALF_LabelSetStyle(priv->label, ALF_LABEL_ALIGN_LEFT | ALF_LABEL_ALIGN_TOP); } static void ALF_GroupBox_ClearPriv(ALFGroupBoxPriv *priv) { ALF_Layout_Clear(&priv->layout); } static void ALF_GroupBox_PaintFrameClassic(ALFGroupBoxPriv *priv, HWND hwnd, HDC dc) { (void)priv; HFONT oldfont = SelectFont(dc, priv->layout.font); RECT rcClient = { 0,0,0,0 }; GetClientRect(hwnd, &rcClient); RECT rcLabel = { 0,0,0,0 }; GetWindowRect(priv->label, &rcLabel); MapWindowRect(NULL, hwnd, &rcLabel); RECT rcFrame; rcFrame.top = (rcLabel.bottom - rcLabel.top) / 2; rcFrame.left = 0; rcFrame.bottom = rcLabel.bottom; rcFrame.right = rcLabel.left - 2; // XXX: is that correct? DrawEdge(dc, &rcFrame, EDGE_ETCHED, BF_TOPLEFT); rcFrame.left = rcLabel.right + 2; // XXX: is that correct? rcFrame.right = rcClient.right; DrawEdge(dc, &rcFrame, EDGE_ETCHED, BF_TOPRIGHT); rcFrame.top = rcLabel.bottom; rcFrame.left = 0; rcFrame.right = rcClient.right; rcFrame.bottom = rcClient.bottom; DrawEdge(dc, &rcFrame, EDGE_ETCHED, BF_LEFT | BF_BOTTOM | BF_RIGHT); SelectFont(dc, oldfont); } static void ALF_GroupBox_Paint(ALFGroupBoxPriv *priv, HWND hwnd, HDC dc, RECT *r) { if (priv->layout.bgcolor == ALF_COLOR_TRANSPARENT) { ALF_Compat_DrawThemeParentBackground(hwnd, dc, r); } else { ALF_FillRect(dc, r, priv->layout.bgcolor); } // unthemed paint ALF_GroupBox_PaintFrameClassic(priv, hwnd, dc); } static void ALF_GroupBox_BeginCalcSizes(ALFLayout *layout, HWND hwnd) { ALFGroupBoxPriv *priv = (ALFGroupBoxPriv *)layout; (void)hwnd; priv->layout.containerMargins.left = 2 + ALF_CentipointsToPixels(525, priv->layout.dpi); priv->layout.containerMargins.top = 2 + ALF_CentipointsToPixels(300, priv->layout.dpi); priv->layout.containerMargins.right = 2 + ALF_CentipointsToPixels(525, priv->layout.dpi); priv->layout.containerMargins.bottom = 2 + ALF_CentipointsToPixels(525, priv->layout.dpi); priv->calculatedLabelSize.cx = 0; priv->calculatedLabelSize.cy = 0; } static void ALF_GroupBox_HandleContainerWidgetSize(ALFLayout *layout, HWND groupbox, HWND child, SIZE *s) { ALFGroupBoxPriv *priv = (ALFGroupBoxPriv *)layout; (void)groupbox; if (child == priv->label) { priv->calculatedLabelSize = *s; int h = priv->calculatedLabelSize.cy + ALF_CentipointsToPixels(300, priv->layout.dpi); if (h > priv->layout.containerMargins.top) { priv->layout.containerMargins.top = h; } } } static void ALF_GroupBox_EndCalcSizes(ALFLayout *layout, HWND hwnd, SIZE *containerMinSize) { ALFGroupBoxPriv *priv = (ALFGroupBoxPriv *)layout; (void)hwnd; containerMinSize->cx = priv->layout.containerMargins.left + priv->layout.containerMargins.right + priv->calculatedLabelSize.cx; containerMinSize->cy = priv->layout.containerMargins.top + priv->layout.containerMargins.bottom; } static void ALF_GroupBox_PreContainerWidgetApplyPos(ALFLayout *layout, HWND groupbox, HWND child, RECT *r) { ALFGroupBoxPriv *priv = (ALFGroupBoxPriv *)layout; if (child == priv->label) { RECT rOld; GetClientRect(child, &rOld); if (rOld.bottom - rOld.top != priv->calculatedLabelSize.cy || rOld.right - rOld.left != priv->calculatedLabelSize.cx) { // if the label changed, the top part of the border needs to be redrawn RECT rcGb; GetClientRect(groupbox, &rcGb); RECT i = { 0, 0, rcGb.right - rcGb.left, priv->calculatedLabelSize.cy }; InvalidateRect(groupbox, &i, TRUE); } r->left = priv->layout.containerMargins.left; r->top = 0; r->right = r->left + priv->calculatedLabelSize.cx; r->bottom = priv->calculatedLabelSize.cy; } } static LRESULT WINAPI ALF_GroupBox_WindowProc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_NCCREATE) { ALFGroupBoxPriv *p = ALF_New(ALFGroupBoxPriv, 1); SetWindowLongPtr(window, 0, (LONG_PTR)p); ALF_GroupBox_IntializePriv(p, window, ((CREATESTRUCT *)lparam)->lpszName); } ALFGroupBoxPriv *priv = (ALFGroupBoxPriv *)GetWindowLongPtr(window, 0); if (!priv) return DefWindowProc(window, msg, wparam, lparam); // FIXME! shouldn't happen if (msg == WM_NCDESTROY) { ALF_GroupBox_ClearPriv(priv); ALF_Free(priv); priv = NULL; SetWindowLongPtr(window, 0, 0); } if (msg == WM_ERASEBKGND) { return TRUE; } if (msg == WM_PRINTCLIENT) { RECT r = { 0, 0, 0, 0 }; GetClientRect(window, &r); ALF_GroupBox_Paint(priv, window, (HDC)wparam, &r); return TRUE; } if (msg == WM_PAINT) { PAINTSTRUCT ps; HDC dc = BeginPaint(window, &ps); ALF_GroupBox_Paint(priv, window, dc, &ps.rcPaint); EndPaint(window, &ps); return 0; } if (msg == WM_SETTEXT) { return SendMessage(priv->label, WM_SETTEXT, wparam, lparam); } if (msg == WM_GETTEXT) { return SendMessage(priv->label, WM_GETTEXT, wparam, lparam); } if (ALF_ShouldMessageBubble(window, msg, wparam, lparam)) return SendMessage(GetParent(window), msg, wparam, lparam); if (msg == WM_SIZE) { RECT c; GetClientRect(window, &c); if (c.right > priv->layout.allocatedWidgetRect.right + priv->layout.containerMargins.right) { // enlarged to the right -> invalidate place occupied by old border RECT i = { priv->layout.allocatedWidgetRect.right, 0, c.right, c.bottom }; InvalidateRect(window, &i, TRUE); } if (c.bottom > priv->layout.allocatedWidgetRect.bottom + priv->layout.containerMargins.bottom) { // enlarged to the bottom -> invalidate place occupied by old border RECT i = { 0, priv->layout.allocatedWidgetRect.bottom, c.right, c.bottom }; InvalidateRect(window, &i, TRUE); } if (c.right < priv->layout.allocatedWidgetRect.right + priv->layout.containerMargins.right) { // shrunk horizontally -> invalidate place now occupied by new border RECT i = { c.right - priv->layout.containerMargins.right, 0, c.right, c.bottom }; InvalidateRect(window, &i, TRUE); } if (c.bottom < priv->layout.allocatedWidgetRect.bottom + priv->layout.containerMargins.bottom) { // shrunk vertically -> invalidate place now ocuupied by new border RECT i = { 0, c.bottom - priv->layout.containerMargins.bottom, c.right, c.bottom }; InvalidateRect(window, &i, TRUE); } ALF_Layout_Apply(&priv->layout, window); } if (msg == WM_ENABLE) { EnableWindow(priv->label, (BOOL)wparam); } LRESULT ret = 0; if (ALF_Layout_HandleMessage(&priv->layout, window, msg, wparam, lparam, &ret)) return ret; return DefWindowProc(window, msg, wparam, lparam); } void ALF_RegisterGroupBoxClass(void) { WNDCLASS cls; ZeroMemory(&cls, sizeof(cls)); TCHAR classNameBuf[256]; ALF_BuildUniqueName(classNameBuf, TEXT("ALFGroupBox."), (ULONG_PTR)&_alf_groupboxClass); cls.hInstance = ALF_HINSTANCE; cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW); cls.lpszClassName = classNameBuf; cls.cbWndExtra = sizeof(void*); cls.lpfnWndProc = ALF_GroupBox_WindowProc; ATOM classatom = RegisterClass(&cls); if (!classatom) MessageBox(NULL, TEXT("FATAL: Could not register groupbox class"), NULL, MB_OK); _alf_groupboxClass = MAKEINTATOM(classatom); } HWND ALF_AddGroupBox(HWND parent, WORD id, int x, int y, const TCHAR *text) { HWND hwndPanel = CreateWindowEx(WS_EX_CONTROLPARENT, _alf_groupboxClass, text, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_TABSTOP, 0, 0, 0, 0, parent, (HMENU)(UINT_PTR)id, ALF_HINSTANCE, NULL); ALF_AddWidget(parent, x, y, hwndPanel, 0, 0, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT | ALF_LAYOUT_INHERITBGCOLOR | ALF_LAYOUT_SENDBGCHANGE | ALF_LAYOUT_SENDDPICHANGE); return hwndPanel; }