qt窗口在windows系统下完美实现边框阴影

    哈喽,好久不见!今天要分享的内容是qt编写的窗口在windows系统下如何完美实现窗口边框阴影。

    qt虽然自带阴影类QGraphicsDropShadowEffect,但是它的原理是需要两层widget,上面一层显示窗口,下面一层作为阴影。但是,如果产品经理反复横跳,刚开始不要阴影,后来又提出需求了,怎么办?这时你的窗口布局什么的全都弄好了,又不好修改。如何做到想加就加,想不要就不要呢?请往下看。

    我们这里是使用的windows api做的,所以做全平台的就不能帮助啦。

    首先,我们使用windows的dwmapi创建一个阴影类。

//*******************************************************h*******************************************

#ifndef WINDWMAPI_H
#define WINDWMAPI_H

#include <windows.h>

typedef struct _MARGINS
{
    int cxLeftWidth;      // width of left border that retains its size
    int cxRightWidth;     // width of right border that retains its size
    int cyTopHeight;      // height of top border that retains its size
    int cyBottomHeight;   // height of bottom border that retains its size
} MARGINS, *PMARGINS;

class WinDwmapi
{
public:
    WinDwmapi();
    ~WinDwmapi();

    typedef HRESULT (WINAPI* DwmIsCompositionEnabledPtr)(BOOL* pfEnabled);
    typedef HRESULT (WINAPI* DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset);

    HRESULT DwmIsCompositionEnabled(BOOL* pfEnabled) const;
    HRESULT DwmExtendFrameIntoClientArea(HWND hWnd, const MARGINS *pMarInset) const;

    static const WinDwmapi* instance();

private:
      DwmIsCompositionEnabledPtr      dwm_is_composition_enabled_;
      DwmExtendFrameIntoClientAreaPtr dwm_extendframe_into_client_area_;
      HMODULE dwmapi_dll_;
};

#endif // WINDWMAPI_H

//********************************************************cpp***************************************************

#include "windwmapi.h"

WinDwmapi::WinDwmapi()
    : dwmapi_dll_(LoadLibraryW(L"dwmapi.dll"))
    , dwm_is_composition_enabled_(NULL)
{
    if (dwmapi_dll_) 
    {
        dwm_is_composition_enabled_       = reinterpret_cast<DwmIsCompositionEnabledPtr>(GetProcAddress(dwmapi_dll_, "DwmIsCompositionEnabled"));
        dwm_extendframe_into_client_area_ = reinterpret_cast<DwmExtendFrameIntoClientAreaPtr>(GetProcAddress(dwmapi_dll_, "DwmExtendFrameIntoClientArea"));
    }
}

WinDwmapi::~WinDwmapi()
{
    if (dwmapi_dll_) 
    {
        FreeLibrary(dwmapi_dll_);
    }
}

HRESULT WinDwmapi::DwmIsCompositionEnabled(BOOL *pfEnabled) const
{
    if (dwm_is_composition_enabled_) 
    {
        return dwm_is_composition_enabled_(pfEnabled);
    }

    return E_NOTIMPL;
}

HRESULT WinDwmapi::DwmExtendFrameIntoClientArea(HWND hWnd, const MARGINS *pMarInset) const
{
    if (dwm_extendframe_into_client_area_) 
    {
        return dwm_extendframe_into_client_area_(hWnd, pMarInset);
    }

    return E_NOTIMPL;
}

const WinDwmapi *WinDwmapi::instance()
{
    static const WinDwmapi s_dwmapi;
    return &s_dwmapi;
}

 

接下来使用的时候,只需要在窗口的构造函数里面调用一下就可以了

    HWND hwnd = (HWND)this->winId();
    DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
    ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
    const MARGINS shadow = { 1, 1, 1, 1 }; //边框保留1px以绘制阴影
    WinDwmapi::instance()->DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);

 

    这里就已经可以出现边框阴影了,但是会带来三个个问题:1、它会使窗口边高大,所以在这后面你需要resize一次。

2、它会带来系统边框,需要在nativeEvent里面解决。3、在全屏的时候会使窗口超出屏幕,这里也在nativeEvent里面解决。解决问题2的思路是在nativeEvent里面截取WM_NCCALCSIZE信息,然后直接返回,使系统无法设置边框。解决3的思路是截取窗口状态变化信息,当为全屏时,在四周设置一定外边框,当取消全屏时就取消四周外边框。

   下面就来解决问题2和3(重载nativeEvent)

    bool CMainWdg::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG *msg = (MSG*)message;

 //去除阴影带来的边框问题
     if (msg->message == WM_NCCALCSIZE)
     {
        *result = 0;
        return true;
     }
         
     //窗口显示时的大小的信号
     if(msg->message == WM_GETMINMAXINFO)
     {
         if (::IsZoomed(msg->hwnd))
         {
             // 最大化时会超出屏幕,所以填充边框间距
             RECT frame = { 0, 0, 0, 0 };
             AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
             frame.left = abs(frame.left);
             frame.top  = abs(frame.bottom);
             m_top = frame.top;
             m_vblMain->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);
             m_hblTitle->setContentsMargins(frame.left, 0, frame.right, 0);
         }
         else
         {
             m_top = 0;
             m_vblMain->setContentsMargins(1, 0, 1, 1);   //这里是恢复我的原来边框设置,如果你没有可以不要
             m_hblTitle->setContentsMargins(0, 0, 0, 0);    //这里也是恢复原来的边框设置,如果你没有可以不要
         }

         //*result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
         //return true;
     }

    return QWidget::nativeEvent(eventType, message, result);

}

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值