Sine Striker
2023-12-11 677e7a737897e8c2a60564085821545810369e02
Widgets: Add window state handler
4个文件已修改
103 ■■■■■ 已修改文件
examples/shared/widgetframe/windowbar.cpp 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent.cpp 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_p.h 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbar.cpp
@@ -248,23 +248,26 @@
            QLabel *label = titleLabel();
            QAbstractButton *maxBtn = maxButton();
            switch (event->type()) {
                case QEvent::WindowIconChange:
                case QEvent::WindowIconChange: {
                    if (d_ptr->autoIcon && iconBtn) {
                        iconBtn->setIcon(w->windowIcon());
                        iconChanged(w->windowIcon());
                    }
                    break;
                case QEvent::WindowTitleChange:
                }
                case QEvent::WindowTitleChange: {
                    if (d_ptr->autoTitle && label) {
                        label->setText(w->windowTitle());
                        titleChanged(w->windowTitle());
                    }
                    break;
                case QEvent::WindowStateChange:
                }
                case QEvent::WindowStateChange: {
                    if (maxBtn) {
                        maxBtn->setChecked(w->isMaximized());
                    }
                    break;
                }
                default:
                    break;
            }
src/core/contexts/win32windowcontext.cpp
@@ -749,33 +749,13 @@
            return ::DefWindowProcW(hWnd, message, wParam, lParam);
        }
        switch (message) {
            case WM_NCCALCSIZE: {
                // Since Qt does the necessary processing of the WM_NCCALCSIZE message, we need to
                // forward it right away and process it in our native event filter.
                WindowsNativeEventFilter::lastMessageContext = ctx;
                LRESULT result = ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
                WindowsNativeEventFilter::lastMessageContext = nullptr;
                return result;
            }
            case WM_WINDOWPOSCHANGING: {
                // When toggling the "Show theme color in title bar and window border" setting in
                // Windows Settings, or calling `DrawMenuBar()`, Windows sends a message of
                // WM_WINDOWPOSCHANGING with flags 0x37. If we do not process this message,
                // the client area as a whole will shift to the left, which looks very abnormal if
                // we don't repaint it. This exception disappears if we add SWP_NOCOPYBITS flag.
                // But I don't know what caused the problem, or why this would solve it.
                const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
                if (windowPos->flags ==
                    (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED)) {
                    windowPos->flags |= SWP_NOCOPYBITS;
                }
                return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
            }
            default:
                break;
        // Since Qt does the necessary processing of the WM_NCCALCSIZE message, we need to
        // forward it right away and process it in our native event filter.
        if (message == WM_NCCALCSIZE) {
            WindowsNativeEventFilter::lastMessageContext = ctx;
            LRESULT result = ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
            WindowsNativeEventFilter::lastMessageContext = nullptr;
            return result;
        }
        // Try hooked procedure and save result
@@ -1250,6 +1230,7 @@
                }
                break;
            }
            case WM_NCHITTEST: {
                // 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口
                // 外部进行resize的,其原理是,WS_THICKFRAME这个窗口样式会在窗
@@ -1538,9 +1519,27 @@
                    return true;
                }
            }
            case WM_WINDOWPOSCHANGING: {
                // ### FIXME: How does this problem happen and why is it solved?
                // When toggling the "Show theme color in title bar and window border" setting in
                // Windows Settings, or calling `DrawMenuBar()`, Windows sends a message of
                // WM_WINDOWPOSCHANGING with flags 0x37. If we do not process this message,
                // the client area as a whole will shift to the left, which looks very abnormal if
                // we don't repaint it. This exception disappears if we add SWP_NOCOPYBITS flag.
                // But I don't know what caused the problem, or why this would solve it.
                const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
                if (windowPos->flags ==
                    (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED)) {
                    windowPos->flags |= SWP_NOCOPYBITS;
                }
                break;
            }
            default:
                break;
        }
        if (!isWin10OrGreater()) {
            switch (message) {
                case WM_NCUAHDRAWCAPTION:
src/widgets/widgetwindowagent.cpp
@@ -3,25 +3,38 @@
#include <QtGui/QtEvents>
#include <QtGui/QPainter>
#include <QtCore/QDebug>
#include "widgetitemdelegate_p.h"
namespace QWK {
    class WidgetPaintFilter : public QObject {
    class WidgetBorderHandler : public QObject {
    public:
        WidgetPaintFilter(QWidget *widget, AbstractWindowContext *ctx) : widget(widget), ctx(ctx) {
        WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx)
            : widget(widget), ctx(ctx) {
            widget->installEventFilter(this);
        }
        void updateMargins() {
            if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
                widget->setContentsMargins({});
            } else {
                widget->setContentsMargins({0, 1, 0, 0});
            }
        }
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override {
            switch (event->type()) {
                case QEvent::Paint: {
                    auto pe = static_cast<QPaintEvent *>(event);
                    if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen))
                        break;
                    auto paintEvent = static_cast<QPaintEvent *>(event);
                    QPainter painter(widget);
                    QRect rect = pe->rect();
                    QRegion region = pe->region();
                    QRect rect = paintEvent->rect();
                    QRegion region = paintEvent->region();
                    void *args[] = {
                        &painter,
                        &rect,
@@ -29,6 +42,11 @@
                    };
                    ctx->virtual_hook(AbstractWindowContext::DrawBordersHook, args);
                    return true;
                }
                case QEvent::WindowStateChange: {
                    updateMargins();
                    break;
                }
                default:
                    break;
@@ -79,13 +97,10 @@
        if (bool needPaintBorder = false;
            d->context->virtual_hook(AbstractWindowContext::NeedsDrawBordersHook, &needPaintBorder),
            needPaintBorder) {
            d->paintFilter = std::make_unique<WidgetPaintFilter>(w, d->context.get());
            auto borderHandler = std::make_unique<WidgetBorderHandler>(w, d->context.get());
            borderHandler->updateMargins();
            d->borderHandler = std::move(borderHandler);
        }
        if (d->context->key() == "win32") {
            w->setContentsMargins(0, 1, 0, 0);
        }
        return true;
    }
src/widgets/widgetwindowagent_p.h
@@ -6,6 +6,8 @@
namespace QWK {
    class WidgetBorderHandler;
    class WidgetWindowAgentPrivate : public WindowAgentBasePrivate {
        Q_DECLARE_PUBLIC(WidgetWindowAgent)
    public:
@@ -17,7 +19,7 @@
        // Host
        QWidget *hostWidget{};
        std::unique_ptr<QObject> paintFilter;
        std::unique_ptr<WidgetBorderHandler> borderHandler;
    };
}