• 让Qt的无边框窗口支持拖拽、Aero Snap、窗口阴影等特性


    环境:Desktop Qt 5.4.1 MSVC2013 32bit

    需要的库:dwmapi.lib 、user32.lib

    需要头文件:<dwmapi.h> 、<windowsx.h>

    在要处理的QWidget 构造函数中,添加以下两行:

    1
    2
    setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
    SetWidgetBorderless(this);

    SetWidgetBorderless的实现如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void SetWidgetBorderless(const QWidget *widget)
    {
    #ifdef Q_OS_WIN
    HWND hwnd = reinterpret_cast<HWND>(widget->winId());

    const LONG style = ( WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN );
    SetWindowLongPtr(hwnd, GWL_STYLE, style);

    const MARGINS shadow = {1, 1, 1, 1};
    DwmExtendFrameIntoClientArea(hwnd, &shadow);

    SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
    #endif
    }

    这个函数的作用是给无边框窗口加上阴影、Aero Snap以及其他动画特效。

    这时窗口还无法手动更改大小,需要更改的话,需要自己实现一个QAbstractNativeEventFilter 类,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    class NativeEventFilter : public QAbstractNativeEventFilter
    {
    public:
    bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE
    {
    #ifdef Q_OS_WIN
    if (eventType != "windows_generic_MSG")
    return false;

    MSG* msg = static_cast<MSG*>(message);
    QWidget* widget = QWidget::find(reinterpret_cast<WId>(msg->hwnd));
    if (!widget)
    return false;

    switch (msg->message) {
    case WM_NCCALCSIZE: {
    *result = 0;
    return true;
    }

    case WM_NCHITTEST: {
    const LONG borderWidth = 9;
    RECT winrect;
    GetWindowRect(msg->hwnd, &winrect);
    long x = GET_X_LPARAM(msg->lParam);
    long y = GET_Y_LPARAM(msg->lParam);

    // bottom left
    if (x >= winrect.left && x < winrect.left + borderWidth &&
    y < winrect.bottom && y >= winrect.bottom - borderWidth)
    {
    *result = HTBOTTOMLEFT;
    return true;
    }

    // bottom right
    if (x < winrect.right && x >= winrect.right - borderWidth &&
    y < winrect.bottom && y >= winrect.bottom - borderWidth)
    {
    *result = HTBOTTOMRIGHT;
    return true;
    }

    // top left
    if (x >= winrect.left && x < winrect.left + borderWidth &&
    y >= winrect.top && y < winrect.top + borderWidth)
    {
    *result = HTTOPLEFT;
    return true;
    }

    // top right
    if (x < winrect.right && x >= winrect.right - borderWidth &&
    y >= winrect.top && y < winrect.top + borderWidth)
    {
    *result = HTTOPRIGHT;
    return true;
    }

    // left
    if (x >= winrect.left && x < winrect.left + borderWidth)
    {
    *result = HTLEFT;
    return true;
    }

    // right
    if (x < winrect.right && x >= winrect.right - borderWidth)
    {
    *result = HTRIGHT;
    return true;
    }

    // bottom
    if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
    {
    *result = HTBOTTOM;
    return true;
    }

    // top
    if (y >= winrect.top && y < winrect.top + borderWidth)
    {
    *result = HTTOP;
    return true;
    }

    return false;
    }
    default:
    break;
    }

    return false;
    #else
    return false;
    #endif
    }
    };

    然后在窗口创建之前,使用QApplication::installNativeEventFilter 方法把监听器注册给主程序。

    要手动移动窗口位置的话,还要重截QWidget::mousePressEvent 方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void MyWidget::mousePressEvent(QMouseEvent *ev)
    {
    QWidget::mousePressEvent(ev);
    if (!ev->isAccepted()) {
    #ifdef Q_OS_WIN
    ReleaseCapture();
    SendMessage(reinterpret_cast<HWND>(winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
    }
    #endif
    }

    在实际操作时,有时还需要添加最大化和关闭按扭,这时正常调用QWidget::showMaximized()QWidget::close() 等Qt自带方法即可。

    最终实现效果大概是这样:

    frameless_window_preview

    缩放动画、阴影什么的和Windows本地窗口一样。

    参考链接:https://github.com/deimos1877/BorderlessWindow

    https://blog.yeatse.com/2015/03/01/qt-frameless-window/

  • 相关阅读:
    杭电1212--Big Number
    杭电1407--Integer Inquiry
    杭电1201--18岁生日
    刚刚创建了这个分组, 也想说叨说叨 。
    南阳325--zb的生日
    杭电1239--Calling Extraterrestrial Intelligence Again
    南阳891--找点(区间覆盖)
    杭电1234--开门人和关门人
    杭电1862--EXCEL排序
    模拟:HDU1034-Candy Sharing Game
  • 原文地址:https://www.cnblogs.com/findumars/p/6204345.html
Copyright © 2020-2023  润新知