Sine Striker
2023-12-24 46285db399f75154ad6c451e12f2cae2e59ace25
Use brilliant workaround to show Windows 10 top border
8个文件已修改
118 ■■■■ 已修改文件
examples/mainwindow/mainwindow.cpp 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext_p.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/nativeeventfilter.cpp 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/nativeeventfilter_p.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/shared/qwkwindowsextra_p.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_win.cpp 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.cpp
@@ -115,6 +115,10 @@
    windowAgent = new QWK::WidgetWindowAgent(this);
    windowAgent->setup(this);
#ifdef Q_OS_WIN
    windowAgent->setWindowAttribute(QStringLiteral("dark-mode"), true);
#endif
    // 2. Construct your title bar
    auto menuBar = [this]() {
        auto menuBar = new QMenuBar();
@@ -298,10 +302,6 @@
    if (!styleSheet().isEmpty() && theme == currentTheme)
        return;
    currentTheme = theme;
#ifdef Q_OS_WIN
    windowAgent->setWindowAttribute(QStringLiteral("dark-mode"), currentTheme == Dark);
#endif
    if (QFile qss(theme == Dark ? QStringLiteral(":/dark-style.qss")
                                : QStringLiteral(":/light-style.qss"));
src/core/contexts/abstractwindowcontext_p.h
@@ -64,7 +64,8 @@
            RaiseWindowHook,
            ShowSystemMenuHook,
            DefaultColorsHook,
            DrawWindows10BorderHook,     // Only works on Windows 10
            DrawWindows10BorderHook,     // Only works on Windows 10, emulated workaround
            DrawWindows10BorderHook2,    // Only works on Windows 10, native workaround
            SystemButtonAreaChangedHook, // Only works on Mac
        };
        virtual void virtual_hook(int id, void *data);
src/core/contexts/win32windowcontext.cpp
@@ -5,6 +5,7 @@
#include <QtCore/QHash>
#include <QtCore/QScopeGuard>
#include <QtCore/QTimer>
#include <QtCore/QDateTime>
#include <QtGui/QGuiApplication>
#include <QtGui/QPainter>
#include <QtGui/QPalette>
@@ -658,6 +659,7 @@
            }
            case DrawWindows10BorderHook: {
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
                if (!windowId)
                    return;
@@ -668,7 +670,7 @@
                const auto hwnd = reinterpret_cast<HWND>(windowId);
                QPen pen;
                pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2);
                pen.setWidth(int(getWindowFrameBorderThickness(hwnd)) * 2);
                const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd);
                if (m_delegate->isWindowActive(m_host)) {
@@ -688,7 +690,7 @@
                }
                painter.save();
                // We needs anti-aliasing to give us better result.
                // We need antialiasing to give us better result.
                painter.setRenderHint(QPainter::Antialiasing);
                painter.setPen(pen);
@@ -697,6 +699,33 @@
                    QPoint{m_windowHandle->width(), 0}
                });
                painter.restore();
                return;
#endif
            }
            case DrawWindows10BorderHook2: {
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
                if (!m_windowHandle)
                    return;
                // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L1025
                // https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
                // Draw a black rectangle to make Windows native top border show
                auto hWnd = reinterpret_cast<HWND>(windowId);
                HDC hdc = ::GetDC(hWnd);
                RECT windowRect{};
                ::GetClientRect(hWnd, &windowRect);
                RECT rcTopBorder = {
                    0,
                    0,
                    RECT_WIDTH(windowRect),
                    int(getWindowFrameBorderThickness(hWnd)),
                };
                ::FillRect(hdc, &rcTopBorder,
                           reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)));
                ::ReleaseDC(hWnd, hdc);
#endif
                return;
            }
@@ -712,7 +741,15 @@
    }
    int Win32WindowContext::borderThickness() const {
        return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
        return int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)));
    }
    void Win32WindowContext::resume(const QByteArray &eventType, void *message,
                                    QT_NATIVE_EVENT_RESULT_TYPE *result) {
        const auto msg = static_cast<const MSG *>(message);
        LRESULT res =
            ::CallWindowProcW(g_qtWindowProc, msg->hwnd, msg->message, msg->wParam, msg->lParam);
        *result = decltype(*result)(res);
    }
    void Win32WindowContext::winIdChanged() {
@@ -732,6 +769,11 @@
        if (!isSystemBorderEnabled()) {
            static constexpr const MARGINS margins = {1, 1, 1, 1};
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins);
        } else if (isWin10OrGreater() && !isWin11OrGreater()) {
            // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940
            // Must call DWM API to extend top frame to client area
            static constexpr const MARGINS margins = {0, 0, 1, 0};
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins);
        }
@@ -956,13 +998,11 @@
            }
            BOOL enable = attribute.toBool();
            if (isWin101903OrGreater()) {
                apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT);
            } else {
                apis.pAllowDarkModeForApp(enable);
            }
            for (const auto attr : {
                     _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
                     _DWMWA_USE_IMMERSIVE_DARK_MODE,
@@ -971,7 +1011,6 @@
            }
            apis.pFlushMenuThemes();
            return true;
        }
        return false;
src/core/contexts/win32windowcontext_p.h
@@ -38,6 +38,9 @@
        bool needBorderPainter() const;
        int borderThickness() const;
        void resume(const QByteArray &eventType, void *message,
                    QT_NATIVE_EVENT_RESULT_TYPE *result) override;
    protected:
        void winIdChanged() override;
        bool windowAttributeChanged(const QString &key, const QVariant &attribute,
src/core/kernel/nativeeventfilter.cpp
@@ -30,6 +30,10 @@
        return false;
    }
    void NativeEventDispatcher::resume(const QByteArray &eventType, void *message,
                                       QT_NATIVE_EVENT_RESULT_TYPE *result) {
    }
    void NativeEventDispatcher::installNativeEventFilter(NativeEventFilter *filter) {
        if (!filter || filter->m_dispatcher)
            return;
src/core/kernel/nativeeventfilter_p.h
@@ -25,6 +25,9 @@
        virtual bool dispatch(const QByteArray &eventType, void *message,
                              QT_NATIVE_EVENT_RESULT_TYPE *result);
        virtual void resume(const QByteArray &eventType, void *message,
                            QT_NATIVE_EVENT_RESULT_TYPE *result);
    public:
        void installNativeEventFilter(NativeEventFilter *filter);
        void removeNativeEventFilter(NativeEventFilter *filter);
src/core/shared/qwkwindowsextra_p.h
@@ -406,9 +406,8 @@
                                                         _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
                                                         &enabled, sizeof(enabled)))) {
            return enabled;
        } else {
            return false;
        }
        return false;
    }
    static inline QColor getAccentColor() {
src/widgets/widgetwindowagent_win.cpp
@@ -22,16 +22,21 @@
            updateGeometry();
        }
        inline bool isNormalWindow() const {
            return widget->windowState() &
                   (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen);
        }
        void updateGeometry() {
            if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
                widget->setContentsMargins({});
            } else {
            if (isNormalWindow()) {
                widget->setContentsMargins({
                    0,
                    ctx->property("borderThickness").toInt(),
                    0,
                    0,
                });
            } else {
                widget->setContentsMargins({});
            }
        }
@@ -62,6 +67,16 @@
                    break;
                }
                case WM_PAINT: {
                    // Let Qt paint first
                    m_dispatcher->resume(eventType, message, result);
                    // Upon receiving the WM_PAINT message, Qt will redraw the entire view, and we
                    // must wait for it to finish redrawing before drawing this top border area
                    ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook2, nullptr);
                    return true;
                }
                default:
                    break;
            }
@@ -71,11 +86,7 @@
        bool eventFilter(QObject *obj, QEvent *event) override {
            Q_UNUSED(obj)
            switch (event->type()) {
                case QEvent::Paint: {
                    if (widget->windowState() &
                        (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen))
                        break;
                case QEvent::UpdateRequest: {
                    // Friend class helping to call `event`
                    class HackedWidget : public QWidget {
                    public:
@@ -85,18 +96,10 @@
                    // Let the widget paint first
                    static_cast<HackedWidget *>(widget)->event(event);
                    // Draw border
                    auto paintEvent = static_cast<QPaintEvent *>(event);
                    auto rect = paintEvent->rect();
                    auto region = paintEvent->region();
                    QPainter painter(widget);
                    void *args[] = {
                        &painter,
                        &rect,
                        &region,
                    };
                    ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args);
                    // Due to the timer or user action, Qt will redraw some regions spontaneously,
                    // even if there is no WM_PAINT message, we must wait for it to finish redrawing
                    // and then update the upper border area
                    ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook2, nullptr);
                    return true;
                }