Skip to content

实现圆角窗口

Warning

The current page still doesn't have a translation for this language.

You can read it through google translate.

在SOUI中实现圆角窗口是一个常见的需求。本文将介绍如何在非半透明窗口中实现圆角效果。

实现方案

半透明窗口的简单方案

如果SOUI的宿主窗口不包含子窗口,可以直接使用窗口的半透明属性:

<window translucent="1">
这种方法可以实现完美的圆角效果,窗口形状完全由背景图决定。

非半透明窗口的解决方案

当窗口中包含子窗口时,半透明属性可能会导致子窗口显示异常。这种情况下,我们需要使用SetWindowRgn来实现圆角效果。

专业实现代码

以下是一个专门用于实现圆角窗口的模板类:

template <class T>
class CWHRoundRectFrameHelper
{
protected:
    SIZE m_sizeWnd;
    void OnSize(UINT nType, CSize size)
    {
        T *pT = static_cast<T*>(this);
        if (nType == SIZE_MINIMIZED)
            return;
        if (size == m_sizeWnd)
            return;

        CRect rcWindow, rcClient;
        CRgn rgnWindow, rgnMinus, rgnAdd;

        pT->CSimpleWnd::GetWindowRect(rcWindow);
        pT->CSimpleWnd::GetClientRect(rcClient);
        pT->CSimpleWnd::ClientToScreen(rcClient);
        rcClient.OffsetRect(- rcWindow.TopLeft());

        // 创建基本区域
        rgnWindow.CreateRectRgn(rcClient.left, rcClient.top + 2,
            rcClient.right, rcClient.bottom - 2);

        // 添加上边圆角
        rgnAdd.CreateRectRgn(rcClient.left + 2, rcClient.top,
            rcClient.right - 2, rcClient.top + 1);
        rgnWindow.CombineRgn(rgnAdd, RGN_OR);

        // 添加下边圆角
        rgnAdd.OffsetRgn(0, rcClient.Height() - 1);
        rgnWindow.CombineRgn(rgnAdd, RGN_OR);

        // 添加上边过渡区
        rgnAdd.SetRectRgn(rcClient.left + 1, rcClient.top + 1,
            rcClient.right - 1, rcClient.top + 2);
        rgnWindow.CombineRgn(rgnAdd, RGN_OR);

        // 添加下边过渡区
        rgnAdd.OffsetRgn(0, rcClient.Height() - 3);
        rgnWindow.CombineRgn(rgnAdd, RGN_OR);

        // 设置窗口区域
        pT->CSimpleWnd::SetWindowRgn(rgnWindow);
        pT->SetMsgHandled(FALSE);
        m_sizeWnd = size;
    }

public:
    BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam,
        LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0)
    {
        BOOL bHandled = TRUE;
        switch(dwMsgMapID)
        {
            case 0:
                if (uMsg == WM_SIZE)
                {
                    OnSize((UINT)wParam,
                        _WTYPES_NS::CSize(GET_X_LPARAM(lParam),
                        GET_Y_LPARAM(lParam)));
                    lResult = 0;
                }
                break;
        }
        return FALSE;
    }
};

使用示例

在SOUI对话框中使用圆角窗口模板类:

class CFuckDialog : public SHostDialog,
    public CWHRoundRectFrameHelper<CFuckDialog>
{
    BEGIN_MSG_MAP_EX(CMainDlg)
        // 重要:必须在SHostDialog之前
        CHAIN_MSG_MAP(CWHRoundRectFrameHelper<CFuckDialog>)
        CHAIN_MSG_MAP(SHostDialog)
        REFLECT_NOTIFICATIONS_EX()
    END_MSG_MAP()
};

注意事项

  1. 圆角实现基于SetWindowRgn API
  2. 需要在窗口大小变化时重新设置区域
  3. 消息映射表中,圆角处理必须在CHAIN_MSG_MAP(SHostDialog)之前

优点与局限

优点

  • 可以与子窗口共存
  • 不影响窗口性能
  • 可以精确控制圆角大小

局限

  • 只能实现规则的圆角形状
  • 不支持不规则的异形窗口

最佳实践

  1. 根据实际需求选择合适的方案
  2. 无子窗口时使用半透明属性
  3. 有子窗口时使用SetWindowRgn

  4. 注意性能优化

  5. 缓存窗口大小避免重复计算
  6. 仅在必要时更新窗口区域