diff --git a/README.md b/README.md index b8058c0..34343ce 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@ - Quick: Restored some 1.x interfaces which may be convenient for Qt Quick users. - Examples: Added QtWebEngine based demo projects for both Qt Widgets and Qt Quick. - Common: Added cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme. -- Common: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed. -- Common: Bug fix and internal refactoring, improved stability on all supported platforms. +- Misc: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed. +- Misc: Migrate to categorized logging output. +- Misc: Bug fixes and internal refactorings, improved stability on all supported platforms. ## Highlights compared to 1.x diff --git a/examples/example.manifest b/examples/example.manifest index 509e3fe..ad6bb95 100644 --- a/examples/example.manifest +++ b/examples/example.manifest @@ -25,6 +25,7 @@ + FramelessHelper Demo @@ -39,6 +40,8 @@ + + @@ -55,6 +58,11 @@ True/PM, True PerMonitorV2, PerMonitor, System + True + True + True + UTF-8 + SegmentHeap diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index d087611..c7519e7 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -28,6 +28,8 @@ set(SOURCES mainwindow.h mainwindow.cpp main.cpp + systembutton.h + systembutton.cpp ) if(WIN32) diff --git a/examples/mainwindow/TitleBar.ui b/examples/mainwindow/TitleBar.ui index 1e2f975..886ec9b 100644 --- a/examples/mainwindow/TitleBar.ui +++ b/examples/mainwindow/TitleBar.ui @@ -151,7 +151,7 @@ - + 0 @@ -188,7 +188,7 @@ - + 0 @@ -225,7 +225,7 @@ - + 0 @@ -264,6 +264,13 @@ + + + SystemButton + QPushButton +
systembutton.h
+
+
diff --git a/examples/mainwindow/systembutton.cpp b/examples/mainwindow/systembutton.cpp new file mode 100644 index 0000000..12725b3 --- /dev/null +++ b/examples/mainwindow/systembutton.cpp @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "systembutton.h" + +SystemButton::SystemButton(QWidget *parent) : QPushButton(parent) +{ +} + +SystemButton::~SystemButton() = default; + +void SystemButton::setHovered(const bool value) +{ + Q_UNUSED(value); + Q_UNIMPLEMENTED(); +} + +void SystemButton::setPressed(const bool value) +{ + Q_UNUSED(value); + Q_UNIMPLEMENTED(); +} diff --git a/examples/mainwindow/systembutton.h b/examples/mainwindow/systembutton.h new file mode 100644 index 0000000..efbb6a5 --- /dev/null +++ b/examples/mainwindow/systembutton.h @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +class SystemButton : public QPushButton +{ + Q_OBJECT + +public: + explicit SystemButton(QWidget *parent = nullptr); + ~SystemButton() override; + +public Q_SLOTS: + void setHovered(const bool value); + void setPressed(const bool value); +}; diff --git a/examples/openglwidget/CMakeLists.txt b/examples/openglwidget/CMakeLists.txt index e917d23..84014a4 100644 --- a/examples/openglwidget/CMakeLists.txt +++ b/examples/openglwidget/CMakeLists.txt @@ -39,6 +39,8 @@ set(SOURCES mainwindow.h mainwindow.cpp main.cpp + systembutton.h + systembutton.cpp ) if(WIN32) diff --git a/examples/openglwidget/mainwindow.cpp b/examples/openglwidget/mainwindow.cpp index da921fb..8e488e3 100644 --- a/examples/openglwidget/mainwindow.cpp +++ b/examples/openglwidget/mainwindow.cpp @@ -24,9 +24,9 @@ #include "mainwindow.h" #include "glwidget.h" +#include "systembutton.h" #include #include -#include FRAMELESSHELPER_USE_NAMESPACE @@ -51,16 +51,16 @@ void MainWindow::setupUi() f.setPointSize(kDefaultTitleBarFontPointSize); m_titleLabel->setFont(f); connect(this, &MainWindow::windowTitleChanged, m_titleLabel, &QLabel::setText); - m_minBtn = new QPushButton(this); + m_minBtn = new SystemButton(this); m_minBtn->setText(tr("MINIMIZE")); - connect(m_minBtn, &QPushButton::clicked, this, &MainWindow::showMinimized); - m_maxBtn = new QPushButton(this); + connect(m_minBtn, &SystemButton::clicked, this, &MainWindow::showMinimized); + m_maxBtn = new SystemButton(this); updateMaximizeButton(); - connect(m_maxBtn, &QPushButton::clicked, this, &MainWindow::toggleMaximized); + connect(m_maxBtn, &SystemButton::clicked, this, &MainWindow::toggleMaximized); connect(this, &MainWindow::zoomedChanged, this, &MainWindow::updateMaximizeButton); - m_closeBtn = new QPushButton(this); + m_closeBtn = new SystemButton(this); m_closeBtn->setText(tr("CLOSE")); - connect(m_closeBtn, &QPushButton::clicked, this, &MainWindow::close); + connect(m_closeBtn, &SystemButton::clicked, this, &MainWindow::close); m_titleBarWidget = new QWidget(this); m_titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_titleBarWidget->setFixedHeight(kDefaultTitleBarHeight); diff --git a/examples/openglwidget/mainwindow.h b/examples/openglwidget/mainwindow.h index 5277dea..17f8cf4 100644 --- a/examples/openglwidget/mainwindow.h +++ b/examples/openglwidget/mainwindow.h @@ -28,10 +28,10 @@ QT_BEGIN_NAMESPACE class QLabel; -class QPushButton; QT_END_NAMESPACE class GLWidget; +class SystemButton; class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget) { @@ -50,9 +50,9 @@ private: private: QLabel *m_titleLabel = nullptr; - QPushButton *m_minBtn = nullptr; - QPushButton *m_maxBtn = nullptr; - QPushButton *m_closeBtn = nullptr; + SystemButton *m_minBtn = nullptr; + SystemButton *m_maxBtn = nullptr; + SystemButton *m_closeBtn = nullptr; QWidget *m_titleBarWidget = nullptr; GLWidget *m_glWidget = nullptr; }; diff --git a/examples/openglwidget/systembutton.cpp b/examples/openglwidget/systembutton.cpp new file mode 100644 index 0000000..12725b3 --- /dev/null +++ b/examples/openglwidget/systembutton.cpp @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "systembutton.h" + +SystemButton::SystemButton(QWidget *parent) : QPushButton(parent) +{ +} + +SystemButton::~SystemButton() = default; + +void SystemButton::setHovered(const bool value) +{ + Q_UNUSED(value); + Q_UNIMPLEMENTED(); +} + +void SystemButton::setPressed(const bool value) +{ + Q_UNUSED(value); + Q_UNIMPLEMENTED(); +} diff --git a/examples/openglwidget/systembutton.h b/examples/openglwidget/systembutton.h new file mode 100644 index 0000000..efbb6a5 --- /dev/null +++ b/examples/openglwidget/systembutton.h @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +class SystemButton : public QPushButton +{ + Q_OBJECT + +public: + explicit SystemButton(QWidget *parent = nullptr); + ~SystemButton() override; + +public Q_SLOTS: + void setHovered(const bool value); + void setPressed(const bool value); +}; diff --git a/examples/quick/MainWindow.qml b/examples/quick/MainWindow.qml index 403fc35..7e6c00f 100644 --- a/examples/quick/MainWindow.qml +++ b/examples/quick/MainWindow.qml @@ -29,11 +29,12 @@ import org.wangwenx190.FramelessHelper 1.0 FramelessWindow { id: window - visible: true + visible: true // Default is false, so won't be visible unless explicitly set to true. width: 800 height: 600 - title: qsTr("Hello, World! - Qt Quick") - color: FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor + title: qsTr("FramelessHelper demo application - Qt Quick") + color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) + ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor Timer { interval: 500 @@ -49,7 +50,7 @@ FramelessWindow { pointSize: 70 bold: true } - color: FramelessUtils.darkModeEnabled ? "white" : "black" + color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? "white" : "black" } StandardTitleBar { @@ -75,15 +76,15 @@ FramelessWindow { } Component.onCompleted: { // Make our homemade title bar snap to the window top frame border. - window.snapToTopBorder(titleBar, FramelessHelper.Top, FramelessHelper.Bottom); + window.snapToTopBorder(titleBar, FramelessHelperConstants.Top, FramelessHelperConstants.Bottom); // Make our homemade title bar draggable, and open the system menu // when the user right clicks on the title bar area. - window.titleBarItem = titleBar; + FramelessHelper.titleBarItem = titleBar; // Make our own items visible to the hit test and on Windows, enable // the snap layout feature (available since Windows 11). - window.setSystemButton(minimizeButton, FramelessHelper.Minimize); - window.setSystemButton(maximizeButton, FramelessHelper.Maximize); - window.setSystemButton(closeButton, FramelessHelper.Close); + FramelessHelper.setSystemButton(minimizeButton, FramelessHelperConstants.Minimize); + FramelessHelper.setSystemButton(maximizeButton, FramelessHelperConstants.Maximize); + FramelessHelper.setSystemButton(closeButton, FramelessHelperConstants.Close); } } } diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index e2493d2..6604ae8 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include FRAMELESSHELPER_USE_NAMESPACE diff --git a/include/FramelessHelper/Quick/FramelessQuickModule b/include/FramelessHelper/Quick/FramelessQuickModule new file mode 100644 index 0000000..5027203 --- /dev/null +++ b/include/FramelessHelper/Quick/FramelessQuickModule @@ -0,0 +1 @@ +#include diff --git a/include/FramelessHelper/Quick/framelessquickhelper.h b/include/FramelessHelper/Quick/framelessquickhelper.h index dc24b86..a9a504c 100644 --- a/include/FramelessHelper/Quick/framelessquickhelper.h +++ b/include/FramelessHelper/Quick/framelessquickhelper.h @@ -25,33 +25,55 @@ #pragma once #include "framelesshelperquick_global.h" -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) -# include -#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - -QT_BEGIN_NAMESPACE -class QQmlEngine; -QT_END_NAMESPACE +#include FRAMELESSHELPER_BEGIN_NAMESPACE -namespace FramelessHelper::Quick -{ -FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine); -} // namespace FramelessHelper::Quick +class FramelessQuickHelperPrivate; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) -class FRAMELESSHELPER_QUICK_API FramelessHelperQuickPlugin : public QQmlEngineExtensionPlugin +class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem { Q_OBJECT - Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessHelper) +#endif + Q_DECLARE_PRIVATE(FramelessQuickHelper) + Q_DISABLE_COPY_MOVE(FramelessQuickHelper) + Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL) public: - explicit FramelessHelperQuickPlugin(QObject *parent = nullptr); - ~FramelessHelperQuickPlugin() override; + explicit FramelessQuickHelper(QQuickItem *parent = nullptr, const Global::UserSettings &settings = {}); + ~FramelessQuickHelper() override; - void initializeEngine(QQmlEngine *engine, const char *uri) override; + Q_NODISCARD static FramelessQuickHelper *qmlAttachedProperties(QObject *parentObject); + + Q_NODISCARD QQuickItem *titleBarItem() const; + Q_NODISCARD bool isWindowFixedSize() const; + +public Q_SLOTS: + void setTitleBarItem(QQuickItem *value); + void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); + void setHitTestVisible(QQuickItem *item); + + void showSystemMenu(const QPoint &pos); + void windowStartSystemMove2(const QPoint &pos); + void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); + + void moveWindowToDesktopCenter(); + void bringWindowToFront(); + void setWindowFixedSize(const bool value, const bool force = false); + +protected: + void itemChange(const ItemChange change, const ItemChangeData &data) override; + +Q_SIGNALS: + void titleBarItemChanged(); + +private: + QScopedPointer d_ptr; }; -#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) FRAMELESSHELPER_END_NAMESPACE + +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper)) +QML_DECLARE_TYPEINFO(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper), QML_HAS_ATTACHED_PROPERTIES) diff --git a/include/FramelessHelper/Quick/framelessquickmodule.h b/include/FramelessHelper/Quick/framelessquickmodule.h new file mode 100644 index 0000000..dc24b86 --- /dev/null +++ b/include/FramelessHelper/Quick/framelessquickmodule.h @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "framelesshelperquick_global.h" +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +# include +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + +QT_BEGIN_NAMESPACE +class QQmlEngine; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +namespace FramelessHelper::Quick +{ +FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine); +} // namespace FramelessHelper::Quick + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +class FRAMELESSHELPER_QUICK_API FramelessHelperQuickPlugin : public QQmlEngineExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) + +public: + explicit FramelessHelperQuickPlugin(QObject *parent = nullptr); + ~FramelessHelperQuickPlugin() override; + + void initializeEngine(QQmlEngine *engine, const char *uri) override; +}; +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + +FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Quick/framelessquickutils.h b/include/FramelessHelper/Quick/framelessquickutils.h index 0867603..b7f96b4 100644 --- a/include/FramelessHelper/Quick/framelessquickutils.h +++ b/include/FramelessHelper/Quick/framelessquickutils.h @@ -31,7 +31,6 @@ QT_BEGIN_NAMESPACE class QQuickWindow; -class QQuickItem; QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE @@ -49,7 +48,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL) Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL) Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL) - Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL) + Q_PROPERTY(QuickGlobal::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL) Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL) Q_PROPERTY(bool titleBarColorized READ titleBarColorized NOTIFY titleBarColorizedChanged FINAL) Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL) @@ -63,24 +62,24 @@ public: explicit FramelessQuickUtils(QObject *parent = nullptr); ~FramelessQuickUtils() override; - Q_NODISCARD static qreal titleBarHeight(); - Q_NODISCARD static bool frameBorderVisible(); - Q_NODISCARD static qreal frameBorderThickness(); - Q_NODISCARD static bool darkModeEnabled(); - Q_NODISCARD static QColor systemAccentColor(); - Q_NODISCARD static bool titleBarColorized(); - Q_NODISCARD static QColor defaultSystemLightColor(); - Q_NODISCARD static QColor defaultSystemDarkColor(); - Q_NODISCARD static QSizeF defaultSystemButtonSize(); - Q_NODISCARD static QSizeF defaultSystemButtonIconSize(); - Q_NODISCARD static QColor defaultSystemButtonBackgroundColor(); - Q_NODISCARD static QColor defaultSystemCloseButtonBackgroundColor(); + Q_NODISCARD qreal titleBarHeight() const; + Q_NODISCARD bool frameBorderVisible() const; + Q_NODISCARD qreal frameBorderThickness() const; + Q_NODISCARD QuickGlobal::SystemTheme systemTheme() const; + Q_NODISCARD QColor systemAccentColor() const; + Q_NODISCARD bool titleBarColorized() const; + Q_NODISCARD QColor defaultSystemLightColor() const; + Q_NODISCARD QColor defaultSystemDarkColor() const; + Q_NODISCARD QSizeF defaultSystemButtonSize() const; + Q_NODISCARD QSizeF defaultSystemButtonIconSize() const; + Q_NODISCARD QColor defaultSystemButtonBackgroundColor() const; + Q_NODISCARD QColor defaultSystemCloseButtonBackgroundColor() const; - Q_NODISCARD Q_INVOKABLE static QColor getSystemButtonBackgroundColor( + Q_NODISCARD Q_INVOKABLE QColor getSystemButtonBackgroundColor( const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); Q_SIGNALS: - void darkModeEnabledChanged(); + void systemThemeChanged(); void systemAccentColorChanged(); void titleBarColorizedChanged(); }; diff --git a/include/FramelessHelper/Quick/framelessquickwindow.h b/include/FramelessHelper/Quick/framelessquickwindow.h index dd99ea3..e43a764 100644 --- a/include/FramelessHelper/Quick/framelessquickwindow.h +++ b/include/FramelessHelper/Quick/framelessquickwindow.h @@ -25,7 +25,6 @@ #pragma once #include "framelesshelperquick_global.h" -#include #include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -45,10 +44,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL) Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) Q_PROPERTY(bool fullScreen READ isFullScreen NOTIFY fullScreenChanged FINAL) - Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL) Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL) - Q_PROPERTY(QuickGlobal::Options options READ options WRITE setOptions NOTIFY optionsChanged FINAL) - Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL) public: explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::UserSettings &settings = {}); @@ -59,30 +55,13 @@ public: Q_NODISCARD bool isMinimized() const; Q_NODISCARD bool isZoomed() const; Q_NODISCARD bool isFullScreen() const; - - Q_NODISCARD bool isFixedSize() const; - void setFixedSize(const bool value); - Q_NODISCARD QColor frameBorderColor() const; - Q_NODISCARD QuickGlobal::Options options() const; - void setOptions(const QuickGlobal::Options value); - - Q_NODISCARD QQuickItem *titleBarItem() const; - void setTitleBarItem(QQuickItem *item); - public Q_SLOTS: void showMinimized2(); void toggleMaximized(); void toggleFullScreen(); - void showSystemMenu(const QPoint &pos); - void startSystemMove2(const QPoint &pos); - void startSystemResize2(const Qt::Edges edges, const QPoint &pos); - void setHitTestVisible(QQuickItem *item); - void moveToDesktopCenter(); - void bringToFront(); void snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor); - void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); Q_SIGNALS: void hiddenChanged(); @@ -90,11 +69,7 @@ Q_SIGNALS: void minimizedChanged(); void zoomedChanged(); void fullScreenChanged(); - void fixedSizeChanged(); void frameBorderColorChanged(); - void systemButtonStateChanged(const QuickGlobal::SystemButtonType, const QuickGlobal::ButtonState); - void optionsChanged(); - void titleBarItemChanged(); private: QScopedPointer d_ptr; diff --git a/include/FramelessHelper/Widgets/framelessmainwindow.h b/include/FramelessHelper/Widgets/framelessmainwindow.h index b8fabd9..4e7e5ec 100644 --- a/include/FramelessHelper/Widgets/framelessmainwindow.h +++ b/include/FramelessHelper/Widgets/framelessmainwindow.h @@ -72,7 +72,6 @@ Q_SIGNALS: void fixedSizeChanged(); void titleBarWidgetChanged(); void systemThemeChanged(); - void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState); private: QScopedPointer d_ptr; diff --git a/include/FramelessHelper/Widgets/framelesswidget.h b/include/FramelessHelper/Widgets/framelesswidget.h index 4408696..2bd132e 100644 --- a/include/FramelessHelper/Widgets/framelesswidget.h +++ b/include/FramelessHelper/Widgets/framelesswidget.h @@ -77,7 +77,6 @@ Q_SIGNALS: void titleBarWidgetChanged(); void contentWidgetChanged(); void systemThemeChanged(); - void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState); private: QScopedPointer d_ptr; diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index d6bf320..2905841 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "framelesswindowsmanager.h" #include "framelesswindowsmanager_p.h" @@ -58,7 +59,6 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) FRAMELESSHELPER_BYTEARRAY_CONSTANT2(Win32MessageTypeName, "windows_generic_MSG") static const QString qThemeSettingChangeEventName = QString::fromWCharArray(kThemeSettingChangeEventName); -static constexpr const wchar_t kDragBarWindowClassName[] = L"FRAMELESSHELPER@DRAG_BAR_WINDOW_CLASS"; FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow) FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW) FRAMELESSHELPER_STRING_CONSTANT(ScreenToClient) @@ -140,6 +140,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) if (data.params.isInsideSystemButtons(qtScenePos, &buttonType)) { switch (buttonType) { case SystemButtonType::Unknown: + Q_ASSERT(false); break; case SystemButtonType::WindowIcon: return HTSYSMENU; @@ -156,7 +157,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) } // The parent window has quite some logic in the hit test handler, we // should forward this message to the parent window and return what it - // returns to make sure our homemade titlebar is still functional. + // returns to make sure our homemade title bar is still functional. return SendMessageW(parentWindowHandle, WM_NCHITTEST, 0, lParam); } case WM_NCMOUSEMOVE: { @@ -197,7 +198,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) } // If we haven't previously asked for mouse tracking, request mouse // tracking. We need to do this so we can get the WM_NCMOUSELEAVE - // message when the mouse leave the titlebar. Otherwise, we won't always + // message when the mouse leave the title bar. Otherwise, we won't always // get that message (especially if the user moves the mouse _real // fast_). if (!data.trackingMouse && ((wParam == HTSYSMENU) || (wParam == HTHELP) @@ -233,7 +234,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) case WM_NCLBUTTONDOWN: case WM_NCLBUTTONDBLCLK: { // Manual handling for mouse clicks in the drag bar. If it's in a - // caption button, then tell the titlebar to "press" the button, which + // caption button, then tell the title bar to "press" the button, which // should change its visual state. // // If it's not in a caption button, then just forward the message along @@ -309,7 +310,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) } // Forward all the mouse events we don't handled here to the parent window, // this is a necessary step to make sure the child widgets/quick items can still - // receive mouse events from our homemade titlebar. + // receive mouse events from our homemade title bar. if (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) || ((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))) { SendMessageW(parentWindowHandle, uMsg, wParam, lParam); @@ -332,6 +333,12 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) } const int titleBarHeight = Utils::getTitleBarHeight(parentWindowId, true); const auto dragBarWindowHandle = reinterpret_cast(dragBarWindowId); + // As you can see from the code, we only use the drag bar window to activate the + // snap layout feature introduced in Windows 11. So you may wonder, why not just + // limit it to the rectangle of the three system buttons, instead of covering the + // whole title bar area? Well, I've tried that solution already and unfortunately + // it doesn't work. Since our current solution works well, I have no plan to dig + // into all the magic behind it. if (SetWindowPos(dragBarWindowHandle, HWND_TOP, 0, 0, parentWindowClientRect.right, titleBarHeight, (SWP_NOACTIVATE | SWP_SHOWWINDOW)) == FALSE) { qWarning() << Utils::getSystemErrorMessage(kSetWindowPos); @@ -358,12 +365,18 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) qWarning() << Utils::getSystemErrorMessage(kGetModuleHandleW); return false; } + static const QString dragBarWindowClassName = QUuid::createUuid().toString(); + Q_ASSERT(!dragBarWindowClassName.isEmpty()); + if (dragBarWindowClassName.isEmpty()) { + qWarning() << "Failed to generate a new UUID."; + return false; + } static const ATOM dragBarWindowClass = [instance]() -> ATOM { WNDCLASSEXW wcex; SecureZeroMemory(&wcex, sizeof(wcex)); wcex.cbSize = sizeof(wcex); wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS); - wcex.lpszClassName = kDragBarWindowClassName; + wcex.lpszClassName = qUtf16Printable(dragBarWindowClassName); wcex.hbrBackground = static_cast(GetStockObject(BLACK_BRUSH)); wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); wcex.lpfnWndProc = DragBarWindowProc; @@ -376,7 +389,8 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) return false; } const HWND dragBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP), - kDragBarWindowClassName, nullptr, WS_CHILD, 0, 0, 0, 0, parentWindowHandle, nullptr, instance, nullptr); + qUtf16Printable(dragBarWindowClassName), nullptr, WS_CHILD, 0, 0, 0, 0, + parentWindowHandle, nullptr, instance, nullptr); Q_ASSERT(dragBarWindowHandle); if (!dragBarWindowHandle) { qWarning() << Utils::getSystemErrorMessage(kCreateWindowExW); diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt index 3ad8bce..b4c3b21 100644 --- a/src/quick/CMakeLists.txt +++ b/src/quick/CMakeLists.txt @@ -22,8 +22,8 @@ SOFTWARE. ]] -find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Qml QuickTemplates2 QuickControls2) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Qml QuickTemplates2 QuickControls2) +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS QuickTemplates2 QuickControls2) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS QuickTemplates2 QuickControls2) set(SUB_PROJ_NAME FramelessHelperQuick) @@ -32,9 +32,10 @@ set(INCLUDE_PREFIX ../../include/FramelessHelper/Quick) set(SOURCES framelesshelperquick.qrc ${INCLUDE_PREFIX}/framelesshelperquick_global.h - ${INCLUDE_PREFIX}/framelessquickutils.h - ${INCLUDE_PREFIX}/framelessquickhelper.h + ${INCLUDE_PREFIX}/framelessquickmodule.h ${INCLUDE_PREFIX}/framelessquickwindow.h + ${INCLUDE_PREFIX}/framelessquickhelper.h + ${INCLUDE_PREFIX}/framelessquickutils.h quickstandardminimizebutton_p.h quickstandardminimizebutton.cpp quickstandardmaximizebutton_p.h @@ -43,10 +44,12 @@ set(SOURCES quickstandardclosebutton.cpp quickstandardtitlebar_p.h quickstandardtitlebar.cpp + framelessquickhelper_p.h framelessquickwindow_p.h - framelessquickhelper.cpp framelessquickutils.cpp + framelessquickmodule.cpp framelessquickwindow.cpp + framelessquickhelper.cpp ) if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC) @@ -91,7 +94,7 @@ if(MSVC) _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} ) target_compile_options(${SUB_PROJ_NAME} PRIVATE - /utf-8 /W4 /WX + /utf-8 /W3 /WX # Can't use /W4 here due to the Q_D macro causes warnings. ) else() target_compile_options(${SUB_PROJ_NAME} PRIVATE @@ -100,7 +103,6 @@ else() endif() target_link_libraries(${SUB_PROJ_NAME} PRIVATE - Qt${QT_VERSION_MAJOR}::QmlPrivate Qt${QT_VERSION_MAJOR}::QuickPrivate Qt${QT_VERSION_MAJOR}::QuickTemplates2Private Qt${QT_VERSION_MAJOR}::QuickControls2Private diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 1a756da..a092e42 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -23,97 +23,641 @@ */ #include "framelessquickhelper.h" -#include "framelessquickutils.h" -#include "framelessquickwindow.h" -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) -# include "quickstandardminimizebutton_p.h" -# include "quickstandardmaximizebutton_p.h" -# include "quickstandardclosebutton_p.h" -# include "quickstandardtitlebar_p.h" -#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - -#ifndef QUICK_URI_SHORT -# define QUICK_URI_SHORT FRAMELESSHELPER_QUICK_URI, 1 -#endif - -#ifndef QUICK_URI_FULL -# define QUICK_URI_FULL QUICK_URI_SHORT, 0 -#endif - -#ifndef QUICK_URI_EXPAND -# define QUICK_URI_EXPAND(name) QUICK_URI_FULL, name -#endif +#include "framelessquickhelper_p.h" +#include +#include // For QWINDOWSIZE_MAX +#include +#include +#include +#include FRAMELESSHELPER_BEGIN_NAMESPACE -void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) +using namespace Global; + +struct QuickHelperData { - Q_ASSERT(engine); - if (!engine) { + bool attached = false; + UserSettings settings = {}; + SystemParameters params = {}; + QPointer titleBarItem = nullptr; + QList hitTestVisibleItems = {}; +}; + +struct QuickHelper +{ + QMutex mutex; + QHash data = {}; +}; + +Q_GLOBAL_STATIC(QuickHelper, g_quickHelper) + +static constexpr const char QTQUICK_ITEM_CLASS_NAME[] = "QQuickItem"; +static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton"; + +FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q, const UserSettings &settings) : QObject(q) +{ + Q_ASSERT(q); + if (!q) { return; } - static bool inited = false; - if (inited) { - return; - } - inited = true; - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qmlRegisterUncreatableType(QUICK_URI_FULL, "FramelessHelper", - FRAMELESSHELPER_STRING_LITERAL("The FramelessHelper namespace is not creatable, you can only use it to access it's enums.")); - qmlRegisterSingletonType(QUICK_URI_EXPAND("FramelessUtils"), - [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine); - Q_UNUSED(scriptEngine); - return new FramelessQuickUtils; - }); - qmlRegisterRevision(QUICK_URI_FULL); - qmlRegisterRevision(QUICK_URI_FULL); - qmlRegisterRevision(QUICK_URI_FULL); - qmlRegisterType(QUICK_URI_EXPAND("FramelessWindow")); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - qmlRegisterType(QUICK_URI_EXPAND("StandardMinimizeButton")); - qmlRegisterType(QUICK_URI_EXPAND("StandardMaximizeButton")); - qmlRegisterType(QUICK_URI_EXPAND("StandardCloseButton")); - qmlRegisterType(QUICK_URI_EXPAND("StandardTitleBar")); -#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMinimizeButton"), - FRAMELESSHELPER_STRING_LITERAL("StandardMinimizeButton is not available until Qt6.")); - qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMaximizeButton"), - FRAMELESSHELPER_STRING_LITERAL("StandardMaximizeButton is not available until Qt6.")); - qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardCloseButton"), - FRAMELESSHELPER_STRING_LITERAL("StandardCloseButton is not available until Qt6.")); - qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardTitleBar"), - FRAMELESSHELPER_STRING_LITERAL("StandardTitleBar is not available until Qt6.")); -#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - qmlRegisterModule(QUICK_URI_FULL); + q_ptr = q; + m_cachedSettings = settings; } -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) -FramelessHelperQuickPlugin::FramelessHelperQuickPlugin(QObject *parent) : QQmlEngineExtensionPlugin(parent) +FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate() = default; + +FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(FramelessQuickHelper *pub) +{ + Q_ASSERT(pub); + if (!pub) { + return nullptr; + } + return pub->d_func(); +} + +const FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(const FramelessQuickHelper *pub) +{ + Q_ASSERT(pub); + if (!pub) { + return nullptr; + } + return pub->d_func(); +} + +QQuickItem *FramelessQuickHelperPrivate::getTitleBarItem() const +{ + return getWindowData().titleBarItem; +} + +void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value) +{ + Q_ASSERT(value); + if (!value) { + return; + } + QMutexLocker locker(&g_quickHelper()->mutex); + QuickHelperData *data = getWindowDataMutable(); + if (data->titleBarItem == value) { + return; + } + data->titleBarItem = value; + Q_Q(FramelessQuickHelper); + Q_EMIT q->titleBarItemChanged(); +} + +void FramelessQuickHelperPrivate::attachToWindow() +{ + Q_Q(FramelessQuickHelper); + QQuickWindow * const window = q->window(); + Q_ASSERT(window); + if (!window) { + return; + } + + g_quickHelper()->mutex.lock(); + QuickHelperData *data = getWindowDataMutable(); + const bool attached = data->attached; + g_quickHelper()->mutex.unlock(); + + if (attached) { + return; + } + + UserSettings settings = m_cachedSettings; + m_cachedSettings = {}; + + SystemParameters params = {}; + params.getWindowId = [window]() -> WId { return window->winId(); }; + params.getWindowFlags = [window]() -> Qt::WindowFlags { return window->flags(); }; + params.setWindowFlags = [window](const Qt::WindowFlags flags) -> void { window->setFlags(flags); }; + params.getWindowSize = [window]() -> QSize { return window->size(); }; + params.setWindowSize = [window](const QSize &size) -> void { window->resize(size); }; + params.getWindowPosition = [window]() -> QPoint { return window->position(); }; + params.setWindowPosition = [window](const QPoint &pos) -> void { window->setX(pos.x()); window->setY(pos.y()); }; + params.getWindowScreen = [window]() -> QScreen * { return window->screen(); }; + params.isWindowFixedSize = [this]() -> bool { return isWindowFixedSize(); }; + params.setWindowFixedSize = [this](const bool value) -> void { setWindowFixedSize(value); }; + params.getWindowState = [window]() -> Qt::WindowState { return window->windowState(); }; + params.setWindowState = [window](const Qt::WindowState state) -> void { window->setWindowState(state); }; + params.getWindowHandle = [q]() -> QWindow * { return q->window(); }; + params.windowToScreen = [window](const QPoint &pos) -> QPoint { return window->mapToGlobal(pos); }; + params.screenToWindow = [window](const QPoint &pos) -> QPoint { return window->mapFromGlobal(pos); }; + params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { + QuickGlobal::SystemButtonType button2 = QuickGlobal::SystemButtonType::Unknown; + const bool result = isInSystemButtons(pos, &button2); + *button = FRAMELESSHELPER_ENUM_QUICK_TO_CORE(SystemButtonType, button2); + return result; + }; + params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); }; + params.getWindowDevicePixelRatio = [window]() -> qreal { return window->effectiveDevicePixelRatio(); }; + params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void { + setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button), + FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state)); + }; + params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); }; + params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); }; + + g_quickHelper()->mutex.lock(); + data->params = params; + data->attached = true; + g_quickHelper()->mutex.unlock(); + + FramelessWindowsManager::instance()->addWindow(settings, params); +} + +void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType) +{ + Q_ASSERT(item); + Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown); + if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) { + return; + } + QMutexLocker locker(&g_quickHelper()->mutex); + QuickHelperData *data = getWindowDataMutable(); + switch (buttonType) { + case QuickGlobal::SystemButtonType::Unknown: + Q_ASSERT(false); + break; + case QuickGlobal::SystemButtonType::WindowIcon: + data->settings.windowIconButton = item; + break; + case QuickGlobal::SystemButtonType::Help: + data->settings.contextHelpButton = item; + break; + case QuickGlobal::SystemButtonType::Minimize: + data->settings.minimizeButton = item; + break; + case QuickGlobal::SystemButtonType::Maximize: + case QuickGlobal::SystemButtonType::Restore: + data->settings.maximizeButton = item; + break; + case QuickGlobal::SystemButtonType::Close: + data->settings.closeButton = item; + break; + } +} + +void FramelessQuickHelperPrivate::setHitTestVisible(QQuickItem *item) +{ + Q_ASSERT(item); + if (!item) { + return; + } + QMutexLocker locker(&g_quickHelper()->mutex); + QuickHelperData *data = getWindowDataMutable(); + static constexpr const bool visible = true; + const bool exists = data->hitTestVisibleItems.contains(item); + if (visible && !exists) { + data->hitTestVisibleItems.append(item); + } + if constexpr (!visible && exists) { + data->hitTestVisibleItems.removeAll(item); + } +} + +void FramelessQuickHelperPrivate::showSystemMenu(const QPoint &pos) +{ +#ifdef Q_OS_WINDOWS + Q_Q(FramelessQuickHelper); + const QQuickWindow * const window = q->window(); + if (!window) { + return; + } + const QPoint globalPos = window->mapToGlobal(pos); + const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint(); + Utils::showSystemMenu(window->winId(), nativePos, {}, false, {}, [this]() -> bool { return isWindowFixedSize(); }); +#else + Q_UNUSED(pos); +#endif +} + +void FramelessQuickHelperPrivate::windowStartSystemMove2(const QPoint &pos) +{ + Q_Q(FramelessQuickHelper); + QQuickWindow * const window = q->window(); + if (!window) { + return; + } + Utils::startSystemMove(window, pos); +} + +void FramelessQuickHelperPrivate::windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos) +{ + Q_Q(FramelessQuickHelper); + QQuickWindow * const window = q->window(); + if (!window) { + return; + } + if (edges == Qt::Edges{}) { + return; + } + Utils::startSystemResize(window, edges, pos); +} + +void FramelessQuickHelperPrivate::moveWindowToDesktopCenter() +{ + Q_Q(FramelessQuickHelper); + QQuickWindow * const window = q->window(); + if (!window) { + return; + } + Utils::moveWindowToDesktopCenter([window]() -> QScreen * { return window->screen(); }, + [window]() -> QSize { return window->size(); }, + [window](const QPoint &pos) -> void { window->setX(pos.x()); window->setY(pos.y()); }, true); +} + +void FramelessQuickHelperPrivate::bringWindowToFront() +{ + Q_Q(FramelessQuickHelper); + QQuickWindow * const window = q->window(); + if (!window) { + return; + } + const QQuickWindow::Visibility visibility = window->visibility(); + if (visibility == QQuickWindow::Hidden) { + window->show(); + } + if (visibility == QQuickWindow::Minimized) { + window->showNormal(); // ### FIXME + } + window->raise(); + window->requestActivate(); +} + +bool FramelessQuickHelperPrivate::isWindowFixedSize() const +{ + const QuickHelperData data = getWindowData(); + if (data.settings.options & Option::DisableResizing) { + return true; + } + Q_Q(const FramelessQuickHelper); + const QQuickWindow * const window = q->window(); + if (!window) { + return false; + } + if (window->flags() & Qt::MSWindowsFixedSizeDialogHint) { + return true; + } + const QSize minSize = window->minimumSize(); + const QSize maxSize = window->maximumSize(); + if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) { + return true; + } + return false; +} + +void FramelessQuickHelperPrivate::setWindowFixedSize(const bool value, const bool force) +{ + if ((isWindowFixedSize() == value) && !force) { + return; + } + Q_Q(FramelessQuickHelper); + QQuickWindow * const window = q->window(); + if (!window) { + return; + } + if (value) { + const QSize size = window->size(); + window->setMinimumSize(size); + window->setMaximumSize(size); + window->setFlags(window->flags() | Qt::MSWindowsFixedSizeDialogHint); + } else { + window->setFlags(window->flags() & ~Qt::MSWindowsFixedSizeDialogHint); + window->setMinimumSize(kDefaultWindowSize); + window->setMaximumSize(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX)); + } +#ifdef Q_OS_WINDOWS + Utils::setAeroSnappingEnabled(window->winId(), !value); +#endif +} + +QRect FramelessQuickHelperPrivate::mapItemGeometryToScene(const QQuickItem * const item) const +{ + Q_ASSERT(item); + if (!item) { + return {}; + } + const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0)); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + const QSizeF size = item->size(); +#else + const QSizeF size = {item->width(), item->height()}; +#endif + return QRectF(originPoint, size).toRect(); +} + +bool FramelessQuickHelperPrivate::isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const +{ + Q_ASSERT(button); + if (!button) { + return false; + } + *button = QuickGlobal::SystemButtonType::Unknown; + const QuickHelperData data = getWindowData(); + if (data.settings.windowIconButton && data.settings.windowIconButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto iconBtn = qobject_cast(data.settings.windowIconButton); + if (mapItemGeometryToScene(iconBtn).contains(pos)) { + *button = QuickGlobal::SystemButtonType::WindowIcon; + return true; + } + } + if (data.settings.contextHelpButton && data.settings.contextHelpButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto helpBtn = qobject_cast(data.settings.contextHelpButton); + if (mapItemGeometryToScene(helpBtn).contains(pos)) { + *button = QuickGlobal::SystemButtonType::Help; + return true; + } + } + if (data.settings.minimizeButton && data.settings.minimizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto minBtn = qobject_cast(data.settings.minimizeButton); + if (mapItemGeometryToScene(minBtn).contains(pos)) { + *button = QuickGlobal::SystemButtonType::Minimize; + return true; + } + } + if (data.settings.maximizeButton && data.settings.maximizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto maxBtn = qobject_cast(data.settings.maximizeButton); + if (mapItemGeometryToScene(maxBtn).contains(pos)) { + *button = QuickGlobal::SystemButtonType::Maximize; + return true; + } + } + if (data.settings.closeButton && data.settings.closeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto closeBtn = qobject_cast(data.settings.closeButton); + if (mapItemGeometryToScene(closeBtn).contains(pos)) { + *button = QuickGlobal::SystemButtonType::Close; + return true; + } + } + return false; +} + +bool FramelessQuickHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos) const +{ + const QuickHelperData data = getWindowData(); + if (!data.titleBarItem) { + return false; + } + QRegion region = mapItemGeometryToScene(data.titleBarItem); + const auto systemButtons = {data.settings.windowIconButton, data.settings.contextHelpButton, + data.settings.minimizeButton, data.settings.maximizeButton, data.settings.closeButton}; + for (auto &&button : qAsConst(systemButtons)) { + if (button && button->inherits(QTQUICK_ITEM_CLASS_NAME)) { + const auto quickButton = qobject_cast(button); + region -= mapItemGeometryToScene(quickButton); + } + } + if (!data.hitTestVisibleItems.isEmpty()) { + for (auto &&item : qAsConst(data.hitTestVisibleItems)) { + Q_ASSERT(item); + if (item) { + region -= mapItemGeometryToScene(item); + } + } + } + return region.contains(pos); +} + +bool FramelessQuickHelperPrivate::shouldIgnoreMouseEvents(const QPoint &pos) const +{ + Q_Q(const FramelessQuickHelper); + const QQuickWindow * const window = q->window(); + if (!window) { + return false; + } + const bool withinFrameBorder = [&pos, window]() -> bool { + if (pos.y() < kDefaultResizeBorderThickness) { + return true; + } +#ifdef Q_OS_WINDOWS + if (Utils::isWindowFrameBorderVisible()) { + return false; + } +#endif + return ((pos.x() < kDefaultResizeBorderThickness) + || (pos.x() >= (window->width() - kDefaultResizeBorderThickness))); + }(); + return ((window->visibility() == QQuickWindow::Windowed) && withinFrameBorder); +} + +void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button, + const QuickGlobal::ButtonState state) +{ + Q_ASSERT(button != QuickGlobal::SystemButtonType::Unknown); + if (button == QuickGlobal::SystemButtonType::Unknown) { + return; + } + const QuickHelperData data = getWindowData(); + QQuickAbstractButton *quickButton = nullptr; + switch (button) { + case QuickGlobal::SystemButtonType::Unknown: { + Q_ASSERT(false); + } break; + case QuickGlobal::SystemButtonType::WindowIcon: { + if (data.settings.windowIconButton && data.settings.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(data.settings.windowIconButton); + } + } break; + case QuickGlobal::SystemButtonType::Help: { + if (data.settings.contextHelpButton && data.settings.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(data.settings.contextHelpButton); + } + } break; + case QuickGlobal::SystemButtonType::Minimize: { + if (data.settings.minimizeButton && data.settings.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(data.settings.minimizeButton); + } + } break; + case QuickGlobal::SystemButtonType::Maximize: + case QuickGlobal::SystemButtonType::Restore: { + if (data.settings.maximizeButton && data.settings.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(data.settings.maximizeButton); + } + } break; + case QuickGlobal::SystemButtonType::Close: { + if (data.settings.closeButton && data.settings.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { + quickButton = qobject_cast(data.settings.closeButton); + } + } break; + } + if (quickButton) { + const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void { + Q_ASSERT(btn); + if (!btn) { + return; + } + switch (state) { + case QuickGlobal::ButtonState::Unspecified: { + btn->setPressed(false); + btn->setHovered(false); + } break; + case QuickGlobal::ButtonState::Hovered: { + btn->setPressed(false); + btn->setHovered(true); + } break; + case QuickGlobal::ButtonState::Pressed: { + btn->setHovered(true); + btn->setPressed(true); + } break; + case QuickGlobal::ButtonState::Clicked: { + // Clicked: pressed --> released, so behave like hovered. + btn->setPressed(false); + btn->setHovered(true); + // "QQuickAbstractButtonPrivate::click()"'s implementation is nothing but + // emits the "clicked" signal of the public interface, so we just emit + // the signal directly to avoid accessing the private implementation. + Q_EMIT btn->clicked(); + } break; + } + }; + updateButtonState(quickButton); + } +} + +QuickHelperData FramelessQuickHelperPrivate::getWindowData() const +{ + Q_Q(const FramelessQuickHelper); + const QQuickWindow * const window = q->window(); + Q_ASSERT(window); + if (!window) { + return {}; + } + const WId windowId = window->winId(); + QMutexLocker locker(&g_quickHelper()->mutex); + if (!g_quickHelper()->data.contains(windowId)) { + g_quickHelper()->data.insert(windowId, {}); + } + return g_quickHelper()->data.value(windowId); +} + +QuickHelperData *FramelessQuickHelperPrivate::getWindowDataMutable() const +{ + Q_Q(const FramelessQuickHelper); + const QQuickWindow * const window = q->window(); + Q_ASSERT(window); + if (!window) { + return nullptr; + } + const WId windowId = window->winId(); + if (!g_quickHelper()->data.contains(windowId)) { + g_quickHelper()->data.insert(windowId, {}); + } + return &g_quickHelper()->data[windowId]; +} + +FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent, const UserSettings &settings) + : QQuickItem(parent), d_ptr(new FramelessQuickHelperPrivate(this, settings)) { } -FramelessHelperQuickPlugin::~FramelessHelperQuickPlugin() = default; +FramelessQuickHelper::~FramelessQuickHelper() = default; -void FramelessHelperQuickPlugin::initializeEngine(QQmlEngine *engine, const char *uri) +FramelessQuickHelper *FramelessQuickHelper::qmlAttachedProperties(QObject *parentObject) { - Q_ASSERT(engine); - Q_ASSERT(uri); - if (!engine || !uri) { - return; + Q_ASSERT(parentObject); + if (!parentObject) { + return nullptr; } - Q_ASSERT(QLatin1String(uri) == QLatin1String(FRAMELESSHELPER_QUICK_URI)); - if (QLatin1String(uri) != QLatin1String(FRAMELESSHELPER_QUICK_URI)) { - return; + const auto item = new FramelessQuickHelper; + const auto parentItem = qobject_cast(parentObject); + if (parentItem) { + item->setParentItem(parentItem); + } else { + item->setParent(parentObject); + } + return item; +} + +QQuickItem *FramelessQuickHelper::titleBarItem() const +{ + Q_D(const FramelessQuickHelper); + return d->getTitleBarItem(); +} + +bool FramelessQuickHelper::isWindowFixedSize() const +{ + Q_D(const FramelessQuickHelper); + return d->isWindowFixedSize(); +} + +void FramelessQuickHelper::setTitleBarItem(QQuickItem *value) +{ + Q_ASSERT(value); + if (!value) { + return; + } + Q_D(FramelessQuickHelper); + d->setTitleBarItem(value); +} + +void FramelessQuickHelper::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType) +{ + Q_ASSERT(item); + Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown); + if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) { + return; + } + Q_D(FramelessQuickHelper); + d->setSystemButton(item, buttonType); +} + +void FramelessQuickHelper::setHitTestVisible(QQuickItem *item) +{ + Q_ASSERT(item); + if (!item) { + return; + } + Q_D(FramelessQuickHelper); + d->setHitTestVisible(item); +} + +void FramelessQuickHelper::showSystemMenu(const QPoint &pos) +{ + Q_D(FramelessQuickHelper); + d->showSystemMenu(pos); +} + +void FramelessQuickHelper::windowStartSystemMove2(const QPoint &pos) +{ + Q_D(FramelessQuickHelper); + d->windowStartSystemMove2(pos); +} + +void FramelessQuickHelper::windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos) +{ + if (edges == Qt::Edges{}) { + return; + } + Q_D(FramelessQuickHelper); + d->windowStartSystemResize2(edges, pos); +} + +void FramelessQuickHelper::moveWindowToDesktopCenter() +{ + Q_D(FramelessQuickHelper); + d->moveWindowToDesktopCenter(); +} + +void FramelessQuickHelper::bringWindowToFront() +{ + Q_D(FramelessQuickHelper); + d->bringWindowToFront(); +} + +void FramelessQuickHelper::setWindowFixedSize(const bool value, const bool force) +{ + Q_D(FramelessQuickHelper); + d->setWindowFixedSize(value, force); +} + +void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeData &data) +{ + QQuickItem::itemChange(change, data); + if ((change == FramelessQuickHelper::ItemSceneChange) && data.window) { + Q_D(FramelessQuickHelper); + d->attachToWindow(); } - FramelessHelper::Quick::registerTypes(engine); } -#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickhelper_p.h b/src/quick/framelessquickhelper_p.h new file mode 100644 index 0000000..fc55bb6 --- /dev/null +++ b/src/quick/framelessquickhelper_p.h @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "framelesshelperquick_global.h" +#include + +QT_BEGIN_NAMESPACE +class QQuickItem; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +struct QuickHelperData; +class FramelessQuickHelper; + +class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject +{ + Q_OBJECT + Q_DECLARE_PUBLIC(FramelessQuickHelper) + Q_DISABLE_COPY_MOVE(FramelessQuickHelperPrivate) + +public: + explicit FramelessQuickHelperPrivate(FramelessQuickHelper *q, const Global::UserSettings &settings = {}); + ~FramelessQuickHelperPrivate() override; + + Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub); + Q_NODISCARD static const FramelessQuickHelperPrivate *get(const FramelessQuickHelper *pub); + + Q_NODISCARD QQuickItem *getTitleBarItem() const; + void setTitleBarItem(QQuickItem *value); + + void attachToWindow(); + void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); + void setHitTestVisible(QQuickItem *item); + void showSystemMenu(const QPoint &pos); + void windowStartSystemMove2(const QPoint &pos); + void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); + + void moveWindowToDesktopCenter(); + void bringWindowToFront(); + + Q_NODISCARD bool isWindowFixedSize() const; + void setWindowFixedSize(const bool value, const bool force = false); + +private: + Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; + Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const; + Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; + Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; + void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); + Q_NODISCARD QuickHelperData getWindowData() const; + Q_NODISCARD QuickHelperData *getWindowDataMutable() const; + +private: + FramelessQuickHelper *q_ptr = nullptr; + Global::UserSettings m_cachedSettings = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickmodule.cpp b/src/quick/framelessquickmodule.cpp new file mode 100644 index 0000000..794ed39 --- /dev/null +++ b/src/quick/framelessquickmodule.cpp @@ -0,0 +1,121 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "framelessquickmodule.h" +#include "framelessquickhelper.h" +#include "framelessquickutils.h" +#include "framelessquickwindow.h" +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +# include "quickstandardminimizebutton_p.h" +# include "quickstandardmaximizebutton_p.h" +# include "quickstandardclosebutton_p.h" +# include "quickstandardtitlebar_p.h" +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + +#ifndef QUICK_URI_SHORT +# define QUICK_URI_SHORT FRAMELESSHELPER_QUICK_URI, 1 +#endif + +#ifndef QUICK_URI_FULL +# define QUICK_URI_FULL QUICK_URI_SHORT, 0 +#endif + +#ifndef QUICK_URI_EXPAND +# define QUICK_URI_EXPAND(name) QUICK_URI_FULL, name +#endif + +FRAMELESSHELPER_BEGIN_NAMESPACE + +void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) +{ + Q_ASSERT(engine); + if (!engine) { + return; + } + static bool inited = false; + if (inited) { + return; + } + inited = true; + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qmlRegisterUncreatableType(QUICK_URI_FULL, "FramelessHelperConstants", + FRAMELESSHELPER_STRING_LITERAL("The FramelessHelperConstants namespace is not creatable, you can only use it to access it's enums.")); + qmlRegisterSingletonType(QUICK_URI_EXPAND("FramelessUtils"), + [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + return new FramelessQuickUtils; + }); + qmlRegisterRevision(QUICK_URI_FULL); + qmlRegisterRevision(QUICK_URI_FULL); + qmlRegisterRevision(QUICK_URI_FULL); + qmlRegisterType(QUICK_URI_EXPAND("FramelessHelper")); + qmlRegisterType(QUICK_URI_EXPAND("FramelessWindow")); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + qmlRegisterType(QUICK_URI_EXPAND("StandardMinimizeButton")); + qmlRegisterType(QUICK_URI_EXPAND("StandardMaximizeButton")); + qmlRegisterType(QUICK_URI_EXPAND("StandardCloseButton")); + qmlRegisterType(QUICK_URI_EXPAND("StandardTitleBar")); +#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMinimizeButton"), + FRAMELESSHELPER_STRING_LITERAL("StandardMinimizeButton is not available until Qt6.")); + qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMaximizeButton"), + FRAMELESSHELPER_STRING_LITERAL("StandardMaximizeButton is not available until Qt6.")); + qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardCloseButton"), + FRAMELESSHELPER_STRING_LITERAL("StandardCloseButton is not available until Qt6.")); + qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardTitleBar"), + FRAMELESSHELPER_STRING_LITERAL("StandardTitleBar is not available until Qt6.")); +#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + qmlRegisterModule(QUICK_URI_FULL); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) +FramelessHelperQuickPlugin::FramelessHelperQuickPlugin(QObject *parent) : QQmlEngineExtensionPlugin(parent) +{ +} + +FramelessHelperQuickPlugin::~FramelessHelperQuickPlugin() = default; + +void FramelessHelperQuickPlugin::initializeEngine(QQmlEngine *engine, const char *uri) +{ + Q_ASSERT(engine); + Q_ASSERT(uri); + if (!engine || !uri) { + return; + } + Q_ASSERT(qstrcmp(uri, FRAMELESSHELPER_QUICK_URI) == 0); + if (qstrcmp(uri, FRAMELESSHELPER_QUICK_URI) != 0) { + return; + } + FramelessHelper::Quick::registerTypes(engine); +} +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + +FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickmodule.h b/src/quick/framelessquickmodule.h new file mode 100644 index 0000000..f2e4126 --- /dev/null +++ b/src/quick/framelessquickmodule.h @@ -0,0 +1,25 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "../../include/FramelessHelper/Quick/framelessquickmodule.h" diff --git a/src/quick/framelessquickutils.cpp b/src/quick/framelessquickutils.cpp index 8d08b0a..7e284d1 100644 --- a/src/quick/framelessquickutils.cpp +++ b/src/quick/framelessquickutils.cpp @@ -33,7 +33,7 @@ using namespace Global; FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent) { connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this](){ - Q_EMIT darkModeEnabledChanged(); + Q_EMIT systemThemeChanged(); Q_EMIT systemAccentColorChanged(); Q_EMIT titleBarColorizedChanged(); }); @@ -41,12 +41,12 @@ FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent) FramelessQuickUtils::~FramelessQuickUtils() = default; -qreal FramelessQuickUtils::titleBarHeight() +qreal FramelessQuickUtils::titleBarHeight() const { return kDefaultTitleBarHeight; } -bool FramelessQuickUtils::frameBorderVisible() +bool FramelessQuickUtils::frameBorderVisible() const { #ifdef Q_OS_WINDOWS return (Utils::isWindowFrameBorderVisible() && !Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2)); @@ -55,7 +55,7 @@ bool FramelessQuickUtils::frameBorderVisible() #endif } -qreal FramelessQuickUtils::frameBorderThickness() +qreal FramelessQuickUtils::frameBorderThickness() const { #ifdef Q_OS_WINDOWS return kDefaultWindowFrameBorderThickness; @@ -64,12 +64,12 @@ qreal FramelessQuickUtils::frameBorderThickness() #endif } -bool FramelessQuickUtils::darkModeEnabled() +QuickGlobal::SystemTheme FramelessQuickUtils::systemTheme() const { - return Utils::shouldAppsUseDarkMode(); + return FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemTheme, Utils::getSystemTheme()); } -QColor FramelessQuickUtils::systemAccentColor() +QColor FramelessQuickUtils::systemAccentColor() const { #ifdef Q_OS_WINDOWS return Utils::getDwmColorizationColor(); @@ -82,37 +82,37 @@ QColor FramelessQuickUtils::systemAccentColor() #endif } -bool FramelessQuickUtils::titleBarColorized() +bool FramelessQuickUtils::titleBarColorized() const { return Utils::isTitleBarColorized(); } -QColor FramelessQuickUtils::defaultSystemLightColor() +QColor FramelessQuickUtils::defaultSystemLightColor() const { return kDefaultSystemLightColor; } -QColor FramelessQuickUtils::defaultSystemDarkColor() +QColor FramelessQuickUtils::defaultSystemDarkColor() const { return kDefaultSystemDarkColor; } -QSizeF FramelessQuickUtils::defaultSystemButtonSize() +QSizeF FramelessQuickUtils::defaultSystemButtonSize() const { return kDefaultSystemButtonSize; } -QSizeF FramelessQuickUtils::defaultSystemButtonIconSize() +QSizeF FramelessQuickUtils::defaultSystemButtonIconSize() const { return kDefaultSystemButtonIconSize; } -QColor FramelessQuickUtils::defaultSystemButtonBackgroundColor() +QColor FramelessQuickUtils::defaultSystemButtonBackgroundColor() const { return kDefaultSystemButtonBackgroundColor; } -QColor FramelessQuickUtils::defaultSystemCloseButtonBackgroundColor() +QColor FramelessQuickUtils::defaultSystemCloseButtonBackgroundColor() const { return kDefaultSystemCloseButtonBackgroundColor; } diff --git a/src/quick/framelessquickwindow.cpp b/src/quick/framelessquickwindow.cpp index f633ee8..0979827 100644 --- a/src/quick/framelessquickwindow.cpp +++ b/src/quick/framelessquickwindow.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -35,67 +34,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -static constexpr const char QTQUICK_ITEM_CLASS_NAME[] = "QQuickItem"; -static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton"; - -[[nodiscard]] static inline QuickGlobal::Options optionsCoreToQuick(const Options value) -{ - QuickGlobal::Options result = {}; - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, ForceHideWindowFrameBorder, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, ForceShowWindowFrameBorder, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontDrawTopWindowFrameBorder, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontForceSquareWindowCorners, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, TransparentWindowBackground, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableWindowsSnapLayout, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, CreateStandardWindowLayout, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, BeCompatibleWithQtFramelessWindowHint, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchQtInternals, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchWindowFrameBorderColor, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontInstallSystemMenuHook, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableSystemMenu, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, NoDoubleClickMaximizeToggle, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableResizing, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableDragging, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchCursorShape, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontMoveWindowToDesktopCenter, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTreatFullScreenAsZoomed, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchHighDpiScalingPolicy, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchScaleFactorRoundingPolicy, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchProcessDpiAwarenessLevel, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontEnsureNonNativeWidgetSiblings, value, result) - FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, SyncNativeControlsThemeWithSystem, value, result) - return result; -} - -[[nodiscard]] static inline Options optionsQuickToCore(const QuickGlobal::Options value) -{ - Options result = {}; - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, ForceHideWindowFrameBorder, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, ForceShowWindowFrameBorder, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontDrawTopWindowFrameBorder, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontForceSquareWindowCorners, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, TransparentWindowBackground, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableWindowsSnapLayout, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, CreateStandardWindowLayout, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, BeCompatibleWithQtFramelessWindowHint, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchQtInternals, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchWindowFrameBorderColor, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontInstallSystemMenuHook, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableSystemMenu, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, NoDoubleClickMaximizeToggle, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableResizing, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableDragging, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchCursorShape, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontMoveWindowToDesktopCenter, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTreatFullScreenAsZoomed, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchHighDpiScalingPolicy, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchScaleFactorRoundingPolicy, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchProcessDpiAwarenessLevel, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontEnsureNonNativeWidgetSiblings, value, result) - FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, SyncNativeControlsThemeWithSystem, value, result) - return result; -} - FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const UserSettings &settings) : QObject(q) { Q_ASSERT(q); @@ -160,23 +98,6 @@ bool FramelessQuickWindowPrivate::isFullScreen() const return (q->visibility() == FramelessQuickWindow::FullScreen); } -bool FramelessQuickWindowPrivate::isFixedSize() const -{ - if (m_settings.options & Option::DisableResizing) { - return true; - } - Q_Q(const FramelessQuickWindow); - if (q->flags() & Qt::MSWindowsFixedSizeDialogHint) { - return true; - } - const QSize minSize = q->minimumSize(); - const QSize maxSize = q->maximumSize(); - if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) { - return true; - } - return false; -} - QColor FramelessQuickWindowPrivate::getFrameBorderColor() const { #ifdef Q_OS_WINDOWS @@ -217,77 +138,6 @@ QQuickAnchorLine FramelessQuickWindowPrivate::getTopBorderVerticalCenter() const return QQuickAnchorLine(m_topBorderRectangle.data(), QQuickAnchors::VCenterAnchor); } -void FramelessQuickWindowPrivate::setTitleBarItem(QQuickItem *item) -{ - Q_ASSERT(item); - if (!item) { - return; - } - if (m_titleBarItem == item) { - return; - } - m_titleBarItem = item; - Q_Q(FramelessQuickWindow); - Q_EMIT q->titleBarItemChanged(); -} - -void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item) -{ - Q_ASSERT(item); - if (!item) { - return; - } - static constexpr const bool visible = true; - const bool exists = m_hitTestVisibleItems.contains(item); - if (visible && !exists) { - m_hitTestVisibleItems.append(item); - } - if constexpr (!visible && exists) { - m_hitTestVisibleItems.removeAll(item); - } -} - -void FramelessQuickWindowPrivate::moveToDesktopCenter() -{ - Utils::moveWindowToDesktopCenter(m_params.getWindowScreen, m_params.getWindowSize, - m_params.setWindowPosition, true); -} - -void FramelessQuickWindowPrivate::setFixedSize(const bool value, const bool force) -{ - if ((isFixedSize() == value) && !force) { - return; - } - Q_Q(FramelessQuickWindow); - if (value) { - const QSize size = q->size(); - q->setMinimumSize(size); - q->setMaximumSize(size); - q->setFlags(q->flags() | Qt::MSWindowsFixedSizeDialogHint); - } else { - q->setFlags(q->flags() & ~Qt::MSWindowsFixedSizeDialogHint); - q->setMinimumSize(kDefaultWindowSize); - q->setMaximumSize(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX)); - } -#ifdef Q_OS_WINDOWS - Utils::setAeroSnappingEnabled(q->winId(), !value); -#endif - Q_EMIT q->fixedSizeChanged(); -} - -void FramelessQuickWindowPrivate::bringToFront() -{ - Q_Q(FramelessQuickWindow); - if (isHidden()) { - q->show(); - } - if (isMinimized()) { - q->showNormal(); // ### FIXME !!! - } - q->raise(); - q->requestActivate(); -} - void FramelessQuickWindowPrivate::snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor) { Q_ASSERT(item); @@ -340,74 +190,6 @@ void FramelessQuickWindowPrivate::snapToTopBorder(QQuickItem *item, const QuickG } } -void FramelessQuickWindowPrivate::setOptions(const QuickGlobal::Options value) -{ - Q_Q(FramelessQuickWindow); - if (m_quickOptions == value) { - return; - } - // ### TODO: re-evaluate some usable options. - m_quickOptions = value; - m_settings.options = optionsQuickToCore(m_quickOptions); - Q_EMIT q->optionsChanged(); -} - -void FramelessQuickWindowPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType) -{ - Q_ASSERT(item); - Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown); - if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) { - return; - } - switch (buttonType) { - case QuickGlobal::SystemButtonType::Unknown: - Q_ASSERT(false); - break; - case QuickGlobal::SystemButtonType::WindowIcon: - m_settings.windowIconButton = item; - break; - case QuickGlobal::SystemButtonType::Help: - m_settings.contextHelpButton = item; - break; - case QuickGlobal::SystemButtonType::Minimize: - m_settings.minimizeButton = item; - break; - case QuickGlobal::SystemButtonType::Maximize: - case QuickGlobal::SystemButtonType::Restore: - m_settings.maximizeButton = item; - break; - case QuickGlobal::SystemButtonType::Close: - m_settings.closeButton = item; - break; - } -} - -bool FramelessQuickWindowPrivate::eventFilter(QObject *object, QEvent *event) -{ - Q_ASSERT(object); - Q_ASSERT(event); - if (!object || !event) { - return false; - } - if (!object->isWindowType()) { - return QObject::eventFilter(object, event); - } - Q_Q(FramelessQuickWindow); - const auto window = qobject_cast(object); - if (window != q) { - return QObject::eventFilter(object, event); - } - switch (event->type()) { - case QEvent::Show: { - const auto showEvent = static_cast(event); - showEventHandler(showEvent); - } break; - default: - break; - } - return QObject::eventFilter(object, event); -} - void FramelessQuickWindowPrivate::showMinimized2() { Q_Q(FramelessQuickWindow); @@ -424,9 +206,6 @@ void FramelessQuickWindowPrivate::showMinimized2() void FramelessQuickWindowPrivate::toggleMaximized() { - if (isFixedSize()) { - return; - } Q_Q(FramelessQuickWindow); if (isZoomed()) { q->showNormal(); @@ -437,9 +216,6 @@ void FramelessQuickWindowPrivate::toggleMaximized() void FramelessQuickWindowPrivate::toggleFullScreen() { - if (isFixedSize()) { - return; - } Q_Q(FramelessQuickWindow); if (isFullScreen()) { q->setVisibility(m_savedVisibility); @@ -449,79 +225,12 @@ void FramelessQuickWindowPrivate::toggleFullScreen() } } -void FramelessQuickWindowPrivate::showSystemMenu(const QPoint &pos) -{ -#ifdef Q_OS_WINDOWS - Q_Q(FramelessQuickWindow); - const QPoint globalPos = q->mapToGlobal(pos); - const QPoint nativePos = QPointF(QPointF(globalPos) * q->effectiveDevicePixelRatio()).toPoint(); - Utils::showSystemMenu(q->winId(), nativePos, m_settings.systemMenuOffset, - false, m_settings.options, m_params.isWindowFixedSize); -#else - Q_UNUSED(pos); -#endif -} - -void FramelessQuickWindowPrivate::startSystemMove2(const QPoint &pos) -{ - Q_Q(FramelessQuickWindow); - Utils::startSystemMove(q, pos); -} - -void FramelessQuickWindowPrivate::startSystemResize2(const Qt::Edges edges, const QPoint &pos) -{ - if (isFixedSize()) { - return; - } - if (edges == Qt::Edges{}) { - return; - } - Q_Q(FramelessQuickWindow); - Utils::startSystemResize(q, edges, pos); -} - void FramelessQuickWindowPrivate::initialize() { Q_Q(FramelessQuickWindow); - m_params.getWindowId = [q]() -> WId { return q->winId(); }; - m_params.getWindowFlags = [q]() -> Qt::WindowFlags { return q->flags(); }; - m_params.setWindowFlags = [q](const Qt::WindowFlags flags) -> void { q->setFlags(flags); }; - m_params.getWindowSize = [q]() -> QSize { return q->size(); }; - m_params.setWindowSize = [q](const QSize &size) -> void { q->resize(size); }; - m_params.getWindowPosition = [q]() -> QPoint { return q->position(); }; - m_params.setWindowPosition = [q](const QPoint &pos) -> void { q->setX(pos.x()); q->setY(pos.y()); }; - m_params.getWindowScreen = [q]() -> QScreen * { return q->screen(); }; - m_params.isWindowFixedSize = [this]() -> bool { return isFixedSize(); }; - m_params.setWindowFixedSize = [this](const bool value) -> void { setFixedSize(value); }; - m_params.getWindowState = [q]() -> Qt::WindowState { return q->windowState(); }; - m_params.setWindowState = [q](const Qt::WindowState state) -> void { q->setWindowState(state); }; - m_params.getWindowHandle = [q]() -> QWindow * { return q; }; - m_params.windowToScreen = [q](const QPoint &pos) -> QPoint { return q->mapToGlobal(pos); }; - m_params.screenToWindow = [q](const QPoint &pos) -> QPoint { return q->mapFromGlobal(pos); }; - m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { - QuickGlobal::SystemButtonType button2 = QuickGlobal::SystemButtonType::Unknown; - const bool result = isInSystemButtons(pos, &button2); - *button = FRAMELESSHELPER_ENUM_QUICK_TO_CORE(SystemButtonType, button2); - return result; - }; - m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); }; - m_params.getWindowDevicePixelRatio = [q]() -> qreal { return q->effectiveDevicePixelRatio(); }; - m_params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void { - setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button), - FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state)); - }; - m_params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); }; - m_params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); }; - if (m_settings.options & Option::DisableResizing) { - setFixedSize(true, true); - } if (m_settings.options & Option::TransparentWindowBackground) { q->setColor(kDefaultTransparentColor); } - m_quickOptions = optionsCoreToQuick(m_settings.options); - FramelessWindowsManager * const manager = FramelessWindowsManager::instance(); - manager->addWindow(m_settings, m_params); - q->installEventFilter(this); QQuickItem * const rootItem = q->contentItem(); const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem); m_topBorderRectangle.reset(new QQuickRectangle(rootItem)); @@ -545,115 +254,12 @@ void FramelessQuickWindowPrivate::initialize() Q_EMIT q->fullScreenChanged(); }); connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor); - connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this, q](){ + connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this, q](){ updateTopBorderColor(); Q_EMIT q->frameBorderColorChanged(); }); } -QRect FramelessQuickWindowPrivate::mapItemGeometryToScene(const QQuickItem * const item) const -{ - Q_ASSERT(item); - if (!item) { - return {}; - } - const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0)); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) - const QSizeF size = item->size(); -#else - const QSizeF size = {item->width(), item->height()}; -#endif - return QRectF(originPoint, size).toRect(); -} - -bool FramelessQuickWindowPrivate::isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const -{ - Q_ASSERT(button); - if (!button) { - return false; - } - *button = QuickGlobal::SystemButtonType::Unknown; - if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { - const auto iconBtn = qobject_cast(m_settings.windowIconButton); - if (mapItemGeometryToScene(iconBtn).contains(pos)) { - *button = QuickGlobal::SystemButtonType::WindowIcon; - return true; - } - } - if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { - const auto helpBtn = qobject_cast(m_settings.contextHelpButton); - if (mapItemGeometryToScene(helpBtn).contains(pos)) { - *button = QuickGlobal::SystemButtonType::Help; - return true; - } - } - if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { - const auto minBtn = qobject_cast(m_settings.minimizeButton); - if (mapItemGeometryToScene(minBtn).contains(pos)) { - *button = QuickGlobal::SystemButtonType::Minimize; - return true; - } - } - if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { - const auto maxBtn = qobject_cast(m_settings.maximizeButton); - if (mapItemGeometryToScene(maxBtn).contains(pos)) { - *button = QuickGlobal::SystemButtonType::Maximize; - return true; - } - } - if (m_settings.closeButton && m_settings.closeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) { - const auto closeBtn = qobject_cast(m_settings.closeButton); - if (mapItemGeometryToScene(closeBtn).contains(pos)) { - *button = QuickGlobal::SystemButtonType::Close; - return true; - } - } - return false; -} - -bool FramelessQuickWindowPrivate::isInTitleBarDraggableArea(const QPoint &pos) const -{ - if (!m_titleBarItem) { - return false; - } - QRegion region = mapItemGeometryToScene(m_titleBarItem); - const auto systemButtons = {m_settings.windowIconButton, m_settings.contextHelpButton, - m_settings.minimizeButton, m_settings.maximizeButton, m_settings.closeButton}; - for (auto &&button : qAsConst(systemButtons)) { - if (button && button->inherits(QTQUICK_ITEM_CLASS_NAME)) { - const auto quickButton = qobject_cast(button); - region -= mapItemGeometryToScene(quickButton); - } - } - if (!m_hitTestVisibleItems.isEmpty()) { - for (auto &&item : qAsConst(m_hitTestVisibleItems)) { - Q_ASSERT(item); - if (item) { - region -= mapItemGeometryToScene(item); - } - } - } - return region.contains(pos); -} - -bool FramelessQuickWindowPrivate::shouldIgnoreMouseEvents(const QPoint &pos) const -{ - Q_Q(const FramelessQuickWindow); - const bool withinFrameBorder = [&pos, q]() -> bool { - if (pos.y() < kDefaultResizeBorderThickness) { - return true; - } -#ifdef Q_OS_WINDOWS - if (Utils::isWindowFrameBorderVisible()) { - return false; - } -#endif - return ((pos.x() < kDefaultResizeBorderThickness) - || (pos.x() >= (q->width() - kDefaultResizeBorderThickness))); - }(); - return (isNormal() && withinFrameBorder); -} - bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const { #ifdef Q_OS_WINDOWS @@ -664,120 +270,6 @@ bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const #endif } -void FramelessQuickWindowPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button, - const QuickGlobal::ButtonState state) -{ - Q_ASSERT(button != QuickGlobal::SystemButtonType::Unknown); - if (button == QuickGlobal::SystemButtonType::Unknown) { - return; - } - QQuickAbstractButton *quickButton = nullptr; - switch (button) { - case QuickGlobal::SystemButtonType::Unknown: { - Q_ASSERT(false); - } break; - case QuickGlobal::SystemButtonType::WindowIcon: { - if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { - quickButton = qobject_cast(m_settings.windowIconButton); - } - } break; - case QuickGlobal::SystemButtonType::Help: { - if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { - quickButton = qobject_cast(m_settings.contextHelpButton); - } - } break; - case QuickGlobal::SystemButtonType::Minimize: { - if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { - quickButton = qobject_cast(m_settings.minimizeButton); - } - } break; - case QuickGlobal::SystemButtonType::Maximize: - case QuickGlobal::SystemButtonType::Restore: { - if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { - quickButton = qobject_cast(m_settings.maximizeButton); - } - } break; - case QuickGlobal::SystemButtonType::Close: { - if (m_settings.closeButton && m_settings.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) { - quickButton = qobject_cast(m_settings.closeButton); - } - } break; - } - if (quickButton) { - const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void { - Q_ASSERT(btn); - if (!btn) { - return; - } - switch (state) { - case QuickGlobal::ButtonState::Unspecified: { - btn->setDown(false); - btn->setPressed(false); - btn->setHovered(false); - } break; - case QuickGlobal::ButtonState::Hovered: { - btn->setDown(false); - btn->setPressed(false); - btn->setHovered(true); - } break; - case QuickGlobal::ButtonState::Pressed: { - btn->setHovered(true); - btn->setDown(true); - btn->setPressed(true); - } break; - case QuickGlobal::ButtonState::Clicked: { - // Clicked: pressed --> released, so behave like hovered. - btn->setDown(false); - btn->setPressed(false); - btn->setHovered(true); - // "QQuickAbstractButtonPrivate::click()"'s implementation is nothing but - // emits the "clicked" signal of the public interface, so we just emit - // the signal directly to avoid accessing the private implementation. - Q_EMIT btn->clicked(); - } break; - } - }; - updateButtonState(quickButton); - } - Q_Q(FramelessQuickWindow); - Q_EMIT q->systemButtonStateChanged(button, state); -} - -void FramelessQuickWindowPrivate::showEventHandler(QShowEvent *event) -{ - Q_ASSERT(event); - if (!event) { - return; - } - if (m_windowExposed) { - return; - } - m_windowExposed = true; - if (m_settings.options & Option::DontMoveWindowToDesktopCenter) { - if (!m_settings.startupPosition.isNull()) { - m_params.setWindowPosition(m_settings.startupPosition); - } - if (!m_settings.startupSize.isEmpty()) { - m_params.setWindowSize(m_settings.startupSize); - } - if (m_settings.startupState != Qt::WindowNoState) { - m_params.setWindowState(m_settings.startupState); - } - } else { - moveToDesktopCenter(); - } -} - -QuickGlobal::Options FramelessQuickWindowPrivate::getOptions() const -{ - return m_quickOptions; -} - -QQuickItem *FramelessQuickWindowPrivate::getTitleBarItem() const -{ - return m_titleBarItem; -} - void FramelessQuickWindowPrivate::updateTopBorderColor() { #ifdef Q_OS_WINDOWS @@ -799,9 +291,9 @@ void FramelessQuickWindowPrivate::updateTopBorderHeight() #endif } -FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings) : QQuickWindow(parent) +FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings) + : QQuickWindow(parent), d_ptr(new FramelessQuickWindowPrivate(this, settings)) { - d_ptr.reset(new FramelessQuickWindowPrivate(this, settings)); } FramelessQuickWindow::~FramelessQuickWindow() = default; @@ -836,74 +328,12 @@ bool FramelessQuickWindow::isFullScreen() const return d->isFullScreen(); } -bool FramelessQuickWindow::isFixedSize() const -{ - Q_D(const FramelessQuickWindow); - return d->isFixedSize(); -} - -void FramelessQuickWindow::setFixedSize(const bool value) -{ - Q_D(FramelessQuickWindow); - d->setFixedSize(value); -} - QColor FramelessQuickWindow::frameBorderColor() const { Q_D(const FramelessQuickWindow); return d->getFrameBorderColor(); } -QuickGlobal::Options FramelessQuickWindow::options() const -{ - Q_D(const FramelessQuickWindow); - return d->getOptions(); -} - -void FramelessQuickWindow::setOptions(const QuickGlobal::Options value) -{ - Q_D(FramelessQuickWindow); - d->setOptions(value); -} - -QQuickItem *FramelessQuickWindow::titleBarItem() const -{ - Q_D(const FramelessQuickWindow); - return d->getTitleBarItem(); -} - -void FramelessQuickWindow::setTitleBarItem(QQuickItem *item) -{ - Q_ASSERT(item); - if (!item) { - return; - } - Q_D(FramelessQuickWindow); - d->setTitleBarItem(item); -} - -void FramelessQuickWindow::setHitTestVisible(QQuickItem *item) -{ - Q_ASSERT(item); - if (!item) { - return; - } - Q_D(FramelessQuickWindow); - d->setHitTestVisible(item); -} - -void FramelessQuickWindow::moveToDesktopCenter() -{ - Q_D(FramelessQuickWindow); - d->moveToDesktopCenter(); -} - -void FramelessQuickWindow::bringToFront() -{ - Q_D(FramelessQuickWindow); - d->bringToFront(); -} - void FramelessQuickWindow::snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor) { Q_ASSERT(item); @@ -914,17 +344,6 @@ void FramelessQuickWindow::snapToTopBorder(QQuickItem *item, const QuickGlobal:: d->snapToTopBorder(item, itemAnchor, topBorderAnchor); } -void FramelessQuickWindow::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType) -{ - Q_ASSERT(item); - Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown); - if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) { - return; - } - Q_D(FramelessQuickWindow); - d->setSystemButton(item, buttonType); -} - void FramelessQuickWindow::showMinimized2() { Q_D(FramelessQuickWindow); @@ -943,25 +362,4 @@ void FramelessQuickWindow::toggleFullScreen() d->toggleFullScreen(); } -void FramelessQuickWindow::showSystemMenu(const QPoint &pos) -{ - Q_D(FramelessQuickWindow); - d->showSystemMenu(pos); -} - -void FramelessQuickWindow::startSystemMove2(const QPoint &pos) -{ - Q_D(FramelessQuickWindow); - d->startSystemMove2(pos); -} - -void FramelessQuickWindow::startSystemResize2(const Qt::Edges edges, const QPoint &pos) -{ - if (edges == Qt::Edges{}) { - return; - } - Q_D(FramelessQuickWindow); - d->startSystemResize2(edges, pos); -} - FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickwindow_p.h b/src/quick/framelessquickwindow_p.h index 208f3b2..88819eb 100644 --- a/src/quick/framelessquickwindow_p.h +++ b/src/quick/framelessquickwindow_p.h @@ -26,7 +26,7 @@ #include "framelesshelperquick_global.h" #include -#include +#include #include QT_BEGIN_NAMESPACE @@ -55,7 +55,6 @@ public: Q_INVOKABLE Q_NODISCARD bool isMinimized() const; Q_INVOKABLE Q_NODISCARD bool isZoomed() const; Q_INVOKABLE Q_NODISCARD bool isFullScreen() const; - Q_INVOKABLE Q_NODISCARD bool isFixedSize() const; Q_INVOKABLE Q_NODISCARD QColor getFrameBorderColor() const; Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderTop() const; @@ -65,39 +64,15 @@ public: Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderHorizontalCenter() const; Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderVerticalCenter() const; - Q_INVOKABLE void showEventHandler(QShowEvent *event); - - Q_INVOKABLE Q_NODISCARD QuickGlobal::Options getOptions() const; - - Q_INVOKABLE Q_NODISCARD QQuickItem *getTitleBarItem() const; - public Q_SLOTS: void showMinimized2(); void toggleMaximized(); void toggleFullScreen(); - void showSystemMenu(const QPoint &pos); - void startSystemMove2(const QPoint &pos); - void startSystemResize2(const Qt::Edges edges, const QPoint &pos); - void setTitleBarItem(QQuickItem *item); - void setHitTestVisible(QQuickItem *item); - void moveToDesktopCenter(); - void setFixedSize(const bool value, const bool force = false); - void bringToFront(); void snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor); - void setOptions(const QuickGlobal::Options value); - void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType); - -protected: - Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; private: void initialize(); - Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; - Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const; - Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; - Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; Q_NODISCARD bool shouldDrawFrameBorder() const; - void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state); private Q_SLOTS: void updateTopBorderColor(); @@ -107,13 +82,9 @@ private: FramelessQuickWindow *q_ptr = nullptr; QScopedPointer m_topBorderRectangle; QScopedPointer m_topBorderAnchors; - QWindow::Visibility m_savedVisibility = QWindow::Windowed; + QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed; Global::UserSettings m_settings = {}; - Global::SystemParameters m_params = {}; bool m_windowExposed = false; - QPointer m_titleBarItem = nullptr; - QList m_hitTestVisibleItems = {}; - QuickGlobal::Options m_quickOptions = {}; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index dd12a82..26add96 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -39,7 +39,6 @@ using namespace Global; static constexpr const char FRAMELESSHELPER_PROP_NAME[] = "__wwx190_FramelessWidgetsHelper_instance"; static constexpr const char QTWIDGETS_MAINWINDOW_CLASS_NAME[] = "QMainWindow"; -static constexpr const char FRAMELESSHELPER_SYSTEMBUTTON_CLASS_NAME[] = "StandardSystemButton"; FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetColorTemplate, "color: %1;") FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetBackgroundColorTemplate, "background-color: %1;") @@ -602,8 +601,6 @@ void FramelessWidgetsHelper::setSystemButtonState(const SystemButtonType button, }; updateButtonState(widgetButton); } - QMetaObject::invokeMethod(q, "systemButtonStateChanged", - Q_ARG(Global::SystemButtonType, button), Q_ARG(Global::ButtonState, state)); } void FramelessWidgetsHelper::updateContentsMargins()