win implementation is mostly settled now

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-03-26 15:31:16 +08:00
parent 389a342a81
commit d4e711d679
30 changed files with 948 additions and 690 deletions

View File

@ -45,7 +45,6 @@ int main(int argc, char *argv[])
QApplication application(argc, argv); QApplication application(argc, argv);
MainWindow mainWindow; MainWindow mainWindow;
mainWindow.moveToDesktopCenter();
mainWindow.show(); mainWindow.show();
return QCoreApplication::exec(); return QCoreApplication::exec();

View File

@ -29,14 +29,11 @@ import org.wangwenx190.FramelessHelper 1.0
FramelessWindow { FramelessWindow {
id: window id: window
visible: true
width: 800 width: 800
height: 600 height: 600
title: qsTr("Hello, World! - Qt Quick") title: qsTr("Hello, World! - Qt Quick")
color: FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor color: FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor
Component.onCompleted: {
window.moveToDesktopCenter();
window.visible = true;
}
Timer { Timer {
interval: 500 interval: 500

View File

@ -45,7 +45,6 @@ int main(int argc, char *argv[])
QApplication application(argc, argv); QApplication application(argc, argv);
Widget widget; Widget widget;
widget.moveToDesktopCenter();
widget.show(); widget.show();
return QCoreApplication::exec(); return QCoreApplication::exec();

View File

@ -32,7 +32,19 @@ FRAMELESSHELPER_USE_NAMESPACE
using namespace Global; 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(); setupUi();
startTimer(500); startTimer(500);

View File

@ -38,7 +38,7 @@ public:
explicit FramelessHelperQt(QObject *parent = nullptr); explicit FramelessHelperQt(QObject *parent = nullptr);
~FramelessHelperQt() override; ~FramelessHelperQt() override;
static void addWindow(const Global::FramelessHelperParams &params); static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
protected: protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -37,7 +37,7 @@ public:
explicit FramelessHelperWin(); explicit FramelessHelperWin();
~FramelessHelperWin() override; ~FramelessHelperWin() override;
static void addWindow(const Global::FramelessHelperParams &params); static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, NATIVE_EVENT_RESULT_TYPE *result) override; Q_NODISCARD bool nativeEventFilter(const QByteArray &eventType, void *message, NATIVE_EVENT_RESULT_TYPE *result) override;
}; };

View File

@ -28,6 +28,7 @@
#include <QtCore/qobject.h> #include <QtCore/qobject.h>
#include <QtCore/qpoint.h> #include <QtCore/qpoint.h>
#include <QtCore/qsize.h> #include <QtCore/qsize.h>
#include <QtCore/qpointer.h>
#include <QtGui/qcolor.h> #include <QtGui/qcolor.h>
#include <QtGui/qwindowdefs.h> #include <QtGui/qwindowdefs.h>
@ -181,10 +182,11 @@ enum class SystemButtonType : int
{ {
Unknown = -1, Unknown = -1,
WindowIcon = 0, WindowIcon = 0,
Minimize = 1, Help = 1,
Maximize = 2, Minimize = 2,
Restore = 3, Maximize = 3,
Close = 4 Restore = 4,
Close = 5
}; };
Q_ENUM_NS(SystemButtonType) Q_ENUM_NS(SystemButtonType)
@ -224,30 +226,65 @@ using SetWindowStateCallback = std::function<void(const Qt::WindowState)>;
using GetWindowHandleCallback = std::function<QWindow *()>; using GetWindowHandleCallback = std::function<QWindow *()>;
struct FramelessHelperParams using WindowToScreenCallback = std::function<QPoint(const QPoint &)>;
using ScreenToWindowCallback = std::function<QPoint(const QPoint &)>;
using IsInsideSystemButtonsCallback = std::function<bool(const QPoint &, SystemButtonType *)>;
using IsInsideTitleBarDraggableAreaCallback = std::function<bool(const QPoint &)>;
using GetWindowDevicePixelRatioCallback = std::function<qreal()>;
struct UserSettings
{ {
WId windowId = 0; QPoint startupPosition = {};
QSize startupSize = {};
Qt::WindowState startupState = Qt::WindowNoState;
Options options = {}; Options options = {};
QPoint systemMenuOffset = {}; QPoint systemMenuOffset = {};
QPointer<QObject> minimizeButton = nullptr;
QPointer<QObject> maximizeButton = nullptr;
QPointer<QObject> closeButton = nullptr;
};
struct SystemParameters
{
WId windowId = 0;
GetWindowFlagsCallback getWindowFlags = nullptr; GetWindowFlagsCallback getWindowFlags = nullptr;
SetWindowFlagsCallback setWindowFlags = nullptr; SetWindowFlagsCallback setWindowFlags = nullptr;
GetWindowSizeCallback getWindowSize = nullptr; GetWindowSizeCallback getWindowSize = nullptr;
SetWindowSizeCallback setWindowSize = nullptr; SetWindowSizeCallback setWindowSize = nullptr;
GetWindowPositionCallback getWindowPosition = nullptr; GetWindowPositionCallback getWindowPosition = nullptr;
SetWindowPositionCallback setWindowPosition = nullptr; SetWindowPositionCallback setWindowPosition = nullptr;
GetWindowScreenCallback getWindowScreen = nullptr; GetWindowScreenCallback getWindowScreen = nullptr;
IsWindowFixedSizeCallback isWindowFixedSize = nullptr; IsWindowFixedSizeCallback isWindowFixedSize = nullptr;
SetWindowFixedSizeCallback setWindowFixedSize = nullptr; SetWindowFixedSizeCallback setWindowFixedSize = nullptr;
GetWindowStateCallback getWindowState = nullptr; GetWindowStateCallback getWindowState = nullptr;
SetWindowStateCallback setWindowState = nullptr; SetWindowStateCallback setWindowState = nullptr;
GetWindowHandleCallback getWindowHandle = nullptr; GetWindowHandleCallback getWindowHandle = nullptr;
WindowToScreenCallback windowToScreen = nullptr;
ScreenToWindowCallback screenToWindow = nullptr;
IsInsideSystemButtonsCallback isInsideSystemButtons = nullptr;
IsInsideTitleBarDraggableAreaCallback isInsideTitleBarDraggableArea = nullptr;
GetWindowDevicePixelRatioCallback getWindowDevicePixelRatio = nullptr;
[[nodiscard]] inline bool isValid() const [[nodiscard]] inline bool isValid() const
{ {
return (windowId && getWindowFlags && setWindowFlags && getWindowSize return (windowId && getWindowFlags && setWindowFlags && getWindowSize
&& setWindowSize && getWindowPosition && setWindowPosition && setWindowSize && getWindowPosition && setWindowPosition
&& isWindowFixedSize && setWindowFixedSize && getWindowState && isWindowFixedSize && setWindowFixedSize && getWindowState
&& setWindowState && getWindowHandle); && setWindowState && getWindowHandle && windowToScreen && screenToWindow
&& isInsideSystemButtons && isInsideTitleBarDraggableArea
&& getWindowDevicePixelRatio);
} }
}; };
@ -255,4 +292,5 @@ struct FramelessHelperParams
FRAMELESSHELPER_END_NAMESPACE 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))

View File

@ -46,7 +46,7 @@ public:
Q_NODISCARD static Global::SystemTheme systemTheme(); Q_NODISCARD static Global::SystemTheme systemTheme();
public Q_SLOTS: public Q_SLOTS:
static void addWindow(const Global::FramelessHelperParams &params); static void addWindow(const Global::UserSettings &settings, const Global::SystemParameters &params);
Q_SIGNALS: Q_SIGNALS:
void systemThemeChanged(); void systemThemeChanged();

View File

@ -43,7 +43,7 @@ getSystemButtonIconResource(const Global::SystemButtonType button,
const Global::SystemTheme theme, const Global::SystemTheme theme,
const Global::ResourceType type); const Global::ResourceType type);
FRAMELESSHELPER_CORE_API void sendMouseReleaseEvent(); 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( FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
const Global::GetWindowScreenCallback &getWindowScreen, const Global::GetWindowScreenCallback &getWindowScreen,
const Global::GetWindowSizeCallback &getWindowSize, const Global::GetWindowSizeCallback &getWindowSize,
@ -60,15 +60,15 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101809OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101809OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin11OrGreater(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin11OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled();
FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId winId); FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId windowId);
FRAMELESSHELPER_CORE_API void updateWindowFrameMargins(const WId winId, const bool reset); FRAMELESSHELPER_CORE_API void updateWindowFrameMargins(const WId windowId, const bool reset);
FRAMELESSHELPER_CORE_API void updateInternalWindowFrameMargins(QWindow *window, const bool enable); FRAMELESSHELPER_CORE_API void updateInternalWindowFrameMargins(QWindow *window, const bool enable);
[[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemErrorMessage(const QString &function); [[nodiscard]] FRAMELESSHELPER_CORE_API QString getSystemErrorMessage(const QString &function);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFullScreen(const WId winId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isFullScreen(const WId windowId);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowNoState(const WId winId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowNoState(const WId windowId);
FRAMELESSHELPER_CORE_API void syncWmPaintWithDwm(); FRAMELESSHELPER_CORE_API void syncWmPaintWithDwm();
FRAMELESSHELPER_CORE_API void showSystemMenu( FRAMELESSHELPER_CORE_API void showSystemMenu(
const WId winId, const WId windowId,
const QPoint &pos, const QPoint &pos,
const Global::Options options, const Global::Options options,
const QPoint &offset, const QPoint &offset,
@ -78,32 +78,32 @@ FRAMELESSHELPER_CORE_API void showSystemMenu(
[[nodiscard]] FRAMELESSHELPER_CORE_API Global::DwmColorizationArea getDwmColorizationArea(); [[nodiscard]] FRAMELESSHELPER_CORE_API Global::DwmColorizationArea getDwmColorizationArea();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isHighContrastModeEnabled(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isHighContrastModeEnabled();
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getPrimaryScreenDpi(const bool horizontal); [[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 getWindowDpi(const WId windowId, const bool horizontal);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId winId, [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId,
const bool horizontal, const bool horizontal,
const bool scaled); const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionHeight(const WId winId, const bool scaled); [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId winId, const bool scaled); [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId winId, [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId,
const bool scaled); const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getFrameBorderColor(const bool active); [[nodiscard]] FRAMELESSHELPER_CORE_API QColor getFrameBorderColor(const bool active);
FRAMELESSHELPER_CORE_API void updateWindowFrameBorderColor(const WId winId, const bool dark); FRAMELESSHELPER_CORE_API void updateWindowFrameBorderColor(const WId windowId, const bool dark);
FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId winId); FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId windowId);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized();
FRAMELESSHELPER_CORE_API void installSystemMenuHook( FRAMELESSHELPER_CORE_API void installSystemMenuHook(
const WId winId, const WId windowId,
const Global::Options options, const Global::Options options,
const QPoint &offset, const QPoint &offset,
const Global::IsWindowFixedSizeCallback &isWindowFixedSize); 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( FRAMELESSHELPER_CORE_API void tryToBeCompatibleWithQtFramelessWindowHint(
const WId winId, const WId windowId,
const Global::GetWindowFlagsCallback &getWindowFlags, const Global::GetWindowFlagsCallback &getWindowFlags,
const Global::SetWindowFlagsCallback &setWindowFlags, const Global::SetWindowFlagsCallback &setWindowFlags,
const bool enable); 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(); FRAMELESSHELPER_CORE_API void tryToEnableHighestDpiAwarenessLevel();
#endif // Q_OS_WINDOWS #endif // Q_OS_WINDOWS

View File

@ -1 +0,0 @@
#include <framelessquickeventfilter.h>

View File

@ -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 <QtCore/qobject.h>
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 &params);
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

View File

@ -40,15 +40,23 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow
#endif #endif
Q_DECLARE_PRIVATE(FramelessQuickWindow) Q_DECLARE_PRIVATE(FramelessQuickWindow)
Q_DISABLE_COPY_MOVE(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(bool fixedSize READ fixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL)
Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL) Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL)
public: public:
explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::Options options = {}); explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::UserSettings &settings = {});
~FramelessQuickWindow() override; ~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; Q_NODISCARD bool fixedSize() const;
void setFixedSize(const bool value); void setFixedSize(const bool value);
@ -65,9 +73,20 @@ public Q_SLOTS:
void setTitleBarItem(QQuickItem *item); void setTitleBarItem(QQuickItem *item);
void setHitTestVisible(QQuickItem *item); void setHitTestVisible(QQuickItem *item);
void moveToDesktopCenter(); 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: Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void minimizedChanged();
void zoomedChanged(); void zoomedChanged();
void fullScreenChanged();
void fixedSizeChanged(); void fixedSizeChanged();
void frameBorderColorChanged(); void frameBorderColorChanged();

View File

@ -35,16 +35,17 @@ class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessMainWindow) 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 zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged 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* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
public: 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; ~FramelessMainWindow() override;
Q_NODISCARD Q_INVOKABLE bool isNormal() const; Q_NODISCARD bool isNormal() const;
Q_NODISCARD bool isZoomed() const; Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFixedSize() const; Q_NODISCARD bool isFixedSize() const;
@ -58,8 +59,13 @@ public Q_SLOTS:
void toggleMaximized(); void toggleMaximized();
void toggleFullScreen(); void toggleFullScreen();
void moveToDesktopCenter(); void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2();
void startSystemResize2(const Qt::Edges edges);
protected: protected:
void showEvent(QShowEvent *event) override;
void changeEvent(QEvent *event) override; void changeEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
@ -67,6 +73,8 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override;
Q_SIGNALS: Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void zoomedChanged(); void zoomedChanged();
void fixedSizeChanged(); void fixedSizeChanged();
void titleBarWidgetChanged(); void titleBarWidgetChanged();

View File

@ -35,17 +35,18 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessWidget) 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 zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged 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* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
Q_PROPERTY(QWidget* contentWidget READ contentWidget WRITE setContentWidget NOTIFY contentWidgetChanged FINAL) Q_PROPERTY(QWidget* contentWidget READ contentWidget WRITE setContentWidget NOTIFY contentWidgetChanged FINAL)
public: public:
explicit FramelessWidget(QWidget *parent = nullptr, const Global::Options options = {}); explicit FramelessWidget(QWidget *parent = nullptr, const Global::UserSettings &settings = {});
~FramelessWidget() override; ~FramelessWidget() override;
Q_NODISCARD Q_INVOKABLE bool isNormal() const; Q_NODISCARD bool isNormal() const;
Q_NODISCARD bool isZoomed() const; Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFixedSize() const; Q_NODISCARD bool isFixedSize() const;
@ -62,8 +63,13 @@ public Q_SLOTS:
void toggleMaximized(); void toggleMaximized();
void toggleFullScreen(); void toggleFullScreen();
void moveToDesktopCenter(); void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2();
void startSystemResize2(const Qt::Edges edges);
protected: protected:
void showEvent(QShowEvent *event) override;
void changeEvent(QEvent *event) override; void changeEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
@ -71,6 +77,8 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override;
Q_SIGNALS: Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void zoomedChanged(); void zoomedChanged();
void fixedSizeChanged(); void fixedSizeChanged();
void titleBarWidgetChanged(); void titleBarWidgetChanged();

View File

@ -32,6 +32,7 @@ QT_BEGIN_NAMESPACE
class QLabel; class QLabel;
class QPushButton; class QPushButton;
class QVBoxLayout; class QVBoxLayout;
class QShowEvent;
class QPaintEvent; class QPaintEvent;
class QMouseEvent; class QMouseEvent;
QT_END_NAMESPACE QT_END_NAMESPACE
@ -44,7 +45,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject
Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper) Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper)
public: public:
explicit FramelessWidgetsHelper(QWidget *q, const Global::Options options = {}); explicit FramelessWidgetsHelper(QWidget *q, const Global::UserSettings &settings = {});
~FramelessWidgetsHelper() override; ~FramelessWidgetsHelper() override;
Q_NODISCARD Q_INVOKABLE bool isNormal() const; Q_NODISCARD Q_INVOKABLE bool isNormal() const;
@ -58,6 +59,7 @@ public:
Q_INVOKABLE void setContentWidget(QWidget *widget); Q_INVOKABLE void setContentWidget(QWidget *widget);
Q_NODISCARD Q_INVOKABLE QWidget *getContentWidget() const; Q_NODISCARD Q_INVOKABLE QWidget *getContentWidget() const;
Q_INVOKABLE void showEventHandler(QShowEvent *event);
Q_INVOKABLE void changeEventHandler(QEvent *event); Q_INVOKABLE void changeEventHandler(QEvent *event);
Q_INVOKABLE void paintEventHandler(QPaintEvent *event); Q_INVOKABLE void paintEventHandler(QPaintEvent *event);
Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event);
@ -69,12 +71,18 @@ public Q_SLOTS:
void toggleMaximized(); void toggleMaximized();
void toggleFullScreen(); void toggleFullScreen();
void moveToDesktopCenter(); void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2();
void startSystemResize2(const Qt::Edges edges);
private: private:
void initialize(); void initialize();
void createSystemTitleBar(); void createSystemTitleBar();
void createUserContentContainer(); void createUserContentContainer();
void setupInitialUi(); 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 isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldDrawFrameBorder() const; Q_NODISCARD bool shouldDrawFrameBorder() const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const; Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
@ -100,7 +108,9 @@ private:
QVBoxLayout *m_userContentContainerLayout = nullptr; QVBoxLayout *m_userContentContainerLayout = nullptr;
Qt::WindowState m_savedWindowState = {}; Qt::WindowState m_savedWindowState = {};
QWindow *m_window = nullptr; QWindow *m_window = nullptr;
Global::FramelessHelperParams m_params = {}; Global::UserSettings m_settings = {};
Global::SystemParameters m_params = {};
bool m_windowExposed = false;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -32,16 +32,17 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; using namespace Global;
struct QtHelperInternalData struct QtHelperData
{ {
FramelessHelperParams params = {}; UserSettings settings = {};
FramelessHelperQt *framelessHelper = nullptr; SystemParameters params = {};
FramelessHelperQt *eventFilter = nullptr;
}; };
struct QtHelper struct QtHelper
{ {
QMutex mutex = {}; QMutex mutex = {};
QHash<WId, QtHelperInternalData> data = {}; QHash<WId, QtHelperData> data = {};
}; };
Q_GLOBAL_STATIC(QtHelper, g_qtHelper) Q_GLOBAL_STATIC(QtHelper, g_qtHelper)
@ -50,7 +51,7 @@ FramelessHelperQt::FramelessHelperQt(QObject *parent) : QObject(parent) {}
FramelessHelperQt::~FramelessHelperQt() = default; FramelessHelperQt::~FramelessHelperQt() = default;
void FramelessHelperQt::addWindow(const FramelessHelperParams &params) void FramelessHelperQt::addWindow(const UserSettings &settings, const SystemParameters &params)
{ {
Q_ASSERT(params.isValid()); Q_ASSERT(params.isValid());
if (!params.isValid()) { if (!params.isValid()) {
@ -61,15 +62,16 @@ void FramelessHelperQt::addWindow(const FramelessHelperParams &params)
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
return; return;
} }
QtHelperInternalData data = {}; QtHelperData data = {};
data.settings = settings;
data.params = params; data.params = params;
QWindow *window = params.getWindowHandle(); QWindow *window = params.getWindowHandle();
// Give it a parent so that it can be deleted even if we forget to do so. // 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()->data.insert(params.windowId, data);
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
window->setFlags(window->flags() | Qt::FramelessWindowHint); params.setWindowFlags(params.getWindowFlags() | Qt::FramelessWindowHint);
window->installEventFilter(data.framelessHelper); window->installEventFilter(data.eventFilter);
} }
bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
@ -95,7 +97,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
return false; return false;
} }
const QtHelperInternalData data = g_qtHelper()->data.value(windowId); const QtHelperData data = g_qtHelper()->data.value(windowId);
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
if (data.params.isWindowFixedSize()) { if (data.params.isWindowFixedSize()) {
return false; return false;
@ -108,7 +110,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
#endif #endif
switch (type) { switch (type) {
case QEvent::MouseMove: { case QEvent::MouseMove: {
if (data.params.options & Option::DontTouchCursorShape) { if (data.settings.options & Option::DontTouchCursorShape) {
return false; return false;
} }
const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos);

View File

@ -37,11 +37,17 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; using namespace Global;
struct Win32HelperData
{
UserSettings settings = {};
SystemParameters params = {};
};
struct Win32Helper struct Win32Helper
{ {
QMutex mutex = {}; QMutex mutex = {};
QScopedPointer<FramelessHelperWin> nativeEventFilter; QScopedPointer<FramelessHelperWin> nativeEventFilter;
QHash<WId, FramelessHelperParams> data = {}; QHash<WId, Win32HelperData> data = {};
}; };
Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
@ -50,7 +56,7 @@ FramelessHelperWin::FramelessHelperWin() : QAbstractNativeEventFilter() {}
FramelessHelperWin::~FramelessHelperWin() = default; FramelessHelperWin::~FramelessHelperWin() = default;
void FramelessHelperWin::addWindow(const FramelessHelperParams &params) void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemParameters &params)
{ {
Q_ASSERT(params.isValid()); Q_ASSERT(params.isValid());
if (!params.isValid()) { if (!params.isValid()) {
@ -61,27 +67,29 @@ void FramelessHelperWin::addWindow(const FramelessHelperParams &params)
g_win32Helper()->mutex.unlock(); g_win32Helper()->mutex.unlock();
return; return;
} }
FramelessHelperParams localParams = params; Win32HelperData data = {};
if ((localParams.options & Option::ForceHideWindowFrameBorder) data.settings = settings;
&& (localParams.options & Option::ForceShowWindowFrameBorder)) { data.params = params;
localParams.options &= ~(Option::ForceHideWindowFrameBorder | Option::ForceShowWindowFrameBorder); 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 " qWarning() << "You can't use both \"Option::ForceHideWindowFrameBorder\" and "
"\"Option::ForceShowWindowFrameBorder\" at the same time."; "\"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()) { if (g_win32Helper()->nativeEventFilter.isNull()) {
g_win32Helper()->nativeEventFilter.reset(new FramelessHelperWin); g_win32Helper()->nativeEventFilter.reset(new FramelessHelperWin);
qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data()); qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data());
} }
g_win32Helper()->mutex.unlock(); g_win32Helper()->mutex.unlock();
if (!(localParams.options & Option::DontTouchQtInternals)) { if (!(settings.options & Option::DontTouchQtInternals)) {
Utils::fixupQtInternals(localParams.windowId); Utils::fixupQtInternals(params.windowId);
} }
Utils::updateInternalWindowFrameMargins(localParams.getWindowHandle(), true); Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true);
Utils::updateWindowFrameMargins(localParams.windowId, false); Utils::updateWindowFrameMargins(params.windowId, false);
if (!(localParams.options & Option::DontTouchWindowFrameBorderColor)) { if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) {
const bool dark = Utils::shouldAppsUseDarkMode(); 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 #else
const auto msg = static_cast<LPMSG>(message); const auto msg = static_cast<LPMSG>(message);
#endif #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? // Why sometimes the window handle is null? Is it designed to be like this?
// Anyway, we should skip the entire processing in this case. // Anyway, we should skip the entire processing in this case.
return false; return false;
} }
const auto winId = reinterpret_cast<WId>(msg->hwnd); const auto windowId = reinterpret_cast<WId>(hWnd);
g_win32Helper()->mutex.lock(); g_win32Helper()->mutex.lock();
if (!g_win32Helper()->data.contains(winId)) { if (!g_win32Helper()->data.contains(windowId)) {
g_win32Helper()->mutex.unlock(); g_win32Helper()->mutex.unlock();
return false; return false;
} }
const FramelessHelperParams params = g_win32Helper()->data.value(winId); const Win32HelperData data = g_win32Helper()->data.value(windowId);
g_win32Helper()->mutex.unlock(); g_win32Helper()->mutex.unlock();
const bool frameBorderVisible = [&params]() -> bool { const bool frameBorderVisible = [&data]() -> bool {
if (params.options & Option::ForceShowWindowFrameBorder) { if (data.settings.options & Option::ForceShowWindowFrameBorder) {
return true; return true;
} }
if (params.options & Option::ForceHideWindowFrameBorder) { if (data.settings.options & Option::ForceHideWindowFrameBorder) {
return false; return false;
} }
return Utils::isWindowFrameBorderVisible(); return Utils::isWindowFrameBorderVisible();
}(); }();
const bool fixedSize = params.isWindowFixedSize(); const UINT uMsg = msg->message;
switch (msg->message) { const WPARAM wParam = msg->wParam;
const LPARAM lParam = msg->lParam;
switch (uMsg) {
case WM_NCCALCSIZE: { case WM_NCCALCSIZE: {
// Windows是根据这个消息的返回值来设置窗口的客户区窗口中真正显示的内容 // Windows是根据这个消息的返回值来设置窗口的客户区窗口中真正显示的内容
// 和非客户区标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分 // 和非客户区标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分
@ -208,14 +219,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// implement an elaborate client-area preservation technique, and // implement an elaborate client-area preservation technique, and
// simply return 0, which means "preserve the entire old client area // simply return 0, which means "preserve the entire old client area
// and align it with the upper-left corner of our new client area". // and align it with the upper-left corner of our new client area".
const auto clientRect = ((static_cast<BOOL>(msg->wParam) == FALSE) const auto clientRect = ((static_cast<BOOL>(wParam) == FALSE)
? reinterpret_cast<LPRECT>(msg->lParam) ? reinterpret_cast<LPRECT>(lParam)
: &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam))->rgrc[0]); : &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]);
if (frameBorderVisible) { if (frameBorderVisible) {
// Store the original top before the default window proc applies the default frame. // Store the original top before the default window proc applies the default frame.
const LONG originalTop = clientRect->top; const LONG originalTop = clientRect->top;
// Apply the default frame. // 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) { if (ret != 0) {
*result = ret; *result = ret;
return true; 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. // Re-apply the original top from before the size of the default frame was applied.
clientRect->top = originalTop; clientRect->top = originalTop;
} }
const bool max = IsMaximized(msg->hwnd); const bool max = IsMaximized(hWnd);
const bool full = Utils::isFullScreen(winId); const bool full = Utils::isFullScreen(windowId);
// We don't need this correction when we're fullscreen. We will // 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 // have the WS_POPUP size, so we don't have to worry about
// borders, and the default frame will be fine. // 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 // 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 // do not appear because you don't need them (because you can't resize
// a window when it's maximized unless you restore it). // 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; clientRect->top += frameSizeY;
if (!frameBorderVisible) { if (!frameBorderVisible) {
clientRect->bottom -= frameSizeY; clientRect->bottom -= frameSizeY;
const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true); const int frameSizeX = Utils::getResizeBorderThickness(windowId, true, true);
clientRect->left += frameSizeX; clientRect->left += frameSizeX;
clientRect->right -= frameSizeX; clientRect->right -= frameSizeX;
} }
@ -265,7 +276,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
MONITORINFO monitorInfo; MONITORINFO monitorInfo;
SecureZeroMemory(&monitorInfo, sizeof(monitorInfo)); SecureZeroMemory(&monitorInfo, sizeof(monitorInfo));
monitorInfo.cbSize = sizeof(monitorInfo); monitorInfo.cbSize = sizeof(monitorInfo);
const HMONITOR monitor = MonitorFromWindow(msg->hwnd, MONITOR_DEFAULTTONEAREST); const HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
if (!monitor) { if (!monitor) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("MonitorFromWindow")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("MonitorFromWindow"));
break; break;
@ -297,7 +308,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
_abd.cbSize = sizeof(_abd); _abd.cbSize = sizeof(_abd);
_abd.hWnd = FindWindowW(L"Shell_TrayWnd", nullptr); _abd.hWnd = FindWindowW(L"Shell_TrayWnd", nullptr);
if (_abd.hWnd) { if (_abd.hWnd) {
const HMONITOR windowMonitor = MonitorFromWindow(msg->hwnd, MONITOR_DEFAULTTONEAREST); const HMONITOR windowMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
if (!windowMonitor) { if (!windowMonitor) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("MonitorFromWindow")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("MonitorFromWindow"));
break; 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 // 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 // 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. // whether it exists on Windows XP to Windows Vista or not.
*result = ((static_cast<BOOL>(msg->wParam) == FALSE) ? 0 : WVR_REDRAW); *result = ((static_cast<BOOL>(wParam) == FALSE) ? 0 : WVR_REDRAW);
return true; return true;
} }
case WM_NCHITTEST: { 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 // another branch, if you are interested in it, you can give it a
// try. // try.
if (fixedSize) { if (data.params.isWindowFixedSize()) {
*result = HTCLIENT; *result = HTCLIENT;
return true; 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; POINT localPos = globalPos;
if (ScreenToClient(msg->hwnd, &localPos) == FALSE) { if (ScreenToClient(hWnd, &localPos) == FALSE) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("ScreenToClient")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("ScreenToClient"));
break; break;
} }
const bool max = IsMaximized(msg->hwnd); if (data.settings.options & Option::MaximizeButtonDocking) {
const bool full = Utils::isFullScreen(winId); const QPoint scenePos = QPointF(QPointF(qreal(localPos.x), qreal(localPos.y))
const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true); / 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 isTop = (localPos.y < frameSizeY);
const bool isTitleBar = (false && !(params.options & Option::DisableDragging)); const bool isTitleBar = (false && !(data.settings.options & Option::DisableDragging));
if (frameBorderVisible) { if (frameBorderVisible) {
// This will handle the left, right and bottom parts of the frame // This will handle the left, right and bottom parts of the frame
// because we didn't change them. // 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) { if (originalRet != HTCLIENT) {
*result = originalRet; *result = originalRet;
return true; return true;
@ -492,7 +529,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
return true; return true;
} }
RECT clientRect = {0, 0, 0, 0}; RECT clientRect = {0, 0, 0, 0};
if (GetClientRect(msg->hwnd, &clientRect) == FALSE) { if (GetClientRect(hWnd, &clientRect) == FALSE) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetClientRect")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetClientRect"));
break; break;
} }
@ -501,7 +538,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
const bool isBottom = (localPos.y >= (height - frameSizeY)); const bool isBottom = (localPos.y >= (height - frameSizeY));
// Make the border a little wider to let the user easy to resize on corners. // 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 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<int>(qRound(qreal(frameSizeX) * scaleFactor)); const auto scaledFrameSizeX = static_cast<int>(qRound(qreal(frameSizeX) * scaleFactor));
const bool isLeft = (localPos.x < scaledFrameSizeX); const bool isLeft = (localPos.x < scaledFrameSizeX);
const bool isRight = (localPos.x >= (width - scaledFrameSizeX)); const bool isRight = (localPos.x >= (width - scaledFrameSizeX));
@ -549,23 +586,23 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
case WM_WINDOWPOSCHANGING: { case WM_WINDOWPOSCHANGING: {
// Tell Windows to discard the entire contents of the client area, as re-using // 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. // parts of the client area would lead to jitter during resize.
const auto windowPos = reinterpret_cast<LPWINDOWPOS>(msg->lParam); const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
windowPos->flags |= SWP_NOCOPYBITS; windowPos->flags |= SWP_NOCOPYBITS;
} break; } break;
#endif #endif
case WM_DPICHANGED: { case WM_DPICHANGED: {
// Sync the internal window frame margins with the latest DPI. // Sync the internal window frame margins with the latest DPI.
Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true); Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true);
} break; } break;
case WM_DWMCOMPOSITIONCHANGED: { case WM_DWMCOMPOSITIONCHANGED: {
// Re-apply the custom window frame if recovered from the basic theme. // Re-apply the custom window frame if recovered from the basic theme.
Utils::updateWindowFrameMargins(winId, false); Utils::updateWindowFrameMargins(windowId, false);
} break; } break;
default: default:
break; break;
} }
if (!frameBorderVisible) { if (!frameBorderVisible) {
switch (msg->message) { switch (uMsg) {
case WM_NCUAHDRAWCAPTION: case WM_NCUAHDRAWCAPTION:
case WM_NCUAHDRAWFRAME: { case WM_NCUAHDRAWFRAME: {
// These undocumented messages are sent to draw themed window // 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 // 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 // Don't use "*result = 0" here, otherwise the window won't respond to the
// window activation state change. // window activation state change.
*result = DefWindowProcW(msg->hwnd, WM_NCACTIVATE, msg->wParam, -1); *result = DefWindowProcW(hWnd, WM_NCACTIVATE, wParam, -1);
} else { } else {
if (static_cast<BOOL>(msg->wParam) == FALSE) { if (static_cast<BOOL>(wParam) == FALSE) {
*result = TRUE; *result = TRUE;
} else { } else {
*result = FALSE; *result = FALSE;
@ -610,26 +647,27 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
// Disable painting while these messages are handled to prevent them // Disable painting while these messages are handled to prevent them
// from drawing a window caption over the client area. // from drawing a window caption over the client area.
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const LONG_PTR oldStyle = GetWindowLongPtrW(msg->hwnd, GWL_STYLE); const auto oldStyle = static_cast<DWORD>(GetWindowLongPtrW(hWnd, GWL_STYLE));
if (oldStyle == 0) { if (oldStyle == 0) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowLongPtrW"));
break; break;
} }
// Prevent Windows from drawing the default title bar by temporarily // Prevent Windows from drawing the default title bar by temporarily
// toggling the WS_VISIBLE style. // toggling the WS_VISIBLE style.
const DWORD newStyle = (oldStyle & ~WS_VISIBLE);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle & ~WS_VISIBLE)) == 0) { if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast<LONG_PTR>(newStyle)) == 0) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break; break;
} }
Utils::triggerFrameChange(winId); Utils::triggerFrameChange(windowId);
const LRESULT ret = DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam); const LRESULT ret = DefWindowProcW(hWnd, uMsg, wParam, lParam);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(msg->hwnd, GWL_STYLE, oldStyle) == 0) { if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle)) == 0) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
break; break;
} }
Utils::triggerFrameChange(winId); Utils::triggerFrameChange(windowId);
*result = ret; *result = ret;
return true; return true;
} }
@ -638,10 +676,10 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
break; break;
} }
} }
const bool themeSettingChanged = [&msg]() -> bool { const bool themeSettingChanged = [uMsg, wParam, lParam]() -> bool {
if (Utils::isWin10OrGreater()) { if (Utils::isWin10OrGreater()) {
if (msg->message == WM_SETTINGCHANGE) { if (uMsg == WM_SETTINGCHANGE) {
if ((msg->wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(msg->lParam)) if ((wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(lParam))
.compare(QU8Str(kThemeSettingChangeEventName), Qt::CaseInsensitive) == 0)) { .compare(QU8Str(kThemeSettingChangeEventName), Qt::CaseInsensitive) == 0)) {
return true; return true;
} }
@ -650,13 +688,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
return false; return false;
}(); }();
if (themeSettingChanged) { if (themeSettingChanged) {
if (!(params.options & Option::DontTouchWindowFrameBorderColor)) { if (!(data.settings.options & Option::DontTouchWindowFrameBorderColor)) {
const bool dark = Utils::shouldAppsUseDarkMode(); const bool dark = Utils::shouldAppsUseDarkMode();
Utils::updateWindowFrameBorderColor(winId, dark); Utils::updateWindowFrameBorderColor(windowId, dark);
} }
} }
if (themeSettingChanged || (msg->message == WM_THEMECHANGED) if (themeSettingChanged || (uMsg == WM_THEMECHANGED)
|| (msg->message == WM_DWMCOLORIZATIONCOLORCHANGED)) { || (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED)) {
Q_EMIT FramelessWindowsManager::instance()->systemThemeChanged(); Q_EMIT FramelessWindowsManager::instance()->systemThemeChanged();
} }
return false; return false;

View File

@ -39,13 +39,13 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; using namespace Global;
struct FramelessWindowsManagerData struct FramelessWindowsManagerHelper
{ {
QMutex mutex = {}; QMutex mutex = {};
QList<WId> windowIds = {}; QList<WId> windowIds = {};
}; };
Q_GLOBAL_STATIC(FramelessWindowsManagerData, g_data) Q_GLOBAL_STATIC(FramelessWindowsManagerHelper, g_helper)
Q_GLOBAL_STATIC(FramelessWindowsManager, g_manager) Q_GLOBAL_STATIC(FramelessWindowsManager, g_manager)
@ -54,6 +54,8 @@ FramelessWindowsManager::FramelessWindowsManager(QObject *parent) : QObject(pare
if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) { if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) {
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
} }
qRegisterMetaType<UserSettings>();
qRegisterMetaType<SystemParameters>();
} }
FramelessWindowsManager::~FramelessWindowsManager() = default; FramelessWindowsManager::~FramelessWindowsManager() = default;
@ -89,19 +91,19 @@ SystemTheme FramelessWindowsManager::systemTheme()
#endif #endif
} }
void FramelessWindowsManager::addWindow(const FramelessHelperParams &params) void FramelessWindowsManager::addWindow(const UserSettings &settings, const SystemParameters &params)
{ {
Q_ASSERT(params.isValid()); Q_ASSERT(params.isValid());
if (!params.isValid()) { if (!params.isValid()) {
return; return;
} }
g_data()->mutex.lock(); g_helper()->mutex.lock();
if (g_data()->windowIds.contains(params.windowId)) { if (g_helper()->windowIds.contains(params.windowId)) {
g_data()->mutex.unlock(); g_helper()->mutex.unlock();
return; return;
} }
g_data()->windowIds.append(params.windowId); g_helper()->windowIds.append(params.windowId);
g_data()->mutex.unlock(); g_helper()->mutex.unlock();
static const bool pureQt = usePureQtImplementation(); static const bool pureQt = usePureQtImplementation();
QWindow *window = params.getWindowHandle(); QWindow *window = params.getWindowHandle();
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
@ -121,15 +123,15 @@ void FramelessWindowsManager::addWindow(const FramelessHelperParams &params)
} }
#endif #endif
if (pureQt) { if (pureQt) {
FramelessHelperQt::addWindow(params); FramelessHelperQt::addWindow(settings, params);
} }
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (!pureQt) { if (!pureQt) {
FramelessHelperWin::addWindow(params); FramelessHelperWin::addWindow(settings, params);
} }
if (!(params.options & Option::DontInstallSystemMenuHook)) { if (!(settings.options & Option::DontInstallSystemMenuHook)) {
Utils::installSystemMenuHook(params.windowId, params.options, Utils::installSystemMenuHook(params.windowId, settings.options,
params.systemMenuOffset, params.isWindowFixedSize); settings.systemMenuOffset, params.isWindowFixedSize);
} }
#endif #endif
} }

View File

@ -103,7 +103,9 @@ QVariant Utils::getSystemButtonIconResource
case SystemButtonType::Unknown: case SystemButtonType::Unknown:
return {}; return {};
case SystemButtonType::WindowIcon: case SystemButtonType::WindowIcon:
return QStringLiteral("windowIcon"); return QStringLiteral("windowicon");
case SystemButtonType::Help:
return QStringLiteral("help");
case SystemButtonType::Minimize: case SystemButtonType::Minimize:
return QStringLiteral("minimize"); return QStringLiteral("minimize");
case SystemButtonType::Maximize: case SystemButtonType::Maximize:
@ -142,10 +144,10 @@ QVariant Utils::getSystemButtonIconResource
return {}; return {};
} }
QWindow *Utils::findWindow(const WId winId) QWindow *Utils::findWindow(const WId windowId)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return nullptr; return nullptr;
} }
const QWindowList windows = QGuiApplication::topLevelWindows(); const QWindowList windows = QGuiApplication::topLevelWindows();
@ -154,7 +156,7 @@ QWindow *Utils::findWindow(const WId winId)
} }
for (auto &&window : qAsConst(windows)) { for (auto &&window : qAsConst(windows)) {
if (window && window->handle()) { if (window && window->handle()) {
if (window->winId() == winId) { if (window->winId() == windowId) {
return window; return window;
} }
} }

View File

@ -54,7 +54,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; using namespace Global;
struct Win32UtilsInternalData struct Win32UtilsHelperData
{ {
WNDPROC originalWindowProc = nullptr; WNDPROC originalWindowProc = nullptr;
Options options = {}; Options options = {};
@ -65,7 +65,7 @@ struct Win32UtilsInternalData
struct Win32UtilsHelper struct Win32UtilsHelper
{ {
QMutex mutex = {}; QMutex mutex = {};
QHash<WId, Win32UtilsInternalData> data = {}; QHash<WId, Win32UtilsHelperData> data = {};
}; };
Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper) Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper)
@ -127,14 +127,14 @@ static const QString successErrorText = QStringLiteral("The operation completed
return __getSystemErrorMessage(function, dwError); 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) const bool horizontal, const bool scaled)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return 0; return 0;
} }
const UINT windowDpi = Utils::getWindowDpi(winId, horizontal); const UINT windowDpi = Utils::getWindowDpi(windowId, horizontal);
static const auto pGetSystemMetricsForDpi = static const auto pGetSystemMetricsForDpi =
reinterpret_cast<decltype(&GetSystemMetricsForDpi)>( reinterpret_cast<decltype(&GetSystemMetricsForDpi)>(
QSystemLibrary::resolve(QStringLiteral("user32"), "GetSystemMetricsForDpi")); QSystemLibrary::resolve(QStringLiteral("user32"), "GetSystemMetricsForDpi"));
@ -189,7 +189,7 @@ static const QString successErrorText = QStringLiteral("The operation completed
g_utilsHelper()->mutex.unlock(); g_utilsHelper()->mutex.unlock();
return DefWindowProcW(hWnd, uMsg, wParam, lParam); 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(); g_utilsHelper()->mutex.unlock();
const auto getGlobalPosFromMouse = [lParam]() -> QPoint { const auto getGlobalPosFromMouse = [lParam]() -> QPoint {
return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
@ -331,23 +331,23 @@ bool Utils::isDwmCompositionEnabled()
return (enabled != FALSE); return (enabled != FALSE);
} }
void Utils::triggerFrameChange(const WId winId) void Utils::triggerFrameChange(const WId windowId)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
static constexpr const UINT flags = (SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); 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) { if (SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, flags) == FALSE) {
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowPos")); 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return; return;
} }
// We can't extend the window frame when DWM composition is disabled. // 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}; return {1, 1, 1, 1};
} }
}(); }();
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
const HRESULT hr = pDwmExtendFrameIntoClientArea(hwnd, &margins); const HRESULT hr = pDwmExtendFrameIntoClientArea(hwnd, &margins);
if (FAILED(hr)) { if (FAILED(hr)) {
qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr); qWarning() << __getSystemErrorMessage(QStringLiteral("DwmExtendFrameIntoClientArea"), hr);
return; return;
} }
triggerFrameChange(winId); triggerFrameChange(windowId);
} }
void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable) void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable)
@ -383,17 +383,17 @@ void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable)
if (!window) { if (!window) {
return; return;
} }
const WId winId = window->winId(); const WId windowId = window->winId();
const QMargins margins = [enable, winId]() -> QMargins { const QMargins margins = [enable, windowId]() -> QMargins {
if (!enable) { if (!enable) {
return {}; return {};
} }
const int titleBarHeight = getTitleBarHeight(winId, true); const int titleBarHeight = getTitleBarHeight(windowId, true);
if (isWindowFrameBorderVisible()) { if (isWindowFrameBorderVisible()) {
return {0, -titleBarHeight, 0, 0}; return {0, -titleBarHeight, 0, 0};
} else { } else {
const int frameSizeX = getResizeBorderThickness(winId, true, true); const int frameSizeX = getResizeBorderThickness(windowId, true, true);
const int frameSizeY = getResizeBorderThickness(winId, false, true); const int frameSizeY = getResizeBorderThickness(windowId, false, true);
return {-frameSizeX, -titleBarHeight, -frameSizeX, -frameSizeY}; return {-frameSizeX, -titleBarHeight, -frameSizeX, -frameSizeY};
} }
}(); }();
@ -414,7 +414,7 @@ void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable)
return; return;
} }
#endif #endif
triggerFrameChange(winId); triggerFrameChange(windowId);
} }
QString Utils::getSystemErrorMessage(const QString &function) QString Utils::getSystemErrorMessage(const QString &function)
@ -493,15 +493,15 @@ DwmColorizationArea Utils::getDwmColorizationArea()
return DwmColorizationArea::None; 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) const QPoint &offset, const IsWindowFixedSizeCallback &isWindowFixedSize)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
Q_ASSERT(isWindowFixedSize); Q_ASSERT(isWindowFixedSize);
if (!winId || !isWindowFixedSize) { if (!windowId || !isWindowFixedSize) {
return; return;
} }
const auto hWnd = reinterpret_cast<HWND>(winId); const auto hWnd = reinterpret_cast<HWND>(windowId);
const HMENU menu = GetSystemMenu(hWnd, FALSE); const HMENU menu = GetSystemMenu(hWnd, FALSE);
if (!menu) { if (!menu) {
// The corresponding window doesn't have a menu, this isn't an error, // 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; return true;
}; };
const bool maxOrFull = (IsMaximized(hWnd) || const bool maxOrFull = (IsMaximized(hWnd) ||
((options & Option::DontTreatFullScreenAsZoomed) ? false : isFullScreen(winId))); ((options & Option::DontTreatFullScreenAsZoomed) ? false : isFullScreen(windowId)));
const bool fixedSize = isWindowFixedSize(); const bool fixedSize = isWindowFixedSize();
if (!setState(SC_RESTORE, (maxOrFull && !fixedSize), true)) { if (!setState(SC_RESTORE, (maxOrFull && !fixedSize), true)) {
return; 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return false; return false;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
RECT wndRect = {}; RECT wndRect = {};
if (GetWindowRect(hwnd, &wndRect) == FALSE) { if (GetWindowRect(hwnd, &wndRect) == FALSE) {
qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowRect")); qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowRect"));
@ -590,13 +590,13 @@ bool Utils::isFullScreen(const WId winId)
&& (wndRect.right == scrRect.right) && (wndRect.bottom == scrRect.bottom)); && (wndRect.right == scrRect.right) && (wndRect.bottom == scrRect.bottom));
} }
bool Utils::isWindowNoState(const WId winId) bool Utils::isWindowNoState(const WId windowId)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return false; return false;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
WINDOWPLACEMENT wp; WINDOWPLACEMENT wp;
SecureZeroMemory(&wp, sizeof(wp)); SecureZeroMemory(&wp, sizeof(wp));
wp.length = sizeof(wp); // This line is important! Don't miss it! wp.length = sizeof(wp); // This line is important! Don't miss it!
@ -755,13 +755,13 @@ QT_WARNING_POP
return USER_DEFAULT_SCREEN_DPI; 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return USER_DEFAULT_SCREEN_DPI; return USER_DEFAULT_SCREEN_DPI;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
QSystemLibrary user32Lib(QStringLiteral("user32")); QSystemLibrary user32Lib(QStringLiteral("user32"));
static const auto pGetDpiForWindow = static const auto pGetDpiForWindow =
reinterpret_cast<decltype(&GetDpiForWindow)>(user32Lib.resolve("GetDpiForWindow")); reinterpret_cast<decltype(&GetDpiForWindow)>(user32Lib.resolve("GetDpiForWindow"));
@ -793,43 +793,43 @@ quint32 Utils::getWindowDpi(const WId winId, const bool horizontal)
return getPrimaryScreenDpi(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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return 0; return 0;
} }
if (horizontal) { if (horizontal) {
return (getSystemMetrics2(winId, SM_CXSIZEFRAME, true, scaled) return (getSystemMetrics2(windowId, SM_CXSIZEFRAME, true, scaled)
+ getSystemMetrics2(winId, SM_CXPADDEDBORDER, true, scaled)); + getSystemMetrics2(windowId, SM_CXPADDEDBORDER, true, scaled));
} else { } else {
return (getSystemMetrics2(winId, SM_CYSIZEFRAME, false, scaled) return (getSystemMetrics2(windowId, SM_CYSIZEFRAME, false, scaled)
+ getSystemMetrics2(winId, SM_CYPADDEDBORDER, 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return 0; 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return 0; 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return 0; return 0;
} }
// There's no window frame border before Windows 10. // There's no window frame border before Windows 10.
@ -842,9 +842,9 @@ quint32 Utils::getFrameBorderThickness(const WId winId, const bool scaled)
if (!pDwmGetWindowAttribute) { if (!pDwmGetWindowAttribute) {
return 0; 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 qreal scaleFactor = (qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI));
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
UINT value = 0; UINT value = 0;
if (SUCCEEDED(pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &value, sizeof(value)))) { if (SUCCEEDED(pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &value, sizeof(value)))) {
const qreal dpr = (scaled ? 1.0 : scaleFactor); 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return; return;
} }
// There's no global dark theme before Win10 1809. // There's no global dark theme before Win10 1809.
@ -890,7 +890,7 @@ void Utils::updateWindowFrameBorderColor(const WId winId, const bool dark)
if (!pDwmSetWindowAttribute) { if (!pDwmSetWindowAttribute) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
const BOOL value = (dark ? TRUE : FALSE); const BOOL value = (dark ? TRUE : FALSE);
// Whether dark window frame is available or not depends on the runtime system version, // 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. // 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)); 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const auto oldClassStyle = static_cast<DWORD>(GetClassLongPtrW(hwnd, GCL_STYLE)); const auto oldClassStyle = static_cast<DWORD>(GetClassLongPtrW(hwnd, GCL_STYLE));
if (oldClassStyle == 0) { if (oldClassStyle == 0) {
@ -929,7 +929,7 @@ void Utils::fixupQtInternals(const WId winId)
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
return; return;
} }
triggerFrameChange(winId); triggerFrameChange(windowId);
} }
void Utils::startSystemMove(QWindow *window) void Utils::startSystemMove(QWindow *window)
@ -1013,19 +1013,19 @@ bool Utils::isFrameBorderColorized()
return isTitleBarColorized(); 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) const IsWindowFixedSizeCallback &isWindowFixedSize)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
Q_ASSERT(isWindowFixedSize); Q_ASSERT(isWindowFixedSize);
if (!winId || !isWindowFixedSize) { if (!windowId || !isWindowFixedSize) {
return; return;
} }
QMutexLocker locker(&g_utilsHelper()->mutex); QMutexLocker locker(&g_utilsHelper()->mutex);
if (g_utilsHelper()->data.contains(winId)) { if (g_utilsHelper()->data.contains(windowId)) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)); const auto originalWindowProc = reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC));
Q_ASSERT(originalWindowProc); Q_ASSERT(originalWindowProc);
@ -1038,38 +1038,38 @@ void Utils::installSystemMenuHook(const WId winId, const Options options, const
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
return; return;
} }
//triggerFrameChange(winId); //triggerFrameChange(windowId);
Win32UtilsInternalData data = {}; Win32UtilsHelperData data = {};
data.originalWindowProc = originalWindowProc; data.originalWindowProc = originalWindowProc;
data.options = options; data.options = options;
data.offset = offset; data.offset = offset;
data.isWindowFixedSize = isWindowFixedSize; 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return; return;
} }
QMutexLocker locker(&g_utilsHelper()->mutex); QMutexLocker locker(&g_utilsHelper()->mutex);
if (!g_utilsHelper()->data.contains(winId)) { if (!g_utilsHelper()->data.contains(windowId)) {
return; return;
} }
const Win32UtilsInternalData data = g_utilsHelper()->data.value(winId); const Win32UtilsHelperData data = g_utilsHelper()->data.value(windowId);
Q_ASSERT(data.originalWindowProc); Q_ASSERT(data.originalWindowProc);
if (!data.originalWindowProc) { if (!data.originalWindowProc) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(data.originalWindowProc)) == 0) { if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(data.originalWindowProc)) == 0) {
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
return; return;
} }
//triggerFrameChange(winId); //triggerFrameChange(windowId);
g_utilsHelper()->data.remove(winId); g_utilsHelper()->data.remove(windowId);
} }
void Utils::sendMouseReleaseEvent() 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 GetWindowFlagsCallback &getWindowFlags,
const SetWindowFlagsCallback &setWindowFlags, const SetWindowFlagsCallback &setWindowFlags,
const bool enable) const bool enable)
{ {
Q_ASSERT(winId); Q_ASSERT(windowId);
Q_ASSERT(getWindowFlags); Q_ASSERT(getWindowFlags);
Q_ASSERT(setWindowFlags); Q_ASSERT(setWindowFlags);
if (!winId || !getWindowFlags || !setWindowFlags) { if (!windowId || !getWindowFlags || !setWindowFlags) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const LONG_PTR originalWindowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); const LONG_PTR originalWindowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE);
if (originalWindowStyle == 0) { if (originalWindowStyle == 0) {
@ -1106,16 +1106,16 @@ void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId winId,
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
return; 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); Q_ASSERT(windowId);
if (!winId) { if (!windowId) {
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(windowId);
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
const auto oldWindowStyle = static_cast<DWORD>(GetWindowLongPtrW(hwnd, GWL_STYLE)); const auto oldWindowStyle = static_cast<DWORD>(GetWindowLongPtrW(hwnd, GWL_STYLE));
if (oldWindowStyle == 0) { if (oldWindowStyle == 0) {
@ -1134,7 +1134,7 @@ void Utils::setAeroSnappingEnabled(const WId winId, const bool enable)
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
return; return;
} }
triggerFrameChange(winId); triggerFrameChange(windowId);
} }
void Utils::tryToEnableHighestDpiAwarenessLevel() void Utils::tryToEnableHighestDpiAwarenessLevel()

View File

@ -6,7 +6,6 @@ set(SOURCES
${INCLUDE_PREFIX}/framelesshelperquick_global.h ${INCLUDE_PREFIX}/framelesshelperquick_global.h
${INCLUDE_PREFIX}/framelessquickutils.h ${INCLUDE_PREFIX}/framelessquickutils.h
${INCLUDE_PREFIX}/framelesshelperimageprovider.h ${INCLUDE_PREFIX}/framelesshelperimageprovider.h
${INCLUDE_PREFIX}/framelessquickeventfilter.h
${INCLUDE_PREFIX}/framelesshelper_quick.h ${INCLUDE_PREFIX}/framelesshelper_quick.h
${INCLUDE_PREFIX}/framelessquickwindow.h ${INCLUDE_PREFIX}/framelessquickwindow.h
framelessquickwindow_p.h framelessquickwindow_p.h
@ -14,7 +13,6 @@ set(SOURCES
framelesshelper_quick.cpp framelesshelper_quick.cpp
framelessquickutils.cpp framelessquickutils.cpp
framelesshelperimageprovider.cpp framelesshelperimageprovider.cpp
framelessquickeventfilter.cpp
framelessquickwindow.cpp framelessquickwindow.cpp
) )

View File

@ -28,6 +28,11 @@
#include "framelessquickutils.h" #include "framelessquickutils.h"
#include "framelessquickwindow.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 "Q_INIT_RESOURCE()" macro can't be used inside a namespace,
// the official workaround is to wrap it into a global function // the official workaround is to wrap it into a global function
// and call the wrapper function inside the namespace. // and call the wrapper function inside the namespace.
@ -38,15 +43,6 @@ static inline void initResource()
FRAMELESSHELPER_BEGIN_NAMESPACE 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) void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
{ {
Q_ASSERT(engine); Q_ASSERT(engine);
@ -66,10 +62,10 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
qmlRegisterAnonymousType<QWindow>(FRAMELESSHELPER_QUICK_URI, 1); qmlRegisterAnonymousType<QWindow>(FRAMELESSHELPER_QUICK_URI, 1);
#endif #endif
initResource(); initResource();
qmlRegisterType(getQmlFileUrl(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton"); qmlRegisterType(QML_URL_EXPAND(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton");
qmlRegisterType(getQmlFileUrl(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton"); qmlRegisterType(QML_URL_EXPAND(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton");
qmlRegisterType(getQmlFileUrl(QStringLiteral("CloseButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton"); qmlRegisterType(QML_URL_EXPAND(QStringLiteral("CloseButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton");
qmlRegisterType(getQmlFileUrl(QStringLiteral("StandardTitleBar")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar"); qmlRegisterType(QML_URL_EXPAND(QStringLiteral("StandardTitleBar")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar");
} }
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -56,6 +56,9 @@ using namespace Global;
if (str.compare(QStringLiteral("windowicon"), Qt::CaseInsensitive) == 0) { if (str.compare(QStringLiteral("windowicon"), Qt::CaseInsensitive) == 0) {
return SystemButtonType::WindowIcon; return SystemButtonType::WindowIcon;
} }
if (str.compare(QStringLiteral("help"), Qt::CaseInsensitive) == 0) {
return SystemButtonType::Help;
}
if (str.compare(QStringLiteral("minimize"), Qt::CaseInsensitive) == 0) { if (str.compare(QStringLiteral("minimize"), Qt::CaseInsensitive) == 0) {
return SystemButtonType::Minimize; return SystemButtonType::Minimize;
} }

View File

@ -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 <QtCore/qmutex.h>
#include <QtCore/qhash.h>
#include <QtCore/qlist.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/qquickitem.h>
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
struct EventFilterDataInternal
{
FramelessQuickEventFilter *eventFilter = nullptr;
QQuickItem *titleBarItem = nullptr;
QList<QQuickItem *> hitTestVisibleItems = {};
FramelessHelperParams params = {};
};
struct EventFilterData
{
QMutex mutex = {};
QHash<WId, EventFilterDataInternal> 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 &params)
{
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<QQuickWindow *>(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<QQuickWindow *>(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<QMouseEvent *>(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

View File

@ -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"

View File

@ -29,37 +29,62 @@
#include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p.h>
#include <framelesswindowsmanager.h> #include <framelesswindowsmanager.h>
#include <utils.h> #include <utils.h>
#include "framelessquickeventfilter.h"
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; 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); Q_ASSERT(q);
if (!q) { if (!q) {
return; return;
} }
q_ptr = q; q_ptr = q;
m_params.options = options; m_settings = settings;
initialize(); initialize();
} }
FramelessQuickWindowPrivate::~FramelessQuickWindowPrivate() = default; 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 bool FramelessQuickWindowPrivate::isZoomed() const
{ {
Q_Q(const FramelessQuickWindow); Q_Q(const FramelessQuickWindow);
const FramelessQuickWindow::Visibility visibility = q->visibility(); const FramelessQuickWindow::Visibility visibility = q->visibility();
return ((visibility == FramelessQuickWindow::Maximized) || return ((visibility == FramelessQuickWindow::Maximized) ||
((m_params.options & Option::DontTreatFullScreenAsZoomed) ((m_settings.options & Option::DontTreatFullScreenAsZoomed)
? false : (visibility == FramelessQuickWindow::FullScreen))); ? false : (visibility == FramelessQuickWindow::FullScreen)));
} }
bool FramelessQuickWindowPrivate::isFullScreen() const
{
Q_Q(const FramelessQuickWindow);
return (q->visibility() == FramelessQuickWindow::FullScreen);
}
bool FramelessQuickWindowPrivate::isFixedSize() const bool FramelessQuickWindowPrivate::isFixedSize() const
{ {
if (m_params.options & Option::DisableResizing) { if (m_settings.options & Option::DisableResizing) {
return true; return true;
} }
Q_Q(const FramelessQuickWindow); Q_Q(const FramelessQuickWindow);
@ -90,8 +115,10 @@ void FramelessQuickWindowPrivate::setTitleBarItem(QQuickItem *item)
if (!item) { if (!item) {
return; return;
} }
Q_Q(FramelessQuickWindow); if (m_titleBarItem == item) {
FramelessQuickEventFilter::setTitleBarItem(q, item); return;
}
m_titleBarItem = item;
} }
void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item) void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item)
@ -100,8 +127,14 @@ void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item)
if (!item) { if (!item) {
return; return;
} }
Q_Q(FramelessQuickWindow); static constexpr const bool visible = true;
FramelessQuickEventFilter::setHitTestVisible(q, item); 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() void FramelessQuickWindowPrivate::moveToDesktopCenter()
@ -132,6 +165,19 @@ void FramelessQuickWindowPrivate::setFixedSize(const bool value, const bool forc
Q_EMIT q->fixedSizeChanged(); 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() void FramelessQuickWindowPrivate::showMinimized2()
{ {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
@ -165,11 +211,10 @@ void FramelessQuickWindowPrivate::toggleFullScreen()
return; return;
} }
Q_Q(FramelessQuickWindow); Q_Q(FramelessQuickWindow);
const QWindow::Visibility visibility = q->visibility(); if (isFullScreen()) {
if (visibility == QWindow::FullScreen) {
q->setVisibility(m_savedVisibility); q->setVisibility(m_savedVisibility);
} else { } else {
m_savedVisibility = visibility; m_savedVisibility = q->visibility();
q->showFullScreen(); q->showFullScreen();
} }
} }
@ -178,14 +223,10 @@ void FramelessQuickWindowPrivate::showSystemMenu(const QPoint &pos)
{ {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
Q_Q(FramelessQuickWindow); Q_Q(FramelessQuickWindow);
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint globalPos = q->mapToGlobal(pos); const QPoint globalPos = q->mapToGlobal(pos);
# else
const QPoint globalPos = q->mapToGlobal(pos);
# endif
const QPoint nativePos = QPointF(QPointF(globalPos) * q->effectiveDevicePixelRatio()).toPoint(); const QPoint nativePos = QPointF(QPointF(globalPos) * q->effectiveDevicePixelRatio()).toPoint();
Utils::showSystemMenu(m_params.windowId, nativePos, m_params.options, Utils::showSystemMenu(m_params.windowId, nativePos, m_settings.options,
m_params.systemMenuOffset, m_params.isWindowFixedSize); m_settings.systemMenuOffset, m_params.isWindowFixedSize);
#endif #endif
} }
@ -240,76 +281,285 @@ void FramelessQuickWindowPrivate::initialize()
m_params.getWindowState = [q]() -> Qt::WindowState { return q->windowState(); }; m_params.getWindowState = [q]() -> Qt::WindowState { return q->windowState(); };
m_params.setWindowState = [q](const Qt::WindowState state) -> void { q->setWindowState(state); }; m_params.setWindowState = [q](const Qt::WindowState state) -> void { q->setWindowState(state); };
m_params.getWindowHandle = [q]() -> QWindow * { return q; }; 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(); FramelessWindowsManager * const manager = FramelessWindowsManager::instance();
manager->addWindow(m_params); manager->addWindow(m_settings, m_params);
FramelessQuickEventFilter::addWindow(m_params); QQuickItem * const rootItem = q->contentItem();
#ifdef Q_OS_WINDOWS const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem);
if (isFrameBorderVisible()) { m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
QQuickItem * const rootItem = q->contentItem(); const bool frameBorderVisible = shouldDrawFrameBorder();
const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem); if (frameBorderVisible) {
m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
updateTopBorderHeight(); updateTopBorderHeight();
updateTopBorderColor(); 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 connect(q, &FramelessQuickWindow::visibilityChanged, this, [this, q, frameBorderVisible](){
if (m_params.options & Option::DisableResizing) { 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); 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<QQuickItem *>(m_settings.minimizeButton);
if (mapItemGeometryToScene(minBtn).contains(pos)) {
*button = SystemButtonType::Minimize;
return true;
}
const auto maxBtn = qobject_cast<QQuickItem *>(m_settings.maximizeButton);
if (mapItemGeometryToScene(maxBtn).contains(pos)) {
*button = SystemButtonType::Maximize;
return true;
}
const auto closeBtn = qobject_cast<QQuickItem *>(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 #ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater()); return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater()
&& isNormal() && !(m_settings.options & Option::DontDrawTopWindowFrameBorder));
#else #else
return false; return false;
#endif #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; 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()); m_topBorderRectangle->setColor(getFrameBorderColor());
#endif
} }
void FramelessQuickWindowPrivate::updateTopBorderHeight() void FramelessQuickWindowPrivate::updateTopBorderHeight()
{ {
if (!isFrameBorderVisible()) { #ifdef Q_OS_WINDOWS
return; const qreal newHeight = (isNormal() ? 1.0 : 0.0);
}
Q_Q(FramelessQuickWindow);
const qreal newHeight = ((q->visibility() == FramelessQuickWindow::Windowed) ? 1.0 : 0.0);
m_topBorderRectangle->setHeight(newHeight); 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; 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); Q_D(const FramelessQuickWindow);
return d->isZoomed(); return d->isZoomed();
} }
bool FramelessQuickWindow::isFullScreen() const
{
Q_D(const FramelessQuickWindow);
return d->isFullScreen();
}
bool FramelessQuickWindow::fixedSize() const bool FramelessQuickWindow::fixedSize() const
{ {
Q_D(const FramelessQuickWindow); Q_D(const FramelessQuickWindow);
@ -354,6 +604,40 @@ void FramelessQuickWindow::moveToDesktopCenter()
d->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() void FramelessQuickWindow::showMinimized2()
{ {
Q_D(FramelessQuickWindow); Q_D(FramelessQuickWindow);

View File

@ -31,6 +31,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QQuickItem; class QQuickItem;
class QQuickRectangle; class QQuickRectangle;
class QQuickAnchors;
QT_END_NAMESPACE QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
@ -44,13 +45,22 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindowPrivate : public QObject
Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate) Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate)
public: public:
explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::Options options); explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::UserSettings &settings = {});
~FramelessQuickWindowPrivate() override; ~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 isZoomed() const;
Q_INVOKABLE Q_NODISCARD bool isFullScreen() const;
Q_INVOKABLE Q_NODISCARD bool isFixedSize() const; Q_INVOKABLE Q_NODISCARD bool isFixedSize() const;
Q_INVOKABLE Q_NODISCARD QColor getFrameBorderColor() 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: public Q_SLOTS:
void showMinimized2(); void showMinimized2();
@ -63,9 +73,15 @@ public Q_SLOTS:
void setHitTestVisible(QQuickItem *item); void setHitTestVisible(QQuickItem *item);
void moveToDesktopCenter(); void moveToDesktopCenter();
void setFixedSize(const bool value, const bool force = false); void setFixedSize(const bool value, const bool force = false);
void bringToFront();
private: private:
void initialize(); 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: private Q_SLOTS:
void updateTopBorderColor(); void updateTopBorderColor();
@ -75,8 +91,13 @@ private:
FramelessQuickWindow *q_ptr = nullptr; FramelessQuickWindow *q_ptr = nullptr;
bool m_initialized = false; bool m_initialized = false;
QScopedPointer<QQuickRectangle> m_topBorderRectangle; QScopedPointer<QQuickRectangle> m_topBorderRectangle;
QScopedPointer<QQuickAnchors> m_topBorderAnchors;
QWindow::Visibility m_savedVisibility = QWindow::Windowed; 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<QQuickItem *> m_hitTestVisibleItems = {};
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -29,9 +29,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; 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; FramelessMainWindow::~FramelessMainWindow() = default;
@ -86,6 +86,32 @@ void FramelessMainWindow::moveToDesktopCenter()
m_helper->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) void FramelessMainWindow::changeEvent(QEvent *event)
{ {
QMainWindow::changeEvent(event); QMainWindow::changeEvent(event);

View File

@ -29,9 +29,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global; 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; FramelessWidget::~FramelessWidget() = default;
@ -96,6 +96,32 @@ void FramelessWidget::moveToDesktopCenter()
m_helper->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) void FramelessWidget::changeEvent(QEvent *event)
{ {
QWidget::changeEvent(event); QWidget::changeEvent(event);

View File

@ -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); Q_ASSERT(q);
if (!q) { if (!q) {
return; return;
} }
this->q = q; this->q = q;
m_params.options = options; m_settings = settings;
initialize(); initialize();
} }
@ -74,12 +74,12 @@ bool FramelessWidgetsHelper::isNormal() const
bool FramelessWidgetsHelper::isZoomed() 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 bool FramelessWidgetsHelper::isFixedSize() const
{ {
if (m_params.options & Option::DisableResizing) { if (m_settings.options & Option::DisableResizing) {
return true; return true;
} }
if (q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) { if (q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) {
@ -124,7 +124,7 @@ void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget)
if (m_userTitleBarWidget == widget) { if (m_userTitleBarWidget == widget) {
return; return;
} }
if (m_params.options & Option::UseStandardWindowLayout) { if (m_settings.options & Option::UseStandardWindowLayout) {
if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) { if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) {
m_mainLayout->removeWidget(m_systemTitleBarWidget); m_mainLayout->removeWidget(m_systemTitleBarWidget);
m_systemTitleBarWidget->hide(); m_systemTitleBarWidget->hide();
@ -152,7 +152,7 @@ void FramelessWidgetsHelper::setContentWidget(QWidget *widget)
if (!widget) { if (!widget) {
return; return;
} }
if (!(m_params.options & Option::UseStandardWindowLayout)) { if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return; return;
} }
if (m_userContentWidget == widget) { 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) void FramelessWidgetsHelper::changeEventHandler(QEvent *event)
{ {
Q_ASSERT(event); Q_ASSERT(event);
@ -198,7 +223,7 @@ void FramelessWidgetsHelper::changeEventHandler(QEvent *event)
if ((type != QEvent::WindowStateChange) && (type != QEvent::ActivationChange)) { if ((type != QEvent::WindowStateChange) && (type != QEvent::ActivationChange)) {
return; return;
} }
const bool standardLayout = (m_params.options & Option::UseStandardWindowLayout); const bool standardLayout = (m_settings.options & Option::UseStandardWindowLayout);
if (type == QEvent::WindowStateChange) { if (type == QEvent::WindowStateChange) {
if (standardLayout) { if (standardLayout) {
if (isZoomed()) { if (isZoomed()) {
@ -241,7 +266,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event)
if (!event) { if (!event) {
return; return;
} }
if (m_params.options & Option::DisableDragging) { if (m_settings.options & Option::DisableDragging) {
return; return;
} }
if (event->button() != Qt::LeftButton) { if (event->button() != Qt::LeftButton) {
@ -258,7 +283,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event)
if (!isInTitleBarDraggableArea(scenePos)) { if (!isInTitleBarDraggableArea(scenePos)) {
return; return;
} }
Utils::startSystemMove(m_window); startSystemMove2();
} }
void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
@ -267,7 +292,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
if (!event) { if (!event) {
return; return;
} }
if (m_params.options & Option::DisableSystemMenu) { if (m_settings.options & Option::DisableSystemMenu) {
return; return;
} }
if (event->button() != Qt::RightButton) { if (event->button() != Qt::RightButton) {
@ -284,16 +309,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
if (!isInTitleBarDraggableArea(scenePos)) { if (!isInTitleBarDraggableArea(scenePos)) {
return; return;
} }
#ifdef Q_OS_WINDOWS showSystemMenu(scenePos);
# 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
} }
void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event)
@ -302,7 +318,7 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event)
if (!event) { if (!event) {
return; return;
} }
if ((m_params.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) { if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) {
return; return;
} }
if (event->button() != Qt::LeftButton) { 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.getWindowState = [this]() -> Qt::WindowState { return Utils::windowStatesToWindowState(q->windowState()); };
m_params.setWindowState = [this](const Qt::WindowState state) -> void { q->setWindowState(state); }; m_params.setWindowState = [this](const Qt::WindowState state) -> void { q->setWindowState(state); };
m_params.getWindowHandle = [this]() -> QWindow * { return m_window; }; 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)) { 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." qWarning() << "\"Option::UseStandardWindowLayout\" is not compatible with QMainWindow and it's subclasses."
" Enabling this option will mess up with your main window's layout."; " 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, Utils::tryToBeCompatibleWithQtFramelessWindowHint(windowId, m_params.getWindowFlags,
m_params.setWindowFlags, true); m_params.setWindowFlags, true);
} }
FramelessWindowsManager * const manager = FramelessWindowsManager::instance(); FramelessWindowsManager * const manager = FramelessWindowsManager::instance();
manager->addWindow(m_params); manager->addWindow(m_settings, m_params);
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){ connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){
if (m_params.options & Option::UseStandardWindowLayout) { if (m_settings.options & Option::UseStandardWindowLayout) {
updateSystemTitleBarStyleSheet(); updateSystemTitleBarStyleSheet();
updateSystemButtonsIcon(); updateSystemButtonsIcon();
q->update(); q->update();
} }
QMetaObject::invokeMethod(q, "systemThemeChanged"); 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"); QMetaObject::invokeMethod(q, "zoomedChanged");
}); });
setupInitialUi(); 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); setFixedSize(true, true);
} }
} }
void FramelessWidgetsHelper::createSystemTitleBar() void FramelessWidgetsHelper::createSystemTitleBar()
{ {
if (!(m_params.options & Option::UseStandardWindowLayout)) { if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return; return;
} }
m_systemTitleBarWidget = new QWidget(q); m_systemTitleBarWidget = new QWidget(q);
@ -440,7 +471,7 @@ void FramelessWidgetsHelper::createSystemTitleBar()
void FramelessWidgetsHelper::createUserContentContainer() void FramelessWidgetsHelper::createUserContentContainer()
{ {
if (!(m_params.options & Option::UseStandardWindowLayout)) { if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return; return;
} }
m_userContentContainerWidget = new QWidget(q); m_userContentContainerWidget = new QWidget(q);
@ -453,7 +484,7 @@ void FramelessWidgetsHelper::createUserContentContainer()
void FramelessWidgetsHelper::setupInitialUi() void FramelessWidgetsHelper::setupInitialUi()
{ {
if (m_params.options & Option::UseStandardWindowLayout) { if (m_settings.options & Option::UseStandardWindowLayout) {
createSystemTitleBar(); createSystemTitleBar();
createUserContentContainer(); createUserContentContainer();
m_mainLayout = new QVBoxLayout(q); m_mainLayout = new QVBoxLayout(q);
@ -468,33 +499,69 @@ void FramelessWidgetsHelper::setupInitialUi()
updateContentsMargins(); 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<QWidget *>(m_settings.minimizeButton);
if (minBtn->geometry().contains(pos)) {
*button = SystemButtonType::Minimize;
return true;
}
const auto maxBtn = qobject_cast<QWidget *>(m_settings.maximizeButton);
if (maxBtn->geometry().contains(pos)) {
*button = SystemButtonType::Maximize;
return true;
}
const auto closeBtn = qobject_cast<QWidget *>(m_settings.closeButton);
if (closeBtn->geometry().contains(pos)) {
*button = SystemButtonType::Close;
return true;
}
return false;
}
bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const
{ {
const QRegion draggableRegion = [this]() -> QRegion { 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) { if (m_userTitleBarWidget) {
QRegion region = mapGeometryToScene(m_userTitleBarWidget); QRegion region = mapWidgetGeometryToScene(m_userTitleBarWidget);
if (!m_hitTestVisibleWidgets.isEmpty()) { if (!m_hitTestVisibleWidgets.isEmpty()) {
for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) { for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) {
Q_ASSERT(widget); Q_ASSERT(widget);
if (widget) { if (widget) {
region -= mapGeometryToScene(widget); region -= mapWidgetGeometryToScene(widget);
} }
} }
} }
return region; return region;
} }
if (m_params.options & Option::UseStandardWindowLayout) { if (m_settings.options & Option::UseStandardWindowLayout) {
QRegion region = mapGeometryToScene(m_systemTitleBarWidget); QRegion region = mapWidgetGeometryToScene(m_systemTitleBarWidget);
region -= mapGeometryToScene(m_systemMinimizeButton); region -= mapWidgetGeometryToScene(m_systemMinimizeButton);
region -= mapGeometryToScene(m_systemMaximizeButton); region -= mapWidgetGeometryToScene(m_systemMaximizeButton);
region -= mapGeometryToScene(m_systemCloseButton); region -= mapWidgetGeometryToScene(m_systemCloseButton);
return region; return region;
} }
return {}; return {};
@ -506,7 +573,7 @@ bool FramelessWidgetsHelper::shouldDrawFrameBorder() const
{ {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater() return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater()
&& isNormal() && !(m_params.options & Option::DontDrawTopWindowFrameBorder)); && isNormal() && !(m_settings.options & Option::DontDrawTopWindowFrameBorder));
#else #else
return false; return false;
#endif #endif
@ -528,7 +595,7 @@ void FramelessWidgetsHelper::updateContentsMargins()
void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet()
{ {
if (!(m_params.options & Option::UseStandardWindowLayout)) { if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return; return;
} }
const bool active = q->isActiveWindow(); const bool active = q->isActiveWindow();
@ -563,7 +630,7 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet()
void FramelessWidgetsHelper::updateSystemButtonsIcon() void FramelessWidgetsHelper::updateSystemButtonsIcon()
{ {
if (!(m_params.options & Option::UseStandardWindowLayout)) { if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return; return;
} }
const SystemTheme theme = ((Utils::shouldAppsUseDarkMode() || Utils::isTitleBarColorized()) ? SystemTheme::Dark : SystemTheme::Light); 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); 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 FRAMELESSHELPER_END_NAMESPACE