From 9e96ea43436d5ed200c57d6b229cddf9bdf9e55a Mon Sep 17 00:00:00 2001 From: Yuhang Zhao Date: Mon, 10 Feb 2025 18:06:41 +0800 Subject: [PATCH 01/26] make demo a GUI application in CMake --- examples/mainwindow/CMakeLists.txt | 9 ++++++++- examples/qml/CMakeLists.txt | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index bd16470..6196bec 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -6,4 +6,11 @@ qwk_add_example(${PROJECT_NAME} SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc QT_LINKS Core Gui Widgets # MultimediaWidgets LINKS QWKWidgets WidgetFrame -) \ No newline at end of file +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED TRUE + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) diff --git a/examples/qml/CMakeLists.txt b/examples/qml/CMakeLists.txt index 9b834fa..fe1cbbf 100644 --- a/examples/qml/CMakeLists.txt +++ b/examples/qml/CMakeLists.txt @@ -11,4 +11,6 @@ qwk_add_example(${PROJECT_NAME} set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE ) From 3e942c3dc8955be577079fbc028ce216e1c594b2 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Tue, 11 Feb 2025 19:07:53 +0800 Subject: [PATCH 02/26] Fix numerous bugs (#162) * Fix win10 top border issue in opengl qml * Fix #163 --------- Co-authored-by: Yuhang Zhao Co-authored-by: Zhao Yuhang <2546789017@qq.com> --- LICENSE | 2 +- README.md | 2 - examples/mainwindow/CMakeLists.txt | 4 +- examples/mainwindow/dark-style.qss | 12 ++- examples/mainwindow/light-style.qss | 20 +++-- examples/mainwindow/mainwindow.cpp | 54 +++++++---- examples/qml/CMakeLists.txt | 4 +- examples/qml/QWKButton.qml | 2 +- examples/qml/main.qml | 12 +-- examples/shared/resources/shared.qrc | 2 + .../shared/resources/window-bar/pin-fill.svg | 9 ++ examples/shared/resources/window-bar/pin.svg | 9 ++ examples/shared/widgetframe/windowbar.cpp | 38 ++++++-- examples/shared/widgetframe/windowbar.h | 4 + examples/shared/widgetframe/windowbar_p.h | 5 +- src/CMakeLists.txt | 4 +- src/core/contexts/abstractwindowcontext.cpp | 33 ++++--- src/core/contexts/abstractwindowcontext_p.h | 14 +-- src/core/contexts/win32windowcontext.cpp | 90 ++++++++++++++----- src/core/kernel/nativeeventfilter_p.h | 2 +- src/core/kernel/sharedeventfilter_p.h | 2 +- src/core/qwindowkit_windows.cpp | 8 +- src/core/qwindowkit_windows.h | 6 +- src/core/shared/qwkwindowsextra_p.h | 1 - src/core/shared/windows10borderhandler_p.h | 16 +++- src/quick/quickwindowagent.cpp | 9 +- src/quick/quickwindowagent_win.cpp | 81 +++++++++++++---- src/widgets/widgetwindowagent.cpp | 13 ++- 28 files changed, 322 insertions(+), 136 deletions(-) create mode 100644 examples/shared/resources/window-bar/pin-fill.svg create mode 100644 examples/shared/resources/window-bar/pin.svg diff --git a/LICENSE b/LICENSE index 36395aa..d6629a9 100644 --- a/LICENSE +++ b/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"); diff --git a/README.md b/README.md index c2e1f9d..6c51124 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,7 @@ Feature requests are welcome. 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 diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index 6196bec..24d2eab 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -11,6 +11,4 @@ qwk_add_example(${PROJECT_NAME} set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE -) +) \ No newline at end of file diff --git a/examples/mainwindow/dark-style.qss b/examples/mainwindow/dark-style.qss index 09f32ab..33ac251 100644 --- a/examples/mainwindow/dark-style.qss +++ b/examples/mainwindow/dark-style.qss @@ -32,9 +32,19 @@ QWK--WindowBar>QAbstractButton[system-button=true] { 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, diff --git a/examples/mainwindow/light-style.qss b/examples/mainwindow/light-style.qss index 9180001..1f7b4c9 100644 --- a/examples/mainwindow/light-style.qss +++ b/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 @@ QWK--WindowBar>QAbstractButton[system-button=true] { 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, diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 7492b43..a8d2c61 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -188,24 +188,26 @@ void MainWindow::installWindowAgent() { 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 - for (const QAction* _act : winStyleGroup->actions()) { - const QString data = _act->data().toString(); - if (data.isEmpty() || data == QStringLiteral("none")) { - continue; - } - windowAgent->setWindowAttribute(data, false); - } - const QString data = action->data().toString(); - if (data == QStringLiteral("none")) { - setProperty("custom-style", false); - } else if (!data.isEmpty()) { - windowAgent->setWindowAttribute(data, true); - setProperty("custom-style", true); - } - style()->polish(this); - }); + 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")) { + continue; + } + windowAgent->setWindowAttribute(data, false); + } + const QString data = action->data().toString(); + if (data == QStringLiteral("none")) { + setProperty("custom-style", false); + } else if (!data.isEmpty()) { + windowAgent->setWindowAttribute(data, true); + setProperty("custom-style", true); + } + style()->polish(this); + }); #elif defined(Q_OS_MAC) auto darkBlurAction = new QAction(tr("Dark blur"), menuBar); @@ -283,6 +285,12 @@ void MainWindow::installWindowAgent() { 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 @@ void MainWindow::installWindowAgent() { 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 @@ void MainWindow::installWindowAgent() { 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 @@ void MainWindow::installWindowAgent() { #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) { diff --git a/examples/qml/CMakeLists.txt b/examples/qml/CMakeLists.txt index fe1cbbf..1755395 100644 --- a/examples/qml/CMakeLists.txt +++ b/examples/qml/CMakeLists.txt @@ -11,6 +11,4 @@ qwk_add_example(${PROJECT_NAME} set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE -) +) \ No newline at end of file diff --git a/examples/qml/QWKButton.qml b/examples/qml/QWKButton.qml index 8bf58a2..e781d69 100644 --- a/examples/qml/QWKButton.qml +++ b/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 diff --git a/examples/qml/main.qml b/examples/qml/main.qml index edb563e..dd4f284 100644 --- a/examples/qml/main.qml +++ b/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 @@ Window { title: qsTr("Hello, world!") Component.onCompleted: { windowAgent.setup(window) + windowAgent.setWindowAttribute("dark-mode", true) window.visible = true - delayInitTimer.start() } QtObject { @@ -32,14 +32,6 @@ Window { 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 { id: windowAgent } diff --git a/examples/shared/resources/shared.qrc b/examples/shared/resources/shared.qrc index 5aec715..46efc75 100644 --- a/examples/shared/resources/shared.qrc +++ b/examples/shared/resources/shared.qrc @@ -6,6 +6,8 @@ window-bar/minimize.svg window-bar/restore.svg window-bar/more-line.svg + window-bar/pin.svg + window-bar/pin-fill.svg app/example.png diff --git a/examples/shared/resources/window-bar/pin-fill.svg b/examples/shared/resources/window-bar/pin-fill.svg new file mode 100644 index 0000000..6be8c4d --- /dev/null +++ b/examples/shared/resources/window-bar/pin-fill.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/examples/shared/resources/window-bar/pin.svg b/examples/shared/resources/window-bar/pin.svg new file mode 100644 index 0000000..3c5d0bf --- /dev/null +++ b/examples/shared/resources/window-bar/pin.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/examples/shared/widgetframe/windowbar.cpp b/examples/shared/widgetframe/windowbar.cpp index 362383f..38a398e 100644 --- a/examples/shared/widgetframe/windowbar.cpp +++ b/examples/shared/widgetframe/windowbar.cpp @@ -79,14 +79,19 @@ namespace QWK { return static_cast(d->widgetAt(WindowBarPrivate::IconButton)); } + QAbstractButton *WindowBar::pinButton() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::PinButton)); + } + QAbstractButton *WindowBar::minButton() const { Q_D(const WindowBar); - return static_cast(d->widgetAt(WindowBarPrivate::MinimumButton)); + return static_cast(d->widgetAt(WindowBarPrivate::MinimizeButton)); } QAbstractButton *WindowBar::maxButton() const { Q_D(const WindowBar); - return static_cast(d->widgetAt(WindowBarPrivate::MaximumButton)); + return static_cast(d->widgetAt(WindowBarPrivate::MaximizeButton)); } QAbstractButton *WindowBar::closeButton() const { @@ -131,6 +136,17 @@ namespace QWK { 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 @@ namespace QWK { 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 @@ namespace QWK { 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 @@ namespace QWK { return static_cast(d->takeWidgetAt(WindowBarPrivate::IconButton)); } + QAbstractButton *WindowBar::takePinButton() { + Q_D(WindowBar); + auto btn = static_cast(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(d->takeWidgetAt(WindowBarPrivate::MinimumButton)); + auto btn = static_cast(d->takeWidgetAt(WindowBarPrivate::MinimizeButton)); if (!btn) { return nullptr; } @@ -191,7 +217,7 @@ namespace QWK { QAbstractButton *WindowBar::takeMaxButton() { Q_D(WindowBar); - auto btn = static_cast(d->takeWidgetAt(WindowBarPrivate::MaximumButton)); + auto btn = static_cast(d->takeWidgetAt(WindowBarPrivate::MaximizeButton)); if (!btn) { return nullptr; } diff --git a/examples/shared/widgetframe/windowbar.h b/examples/shared/widgetframe/windowbar.h index 2e802f2..e9cf04c 100644 --- a/examples/shared/widgetframe/windowbar.h +++ b/examples/shared/widgetframe/windowbar.h @@ -25,6 +25,7 @@ namespace QWK { 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 @@ namespace QWK { 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 @@ namespace QWK { QMenuBar *takeMenuBar(); QLabel *takeTitleLabel(); QAbstractButton *takeIconButton(); + QAbstractButton *takePinButton(); QAbstractButton *takeMinButton(); QAbstractButton *takeMaxButton(); QAbstractButton *takeCloseButton(); @@ -53,6 +56,7 @@ namespace QWK { void setIconFollowWindow(bool value); Q_SIGNALS: + void pinRequested(bool pin = false); void minimizeRequested(); void maximizeRequested(bool max = false); void closeRequested(); diff --git a/examples/shared/widgetframe/windowbar_p.h b/examples/shared/widgetframe/windowbar_p.h index 36ceb17..35422ae 100644 --- a/examples/shared/widgetframe/windowbar_p.h +++ b/examples/shared/widgetframe/windowbar_p.h @@ -29,8 +29,9 @@ namespace QWK { IconButton, MenuWidget, TitleLabel, - MinimumButton, - MaximumButton, + PinButton, + MinimizeButton, + MaximizeButton, CloseButton, }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6279fb5..510c161 100644 --- a/src/CMakeLists.txt +++ b/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) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index f838064..f3a9c7f 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -240,13 +240,13 @@ namespace QWK { 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 @@ namespace QWK { } 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 @@ namespace QWK { 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; diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h index 062494d..e978cb4 100644 --- a/src/core/contexts/abstractwindowcontext_p.h +++ b/src/core/contexts/abstractwindowcontext_p.h @@ -15,7 +15,9 @@ // #include +#include #include +#include #include #include @@ -88,9 +90,9 @@ namespace QWK { RaiseWindowHook, ShowSystemMenuHook, DefaultColorsHook, - DrawWindows10BorderHook, // Only works on Windows 10, emulated workaround - DrawWindows10BorderHook2, // Only works on Windows 10, native workaround - SystemButtonAreaChangedHook, // Only works on Mac + 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,9 +124,11 @@ namespace QWK { QObject *m_titleBar{}; std::array m_systemButtons{}; - QVariantHash m_windowAttributes; + std::list> m_windowAttributesOrder; + QHash m_windowAttributes; + std::unique_ptr m_winIdChangeEventFilter; - + void removeSystemButtonsAndHitTestItems(); private: diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 775f2ca..3360b9d 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -86,7 +86,10 @@ namespace QWK { 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 @@ namespace QWK { [[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 @@ namespace QWK { 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 @@ namespace QWK { // 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 @@ namespace QWK { 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 @@ namespace QWK { if (!isNonClientMessage(message)) { ::ScreenToClient(hWnd, &msg.pt); } + + msg.time = ::GetMessageTime(); return msg; } @@ -400,7 +414,7 @@ namespace QWK { } // 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 @@ namespace QWK { } // 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 @@ namespace QWK { // 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 @@ namespace QWK { } #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) - case DrawWindows10BorderHook: { + case DrawWindows10BorderHook_Emulated: { if (!m_windowId) return; @@ -731,7 +750,7 @@ namespace QWK { return; } - case DrawWindows10BorderHook2: { + case DrawWindows10BorderHook_Native: { if (!m_windowId) return; @@ -897,7 +916,6 @@ namespace QWK { Q_UNUSED(oldAttribute) const auto hwnd = reinterpret_cast(m_windowId); - Q_ASSERT(hwnd); if (!hwnd) { return false; } @@ -933,10 +951,28 @@ namespace QWK { }; const auto &restoreMargins = [this, &apis, hwnd]() { auto margins = qmargins2margins( - m_windowAttributes.value(QStringLiteral("extra-margins")).value()); + windowAttribute(QStringLiteral("extra-margins")).value()); 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 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")) { auto margins = qmargins2margins(attribute.value()); return SUCCEEDED(apis.pDwmExtendFrameIntoClientArea(hwnd, &margins)); @@ -953,7 +989,8 @@ namespace QWK { } 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 @@ namespace QWK { } restoreMargins(); } + effectBugWorkaround(); return true; } @@ -1010,6 +1048,7 @@ namespace QWK { sizeof(backdropType)); restoreMargins(); } + effectBugWorkaround(); return true; } @@ -1055,6 +1094,7 @@ namespace QWK { restoreMargins(); } + effectBugWorkaround(); return true; } @@ -1094,6 +1134,7 @@ namespace QWK { apis.pDwmEnableBlurBehindWindow(hwnd, &bb); } } + effectBugWorkaround(); return true; } return false; @@ -1571,7 +1612,7 @@ namespace QWK { 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,20 +2060,21 @@ namespace QWK { // 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(lParam))->rgrc[0] : reinterpret_cast(lParam); - [[maybe_unused]] const auto& flickerReducer = qScopeGuard([this]() { + [[maybe_unused]] const auto &flickerReducer = qScopeGuard([this]() { // When we receive this message, it means the window size has changed // already, and it seems this message always come before any client // area size notifications (eg. WM_WINDOWPOSCHANGED and WM_SIZE). Let // 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; + const auto &isTargetSurface = [](const QSurface::SurfaceType st) { + 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(); } }); diff --git a/src/core/kernel/nativeeventfilter_p.h b/src/core/kernel/nativeeventfilter_p.h index cef78b7..d7d3e3f 100644 --- a/src/core/kernel/nativeeventfilter_p.h +++ b/src/core/kernel/nativeeventfilter_p.h @@ -34,7 +34,7 @@ namespace QWK { void removeNativeEventFilter(NativeEventFilter *filter); protected: - QVector m_nativeEventFilters; + QList m_nativeEventFilters; friend class NativeEventFilter; diff --git a/src/core/kernel/sharedeventfilter_p.h b/src/core/kernel/sharedeventfilter_p.h index eeaf38f..5c1935b 100644 --- a/src/core/kernel/sharedeventfilter_p.h +++ b/src/core/kernel/sharedeventfilter_p.h @@ -33,7 +33,7 @@ namespace QWK { void removeSharedEventFilter(SharedEventFilter *filter); protected: - QVector m_sharedEventFilters; + QList m_sharedEventFilters; friend class SharedEventFilter; diff --git a/src/core/qwindowkit_windows.cpp b/src/core/qwindowkit_windows.cpp index 58c3d0f..640528c 100644 --- a/src/core/qwindowkit_windows.cpp +++ b/src/core/qwindowkit_windows.cpp @@ -71,21 +71,21 @@ namespace QWK { return result; } - QPair WindowsRegistryKey::dwordValue(QStringView subKey) const { + std::pair WindowsRegistryKey::dwordValue(QStringView subKey) const { if (!isValid()) - return qMakePair(0, false); + return std::make_pair(0, false); DWORD type; auto subKeyC = reinterpret_cast(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(&value), &size) == ERROR_SUCCESS; - return qMakePair(value, ok); + return std::make_pair(value, ok); } #endif } \ No newline at end of file diff --git a/src/core/qwindowkit_windows.h b/src/core/qwindowkit_windows.h index d6c0afe..7e9b8a7 100644 --- a/src/core/qwindowkit_windows.h +++ b/src/core/qwindowkit_windows.h @@ -145,7 +145,7 @@ namespace QWK { void close(); QString stringValue(QStringView subKey) const; - QPair dwordValue(QStringView subKey) const; + std::pair dwordValue(QStringView subKey) const; private: HKEY m_key; @@ -166,10 +166,10 @@ namespace QWK { : QWinRegistryKey(parentHandle, subKey, permissions, access) { } - inline QPair dwordValue(QStringView subKey) const; + inline std::pair dwordValue(QStringView subKey) const; }; - inline QPair WindowsRegistryKey::dwordValue(QStringView subKey) const { + inline std::pair WindowsRegistryKey::dwordValue(QStringView subKey) const { const auto val = value(subKey); if (!val) { return {0, false}; diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h index 65c1faa..8fdc86e 100644 --- a/src/core/shared/qwkwindowsextra_p.h +++ b/src/core/shared/qwkwindowsextra_p.h @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/src/core/shared/windows10borderhandler_p.h b/src/core/shared/windows10borderhandler_p.h index cf530e8..8099fe0 100644 --- a/src/core/shared/windows10borderhandler_p.h +++ b/src/core/shared/windows10borderhandler_p.h @@ -46,7 +46,7 @@ namespace QWK { } inline void drawBorder() { - ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook2, nullptr); + ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Native, nullptr); } inline int borderThickness() const { @@ -94,6 +94,20 @@ namespace QWK { 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; } diff --git a/src/quick/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp index 9270311..aaa321c 100644 --- a/src/quick/quickwindowagent.cpp +++ b/src/quick/quickwindowagent.cpp @@ -44,18 +44,15 @@ namespace QWK { 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; } diff --git a/src/quick/quickwindowagent_win.cpp b/src/quick/quickwindowagent_win.cpp index a47f4c0..acfdd42 100644 --- a/src/quick/quickwindowagent_win.cpp +++ b/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 @@ namespace QWK { explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context); ~BorderItem() override; + bool shouldEnableEmulatedPainter() const; void updateGeometry() override; public: @@ -27,15 +35,35 @@ namespace QWK { protected: bool sharedEventFilter(QObject *obj, QEvent *event) override; + bool nativeEventFilter(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result) override; -# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + private: volatile bool needPaint = false; - private: 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) { setAntialiasing(true); // We need anti-aliasing to give us better result. @@ -56,6 +84,8 @@ namespace QWK { 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 @@ namespace QWK { 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 @@ namespace QWK { &rect, ®ion, }; - 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 @@ namespace QWK { 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(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 diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp index ea6b22c..e2a36d1 100644 --- a/src/widgets/widgetwindowagent.cpp +++ b/src/widgets/widgetwindowagent.cpp @@ -55,8 +55,13 @@ namespace QWK { 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; @@ -64,12 +69,6 @@ namespace QWK { #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; } From c013e1b4a2e855ecae467cd1dc29adc2cbc53173 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Tue, 11 Feb 2025 19:09:16 +0800 Subject: [PATCH 03/26] never forget about version update! --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f5d54e..5b2b5e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.19) -project(QWindowKit VERSION 1.3.2.0 LANGUAGES CXX) +project(QWindowKit VERSION 1.4.0.0 LANGUAGES CXX) # ---------------------------------- # Build Options From e9f790d3533ebaee1f782e91b6e13b2c6375c86a Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Tue, 11 Feb 2025 23:06:01 +0800 Subject: [PATCH 04/26] AWC: fix window attribute handler --- src/core/contexts/abstractwindowcontext.cpp | 40 +++++++++++---------- src/core/contexts/cocoawindowcontext.mm | 3 +- src/core/contexts/win32windowcontext.cpp | 4 +-- src/quick/quickwindowagent.cpp | 3 -- src/widgets/widgetwindowagent.cpp | 2 +- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index f3a9c7f..22e3c64 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -230,28 +230,30 @@ namespace QWK { if (m_windowHandle) { removeEventFilter(m_windowHandle); } + + auto oldWindowHandle = m_windowHandle.data(); m_windowHandle = m_delegate->hostWindow(m_host); if (oldWinId != m_windowId) { winIdChanged(m_windowId, oldWinId); - } - if (m_windowHandle) { - m_windowHandle->installEventFilter(this); - - // Refresh window attributes - 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; + if (m_windowId) { + // Installing twice has no side-effect. + m_windowHandle->installEventFilter(this); + + // Refresh window attributes + 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; + } + ++it; } - ++it; } - } - // Send to shared dispatchers - if (oldWinId != m_windowId) { + // Send to shared dispatchers QEvent e(QEvent::WinIdChange); sharedDispatch(m_host, &e); } @@ -271,18 +273,18 @@ namespace QWK { if (!attribute.isValid()) { return true; } - if (m_windowHandle && !windowAttributeChanged(key, attribute, {})) { + if (m_windowId && !windowAttributeChanged(key, attribute, {})) { return false; } - m_windowAttributes.insert(key, - m_windowAttributesOrder.insert(m_windowAttributesOrder.end(), - std::make_pair(key, attribute))); + m_windowAttributes.insert( + key, m_windowAttributesOrder.insert(m_windowAttributesOrder.end(), + std::make_pair(key, attribute))); return true; } auto &listIter = it.value(); auto &oldAttr = listIter->second; - if (m_windowHandle && !windowAttributeChanged(key, attribute, oldAttr)) { + if (m_windowId && !windowAttributeChanged(key, attribute, oldAttr)) { return false; } diff --git a/src/core/contexts/cocoawindowcontext.mm b/src/core/contexts/cocoawindowcontext.mm index 9b9e009..c26152f 100644 --- a/src/core/contexts/cocoawindowcontext.mm +++ b/src/core/contexts/cocoawindowcontext.mm @@ -708,6 +708,8 @@ static inline void releaseWindowProxy(const WId windowId) { const QVariant &oldAttribute) { Q_UNUSED(oldAttribute) + Q_ASSERT(m_windowId); + if (key == QStringLiteral("no-system-buttons")) { if (attribute.type() != QVariant::Bool) return false; @@ -740,7 +742,6 @@ static inline void releaseWindowProxy(const WId windowId) { } return ensureWindowProxy(m_windowId)->setBlurEffect(mode); } - return false; } diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 3360b9d..00ff1b4 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -916,9 +916,7 @@ namespace QWK { Q_UNUSED(oldAttribute) const auto hwnd = reinterpret_cast(m_windowId); - if (!hwnd) { - return false; - } + Q_ASSERT(hwnd); const DynamicApis &apis = DynamicApis::instance(); const auto &extendMargins = [this, &apis, hwnd]() { diff --git a/src/quick/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp index aaa321c..5225a09 100644 --- a/src/quick/quickwindowagent.cpp +++ b/src/quick/quickwindowagent.cpp @@ -44,9 +44,6 @@ namespace QWK { 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; diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp index e2a36d1..ca5afa4 100644 --- a/src/widgets/widgetwindowagent.cpp +++ b/src/widgets/widgetwindowagent.cpp @@ -61,7 +61,7 @@ namespace QWK { w->setAttribute(Qt::WA_DontCreateNativeAncestors); // Make sure the native window handle is actually created before we apply // various hooks. - w->setAttribute(Qt::WA_NativeWindow); + w->setAttribute(Qt::WA_NativeWindow); // ### FIXME: Remove d->setup(w, new WidgetItemDelegate()); d->hostWidget = w; From 5706ba3d99f6a147499d6a122959ed865e447470 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Wed, 12 Feb 2025 21:23:14 +0800 Subject: [PATCH 05/26] Update --- src/core/contexts/abstractwindowcontext.cpp | 8 +++----- src/core/qwkglobal.cpp | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index 22e3c64..beea180 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -230,17 +230,15 @@ namespace QWK { if (m_windowHandle) { removeEventFilter(m_windowHandle); } - - auto oldWindowHandle = m_windowHandle.data(); m_windowHandle = m_delegate->hostWindow(m_host); + if (m_windowHandle) { + m_windowHandle->installEventFilter(this); + } if (oldWinId != m_windowId) { winIdChanged(m_windowId, oldWinId); if (m_windowId) { - // Installing twice has no side-effect. - m_windowHandle->installEventFilter(this); - // Refresh window attributes for (auto it = m_windowAttributesOrder.begin(); it != m_windowAttributesOrder.end();) { diff --git a/src/core/qwkglobal.cpp b/src/core/qwkglobal.cpp index ee33221..67b9c67 100644 --- a/src/core/qwkglobal.cpp +++ b/src/core/qwkglobal.cpp @@ -43,4 +43,5 @@ namespace QWK { } return false; } + } From 967e741b1696b2d43f9a742f4489be955cd97b92 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Mon, 24 Feb 2025 02:59:04 +0800 Subject: [PATCH 06/26] Update --- README.md | 3 +-- qmsetup | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6c51124..253d8d4 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ Cross-platform window customization framework for Qt Widgets and Qt Quick. -This project inherited most of [wangwenx190 FramelessHelper](https://github.com/wangwenx190/framelesshelper) -implementation, with a complete refactoring and upgrading of the architecture. +This project inherited the major implementations from [wangwenx190 FramelessHelper](https://github.com/wangwenx190/framelesshelper), with a complete refactoring and upgrading of the architecture. Feature requests are welcome. diff --git a/qmsetup b/qmsetup index d27b40c..1ddf1c2 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit d27b40ce22011cdbbd17f059f9084da931dcb3c0 +Subproject commit 1ddf1c280158a966923c12e0f0d2cdc0036da6d0 From 0aada6f97675200605951719f6629a0226cae0b8 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Mon, 3 Mar 2025 23:11:36 +0800 Subject: [PATCH 07/26] minor tweaks --- CMakeLists.txt | 11 ++++++++++- examples/mainwindow/CMakeLists.txt | 6 +----- examples/qml/CMakeLists.txt | 6 +----- examples/shared/widgetframe/CMakeLists.txt | 6 +----- src/CMakeLists.txt | 10 +++------- src/core/qwkglobal.h | 7 +++++++ 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b2b5e4..3c03032 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,11 @@ cmake_minimum_required(VERSION 3.19) -project(QWindowKit VERSION 1.4.0.0 LANGUAGES CXX) +project(QWindowKit + VERSION 1.4.0.0 + LANGUAGES CXX + HOMEPAGE_URL "https://github.com/stdware/qwindowkit" + DESCRIPTION "Cross-platform window customization framework" +) # ---------------------------------- # Build Options @@ -69,6 +74,10 @@ endif() set(QWINDOWKIT_VERSION ${PROJECT_VERSION}) set(QWINDOWKIT_INSTALL_NAME ${PROJECT_NAME}) +string(TIMESTAMP _QACTIONKIT_CURRENT_YEAR "%Y") +set(QACTIONKIT_COPYRIGHT "Copyright 2023-${_QACTIONKIT_CURRENT_YEAR} Stdware Collections") +set(QACTIONKIT_DESCRIPTION ${PROJECT_DESCRIPTION}) + # ---------------------------------- # Find basic dependencies # ---------------------------------- diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index 24d2eab..644e733 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -3,12 +3,8 @@ project(QWKExample_MainWindow) file(GLOB _src *.h *.cpp) qwk_add_example(${PROJECT_NAME} + FEATURES cxx_std_17 SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc QT_LINKS Core Gui Widgets # MultimediaWidgets LINKS QWKWidgets WidgetFrame -) - -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED TRUE ) \ No newline at end of file diff --git a/examples/qml/CMakeLists.txt b/examples/qml/CMakeLists.txt index 1755395..e99d23c 100644 --- a/examples/qml/CMakeLists.txt +++ b/examples/qml/CMakeLists.txt @@ -3,12 +3,8 @@ project(QWKExample_QML) file(GLOB _src *.h *.cpp *.qrc) qwk_add_example(${PROJECT_NAME} + FEATURES cxx_std_17 SOURCES ${_src} ../shared/resources/shared.qrc QT_LINKS Core Gui Qml Quick LINKS QWKQuick -) - -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED TRUE ) \ No newline at end of file diff --git a/examples/shared/widgetframe/CMakeLists.txt b/examples/shared/widgetframe/CMakeLists.txt index 05ac949..e4e2a9b 100644 --- a/examples/shared/widgetframe/CMakeLists.txt +++ b/examples/shared/widgetframe/CMakeLists.txt @@ -9,13 +9,9 @@ file(GLOB _src *.h *.cpp) add_library(${PROJECT_NAME} STATIC) qm_configure_target(${PROJECT_NAME} + FEATURES cxx_std_17 SOURCES ${_src} QT_LINKS Core Gui Widgets ) -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED TRUE -) - target_include_directories(${PROJECT_NAME} PUBLIC . ..) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 510c161..815e4da 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,5 @@ qm_import(Preprocess) -string(TIMESTAMP _current_year "%Y") - -set(QWINDOWKIT_PROJECT_DESCRIPTION "Cross-platform window customization framework") -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) @@ -60,8 +56,8 @@ macro(qwk_add_library _target) if(WIN32 AND NOT FUNC_NO_WIN_RC AND(${_type} STREQUAL "SHARED")) qm_add_win_rc(${_target} NAME ${QWINDOWKIT_INSTALL_NAME} - DESCRIPTION ${QWINDOWKIT_PROJECT_DESCRIPTION} - COPYRIGHT ${QWINDOWKIT_PROJECT_COPYRIGHT} + DESCRIPTION ${QWINDOWKIT_DESCRIPTION} + COPYRIGHT ${QACTIONKIT_COPYRIGHT} ) endif() @@ -164,7 +160,7 @@ if(QWINDOWKIT_BUILD_DOCUMENTATIONS) qm_import(Doxygen) qm_setup_doxygen(QWindowKit_RunDoxygen NAME "QWindowKit" - DESCRIPTION "${QWINDOWKIT_PROJECT_DESCRIPTION}" + DESCRIPTION "${QWINDOWKIT_DESCRIPTION}" MDFILE ../README.md OUTPUT_DIR ${CMAKE_BUILD_SHARE_DIR}/doc/${QWINDOWKIT_INSTALL_NAME} INPUT ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} diff --git a/src/core/qwkglobal.h b/src/core/qwkglobal.h index ded26b5..3e98112 100644 --- a/src/core/qwkglobal.h +++ b/src/core/qwkglobal.h @@ -34,6 +34,13 @@ using QT_ENTER_EVENT_TYPE = QEvent; # define QWINDOWKIT_CONFIG(feature) ((1 / QWINDOWKIT_##feature) == 1) #endif +#if defined(__GNUC__) || defined(__clang__) +# define QWINDOWKIT_PRINTF_FORMAT(fmtpos, attrpos) \ + __attribute__((__format__(__printf__, fmtpos, attrpos))) +#else +# define QWINDOWKIT_PRINTF_FORMAT(fmtpos, attrpos) +#endif + namespace QWK { using ScreenRectCallback = std::function; From 1d08c8b1144a62887a2b73903eef4129dc9be207 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Tue, 4 Mar 2025 18:29:06 +0800 Subject: [PATCH 08/26] Fix top border & misc tweaks (#167) * Update * update --------- Co-authored-by: SineStriker <55847490+SineStriker@users.noreply.github.com> --- src/core/contexts/win32windowcontext.cpp | 10 +++----- src/core/shared/windows10borderhandler_p.h | 27 ++++++++++++++++++---- src/quick/quickwindowagent_win.cpp | 13 +++-------- src/widgets/widgetwindowagent.cpp | 2 +- src/widgets/widgetwindowagent_win.cpp | 4 ++-- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 00ff1b4..f8a4f67 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -920,13 +920,8 @@ namespace QWK { const DynamicApis &apis = DynamicApis::instance(); const auto &extendMargins = [this, &apis, hwnd]() { - // For some unknown reason, the window background is totally black when the host object - // is a QWidget. And extending the window frame into the client area seems to fix it - // magically. - // We don't need the following *HACK* for QtQuick windows. - if (!m_host->isWidgetType()) { - return; - } + // For some unknown reason, the window background is totally black and extending + // the window frame into the client area seems to fix it magically. // After many times of trying, we found that the Acrylic/Mica/Mica Alt background // only appears on the native Win32 window's background, so naturally we want to // extend the window frame into the whole client area to be able to let the special @@ -955,6 +950,7 @@ namespace QWK { const auto &effectBugWorkaround = [this, hwnd]() { // We don't need the following *HACK* for QWidget windows. + // Completely based on actual experiments, root reason is totally unknown. if (m_host->isWidgetType()) { return; } diff --git a/src/core/shared/windows10borderhandler_p.h b/src/core/shared/windows10borderhandler_p.h index 8099fe0..8f728f4 100644 --- a/src/core/shared/windows10borderhandler_p.h +++ b/src/core/shared/windows10borderhandler_p.h @@ -31,10 +31,12 @@ namespace QWK { } inline void setupNecessaryAttributes() { - // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940 - // Must extend top frame to client area - static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); - ctx->setWindowAttribute(QStringLiteral("extra-margins"), defaultMargins); + if (!isWin11OrGreater()) { + // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L940 + // Must extend top frame to client area + static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); + ctx->setWindowAttribute(QStringLiteral("extra-margins"), defaultMargins); + } // Enable dark mode by default, otherwise the system borders are white ctx->setWindowAttribute(QStringLiteral("dark-mode"), true); @@ -45,7 +47,17 @@ namespace QWK { (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)); } - inline void drawBorder() { + inline void drawBorderEmulated(QPainter *painter, const QRect &rect) { + QRegion region(rect); + void *args[] = { + painter, + const_cast(&rect), + ®ion, + }; + ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Emulated, args); + } + + inline void drawBorderNative() { ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Native, nullptr); } @@ -54,6 +66,11 @@ namespace QWK { } inline void updateExtraMargins(bool windowActive) { + if (isWin11OrGreater()) { + return; + } + + // ### FIXME: transparent seam if (windowActive) { // Restore margins when the window is active static QVariant defaultMargins = QVariant::fromValue(QMargins(0, 1, 0, 0)); diff --git a/src/quick/quickwindowagent_win.cpp b/src/quick/quickwindowagent_win.cpp index acfdd42..95bb6f8 100644 --- a/src/quick/quickwindowagent_win.cpp +++ b/src/quick/quickwindowagent_win.cpp @@ -97,21 +97,14 @@ namespace QWK { BorderItem::~BorderItem() = default; void BorderItem::updateGeometry() { - setHeight(borderThickness()); + setHeight(borderThickness() / window()->devicePixelRatio()); setVisible(isNormalWindow()); } void BorderItem::paint(QPainter *painter) { Q_UNUSED(painter) if (shouldEnableEmulatedPainter()) { - QRect rect(QPoint(0, 0), size().toSize()); - QRegion region(rect); - void *args[] = { - painter, - &rect, - ®ion, - }; - ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook_Emulated, args); + drawBorderEmulated(painter, QRect({0, 0}, size().toSize())); } else { needPaint = true; } @@ -170,7 +163,7 @@ namespace QWK { void BorderItem::_q_afterSynchronizing() { if (needPaint) { needPaint = false; - drawBorder(); + drawBorderNative(); } } diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp index ca5afa4..1a86d71 100644 --- a/src/widgets/widgetwindowagent.cpp +++ b/src/widgets/widgetwindowagent.cpp @@ -61,7 +61,7 @@ namespace QWK { w->setAttribute(Qt::WA_DontCreateNativeAncestors); // Make sure the native window handle is actually created before we apply // various hooks. - w->setAttribute(Qt::WA_NativeWindow); // ### FIXME: Remove + //w->setAttribute(Qt::WA_NativeWindow); // ### FIXME: Check d->setup(w, new WidgetItemDelegate()); d->hostWidget = w; diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp index 1522cab..f7501e0 100644 --- a/src/widgets/widgetwindowagent_win.cpp +++ b/src/widgets/widgetwindowagent_win.cpp @@ -83,7 +83,7 @@ namespace QWK { // Due to the timer or user action, Qt will repaint some regions spontaneously, // even if there is no WM_PAINT message, we must wait for it to finish painting // and then update the top border area. - drawBorder(); + drawBorderNative(); } inline void forwardEventToWindowAndDraw(QWindow *window, QEvent *event) { @@ -97,7 +97,7 @@ namespace QWK { // Upon receiving the WM_PAINT message, Qt will repaint the entire view, and we // must wait for it to finish painting before drawing this top border area. - drawBorder(); + drawBorderNative(); } protected: From 835e769f97eda4ff3ecc16ddca6d36c5496dc50d Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Thu, 6 Mar 2025 18:32:04 +0800 Subject: [PATCH 09/26] fix build with old win-sdk --- src/core/contexts/win32windowcontext.cpp | 4 +++ src/core/qwindowkit_windows.h | 8 +++++ src/core/shared/qwkwindowsextra_p.h | 45 ++++++++++++++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index f8a4f67..0d70129 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -37,6 +37,10 @@ # error Current Qt version has a critical bug which will break QWK functionality. Please upgrade to > 6.6.1 or downgrade to < 6.6.0 #endif +#ifndef DWM_BB_ENABLE +# define DWM_BB_ENABLE 0x00000001 +#endif + namespace QWK { enum IconButtonClickLevelFlag { diff --git a/src/core/qwindowkit_windows.h b/src/core/qwindowkit_windows.h index 7e9b8a7..aad3775 100644 --- a/src/core/qwindowkit_windows.h +++ b/src/core/qwindowkit_windows.h @@ -5,6 +5,14 @@ #ifndef QWINDOWKIT_WINDOWS_H #define QWINDOWKIT_WINDOWS_H +#ifndef _USER32_ +# define _USER32_ +#endif + +#ifndef _DWMAPI_ +# define _DWMAPI_ +#endif + #include #include diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h index 8fdc86e..3bd086e 100644 --- a/src/core/shared/qwkwindowsextra_p.h +++ b/src/core/shared/qwkwindowsextra_p.h @@ -14,10 +14,6 @@ // version without notice, or may even be removed. // -#include -#include -#include - #include #include @@ -29,6 +25,41 @@ // Don't include this header in any header files. +typedef struct _MARGINS +{ + int cxLeftWidth; + int cxRightWidth; + int cyTopHeight; + int cyBottomHeight; +} MARGINS, *PMARGINS; + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +typedef struct _DWM_BLURBEHIND +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND, *PDWM_BLURBEHIND; + +extern "C" { + UINT WINAPI GetDpiForWindow(HWND); + int WINAPI GetSystemMetricsForDpi(int, UINT); + HRESULT WINAPI GetDpiForMonitor(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *); + HRESULT WINAPI DwmFlush(); + HRESULT WINAPI DwmIsCompositionEnabled(BOOL*); + HRESULT WINAPI DwmGetWindowAttribute(HWND, DWORD, PVOID, DWORD); + HRESULT WINAPI DwmSetWindowAttribute(HWND, DWORD, LPCVOID, DWORD); + HRESULT WINAPI DwmExtendFrameIntoClientArea(HWND, const MARGINS*); + HRESULT WINAPI DwmEnableBlurBehindWindow(HWND, const DWM_BLURBEHIND*); +} // extern "C" + namespace QWK { enum _DWMWINDOWATTRIBUTE { @@ -160,7 +191,7 @@ namespace QWK { namespace { struct DynamicApis { - static const DynamicApis &instance() { + static inline const DynamicApis &instance() { static const DynamicApis inst; return inst; } @@ -188,7 +219,7 @@ namespace QWK { SetPreferredAppModePtr pSetPreferredAppMode = nullptr; private: - DynamicApis() { + inline DynamicApis() { #define DYNAMIC_API_RESOLVE(DLL, NAME) \ p##NAME = reinterpret_cast(DLL.resolve(#NAME)) @@ -224,7 +255,7 @@ namespace QWK { #undef UNDOC_API_RESOLVE } - ~DynamicApis() = default; + inline ~DynamicApis() = default; Q_DISABLE_COPY(DynamicApis) }; From a1c500e1dd254445e7bc153f4e0f049cabab35a0 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Sun, 9 Mar 2025 15:22:11 +0800 Subject: [PATCH 10/26] add missing Win10 API --- src/core/shared/qwkwindowsextra_p.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h index 3bd086e..a173be9 100644 --- a/src/core/shared/qwkwindowsextra_p.h +++ b/src/core/shared/qwkwindowsextra_p.h @@ -51,6 +51,7 @@ typedef struct _DWM_BLURBEHIND extern "C" { UINT WINAPI GetDpiForWindow(HWND); int WINAPI GetSystemMetricsForDpi(int, UINT); + BOOL WINAPI AdjustWindowRectExForDpi(LPRECT, DWORD, BOOL, DWORD, UINT); HRESULT WINAPI GetDpiForMonitor(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *); HRESULT WINAPI DwmFlush(); HRESULT WINAPI DwmIsCompositionEnabled(BOOL*); From c00e986f38dbc4843d92cc76a385f826658c1e03 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Tue, 11 Mar 2025 18:05:31 +0800 Subject: [PATCH 11/26] add missing win32 constant for old win sdk --- src/core/contexts/win32windowcontext.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 0d70129..7dd6b0c 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -41,6 +41,10 @@ # define DWM_BB_ENABLE 0x00000001 #endif +#ifndef ABM_GETAUTOHIDEBAREX +# define ABM_GETAUTOHIDEBAREX 0x0000000b +#endif + namespace QWK { enum IconButtonClickLevelFlag { From 921dbf5719c9b734fbd26d7b6300e29c943093f3 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Tue, 11 Mar 2025 19:54:51 +0800 Subject: [PATCH 12/26] update --- src/core/contexts/abstractwindowcontext.cpp | 66 ++++----------------- src/core/contexts/abstractwindowcontext_p.h | 13 ++-- src/core/contexts/win32windowcontext.cpp | 2 +- 3 files changed, 16 insertions(+), 65 deletions(-) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index beea180..2d89ae1 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -32,21 +32,16 @@ namespace QWK { return false; } - auto it = m_hitTestVisibleItems.find(obj); if (visible) { - if (it != m_hitTestVisibleItems.end()) { - return true; - } - connect(obj, &QObject::destroyed, this, - &AbstractWindowContext::_q_hitTestVisibleItemDestroyed); - m_hitTestVisibleItems.insert(obj); + m_hitTestVisibleItems.removeAll(nullptr); + m_hitTestVisibleItems.removeAll(obj); + m_hitTestVisibleItems.append(obj); } else { - if (it == m_hitTestVisibleItems.end()) { - return false; + for (auto &item : m_hitTestVisibleItems) { + if (item == obj) { + item = nullptr; + } } - disconnect(obj, &QObject::destroyed, this, - &AbstractWindowContext::_q_hitTestVisibleItemDestroyed); - m_hitTestVisibleItems.erase(it); } return true; } @@ -60,16 +55,7 @@ namespace QWK { auto org = m_systemButtons[button]; if (org == obj) { - return true; - } - - if (org) { - disconnect(org, &QObject::destroyed, this, - &AbstractWindowContext::_q_systemButtonDestroyed); - } - if (obj) { - connect(obj, &QObject::destroyed, this, - &AbstractWindowContext::_q_systemButtonDestroyed); + return false; } m_systemButtons[button] = obj; return true; @@ -85,11 +71,6 @@ namespace QWK { if (org) { // Since the title bar is changed, all items inside it should be dereferenced right away removeSystemButtonsAndHitTestItems(); - disconnect(org, &QObject::destroyed, this, - &AbstractWindowContext::_q_titleBarDistroyed); - } - if (item) { - connect(item, &QObject::destroyed, this, &AbstractWindowContext::_q_titleBarDistroyed); } m_titleBar = item; return true; @@ -150,13 +131,12 @@ namespace QWK { } } - for (auto widget : m_hitTestVisibleItems) { - if (widget && m_delegate->isVisible(widget) && m_delegate->isEnabled(widget) && - m_delegate->mapGeometryToScene(widget).contains(pos)) { + for (auto item : m_hitTestVisibleItems) { + if (item && m_delegate->isVisible(item) && m_delegate->isEnabled(item) && + m_delegate->mapGeometryToScene(item).contains(pos)) { return false; } } - return true; } @@ -315,33 +295,9 @@ namespace QWK { if (!button) { continue; } - disconnect(button, &QObject::destroyed, this, - &AbstractWindowContext::_q_systemButtonDestroyed); button = nullptr; } - for (auto &item : m_hitTestVisibleItems) { - disconnect(item, &QObject::destroyed, this, - &AbstractWindowContext::_q_hitTestVisibleItemDestroyed); - } m_hitTestVisibleItems.clear(); } - void AbstractWindowContext::_q_titleBarDistroyed(QObject *obj) { - Q_UNUSED(obj) - removeSystemButtonsAndHitTestItems(); - m_titleBar = nullptr; - } - - void AbstractWindowContext::_q_hitTestVisibleItemDestroyed(QObject *obj) { - m_hitTestVisibleItems.remove(obj); - } - - void AbstractWindowContext::_q_systemButtonDestroyed(QObject *obj) { - for (auto &item : m_systemButtons) { - if (item == obj) { - item = nullptr; - } - } - } - } diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h index e978cb4..dc18f37 100644 --- a/src/core/contexts/abstractwindowcontext_p.h +++ b/src/core/contexts/abstractwindowcontext_p.h @@ -116,13 +116,13 @@ namespace QWK { QPointer m_windowHandle; WId m_windowId{}; - QSet m_hitTestVisibleItems; + QVector> m_hitTestVisibleItems; #ifdef Q_OS_MAC ScreenRectCallback m_systemButtonAreaCallback; #endif - QObject *m_titleBar{}; - std::array m_systemButtons{}; + QPointer m_titleBar{}; + std::array, WindowAgentBase::Close + 1> m_systemButtons{}; std::list> m_windowAttributesOrder; QHash m_windowAttributes; @@ -130,11 +130,6 @@ namespace QWK { std::unique_ptr m_winIdChangeEventFilter; void removeSystemButtonsAndHitTestItems(); - - private: - void _q_titleBarDistroyed(QObject *obj); - void _q_hitTestVisibleItemDestroyed(QObject *obj); - void _q_systemButtonDestroyed(QObject *obj); }; inline QObject *AbstractWindowContext::host() const { @@ -154,7 +149,7 @@ namespace QWK { } inline bool AbstractWindowContext::isHitTestVisible(const QObject *obj) const { - return m_hitTestVisibleItems.contains(obj); + return m_hitTestVisibleItems.contains(const_cast(obj)); } inline QObject * diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 7dd6b0c..fa2b53f 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -2329,7 +2329,7 @@ namespace QWK { if (!mouseHook) { mouseHook = ::SetWindowsHookExW( WH_MOUSE, - [](int nCode, WPARAM wParam, LPARAM lParam) { + [](int nCode, WPARAM wParam, LPARAM lParam) -> LRESULT WINAPI { if (nCode >= 0) { switch (wParam) { case WM_LBUTTONDBLCLK: From 7966371dead9076db6c56de4074b36d6f628678b Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:35:50 +0800 Subject: [PATCH 13/26] AWC: remove isEnabled in test methods --- src/core/contexts/abstractwindowcontext.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index 2d89ae1..dcf41b5 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -88,8 +88,7 @@ namespace QWK { *button = WindowAgentBase::Unknown; for (int i = WindowAgentBase::WindowIcon; i <= WindowAgentBase::Close; ++i) { auto currentButton = m_systemButtons[i]; - if (!currentButton || !m_delegate->isVisible(currentButton) || - !m_delegate->isEnabled(currentButton)) { + if (!currentButton || !m_delegate->isVisible(currentButton)) { continue; } if (m_delegate->mapGeometryToScene(currentButton).contains(pos)) { @@ -122,17 +121,13 @@ namespace QWK { return false; } - for (int i = WindowAgentBase::WindowIcon; i <= WindowAgentBase::Close; ++i) { - auto currentButton = m_systemButtons[i]; - if (currentButton && m_delegate->isVisible(currentButton) && - m_delegate->isEnabled(currentButton) && - m_delegate->mapGeometryToScene(currentButton).contains(pos)) { - return false; - } + WindowAgentBase::SystemButton button; + if (isInSystemButtons(pos, &button)) { + return true; } for (auto item : m_hitTestVisibleItems) { - if (item && m_delegate->isVisible(item) && m_delegate->isEnabled(item) && + if (item && m_delegate->isVisible(item)&& m_delegate->mapGeometryToScene(item).contains(pos)) { return false; } From 3e4d1d3e15bbf18961afb20a4afdad978050af66 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Fri, 14 Mar 2025 17:53:13 +0800 Subject: [PATCH 14/26] update qmsetup --- qmsetup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmsetup b/qmsetup index 1ddf1c2..04526c3 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 1ddf1c280158a966923c12e0f0d2cdc0036da6d0 +Subproject commit 04526c37b4087d151b8a373fbc9ea0207702135b From 2d5d5179a491e4b6ba448593e5968a38cb448605 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Fri, 14 Mar 2025 17:54:59 +0800 Subject: [PATCH 15/26] fix logic error --- src/core/contexts/abstractwindowcontext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index dcf41b5..f2fc730 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -123,11 +123,11 @@ namespace QWK { WindowAgentBase::SystemButton button; if (isInSystemButtons(pos, &button)) { - return true; + return false; } for (auto item : m_hitTestVisibleItems) { - if (item && m_delegate->isVisible(item)&& + if (item && m_delegate->isVisible(item) && m_delegate->mapGeometryToScene(item).contains(pos)) { return false; } From 758b00cb6c2d924be3a1ea137ec366dc33a5132d Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Sun, 23 Mar 2025 17:26:41 +0800 Subject: [PATCH 16/26] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 253d8d4..2f100c5 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ git clone --recursive https://github.com/stdware/qwindowkit cd qwindowkit cmake -B build -S . \ + -DCMAKE_PREFIX_PATH= \ -Dqmsetup_DIR= \ # Optional -DQWINDOWKIT_BUILD_QUICK=TRUE \ # Optional -DCMAKE_INSTALL_PREFIX=/path/install \ From f79b4c3ef8fae15e1bce56e4443caa76a9e2b00a Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Thu, 10 Apr 2025 11:26:28 +0800 Subject: [PATCH 17/26] update qmsetup --- qmsetup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmsetup b/qmsetup index 04526c3..99ce968 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 04526c37b4087d151b8a373fbc9ea0207702135b +Subproject commit 99ce9684ef6fce3ded113847378f8ecdefb15cc4 From 68931190bda74f6b2ed4c98287f0d40176a6ca69 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Thu, 10 Apr 2025 15:45:59 +0800 Subject: [PATCH 18/26] update qmsetup --- qmsetup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmsetup b/qmsetup index 99ce968..92c2f5b 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 99ce9684ef6fce3ded113847378f8ecdefb15cc4 +Subproject commit 92c2f5bb99618bfe8a08440620d7b942c6e387ec From 0131d673092ab18afd69fac84f4a17ad2ba615f2 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Sat, 12 Apr 2025 22:17:19 +0800 Subject: [PATCH 19/26] update qmsetup --- qmsetup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmsetup b/qmsetup index 92c2f5b..7176162 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 92c2f5bb99618bfe8a08440620d7b942c6e387ec +Subproject commit 71761623eee1c670148706094d9bc6cd0a017406 From 4df5d723cb3ef9d0f4b1d2f908af2dedce03cf0a Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Mon, 14 Apr 2025 11:40:13 +0800 Subject: [PATCH 20/26] update qmsetup --- qmsetup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmsetup b/qmsetup index 7176162..0cc2406 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 71761623eee1c670148706094d9bc6cd0a017406 +Subproject commit 0cc2406a168c2730601cdd801879424b3dd2e2c3 From 469c975a708ed86c834b59ed751a4548c693a7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=A8=81=20wei=2Eli?= <108964761+liwey1985@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:34:54 +0800 Subject: [PATCH 21/26] Fix NSWindowProxy::setSystemButtonVisible not taking effect (#175) --- examples/mainwindow/mainwindow.cpp | 5 + src/core/contexts/cocoawindowcontext.mm | 118 ++++++++++++++++++------ 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index a8d2c61..fb3d5eb 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -210,6 +210,11 @@ void MainWindow::installWindowAgent() { }); #elif defined(Q_OS_MAC) + // Set whether to use system buttons (close/minimize/zoom) + // - true: Hide system buttons (use custom UI controls) + // - false: Show native system buttons (default behavior) + windowAgent->setWindowAttribute(QStringLiteral("no-system-buttons"), false); + auto darkBlurAction = new QAction(tr("Dark blur"), menuBar); darkBlurAction->setCheckable(true); connect(darkBlurAction, &QAction::toggled, this, [this](bool checked) { diff --git a/src/core/contexts/cocoawindowcontext.mm b/src/core/contexts/cocoawindowcontext.mm index c26152f..28738c6 100644 --- a/src/core/contexts/cocoawindowcontext.mm +++ b/src/core/contexts/cocoawindowcontext.mm @@ -23,10 +23,6 @@ using ProxyList = QHash; Q_GLOBAL_STATIC(ProxyList, g_proxyList); - - using ProxyList2 = QHash; - Q_GLOBAL_STATIC(ProxyList2, g_proxyIndexes); - } struct QWK_NSWindowDelegate { @@ -87,7 +83,8 @@ - (void)dealloc { - (void)windowWillEnterFullScreen:(NSNotification *)notification { auto nswindow = reinterpret_cast(notification.object); - if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + auto nsview = [nswindow contentView]; + if (auto proxy = QWK::g_proxyList->value(reinterpret_cast(nsview))) { reinterpret_cast(proxy)->windowEvent( QWK_NSWindowDelegate::WillEnterFullScreen); } @@ -95,7 +92,8 @@ - (void)windowWillEnterFullScreen:(NSNotification *)notification { - (void)windowDidEnterFullScreen:(NSNotification *)notification { auto nswindow = reinterpret_cast(notification.object); - if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + auto nsview = [nswindow contentView]; + if (auto proxy = QWK::g_proxyList->value(reinterpret_cast(nsview))) { reinterpret_cast(proxy)->windowEvent( QWK_NSWindowDelegate::DidEnterFullScreen); } @@ -103,7 +101,8 @@ - (void)windowDidEnterFullScreen:(NSNotification *)notification { - (void)windowWillExitFullScreen:(NSNotification *)notification { auto nswindow = reinterpret_cast(notification.object); - if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + auto nsview = [nswindow contentView]; + if (auto proxy = QWK::g_proxyList->value(reinterpret_cast(nsview))) { reinterpret_cast(proxy)->windowEvent( QWK_NSWindowDelegate::WillExitFullScreen); } @@ -111,7 +110,8 @@ - (void)windowWillExitFullScreen:(NSNotification *)notification { - (void)windowDidExitFullScreen:(NSNotification *)notification { auto nswindow = reinterpret_cast(notification.object); - if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + auto nsview = [nswindow contentView]; + if (auto proxy = QWK::g_proxyList->value(reinterpret_cast(nsview))) { reinterpret_cast(proxy)->windowEvent( QWK_NSWindowDelegate::DidExitFullScreen); } @@ -119,7 +119,8 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { - (void)windowDidResize:(NSNotification *)notification { auto nswindow = reinterpret_cast(notification.object); - if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) { + auto nsview = [nswindow contentView]; + if (auto proxy = QWK::g_proxyList->value(reinterpret_cast(nsview))) { reinterpret_cast(proxy)->windowEvent( QWK_NSWindowDelegate::DidResize); } @@ -127,6 +128,10 @@ - (void)windowDidResize:(NSNotification *)notification { @end +@interface QWK_NSViewObserver : NSObject +- (instancetype)initWithProxy:(QWK::NSWindowProxy*)proxy; +@end + // // Objective C++ End // @@ -140,13 +145,19 @@ - (void)windowDidResize:(NSNotification *)notification { None, }; - NSWindowProxy(NSWindow *macWindow) { - nswindow = macWindow; - g_proxyIndexes->insert(nswindow, this); + NSWindowProxy(NSView *macView) { + nsview = macView; + + observer = [[QWK_NSViewObserver alloc] initWithProxy:this]; + [nsview addObserver:observer + forKeyPath:@"window" + options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld + context:nil]; } ~NSWindowProxy() override { - g_proxyIndexes->remove(nswindow); + [nsview removeObserver:observer forKeyPath:@"window"]; + [observer release]; } // Delegate @@ -213,6 +224,9 @@ void setScreenRectCallback(const ScreenRectCallback &callback) { } void updateSystemButtonRect() { + if (!screenRectCallback || !systemButtonVisible) { + return; + } const auto &buttons = systemButtons(); const auto &leftButton = buttons[0]; const auto &midButton = buttons[1]; @@ -225,8 +239,7 @@ void updateSystemButtonRect() { auto width = midButton.frame.size.width; auto height = midButton.frame.size.height; - auto viewSize = - nswindow.contentView ? nswindow.contentView.frame.size : nswindow.frame.size; + auto viewSize = nsview.frame.size; QPoint center = screenRectCallback(QSize(viewSize.width, titlebarHeight)).center(); // The origin of the NSWindow coordinate system is in the lower left corner, we @@ -256,6 +269,10 @@ void updateSystemButtonRect() { } inline std::array systemButtons() { + auto nswindow = [nsview window]; + if (!nswindow) { + return {nullptr, nullptr, nullptr}; + } NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; NSButton *minimizeBtn = [nswindow standardWindowButton:NSWindowMiniaturizeButton]; NSButton *zoomBtn = [nswindow standardWindowButton:NSWindowZoomButton]; @@ -263,6 +280,10 @@ void updateSystemButtonRect() { } inline int titleBarHeight() const { + auto nswindow = [nsview window]; + if (!nswindow) { + return 0; + } NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton]; return closeBtn.superview.frame.size.height; } @@ -274,8 +295,7 @@ bool setBlurEffect(BlurMode mode) { return false; NSVisualEffectView *effectView = nil; - NSView *const view = [nswindow contentView]; - for (NSView *subview in [[view superview] subviews]) { + for (NSView *subview in [[nsview superview] subviews]) { if ([subview isKindOfClass:visualEffectViewClass]) { effectView = reinterpret_cast(subview); } @@ -311,8 +331,8 @@ bool setBlurEffect(BlurMode mode) { // System title bar void setSystemTitleBarVisible(const bool visible) { - NSView *nsview = [nswindow contentView]; - if (!nsview) { + auto nswindow = [nsview window]; + if (!nswindow) { return; } @@ -396,7 +416,9 @@ static void restoreImplementations() { protected: static BOOL canBecomeKeyWindow(id obj, SEL sel) { - if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + auto nswindow = reinterpret_cast(obj); + auto nsview = [nswindow contentView]; + if (g_proxyList->contains(reinterpret_cast(nsview))) { return YES; } @@ -408,7 +430,9 @@ static BOOL canBecomeKeyWindow(id obj, SEL sel) { } static BOOL canBecomeMainWindow(id obj, SEL sel) { - if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + auto nswindow = reinterpret_cast(obj); + auto nsview = [nswindow contentView]; + if (g_proxyList->contains(reinterpret_cast(nsview))) { return YES; } @@ -420,7 +444,9 @@ static BOOL canBecomeMainWindow(id obj, SEL sel) { } static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) { - if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + auto nswindow = reinterpret_cast(obj); + auto nsview = [nswindow contentView]; + if (g_proxyList->contains(reinterpret_cast(nsview))) { styleMask |= NSWindowStyleMaskFullSizeContentView; } @@ -430,7 +456,9 @@ static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) { } static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) { - if (g_proxyIndexes->contains(reinterpret_cast(obj))) { + auto nswindow = reinterpret_cast(obj); + auto nsview = [nswindow contentView]; + if (g_proxyList->contains(reinterpret_cast(nsview))) { transparent = YES; } @@ -463,7 +491,8 @@ static void sendEvent(id obj, SEL sel, NSEvent *event) { private: Q_DISABLE_COPY(NSWindowProxy) - NSWindow *nswindow = nil; + NSView *nsview = nil; + QWK_NSViewObserver* observer = nil; bool systemButtonVisible = true; ScreenRectCallback screenRectCallback; @@ -500,8 +529,8 @@ static void sendEvent(id obj, SEL sel, NSEvent *event) { auto it = g_proxyList->find(windowId); if (it == g_proxyList->end()) { - NSWindow *nswindow = mac_getNSWindow(windowId); - const auto proxy = new NSWindowProxy(nswindow); + NSView *nsview = reinterpret_cast(windowId); + const auto proxy = new NSWindowProxy(nsview); it = g_proxyList->insert(windowId, proxy); } return it.value(); @@ -701,7 +730,12 @@ static inline void releaseWindowProxy(const WId windowId) { } // Allocate new resources - ensureWindowProxy(winId)->setSystemTitleBarVisible(false); + const auto proxy = ensureWindowProxy(winId); + if (proxy) { + proxy->setSystemButtonVisible(!windowAttribute(QStringLiteral("no-system-buttons")).toBool()); + proxy->setScreenRectCallback(m_systemButtonAreaCallback); + proxy->setSystemTitleBarVisible(false); + } } bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute, @@ -746,3 +780,33 @@ static inline void releaseWindowProxy(const WId windowId) { } } + +@implementation QWK_NSViewObserver { + QWK::NSWindowProxy* _proxy; // Weak reference +} + +- (instancetype)initWithProxy:(QWK::NSWindowProxy*)proxy { + if (self = [super init]) { + _proxy = proxy; + } + return self; +} + +// Using QEvent::Show to call setSystemTitleBarVisible/updateSystemButtonRect could also work, +// but observing the window property change via KVO provides more immediate notification when +// the NSWindow becomes available, making this approach more natural and reliable. +- (void)observeValueForKeyPath:(NSString*)keyPath + ofObject:(id)object + change:(NSDictionary*)change + context:(void*)context { + if ([keyPath isEqualToString:@"window"]) { + NSWindow* newWindow = change[NSKeyValueChangeNewKey]; + // NSWindow* oldWindow = change[NSKeyValueChangeOldKey]; + if (newWindow) { + _proxy->setSystemTitleBarVisible(false); + _proxy->updateSystemButtonRect(); + } + } +} + +@end From 153e1f4f2fb711f5ec7072e0e49346fc76901cc3 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Sat, 26 Apr 2025 13:41:20 +0800 Subject: [PATCH 22/26] Update --- qmsetup | 2 +- src/core/contexts/win32windowcontext.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qmsetup b/qmsetup index 0cc2406..e6e93ea 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 0cc2406a168c2730601cdd801879424b3dd2e2c3 +Subproject commit e6e93eaa57c4bfb7ec6e5b4571be3f9831b29c6e diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index fa2b53f..7ffd7da 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -2329,7 +2329,7 @@ namespace QWK { if (!mouseHook) { mouseHook = ::SetWindowsHookExW( WH_MOUSE, - [](int nCode, WPARAM wParam, LPARAM lParam) -> LRESULT WINAPI { + [](int nCode, WPARAM wParam, LPARAM lParam) -> LRESULT { if (nCode >= 0) { switch (wParam) { case WM_LBUTTONDBLCLK: From 576654b6173e97425411010b2f40b3e9519b85b8 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: Wed, 7 May 2025 18:44:38 +0800 Subject: [PATCH 23/26] fix possible QWKQuick crash --- src/quick/quickwindowagent_win.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/quick/quickwindowagent_win.cpp b/src/quick/quickwindowagent_win.cpp index 95bb6f8..712e742 100644 --- a/src/quick/quickwindowagent_win.cpp +++ b/src/quick/quickwindowagent_win.cpp @@ -47,7 +47,11 @@ namespace QWK { bool BorderItem::shouldEnableEmulatedPainter() const { # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - auto api = window()->rendererInterface()->graphicsApi(); + const QQuickWindow* win = window(); + if (!win) { + return true; + } + auto api = win->rendererInterface()->graphicsApi(); switch (api) { case QSGRendererInterface::OpenGL: // FIXME: experimental, try to find the exact fixed version. @@ -97,7 +101,11 @@ namespace QWK { BorderItem::~BorderItem() = default; void BorderItem::updateGeometry() { - setHeight(borderThickness() / window()->devicePixelRatio()); + const QQuickWindow* win = window(); + if (!win) { + return; + } + setHeight(borderThickness() / win->effectiveDevicePixelRatio()); setVisible(isNormalWindow()); } From e9999fa61ea0ffa602c69e10e54996b3a636a5e6 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Fri, 9 May 2025 16:03:45 +0800 Subject: [PATCH 24/26] fix window reopen issue (#179) --- examples/qml/FramelessWindow.qml | 259 +++++++++++++++++++++++ examples/qml/main.qml | 254 +--------------------- examples/qml/qml.qrc | 1 + src/core/contexts/win32windowcontext.cpp | 12 ++ 4 files changed, 281 insertions(+), 245 deletions(-) create mode 100644 examples/qml/FramelessWindow.qml diff --git a/examples/qml/FramelessWindow.qml b/examples/qml/FramelessWindow.qml new file mode 100644 index 0000000..7a669ea --- /dev/null +++ b/examples/qml/FramelessWindow.qml @@ -0,0 +1,259 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import Qt.labs.platform 1.1 +import QWindowKit 1.0 + +Window { + property bool showWhenReady: true + + id: window + width: 800 + height: 600 + color: darkStyle.windowBackgroundColor + title: qsTr("QWindowKit QtQuick Demo") + Component.onCompleted: { + windowAgent.setup(window) + windowAgent.setWindowAttribute("dark-mode", true) + if (window.showWhenReady) { + window.visible = true + } + } + + QtObject { + id: lightStyle + } + + QtObject { + id: darkStyle + readonly property color windowBackgroundColor: "#1E1E1E" + } + + Timer { + interval: 100 + running: true + repeat: true + onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") + } + + WindowAgent { + id: windowAgent + } + + TapHandler { + acceptedButtons: Qt.RightButton + onTapped: contextMenu.open() + } + + Rectangle { + id: titleBar + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: 32 + //color: window.active ? "#3C3C3C" : "#505050" + color: "transparent" + Component.onCompleted: windowAgent.setTitleBar(titleBar) + + Image { + id: iconButton + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: 10 + } + width: 18 + height: 18 + mipmap: true + source: "qrc:///app/example.png" + fillMode: Image.PreserveAspectFit + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.WindowIcon, iconButton) + } + + Text { + anchors { + verticalCenter: parent.verticalCenter + left: iconButton.right + leftMargin: 10 + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: window.title + font.pixelSize: 14 + color: "#ECECEC" + } + + Row { + anchors { + top: parent.top + right: parent.right + } + height: parent.height + + QWKButton { + id: minButton + height: parent.height + source: "qrc:///window-bar/minimize.svg" + onClicked: window.showMinimized() + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Minimize, minButton) + } + + QWKButton { + id: maxButton + height: parent.height + source: window.visibility === Window.Maximized ? "qrc:///window-bar/restore.svg" : "qrc:///window-bar/maximize.svg" + onClicked: { + if (window.visibility === Window.Maximized) { + window.showNormal() + } else { + window.showMaximized() + } + } + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Maximize, maxButton) + } + + QWKButton { + id: closeButton + height: parent.height + source: "qrc:///window-bar/close.svg" + background: Rectangle { + color: { + if (!closeButton.enabled) { + return "gray"; + } + if (closeButton.pressed) { + return "#e81123"; + } + if (closeButton.hovered) { + return "#e81123"; + } + return "transparent"; + } + } + onClicked: window.close() + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Close, closeButton) + } + } + } + + Label { + id: timeLabel + anchors.centerIn: parent + font { + pointSize: 75 + bold: true + } + color: "#FEFEFE" + Component.onCompleted: { + if ($curveRenderingAvailable) { + console.log("Curve rendering for text is available.") + timeLabel.renderType = Text.CurveRendering + } + } + } + + Menu { + id: contextMenu + + Menu { + id: themeMenu + title: qsTr("Theme") + + MenuItemGroup { + id: themeMenuGroup + items: themeMenu.items + } + + MenuItem { + text: qsTr("Light") + checkable: true + onTriggered: windowAgent.setWindowAttribute("dark-mode", false) + } + + MenuItem { + text: qsTr("Dark") + checkable: true + checked: true + onTriggered: windowAgent.setWindowAttribute("dark-mode", true) + } + } + + Menu { + id: specialEffectMenu + title: qsTr("Special effect") + + MenuItemGroup { + id: specialEffectMenuGroup + items: specialEffectMenu.items + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("None") + checkable: true + checked: true + onTriggered: { + window.color = darkStyle.windowBackgroundColor + windowAgent.setWindowAttribute("dwm-blur", false) + windowAgent.setWindowAttribute("acrylic-material", false) + windowAgent.setWindowAttribute("mica", false) + windowAgent.setWindowAttribute("mica-alt", false) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("DWM blur") + checkable: true + onTriggered: { + window.color = "transparent" + windowAgent.setWindowAttribute("acrylic-material", false) + windowAgent.setWindowAttribute("mica", false) + windowAgent.setWindowAttribute("mica-alt", false) + windowAgent.setWindowAttribute("dwm-blur", true) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("Acrylic material") + checkable: true + onTriggered: { + window.color = "transparent" + windowAgent.setWindowAttribute("dwm-blur", false) + windowAgent.setWindowAttribute("mica", false) + windowAgent.setWindowAttribute("mica-alt", false) + windowAgent.setWindowAttribute("acrylic-material", true) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("Mica") + checkable: true + onTriggered: { + window.color = "transparent" + windowAgent.setWindowAttribute("dwm-blur", false) + windowAgent.setWindowAttribute("acrylic-material", false) + windowAgent.setWindowAttribute("mica-alt", false) + windowAgent.setWindowAttribute("mica", true) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("Mica Alt") + checkable: true + onTriggered: { + window.color = "transparent" + windowAgent.setWindowAttribute("dwm-blur", false) + windowAgent.setWindowAttribute("acrylic-material", false) + windowAgent.setWindowAttribute("mica", false) + windowAgent.setWindowAttribute("mica-alt", true) + } + } + } + } +} diff --git a/examples/qml/main.qml b/examples/qml/main.qml index dd4f284..7f203c4 100644 --- a/examples/qml/main.qml +++ b/examples/qml/main.qml @@ -1,255 +1,19 @@ import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 -import Qt.labs.platform 1.1 -import QWindowKit 1.0 -Window { - id: window - width: 800 - height: 600 - color: darkStyle.windowBackgroundColor - title: qsTr("Hello, world!") - Component.onCompleted: { - windowAgent.setup(window) - windowAgent.setWindowAttribute("dark-mode", true) - window.visible = true +FramelessWindow { + property FramelessWindow childWindow: FramelessWindow { + showWhenReady: false } - QtObject { - id: lightStyle - } - - QtObject { - id: darkStyle - readonly property color windowBackgroundColor: "#1E1E1E" - } - - Timer { - interval: 100 - running: true - repeat: true - onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") - } - - WindowAgent { - id: windowAgent - } - - TapHandler { - acceptedButtons: Qt.RightButton - onTapped: contextMenu.open() - } - - Rectangle { - id: titleBar + Button { anchors { - top: parent.top - left: parent.left - right: parent.right - } - height: 32 - //color: window.active ? "#3C3C3C" : "#505050" - color: "transparent" - Component.onCompleted: windowAgent.setTitleBar(titleBar) - - Image { - id: iconButton - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: 10 - } - width: 18 - height: 18 - mipmap: true - source: "qrc:///app/example.png" - fillMode: Image.PreserveAspectFit - Component.onCompleted: windowAgent.setSystemButton(WindowAgent.WindowIcon, iconButton) - } - - Text { - anchors { - verticalCenter: parent.verticalCenter - left: iconButton.right - leftMargin: 10 - } - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - text: window.title - font.pixelSize: 14 - color: "#ECECEC" - } - - Row { - anchors { - top: parent.top - right: parent.right - } - height: parent.height - - QWKButton { - id: minButton - height: parent.height - source: "qrc:///window-bar/minimize.svg" - onClicked: window.showMinimized() - Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Minimize, minButton) - } - - QWKButton { - id: maxButton - height: parent.height - source: window.visibility === Window.Maximized ? "qrc:///window-bar/restore.svg" : "qrc:///window-bar/maximize.svg" - onClicked: { - if (window.visibility === Window.Maximized) { - window.showNormal() - } else { - window.showMaximized() - } - } - Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Maximize, maxButton) - } - - QWKButton { - id: closeButton - height: parent.height - source: "qrc:///window-bar/close.svg" - background: Rectangle { - color: { - if (!closeButton.enabled) { - return "gray"; - } - if (closeButton.pressed) { - return "#e81123"; - } - if (closeButton.hovered) { - return "#e81123"; - } - return "transparent"; - } - } - onClicked: window.close() - Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Close, closeButton) - } - } - } - - Label { - id: timeLabel - anchors.centerIn: parent - font { - pointSize: 75 - bold: true - } - color: "#FEFEFE" - Component.onCompleted: { - if ($curveRenderingAvailable) { - console.log("Curve rendering for text is available.") - timeLabel.renderType = Text.CurveRendering - } - } - } - - Menu { - id: contextMenu - - Menu { - id: themeMenu - title: qsTr("Theme") - - MenuItemGroup { - id: themeMenuGroup - items: themeMenu.items - } - - MenuItem { - text: qsTr("Light") - checkable: true - onTriggered: windowAgent.setWindowAttribute("dark-mode", false) - } - - MenuItem { - text: qsTr("Dark") - checkable: true - checked: true - onTriggered: windowAgent.setWindowAttribute("dark-mode", true) - } - } - - Menu { - id: specialEffectMenu - title: qsTr("Special effect") - - MenuItemGroup { - id: specialEffectMenuGroup - items: specialEffectMenu.items - } - - MenuItem { - enabled: Qt.platform.os === "windows" - text: qsTr("None") - checkable: true - checked: true - onTriggered: { - window.color = darkStyle.windowBackgroundColor - windowAgent.setWindowAttribute("dwm-blur", false) - windowAgent.setWindowAttribute("acrylic-material", false) - windowAgent.setWindowAttribute("mica", false) - windowAgent.setWindowAttribute("mica-alt", false) - } - } - - MenuItem { - enabled: Qt.platform.os === "windows" - text: qsTr("DWM blur") - checkable: true - onTriggered: { - window.color = "transparent" - windowAgent.setWindowAttribute("acrylic-material", false) - windowAgent.setWindowAttribute("mica", false) - windowAgent.setWindowAttribute("mica-alt", false) - windowAgent.setWindowAttribute("dwm-blur", true) - } - } - - MenuItem { - enabled: Qt.platform.os === "windows" - text: qsTr("Acrylic material") - checkable: true - onTriggered: { - window.color = "transparent" - windowAgent.setWindowAttribute("dwm-blur", false) - windowAgent.setWindowAttribute("mica", false) - windowAgent.setWindowAttribute("mica-alt", false) - windowAgent.setWindowAttribute("acrylic-material", true) - } - } - - MenuItem { - enabled: Qt.platform.os === "windows" - text: qsTr("Mica") - checkable: true - onTriggered: { - window.color = "transparent" - windowAgent.setWindowAttribute("dwm-blur", false) - windowAgent.setWindowAttribute("acrylic-material", false) - windowAgent.setWindowAttribute("mica-alt", false) - windowAgent.setWindowAttribute("mica", true) - } - } - - MenuItem { - enabled: Qt.platform.os === "windows" - text: qsTr("Mica Alt") - checkable: true - onTriggered: { - window.color = "transparent" - windowAgent.setWindowAttribute("dwm-blur", false) - windowAgent.setWindowAttribute("acrylic-material", false) - windowAgent.setWindowAttribute("mica", false) - windowAgent.setWindowAttribute("mica-alt", true) - } - } + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: 20 } + text: qsTr("Open Child Window") + onClicked: childWindow.visible = true } } \ No newline at end of file diff --git a/examples/qml/qml.qrc b/examples/qml/qml.qrc index 865977e..7eaf353 100644 --- a/examples/qml/qml.qrc +++ b/examples/qml/qml.qrc @@ -2,5 +2,6 @@ main.qml QWKButton.qml + FramelessWindow.qml \ No newline at end of file diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 7ffd7da..e155d3d 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -1909,6 +1909,18 @@ namespace QWK { break; } + case WM_SHOWWINDOW: { + if (!wParam || !isWindowNoState(hWnd) || isFullScreen(hWnd)) { + break; + } + RECT windowRect{}; + ::GetWindowRect(hWnd, &windowRect); + static constexpr const auto swpFlags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOOWNERZORDER; + ::SetWindowPos(hWnd, nullptr, 0, 0, RECT_WIDTH(windowRect) + 1, RECT_HEIGHT(windowRect) + 1, swpFlags); + ::SetWindowPos(hWnd, nullptr, 0, 0, RECT_WIDTH(windowRect), RECT_HEIGHT(windowRect), swpFlags); + break; + } + default: break; } From ac1a62b2a76e4d15e412b9579b80da2dafbc6d6a Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: Mon, 19 May 2025 15:38:12 +0800 Subject: [PATCH 25/26] Update qmsetup --- qmsetup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmsetup b/qmsetup index e6e93ea..322f708 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit e6e93eaa57c4bfb7ec6e5b4571be3f9831b29c6e +Subproject commit 322f708a08c22db26da8e8dfaa2acac770e8f038 From 5c04046acae6e61280fba7fef31401edd7c36087 Mon Sep 17 00:00:00 2001 From: Dylan Liu <312902918@qq.com> Date: Fri, 23 May 2025 16:04:20 +0800 Subject: [PATCH 26/26] QWKCore/cocoawindowcontext: Use typeId() instead of type() in Qt 6.0+. (#181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用typeId()代替type()来消除在Qt 6.0+上出现的编译警告,因为type()已经被标记为弃用。 --- src/core/contexts/cocoawindowcontext.mm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/contexts/cocoawindowcontext.mm b/src/core/contexts/cocoawindowcontext.mm index 28738c6..dbf260e 100644 --- a/src/core/contexts/cocoawindowcontext.mm +++ b/src/core/contexts/cocoawindowcontext.mm @@ -745,7 +745,11 @@ static inline void releaseWindowProxy(const WId windowId) { Q_ASSERT(m_windowId); if (key == QStringLiteral("no-system-buttons")) { +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (attribute.type() != QVariant::Bool) +#else + if (attribute.typeId() != QMetaType::Type::Bool) +#endif return false; ensureWindowProxy(m_windowId)->setSystemButtonVisible(!attribute.toBool()); return true; @@ -753,14 +757,22 @@ static inline void releaseWindowProxy(const WId windowId) { if (key == QStringLiteral("blur-effect")) { auto mode = NSWindowProxy::BlurMode::None; +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (attribute.type() == QVariant::Bool) { +#else + if (attribute.typeId() == QMetaType::Type::Bool) { +#endif if (attribute.toBool()) { NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; mode = [osxMode isEqualToString:@"Dark"] ? NSWindowProxy::BlurMode::Dark : NSWindowProxy::BlurMode::Light; } +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) } else if (attribute.type() == QVariant::String) { +#else + } else if (attribute.typeId() == QMetaType::Type::QString) { +#endif auto value = attribute.toString(); if (value == QStringLiteral("dark")) { mode = NSWindowProxy::BlurMode::Dark;