SineStriker
2025-02-11 3e942c3dc8955be577079fbc028ce216e1c594b2
Fix numerous bugs (#162)

* Fix win10 top border issue in opengl qml

* Fix #163

---------

Co-authored-by: Yuhang Zhao <zhaoyuhang@rankyee.com>
Co-authored-by: Zhao Yuhang <2546789017@qq.com>
26个文件已修改
2个文件已添加
416 ■■■■ 已修改文件
LICENSE 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/CMakeLists.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/dark-style.qss 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/light-style.qss 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.cpp 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/qml/CMakeLists.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/qml/QWKButton.qml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/qml/main.qml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/shared.qrc 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/pin-fill.svg 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/pin.svg 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbar.cpp 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbar.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbar_p.h 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/CMakeLists.txt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/nativeeventfilter_p.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/kernel/sharedeventfilter_p.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwindowkit_windows.cpp 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwindowkit_windows.h 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/shared/qwkwindowsextra_p.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/shared/windows10borderhandler_p.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickwindowagent.cpp 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickwindowagent_win.cpp 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent.cpp 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LICENSE
@@ -186,7 +186,7 @@
      same "printed page" as the copyright notice for easier
      identification within third-party archives.
   Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
   Copyright (C) 2023-2025 Stdware Collections (https://www.github.com/stdware)
   Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
   Licensed under the Apache License, Version 2.0 (the "License");
README.md
@@ -12,9 +12,7 @@
You can share your findings, thoughts and ideas on improving / implementing QWindowKit functionalities on more platforms and apps!
- Chat with us on [Discord](https://discord.gg/grrM4Tmesy)
  - Please inform us if your product uses QWK, we would like to show it on this README!
- 中文用户可加入 QQ 群 876419693
  - 如果您的产品使用了QWK,请告知我们。我们希望在这个自述文件上展示它!
## Supported Platforms
examples/mainwindow/CMakeLists.txt
@@ -11,6 +11,4 @@
set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED TRUE
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)
examples/mainwindow/dark-style.qss
@@ -32,9 +32,19 @@
    background-color: transparent;
}
QWK--WindowBar>QAbstractButton#pin-button {
    qproperty-iconNormal: url(":/window-bar/pin.svg");
    qproperty-iconChecked: url(":/window-bar/pin-fill.svg");
    qproperty-iconSize: 15px 15px;
}
QWK--WindowBar>QAbstractButton#pin-button:hover,
QWK--WindowBar>QAbstractButton#pin-button:pressed {
    background-color: rgba(255, 255, 255, 15%);
}
QWK--WindowBar>QAbstractButton#min-button {
    qproperty-iconNormal: url(":/window-bar/minimize.svg");
    qproperty-iconSize: 12px 12px;
}
QWK--WindowBar>QAbstractButton#min-button:hover,
examples/mainwindow/light-style.qss
@@ -1,13 +1,13 @@
/* Window bar */
QWK--WindowBar[bar-active=true] {
    /*background-color: #195ABE;*/
    background-color: transparent;
    background-color: #195ABE;
    /* background-color: transparent; */
}
QWK--WindowBar[bar-active=false] {
    /*background-color: #195ABE;*/
    background-color: transparent;
    background-color: #195ABE;
    /* background-color: transparent; */
}
@@ -32,9 +32,19 @@
    background-color: transparent;
}
QWK--WindowBar>QAbstractButton#pin-button {
    qproperty-iconNormal: url(":/window-bar/pin.svg");
    qproperty-iconChecked: url(":/window-bar/pin-fill.svg");
    qproperty-iconSize: 15px 15px;
}
QWK--WindowBar>QAbstractButton#pin-button:hover,
QWK--WindowBar>QAbstractButton#pin-button:pressed {
    background-color: rgba(0, 0, 0, 15%);
}
QWK--WindowBar>QAbstractButton#min-button {
    qproperty-iconNormal: url(":/window-bar/minimize.svg");
    qproperty-iconSize: 12px 12px;
}
QWK--WindowBar>QAbstractButton#min-button:hover,
examples/mainwindow/mainwindow.cpp
@@ -188,8 +188,10 @@
        winStyleGroup->addAction(acrylicAction);
        winStyleGroup->addAction(micaAction);
        winStyleGroup->addAction(micaAltAction);
        connect(winStyleGroup, &QActionGroup::triggered, this, [this, winStyleGroup](QAction *action) {
            // Unset all custom style attributes first, otherwise the style will not display correctly
        connect(winStyleGroup, &QActionGroup::triggered, this,
                [this, winStyleGroup](QAction *action) {
                    // Unset all custom style attributes first, otherwise the style will not display
                    // correctly
            for (const QAction* _act : winStyleGroup->actions()) {
                const QString data = _act->data().toString();
                if (data.isEmpty() || data == QStringLiteral("none")) {
@@ -283,6 +285,12 @@
    iconButton->setObjectName(QStringLiteral("icon-button"));
    iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    auto pinButton = new QWK::WindowButton();
    pinButton->setCheckable(true);
    pinButton->setObjectName(QStringLiteral("pin-button"));
    pinButton->setProperty("system-button", true);
    pinButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    auto minButton = new QWK::WindowButton();
    minButton->setObjectName(QStringLiteral("min-button"));
    minButton->setProperty("system-button", true);
@@ -303,6 +311,7 @@
    auto windowBar = new QWK::WindowBar();
#ifndef Q_OS_MAC
    windowBar->setIconButton(iconButton);
    windowBar->setPinButton(pinButton);
    windowBar->setMinButton(minButton);
    windowBar->setMaxButton(maxButton);
    windowBar->setCloseButton(closeButton);
@@ -313,6 +322,7 @@
    windowAgent->setTitleBar(windowBar);
#ifndef Q_OS_MAC
    windowAgent->setHitTestVisible(pinButton, true);
    windowAgent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton);
    windowAgent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton);
    windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton);
@@ -331,6 +341,14 @@
#ifndef Q_OS_MAC
    connect(windowBar, &QWK::WindowBar::pinRequested, this, [this, pinButton](bool pin){
        if (isHidden() || isMinimized() || isMaximized() || isFullScreen()) {
            return;
        }
        setWindowFlag(Qt::WindowStaysOnTopHint, pin);
        show();
        pinButton->setChecked(pin);
    });
    connect(windowBar, &QWK::WindowBar::minimizeRequested, this, &QWidget::showMinimized);
    connect(windowBar, &QWK::WindowBar::maximizeRequested, this, [this, maxButton](bool max) {
        if (max) {
examples/qml/CMakeLists.txt
@@ -11,6 +11,4 @@
set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED TRUE
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)
examples/qml/QWKButton.qml
@@ -1,5 +1,5 @@
import QtQuick 2.15
import QtQuick.Controls.Basic 2.15
import QtQuick.Controls 2.15
Button {
    id: root
examples/qml/main.qml
@@ -1,6 +1,6 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls.Basic 2.15
import QtQuick.Controls 2.15
import Qt.labs.platform 1.1
import QWindowKit 1.0
@@ -12,8 +12,8 @@
    title: qsTr("Hello, world!")
    Component.onCompleted: {
        windowAgent.setup(window)
        windowAgent.setWindowAttribute("dark-mode", true)
        window.visible = true
        delayInitTimer.start()
    }
    QtObject {
@@ -30,14 +30,6 @@
        running: true
        repeat: true
        onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss")
    }
    Timer {
        id: delayInitTimer
        interval: 100
        running: false
        repeat: false
        onTriggered: windowAgent.setWindowAttribute("dark-mode", true)
    }
    WindowAgent {
examples/shared/resources/shared.qrc
@@ -6,6 +6,8 @@
        <file>window-bar/minimize.svg</file>
        <file>window-bar/restore.svg</file>
        <file>window-bar/more-line.svg</file>
        <file>window-bar/pin.svg</file>
        <file>window-bar/pin-fill.svg</file>
        <file>app/example.png</file>
    </qresource>
</RCC>
examples/shared/resources/window-bar/pin-fill.svg
New file
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1735286082742" class="icon" viewBox="0 0 1024 1024" version="1.1"
    xmlns="http://www.w3.org/2000/svg" p-id="2501" width="512" height="512"
    xmlns:xlink="http://www.w3.org/1999/xlink">
    <path
        d="M648.728381 130.779429a73.142857 73.142857 0 0 1 22.674286 15.433142l191.561143 191.756191a73.142857 73.142857 0 0 1-22.137905 118.564571l-67.876572 30.061715-127.341714 127.488-10.093714 140.239238a73.142857 73.142857 0 0 1-124.684191 46.445714l-123.66019-123.782095-210.724572 211.699809-51.833904-51.614476 210.846476-211.821714-127.926857-128.024381a73.142857 73.142857 0 0 1 46.299428-124.635429l144.237715-10.776381 125.074285-125.220571 29.379048-67.779048a73.142857 73.142857 0 0 1 96.207238-38.034285z"
        p-id="2502" fill="white"></path>
</svg>
examples/shared/resources/window-bar/pin.svg
New file
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1735285955420" class="icon" viewBox="0 0 1024 1024" version="1.1"
    xmlns="http://www.w3.org/2000/svg" p-id="2053" width="512" height="512"
    xmlns:xlink="http://www.w3.org/1999/xlink">
    <path
        d="M648.728381 130.779429a73.142857 73.142857 0 0 1 22.674286 15.433142l191.561143 191.756191a73.142857 73.142857 0 0 1-22.137905 118.564571l-67.876572 30.061715-127.341714 127.488-10.093714 140.239238a73.142857 73.142857 0 0 1-124.684191 46.445714l-123.66019-123.782095-210.724572 211.699809-51.833904-51.614476 210.846476-211.821714-127.926857-128.024381a73.142857 73.142857 0 0 1 46.299428-124.635429l144.237715-10.776381 125.074285-125.220571 29.379048-67.779048a73.142857 73.142857 0 0 1 96.207238-38.034285z m-29.086476 67.120761l-34.913524 80.530286-154.087619 154.331429-171.398095 12.751238 303.323428 303.542857 12.044191-167.399619 156.233143-156.428191 80.384-35.59619-191.585524-191.73181z"
        p-id="2054" fill="white"></path>
</svg>
examples/shared/widgetframe/windowbar.cpp
@@ -79,14 +79,19 @@
        return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::IconButton));
    }
    QAbstractButton *WindowBar::pinButton() const {
        Q_D(const WindowBar);
        return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::PinButton));
    }
    QAbstractButton *WindowBar::minButton() const {
        Q_D(const WindowBar);
        return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::MinimumButton));
        return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::MinimizeButton));
    }
    QAbstractButton *WindowBar::maxButton() const {
        Q_D(const WindowBar);
        return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::MaximumButton));
        return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::MaximizeButton));
    }
    QAbstractButton *WindowBar::closeButton() const {
@@ -131,6 +136,17 @@
        btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
    }
    void WindowBar::setPinButton(QAbstractButton *btn) {
        Q_D(WindowBar);
        auto org = takePinButton();
        if (org)
            org->deleteLater();
        if (!btn)
            return;
        d->setWidgetAt(WindowBarPrivate::PinButton, btn);
        connect(btn, &QAbstractButton::clicked, this, &WindowBar::pinRequested);
    }
    void WindowBar::setMinButton(QAbstractButton *btn) {
        Q_D(WindowBar);
        auto org = takeMinButton();
@@ -138,7 +154,7 @@
            org->deleteLater();
        if (!btn)
            return;
        d->setWidgetAt(WindowBarPrivate::MinimumButton, btn);
        d->setWidgetAt(WindowBarPrivate::MinimizeButton, btn);
        connect(btn, &QAbstractButton::clicked, this, &WindowBar::minimizeRequested);
    }
@@ -149,7 +165,7 @@
            org->deleteLater();
        if (!btn)
            return;
        d->setWidgetAt(WindowBarPrivate::MaximumButton, btn);
        d->setWidgetAt(WindowBarPrivate::MaximizeButton, btn);
        connect(btn, &QAbstractButton::clicked, this, &WindowBar::maximizeRequested);
    }
@@ -179,9 +195,19 @@
        return static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::IconButton));
    }
    QAbstractButton *WindowBar::takePinButton() {
        Q_D(WindowBar);
        auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::PinButton));
        if (!btn) {
            return nullptr;
        }
        disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::pinRequested);
        return btn;
    }
    QAbstractButton *WindowBar::takeMinButton() {
        Q_D(WindowBar);
        auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::MinimumButton));
        auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::MinimizeButton));
        if (!btn) {
            return nullptr;
        }
@@ -191,7 +217,7 @@
    QAbstractButton *WindowBar::takeMaxButton() {
        Q_D(WindowBar);
        auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::MaximumButton));
        auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::MaximizeButton));
        if (!btn) {
            return nullptr;
        }
examples/shared/widgetframe/windowbar.h
@@ -25,6 +25,7 @@
        QMenuBar *menuBar() const;
        QLabel *titleLabel() const;
        QAbstractButton *iconButton() const;
        QAbstractButton *pinButton() const;
        QAbstractButton *minButton() const;
        QAbstractButton *maxButton() const;
        QAbstractButton *closeButton() const;
@@ -32,6 +33,7 @@
        void setMenuBar(QMenuBar *menuBar);
        void setTitleLabel(QLabel *label);
        void setIconButton(QAbstractButton *btn);
        void setPinButton(QAbstractButton *btn);
        void setMinButton(QAbstractButton *btn);
        void setMaxButton(QAbstractButton *btn);
        void setCloseButton(QAbstractButton *btn);
@@ -39,6 +41,7 @@
        QMenuBar *takeMenuBar();
        QLabel *takeTitleLabel();
        QAbstractButton *takeIconButton();
        QAbstractButton *takePinButton();
        QAbstractButton *takeMinButton();
        QAbstractButton *takeMaxButton();
        QAbstractButton *takeCloseButton();
@@ -53,6 +56,7 @@
        void setIconFollowWindow(bool value);
    Q_SIGNALS:
        void pinRequested(bool pin = false);
        void minimizeRequested();
        void maximizeRequested(bool max = false);
        void closeRequested();
examples/shared/widgetframe/windowbar_p.h
@@ -29,8 +29,9 @@
            IconButton,
            MenuWidget,
            TitleLabel,
            MinimumButton,
            MaximumButton,
            PinButton,
            MinimizeButton,
            MaximizeButton,
            CloseButton,
        };
src/CMakeLists.txt
@@ -1,7 +1,9 @@
qm_import(Preprocess)
string(TIMESTAMP _current_year "%Y")
set(QWINDOWKIT_PROJECT_DESCRIPTION "Cross-platform window customization framework")
set(QWINDOWKIT_PROJECT_COPYRIGHT "Copyright 2023 Stdware Collections")
set(QWINDOWKIT_PROJECT_COPYRIGHT "Copyright 2023-${_current_year} Stdware Collections")
set(QWINDOWKIT_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/../include)
set(QWINDOWKIT_BUILD_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/../etc/include)
src/core/contexts/abstractwindowcontext.cpp
@@ -240,13 +240,13 @@
            m_windowHandle->installEventFilter(this);
            // Refresh window attributes
            auto attributes = m_windowAttributes;
            m_windowAttributes.clear();
            for (auto it = attributes.begin(); it != attributes.end(); ++it) {
                if (!windowAttributeChanged(it.key(), it.value(), {})) {
            for (auto it = m_windowAttributesOrder.begin(); it != m_windowAttributesOrder.end();) {
                if (!windowAttributeChanged(it->first, it->second, {})) {
                    m_windowAttributes.remove(it->first);
                    it = m_windowAttributesOrder.erase(it);
                    continue;
                }
                m_windowAttributes.insert(it.key(), it.value());
                ++it;
            }
        }
@@ -258,7 +258,11 @@
    }
    QVariant AbstractWindowContext::windowAttribute(const QString &key) const {
        return m_windowAttributes.value(key);
        auto it = m_windowAttributes.find(key);
        if (it == m_windowAttributes.end()) {
            return {};
        }
        return it.value()->second;
    }
    bool AbstractWindowContext::setWindowAttribute(const QString &key, const QVariant &attribute) {
@@ -267,22 +271,27 @@
            if (!attribute.isValid()) {
                return true;
            }
            if (!m_windowHandle || !windowAttributeChanged(key, attribute, {})) {
            if (m_windowHandle && !windowAttributeChanged(key, attribute, {})) {
                return false;
            }
            m_windowAttributes.insert(key, attribute);
            m_windowAttributes.insert(key,
                                      m_windowAttributesOrder.insert(m_windowAttributesOrder.end(),
                                      std::make_pair(key, attribute)));
            return true;
        }
        if (it.value() == attribute)
            return true;
        if (!m_windowHandle || !windowAttributeChanged(key, attribute, it.value())) {
        auto &listIter = it.value();
        auto &oldAttr = listIter->second;
        if (m_windowHandle && !windowAttributeChanged(key, attribute, oldAttr)) {
            return false;
        }
        if (attribute.isValid()) {
            it.value() = attribute;
            oldAttr = attribute;
            m_windowAttributesOrder.splice(m_windowAttributesOrder.end(), m_windowAttributesOrder,
                                           listIter);
        } else {
            m_windowAttributesOrder.erase(listIter);
            m_windowAttributes.erase(it);
        }
        return true;
src/core/contexts/abstractwindowcontext_p.h
@@ -15,7 +15,9 @@
//
#include <array>
#include <list>
#include <memory>
#include <utility>
#include <QtCore/QSet>
#include <QtCore/QPointer>
@@ -88,8 +90,8 @@
            RaiseWindowHook,
            ShowSystemMenuHook,
            DefaultColorsHook,
            DrawWindows10BorderHook,     // Only works on Windows 10, emulated workaround
            DrawWindows10BorderHook2,    // Only works on Windows 10, native workaround
            DrawWindows10BorderHook_Emulated, // Only works on Windows 10, emulated workaround
            DrawWindows10BorderHook_Native,   // Only works on Windows 10, native workaround
            SystemButtonAreaChangedHook, // Only works on Mac
        };
        virtual void virtual_hook(int id, void *data);
@@ -122,7 +124,9 @@
        QObject *m_titleBar{};
        std::array<QObject *, WindowAgentBase::Close + 1> m_systemButtons{};
        QVariantHash m_windowAttributes;
        std::list<std::pair<QString, QVariant>> m_windowAttributesOrder;
        QHash<QString, decltype(m_windowAttributesOrder)::iterator> m_windowAttributes;
        std::unique_ptr<WinIdChangeEventFilter> m_winIdChangeEventFilter;
        
        void removeSystemButtonsAndHitTestItems();
src/core/contexts/win32windowcontext.cpp
@@ -86,7 +86,10 @@
    static void setInternalWindowFrameMargins(QWindow *window, const QMargins &margins) {
        const QVariant marginsVar = QVariant::fromValue(margins);
        // TODO: Add comments
        // We need to tell Qt we have set a custom margin, because we are hiding
        // the title bar by pretending the whole window is filled by client area,
        // this however confuses Qt's internal logic. We need to do the following
        // hack to let Qt consider the extra margin when changing window geometry.
        window->setProperty("_q_windowsCustomMargins", marginsVar);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        if (QPlatformWindow *platformWindow = window->handle()) {
@@ -197,8 +200,8 @@
        [[maybe_unused]] const auto &cleaner =
            qScopeGuard([windowThreadProcessId, currentThreadId]() {
                ::AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE); //
            }); // TODO: Remove it
                ::AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE);
            });
        ::BringWindowToTop(hwnd);
        // Activate the window too. This will force us to the virtual desktop this
@@ -219,12 +222,20 @@
            return true;
        }
        const auto windowStyles = ::GetWindowLongPtrW(hWnd, GWL_STYLE);
        const bool allowMaximize = windowStyles & WS_MAXIMIZEBOX;
        const bool allowMinimize = windowStyles & WS_MINIMIZEBOX;
        const bool maxOrFull = isMaximized(hWnd) || isFullScreen(hWnd);
        ::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | MFS_ENABLED));
        ::EnableMenuItem(hMenu, SC_MAXIMIZE,
                         (MF_BYCOMMAND | ((maxOrFull || fixedSize) ? MFS_DISABLED : MFS_ENABLED)));
        ::EnableMenuItem(hMenu, SC_RESTORE,
                         (MF_BYCOMMAND | ((maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
        ::EnableMenuItem(
            hMenu, SC_MAXIMIZE,
            (MF_BYCOMMAND |
             ((maxOrFull || fixedSize || !allowMaximize) ? MFS_DISABLED : MFS_ENABLED)));
        ::EnableMenuItem(
            hMenu, SC_RESTORE,
            (MF_BYCOMMAND |
             ((maxOrFull && !fixedSize && allowMaximize) ? MFS_ENABLED : MFS_DISABLED)));
        // The first menu item should be selected by default if the menu is brought
        // up by keyboard. I don't know how to pre-select a menu item but it seems
        // highlight can do the job. However, there's an annoying issue if we do
@@ -236,7 +247,8 @@
        // the menu look kind of weird. Currently I don't know how to fix this issue.
        ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE,
                         (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE)));
        ::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | MFS_ENABLED));
        ::EnableMenuItem(hMenu, SC_MINIMIZE,
                         (MF_BYCOMMAND | (allowMinimize ? MFS_ENABLED : MFS_DISABLED)));
        ::EnableMenuItem(hMenu, SC_SIZE,
                         (MF_BYCOMMAND | ((maxOrFull || fixedSize) ? MFS_DISABLED : MFS_ENABLED)));
        ::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (maxOrFull ? MFS_DISABLED : MFS_ENABLED)));
@@ -355,8 +367,8 @@
    static MSG createMessageBlock(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
        MSG msg;
        msg.hwnd = hWnd;       // re-create MSG structure
        msg.message = message; // time and pt fields ignored
        msg.hwnd = hWnd;
        msg.message = message;
        msg.wParam = wParam;
        msg.lParam = lParam;
@@ -366,6 +378,8 @@
        if (!isNonClientMessage(message)) {
            ::ScreenToClient(hWnd, &msg.pt);
        }
        msg.time = ::GetMessageTime();
        return msg;
    }
@@ -400,7 +414,7 @@
    }
    // Send to QAbstractEventDispatcher
    bool filterNativeEvent(MSG *msg, LRESULT *result) {
    static bool filterNativeEvent(MSG *msg, LRESULT *result) {
        auto dispatcher = QAbstractEventDispatcher::instance();
        QT_NATIVE_EVENT_RESULT_TYPE filterResult = *result;
        if (dispatcher && dispatcher->filterNativeEvent(nativeEventType(), msg, &filterResult)) {
@@ -411,7 +425,7 @@
    }
    // Send to QWindowSystemInterface
    bool filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result) {
    static bool filterNativeEvent(QWindow *window, MSG *msg, LRESULT *result) {
        QT_NATIVE_EVENT_RESULT_TYPE filterResult = *result;
        if (QWindowSystemInterface::handleNativeEvent(window, nativeEventType(), msg,
                                                      &filterResult)) {
@@ -624,6 +638,11 @@
        // Save window handle mapping
        g_wndProcHash->insert(hWnd, ctx);
        // Force a WM_NCCALCSIZE message manually to avoid the title bar become visible
        // while Qt is re-creating the window (such as setWindowFlag(s) calls). It has
        // been observed by our users.
        triggerFrameChange(hWnd);
    }
    static inline void removeManagedWindow(HWND hWnd) {
@@ -688,7 +707,7 @@
            }
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
            case DrawWindows10BorderHook: {
            case DrawWindows10BorderHook_Emulated: {
                if (!m_windowId)
                    return;
@@ -731,7 +750,7 @@
                return;
            }
            case DrawWindows10BorderHook2: {
            case DrawWindows10BorderHook_Native: {
                if (!m_windowId)
                    return;
@@ -897,7 +916,6 @@
        Q_UNUSED(oldAttribute)
        const auto hwnd = reinterpret_cast<HWND>(m_windowId);
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return false;
        }
@@ -933,8 +951,26 @@
        };
        const auto &restoreMargins = [this, &apis, hwnd]() {
            auto margins = qmargins2margins(
                m_windowAttributes.value(QStringLiteral("extra-margins")).value<QMargins>());
                windowAttribute(QStringLiteral("extra-margins")).value<QMargins>());
            apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
        };
        const auto &effectBugWorkaround = [this, hwnd]() {
            // We don't need the following *HACK* for QWidget windows.
            if (m_host->isWidgetType()) {
                return;
            }
            static QSet<WId> bugWindowSet{};
            if (bugWindowSet.contains(m_windowId)) {
                return;
            }
            bugWindowSet.insert(m_windowId);
            RECT rect{};
            ::GetWindowRect(hwnd, &rect);
            ::MoveWindow(hwnd, rect.left, rect.top, 1, 1, FALSE);
            ::MoveWindow(hwnd, rect.right - 1, rect.bottom - 1, 1, 1, FALSE);
            ::MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
                         FALSE);
        };
        if (key == QStringLiteral("extra-margins")) {
@@ -953,7 +989,8 @@
            } else {
                apis.pAllowDarkModeForApp(enable);
            }
            const auto attr = isWin1020H1OrGreater() ? _DWMWA_USE_IMMERSIVE_DARK_MODE : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
            const auto attr = isWin1020H1OrGreater() ? _DWMWA_USE_IMMERSIVE_DARK_MODE
                                                     : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
            apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable));
            apis.pFlushMenuThemes();
@@ -990,6 +1027,7 @@
                }
                restoreMargins();
            }
            effectBugWorkaround();
            return true;
        }
@@ -1010,6 +1048,7 @@
                                            sizeof(backdropType));
                restoreMargins();
            }
            effectBugWorkaround();
            return true;
        }
@@ -1055,6 +1094,7 @@
                restoreMargins();
            }
            effectBugWorkaround();
            return true;
        }
@@ -1094,6 +1134,7 @@
                    apis.pDwmEnableBlurBehindWindow(hwnd, &bb);
                }
            }
            effectBugWorkaround();
            return true;
        }
        return false;
@@ -1571,7 +1612,7 @@
                bool isInTitleBar = isInTitleBarDraggableArea(qtScenePos);
                WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown;
                bool isInCaptionButtons = isInSystemButtons(qtScenePos, &sysButtonType);
                bool dontOverrideCursor = false; // ### TODO
                static constexpr bool dontOverrideCursor = false; // ### TODO
                if (isInCaptionButtons) {
                    // Firstly, we set the hit test result to a default value to be able to detect
@@ -2019,6 +2060,7 @@
        // implement an elaborate client-area preservation technique, and
        // simply return 0, which means "preserve the entire old client area
        // and align it with the upper-left corner of our new client area".
        const auto clientRect = wParam ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]
                                       : reinterpret_cast<LPRECT>(lParam);
        [[maybe_unused]] const auto& flickerReducer = qScopeGuard([this]() {
@@ -2028,11 +2070,11 @@
            // D3D/VK paint immediately to let user see the latest result as soon
            // as possible.
            const auto& isTargetSurface = [](const QSurface::SurfaceType st){
                return st != QSurface::RasterSurface && st != QSurface::OpenGLSurface
                       && st != QSurface::RasterGLSurface && st != QSurface::OpenVGSurface;
                return st != QSurface::RasterSurface && st != QSurface::OpenGLSurface &&
                       st != QSurface::RasterGLSurface && st != QSurface::OpenVGSurface;
            };
            if (m_windowHandle && isTargetSurface(m_windowHandle->surfaceType())
                && isDwmCompositionEnabled() && DynamicApis::instance().pDwmFlush) {
            if (m_windowHandle && isTargetSurface(m_windowHandle->surfaceType()) &&
                isDwmCompositionEnabled() && DynamicApis::instance().pDwmFlush) {
                DynamicApis::instance().pDwmFlush();
            }
        });
src/core/kernel/nativeeventfilter_p.h
@@ -34,7 +34,7 @@
        void removeNativeEventFilter(NativeEventFilter *filter);
    protected:
        QVector<NativeEventFilter *> m_nativeEventFilters;
        QList<NativeEventFilter *> m_nativeEventFilters;
        friend class NativeEventFilter;
src/core/kernel/sharedeventfilter_p.h
@@ -33,7 +33,7 @@
        void removeSharedEventFilter(SharedEventFilter *filter);
    protected:
        QVector<SharedEventFilter *> m_sharedEventFilters;
        QList<SharedEventFilter *> m_sharedEventFilters;
        friend class SharedEventFilter;
src/core/qwindowkit_windows.cpp
@@ -71,21 +71,21 @@
        return result;
    }
    QPair<DWORD, bool> WindowsRegistryKey::dwordValue(QStringView subKey) const {
    std::pair<DWORD, bool> WindowsRegistryKey::dwordValue(QStringView subKey) const {
        if (!isValid())
            return qMakePair(0, false);
            return std::make_pair(0, false);
        DWORD type;
        auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
        if (::RegQueryValueExW(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS ||
            type != REG_DWORD) {
            return qMakePair(0, false);
            return std::make_pair(0, false);
        }
        DWORD value = 0;
        DWORD size = sizeof(value);
        const bool ok =
            ::RegQueryValueExW(m_key, subKeyC, nullptr, nullptr,
                               reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS;
        return qMakePair(value, ok);
        return std::make_pair(value, ok);
    }
#endif
}
src/core/qwindowkit_windows.h
@@ -145,7 +145,7 @@
        void close();
        QString stringValue(QStringView subKey) const;
        QPair<DWORD, bool> dwordValue(QStringView subKey) const;
        std::pair<DWORD, bool> dwordValue(QStringView subKey) const;
    private:
        HKEY m_key;
@@ -166,10 +166,10 @@
            : QWinRegistryKey(parentHandle, subKey, permissions, access) {
        }
        inline QPair<DWORD, bool> dwordValue(QStringView subKey) const;
        inline std::pair<DWORD, bool> dwordValue(QStringView subKey) const;
    };
    inline QPair<DWORD, bool> WindowsRegistryKey::dwordValue(QStringView subKey) const {
    inline std::pair<DWORD, bool> WindowsRegistryKey::dwordValue(QStringView subKey) const {
        const auto val = value<DWORD>(subKey);
        if (!val) {
            return {0, false};
src/core/shared/qwkwindowsextra_p.h
@@ -21,7 +21,6 @@
#include <QWKCore/qwindowkit_windows.h>
#include <QtCore/QtMath>
#include <QtCore/QPair>
#include <QtGui/QGuiApplication>
#include <QtGui/QStyleHints>
#include <QtGui/QPalette>
src/core/shared/windows10borderhandler_p.h
@@ -46,7 +46,7 @@
        }
        inline void drawBorder() {
            ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook2, nullptr);
            ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Native, nullptr);
        }
        inline int borderThickness() const {
@@ -94,6 +94,20 @@
                    break;
                }
                case WM_THEMECHANGED:
                case WM_SYSCOLORCHANGE:
                case WM_DWMCOLORIZATIONCOLORCHANGED: {
                    // If we do not refresh this property, the native border will turn white
                    // permanently (like the dark mode is turned off) after the user changes
                    // the accent color in system personalization settings.
                    // So we need this ugly hack to re-apply dark mode to get rid of this
                    // strange Windows bug.
                    if (ctx->windowAttribute(QStringLiteral("dark-mode")).toBool()) {
                        ctx->setWindowAttribute(QStringLiteral("dark-mode"), true);
                    }
                    break;
                }
                default:
                    break;
            }
src/quick/quickwindowagent.cpp
@@ -44,17 +44,14 @@
            return false;
        }
        // Make sure the native window handle is actually created before we apply
        // various hooks. But we don't need the actual window handle so just ignore it.
        std::ignore = window->winId();
        d->setup(window, new QuickItemDelegate());
        d->hostWindow = window;
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
        d->setupWindows10BorderWorkaround();
#endif
#ifdef Q_OS_WINDOWS
        if (!windowAttribute(QStringLiteral("windows-system-border-enabled")).toBool()) {
            window->setFlag(Qt::FramelessWindowHint);
        }
#endif
        return true;
    }
src/quick/quickwindowagent_win.cpp
@@ -12,6 +12,13 @@
namespace QWK {
    static inline bool isWindows1022H2OrGreater() {
        RTL_OSVERSIONINFOW rovi = Private::GetRealOSVersion();
        return (rovi.dwMajorVersion > 10) ||
               (rovi.dwMajorVersion == 10 &&
                (rovi.dwMinorVersion > 0 || rovi.dwBuildNumber >= 19045));
    }
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
    class BorderItem : public QQuickPaintedItem, public Windows10BorderHandler {
@@ -19,6 +26,7 @@
        explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context);
        ~BorderItem() override;
        bool shouldEnableEmulatedPainter() const;
        void updateGeometry() override;
    public:
@@ -27,14 +35,34 @@
    protected:
        bool sharedEventFilter(QObject *obj, QEvent *event) override;
#  if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        volatile bool needPaint = false;
        bool nativeEventFilter(const QByteArray &eventType, void *message,
                               QT_NATIVE_EVENT_RESULT_TYPE *result) override;
    private:
        volatile bool needPaint = false;
        void _q_afterSynchronizing();
#  endif
        void _q_windowActivityChanged();
    };
    bool BorderItem::shouldEnableEmulatedPainter() const {
#  if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        auto api = window()->rendererInterface()->graphicsApi();
        switch (api) {
            case QSGRendererInterface::OpenGL:
                // FIXME: experimental, try to find the exact fixed version.
                return !isWindows1022H2OrGreater();
            case QSGRendererInterface::Direct3D11:
#    if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
            case QSGRendererInterface::Direct3D12:
#    endif
                return false;
            default:
                break;
        }
#  endif
        return true;
    }
    BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context)
        : QQuickPaintedItem(parent), Windows10BorderHandler(context) {
@@ -56,6 +84,8 @@
        connect(window(), &QQuickWindow::afterSynchronizing, this,
                &BorderItem::_q_afterSynchronizing, Qt::DirectConnection);
#  endif
        connect(window(), &QQuickWindow::activeChanged, this,
                &BorderItem::_q_windowActivityChanged);
        // First update
        if (context->windowId()) {
@@ -73,16 +103,7 @@
    void BorderItem::paint(QPainter *painter) {
        Q_UNUSED(painter)
#  if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        if (auto api = window()->rendererInterface()->graphicsApi();
            !(api == QSGRendererInterface::Direct3D11
#    if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
              || api == QSGRendererInterface::Direct3D12
#    endif
              )) {
#  endif
        if (shouldEnableEmulatedPainter()) {
            QRect rect(QPoint(0, 0), size().toSize());
            QRegion region(rect);
            void *args[] = {
@@ -90,12 +111,10 @@
                &rect,
                &region,
            };
            ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args);
#  if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
            ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Emulated, args);
        } else {
            needPaint = true;
        }
#  endif
    }
    void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) {
@@ -124,14 +143,40 @@
        return Windows10BorderHandler::sharedEventFilter(obj, event);
    }
#  if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    bool BorderItem::nativeEventFilter(const QByteArray &eventType, void *message,
                                       QT_NATIVE_EVENT_RESULT_TYPE *result) {
        const auto msg = static_cast<const MSG *>(message);
        switch (msg->message) {
            case WM_THEMECHANGED:
            case WM_SYSCOLORCHANGE:
            case WM_DWMCOLORIZATIONCOLORCHANGED: {
                update();
                break;
            }
            case WM_SETTINGCHANGE: {
                if (isImmersiveColorSetChange(msg->wParam, msg->lParam)) {
                    update();
                }
                break;
            }
            default:
                break;
        }
        return Windows10BorderHandler::nativeEventFilter(eventType, message, result);
    }
    void BorderItem::_q_afterSynchronizing() {
        if (needPaint) {
            needPaint = false;
            drawBorder();
        }
    }
#  endif
    void BorderItem::_q_windowActivityChanged() {
        update();
    }
    void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() {
        // Install painting hook
src/widgets/widgetwindowagent.cpp
@@ -55,20 +55,19 @@
            return false;
        }
        // Qt will create invisible native window container for native QWidget
        // without this attribute, and this behavior will break QWK functionality.
        // So far enabling this attribute is a must for QWK users.
        w->setAttribute(Qt::WA_DontCreateNativeAncestors);
        w->setAttribute(Qt::WA_NativeWindow); // Create new window id
        // Make sure the native window handle is actually created before we apply
        // various hooks.
        w->setAttribute(Qt::WA_NativeWindow);
        d->setup(w, new WidgetItemDelegate());
        d->hostWidget = w;
#if defined(Q_OS_WINDOWS) && QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS)
        d->setupWindows10BorderWorkaround();
#endif
#ifdef Q_OS_WINDOWS
        if (!windowAttribute(QStringLiteral("windows-system-border-enabled")).toBool()) {
            w->setWindowFlag(Qt::FramelessWindowHint);
        }
#endif
        return true;
    }