From d4e711d679acee0605e72b6e545a8a2e4b816d7e Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Sat, 26 Mar 2022 15:31:16 +0800 Subject: [PATCH] win implementation is mostly settled now Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- examples/mainwindow/main.cpp | 1 - examples/quick/qml/MainWindow.qml | 5 +- examples/widget/main.cpp | 1 - examples/widget/widget.cpp | 14 +- .../FramelessHelper/Core/framelesshelper_qt.h | 2 +- .../Core/framelesshelper_win.h | 2 +- .../Core/framelesshelpercore_global.h | 54 ++- .../Core/framelesswindowsmanager.h | 2 +- include/FramelessHelper/Core/utils.h | 34 +- .../Quick/FramelessQuickEventFilter | 1 - .../Quick/framelessquickeventfilter.h | 54 --- .../Quick/framelessquickwindow.h | 25 +- .../Widgets/framelessmainwindow.h | 14 +- .../FramelessHelper/Widgets/framelesswidget.h | 14 +- .../Widgets/framelesswidgetshelper.h | 14 +- src/core/framelesshelper_qt.cpp | 24 +- src/core/framelesshelper_win.cpp | 162 +++++--- src/core/framelesswindowsmanager.cpp | 28 +- src/core/utils.cpp | 12 +- src/core/utils_win.cpp | 188 ++++----- src/quick/CMakeLists.txt | 2 - src/quick/framelesshelper_quick.cpp | 22 +- src/quick/framelesshelperimageprovider.cpp | 3 + src/quick/framelessquickeventfilter.cpp | 257 ------------ src/quick/framelessquickeventfilter.h | 25 -- src/quick/framelessquickwindow.cpp | 388 +++++++++++++++--- src/quick/framelessquickwindow_p.h | 27 +- src/widgets/framelessmainwindow.cpp | 30 +- src/widgets/framelesswidget.cpp | 30 +- src/widgets/framelesswidgetshelper.cpp | 203 ++++++--- 30 files changed, 948 insertions(+), 690 deletions(-) delete mode 100644 include/FramelessHelper/Quick/FramelessQuickEventFilter delete mode 100644 include/FramelessHelper/Quick/framelessquickeventfilter.h delete mode 100644 src/quick/framelessquickeventfilter.cpp delete mode 100644 src/quick/framelessquickeventfilter.h diff --git a/examples/mainwindow/main.cpp b/examples/mainwindow/main.cpp index a403e7c..efd75ac 100644 --- a/examples/mainwindow/main.cpp +++ b/examples/mainwindow/main.cpp @@ -45,7 +45,6 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); MainWindow mainWindow; - mainWindow.moveToDesktopCenter(); mainWindow.show(); return QCoreApplication::exec(); diff --git a/examples/quick/qml/MainWindow.qml b/examples/quick/qml/MainWindow.qml index 415a22c..bc34d91 100644 --- a/examples/quick/qml/MainWindow.qml +++ b/examples/quick/qml/MainWindow.qml @@ -29,14 +29,11 @@ import org.wangwenx190.FramelessHelper 1.0 FramelessWindow { id: window + visible: true width: 800 height: 600 title: qsTr("Hello, World! - Qt Quick") color: FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor - Component.onCompleted: { - window.moveToDesktopCenter(); - window.visible = true; - } Timer { interval: 500 diff --git a/examples/widget/main.cpp b/examples/widget/main.cpp index b468bfe..fb24c0b 100644 --- a/examples/widget/main.cpp +++ b/examples/widget/main.cpp @@ -45,7 +45,6 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); Widget widget; - widget.moveToDesktopCenter(); widget.show(); return QCoreApplication::exec(); diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index 616f460..1ef06f8 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -32,7 +32,19 @@ FRAMELESSHELPER_USE_NAMESPACE using namespace Global; -Widget::Widget(QWidget *parent) : FramelessWidget(parent, {Option::UseStandardWindowLayout}) +static const UserSettings settings = +{ + /* startupPosition */ QPoint(), + /* startupSize */ QSize(), + /* startupState */ Qt::WindowNoState, + /* options */ { Option::UseStandardWindowLayout }, + /* systemMenuOffset */ QPoint(), + /* minimizeButton */ nullptr, + /* maximizeButton */ nullptr, + /* closeButton */ nullptr +}; + +Widget::Widget(QWidget *parent) : FramelessWidget(parent, settings) { setupUi(); startTimer(500); diff --git a/include/FramelessHelper/Core/framelesshelper_qt.h b/include/FramelessHelper/Core/framelesshelper_qt.h index 549f95a..fbff00e 100644 --- a/include/FramelessHelper/Core/framelesshelper_qt.h +++ b/include/FramelessHelper/Core/framelesshelper_qt.h @@ -38,7 +38,7 @@ public: explicit FramelessHelperQt(QObject *parent = nullptr); ~FramelessHelperQt() override; - static void addWindow(const Global::FramelessHelperParams ¶ms); + static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters ¶ms); protected: Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; diff --git a/include/FramelessHelper/Core/framelesshelper_win.h b/include/FramelessHelper/Core/framelesshelper_win.h index cb91fd3..b1e818b 100644 --- a/include/FramelessHelper/Core/framelesshelper_win.h +++ b/include/FramelessHelper/Core/framelesshelper_win.h @@ -37,7 +37,7 @@ public: explicit FramelessHelperWin(); ~FramelessHelperWin() override; - static void addWindow(const Global::FramelessHelperParams ¶ms); + static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters ¶ms); Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, NATIVE_EVENT_RESULT_TYPE *result) override; }; diff --git a/include/FramelessHelper/Core/framelesshelpercore_global.h b/include/FramelessHelper/Core/framelesshelpercore_global.h index 375a165..25d3858 100644 --- a/include/FramelessHelper/Core/framelesshelpercore_global.h +++ b/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -181,10 +182,11 @@ enum class SystemButtonType : int { Unknown = -1, WindowIcon = 0, - Minimize = 1, - Maximize = 2, - Restore = 3, - Close = 4 + Help = 1, + Minimize = 2, + Maximize = 3, + Restore = 4, + Close = 5 }; Q_ENUM_NS(SystemButtonType) @@ -224,30 +226,65 @@ using SetWindowStateCallback = std::function; using GetWindowHandleCallback = std::function; -struct FramelessHelperParams +using WindowToScreenCallback = std::function; +using ScreenToWindowCallback = std::function; + +using IsInsideSystemButtonsCallback = std::function; +using IsInsideTitleBarDraggableAreaCallback = std::function; + +using GetWindowDevicePixelRatioCallback = std::function; + +struct UserSettings { - WId windowId = 0; + QPoint startupPosition = {}; + QSize startupSize = {}; + Qt::WindowState startupState = Qt::WindowNoState; Options options = {}; QPoint systemMenuOffset = {}; + QPointer minimizeButton = nullptr; + QPointer maximizeButton = nullptr; + QPointer closeButton = nullptr; +}; + +struct SystemParameters +{ + WId windowId = 0; + GetWindowFlagsCallback getWindowFlags = nullptr; SetWindowFlagsCallback setWindowFlags = nullptr; + GetWindowSizeCallback getWindowSize = nullptr; SetWindowSizeCallback setWindowSize = nullptr; + GetWindowPositionCallback getWindowPosition = nullptr; SetWindowPositionCallback setWindowPosition = nullptr; + GetWindowScreenCallback getWindowScreen = nullptr; + IsWindowFixedSizeCallback isWindowFixedSize = nullptr; SetWindowFixedSizeCallback setWindowFixedSize = nullptr; + GetWindowStateCallback getWindowState = nullptr; SetWindowStateCallback setWindowState = nullptr; + GetWindowHandleCallback getWindowHandle = nullptr; + WindowToScreenCallback windowToScreen = nullptr; + ScreenToWindowCallback screenToWindow = nullptr; + + IsInsideSystemButtonsCallback isInsideSystemButtons = nullptr; + IsInsideTitleBarDraggableAreaCallback isInsideTitleBarDraggableArea = nullptr; + + GetWindowDevicePixelRatioCallback getWindowDevicePixelRatio = nullptr; + [[nodiscard]] inline bool isValid() const { return (windowId && getWindowFlags && setWindowFlags && getWindowSize && setWindowSize && getWindowPosition && setWindowPosition && isWindowFixedSize && setWindowFixedSize && getWindowState - && setWindowState && getWindowHandle); + && setWindowState && getWindowHandle && windowToScreen && screenToWindow + && isInsideSystemButtons && isInsideTitleBarDraggableArea + && getWindowDevicePixelRatio); } }; @@ -255,4 +292,5 @@ struct FramelessHelperParams FRAMELESSHELPER_END_NAMESPACE -Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global::FramelessHelperParams)) +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global::UserSettings)) +Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global::SystemParameters)) diff --git a/include/FramelessHelper/Core/framelesswindowsmanager.h b/include/FramelessHelper/Core/framelesswindowsmanager.h index 59ce147..e78031d 100644 --- a/include/FramelessHelper/Core/framelesswindowsmanager.h +++ b/include/FramelessHelper/Core/framelesswindowsmanager.h @@ -46,7 +46,7 @@ public: Q_NODISCARD static Global::SystemTheme systemTheme(); public Q_SLOTS: - static void addWindow(const Global::FramelessHelperParams ¶ms); + static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters ¶ms); Q_SIGNALS: void systemThemeChanged(); diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index c511ca1..30aba44 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -43,7 +43,7 @@ getSystemButtonIconResource(const Global::SystemButtonType button, const Global::SystemTheme theme, const Global::ResourceType type); FRAMELESSHELPER_CORE_API void sendMouseReleaseEvent(); -[[nodiscard]] FRAMELESSHELPER_CORE_API QWindow *findWindow(const WId winId); +[[nodiscard]] FRAMELESSHELPER_CORE_API QWindow *findWindow(const WId windowId); FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter( const Global::GetWindowScreenCallback &getWindowScreen, const Global::GetWindowSizeCallback &getWindowSize, @@ -60,15 +60,15 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter( [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101809OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin11OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled(); -FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId winId); -FRAMELESSHELPER_CORE_API void updateWindowFrameMargins(const WId winId, const bool reset); +FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId windowId); +FRAMELESSHELPER_CORE_API void updateWindowFrameMargins(const WId windowId, const bool reset); FRAMELESSHELPER_CORE_API void updateInternalWindowFrameMargins(QWindow *window, const bool enable); [[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemErrorMessage(const QString &function); -[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFullScreen(const WId winId); -[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowNoState(const WId winId); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFullScreen(const WId windowId); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowNoState(const WId windowId); FRAMELESSHELPER_CORE_API void syncWmPaintWithDwm(); FRAMELESSHELPER_CORE_API void showSystemMenu( - const WId winId, + const WId windowId, const QPoint &pos, const Global::Options options, const QPoint &offset, @@ -78,32 +78,32 @@ FRAMELESSHELPER_CORE_API void showSystemMenu( [[nodiscard]] FRAMELESSHELPER_CORE_API Global::DwmColorizationArea getDwmColorizationArea(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isHighContrastModeEnabled(); [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getPrimaryScreenDpi(const bool horizontal); -[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getWindowDpi(const WId winId, const bool horizontal); -[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId winId, +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getWindowDpi(const WId windowId, const bool horizontal); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId, const bool horizontal, const bool scaled); -[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionHeight(const WId winId, const bool scaled); -[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId winId, const bool scaled); -[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId winId, +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionHeight(const WId windowId, const bool scaled); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled); +[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId, const bool scaled); [[nodiscard]] FRAMELESSHELPER_CORE_API QColor getFrameBorderColor(const bool active); -FRAMELESSHELPER_CORE_API void updateWindowFrameBorderColor(const WId winId, const bool dark); -FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId winId); +FRAMELESSHELPER_CORE_API void updateWindowFrameBorderColor(const WId windowId, const bool dark); +FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId windowId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized(); FRAMELESSHELPER_CORE_API void installSystemMenuHook( - const WId winId, + const WId windowId, const Global::Options options, const QPoint &offset, const Global::IsWindowFixedSizeCallback &isWindowFixedSize); -FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId winId); +FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId windowId); FRAMELESSHELPER_CORE_API void tryToBeCompatibleWithQtFramelessWindowHint( - const WId winId, + const WId windowId, const Global::GetWindowFlagsCallback &getWindowFlags, const Global::SetWindowFlagsCallback &setWindowFlags, const bool enable); -FRAMELESSHELPER_CORE_API void setAeroSnappingEnabled(const WId winId, const bool enable); +FRAMELESSHELPER_CORE_API void setAeroSnappingEnabled(const WId windowId, const bool enable); FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel(); #endif // Q_OS_WINDOWS diff --git a/include/FramelessHelper/Quick/FramelessQuickEventFilter b/include/FramelessHelper/Quick/FramelessQuickEventFilter deleted file mode 100644 index 91fa73a..0000000 --- a/include/FramelessHelper/Quick/FramelessQuickEventFilter +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/include/FramelessHelper/Quick/framelessquickeventfilter.h b/include/FramelessHelper/Quick/framelessquickeventfilter.h deleted file mode 100644 index 26389c6..0000000 --- a/include/FramelessHelper/Quick/framelessquickeventfilter.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 QQuickWindow; -class QQuickItem; -QT_END_NAMESPACE - -FRAMELESSHELPER_BEGIN_NAMESPACE - -class FRAMELESSHELPER_QUICK_API FramelessQuickEventFilter : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY_MOVE(FramelessQuickEventFilter) - -public: - explicit FramelessQuickEventFilter(QObject *parent = nullptr); - ~FramelessQuickEventFilter() override; - - static void addWindow(const Global::FramelessHelperParams ¶ms); - static void setTitleBarItem(QQuickWindow *window, QQuickItem *item); - static void setHitTestVisible(QQuickWindow *window, QQuickItem *item); - -protected: - Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; -}; - -FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Quick/framelessquickwindow.h b/include/FramelessHelper/Quick/framelessquickwindow.h index 49dc49e..6212b6b 100644 --- a/include/FramelessHelper/Quick/framelessquickwindow.h +++ b/include/FramelessHelper/Quick/framelessquickwindow.h @@ -40,15 +40,23 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow #endif Q_DECLARE_PRIVATE(FramelessQuickWindow) Q_DISABLE_COPY_MOVE(FramelessQuickWindow) - Q_PROPERTY(bool zoomed READ zoomed NOTIFY zoomedChanged FINAL) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) + 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 fixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL) Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL) public: - explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::Options options = {}); + explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::UserSettings &settings = {}); ~FramelessQuickWindow() override; - Q_NODISCARD bool zoomed() const; + Q_NODISCARD bool isHidden() const; + Q_NODISCARD bool isNormal() const; + Q_NODISCARD bool isMinimized() const; + Q_NODISCARD bool isZoomed() const; + Q_NODISCARD bool isFullScreen() const; Q_NODISCARD bool fixedSize() const; void setFixedSize(const bool value); @@ -65,9 +73,20 @@ public Q_SLOTS: void setTitleBarItem(QQuickItem *item); void setHitTestVisible(QQuickItem *item); void moveToDesktopCenter(); + void bringToFront(); + +protected: + void showEvent(QShowEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); + void minimizedChanged(); void zoomedChanged(); + void fullScreenChanged(); void fixedSizeChanged(); void frameBorderColorChanged(); diff --git a/include/FramelessHelper/Widgets/framelessmainwindow.h b/include/FramelessHelper/Widgets/framelessmainwindow.h index ce297fa..d35a591 100644 --- a/include/FramelessHelper/Widgets/framelessmainwindow.h +++ b/include/FramelessHelper/Widgets/framelessmainwindow.h @@ -35,16 +35,17 @@ class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow { Q_OBJECT Q_DISABLE_COPY_MOVE(FramelessMainWindow) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL) Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL) public: - explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}, const Global::Options options = {}); + explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}, const Global::UserSettings &settings = {}); ~FramelessMainWindow() override; - Q_NODISCARD Q_INVOKABLE bool isNormal() const; - + Q_NODISCARD bool isNormal() const; Q_NODISCARD bool isZoomed() const; Q_NODISCARD bool isFixedSize() const; @@ -58,8 +59,13 @@ public Q_SLOTS: void toggleMaximized(); void toggleFullScreen(); void moveToDesktopCenter(); + void bringToFront(); + void showSystemMenu(const QPoint &pos); + void startSystemMove2(); + void startSystemResize2(const Qt::Edges edges); protected: + void showEvent(QShowEvent *event) override; void changeEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; @@ -67,6 +73,8 @@ protected: void mouseDoubleClickEvent(QMouseEvent *event) override; Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); void zoomedChanged(); void fixedSizeChanged(); void titleBarWidgetChanged(); diff --git a/include/FramelessHelper/Widgets/framelesswidget.h b/include/FramelessHelper/Widgets/framelesswidget.h index f409449..731467f 100644 --- a/include/FramelessHelper/Widgets/framelesswidget.h +++ b/include/FramelessHelper/Widgets/framelesswidget.h @@ -35,17 +35,18 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget { Q_OBJECT Q_DISABLE_COPY_MOVE(FramelessWidget) + Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL) + Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL) Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL) Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL) Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL) Q_PROPERTY(QWidget* contentWidget READ contentWidget WRITE setContentWidget NOTIFY contentWidgetChanged FINAL) public: - explicit FramelessWidget(QWidget *parent = nullptr, const Global::Options options = {}); + explicit FramelessWidget(QWidget *parent = nullptr, const Global::UserSettings &settings = {}); ~FramelessWidget() override; - Q_NODISCARD Q_INVOKABLE bool isNormal() const; - + Q_NODISCARD bool isNormal() const; Q_NODISCARD bool isZoomed() const; Q_NODISCARD bool isFixedSize() const; @@ -62,8 +63,13 @@ public Q_SLOTS: void toggleMaximized(); void toggleFullScreen(); void moveToDesktopCenter(); + void bringToFront(); + void showSystemMenu(const QPoint &pos); + void startSystemMove2(); + void startSystemResize2(const Qt::Edges edges); protected: + void showEvent(QShowEvent *event) override; void changeEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; @@ -71,6 +77,8 @@ protected: void mouseDoubleClickEvent(QMouseEvent *event) override; Q_SIGNALS: + void hiddenChanged(); + void normalChanged(); void zoomedChanged(); void fixedSizeChanged(); void titleBarWidgetChanged(); diff --git a/include/FramelessHelper/Widgets/framelesswidgetshelper.h b/include/FramelessHelper/Widgets/framelesswidgetshelper.h index e758d7f..104c2b8 100644 --- a/include/FramelessHelper/Widgets/framelesswidgetshelper.h +++ b/include/FramelessHelper/Widgets/framelesswidgetshelper.h @@ -32,6 +32,7 @@ QT_BEGIN_NAMESPACE class QLabel; class QPushButton; class QVBoxLayout; +class QShowEvent; class QPaintEvent; class QMouseEvent; QT_END_NAMESPACE @@ -44,7 +45,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper) public: - explicit FramelessWidgetsHelper(QWidget *q, const Global::Options options = {}); + explicit FramelessWidgetsHelper(QWidget *q, const Global::UserSettings &settings = {}); ~FramelessWidgetsHelper() override; Q_NODISCARD Q_INVOKABLE bool isNormal() const; @@ -58,6 +59,7 @@ public: Q_INVOKABLE void setContentWidget(QWidget *widget); Q_NODISCARD Q_INVOKABLE QWidget *getContentWidget() const; + Q_INVOKABLE void showEventHandler(QShowEvent *event); Q_INVOKABLE void changeEventHandler(QEvent *event); Q_INVOKABLE void paintEventHandler(QPaintEvent *event); Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); @@ -69,12 +71,18 @@ public Q_SLOTS: void toggleMaximized(); void toggleFullScreen(); void moveToDesktopCenter(); + void bringToFront(); + void showSystemMenu(const QPoint &pos); + void startSystemMove2(); + void startSystemResize2(const Qt::Edges edges); private: void initialize(); void createSystemTitleBar(); void createUserContentContainer(); void setupInitialUi(); + Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; + Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const; Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool shouldDrawFrameBorder() const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; @@ -100,7 +108,9 @@ private: QVBoxLayout *m_userContentContainerLayout = nullptr; Qt::WindowState m_savedWindowState = {}; QWindow *m_window = nullptr; - Global::FramelessHelperParams m_params = {}; + Global::UserSettings m_settings = {}; + Global::SystemParameters m_params = {}; + bool m_windowExposed = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index 7f3e419..f2f3c46 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -32,16 +32,17 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -struct QtHelperInternalData +struct QtHelperData { - FramelessHelperParams params = {}; - FramelessHelperQt *framelessHelper = nullptr; + UserSettings settings = {}; + SystemParameters params = {}; + FramelessHelperQt *eventFilter = nullptr; }; struct QtHelper { QMutex mutex = {}; - QHash data = {}; + QHash data = {}; }; Q_GLOBAL_STATIC(QtHelper, g_qtHelper) @@ -50,7 +51,7 @@ FramelessHelperQt::FramelessHelperQt(QObject *parent) : QObject(parent) {} FramelessHelperQt::~FramelessHelperQt() = default; -void FramelessHelperQt::addWindow(const FramelessHelperParams ¶ms) +void FramelessHelperQt::addWindow(const UserSettings &settings, const SystemParameters ¶ms) { Q_ASSERT(params.isValid()); if (!params.isValid()) { @@ -61,15 +62,16 @@ void FramelessHelperQt::addWindow(const FramelessHelperParams ¶ms) g_qtHelper()->mutex.unlock(); return; } - QtHelperInternalData data = {}; + QtHelperData data = {}; + data.settings = settings; data.params = params; QWindow *window = params.getWindowHandle(); // Give it a parent so that it can be deleted even if we forget to do so. - data.framelessHelper = new FramelessHelperQt(window); + data.eventFilter = new FramelessHelperQt(window); g_qtHelper()->data.insert(params.windowId, data); g_qtHelper()->mutex.unlock(); - window->setFlags(window->flags() | Qt::FramelessWindowHint); - window->installEventFilter(data.framelessHelper); + params.setWindowFlags(params.getWindowFlags() | Qt::FramelessWindowHint); + window->installEventFilter(data.eventFilter); } bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) @@ -95,7 +97,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) g_qtHelper()->mutex.unlock(); return false; } - const QtHelperInternalData data = g_qtHelper()->data.value(windowId); + const QtHelperData data = g_qtHelper()->data.value(windowId); g_qtHelper()->mutex.unlock(); if (data.params.isWindowFixedSize()) { return false; @@ -108,7 +110,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) #endif switch (type) { case QEvent::MouseMove: { - if (data.params.options & Option::DontTouchCursorShape) { + if (data.settings.options & Option::DontTouchCursorShape) { return false; } const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 10b0d18..90c4406 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -37,11 +37,17 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; +struct Win32HelperData +{ + UserSettings settings = {}; + SystemParameters params = {}; +}; + struct Win32Helper { QMutex mutex = {}; QScopedPointer nativeEventFilter; - QHash data = {}; + QHash data = {}; }; Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) @@ -50,7 +56,7 @@ FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {} FramelessHelperWin::~FramelessHelperWin() = default; -void FramelessHelperWin::addWindow(const FramelessHelperParams ¶ms) +void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemParameters ¶ms) { Q_ASSERT(params.isValid()); if (!params.isValid()) { @@ -61,27 +67,29 @@ void FramelessHelperWin::addWindow(const FramelessHelperParams ¶ms) g_win32Helper()->mutex.unlock(); return; } - FramelessHelperParams localParams = params; - if ((localParams.options & Option::ForceHideWindowFrameBorder) - && (localParams.options & Option::ForceShowWindowFrameBorder)) { - localParams.options &= ~(Option::ForceHideWindowFrameBorder | Option::ForceShowWindowFrameBorder); + Win32HelperData data = {}; + data.settings = settings; + data.params = params; + if ((settings.options & Option::ForceHideWindowFrameBorder) + && (settings.options & Option::ForceShowWindowFrameBorder)) { + data.settings.options &= ~(Option::ForceHideWindowFrameBorder | Option::ForceShowWindowFrameBorder); qWarning() << "You can't use both \"Option::ForceHideWindowFrameBorder\" and " "\"Option::ForceShowWindowFrameBorder\" at the same time."; } - g_win32Helper()->data.insert(localParams.windowId, localParams); + g_win32Helper()->data.insert(params.windowId, data); if (g_win32Helper()->nativeEventFilter.isNull()) { g_win32Helper()->nativeEventFilter.reset(new FramelessHelperWin); qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data()); } g_win32Helper()->mutex.unlock(); - if (!(localParams.options & Option::DontTouchQtInternals)) { - Utils::fixupQtInternals(localParams.windowId); + if (!(settings.options & Option::DontTouchQtInternals)) { + Utils::fixupQtInternals(params.windowId); } - Utils::updateInternalWindowFrameMargins(localParams.getWindowHandle(), true); - Utils::updateWindowFrameMargins(localParams.windowId, false); - if (!(localParams.options & Option::DontTouchWindowFrameBorderColor)) { + Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true); + Utils::updateWindowFrameMargins(params.windowId, false); + if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) { const bool dark = Utils::shouldAppsUseDarkMode(); - Utils::updateWindowFrameBorderColor(localParams.windowId, dark); + Utils::updateWindowFrameBorderColor(params.windowId, dark); } } @@ -96,30 +104,33 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me #else const auto msg = static_cast(message); #endif - if (!msg->hwnd) { + const HWND hWnd = msg->hwnd; + if (!hWnd) { // Why sometimes the window handle is null? Is it designed to be like this? // Anyway, we should skip the entire processing in this case. return false; } - const auto winId = reinterpret_cast(msg->hwnd); + const auto windowId = reinterpret_cast(hWnd); g_win32Helper()->mutex.lock(); - if (!g_win32Helper()->data.contains(winId)) { + if (!g_win32Helper()->data.contains(windowId)) { g_win32Helper()->mutex.unlock(); return false; } - const FramelessHelperParams params = g_win32Helper()->data.value(winId); + const Win32HelperData data = g_win32Helper()->data.value(windowId); g_win32Helper()->mutex.unlock(); - const bool frameBorderVisible = [¶ms]() -> bool { - if (params.options & Option::ForceShowWindowFrameBorder) { + const bool frameBorderVisible = [&data]() -> bool { + if (data.settings.options & Option::ForceShowWindowFrameBorder) { return true; } - if (params.options & Option::ForceHideWindowFrameBorder) { + if (data.settings.options & Option::ForceHideWindowFrameBorder) { return false; } return Utils::isWindowFrameBorderVisible(); }(); - const bool fixedSize = params.isWindowFixedSize(); - switch (msg->message) { + const UINT uMsg = msg->message; + const WPARAM wParam = msg->wParam; + const LPARAM lParam = msg->lParam; + switch (uMsg) { case WM_NCCALCSIZE: { // Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容) // 和非客户区(标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分 @@ -208,14 +219,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // implement an elaborate client-area preservation technique, and // simply return 0, which means "preserve the entire old client area // and align it with the upper-left corner of our new client area". - const auto clientRect = ((static_cast(msg->wParam) == FALSE) - ? reinterpret_cast(msg->lParam) - : &(reinterpret_cast(msg->lParam))->rgrc[0]); + const auto clientRect = ((static_cast(wParam) == FALSE) + ? reinterpret_cast(lParam) + : &(reinterpret_cast(lParam))->rgrc[0]); if (frameBorderVisible) { // Store the original top before the default window proc applies the default frame. const LONG originalTop = clientRect->top; // Apply the default frame. - const LRESULT ret = DefWindowProcW(msg->hwnd, WM_NCCALCSIZE, msg->wParam, msg->lParam); + const LRESULT ret = DefWindowProcW(hWnd, WM_NCCALCSIZE, wParam, lParam); if (ret != 0) { *result = ret; return true; @@ -223,8 +234,8 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // Re-apply the original top from before the size of the default frame was applied. clientRect->top = originalTop; } - const bool max = IsMaximized(msg->hwnd); - const bool full = Utils::isFullScreen(winId); + const bool max = IsMaximized(hWnd); + const bool full = Utils::isFullScreen(windowId); // We don't need this correction when we're fullscreen. We will // have the WS_POPUP size, so we don't have to worry about // borders, and the default frame will be fine. @@ -235,11 +246,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // then the window is clipped to the monitor so that the resize handle // do not appear because you don't need them (because you can't resize // a window when it's maximized unless you restore it). - const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true); + const int frameSizeY = Utils::getResizeBorderThickness(windowId, false, true); clientRect->top += frameSizeY; if (!frameBorderVisible) { clientRect->bottom -= frameSizeY; - const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true); + const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true); clientRect->left += frameSizeX; clientRect->right -= frameSizeX; } @@ -265,7 +276,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me MONITORINFO monitorInfo; SecureZeroMemory(&monitorInfo, sizeof(monitorInfo)); monitorInfo.cbSize = sizeof(monitorInfo); - const HMONITOR monitor = MonitorFromWindow(msg->hwnd, MONITOR_DEFAULTTONEAREST); + const HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); if (!monitor) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("MonitorFromWindow")); break; @@ -297,7 +308,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me _abd.cbSize = sizeof(_abd); _abd.hWnd = FindWindowW(L"Shell_TrayWnd", nullptr); if (_abd.hWnd) { - const HMONITOR windowMonitor = MonitorFromWindow(msg->hwnd, MONITOR_DEFAULTTONEAREST); + const HMONITOR windowMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); if (!windowMonitor) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("MonitorFromWindow")); break; @@ -367,7 +378,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // of the upper-left non-client area. It's confirmed that this issue exists // from Windows 7 to Windows 10. Not tested on Windows 11 yet. Don't know // whether it exists on Windows XP to Windows Vista or not. - *result = ((static_cast(msg->wParam) == FALSE) ? 0 : WVR_REDRAW); + *result = ((static_cast(wParam) == FALSE) ? 0 : WVR_REDRAW); return true; } case WM_NCHITTEST: { @@ -436,25 +447,51 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // another branch, if you are interested in it, you can give it a // try. - if (fixedSize) { + if (data.params.isWindowFixedSize()) { *result = HTCLIENT; return true; } - const POINT globalPos = {GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam)}; + const POINT globalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; POINT localPos = globalPos; - if (ScreenToClient(msg->hwnd, &localPos) == FALSE) { + if (ScreenToClient(hWnd, &localPos) == FALSE) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("ScreenToClient")); break; } - const bool max = IsMaximized(msg->hwnd); - const bool full = Utils::isFullScreen(winId); - const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true); + if (data.settings.options & Option::MaximizeButtonDocking) { + const QPoint scenePos = QPointF(QPointF(qreal(localPos.x), qreal(localPos.y)) + / data.params.getWindowDevicePixelRatio()).toPoint(); + SystemButtonType systemButton = SystemButtonType::Unknown; + if (data.params.isInsideSystemButtons(scenePos, &systemButton)) { + switch (systemButton) { + case SystemButtonType::Help: + *result = HTHELP; + break; + case SystemButtonType::Minimize: + *result = HTREDUCE; + break; + case SystemButtonType::Maximize: + case SystemButtonType::Restore: + *result = HTZOOM; + break; + case SystemButtonType::Close: + *result = HTCLOSE; + break; + default: + *result = HTCAPTION; + break; + } + return true; + } + } + const bool max = IsMaximized(hWnd); + const bool full = Utils::isFullScreen(windowId); + const int frameSizeY = Utils::getResizeBorderThickness(windowId, false, true); const bool isTop = (localPos.y < frameSizeY); - const bool isTitleBar = (false && !(params.options & Option::DisableDragging)); + const bool isTitleBar = (false && !(data.settings.options & Option::DisableDragging)); if (frameBorderVisible) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. - const LRESULT originalRet = DefWindowProcW(msg->hwnd, WM_NCHITTEST, 0, msg->lParam); + const LRESULT originalRet = DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam); if (originalRet != HTCLIENT) { *result = originalRet; return true; @@ -492,7 +529,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me return true; } RECT clientRect = {0, 0, 0, 0}; - if (GetClientRect(msg->hwnd, &clientRect) == FALSE) { + if (GetClientRect(hWnd, &clientRect) == FALSE) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetClientRect")); break; } @@ -501,7 +538,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const bool isBottom = (localPos.y >= (height - frameSizeY)); // Make the border a little wider to let the user easy to resize on corners. const qreal scaleFactor = ((isTop || isBottom) ? 2.0 : 1.0); - const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true); + const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true); const auto scaledFrameSizeX = static_cast(qRound(qreal(frameSizeX) * scaleFactor)); const bool isLeft = (localPos.x < scaledFrameSizeX); const bool isRight = (localPos.x >= (width - scaledFrameSizeX)); @@ -549,23 +586,23 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me case WM_WINDOWPOSCHANGING: { // Tell Windows to discard the entire contents of the client area, as re-using // parts of the client area would lead to jitter during resize. - const auto windowPos = reinterpret_cast(msg->lParam); + const auto windowPos = reinterpret_cast(lParam); windowPos->flags |= SWP_NOCOPYBITS; } break; #endif case WM_DPICHANGED: { // Sync the internal window frame margins with the latest DPI. - Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true); + Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true); } break; case WM_DWMCOMPOSITIONCHANGED: { // Re-apply the custom window frame if recovered from the basic theme. - Utils::updateWindowFrameMargins(winId, false); + Utils::updateWindowFrameMargins(windowId, false); } break; default: break; } if (!frameBorderVisible) { - switch (msg->message) { + switch (uMsg) { case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWFRAME: { // These undocumented messages are sent to draw themed window @@ -594,9 +631,9 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate // Don't use "*result = 0" here, otherwise the window won't respond to the // window activation state change. - *result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); + *result = DefWindowProcW(hWnd, WM_NCACTIVATE, wParam, -1); } else { - if (static_cast(msg->wParam) == FALSE) { + if (static_cast(wParam) == FALSE) { *result = TRUE; } else { *result = FALSE; @@ -610,26 +647,27 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // Disable painting while these messages are handled to prevent them // from drawing a window caption over the client area. SetLastError(ERROR_SUCCESS); - const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); + const auto oldStyle = static_cast(GetWindowLongPtrW(hWnd, GWL_STYLE)); if (oldStyle == 0) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW")); break; } // Prevent Windows from drawing the default title bar by temporarily // toggling the WS_VISIBLE style. + const DWORD newStyle = (oldStyle & ~WS_VISIBLE); SetLastError(ERROR_SUCCESS); - if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast(oldStyle & ~WS_VISIBLE)) == 0) { + if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast(newStyle)) == 0) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); break; } - Utils::triggerFrameChange(winId); - const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); + Utils::triggerFrameChange(windowId); + const LRESULT ret = DefWindowProcW(hWnd, uMsg, wParam, lParam); SetLastError(ERROR_SUCCESS); - if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) { + if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast(oldStyle)) == 0) { qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); break; } - Utils::triggerFrameChange(winId); + Utils::triggerFrameChange(windowId); *result = ret; return true; } @@ -638,10 +676,10 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me break; } } - const bool themeSettingChanged = [&msg]() -> bool { + const bool themeSettingChanged = [uMsg, wParam, lParam]() -> bool { if (Utils::isWin10OrGreater()) { - if (msg->message == WM_SETTINGCHANGE) { - if ((msg->wParam == 0) && (QString::fromWCharArray(reinterpret_cast(msg->lParam)) + if (uMsg == WM_SETTINGCHANGE) { + if ((wParam == 0) && (QString::fromWCharArray(reinterpret_cast(lParam)) .compare(QU8Str(kThemeSettingChangeEventName), Qt::CaseInsensitive) == 0)) { return true; } @@ -650,13 +688,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me return false; }(); if (themeSettingChanged) { - if (!(params.options & Option::DontTouchWindowFrameBorderColor)) { + if (!(data.settings.options & Option::DontTouchWindowFrameBorderColor)) { const bool dark = Utils::shouldAppsUseDarkMode(); - Utils::updateWindowFrameBorderColor(winId, dark); + Utils::updateWindowFrameBorderColor(windowId, dark); } } - if (themeSettingChanged || (msg->message == WM_THEMECHANGED) - || (msg->message == WM_DWMCOLORIZATIONCOLORCHANGED)) { + if (themeSettingChanged || (uMsg == WM_THEMECHANGED) + || (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED)) { Q_EMIT FramelessWindowsManager::instance()->systemThemeChanged(); } return false; diff --git a/src/core/framelesswindowsmanager.cpp b/src/core/framelesswindowsmanager.cpp index 3774138..2a63ab5 100644 --- a/src/core/framelesswindowsmanager.cpp +++ b/src/core/framelesswindowsmanager.cpp @@ -39,13 +39,13 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -struct FramelessWindowsManagerData +struct FramelessWindowsManagerHelper { QMutex mutex = {}; QList windowIds = {}; }; -Q_GLOBAL_STATIC(FramelessWindowsManagerData, g_data) +Q_GLOBAL_STATIC(FramelessWindowsManagerHelper, g_helper) Q_GLOBAL_STATIC(FramelessWindowsManager, g_manager) @@ -54,6 +54,8 @@ FramelessWindowsManager::FramelessWindowsManager(QObject *parent) : QObject(pare if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) { QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); } + qRegisterMetaType(); + qRegisterMetaType(); } FramelessWindowsManager::~FramelessWindowsManager() = default; @@ -89,19 +91,19 @@ SystemTheme FramelessWindowsManager::systemTheme() #endif } -void FramelessWindowsManager::addWindow(const FramelessHelperParams ¶ms) +void FramelessWindowsManager::addWindow(const UserSettings &settings, const SystemParameters ¶ms) { Q_ASSERT(params.isValid()); if (!params.isValid()) { return; } - g_data()->mutex.lock(); - if (g_data()->windowIds.contains(params.windowId)) { - g_data()->mutex.unlock(); + g_helper()->mutex.lock(); + if (g_helper()->windowIds.contains(params.windowId)) { + g_helper()->mutex.unlock(); return; } - g_data()->windowIds.append(params.windowId); - g_data()->mutex.unlock(); + g_helper()->windowIds.append(params.windowId); + g_helper()->mutex.unlock(); static const bool pureQt = usePureQtImplementation(); QWindow *window = params.getWindowHandle(); #ifdef Q_OS_WINDOWS @@ -121,15 +123,15 @@ void FramelessWindowsManager::addWindow(const FramelessHelperParams ¶ms) } #endif if (pureQt) { - FramelessHelperQt::addWindow(params); + FramelessHelperQt::addWindow(settings, params); } #ifdef Q_OS_WINDOWS if (!pureQt) { - FramelessHelperWin::addWindow(params); + FramelessHelperWin::addWindow(settings, params); } - if (!(params.options & Option::DontInstallSystemMenuHook)) { - Utils::installSystemMenuHook(params.windowId, params.options, - params.systemMenuOffset, params.isWindowFixedSize); + if (!(settings.options & Option::DontInstallSystemMenuHook)) { + Utils::installSystemMenuHook(params.windowId, settings.options, + settings.systemMenuOffset, params.isWindowFixedSize); } #endif } diff --git a/src/core/utils.cpp b/src/core/utils.cpp index e530b1f..6399d78 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -103,7 +103,9 @@ QVariant Utils::getSystemButtonIconResource case SystemButtonType::Unknown: return {}; case SystemButtonType::WindowIcon: - return QStringLiteral("windowIcon"); + return QStringLiteral("windowicon"); + case SystemButtonType::Help: + return QStringLiteral("help"); case SystemButtonType::Minimize: return QStringLiteral("minimize"); case SystemButtonType::Maximize: @@ -142,10 +144,10 @@ QVariant Utils::getSystemButtonIconResource return {}; } -QWindow *Utils::findWindow(const WId winId) +QWindow *Utils::findWindow(const WId windowId) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return nullptr; } const QWindowList windows = QGuiApplication::topLevelWindows(); @@ -154,7 +156,7 @@ QWindow *Utils::findWindow(const WId winId) } for (auto &&window : qAsConst(windows)) { if (window && window->handle()) { - if (window->winId() == winId) { + if (window->winId() == windowId) { return window; } } diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index 6bf497f..b141800 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -54,7 +54,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -struct Win32UtilsInternalData +struct Win32UtilsHelperData { WNDPROC originalWindowProc = nullptr; Options options = {}; @@ -65,7 +65,7 @@ struct Win32UtilsInternalData struct Win32UtilsHelper { QMutex mutex = {}; - QHash data = {}; + QHash data = {}; }; Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper) @@ -127,14 +127,14 @@ static const QString successErrorText = QStringLiteral("The operation completed return __getSystemErrorMessage(function, dwError); } -[[nodiscard]] static inline int getSystemMetrics2(const WId winId, const int index, +[[nodiscard]] static inline int getSystemMetrics2(const WId windowId, const int index, const bool horizontal, const bool scaled) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return 0; } - const UINT windowDpi = Utils::getWindowDpi(winId, horizontal); + const UINT windowDpi = Utils::getWindowDpi(windowId, horizontal); static const auto pGetSystemMetricsForDpi = reinterpret_cast( QSystemLibrary::resolve(QStringLiteral("user32"), "GetSystemMetricsForDpi")); @@ -189,7 +189,7 @@ static const QString successErrorText = QStringLiteral("The operation completed g_utilsHelper()->mutex.unlock(); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } - const Win32UtilsInternalData data = g_utilsHelper()->data.value(windowId); + const Win32UtilsHelperData data = g_utilsHelper()->data.value(windowId); g_utilsHelper()->mutex.unlock(); const auto getGlobalPosFromMouse = [lParam]() -> QPoint { return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; @@ -331,23 +331,23 @@ bool Utils::isDwmCompositionEnabled() return (enabled != FALSE); } -void Utils::triggerFrameChange(const WId winId) +void Utils::triggerFrameChange(const WId windowId) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); static constexpr const UINT flags = (SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); if (SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, flags) == FALSE) { qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowPos")); } } -void Utils::updateWindowFrameMargins(const WId winId, const bool reset) +void Utils::updateWindowFrameMargins(const WId windowId, const bool reset) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return; } // We can't extend the window frame when DWM composition is disabled. @@ -368,13 +368,13 @@ void Utils::updateWindowFrameMargins(const WId winId, const bool reset) return {1, 1, 1, 1}; } }(); - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); const HRESULT hr = pDwmExtendFrameIntoClientArea(hwnd, &margins); if (FAILED(hr)) { qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr); return; } - triggerFrameChange(winId); + triggerFrameChange(windowId); } void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable) @@ -383,17 +383,17 @@ void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable) if (!window) { return; } - const WId winId = window->winId(); - const QMargins margins = [enable, winId]() -> QMargins { + const WId windowId = window->winId(); + const QMargins margins = [enable, windowId]() -> QMargins { if (!enable) { return {}; } - const int titleBarHeight = getTitleBarHeight(winId, true); + const int titleBarHeight = getTitleBarHeight(windowId, true); if (isWindowFrameBorderVisible()) { return {0, -titleBarHeight, 0, 0}; } else { - const int frameSizeX = getResizeBorderThickness(winId, true, true); - const int frameSizeY = getResizeBorderThickness(winId, false, true); + const int frameSizeX = getResizeBorderThickness(windowId, true, true); + const int frameSizeY = getResizeBorderThickness(windowId, false, true); return {-frameSizeX, -titleBarHeight, -frameSizeX, -frameSizeY}; } }(); @@ -414,7 +414,7 @@ void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable) return; } #endif - triggerFrameChange(winId); + triggerFrameChange(windowId); } QString Utils::getSystemErrorMessage(const QString &function) @@ -493,15 +493,15 @@ DwmColorizationArea Utils::getDwmColorizationArea() return DwmColorizationArea::None; } -void Utils::showSystemMenu(const WId winId, const QPoint &pos, const Options options, +void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const Options options, const QPoint &offset, const IsWindowFixedSizeCallback &isWindowFixedSize) { - Q_ASSERT(winId); + Q_ASSERT(windowId); Q_ASSERT(isWindowFixedSize); - if (!winId || !isWindowFixedSize) { + if (!windowId || !isWindowFixedSize) { return; } - const auto hWnd = reinterpret_cast(winId); + const auto hWnd = reinterpret_cast(windowId); const HMENU menu = GetSystemMenu(hWnd, FALSE); if (!menu) { // The corresponding window doesn't have a menu, this isn't an error, @@ -522,7 +522,7 @@ void Utils::showSystemMenu(const WId winId, const QPoint &pos, const Options opt return true; }; const bool maxOrFull = (IsMaximized(hWnd) || - ((options & Option::DontTreatFullScreenAsZoomed) ? false : isFullScreen(winId))); + ((options & Option::DontTreatFullScreenAsZoomed) ? false : isFullScreen(windowId))); const bool fixedSize = isWindowFixedSize(); if (!setState(SC_RESTORE, (maxOrFull && !fixedSize), true)) { return; @@ -558,13 +558,13 @@ void Utils::showSystemMenu(const WId winId, const QPoint &pos, const Options opt } } -bool Utils::isFullScreen(const WId winId) +bool Utils::isFullScreen(const WId windowId) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return false; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); RECT wndRect = {}; if (GetWindowRect(hwnd, &wndRect) == FALSE) { qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowRect")); @@ -590,13 +590,13 @@ bool Utils::isFullScreen(const WId winId) && (wndRect.right == scrRect.right) && (wndRect.bottom == scrRect.bottom)); } -bool Utils::isWindowNoState(const WId winId) +bool Utils::isWindowNoState(const WId windowId) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return false; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); WINDOWPLACEMENT wp; SecureZeroMemory(&wp, sizeof(wp)); wp.length = sizeof(wp); // This line is important! Don't miss it! @@ -755,13 +755,13 @@ QT_WARNING_POP return USER_DEFAULT_SCREEN_DPI; } -quint32 Utils::getWindowDpi(const WId winId, const bool horizontal) +quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return USER_DEFAULT_SCREEN_DPI; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); QSystemLibrary user32Lib(QStringLiteral("user32")); static const auto pGetDpiForWindow = reinterpret_cast(user32Lib.resolve("GetDpiForWindow")); @@ -793,43 +793,43 @@ quint32 Utils::getWindowDpi(const WId winId, const bool horizontal) return getPrimaryScreenDpi(horizontal); } -quint32 Utils::getResizeBorderThickness(const WId winId, const bool horizontal, const bool scaled) +quint32 Utils::getResizeBorderThickness(const WId windowId, const bool horizontal, const bool scaled) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return 0; } if (horizontal) { - return (getSystemMetrics2(winId, SM_CXSIZEFRAME, true, scaled) - + getSystemMetrics2(winId, SM_CXPADDEDBORDER, true, scaled)); + return (getSystemMetrics2(windowId, SM_CXSIZEFRAME, true, scaled) + + getSystemMetrics2(windowId, SM_CXPADDEDBORDER, true, scaled)); } else { - return (getSystemMetrics2(winId, SM_CYSIZEFRAME, false, scaled) - + getSystemMetrics2(winId, SM_CYPADDEDBORDER, false, scaled)); + return (getSystemMetrics2(windowId, SM_CYSIZEFRAME, false, scaled) + + getSystemMetrics2(windowId, SM_CYPADDEDBORDER, false, scaled)); } } -quint32 Utils::getCaptionHeight(const WId winId, const bool scaled) +quint32 Utils::getCaptionHeight(const WId windowId, const bool scaled) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return 0; } - return getSystemMetrics2(winId, SM_CYCAPTION, false, scaled); + return getSystemMetrics2(windowId, SM_CYCAPTION, false, scaled); } -quint32 Utils::getTitleBarHeight(const WId winId, const bool scaled) +quint32 Utils::getTitleBarHeight(const WId windowId, const bool scaled) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return 0; } - return (getCaptionHeight(winId, scaled) + getResizeBorderThickness(winId, false, scaled)); + return (getCaptionHeight(windowId, scaled) + getResizeBorderThickness(windowId, false, scaled)); } -quint32 Utils::getFrameBorderThickness(const WId winId, const bool scaled) +quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return 0; } // There's no window frame border before Windows 10. @@ -842,9 +842,9 @@ quint32 Utils::getFrameBorderThickness(const WId winId, const bool scaled) if (!pDwmGetWindowAttribute) { return 0; } - const UINT dpi = getWindowDpi(winId, true); + const UINT dpi = getWindowDpi(windowId, true); const qreal scaleFactor = (qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI)); - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); UINT value = 0; if (SUCCEEDED(pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &value, sizeof(value)))) { const qreal dpr = (scaled ? 1.0 : scaleFactor); @@ -874,10 +874,10 @@ QColor Utils::getFrameBorderColor(const bool active) } } -void Utils::updateWindowFrameBorderColor(const WId winId, const bool dark) +void Utils::updateWindowFrameBorderColor(const WId windowId, const bool dark) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return; } // There's no global dark theme before Win10 1809. @@ -890,7 +890,7 @@ void Utils::updateWindowFrameBorderColor(const WId winId, const bool dark) if (!pDwmSetWindowAttribute) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); const BOOL value = (dark ? TRUE : FALSE); // Whether dark window frame is available or not depends on the runtime system version, // it's totally OK if it's not available, so just ignore the errors. @@ -898,13 +898,13 @@ void Utils::updateWindowFrameBorderColor(const WId winId, const bool dark) pDwmSetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value)); } -void Utils::fixupQtInternals(const WId winId) +void Utils::fixupQtInternals(const WId windowId) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); const auto oldClassStyle = static_cast(GetClassLongPtrW(hwnd, GCL_STYLE)); if (oldClassStyle == 0) { @@ -929,7 +929,7 @@ void Utils::fixupQtInternals(const WId winId) qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - triggerFrameChange(winId); + triggerFrameChange(windowId); } void Utils::startSystemMove(QWindow *window) @@ -1013,19 +1013,19 @@ bool Utils::isFrameBorderColorized() return isTitleBarColorized(); } -void Utils::installSystemMenuHook(const WId winId, const Options options, const QPoint &offset, +void Utils::installSystemMenuHook(const WId windowId, const Options options, const QPoint &offset, const IsWindowFixedSizeCallback &isWindowFixedSize) { - Q_ASSERT(winId); + Q_ASSERT(windowId); Q_ASSERT(isWindowFixedSize); - if (!winId || !isWindowFixedSize) { + if (!windowId || !isWindowFixedSize) { return; } QMutexLocker locker(&g_utilsHelper()->mutex); - if (g_utilsHelper()->data.contains(winId)) { + if (g_utilsHelper()->data.contains(windowId)) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); const auto originalWindowProc = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)); Q_ASSERT(originalWindowProc); @@ -1038,38 +1038,38 @@ void Utils::installSystemMenuHook(const WId winId, const Options options, const qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - //triggerFrameChange(winId); - Win32UtilsInternalData data = {}; + //triggerFrameChange(windowId); + Win32UtilsHelperData data = {}; data.originalWindowProc = originalWindowProc; data.options = options; data.offset = offset; data.isWindowFixedSize = isWindowFixedSize; - g_utilsHelper()->data.insert(winId, data); + g_utilsHelper()->data.insert(windowId, data); } -void Utils::uninstallSystemMenuHook(const WId winId) +void Utils::uninstallSystemMenuHook(const WId windowId) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return; } QMutexLocker locker(&g_utilsHelper()->mutex); - if (!g_utilsHelper()->data.contains(winId)) { + if (!g_utilsHelper()->data.contains(windowId)) { return; } - const Win32UtilsInternalData data = g_utilsHelper()->data.value(winId); + const Win32UtilsHelperData data = g_utilsHelper()->data.value(windowId); Q_ASSERT(data.originalWindowProc); if (!data.originalWindowProc) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast(data.originalWindowProc)) == 0) { qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - //triggerFrameChange(winId); - g_utilsHelper()->data.remove(winId); + //triggerFrameChange(windowId); + g_utilsHelper()->data.remove(windowId); } void Utils::sendMouseReleaseEvent() @@ -1079,18 +1079,18 @@ void Utils::sendMouseReleaseEvent() } } -void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId winId, +void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId windowId, const GetWindowFlagsCallback &getWindowFlags, const SetWindowFlagsCallback &setWindowFlags, const bool enable) { - Q_ASSERT(winId); + Q_ASSERT(windowId); Q_ASSERT(getWindowFlags); Q_ASSERT(setWindowFlags); - if (!winId || !getWindowFlags || !setWindowFlags) { + if (!windowId || !getWindowFlags || !setWindowFlags) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); const LONG_PTR originalWindowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); if (originalWindowStyle == 0) { @@ -1106,16 +1106,16 @@ void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId winId, qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - triggerFrameChange(winId); + triggerFrameChange(windowId); } -void Utils::setAeroSnappingEnabled(const WId winId, const bool enable) +void Utils::setAeroSnappingEnabled(const WId windowId, const bool enable) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(windowId); + if (!windowId) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); const auto oldWindowStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_STYLE)); if (oldWindowStyle == 0) { @@ -1134,7 +1134,7 @@ void Utils::setAeroSnappingEnabled(const WId winId, const bool enable) qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - triggerFrameChange(winId); + triggerFrameChange(windowId); } void Utils::tryToEnableHighestDpiAwarenessLevel() diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt index eaa0c98..dbd0eb7 100644 --- a/src/quick/CMakeLists.txt +++ b/src/quick/CMakeLists.txt @@ -6,7 +6,6 @@ set(SOURCES ${INCLUDE_PREFIX}/framelesshelperquick_global.h ${INCLUDE_PREFIX}/framelessquickutils.h ${INCLUDE_PREFIX}/framelesshelperimageprovider.h - ${INCLUDE_PREFIX}/framelessquickeventfilter.h ${INCLUDE_PREFIX}/framelesshelper_quick.h ${INCLUDE_PREFIX}/framelessquickwindow.h framelessquickwindow_p.h @@ -14,7 +13,6 @@ set(SOURCES framelesshelper_quick.cpp framelessquickutils.cpp framelesshelperimageprovider.cpp - framelessquickeventfilter.cpp framelessquickwindow.cpp ) diff --git a/src/quick/framelesshelper_quick.cpp b/src/quick/framelesshelper_quick.cpp index 04e22cd..00533ee 100644 --- a/src/quick/framelesshelper_quick.cpp +++ b/src/quick/framelesshelper_quick.cpp @@ -28,6 +28,11 @@ #include "framelessquickutils.h" #include "framelessquickwindow.h" +#ifndef QML_URL_EXPAND +# define QML_URL_EXPAND(fileName) \ + QUrl(QStringLiteral("qrc:///org.wangwenx190.FramelessHelper/qml/%1.qml").arg(fileName)) +#endif + // The "Q_INIT_RESOURCE()" macro can't be used inside a namespace, // the official workaround is to wrap it into a global function // and call the wrapper function inside the namespace. @@ -38,15 +43,6 @@ static inline void initResource() FRAMELESSHELPER_BEGIN_NAMESPACE -[[nodiscard]] static inline QUrl getQmlFileUrl(const QString &qml) -{ - Q_ASSERT(!qml.isEmpty()); - if (qml.isEmpty()) { - return {}; - } - return QUrl(QStringLiteral("qrc:///org.wangwenx190.FramelessHelper/qml/%1.qml").arg(qml)); -} - void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) { Q_ASSERT(engine); @@ -66,10 +62,10 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) qmlRegisterAnonymousType(FRAMELESSHELPER_QUICK_URI, 1); #endif initResource(); - qmlRegisterType(getQmlFileUrl(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton"); - qmlRegisterType(getQmlFileUrl(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton"); - qmlRegisterType(getQmlFileUrl(QStringLiteral("CloseButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton"); - qmlRegisterType(getQmlFileUrl(QStringLiteral("StandardTitleBar")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar"); + qmlRegisterType(QML_URL_EXPAND(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton"); + qmlRegisterType(QML_URL_EXPAND(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton"); + qmlRegisterType(QML_URL_EXPAND(QStringLiteral("CloseButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton"); + qmlRegisterType(QML_URL_EXPAND(QStringLiteral("StandardTitleBar")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar"); } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelesshelperimageprovider.cpp b/src/quick/framelesshelperimageprovider.cpp index cf505bf..e85b855 100644 --- a/src/quick/framelesshelperimageprovider.cpp +++ b/src/quick/framelesshelperimageprovider.cpp @@ -56,6 +56,9 @@ using namespace Global; if (str.compare(QStringLiteral("windowicon"), Qt::CaseInsensitive) == 0) { return SystemButtonType::WindowIcon; } + if (str.compare(QStringLiteral("help"), Qt::CaseInsensitive) == 0) { + return SystemButtonType::Help; + } if (str.compare(QStringLiteral("minimize"), Qt::CaseInsensitive) == 0) { return SystemButtonType::Minimize; } diff --git a/src/quick/framelessquickeventfilter.cpp b/src/quick/framelessquickeventfilter.cpp deleted file mode 100644 index 9b3c890..0000000 --- a/src/quick/framelessquickeventfilter.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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 "framelessquickeventfilter.h" -#include -#include -#include -#include -#include -#include - -FRAMELESSHELPER_BEGIN_NAMESPACE - -using namespace Global; - -struct EventFilterDataInternal -{ - FramelessQuickEventFilter *eventFilter = nullptr; - QQuickItem *titleBarItem = nullptr; - QList hitTestVisibleItems = {}; - FramelessHelperParams params = {}; -}; - -struct EventFilterData -{ - QMutex mutex = {}; - QHash data = {}; -}; - -Q_GLOBAL_STATIC(EventFilterData, g_data) - -[[nodiscard]] static inline bool isInTitleBarDraggableArea(QQuickWindow *window, const QPoint &pos) -{ - Q_ASSERT(window); - if (!window) { - return false; - } - const WId windowId = window->winId(); - g_data()->mutex.lock(); - if (!g_data()->data.contains(windowId)) { - g_data()->mutex.unlock(); - return false; - } - const EventFilterDataInternal data = g_data()->data.value(windowId); - g_data()->mutex.unlock(); - if (!data.titleBarItem) { - return false; - } - const auto mapGeometryToScene = [](const QQuickItem * const item) -> QRect { - Q_ASSERT(item); - if (!item) { - return {}; - } - return QRect(item->mapToScene(QPointF(0.0, 0.0)).toPoint(), item->size().toSize()); - }; - QRegion region = mapGeometryToScene(data.titleBarItem); - if (!data.hitTestVisibleItems.isEmpty()) { - for (auto &&item : qAsConst(data.hitTestVisibleItems)) { - Q_ASSERT(item); - if (item) { - region -= mapGeometryToScene(item); - } - } - } - return region.contains(pos); -} - -FramelessQuickEventFilter::FramelessQuickEventFilter(QObject *parent) : QObject(parent) {} - -FramelessQuickEventFilter::~FramelessQuickEventFilter() = default; - -void FramelessQuickEventFilter::addWindow(const FramelessHelperParams ¶ms) -{ - Q_ASSERT(params.isValid()); - if (!params.isValid()) { - return; - } - g_data()->mutex.lock(); - if (g_data()->data.contains(params.windowId)) { - g_data()->mutex.unlock(); - return; - } - auto data = EventFilterDataInternal{}; - data.params = params; - const auto window = qobject_cast(params.getWindowHandle()); - // Give it a parent so that it can be deleted even if we forget to do so. - data.eventFilter = new FramelessQuickEventFilter(window); - g_data()->data.insert(params.windowId, data); - g_data()->mutex.unlock(); - window->installEventFilter(data.eventFilter); -} - -void FramelessQuickEventFilter::setTitleBarItem(QQuickWindow *window, QQuickItem *item) -{ - Q_ASSERT(window); - Q_ASSERT(item); - if (!window || !item) { - return; - } - const WId windowId = window->winId(); - QMutexLocker locker(&g_data()->mutex); - if (!g_data()->data.contains(windowId)) { - return; - } - g_data()->data[windowId].titleBarItem = item; -} - -void FramelessQuickEventFilter::setHitTestVisible(QQuickWindow *window, QQuickItem *item) -{ - Q_ASSERT(window); - Q_ASSERT(item); - if (!window || !item) { - return; - } - const WId windowId = window->winId(); - QMutexLocker locker(&g_data()->mutex); - if (!g_data()->data.contains(windowId)) { - return; - } - auto &items = g_data()->data[windowId].hitTestVisibleItems; - static constexpr const bool visible = true; - const bool exists = items.contains(item); - if (visible && !exists) { - items.append(item); - } - if constexpr (!visible && exists) { - items.removeAll(item); - } -} - -bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event) -{ - Q_ASSERT(object); - Q_ASSERT(event); - if (!object || !event) { - return false; - } - if (!object->isWindowType()) { - return false; - } - const auto window = qobject_cast(object); - if (!window) { - return false; - } - const WId windowId = window->winId(); - g_data()->mutex.lock(); - if (!g_data()->data.contains(windowId)) { - g_data()->mutex.unlock(); - return false; - } - const EventFilterDataInternal data = g_data()->data.value(windowId); - g_data()->mutex.unlock(); - const QEvent::Type eventType = event->type(); - if ((eventType != QEvent::MouseButtonPress) && (eventType != QEvent::MouseButtonRelease) - && (eventType != QEvent::MouseButtonDblClick)) { - return false; - } - const auto mouseEvent = static_cast(event); - const Qt::MouseButton button = mouseEvent->button(); - if ((button != Qt::LeftButton) && (button != Qt::RightButton)) { - return false; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint scenePos = mouseEvent->scenePosition().toPoint(); -#else - const QPoint scenePos = mouseEvent->windowPos().toPoint(); -#endif - const QQuickWindow::Visibility visibility = window->visibility(); - if ((visibility == QQuickWindow::Windowed) - && ((scenePos.x() < kDefaultResizeBorderThickness) - || (scenePos.x() >= (window->width() - kDefaultResizeBorderThickness)) - || (scenePos.y() < kDefaultResizeBorderThickness))) { - return false; - } - const bool titleBar = isInTitleBarDraggableArea(window, scenePos); - const bool isFixedSize = data.params.isWindowFixedSize(); - switch (eventType) { - case QEvent::MouseButtonPress: { - if (data.params.options & Option::DisableDragging) { - return false; - } - if (button != Qt::LeftButton) { - return false; - } - if (!titleBar) { - return false; - } - Utils::startSystemMove(window); - return true; - } - case QEvent::MouseButtonRelease: { - if (data.params.options & Option::DisableSystemMenu) { - return false; - } - if (button != Qt::RightButton) { - return false; - } - if (!titleBar) { - return false; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint globalPos = mouseEvent->globalPosition().toPoint(); -#else - const QPoint globalPos = mouseEvent->globalPos(); -#endif - const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint(); - Utils::showSystemMenu(windowId, nativePos, data.params.options, - data.params.systemMenuOffset, data.params.isWindowFixedSize); - return true; - } - case QEvent::MouseButtonDblClick: { - if ((data.params.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize) { - return false; - } - if (button != Qt::LeftButton) { - return false; - } - if (!titleBar) { - return false; - } - if ((visibility == QQuickWindow::Maximized) - || ((data.params.options & Option::DontTreatFullScreenAsZoomed) - ? false : (visibility == QQuickWindow::FullScreen))) { - window->showNormal(); - } else { - window->showMaximized(); - } - return true; - } - default: - break; - } - return false; -} - -FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelessquickeventfilter.h b/src/quick/framelessquickeventfilter.h deleted file mode 100644 index 9bc33dd..0000000 --- a/src/quick/framelessquickeventfilter.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/framelessquickeventfilter.h" diff --git a/src/quick/framelessquickwindow.cpp b/src/quick/framelessquickwindow.cpp index 51a38b0..4839273 100644 --- a/src/quick/framelessquickwindow.cpp +++ b/src/quick/framelessquickwindow.cpp @@ -29,37 +29,62 @@ #include #include #include -#include "framelessquickeventfilter.h" FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Options options) : QObject(q) +static constexpr const char QT_QUICKITEM_CLASS_NAME[] = "QQuickItem"; + +FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const UserSettings &settings) : QObject(q) { Q_ASSERT(q); if (!q) { return; } q_ptr = q; - m_params.options = options; + m_settings = settings; initialize(); } FramelessQuickWindowPrivate::~FramelessQuickWindowPrivate() = default; +bool FramelessQuickWindowPrivate::isHidden() const +{ + Q_Q(const FramelessQuickWindow); + return (q->visibility() == FramelessQuickWindow::Hidden); +} + +bool FramelessQuickWindowPrivate::isNormal() const +{ + Q_Q(const FramelessQuickWindow); + return (q->visibility() == FramelessQuickWindow::Windowed); +} + +bool FramelessQuickWindowPrivate::isMinimized() const +{ + Q_Q(const FramelessQuickWindow); + return (q->visibility() == FramelessQuickWindow::Minimized); +} + bool FramelessQuickWindowPrivate::isZoomed() const { Q_Q(const FramelessQuickWindow); const FramelessQuickWindow::Visibility visibility = q->visibility(); return ((visibility == FramelessQuickWindow::Maximized) || - ((m_params.options & Option::DontTreatFullScreenAsZoomed) + ((m_settings.options & Option::DontTreatFullScreenAsZoomed) ? false : (visibility == FramelessQuickWindow::FullScreen))); } +bool FramelessQuickWindowPrivate::isFullScreen() const +{ + Q_Q(const FramelessQuickWindow); + return (q->visibility() == FramelessQuickWindow::FullScreen); +} + bool FramelessQuickWindowPrivate::isFixedSize() const { - if (m_params.options & Option::DisableResizing) { + if (m_settings.options & Option::DisableResizing) { return true; } Q_Q(const FramelessQuickWindow); @@ -90,8 +115,10 @@ void FramelessQuickWindowPrivate::setTitleBarItem(QQuickItem *item) if (!item) { return; } - Q_Q(FramelessQuickWindow); - FramelessQuickEventFilter::setTitleBarItem(q, item); + if (m_titleBarItem == item) { + return; + } + m_titleBarItem = item; } void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item) @@ -100,8 +127,14 @@ void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item) if (!item) { return; } - Q_Q(FramelessQuickWindow); - FramelessQuickEventFilter::setHitTestVisible(q, item); + 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() @@ -132,6 +165,19 @@ void FramelessQuickWindowPrivate::setFixedSize(const bool value, const bool forc 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::showMinimized2() { #ifdef Q_OS_WINDOWS @@ -165,11 +211,10 @@ void FramelessQuickWindowPrivate::toggleFullScreen() return; } Q_Q(FramelessQuickWindow); - const QWindow::Visibility visibility = q->visibility(); - if (visibility == QWindow::FullScreen) { + if (isFullScreen()) { q->setVisibility(m_savedVisibility); } else { - m_savedVisibility = visibility; + m_savedVisibility = q->visibility(); q->showFullScreen(); } } @@ -178,14 +223,10 @@ void FramelessQuickWindowPrivate::showSystemMenu(const QPoint &pos) { #ifdef Q_OS_WINDOWS Q_Q(FramelessQuickWindow); -# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) const QPoint globalPos = q->mapToGlobal(pos); -# else - const QPoint globalPos = q->mapToGlobal(pos); -# endif const QPoint nativePos = QPointF(QPointF(globalPos) * q->effectiveDevicePixelRatio()).toPoint(); - Utils::showSystemMenu(m_params.windowId, nativePos, m_params.options, - m_params.systemMenuOffset, m_params.isWindowFixedSize); + Utils::showSystemMenu(m_params.windowId, nativePos, m_settings.options, + m_settings.systemMenuOffset, m_params.isWindowFixedSize); #endif } @@ -240,76 +281,285 @@ void FramelessQuickWindowPrivate::initialize() 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 { return isInSystemButtons(pos, button); }; + m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); }; + m_params.getWindowDevicePixelRatio = [q]() -> qreal { return q->effectiveDevicePixelRatio(); }; FramelessWindowsManager * const manager = FramelessWindowsManager::instance(); - manager->addWindow(m_params); - FramelessQuickEventFilter::addWindow(m_params); -#ifdef Q_OS_WINDOWS - if (isFrameBorderVisible()) { - QQuickItem * const rootItem = q->contentItem(); - const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem); - m_topBorderRectangle.reset(new QQuickRectangle(rootItem)); + manager->addWindow(m_settings, m_params); + QQuickItem * const rootItem = q->contentItem(); + const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem); + m_topBorderRectangle.reset(new QQuickRectangle(rootItem)); + const bool frameBorderVisible = shouldDrawFrameBorder(); + if (frameBorderVisible) { updateTopBorderHeight(); updateTopBorderColor(); - connect(q, &FramelessQuickWindow::visibilityChanged, this, [this, q](){ - updateTopBorderHeight(); - Q_EMIT q->zoomedChanged(); - }); - connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor); - connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this, q](){ - updateTopBorderColor(); - Q_EMIT q->frameBorderColorChanged(); - }); - const auto topBorderAnchors = new QQuickAnchors(m_topBorderRectangle.data(), m_topBorderRectangle.data()); - topBorderAnchors->setTop(rootItemPrivate->top()); - topBorderAnchors->setLeft(rootItemPrivate->left()); - topBorderAnchors->setRight(rootItemPrivate->right()); } -#endif - if (m_params.options & Option::DisableResizing) { + connect(q, &FramelessQuickWindow::visibilityChanged, this, [this, q, frameBorderVisible](){ + if (frameBorderVisible) { + updateTopBorderHeight(); + } + Q_EMIT q->hiddenChanged(); + Q_EMIT q->normalChanged(); + Q_EMIT q->minimizedChanged(); + Q_EMIT q->zoomedChanged(); + Q_EMIT q->fullScreenChanged(); + }); + connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor); + connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this, q, frameBorderVisible](){ + if (frameBorderVisible) { + updateTopBorderColor(); + } + Q_EMIT q->frameBorderColorChanged(); + }); + m_topBorderAnchors.reset(new QQuickAnchors(m_topBorderRectangle.data(), m_topBorderRectangle.data())); + m_topBorderAnchors->setTop(rootItemPrivate->top()); + m_topBorderAnchors->setLeft(rootItemPrivate->left()); + m_topBorderAnchors->setRight(rootItemPrivate->right()); + if (m_settings.options & Option::DisableResizing) { setFixedSize(true, true); } } -bool FramelessQuickWindowPrivate::isFrameBorderVisible() const +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, Global::SystemButtonType *button) const +{ + Q_ASSERT(button); + if (!button) { + return false; + } + *button = SystemButtonType::Unknown; + if (!m_settings.minimizeButton || !m_settings.maximizeButton || !m_settings.closeButton) { + return false; + } + if (!m_settings.minimizeButton->inherits(QT_QUICKITEM_CLASS_NAME) + || !m_settings.maximizeButton->inherits(QT_QUICKITEM_CLASS_NAME) + || !m_settings.closeButton->inherits(QT_QUICKITEM_CLASS_NAME)) { + return false; + } + const auto minBtn = qobject_cast(m_settings.minimizeButton); + if (mapItemGeometryToScene(minBtn).contains(pos)) { + *button = SystemButtonType::Minimize; + return true; + } + const auto maxBtn = qobject_cast(m_settings.maximizeButton); + if (mapItemGeometryToScene(maxBtn).contains(pos)) { + *button = SystemButtonType::Maximize; + return true; + } + const auto closeBtn = qobject_cast(m_settings.closeButton); + if (mapItemGeometryToScene(closeBtn).contains(pos)) { + *button = SystemButtonType::Close; + return true; + } + return false; +} + +bool FramelessQuickWindowPrivate::isInTitleBarDraggableArea(const QPoint &pos) const +{ + if (!m_titleBarItem) { + return false; + } + QRegion region = mapItemGeometryToScene(m_titleBarItem); + if (!m_hitTestVisibleItems.isEmpty()) { + for (auto &&item : qAsConst(m_hitTestVisibleItems)) { + Q_ASSERT(item); + if (item) { + region -= mapItemGeometryToScene(item); + } + } + } + return region.contains(pos); +} + +bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const { #ifdef Q_OS_WINDOWS - return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater()); + return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater() + && isNormal() && !(m_settings.options & Option::DontDrawTopWindowFrameBorder)); #else return false; #endif } -void FramelessQuickWindowPrivate::updateTopBorderColor() +bool FramelessQuickWindowPrivate::shouldIgnoreMouseEvents(const QPoint &pos) const { - if (!isFrameBorderVisible()) { + Q_Q(const FramelessQuickWindow); + return (isNormal() && ((pos.x() < kDefaultResizeBorderThickness) + || (pos.x() >= (q->width() - kDefaultResizeBorderThickness)) + || (pos.y() < kDefaultResizeBorderThickness))); +} + +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(); + } +} + +void FramelessQuickWindowPrivate::mousePressEventHandler(QMouseEvent *event) +{ + Q_ASSERT(event); + if (!event) { + return; + } + if (m_settings.options & Option::DisableDragging) { + return; + } + if (event->button() != Qt::LeftButton) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPoint scenePos = event->scenePosition().toPoint(); +#else + const QPoint scenePos = event->windowPos().toPoint(); +#endif + if (shouldIgnoreMouseEvents(scenePos)) { + return; + } + if (!isInTitleBarDraggableArea(scenePos)) { + return; + } + startSystemMove2(); +} + +void FramelessQuickWindowPrivate::mouseReleaseEventHandler(QMouseEvent *event) +{ + Q_ASSERT(event); + if (!event) { + return; + } + if (m_settings.options & Option::DisableSystemMenu) { + return; + } + if (event->button() != Qt::RightButton) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPoint scenePos = event->scenePosition().toPoint(); +#else + const QPoint scenePos = event->windowPos().toPoint(); +#endif + if (shouldIgnoreMouseEvents(scenePos)) { + return; + } + if (!isInTitleBarDraggableArea(scenePos)) { + return; + } + showSystemMenu(scenePos); +} + +void FramelessQuickWindowPrivate::mouseDoubleClickEventHandler(QMouseEvent *event) +{ + Q_ASSERT(event); + if (!event) { + return; + } + if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) { + return; + } + if (event->button() != Qt::LeftButton) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPoint scenePos = event->scenePosition().toPoint(); +#else + const QPoint scenePos = event->windowPos().toPoint(); +#endif + if (shouldIgnoreMouseEvents(scenePos)) { + return; + } + if (!isInTitleBarDraggableArea(scenePos)) { + return; + } + toggleMaximize(); +} + +void FramelessQuickWindowPrivate::updateTopBorderColor() +{ +#ifdef Q_OS_WINDOWS m_topBorderRectangle->setColor(getFrameBorderColor()); +#endif } void FramelessQuickWindowPrivate::updateTopBorderHeight() { - if (!isFrameBorderVisible()) { - return; - } - Q_Q(FramelessQuickWindow); - const qreal newHeight = ((q->visibility() == FramelessQuickWindow::Windowed) ? 1.0 : 0.0); +#ifdef Q_OS_WINDOWS + const qreal newHeight = (isNormal() ? 1.0 : 0.0); m_topBorderRectangle->setHeight(newHeight); +#endif } -FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const Options options) : QQuickWindow(parent) +FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings) : QQuickWindow(parent) { - d_ptr.reset(new FramelessQuickWindowPrivate(this, options)); + d_ptr.reset(new FramelessQuickWindowPrivate(this, settings)); } FramelessQuickWindow::~FramelessQuickWindow() = default; -bool FramelessQuickWindow::zoomed() const +bool FramelessQuickWindow::isHidden() const +{ + Q_D(const FramelessQuickWindow); + return d->isHidden(); +} + +bool FramelessQuickWindow::isNormal() const +{ + Q_D(const FramelessQuickWindow); + return d->isNormal(); +} + +bool FramelessQuickWindow::isMinimized() const +{ + Q_D(const FramelessQuickWindow); + return d->isMinimized(); +} + +bool FramelessQuickWindow::isZoomed() const { Q_D(const FramelessQuickWindow); return d->isZoomed(); } +bool FramelessQuickWindow::isFullScreen() const +{ + Q_D(const FramelessQuickWindow); + return d->isFullScreen(); +} + bool FramelessQuickWindow::fixedSize() const { Q_D(const FramelessQuickWindow); @@ -354,6 +604,40 @@ void FramelessQuickWindow::moveToDesktopCenter() d->moveToDesktopCenter(); } +void FramelessQuickWindow::bringToFront() +{ + Q_D(FramelessQuickWindow); + d->bringToFront(); +} + +void FramelessQuickWindow::showEvent(QShowEvent *event) +{ + QQuickWindow::showEvent(event); + Q_D(FramelessQuickWindow); + d->showEventHandler(event); +} + +void FramelessQuickWindow::mousePressEvent(QMouseEvent *event) +{ + QQuickWindow::mousePressEvent(event); + Q_D(FramelessQuickWindow); + d->mousePressEventHandler(event); +} + +void FramelessQuickWindow::mouseReleaseEvent(QMouseEvent *event) +{ + QQuickWindow::mouseReleaseEvent(event); + Q_D(FramelessQuickWindow); + d->mouseReleaseEventHandler(event); +} + +void FramelessQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) +{ + QQuickWindow::mouseDoubleClickEvent(event); + Q_D(FramelessQuickWindow); + d->mouseDoubleClickEventHandler(event); +} + void FramelessQuickWindow::showMinimized2() { Q_D(FramelessQuickWindow); diff --git a/src/quick/framelessquickwindow_p.h b/src/quick/framelessquickwindow_p.h index 9ec7466..58dac76 100644 --- a/src/quick/framelessquickwindow_p.h +++ b/src/quick/framelessquickwindow_p.h @@ -31,6 +31,7 @@ QT_BEGIN_NAMESPACE class QQuickItem; class QQuickRectangle; +class QQuickAnchors; QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE @@ -44,13 +45,22 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindowPrivate : public QObject Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate) public: - explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::Options options); + explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::UserSettings &settings = {}); ~FramelessQuickWindowPrivate() override; + Q_INVOKABLE Q_NODISCARD bool isHidden() const; + Q_INVOKABLE Q_NODISCARD bool isNormal() const; + 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 bool isFrameBorderVisible() const; + + Q_INVOKABLE void showEventHandler(QShowEvent *event); + Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); + Q_INVOKABLE void mouseReleaseEventHandler(QMouseEvent *event); + Q_INVOKABLE void mouseDoubleClickEventHandler(QMouseEvent *event); public Q_SLOTS: void showMinimized2(); @@ -63,9 +73,15 @@ public Q_SLOTS: void setHitTestVisible(QQuickItem *item); void moveToDesktopCenter(); void setFixedSize(const bool value, const bool force = false); + void bringToFront(); private: void initialize(); + Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; + Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const; + Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; + Q_NODISCARD bool shouldDrawFrameBorder() const; + Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; private Q_SLOTS: void updateTopBorderColor(); @@ -75,8 +91,13 @@ private: FramelessQuickWindow *q_ptr = nullptr; bool m_initialized = false; QScopedPointer m_topBorderRectangle; + QScopedPointer m_topBorderAnchors; QWindow::Visibility m_savedVisibility = QWindow::Windowed; - Global::FramelessHelperParams m_params = {}; + Global::UserSettings m_settings = {}; + Global::SystemParameters m_params = {}; + bool m_windowExposed = false; + QQuickItem *m_titleBarItem = nullptr; + QList m_hitTestVisibleItems = {}; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/framelessmainwindow.cpp b/src/widgets/framelessmainwindow.cpp index b1a99e6..f42f158 100644 --- a/src/widgets/framelessmainwindow.cpp +++ b/src/widgets/framelessmainwindow.cpp @@ -29,9 +29,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags, const Options options) : QMainWindow(parent, flags) +FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags, const UserSettings &settings) : QMainWindow(parent, flags) { - m_helper.reset(new FramelessWidgetsHelper(this, options)); + m_helper.reset(new FramelessWidgetsHelper(this, settings)); } FramelessMainWindow::~FramelessMainWindow() = default; @@ -86,6 +86,32 @@ void FramelessMainWindow::moveToDesktopCenter() m_helper->moveToDesktopCenter(); } +void FramelessMainWindow::bringToFront() +{ + m_helper->bringToFront(); +} + +void FramelessMainWindow::showSystemMenu(const QPoint &pos) +{ + m_helper->showSystemMenu(pos); +} + +void FramelessMainWindow::startSystemMove2() +{ + m_helper->startSystemMove2(); +} + +void FramelessMainWindow::startSystemResize2(const Qt::Edges edges) +{ + m_helper->startSystemResize2(edges); +} + +void FramelessMainWindow::showEvent(QShowEvent *event) +{ + QMainWindow::showEvent(event); + m_helper->showEventHandler(event); +} + void FramelessMainWindow::changeEvent(QEvent *event) { QMainWindow::changeEvent(event); diff --git a/src/widgets/framelesswidget.cpp b/src/widgets/framelesswidget.cpp index e18d776..f937c37 100644 --- a/src/widgets/framelesswidget.cpp +++ b/src/widgets/framelesswidget.cpp @@ -29,9 +29,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -FramelessWidget::FramelessWidget(QWidget *parent, const Options options) : QWidget(parent) +FramelessWidget::FramelessWidget(QWidget *parent, const UserSettings &settings) : QWidget(parent) { - m_helper.reset(new FramelessWidgetsHelper(this, options)); + m_helper.reset(new FramelessWidgetsHelper(this, settings)); } FramelessWidget::~FramelessWidget() = default; @@ -96,6 +96,32 @@ void FramelessWidget::moveToDesktopCenter() m_helper->moveToDesktopCenter(); } +void FramelessWidget::bringToFront() +{ + m_helper->bringToFront(); +} + +void FramelessWidget::showSystemMenu(const QPoint &pos) +{ + m_helper->showSystemMenu(pos); +} + +void FramelessWidget::startSystemMove2() +{ + m_helper->startSystemMove2(); +} + +void FramelessWidget::startSystemResize2(const Qt::Edges edges) +{ + m_helper->startSystemResize2(edges); +} + +void FramelessWidget::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + m_helper->showEventHandler(event); +} + void FramelessWidget::changeEvent(QEvent *event) { QWidget::changeEvent(event); diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index d492a81..9da5fd4 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -54,14 +54,14 @@ QPushButton:pressed { } )"); -FramelessWidgetsHelper::FramelessWidgetsHelper(QWidget *q, const Options options) : QObject(q) +FramelessWidgetsHelper::FramelessWidgetsHelper(QWidget *q, const UserSettings &settings) : QObject(q) { Q_ASSERT(q); if (!q) { return; } this->q = q; - m_params.options = options; + m_settings = settings; initialize(); } @@ -74,12 +74,12 @@ bool FramelessWidgetsHelper::isNormal() const bool FramelessWidgetsHelper::isZoomed() const { - return (q->isMaximized() || ((m_params.options & Option::DontTreatFullScreenAsZoomed) ? false : q->isFullScreen())); + return (q->isMaximized() || ((m_settings.options & Option::DontTreatFullScreenAsZoomed) ? false : q->isFullScreen())); } bool FramelessWidgetsHelper::isFixedSize() const { - if (m_params.options & Option::DisableResizing) { + if (m_settings.options & Option::DisableResizing) { return true; } if (q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) { @@ -124,7 +124,7 @@ void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget) if (m_userTitleBarWidget == widget) { return; } - if (m_params.options & Option::UseStandardWindowLayout) { + if (m_settings.options & Option::UseStandardWindowLayout) { if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) { m_mainLayout->removeWidget(m_systemTitleBarWidget); m_systemTitleBarWidget->hide(); @@ -152,7 +152,7 @@ void FramelessWidgetsHelper::setContentWidget(QWidget *widget) if (!widget) { return; } - if (!(m_params.options & Option::UseStandardWindowLayout)) { + if (!(m_settings.options & Option::UseStandardWindowLayout)) { return; } if (m_userContentWidget == widget) { @@ -188,6 +188,31 @@ void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget) } } +void FramelessWidgetsHelper::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(); + } +} + void FramelessWidgetsHelper::changeEventHandler(QEvent *event) { Q_ASSERT(event); @@ -198,7 +223,7 @@ void FramelessWidgetsHelper::changeEventHandler(QEvent *event) if ((type != QEvent::WindowStateChange) && (type != QEvent::ActivationChange)) { return; } - const bool standardLayout = (m_params.options & Option::UseStandardWindowLayout); + const bool standardLayout = (m_settings.options & Option::UseStandardWindowLayout); if (type == QEvent::WindowStateChange) { if (standardLayout) { if (isZoomed()) { @@ -241,7 +266,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event) if (!event) { return; } - if (m_params.options & Option::DisableDragging) { + if (m_settings.options & Option::DisableDragging) { return; } if (event->button() != Qt::LeftButton) { @@ -258,7 +283,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event) if (!isInTitleBarDraggableArea(scenePos)) { return; } - Utils::startSystemMove(m_window); + startSystemMove2(); } void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) @@ -267,7 +292,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) if (!event) { return; } - if (m_params.options & Option::DisableSystemMenu) { + if (m_settings.options & Option::DisableSystemMenu) { return; } if (event->button() != Qt::RightButton) { @@ -284,16 +309,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) if (!isInTitleBarDraggableArea(scenePos)) { return; } -#ifdef Q_OS_WINDOWS -# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPoint globalPos = event->globalPosition().toPoint(); -# else - const QPoint globalPos = event->globalPos(); -# endif - const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint(); - Utils::showSystemMenu(m_params.windowId, nativePos, m_params.options, - m_params.systemMenuOffset, m_params.isWindowFixedSize); -#endif + showSystemMenu(scenePos); } void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) @@ -302,7 +318,7 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) if (!event) { return; } - if ((m_params.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) { + if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) { return; } if (event->button() != Qt::LeftButton) { @@ -365,39 +381,54 @@ void FramelessWidgetsHelper::initialize() m_params.getWindowState = [this]() -> Qt::WindowState { return Utils::windowStatesToWindowState(q->windowState()); }; m_params.setWindowState = [this](const Qt::WindowState state) -> void { q->setWindowState(state); }; m_params.getWindowHandle = [this]() -> QWindow * { return m_window; }; - if (m_params.options & Option::UseStandardWindowLayout) { + m_params.windowToScreen = [this](const QPoint &pos) -> QPoint { return q->mapToGlobal(pos); }; + m_params.screenToWindow = [this](const QPoint &pos) -> QPoint { return q->mapFromGlobal(pos); }; + m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); }; + m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); }; + m_params.getWindowDevicePixelRatio = [this]() -> qreal { return q->devicePixelRatioF(); }; + if (m_settings.options & Option::UseStandardWindowLayout) { if (q->inherits(QT_MAINWINDOW_CLASS_NAME)) { - m_params.options &= ~Options(Option::UseStandardWindowLayout); + m_settings.options &= ~Options(Option::UseStandardWindowLayout); qWarning() << "\"Option::UseStandardWindowLayout\" is not compatible with QMainWindow and it's subclasses." " Enabling this option will mess up with your main window's layout."; } } - if (m_params.options & Option::BeCompatibleWithQtFramelessWindowHint) { + if (m_settings.options & Option::BeCompatibleWithQtFramelessWindowHint) { Utils::tryToBeCompatibleWithQtFramelessWindowHint(windowId, m_params.getWindowFlags, m_params.setWindowFlags, true); } FramelessWindowsManager * const manager = FramelessWindowsManager::instance(); - manager->addWindow(m_params); + manager->addWindow(m_settings, m_params); connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){ - if (m_params.options & Option::UseStandardWindowLayout) { + if (m_settings.options & Option::UseStandardWindowLayout) { updateSystemTitleBarStyleSheet(); updateSystemButtonsIcon(); q->update(); } QMetaObject::invokeMethod(q, "systemThemeChanged"); }); - connect(m_window, &QWindow::windowStateChanged, this, [this](){ + connect(m_window, &QWindow::visibilityChanged, this, [this](){ + QMetaObject::invokeMethod(q, "hiddenChanged"); + QMetaObject::invokeMethod(q, "normalChanged"); QMetaObject::invokeMethod(q, "zoomedChanged"); }); setupInitialUi(); - if (m_params.options & Option::DisableResizing) { + if (m_settings.options & Option::UseStandardWindowLayout) { + Q_ASSERT(m_systemMinimizeButton); + Q_ASSERT(m_systemMaximizeButton); + Q_ASSERT(m_systemCloseButton); + m_settings.minimizeButton = m_systemMinimizeButton; + m_settings.maximizeButton = m_systemMaximizeButton; + m_settings.closeButton = m_systemCloseButton; + } + if (m_settings.options & Option::DisableResizing) { setFixedSize(true, true); } } void FramelessWidgetsHelper::createSystemTitleBar() { - if (!(m_params.options & Option::UseStandardWindowLayout)) { + if (!(m_settings.options & Option::UseStandardWindowLayout)) { return; } m_systemTitleBarWidget = new QWidget(q); @@ -440,7 +471,7 @@ void FramelessWidgetsHelper::createSystemTitleBar() void FramelessWidgetsHelper::createUserContentContainer() { - if (!(m_params.options & Option::UseStandardWindowLayout)) { + if (!(m_settings.options & Option::UseStandardWindowLayout)) { return; } m_userContentContainerWidget = new QWidget(q); @@ -453,7 +484,7 @@ void FramelessWidgetsHelper::createUserContentContainer() void FramelessWidgetsHelper::setupInitialUi() { - if (m_params.options & Option::UseStandardWindowLayout) { + if (m_settings.options & Option::UseStandardWindowLayout) { createSystemTitleBar(); createUserContentContainer(); m_mainLayout = new QVBoxLayout(q); @@ -468,33 +499,69 @@ void FramelessWidgetsHelper::setupInitialUi() updateContentsMargins(); } +QRect FramelessWidgetsHelper::mapWidgetGeometryToScene(const QWidget * const widget) const +{ + Q_ASSERT(widget); + if (!widget) { + return {}; + } + const QPoint originPoint = widget->mapTo(q, QPoint(0, 0)); + const QSize size = widget->size(); + return QRect(originPoint, size); +} + +bool FramelessWidgetsHelper::isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const +{ + Q_ASSERT(button); + if (!button) { + return false; + } + *button = SystemButtonType::Unknown; + if (!m_settings.minimizeButton || !m_settings.maximizeButton || !m_settings.closeButton) { + return false; + } + if (!m_settings.minimizeButton->isWidgetType() || !m_settings.maximizeButton->isWidgetType() + || !m_settings.closeButton->isWidgetType()) { + return false; + } + const auto minBtn = qobject_cast(m_settings.minimizeButton); + if (minBtn->geometry().contains(pos)) { + *button = SystemButtonType::Minimize; + return true; + } + const auto maxBtn = qobject_cast(m_settings.maximizeButton); + if (maxBtn->geometry().contains(pos)) { + *button = SystemButtonType::Maximize; + return true; + } + const auto closeBtn = qobject_cast(m_settings.closeButton); + if (closeBtn->geometry().contains(pos)) { + *button = SystemButtonType::Close; + return true; + } + return false; +} + bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const { const QRegion draggableRegion = [this]() -> QRegion { - const auto mapGeometryToScene = [this](const QWidget * const widget) -> QRect { - Q_ASSERT(widget); - if (!widget) { - return {}; - } - return QRect(widget->mapTo(q, QPoint(0, 0)), widget->size()); - }; if (m_userTitleBarWidget) { - QRegion region = mapGeometryToScene(m_userTitleBarWidget); + QRegion region = mapWidgetGeometryToScene(m_userTitleBarWidget); if (!m_hitTestVisibleWidgets.isEmpty()) { for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) { Q_ASSERT(widget); if (widget) { - region -= mapGeometryToScene(widget); + region -= mapWidgetGeometryToScene(widget); } } } return region; } - if (m_params.options & Option::UseStandardWindowLayout) { - QRegion region = mapGeometryToScene(m_systemTitleBarWidget); - region -= mapGeometryToScene(m_systemMinimizeButton); - region -= mapGeometryToScene(m_systemMaximizeButton); - region -= mapGeometryToScene(m_systemCloseButton); + if (m_settings.options & Option::UseStandardWindowLayout) { + QRegion region = mapWidgetGeometryToScene(m_systemTitleBarWidget); + region -= mapWidgetGeometryToScene(m_systemMinimizeButton); + region -= mapWidgetGeometryToScene(m_systemMaximizeButton); + region -= mapWidgetGeometryToScene(m_systemCloseButton); return region; } return {}; @@ -506,7 +573,7 @@ bool FramelessWidgetsHelper::shouldDrawFrameBorder() const { #ifdef Q_OS_WINDOWS return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater() - && isNormal() && !(m_params.options & Option::DontDrawTopWindowFrameBorder)); + && isNormal() && !(m_settings.options & Option::DontDrawTopWindowFrameBorder)); #else return false; #endif @@ -528,7 +595,7 @@ void FramelessWidgetsHelper::updateContentsMargins() void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() { - if (!(m_params.options & Option::UseStandardWindowLayout)) { + if (!(m_settings.options & Option::UseStandardWindowLayout)) { return; } const bool active = q->isActiveWindow(); @@ -563,7 +630,7 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() void FramelessWidgetsHelper::updateSystemButtonsIcon() { - if (!(m_params.options & Option::UseStandardWindowLayout)) { + if (!(m_settings.options & Option::UseStandardWindowLayout)) { return; } const SystemTheme theme = ((Utils::shouldAppsUseDarkMode() || Utils::isTitleBarColorized()) ? SystemTheme::Dark : SystemTheme::Light); @@ -609,4 +676,44 @@ void FramelessWidgetsHelper::moveToDesktopCenter() m_params.getWindowSize, m_params.setWindowPosition, true); } +void FramelessWidgetsHelper::bringToFront() +{ + if (q->isHidden()) { + q->show(); + } + if (q->isMinimized()) { + q->setWindowState(q->windowState() & ~Qt::WindowMinimized); + } + q->raise(); + q->activateWindow(); +} + +void FramelessWidgetsHelper::showSystemMenu(const QPoint &pos) +{ +#ifdef Q_OS_WINDOWS + const QPoint globalPos = q->mapToGlobal(pos); + const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint(); + Utils::showSystemMenu(m_params.windowId, nativePos, m_settings.options, + m_settings.systemMenuOffset, m_params.isWindowFixedSize); +#endif +} + +void FramelessWidgetsHelper::startSystemMove2() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + m_window->startSystemMove(); +#else + Utils::startSystemMove(m_window); +#endif +} + +void FramelessWidgetsHelper::startSystemResize2(const Qt::Edges edges) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + m_window->startSystemResize(edges); +#else + Utils::startSystemResize(m_window, edges); +#endif +} + FRAMELESSHELPER_END_NAMESPACE