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);
MainWindow mainWindow;
mainWindow.moveToDesktopCenter();
mainWindow.show();
return QCoreApplication::exec();

View File

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

View File

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

View File

@ -32,7 +32,19 @@ FRAMELESSHELPER_USE_NAMESPACE
using namespace Global;
Widget::Widget(QWidget *parent) : FramelessWidget(parent, {Option::UseStandardWindowLayout})
static const UserSettings settings =
{
/* startupPosition */ QPoint(),
/* startupSize */ QSize(),
/* startupState */ Qt::WindowNoState,
/* options */ { Option::UseStandardWindowLayout },
/* systemMenuOffset */ QPoint(),
/* minimizeButton */ nullptr,
/* maximizeButton */ nullptr,
/* closeButton */ nullptr
};
Widget::Widget(QWidget *parent) : FramelessWidget(parent, settings)
{
setupUi();
startTimer(500);

View File

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

View File

@ -37,7 +37,7 @@ public:
explicit FramelessHelperWin();
~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;
};

View File

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

View File

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

View File

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

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
Q_DECLARE_PRIVATE(FramelessQuickWindow)
Q_DISABLE_COPY_MOVE(FramelessQuickWindow)
Q_PROPERTY(bool zoomed READ zoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL)
Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL)
Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL)
Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fullScreen READ isFullScreen NOTIFY fullScreenChanged FINAL)
Q_PROPERTY(bool fixedSize READ fixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL)
Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL)
public:
explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::Options options = {});
explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::UserSettings &settings = {});
~FramelessQuickWindow() override;
Q_NODISCARD bool zoomed() const;
Q_NODISCARD bool isHidden() const;
Q_NODISCARD bool isNormal() const;
Q_NODISCARD bool isMinimized() const;
Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFullScreen() const;
Q_NODISCARD bool fixedSize() const;
void setFixedSize(const bool value);
@ -65,9 +73,20 @@ public Q_SLOTS:
void setTitleBarItem(QQuickItem *item);
void setHitTestVisible(QQuickItem *item);
void moveToDesktopCenter();
void bringToFront();
protected:
void showEvent(QShowEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void minimizedChanged();
void zoomedChanged();
void fullScreenChanged();
void fixedSizeChanged();
void frameBorderColorChanged();

View File

@ -35,16 +35,17 @@ class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessMainWindow)
Q_PROPERTY(bool hidden READ isHidden NOTIFY hiddenChanged FINAL)
Q_PROPERTY(bool normal READ isNormal NOTIFY normalChanged FINAL)
Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL)
Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
public:
explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}, const Global::Options options = {});
explicit FramelessMainWindow(QWidget *parent = nullptr, const Qt::WindowFlags flags = {}, const Global::UserSettings &settings = {});
~FramelessMainWindow() override;
Q_NODISCARD Q_INVOKABLE bool isNormal() const;
Q_NODISCARD bool isNormal() const;
Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFixedSize() const;
@ -58,8 +59,13 @@ public Q_SLOTS:
void toggleMaximized();
void toggleFullScreen();
void moveToDesktopCenter();
void bringToFront();
void showSystemMenu(const QPoint &pos);
void startSystemMove2();
void startSystemResize2(const Qt::Edges edges);
protected:
void showEvent(QShowEvent *event) override;
void changeEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
@ -67,6 +73,8 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *event) override;
Q_SIGNALS:
void hiddenChanged();
void normalChanged();
void zoomedChanged();
void fixedSizeChanged();
void titleBarWidgetChanged();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,6 +28,11 @@
#include "framelessquickutils.h"
#include "framelessquickwindow.h"
#ifndef QML_URL_EXPAND
# define QML_URL_EXPAND(fileName) \
QUrl(QStringLiteral("qrc:///org.wangwenx190.FramelessHelper/qml/%1.qml").arg(fileName))
#endif
// The "Q_INIT_RESOURCE()" macro can't be used inside a namespace,
// the official workaround is to wrap it into a global function
// and call the wrapper function inside the namespace.
@ -38,15 +43,6 @@ static inline void initResource()
FRAMELESSHELPER_BEGIN_NAMESPACE
[[nodiscard]] static inline QUrl getQmlFileUrl(const QString &qml)
{
Q_ASSERT(!qml.isEmpty());
if (qml.isEmpty()) {
return {};
}
return QUrl(QStringLiteral("qrc:///org.wangwenx190.FramelessHelper/qml/%1.qml").arg(qml));
}
void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
{
Q_ASSERT(engine);
@ -66,10 +62,10 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
qmlRegisterAnonymousType<QWindow>(FRAMELESSHELPER_QUICK_URI, 1);
#endif
initResource();
qmlRegisterType(getQmlFileUrl(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton");
qmlRegisterType(getQmlFileUrl(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton");
qmlRegisterType(getQmlFileUrl(QStringLiteral("CloseButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton");
qmlRegisterType(getQmlFileUrl(QStringLiteral("StandardTitleBar")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar");
qmlRegisterType(QML_URL_EXPAND(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton");
qmlRegisterType(QML_URL_EXPAND(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton");
qmlRegisterType(QML_URL_EXPAND(QStringLiteral("CloseButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton");
qmlRegisterType(QML_URL_EXPAND(QStringLiteral("StandardTitleBar")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar");
}
FRAMELESSHELPER_END_NAMESPACE

View File

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

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 <framelesswindowsmanager.h>
#include <utils.h>
#include "framelessquickeventfilter.h"
FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Options options) : QObject(q)
static constexpr const char QT_QUICKITEM_CLASS_NAME[] = "QQuickItem";
FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const UserSettings &settings) : QObject(q)
{
Q_ASSERT(q);
if (!q) {
return;
}
q_ptr = q;
m_params.options = options;
m_settings = settings;
initialize();
}
FramelessQuickWindowPrivate::~FramelessQuickWindowPrivate() = default;
bool FramelessQuickWindowPrivate::isHidden() const
{
Q_Q(const FramelessQuickWindow);
return (q->visibility() == FramelessQuickWindow::Hidden);
}
bool FramelessQuickWindowPrivate::isNormal() const
{
Q_Q(const FramelessQuickWindow);
return (q->visibility() == FramelessQuickWindow::Windowed);
}
bool FramelessQuickWindowPrivate::isMinimized() const
{
Q_Q(const FramelessQuickWindow);
return (q->visibility() == FramelessQuickWindow::Minimized);
}
bool FramelessQuickWindowPrivate::isZoomed() const
{
Q_Q(const FramelessQuickWindow);
const FramelessQuickWindow::Visibility visibility = q->visibility();
return ((visibility == FramelessQuickWindow::Maximized) ||
((m_params.options & Option::DontTreatFullScreenAsZoomed)
((m_settings.options & Option::DontTreatFullScreenAsZoomed)
? false : (visibility == FramelessQuickWindow::FullScreen)));
}
bool FramelessQuickWindowPrivate::isFullScreen() const
{
Q_Q(const FramelessQuickWindow);
return (q->visibility() == FramelessQuickWindow::FullScreen);
}
bool FramelessQuickWindowPrivate::isFixedSize() const
{
if (m_params.options & Option::DisableResizing) {
if (m_settings.options & Option::DisableResizing) {
return true;
}
Q_Q(const FramelessQuickWindow);
@ -90,8 +115,10 @@ void FramelessQuickWindowPrivate::setTitleBarItem(QQuickItem *item)
if (!item) {
return;
}
Q_Q(FramelessQuickWindow);
FramelessQuickEventFilter::setTitleBarItem(q, item);
if (m_titleBarItem == item) {
return;
}
m_titleBarItem = item;
}
void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item)
@ -100,8 +127,14 @@ void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item)
if (!item) {
return;
}
Q_Q(FramelessQuickWindow);
FramelessQuickEventFilter::setHitTestVisible(q, item);
static constexpr const bool visible = true;
const bool exists = m_hitTestVisibleItems.contains(item);
if (visible && !exists) {
m_hitTestVisibleItems.append(item);
}
if constexpr (!visible && exists) {
m_hitTestVisibleItems.removeAll(item);
}
}
void FramelessQuickWindowPrivate::moveToDesktopCenter()
@ -132,6 +165,19 @@ void FramelessQuickWindowPrivate::setFixedSize(const bool value, const bool forc
Q_EMIT q->fixedSizeChanged();
}
void FramelessQuickWindowPrivate::bringToFront()
{
Q_Q(FramelessQuickWindow);
if (isHidden()) {
q->show();
}
if (isMinimized()) {
q->showNormal(); // ### FIXME !!!
}
q->raise();
q->requestActivate();
}
void FramelessQuickWindowPrivate::showMinimized2()
{
#ifdef Q_OS_WINDOWS
@ -165,11 +211,10 @@ void FramelessQuickWindowPrivate::toggleFullScreen()
return;
}
Q_Q(FramelessQuickWindow);
const QWindow::Visibility visibility = q->visibility();
if (visibility == QWindow::FullScreen) {
if (isFullScreen()) {
q->setVisibility(m_savedVisibility);
} else {
m_savedVisibility = visibility;
m_savedVisibility = q->visibility();
q->showFullScreen();
}
}
@ -178,14 +223,10 @@ void FramelessQuickWindowPrivate::showSystemMenu(const QPoint &pos)
{
#ifdef Q_OS_WINDOWS
Q_Q(FramelessQuickWindow);
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint globalPos = q->mapToGlobal(pos);
# else
const QPoint globalPos = q->mapToGlobal(pos);
# endif
const QPoint nativePos = QPointF(QPointF(globalPos) * q->effectiveDevicePixelRatio()).toPoint();
Utils::showSystemMenu(m_params.windowId, nativePos, m_params.options,
m_params.systemMenuOffset, m_params.isWindowFixedSize);
Utils::showSystemMenu(m_params.windowId, nativePos, m_settings.options,
m_settings.systemMenuOffset, m_params.isWindowFixedSize);
#endif
}
@ -240,76 +281,285 @@ void FramelessQuickWindowPrivate::initialize()
m_params.getWindowState = [q]() -> Qt::WindowState { return q->windowState(); };
m_params.setWindowState = [q](const Qt::WindowState state) -> void { q->setWindowState(state); };
m_params.getWindowHandle = [q]() -> QWindow * { return q; };
m_params.windowToScreen = [q](const QPoint &pos) -> QPoint { return q->mapToGlobal(pos); };
m_params.screenToWindow = [q](const QPoint &pos) -> QPoint { return q->mapFromGlobal(pos); };
m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); };
m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
m_params.getWindowDevicePixelRatio = [q]() -> qreal { return q->effectiveDevicePixelRatio(); };
FramelessWindowsManager * const manager = FramelessWindowsManager::instance();
manager->addWindow(m_params);
FramelessQuickEventFilter::addWindow(m_params);
#ifdef Q_OS_WINDOWS
if (isFrameBorderVisible()) {
manager->addWindow(m_settings, m_params);
QQuickItem * const rootItem = q->contentItem();
const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem);
m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
const bool frameBorderVisible = shouldDrawFrameBorder();
if (frameBorderVisible) {
updateTopBorderHeight();
updateTopBorderColor();
connect(q, &FramelessQuickWindow::visibilityChanged, this, [this, q](){
}
connect(q, &FramelessQuickWindow::visibilityChanged, this, [this, q, frameBorderVisible](){
if (frameBorderVisible) {
updateTopBorderHeight();
}
Q_EMIT q->hiddenChanged();
Q_EMIT q->normalChanged();
Q_EMIT q->minimizedChanged();
Q_EMIT q->zoomedChanged();
Q_EMIT q->fullScreenChanged();
});
connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor);
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this, q](){
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this, q, frameBorderVisible](){
if (frameBorderVisible) {
updateTopBorderColor();
}
Q_EMIT q->frameBorderColorChanged();
});
const auto topBorderAnchors = new QQuickAnchors(m_topBorderRectangle.data(), m_topBorderRectangle.data());
topBorderAnchors->setTop(rootItemPrivate->top());
topBorderAnchors->setLeft(rootItemPrivate->left());
topBorderAnchors->setRight(rootItemPrivate->right());
}
#endif
if (m_params.options & Option::DisableResizing) {
m_topBorderAnchors.reset(new QQuickAnchors(m_topBorderRectangle.data(), m_topBorderRectangle.data()));
m_topBorderAnchors->setTop(rootItemPrivate->top());
m_topBorderAnchors->setLeft(rootItemPrivate->left());
m_topBorderAnchors->setRight(rootItemPrivate->right());
if (m_settings.options & Option::DisableResizing) {
setFixedSize(true, true);
}
}
bool FramelessQuickWindowPrivate::isFrameBorderVisible() const
QRect FramelessQuickWindowPrivate::mapItemGeometryToScene(const QQuickItem * const item) const
{
Q_ASSERT(item);
if (!item) {
return {};
}
const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
const QSizeF size = item->size();
#else
const QSizeF size = {item->width(), item->height()};
#endif
return QRectF(originPoint, size).toRect();
}
bool FramelessQuickWindowPrivate::isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const
{
Q_ASSERT(button);
if (!button) {
return false;
}
*button = SystemButtonType::Unknown;
if (!m_settings.minimizeButton || !m_settings.maximizeButton || !m_settings.closeButton) {
return false;
}
if (!m_settings.minimizeButton->inherits(QT_QUICKITEM_CLASS_NAME)
|| !m_settings.maximizeButton->inherits(QT_QUICKITEM_CLASS_NAME)
|| !m_settings.closeButton->inherits(QT_QUICKITEM_CLASS_NAME)) {
return false;
}
const auto minBtn = qobject_cast<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
return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater());
return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater()
&& isNormal() && !(m_settings.options & Option::DontDrawTopWindowFrameBorder));
#else
return false;
#endif
}
void FramelessQuickWindowPrivate::updateTopBorderColor()
bool FramelessQuickWindowPrivate::shouldIgnoreMouseEvents(const QPoint &pos) const
{
if (!isFrameBorderVisible()) {
Q_Q(const FramelessQuickWindow);
return (isNormal() && ((pos.x() < kDefaultResizeBorderThickness)
|| (pos.x() >= (q->width() - kDefaultResizeBorderThickness))
|| (pos.y() < kDefaultResizeBorderThickness)));
}
void FramelessQuickWindowPrivate::showEventHandler(QShowEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (m_windowExposed) {
return;
}
m_windowExposed = true;
if (m_settings.options & Option::DontMoveWindowToDesktopCenter) {
if (!m_settings.startupPosition.isNull()) {
m_params.setWindowPosition(m_settings.startupPosition);
}
if (!m_settings.startupSize.isEmpty()) {
m_params.setWindowSize(m_settings.startupSize);
}
if (m_settings.startupState != Qt::WindowNoState) {
m_params.setWindowState(m_settings.startupState);
}
} else {
moveToDesktopCenter();
}
}
void FramelessQuickWindowPrivate::mousePressEventHandler(QMouseEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (m_settings.options & Option::DisableDragging) {
return;
}
if (event->button() != Qt::LeftButton) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint scenePos = event->scenePosition().toPoint();
#else
const QPoint scenePos = event->windowPos().toPoint();
#endif
if (shouldIgnoreMouseEvents(scenePos)) {
return;
}
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
startSystemMove2();
}
void FramelessQuickWindowPrivate::mouseReleaseEventHandler(QMouseEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (m_settings.options & Option::DisableSystemMenu) {
return;
}
if (event->button() != Qt::RightButton) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint scenePos = event->scenePosition().toPoint();
#else
const QPoint scenePos = event->windowPos().toPoint();
#endif
if (shouldIgnoreMouseEvents(scenePos)) {
return;
}
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
showSystemMenu(scenePos);
}
void FramelessQuickWindowPrivate::mouseDoubleClickEventHandler(QMouseEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) {
return;
}
if (event->button() != Qt::LeftButton) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint scenePos = event->scenePosition().toPoint();
#else
const QPoint scenePos = event->windowPos().toPoint();
#endif
if (shouldIgnoreMouseEvents(scenePos)) {
return;
}
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
toggleMaximize();
}
void FramelessQuickWindowPrivate::updateTopBorderColor()
{
#ifdef Q_OS_WINDOWS
m_topBorderRectangle->setColor(getFrameBorderColor());
#endif
}
void FramelessQuickWindowPrivate::updateTopBorderHeight()
{
if (!isFrameBorderVisible()) {
return;
}
Q_Q(FramelessQuickWindow);
const qreal newHeight = ((q->visibility() == FramelessQuickWindow::Windowed) ? 1.0 : 0.0);
#ifdef Q_OS_WINDOWS
const qreal newHeight = (isNormal() ? 1.0 : 0.0);
m_topBorderRectangle->setHeight(newHeight);
#endif
}
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const Options options) : QQuickWindow(parent)
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings) : QQuickWindow(parent)
{
d_ptr.reset(new FramelessQuickWindowPrivate(this, options));
d_ptr.reset(new FramelessQuickWindowPrivate(this, settings));
}
FramelessQuickWindow::~FramelessQuickWindow() = default;
bool FramelessQuickWindow::zoomed() const
bool FramelessQuickWindow::isHidden() const
{
Q_D(const FramelessQuickWindow);
return d->isHidden();
}
bool FramelessQuickWindow::isNormal() const
{
Q_D(const FramelessQuickWindow);
return d->isNormal();
}
bool FramelessQuickWindow::isMinimized() const
{
Q_D(const FramelessQuickWindow);
return d->isMinimized();
}
bool FramelessQuickWindow::isZoomed() const
{
Q_D(const FramelessQuickWindow);
return d->isZoomed();
}
bool FramelessQuickWindow::isFullScreen() const
{
Q_D(const FramelessQuickWindow);
return d->isFullScreen();
}
bool FramelessQuickWindow::fixedSize() const
{
Q_D(const FramelessQuickWindow);
@ -354,6 +604,40 @@ void FramelessQuickWindow::moveToDesktopCenter()
d->moveToDesktopCenter();
}
void FramelessQuickWindow::bringToFront()
{
Q_D(FramelessQuickWindow);
d->bringToFront();
}
void FramelessQuickWindow::showEvent(QShowEvent *event)
{
QQuickWindow::showEvent(event);
Q_D(FramelessQuickWindow);
d->showEventHandler(event);
}
void FramelessQuickWindow::mousePressEvent(QMouseEvent *event)
{
QQuickWindow::mousePressEvent(event);
Q_D(FramelessQuickWindow);
d->mousePressEventHandler(event);
}
void FramelessQuickWindow::mouseReleaseEvent(QMouseEvent *event)
{
QQuickWindow::mouseReleaseEvent(event);
Q_D(FramelessQuickWindow);
d->mouseReleaseEventHandler(event);
}
void FramelessQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
QQuickWindow::mouseDoubleClickEvent(event);
Q_D(FramelessQuickWindow);
d->mouseDoubleClickEventHandler(event);
}
void FramelessQuickWindow::showMinimized2()
{
Q_D(FramelessQuickWindow);

View File

@ -31,6 +31,7 @@
QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickRectangle;
class QQuickAnchors;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -44,13 +45,22 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindowPrivate : public QObject
Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate)
public:
explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::Options options);
explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Global::UserSettings &settings = {});
~FramelessQuickWindowPrivate() override;
Q_INVOKABLE Q_NODISCARD bool isHidden() const;
Q_INVOKABLE Q_NODISCARD bool isNormal() const;
Q_INVOKABLE Q_NODISCARD bool isMinimized() const;
Q_INVOKABLE Q_NODISCARD bool isZoomed() const;
Q_INVOKABLE Q_NODISCARD bool isFullScreen() const;
Q_INVOKABLE Q_NODISCARD bool isFixedSize() const;
Q_INVOKABLE Q_NODISCARD QColor getFrameBorderColor() const;
Q_INVOKABLE Q_NODISCARD bool isFrameBorderVisible() const;
Q_INVOKABLE void showEventHandler(QShowEvent *event);
Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event);
Q_INVOKABLE void mouseReleaseEventHandler(QMouseEvent *event);
Q_INVOKABLE void mouseDoubleClickEventHandler(QMouseEvent *event);
public Q_SLOTS:
void showMinimized2();
@ -63,9 +73,15 @@ public Q_SLOTS:
void setHitTestVisible(QQuickItem *item);
void moveToDesktopCenter();
void setFixedSize(const bool value, const bool force = false);
void bringToFront();
private:
void initialize();
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldDrawFrameBorder() const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
private Q_SLOTS:
void updateTopBorderColor();
@ -75,8 +91,13 @@ private:
FramelessQuickWindow *q_ptr = nullptr;
bool m_initialized = false;
QScopedPointer<QQuickRectangle> m_topBorderRectangle;
QScopedPointer<QQuickAnchors> m_topBorderAnchors;
QWindow::Visibility m_savedVisibility = QWindow::Windowed;
Global::FramelessHelperParams m_params = {};
Global::UserSettings m_settings = {};
Global::SystemParameters m_params = {};
bool m_windowExposed = false;
QQuickItem *m_titleBarItem = nullptr;
QList<QQuickItem *> m_hitTestVisibleItems = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -29,9 +29,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags, const Options options) : QMainWindow(parent, flags)
FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags, const UserSettings &settings) : QMainWindow(parent, flags)
{
m_helper.reset(new FramelessWidgetsHelper(this, options));
m_helper.reset(new FramelessWidgetsHelper(this, settings));
}
FramelessMainWindow::~FramelessMainWindow() = default;
@ -86,6 +86,32 @@ void FramelessMainWindow::moveToDesktopCenter()
m_helper->moveToDesktopCenter();
}
void FramelessMainWindow::bringToFront()
{
m_helper->bringToFront();
}
void FramelessMainWindow::showSystemMenu(const QPoint &pos)
{
m_helper->showSystemMenu(pos);
}
void FramelessMainWindow::startSystemMove2()
{
m_helper->startSystemMove2();
}
void FramelessMainWindow::startSystemResize2(const Qt::Edges edges)
{
m_helper->startSystemResize2(edges);
}
void FramelessMainWindow::showEvent(QShowEvent *event)
{
QMainWindow::showEvent(event);
m_helper->showEventHandler(event);
}
void FramelessMainWindow::changeEvent(QEvent *event)
{
QMainWindow::changeEvent(event);

View File

@ -29,9 +29,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
FramelessWidget::FramelessWidget(QWidget *parent, const Options options) : QWidget(parent)
FramelessWidget::FramelessWidget(QWidget *parent, const UserSettings &settings) : QWidget(parent)
{
m_helper.reset(new FramelessWidgetsHelper(this, options));
m_helper.reset(new FramelessWidgetsHelper(this, settings));
}
FramelessWidget::~FramelessWidget() = default;
@ -96,6 +96,32 @@ void FramelessWidget::moveToDesktopCenter()
m_helper->moveToDesktopCenter();
}
void FramelessWidget::bringToFront()
{
m_helper->bringToFront();
}
void FramelessWidget::showSystemMenu(const QPoint &pos)
{
m_helper->showSystemMenu(pos);
}
void FramelessWidget::startSystemMove2()
{
m_helper->startSystemMove2();
}
void FramelessWidget::startSystemResize2(const Qt::Edges edges)
{
m_helper->startSystemResize2(edges);
}
void FramelessWidget::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
m_helper->showEventHandler(event);
}
void FramelessWidget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);

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);
if (!q) {
return;
}
this->q = q;
m_params.options = options;
m_settings = settings;
initialize();
}
@ -74,12 +74,12 @@ bool FramelessWidgetsHelper::isNormal() const
bool FramelessWidgetsHelper::isZoomed() const
{
return (q->isMaximized() || ((m_params.options & Option::DontTreatFullScreenAsZoomed) ? false : q->isFullScreen()));
return (q->isMaximized() || ((m_settings.options & Option::DontTreatFullScreenAsZoomed) ? false : q->isFullScreen()));
}
bool FramelessWidgetsHelper::isFixedSize() const
{
if (m_params.options & Option::DisableResizing) {
if (m_settings.options & Option::DisableResizing) {
return true;
}
if (q->windowFlags() & Qt::MSWindowsFixedSizeDialogHint) {
@ -124,7 +124,7 @@ void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget)
if (m_userTitleBarWidget == widget) {
return;
}
if (m_params.options & Option::UseStandardWindowLayout) {
if (m_settings.options & Option::UseStandardWindowLayout) {
if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) {
m_mainLayout->removeWidget(m_systemTitleBarWidget);
m_systemTitleBarWidget->hide();
@ -152,7 +152,7 @@ void FramelessWidgetsHelper::setContentWidget(QWidget *widget)
if (!widget) {
return;
}
if (!(m_params.options & Option::UseStandardWindowLayout)) {
if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return;
}
if (m_userContentWidget == widget) {
@ -188,6 +188,31 @@ void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget)
}
}
void FramelessWidgetsHelper::showEventHandler(QShowEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (m_windowExposed) {
return;
}
m_windowExposed = true;
if (m_settings.options & Option::DontMoveWindowToDesktopCenter) {
if (!m_settings.startupPosition.isNull()) {
m_params.setWindowPosition(m_settings.startupPosition);
}
if (!m_settings.startupSize.isEmpty()) {
m_params.setWindowSize(m_settings.startupSize);
}
if (m_settings.startupState != Qt::WindowNoState) {
m_params.setWindowState(m_settings.startupState);
}
} else {
moveToDesktopCenter();
}
}
void FramelessWidgetsHelper::changeEventHandler(QEvent *event)
{
Q_ASSERT(event);
@ -198,7 +223,7 @@ void FramelessWidgetsHelper::changeEventHandler(QEvent *event)
if ((type != QEvent::WindowStateChange) && (type != QEvent::ActivationChange)) {
return;
}
const bool standardLayout = (m_params.options & Option::UseStandardWindowLayout);
const bool standardLayout = (m_settings.options & Option::UseStandardWindowLayout);
if (type == QEvent::WindowStateChange) {
if (standardLayout) {
if (isZoomed()) {
@ -241,7 +266,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event)
if (!event) {
return;
}
if (m_params.options & Option::DisableDragging) {
if (m_settings.options & Option::DisableDragging) {
return;
}
if (event->button() != Qt::LeftButton) {
@ -258,7 +283,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event)
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
Utils::startSystemMove(m_window);
startSystemMove2();
}
void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
@ -267,7 +292,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
if (!event) {
return;
}
if (m_params.options & Option::DisableSystemMenu) {
if (m_settings.options & Option::DisableSystemMenu) {
return;
}
if (event->button() != Qt::RightButton) {
@ -284,16 +309,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
if (!isInTitleBarDraggableArea(scenePos)) {
return;
}
#ifdef Q_OS_WINDOWS
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint globalPos = event->globalPosition().toPoint();
# else
const QPoint globalPos = event->globalPos();
# endif
const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint();
Utils::showSystemMenu(m_params.windowId, nativePos, m_params.options,
m_params.systemMenuOffset, m_params.isWindowFixedSize);
#endif
showSystemMenu(scenePos);
}
void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event)
@ -302,7 +318,7 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event)
if (!event) {
return;
}
if ((m_params.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) {
if ((m_settings.options & Option::NoDoubleClickMaximizeToggle) || isFixedSize()) {
return;
}
if (event->button() != Qt::LeftButton) {
@ -365,39 +381,54 @@ void FramelessWidgetsHelper::initialize()
m_params.getWindowState = [this]() -> Qt::WindowState { return Utils::windowStatesToWindowState(q->windowState()); };
m_params.setWindowState = [this](const Qt::WindowState state) -> void { q->setWindowState(state); };
m_params.getWindowHandle = [this]() -> QWindow * { return m_window; };
if (m_params.options & Option::UseStandardWindowLayout) {
m_params.windowToScreen = [this](const QPoint &pos) -> QPoint { return q->mapToGlobal(pos); };
m_params.screenToWindow = [this](const QPoint &pos) -> QPoint { return q->mapFromGlobal(pos); };
m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool { return isInSystemButtons(pos, button); };
m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
m_params.getWindowDevicePixelRatio = [this]() -> qreal { return q->devicePixelRatioF(); };
if (m_settings.options & Option::UseStandardWindowLayout) {
if (q->inherits(QT_MAINWINDOW_CLASS_NAME)) {
m_params.options &= ~Options(Option::UseStandardWindowLayout);
m_settings.options &= ~Options(Option::UseStandardWindowLayout);
qWarning() << "\"Option::UseStandardWindowLayout\" is not compatible with QMainWindow and it's subclasses."
" Enabling this option will mess up with your main window's layout.";
}
}
if (m_params.options & Option::BeCompatibleWithQtFramelessWindowHint) {
if (m_settings.options & Option::BeCompatibleWithQtFramelessWindowHint) {
Utils::tryToBeCompatibleWithQtFramelessWindowHint(windowId, m_params.getWindowFlags,
m_params.setWindowFlags, true);
}
FramelessWindowsManager * const manager = FramelessWindowsManager::instance();
manager->addWindow(m_params);
manager->addWindow(m_settings, m_params);
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){
if (m_params.options & Option::UseStandardWindowLayout) {
if (m_settings.options & Option::UseStandardWindowLayout) {
updateSystemTitleBarStyleSheet();
updateSystemButtonsIcon();
q->update();
}
QMetaObject::invokeMethod(q, "systemThemeChanged");
});
connect(m_window, &QWindow::windowStateChanged, this, [this](){
connect(m_window, &QWindow::visibilityChanged, this, [this](){
QMetaObject::invokeMethod(q, "hiddenChanged");
QMetaObject::invokeMethod(q, "normalChanged");
QMetaObject::invokeMethod(q, "zoomedChanged");
});
setupInitialUi();
if (m_params.options & Option::DisableResizing) {
if (m_settings.options & Option::UseStandardWindowLayout) {
Q_ASSERT(m_systemMinimizeButton);
Q_ASSERT(m_systemMaximizeButton);
Q_ASSERT(m_systemCloseButton);
m_settings.minimizeButton = m_systemMinimizeButton;
m_settings.maximizeButton = m_systemMaximizeButton;
m_settings.closeButton = m_systemCloseButton;
}
if (m_settings.options & Option::DisableResizing) {
setFixedSize(true, true);
}
}
void FramelessWidgetsHelper::createSystemTitleBar()
{
if (!(m_params.options & Option::UseStandardWindowLayout)) {
if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return;
}
m_systemTitleBarWidget = new QWidget(q);
@ -440,7 +471,7 @@ void FramelessWidgetsHelper::createSystemTitleBar()
void FramelessWidgetsHelper::createUserContentContainer()
{
if (!(m_params.options & Option::UseStandardWindowLayout)) {
if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return;
}
m_userContentContainerWidget = new QWidget(q);
@ -453,7 +484,7 @@ void FramelessWidgetsHelper::createUserContentContainer()
void FramelessWidgetsHelper::setupInitialUi()
{
if (m_params.options & Option::UseStandardWindowLayout) {
if (m_settings.options & Option::UseStandardWindowLayout) {
createSystemTitleBar();
createUserContentContainer();
m_mainLayout = new QVBoxLayout(q);
@ -468,33 +499,69 @@ void FramelessWidgetsHelper::setupInitialUi()
updateContentsMargins();
}
bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const
QRect FramelessWidgetsHelper::mapWidgetGeometryToScene(const QWidget * const widget) const
{
const QRegion draggableRegion = [this]() -> QRegion {
const auto mapGeometryToScene = [this](const QWidget * const widget) -> QRect {
Q_ASSERT(widget);
if (!widget) {
return {};
}
return QRect(widget->mapTo(q, QPoint(0, 0)), widget->size());
};
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
{
const QRegion draggableRegion = [this]() -> QRegion {
if (m_userTitleBarWidget) {
QRegion region = mapGeometryToScene(m_userTitleBarWidget);
QRegion region = mapWidgetGeometryToScene(m_userTitleBarWidget);
if (!m_hitTestVisibleWidgets.isEmpty()) {
for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) {
Q_ASSERT(widget);
if (widget) {
region -= mapGeometryToScene(widget);
region -= mapWidgetGeometryToScene(widget);
}
}
}
return region;
}
if (m_params.options & Option::UseStandardWindowLayout) {
QRegion region = mapGeometryToScene(m_systemTitleBarWidget);
region -= mapGeometryToScene(m_systemMinimizeButton);
region -= mapGeometryToScene(m_systemMaximizeButton);
region -= mapGeometryToScene(m_systemCloseButton);
if (m_settings.options & Option::UseStandardWindowLayout) {
QRegion region = mapWidgetGeometryToScene(m_systemTitleBarWidget);
region -= mapWidgetGeometryToScene(m_systemMinimizeButton);
region -= mapWidgetGeometryToScene(m_systemMaximizeButton);
region -= mapWidgetGeometryToScene(m_systemCloseButton);
return region;
}
return {};
@ -506,7 +573,7 @@ bool FramelessWidgetsHelper::shouldDrawFrameBorder() const
{
#ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater()
&& isNormal() && !(m_params.options & Option::DontDrawTopWindowFrameBorder));
&& isNormal() && !(m_settings.options & Option::DontDrawTopWindowFrameBorder));
#else
return false;
#endif
@ -528,7 +595,7 @@ void FramelessWidgetsHelper::updateContentsMargins()
void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet()
{
if (!(m_params.options & Option::UseStandardWindowLayout)) {
if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return;
}
const bool active = q->isActiveWindow();
@ -563,7 +630,7 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet()
void FramelessWidgetsHelper::updateSystemButtonsIcon()
{
if (!(m_params.options & Option::UseStandardWindowLayout)) {
if (!(m_settings.options & Option::UseStandardWindowLayout)) {
return;
}
const SystemTheme theme = ((Utils::shouldAppsUseDarkMode() || Utils::isTitleBarColorized()) ? SystemTheme::Dark : SystemTheme::Light);
@ -609,4 +676,44 @@ void FramelessWidgetsHelper::moveToDesktopCenter()
m_params.getWindowSize, m_params.setWindowPosition, true);
}
void FramelessWidgetsHelper::bringToFront()
{
if (q->isHidden()) {
q->show();
}
if (q->isMinimized()) {
q->setWindowState(q->windowState() & ~Qt::WindowMinimized);
}
q->raise();
q->activateWindow();
}
void FramelessWidgetsHelper::showSystemMenu(const QPoint &pos)
{
#ifdef Q_OS_WINDOWS
const QPoint globalPos = q->mapToGlobal(pos);
const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint();
Utils::showSystemMenu(m_params.windowId, nativePos, m_settings.options,
m_settings.systemMenuOffset, m_params.isWindowFixedSize);
#endif
}
void FramelessWidgetsHelper::startSystemMove2()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
m_window->startSystemMove();
#else
Utils::startSystemMove(m_window);
#endif
}
void FramelessWidgetsHelper::startSystemResize2(const Qt::Edges edges)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
m_window->startSystemResize(edges);
#else
Utils::startSystemResize(m_window, edges);
#endif
}
FRAMELESSHELPER_END_NAMESPACE