Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-03-23 16:38:14 +08:00
parent 059b8d7982
commit 6ffc894213
26 changed files with 417 additions and 241 deletions

View File

@ -79,10 +79,10 @@ void MainWindow::setupUi()
setTitleBarWidget(titleBarWidget); setTitleBarWidget(titleBarWidget);
setHitTestVisible(titleBar->iconButton, true); setHitTestVisible(titleBar->iconButton);
setHitTestVisible(titleBar->minimizeButton, true); setHitTestVisible(titleBar->minimizeButton);
setHitTestVisible(titleBar->maximizeButton, true); setHitTestVisible(titleBar->maximizeButton);
setHitTestVisible(titleBar->closeButton, true); setHitTestVisible(titleBar->closeButton);
connect(titleBar->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized); connect(titleBar->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized);
connect(titleBar->maximizeButton, &QPushButton::clicked, this, &MainWindow::toggleMaximized); connect(titleBar->maximizeButton, &QPushButton::clicked, this, &MainWindow::toggleMaximized);

View File

@ -65,21 +65,21 @@ FramelessWindow {
title: window.title title: window.title
minimizeButton { minimizeButton {
id: minimizeButton id: minimizeButton
onClicked: FramelessUtils.showMinimized2(window) onClicked: window.showMinimized2()
} }
maximizeButton { maximizeButton {
id: maximizeButton id: maximizeButton
onClicked: FramelessUtils.toggleMaximize(window) onClicked: window.toggleMaximize()
} }
closeButton { closeButton {
id: closeButton id: closeButton
onClicked: window.close() onClicked: window.close()
} }
Component.onCompleted: { Component.onCompleted: {
FramelessHelper.setTitleBarItem(window, titleBar); window.setTitleBarItem(titleBar);
FramelessHelper.setHitTestVisible(window, minimizeButton, true); window.setHitTestVisible(minimizeButton);
FramelessHelper.setHitTestVisible(window, maximizeButton, true); window.setHitTestVisible(maximizeButton);
FramelessHelper.setHitTestVisible(window, closeButton, true); window.setHitTestVisible(closeButton);
} }
} }
} }

View File

@ -148,7 +148,8 @@ enum class Option : int
DisableResizing = 0x00002000, // Disable resizing of the window. DisableResizing = 0x00002000, // Disable resizing of the window.
DisableDragging = 0x00004000, // Disable dragging through the titlebar of the window. DisableDragging = 0x00004000, // Disable dragging through the titlebar of the window.
DontTouchCursorShape = 0x00008000, // Don't change the cursor shape while the mouse is hovering above the window. DontTouchCursorShape = 0x00008000, // Don't change the cursor shape while the mouse is hovering above the window.
DontMoveWindowToDesktopCenter = 0x00010000 // Don't move the window to the desktop center before shown. DontMoveWindowToDesktopCenter = 0x00010000, // Don't move the window to the desktop center before shown.
DontTreatFullScreenAsZoomed = 0x00020000 // Don't treat fullscreen as zoomed (maximized).
}; };
Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_FLAGS(Options, Option)
Q_FLAG_NS(Options) Q_FLAG_NS(Options)

View File

@ -46,7 +46,7 @@ public:
Q_INVOKABLE static void addWindow(QQuickWindow *window); Q_INVOKABLE static void addWindow(QQuickWindow *window);
Q_INVOKABLE static void removeWindow(QQuickWindow *window); Q_INVOKABLE static void removeWindow(QQuickWindow *window);
Q_INVOKABLE static void setTitleBarItem(QQuickWindow *window, QQuickItem *item); Q_INVOKABLE static void setTitleBarItem(QQuickWindow *window, QQuickItem *item);
Q_INVOKABLE static void setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible); Q_INVOKABLE static void setHitTestVisible(QQuickWindow *window, QQuickItem *item);
protected: protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -52,7 +52,7 @@ public:
Q_INVOKABLE static void addWindow(QQuickWindow *window); Q_INVOKABLE static void addWindow(QQuickWindow *window);
Q_INVOKABLE static void removeWindow(QQuickWindow *window); Q_INVOKABLE static void removeWindow(QQuickWindow *window);
Q_INVOKABLE static void setTitleBarItem(QQuickWindow *window, QQuickItem *item); Q_INVOKABLE static void setTitleBarItem(QQuickWindow *window, QQuickItem *item);
Q_INVOKABLE static void setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible); Q_INVOKABLE static void setHitTestVisible(QQuickWindow *window, QQuickItem *item);
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -47,11 +47,9 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL) Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL)
Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL) Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL) Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL)
Q_PROPERTY(QColor frameBorderActiveColor READ frameBorderActiveColor NOTIFY frameBorderActiveColorChanged FINAL)
Q_PROPERTY(QColor frameBorderInactiveColor READ frameBorderInactiveColor NOTIFY frameBorderInactiveColorChanged FINAL)
Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL) Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL)
Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL) Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL)
Q_PROPERTY(bool titleBarColorVisible READ titleBarColorVisible NOTIFY titleBarColorVisibleChanged FINAL) Q_PROPERTY(bool titleBarColorized READ titleBarColorized NOTIFY titleBarColorizedChanged FINAL)
Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL) Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL)
Q_PROPERTY(QColor defaultSystemDarkColor READ defaultSystemDarkColor CONSTANT FINAL) Q_PROPERTY(QColor defaultSystemDarkColor READ defaultSystemDarkColor CONSTANT FINAL)
Q_PROPERTY(QSizeF defaultSystemButtonSize READ defaultSystemButtonSize CONSTANT FINAL) Q_PROPERTY(QSizeF defaultSystemButtonSize READ defaultSystemButtonSize CONSTANT FINAL)
@ -64,28 +62,18 @@ public:
Q_NODISCARD static qreal titleBarHeight(); Q_NODISCARD static qreal titleBarHeight();
Q_NODISCARD static bool frameBorderVisible(); Q_NODISCARD static bool frameBorderVisible();
Q_NODISCARD static qreal frameBorderThickness(); Q_NODISCARD static qreal frameBorderThickness();
Q_NODISCARD static QColor frameBorderActiveColor();
Q_NODISCARD static QColor frameBorderInactiveColor();
Q_NODISCARD static bool darkModeEnabled(); Q_NODISCARD static bool darkModeEnabled();
Q_NODISCARD static QColor systemAccentColor(); Q_NODISCARD static QColor systemAccentColor();
Q_NODISCARD static bool titleBarColorVisible(); Q_NODISCARD static bool titleBarColorized();
Q_NODISCARD static QColor defaultSystemLightColor(); Q_NODISCARD static QColor defaultSystemLightColor();
Q_NODISCARD static QColor defaultSystemDarkColor(); Q_NODISCARD static QColor defaultSystemDarkColor();
Q_NODISCARD static QSizeF defaultSystemButtonSize(); Q_NODISCARD static QSizeF defaultSystemButtonSize();
Q_NODISCARD static QSizeF defaultSystemButtonIconSize(); Q_NODISCARD static QSizeF defaultSystemButtonIconSize();
Q_INVOKABLE static void showMinimized2(QQuickWindow *window);
Q_INVOKABLE static void toggleMaximize(QQuickWindow *window);
Q_INVOKABLE static void showSystemMenu(QQuickWindow *window, const QPoint &pos);
Q_INVOKABLE static void startSystemMove2(QQuickWindow *window);
Q_INVOKABLE static void startSystemResize2(QQuickWindow *window, const Qt::Edges edges);
Q_SIGNALS: Q_SIGNALS:
void frameBorderActiveColorChanged();
void frameBorderInactiveColorChanged();
void darkModeEnabledChanged(); void darkModeEnabledChanged();
void systemAccentColorChanged(); void systemAccentColorChanged();
void titleBarColorVisibleChanged(); void titleBarColorizedChanged();
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -25,6 +25,7 @@
#pragma once #pragma once
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickwindow.h> #include <QtQuick/qquickwindow.h>
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
@ -34,18 +35,34 @@ class FramelessQuickWindowPrivate;
class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow
{ {
Q_OBJECT Q_OBJECT
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessWindow)
#endif
Q_DECLARE_PRIVATE(FramelessQuickWindow) Q_DECLARE_PRIVATE(FramelessQuickWindow)
Q_DISABLE_COPY_MOVE(FramelessQuickWindow) Q_DISABLE_COPY_MOVE(FramelessQuickWindow)
Q_PROPERTY(bool zoomed READ zoomed NOTIFY zoomedChanged FINAL) Q_PROPERTY(bool zoomed READ zoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL)
public: public:
explicit FramelessQuickWindow(QWindow *parent = nullptr); explicit FramelessQuickWindow(QWindow *parent = nullptr, const Options options = {});
~FramelessQuickWindow() override; ~FramelessQuickWindow() override;
Q_NODISCARD bool zoomed() const; Q_NODISCARD bool zoomed() const;
Q_NODISCARD QColor frameBorderColor() const;
public Q_SLOTS:
void showMinimized2();
void toggleMaximize();
void toggleFullScreen();
void showSystemMenu(const QPoint &pos);
void startSystemMove2();
void startSystemResize2(const Qt::Edges edges);
void setTitleBarItem(QQuickItem *item);
void setHitTestVisible(QQuickItem *item);
Q_SIGNALS: Q_SIGNALS:
void zoomedChanged(); void zoomedChanged();
void frameBorderColorChanged();
private: private:
QScopedPointer<FramelessQuickWindowPrivate> d_ptr; QScopedPointer<FramelessQuickWindowPrivate> d_ptr;

View File

@ -47,9 +47,10 @@ public:
void setTitleBarWidget(QWidget *widget); void setTitleBarWidget(QWidget *widget);
Q_NODISCARD QWidget *titleBarWidget() const; Q_NODISCARD QWidget *titleBarWidget() const;
Q_INVOKABLE void setHitTestVisible(QWidget *widget, const bool visible); Q_INVOKABLE void setHitTestVisible(QWidget *widget);
Q_INVOKABLE void toggleMaximized(); Q_INVOKABLE void toggleMaximized();
Q_INVOKABLE void toggleFullScreen();
protected: protected:
void changeEvent(QEvent *event) override; void changeEvent(QEvent *event) override;

View File

@ -51,9 +51,10 @@ public:
void setContentWidget(QWidget *widget); void setContentWidget(QWidget *widget);
Q_NODISCARD QWidget *contentWidget() const; Q_NODISCARD QWidget *contentWidget() const;
Q_INVOKABLE void setHitTestVisible(QWidget *widget, const bool visible); Q_INVOKABLE void setHitTestVisible(QWidget *widget);
Q_INVOKABLE void toggleMaximized(); Q_INVOKABLE void toggleMaximized();
Q_INVOKABLE void toggleFullScreen();
protected: protected:
void changeEvent(QEvent *event) override; void changeEvent(QEvent *event) override;

View File

@ -57,9 +57,10 @@ public:
Q_INVOKABLE void setContentWidget(QWidget *widget); Q_INVOKABLE void setContentWidget(QWidget *widget);
Q_NODISCARD Q_INVOKABLE QWidget *contentWidget() const; Q_NODISCARD Q_INVOKABLE QWidget *contentWidget() const;
Q_INVOKABLE void setHitTestVisible(QWidget *widget, const bool visible); Q_INVOKABLE void setHitTestVisible(QWidget *widget);
Q_INVOKABLE void toggleMaximized(); Q_INVOKABLE void toggleMaximized();
Q_INVOKABLE void toggleFullScreen();
Q_INVOKABLE void changeEventHandler(QEvent *event); Q_INVOKABLE void changeEventHandler(QEvent *event);
Q_INVOKABLE void paintEventHandler(QPaintEvent *event); Q_INVOKABLE void paintEventHandler(QPaintEvent *event);
@ -96,6 +97,8 @@ private:
QWidgetList m_hitTestVisibleWidgets = {}; QWidgetList m_hitTestVisibleWidgets = {};
QWidget *m_userContentContainerWidget = nullptr; QWidget *m_userContentContainerWidget = nullptr;
QVBoxLayout *m_userContentContainerLayout = nullptr; QVBoxLayout *m_userContentContainerLayout = nullptr;
Qt::WindowStates m_savedWindowState = {};
QWindow *m_window = nullptr;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -30,11 +30,17 @@
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
struct QtHelperInternalData
{
QWindow *window = nullptr;
FramelessHelperQt *qtFramelessHelper = nullptr;
Options options = {};
};
struct QtHelper struct QtHelper
{ {
QMutex mutex = {}; QMutex mutex = {};
QHash<QWindow *, FramelessHelperQt *> qtFramelessHelpers = {}; QHash<QWindow *, QtHelperInternalData> data = {};
QHash<QWindow *, Options> options = {};
explicit QtHelper() = default; explicit QtHelper() = default;
~QtHelper() = default; ~QtHelper() = default;
@ -56,18 +62,19 @@ void FramelessHelperQt::addWindow(QWindow *window)
return; return;
} }
g_qtHelper()->mutex.lock(); g_qtHelper()->mutex.lock();
if (g_qtHelper()->qtFramelessHelpers.contains(window)) { if (g_qtHelper()->data.contains(window)) {
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
return; return;
} }
const auto options = qvariant_cast<Options>(window->property(kInternalOptionsFlag)); QtHelperInternalData data = {};
g_qtHelper()->options.insert(window, options); data.window = window;
// Give it a parent so that it can be deleted even if we forget to do so. // Give it a parent so that it can be deleted even if we forget to do so.
const auto qtFramelessHelper = new FramelessHelperQt(window); data.qtFramelessHelper = new FramelessHelperQt(window);
g_qtHelper()->qtFramelessHelpers.insert(window, qtFramelessHelper); data.options = qvariant_cast<Options>(window->property(kInternalOptionsFlag));
g_qtHelper()->data.insert(window, data);
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
window->setFlags(window->flags() | Qt::FramelessWindowHint); window->setFlags(window->flags() | Qt::FramelessWindowHint);
window->installEventFilter(qtFramelessHelper); window->installEventFilter(data.qtFramelessHelper);
} }
void FramelessHelperQt::removeWindow(QWindow *window) void FramelessHelperQt::removeWindow(QWindow *window)
@ -77,17 +84,15 @@ void FramelessHelperQt::removeWindow(QWindow *window)
return; return;
} }
g_qtHelper()->mutex.lock(); g_qtHelper()->mutex.lock();
if (!g_qtHelper()->qtFramelessHelpers.contains(window)) { if (!g_qtHelper()->data.contains(window)) {
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
return; return;
} }
g_qtHelper()->options.remove(window); const QtHelperInternalData data = g_qtHelper()->data.value(window);
FramelessHelperQt *qtFramelessHelper = g_qtHelper()->qtFramelessHelpers.value(window); g_qtHelper()->data.remove(window);
g_qtHelper()->qtFramelessHelpers.remove(window);
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
window->removeEventFilter(qtFramelessHelper); window->removeEventFilter(data.qtFramelessHelper);
delete qtFramelessHelper; delete data.qtFramelessHelper;
qtFramelessHelper = nullptr;
window->setFlags(window->flags() & ~Qt::FramelessWindowHint); window->setFlags(window->flags() & ~Qt::FramelessWindowHint);
} }
@ -109,11 +114,11 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
} }
const auto window = qobject_cast<QWindow *>(object); const auto window = qobject_cast<QWindow *>(object);
g_qtHelper()->mutex.lock(); g_qtHelper()->mutex.lock();
if (!g_qtHelper()->qtFramelessHelpers.contains(window)) { if (!g_qtHelper()->data.contains(window)) {
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
return false; return false;
} }
const Options options = g_qtHelper()->options.value(window); const QtHelperInternalData data = g_qtHelper()->data.value(window);
g_qtHelper()->mutex.unlock(); g_qtHelper()->mutex.unlock();
if (Utils::isWindowFixedSize(window)) { if (Utils::isWindowFixedSize(window)) {
return false; return false;
@ -126,7 +131,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event)
#endif #endif
switch (type) { switch (type) {
case QEvent::MouseMove: { case QEvent::MouseMove: {
if (options & Option::DontTouchCursorShape) { if (data.options & Option::DontTouchCursorShape) {
return false; return false;
} }
const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos);

View File

@ -53,11 +53,18 @@ Q_DECLARE_METATYPE(QMargins)
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
struct Win32UtilsInternalData
{
HWND hwnd = nullptr;
QWindow *window = nullptr;
WNDPROC originalWindowProc = nullptr;
Options options = {};
};
struct Win32UtilsHelper struct Win32UtilsHelper
{ {
QMutex mutex = {}; QMutex mutex = {};
QHash<HWND, WNDPROC> qtWindowProcs = {}; QHash<HWND, Win32UtilsInternalData> data = {};
QHash<HWND, QWindow *> windowMapping = {};
explicit Win32UtilsHelper() = default; explicit Win32UtilsHelper() = default;
~Win32UtilsHelper() = default; ~Win32UtilsHelper() = default;
@ -178,28 +185,28 @@ static const QString successErrorText = QStringLiteral("The operation completed
(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) (const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam)
{ {
g_utilsHelper()->mutex.lock(); g_utilsHelper()->mutex.lock();
if (!g_utilsHelper()->qtWindowProcs.contains(hWnd)) { if (!g_utilsHelper()->data.contains(hWnd)) {
g_utilsHelper()->mutex.unlock(); g_utilsHelper()->mutex.unlock();
return DefWindowProcW(hWnd, uMsg, wParam, lParam); return DefWindowProcW(hWnd, uMsg, wParam, lParam);
} }
const QWindow * const window = g_utilsHelper()->windowMapping.value(hWnd); const Win32UtilsInternalData data = g_utilsHelper()->data.value(hWnd);
Q_ASSERT(window);
if (!window) {
g_utilsHelper()->mutex.unlock(); g_utilsHelper()->mutex.unlock();
Q_ASSERT(data.window);
if (!data.window) {
return DefWindowProcW(hWnd, uMsg, wParam, lParam); return DefWindowProcW(hWnd, uMsg, wParam, lParam);
} }
g_utilsHelper()->mutex.unlock();
const auto winId = reinterpret_cast<WId>(hWnd); const auto winId = reinterpret_cast<WId>(hWnd);
const auto getGlobalPosFromMouse = [lParam]() -> QPoint { const auto getGlobalPosFromMouse = [lParam]() -> QPoint {
return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
}; };
const auto getGlobalPosFromKeyboard = [hWnd, winId]() -> QPoint { const auto getGlobalPosFromKeyboard = [hWnd, winId, &data]() -> QPoint {
RECT windowPos = {}; RECT windowPos = {};
if (GetWindowRect(hWnd, &windowPos) == FALSE) { if (GetWindowRect(hWnd, &windowPos) == FALSE) {
qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowRect")); qWarning() << Utils::getSystemErrorMessage(QStringLiteral("GetWindowRect"));
return {}; return {};
} }
const bool maxOrFull = (IsMaximized(hWnd) || Utils::isFullScreen(winId)); const bool maxOrFull = (IsMaximized(hWnd) ||
((data.options & Option::DontTreatFullScreenAsZoomed) ? false : Utils::isFullScreen(winId)));
const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true); const int frameSizeX = Utils::getResizeBorderThickness(winId, true, true);
const bool frameBorderVisible = Utils::isWindowFrameBorderVisible(); const bool frameBorderVisible = Utils::isWindowFrameBorderVisible();
const int horizontalOffset = ((maxOrFull || !frameBorderVisible) ? 0 : frameSizeX); const int horizontalOffset = ((maxOrFull || !frameBorderVisible) ? 0 : frameSizeX);
@ -244,21 +251,18 @@ static const QString successErrorText = QStringLiteral("The operation completed
} }
} }
if (shouldShowSystemMenu) { if (shouldShowSystemMenu) {
Utils::showSystemMenu(window, globalPos); Utils::showSystemMenu(data.window, globalPos);
// QPA's internal code will handle system menu events separately, and its // QPA's internal code will handle system menu events separately, and its
// behavior is not what we would want to see because it doesn't know our // behavior is not what we would want to see because it doesn't know our
// window doesn't have any window frame now, so return early here to avoid // window doesn't have any window frame now, so return early here to avoid
// entering Qt's own handling logic. // entering Qt's own handling logic.
return 0; // Return 0 means we have handled this event. return 0; // Return 0 means we have handled this event.
} }
g_utilsHelper()->mutex.lock(); Q_ASSERT(data.originalWindowProc);
const WNDPROC originalWindowProc = g_utilsHelper()->qtWindowProcs.value(hWnd); if (data.originalWindowProc) {
g_utilsHelper()->mutex.unlock();
Q_ASSERT(originalWindowProc);
if (originalWindowProc) {
// Hand over to Qt's original window proc function for events we are not // Hand over to Qt's original window proc function for events we are not
// interested in. // interested in.
return CallWindowProcW(originalWindowProc, hWnd, uMsg, wParam, lParam); return CallWindowProcW(data.originalWindowProc, hWnd, uMsg, wParam, lParam);
} else { } else {
return DefWindowProcW(hWnd, uMsg, wParam, lParam); return DefWindowProcW(hWnd, uMsg, wParam, lParam);
} }
@ -519,9 +523,10 @@ void Utils::showSystemMenu(const QWindow *window, const QPoint &pos)
} }
return true; return true;
}; };
const bool maxOrFull = (IsMaximized(hWnd) || isFullScreen(reinterpret_cast<WId>(hWnd)));
const bool fixedSize = isWindowFixedSize(window);
const auto options = qvariant_cast<Options>(window->property(kInternalOptionsFlag)); const auto options = qvariant_cast<Options>(window->property(kInternalOptionsFlag));
const bool maxOrFull = (IsMaximized(hWnd) ||
((options & Option::DontTreatFullScreenAsZoomed) ? false : isFullScreen(reinterpret_cast<WId>(hWnd))));
const bool fixedSize = isWindowFixedSize(window);
if (!setState(SC_RESTORE, (maxOrFull && !fixedSize), true)) { if (!setState(SC_RESTORE, (maxOrFull && !fixedSize), true)) {
return; return;
} }
@ -1027,7 +1032,7 @@ void Utils::installSystemMenuHook(const QWindow *window)
} }
const auto hwnd = reinterpret_cast<HWND>(window->winId()); const auto hwnd = reinterpret_cast<HWND>(window->winId());
QMutexLocker locker(&g_utilsHelper()->mutex); QMutexLocker locker(&g_utilsHelper()->mutex);
if (g_utilsHelper()->qtWindowProcs.contains(hwnd)) { if (g_utilsHelper()->data.contains(hwnd)) {
return; return;
} }
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
@ -1043,8 +1048,12 @@ void Utils::installSystemMenuHook(const QWindow *window)
return; return;
} }
//triggerFrameChange(winId); //triggerFrameChange(winId);
g_utilsHelper()->qtWindowProcs.insert(hwnd, originalWindowProc); Win32UtilsInternalData data = {};
g_utilsHelper()->windowMapping.insert(hwnd, const_cast<QWindow *>(window)); data.hwnd = hwnd;
data.window = const_cast<QWindow *>(window);
data.originalWindowProc = originalWindowProc;
data.options = qvariant_cast<Options>(window->property(kInternalOptionsFlag));
g_utilsHelper()->data.insert(hwnd, data);
} }
void Utils::uninstallSystemMenuHook(const WId winId) void Utils::uninstallSystemMenuHook(const WId winId)
@ -1055,22 +1064,21 @@ void Utils::uninstallSystemMenuHook(const WId winId)
} }
const auto hwnd = reinterpret_cast<HWND>(winId); const auto hwnd = reinterpret_cast<HWND>(winId);
QMutexLocker locker(&g_utilsHelper()->mutex); QMutexLocker locker(&g_utilsHelper()->mutex);
if (!g_utilsHelper()->qtWindowProcs.contains(hwnd)) { if (!g_utilsHelper()->data.contains(hwnd)) {
return; return;
} }
const WNDPROC originalWindowProc = g_utilsHelper()->qtWindowProcs.value(hwnd); const Win32UtilsInternalData data = g_utilsHelper()->data.value(hwnd);
Q_ASSERT(originalWindowProc); Q_ASSERT(data.originalWindowProc);
if (!originalWindowProc) { if (!data.originalWindowProc) {
return; return;
} }
SetLastError(ERROR_SUCCESS); SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(originalWindowProc)) == 0) { if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(data.originalWindowProc)) == 0) {
qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW"));
return; return;
} }
//triggerFrameChange(winId); //triggerFrameChange(winId);
g_utilsHelper()->qtWindowProcs.remove(hwnd); g_utilsHelper()->data.remove(hwnd);
g_utilsHelper()->windowMapping.remove(hwnd);
} }
void Utils::sendMouseReleaseEvent() void Utils::sendMouseReleaseEvent()

View File

@ -58,7 +58,7 @@ if(MSVC)
_WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO}
) )
target_compile_options(${SUB_PROJ_NAME} PRIVATE target_compile_options(${SUB_PROJ_NAME} PRIVATE
/W4 /WX /utf-8 /W4 /WX
) )
else() else()
target_compile_options(${SUB_PROJ_NAME} PRIVATE target_compile_options(${SUB_PROJ_NAME} PRIVATE

View File

@ -147,7 +147,7 @@ void FramelessQuickEventFilter::setTitleBarItem(QQuickWindow *window, QQuickItem
g_data()->data[window].titleBarItem = item; g_data()->data[window].titleBarItem = item;
} }
void FramelessQuickEventFilter::setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible) void FramelessQuickEventFilter::setHitTestVisible(QQuickWindow *window, QQuickItem *item)
{ {
Q_ASSERT(window); Q_ASSERT(window);
Q_ASSERT(item); Q_ASSERT(item);
@ -159,11 +159,12 @@ void FramelessQuickEventFilter::setHitTestVisible(QQuickWindow *window, QQuickIt
return; return;
} }
auto &items = g_data()->data[window].hitTestVisibleItems; auto &items = g_data()->data[window].hitTestVisibleItems;
static constexpr const bool visible = true;
const bool exists = items.contains(item); const bool exists = items.contains(item);
if (visible && !exists) { if (visible && !exists) {
items.append(item); items.append(item);
} }
if (!visible && exists) { if constexpr (!visible && exists) {
items.removeAll(item); items.removeAll(item);
} }
} }
@ -246,7 +247,7 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event)
return true; return true;
} }
case QEvent::MouseButtonDblClick: { case QEvent::MouseButtonDblClick: {
if ((options & Option::NoDoubleClickMaximizeToggle) || (options & Option::DisableResizing)) { if ((options & Option::NoDoubleClickMaximizeToggle) || Utils::isWindowFixedSize(window)) {
return false; return false;
} }
if (button != Qt::LeftButton) { if (button != Qt::LeftButton) {
@ -255,7 +256,8 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event)
if (!titleBar) { if (!titleBar) {
return false; return false;
} }
if ((visibility == QQuickWindow::Maximized) || (visibility == QQuickWindow::FullScreen)) { if ((visibility == QQuickWindow::Maximized) ||
((options & Option::DontTreatFullScreenAsZoomed) ? false : (visibility == QQuickWindow::FullScreen))) {
window->showNormal(); window->showNormal();
} else { } else {
window->showMaximized(); window->showMaximized();

View File

@ -63,14 +63,14 @@ void FramelessQuickHelper::setTitleBarItem(QQuickWindow *window, QQuickItem *ite
FramelessQuickEventFilter::setTitleBarItem(window, item); FramelessQuickEventFilter::setTitleBarItem(window, item);
} }
void FramelessQuickHelper::setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible) void FramelessQuickHelper::setHitTestVisible(QQuickWindow *window, QQuickItem *item)
{ {
Q_ASSERT(window); Q_ASSERT(window);
Q_ASSERT(item); Q_ASSERT(item);
if (!window || !item) { if (!window || !item) {
return; return;
} }
FramelessQuickEventFilter::setHitTestVisible(window, item, visible); FramelessQuickEventFilter::setHitTestVisible(window, item);
} }
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -23,27 +23,21 @@
*/ */
#include "framelessquickutils.h" #include "framelessquickutils.h"
#include <QtQuick/qquickwindow.h>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))
# include <QtGui/qpa/qplatformtheme.h> # include <QtGui/qpa/qplatformtheme.h>
# include <QtGui/private/qguiapplication_p.h> # include <QtGui/private/qguiapplication_p.h>
#endif #endif
#include <framelesswindowsmanager.h> #include <framelesswindowsmanager.h>
#include <utils.h> #include <utils.h>
#ifdef Q_OS_WINDOWS
# include <framelesshelper_windows.h>
#endif
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent) FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
{ {
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this](){ connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this](){
Q_EMIT frameBorderActiveColorChanged();
Q_EMIT frameBorderInactiveColorChanged();
Q_EMIT darkModeEnabledChanged(); Q_EMIT darkModeEnabledChanged();
Q_EMIT systemAccentColorChanged(); Q_EMIT systemAccentColorChanged();
Q_EMIT titleBarColorVisibleChanged(); Q_EMIT titleBarColorizedChanged();
}); });
} }
@ -51,7 +45,7 @@ FramelessQuickUtils::~FramelessQuickUtils() = default;
qreal FramelessQuickUtils::titleBarHeight() qreal FramelessQuickUtils::titleBarHeight()
{ {
return 30; return 30.0;
} }
bool FramelessQuickUtils::frameBorderVisible() bool FramelessQuickUtils::frameBorderVisible()
@ -65,25 +59,7 @@ bool FramelessQuickUtils::frameBorderVisible()
qreal FramelessQuickUtils::frameBorderThickness() qreal FramelessQuickUtils::frameBorderThickness()
{ {
return 1; return 1.0;
}
QColor FramelessQuickUtils::frameBorderActiveColor()
{
#ifdef Q_OS_WINDOWS
return Utils::getFrameBorderColor(true);
#else
return {};
#endif
}
QColor FramelessQuickUtils::frameBorderInactiveColor()
{
#ifdef Q_OS_WINDOWS
return Utils::getFrameBorderColor(false);
#else
return {};
#endif
} }
bool FramelessQuickUtils::darkModeEnabled() bool FramelessQuickUtils::darkModeEnabled()
@ -111,7 +87,7 @@ QColor FramelessQuickUtils::systemAccentColor()
#endif #endif
} }
bool FramelessQuickUtils::titleBarColorVisible() bool FramelessQuickUtils::titleBarColorized()
{ {
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
return Utils::isTitleBarColorized(); return Utils::isTitleBarColorized();
@ -140,81 +116,4 @@ QSizeF FramelessQuickUtils::defaultSystemButtonIconSize()
return kDefaultSystemButtonIconSize; return kDefaultSystemButtonIconSize;
} }
void FramelessQuickUtils::showMinimized2(QQuickWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
// Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711
// Don't use "SW_SHOWMINIMIZED" because it will activate the current window
// instead of the next window in the Z order, which is not the default behavior
// of native Win32 applications.
ShowWindow(reinterpret_cast<HWND>(window->winId()), SW_MINIMIZE);
#else
window->showMinimized();
#endif
}
void FramelessQuickUtils::toggleMaximize(QQuickWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
const QQuickWindow::Visibility visibility = window->visibility();
if ((visibility == QQuickWindow::Maximized) || (visibility == QQuickWindow::FullScreen)) {
window->showNormal();
} else {
window->showMaximized();
}
}
void FramelessQuickUtils::showSystemMenu(QQuickWindow *window, const QPoint &pos)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
const QPoint globalPos = window->mapToGlobal(pos);
# else
const QPoint globalPos = window->mapToGlobal(pos);
# endif
const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint();
Utils::showSystemMenu(window, nativePos);
#endif
}
void FramelessQuickUtils::startSystemMove2(QQuickWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemMove();
#else
Utils::startSystemMove(window);
#endif
}
void FramelessQuickUtils::startSystemResize2(QQuickWindow *window, const Qt::Edges edges)
{
Q_ASSERT(window);
if (!window) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemResize(edges);
#else
Utils::startSystemResize(window, edges);
#endif
}
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -29,16 +29,18 @@
#include <QtQuick/private/qquickanchors_p.h> #include <QtQuick/private/qquickanchors_p.h>
#include <framelesswindowsmanager.h> #include <framelesswindowsmanager.h>
#include <utils.h> #include <utils.h>
#include "framelessquickhelper.h"
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q) : QObject(q) FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Options options) : QObject(q)
{ {
Q_ASSERT(q); Q_ASSERT(q);
if (!q) { if (!q) {
return; return;
} }
q_ptr = q; q_ptr = q;
m_options = options;
initialize(); initialize();
} }
@ -48,45 +50,191 @@ bool FramelessQuickWindowPrivate::isZoomed() const
{ {
Q_Q(const FramelessQuickWindow); Q_Q(const FramelessQuickWindow);
const FramelessQuickWindow::Visibility visibility = q->visibility(); const FramelessQuickWindow::Visibility visibility = q->visibility();
return ((visibility == FramelessQuickWindow::Maximized) || (visibility == FramelessQuickWindow::FullScreen)); return ((visibility == FramelessQuickWindow::Maximized) ||
((m_options & Option::DontTreatFullScreenAsZoomed) ? false : (visibility == FramelessQuickWindow::FullScreen)));
}
QColor FramelessQuickWindowPrivate::getFrameBorderColor() const
{
#ifdef Q_OS_WINDOWS
Q_Q(const FramelessQuickWindow);
return Utils::getFrameBorderColor(q->isActive());
#else
return {};
#endif
}
void FramelessQuickWindowPrivate::setTitleBarItem(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_Q(FramelessQuickWindow);
FramelessQuickHelper::setTitleBarItem(q, item);
}
void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_Q(FramelessQuickWindow);
FramelessQuickHelper::setHitTestVisible(q, item);
}
void FramelessQuickWindowPrivate::showMinimized2()
{
Q_Q(FramelessQuickWindow);
#ifdef Q_OS_WINDOWS
// Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711
// Don't use "SW_SHOWMINIMIZED" because it will activate the current window
// instead of the next window in the Z order, which is not the default behavior
// of native Win32 applications.
ShowWindow(reinterpret_cast<HWND>(q->winId()), SW_MINIMIZE);
#else
q->showMinimized();
#endif
}
void FramelessQuickWindowPrivate::toggleMaximize()
{
Q_Q(FramelessQuickWindow);
if (Utils::isWindowFixedSize(q)) {
return;
}
if (isZoomed()) {
q->showNormal();
} else {
q->showMaximized();
}
}
void FramelessQuickWindowPrivate::toggleFullScreen()
{
Q_Q(FramelessQuickWindow);
if (Utils::isWindowFixedSize(q)) {
return;
}
const QWindow::Visibility visibility = q->visibility();
if (visibility == QWindow::FullScreen) {
q->setVisibility(m_savedVisibility);
} else {
m_savedVisibility = visibility;
q->showFullScreen();
}
}
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(q, nativePos);
#endif
}
void FramelessQuickWindowPrivate::startSystemMove2()
{
Q_Q(FramelessQuickWindow);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
q->startSystemMove();
#else
Utils::startSystemMove(q);
#endif
}
void FramelessQuickWindowPrivate::startSystemResize2(const Qt::Edges edges)
{
if (edges == Qt::Edges{}) {
return;
}
Q_Q(FramelessQuickWindow);
if (Utils::isWindowFixedSize(q)) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
q->startSystemResize(edges);
#else
Utils::startSystemResize(q, edges);
#endif
} }
void FramelessQuickWindowPrivate::initialize() void FramelessQuickWindowPrivate::initialize()
{ {
if (m_initialized) {
return;
}
m_initialized = true;
Q_Q(FramelessQuickWindow); Q_Q(FramelessQuickWindow);
FramelessWindowsManager * const manager = FramelessWindowsManager::instance(); q->setProperty(kInternalOptionsFlag, QVariant::fromValue(m_options));
manager->addWindow(q); FramelessQuickHelper::addWindow(q);
#ifdef Q_OS_WINDOWS
if (isFrameBorderVisible()) {
QQuickItem * const rootItem = q->contentItem(); QQuickItem * const rootItem = q->contentItem();
const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem); const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem);
m_topBorderRectangle.reset(new QQuickRectangle(rootItem)); m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
updateTopBorderHeight(); updateTopBorderHeight();
updateTopBorderColor(); updateTopBorderColor();
connect(q, &FramelessQuickWindow::visibilityChanged, this, &FramelessQuickWindowPrivate::updateTopBorderHeight); connect(q, &FramelessQuickWindow::visibilityChanged, this, [this, q](){
updateTopBorderHeight();
Q_EMIT q->zoomedChanged();
});
connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor); connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor);
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor); connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this, q](){
updateTopBorderColor();
Q_EMIT q->frameBorderColorChanged();
});
const auto topBorderAnchors = new QQuickAnchors(m_topBorderRectangle.data(), m_topBorderRectangle.data()); const auto topBorderAnchors = new QQuickAnchors(m_topBorderRectangle.data(), m_topBorderRectangle.data());
topBorderAnchors->setTop(rootItemPrivate->top()); topBorderAnchors->setTop(rootItemPrivate->top());
topBorderAnchors->setLeft(rootItemPrivate->left()); topBorderAnchors->setLeft(rootItemPrivate->left());
topBorderAnchors->setRight(rootItemPrivate->right()); topBorderAnchors->setRight(rootItemPrivate->right());
connect(q, &FramelessQuickWindow::visibilityChanged, q, &FramelessQuickWindow::zoomedChanged); }
#endif
Utils::moveWindowToDesktopCenter([q]() -> QScreen * { return q->screen(); },
[q]() -> QSize { return q->size(); },
[q](const int x, const int y) -> void {
q->setX(x);
q->setY(y);
}, true);
}
bool FramelessQuickWindowPrivate::isFrameBorderVisible() const
{
#ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWin11OrGreater());
#else
return false;
#endif
} }
void FramelessQuickWindowPrivate::updateTopBorderColor() void FramelessQuickWindowPrivate::updateTopBorderColor()
{ {
Q_Q(FramelessQuickWindow); if (!isFrameBorderVisible()) {
m_topBorderRectangle->setColor(Utils::getFrameBorderColor(q->isActive())); return;
}
m_topBorderRectangle->setColor(getFrameBorderColor());
} }
void FramelessQuickWindowPrivate::updateTopBorderHeight() void FramelessQuickWindowPrivate::updateTopBorderHeight()
{ {
if (!isFrameBorderVisible()) {
return;
}
Q_Q(FramelessQuickWindow); Q_Q(FramelessQuickWindow);
const qreal newHeight = ((q->visibility() == FramelessQuickWindow::Windowed) ? 1.0 : 0.0); const qreal newHeight = ((q->visibility() == FramelessQuickWindow::Windowed) ? 1.0 : 0.0);
m_topBorderRectangle->setHeight(newHeight); m_topBorderRectangle->setHeight(newHeight);
} }
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent) : QQuickWindow(parent) FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const Options options) : QQuickWindow(parent)
{ {
d_ptr.reset(new FramelessQuickWindowPrivate(this)); d_ptr.reset(new FramelessQuickWindowPrivate(this, options));
} }
FramelessQuickWindow::~FramelessQuickWindow() = default; FramelessQuickWindow::~FramelessQuickWindow() = default;
@ -97,4 +245,66 @@ bool FramelessQuickWindow::zoomed() const
return d->isZoomed(); return d->isZoomed();
} }
QColor FramelessQuickWindow::frameBorderColor() const
{
Q_D(const FramelessQuickWindow);
return d->getFrameBorderColor();
}
void FramelessQuickWindow::setTitleBarItem(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_D(FramelessQuickWindow);
d->setTitleBarItem(item);
}
void FramelessQuickWindow::setHitTestVisible(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_D(FramelessQuickWindow);
d->setHitTestVisible(item);
}
void FramelessQuickWindow::showMinimized2()
{
Q_D(FramelessQuickWindow);
d->showMinimized2();
}
void FramelessQuickWindow::toggleMaximize()
{
Q_D(FramelessQuickWindow);
d->toggleMaximize();
}
void FramelessQuickWindow::toggleFullScreen()
{
Q_D(FramelessQuickWindow);
d->toggleFullScreen();
}
void FramelessQuickWindow::showSystemMenu(const QPoint &pos)
{
Q_D(FramelessQuickWindow);
d->showSystemMenu(pos);
}
void FramelessQuickWindow::startSystemMove2()
{
Q_D(FramelessQuickWindow);
d->startSystemMove2();
}
void FramelessQuickWindow::startSystemResize2(const Qt::Edges edges)
{
Q_D(FramelessQuickWindow);
d->startSystemResize2(edges);
}
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -26,8 +26,10 @@
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#include <QtCore/qobject.h> #include <QtCore/qobject.h>
#include <QtGui/qwindow.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickRectangle; class QQuickRectangle;
QT_END_NAMESPACE QT_END_NAMESPACE
@ -42,10 +44,22 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindowPrivate : public QObject
Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate) Q_DISABLE_COPY_MOVE(FramelessQuickWindowPrivate)
public: public:
explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q); explicit FramelessQuickWindowPrivate(FramelessQuickWindow *q, const Options options);
~FramelessQuickWindowPrivate() override; ~FramelessQuickWindowPrivate() override;
Q_NODISCARD bool isZoomed() const; Q_INVOKABLE Q_NODISCARD bool isZoomed() const;
Q_INVOKABLE Q_NODISCARD QColor getFrameBorderColor() const;
Q_INVOKABLE Q_NODISCARD bool isFrameBorderVisible() const;
public Q_SLOTS:
void showMinimized2();
void toggleMaximize();
void toggleFullScreen();
void showSystemMenu(const QPoint &pos);
void startSystemMove2();
void startSystemResize2(const Qt::Edges edges);
void setTitleBarItem(QQuickItem *item);
void setHitTestVisible(QQuickItem *item);
private: private:
void initialize(); void initialize();
@ -56,7 +70,10 @@ private Q_SLOTS:
private: private:
FramelessQuickWindow *q_ptr = nullptr; FramelessQuickWindow *q_ptr = nullptr;
bool m_initialized = false;
QScopedPointer<QQuickRectangle> m_topBorderRectangle; QScopedPointer<QQuickRectangle> m_topBorderRectangle;
QWindow::Visibility m_savedVisibility = QWindow::Windowed;
Options m_options = {};
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -36,7 +36,7 @@ Button {
Image { Image {
anchors.centerIn: parent anchors.centerIn: parent
source: (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) source: (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorized)
? "image://framelesshelper/dark/close" : "image://framelesshelper/light/close" ? "image://framelesshelper/dark/close" : "image://framelesshelper/light/close"
} }
} }

View File

@ -39,9 +39,9 @@ Button {
Image { Image {
anchors.centerIn: parent anchors.centerIn: parent
source: button.maximized ? source: button.maximized ?
((FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) ((FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorized)
? "image://framelesshelper/dark/restore" : "image://framelesshelper/light/restore") : ? "image://framelesshelper/dark/restore" : "image://framelesshelper/light/restore") :
((FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) ((FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorized)
? "image://framelesshelper/dark/maximize" : "image://framelesshelper/light/maximize") ? "image://framelesshelper/dark/maximize" : "image://framelesshelper/light/maximize")
} }
} }

View File

@ -36,7 +36,7 @@ Button {
Image { Image {
anchors.centerIn: parent anchors.centerIn: parent
source: (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) source: (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorized)
? "image://framelesshelper/dark/minimize" : "image://framelesshelper/light/minimize" ? "image://framelesshelper/dark/minimize" : "image://framelesshelper/light/minimize"
} }
} }

View File

@ -36,7 +36,7 @@ Rectangle {
id: titleBar id: titleBar
height: FramelessUtils.titleBarHeight height: FramelessUtils.titleBarHeight
color: titleBar.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor color: titleBar.active ? (FramelessUtils.titleBarColorized ? FramelessUtils.systemAccentColor
: (FramelessUtils.darkModeEnabled ? "black" : "white")) : (FramelessUtils.darkModeEnabled ? "black" : "white"))
: (FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : "white") : (FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : "white")
@ -44,7 +44,7 @@ Rectangle {
id: windowTitleLabel id: windowTitleLabel
font.pointSize: 11 font.pointSize: 11
color: titleBar.active ? ((FramelessUtils.darkModeEnabled color: titleBar.active ? ((FramelessUtils.darkModeEnabled
|| FramelessUtils.titleBarColorVisible) ? "white" : "black") : "darkGray" || FramelessUtils.titleBarColorized) ? "white" : "black") : "darkGray"
anchors { anchors {
left: parent.left left: parent.left
leftMargin: 10 leftMargin: 10

View File

@ -41,7 +41,7 @@ target_compile_definitions(${SUB_PROJ_NAME} PRIVATE
if(MSVC) if(MSVC)
target_compile_options(${SUB_PROJ_NAME} PRIVATE target_compile_options(${SUB_PROJ_NAME} PRIVATE
/W4 /WX /utf-8 /W4 /WX
) )
else() else()
target_compile_options(${SUB_PROJ_NAME} PRIVATE target_compile_options(${SUB_PROJ_NAME} PRIVATE

View File

@ -54,9 +54,9 @@ QWidget *FramelessMainWindow::titleBarWidget() const
return m_helper->titleBarWidget(); return m_helper->titleBarWidget();
} }
void FramelessMainWindow::setHitTestVisible(QWidget *widget, const bool visible) void FramelessMainWindow::setHitTestVisible(QWidget *widget)
{ {
m_helper->setHitTestVisible(widget, visible); m_helper->setHitTestVisible(widget);
} }
void FramelessMainWindow::toggleMaximized() void FramelessMainWindow::toggleMaximized()
@ -64,6 +64,11 @@ void FramelessMainWindow::toggleMaximized()
m_helper->toggleMaximized(); m_helper->toggleMaximized();
} }
void FramelessMainWindow::toggleFullScreen()
{
m_helper->toggleFullScreen();
}
void FramelessMainWindow::changeEvent(QEvent *event) void FramelessMainWindow::changeEvent(QEvent *event)
{ {
QMainWindow::changeEvent(event); QMainWindow::changeEvent(event);

View File

@ -64,9 +64,9 @@ QWidget *FramelessWidget::contentWidget() const
return m_helper->contentWidget(); return m_helper->contentWidget();
} }
void FramelessWidget::setHitTestVisible(QWidget *widget, const bool visible) void FramelessWidget::setHitTestVisible(QWidget *widget)
{ {
m_helper->setHitTestVisible(widget, visible); m_helper->setHitTestVisible(widget);
} }
void FramelessWidget::toggleMaximized() void FramelessWidget::toggleMaximized()
@ -74,6 +74,11 @@ void FramelessWidget::toggleMaximized()
m_helper->toggleMaximized(); m_helper->toggleMaximized();
} }
void FramelessWidget::toggleFullScreen()
{
m_helper->toggleFullScreen();
}
void FramelessWidget::changeEvent(QEvent *event) void FramelessWidget::changeEvent(QEvent *event)
{ {
QWidget::changeEvent(event); QWidget::changeEvent(event);

View File

@ -73,7 +73,7 @@ bool FramelessWidgetsHelper::isNormal() const
bool FramelessWidgetsHelper::isZoomed() const bool FramelessWidgetsHelper::isZoomed() const
{ {
return (q->isMaximized() || q->isFullScreen()); return (q->isMaximized() || ((m_options & Option::DontTreatFullScreenAsZoomed) ? false : q->isFullScreen()));
} }
void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget) void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget)
@ -133,17 +133,18 @@ QWidget *FramelessWidgetsHelper::contentWidget() const
return m_userContentWidget; return m_userContentWidget;
} }
void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget, const bool visible) void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget)
{ {
Q_ASSERT(widget); Q_ASSERT(widget);
if (!widget) { if (!widget) {
return; return;
} }
static constexpr const bool visible = true;
const bool exists = m_hitTestVisibleWidgets.contains(widget); const bool exists = m_hitTestVisibleWidgets.contains(widget);
if (visible && !exists) { if (visible && !exists) {
m_hitTestVisibleWidgets.append(widget); m_hitTestVisibleWidgets.append(widget);
} }
if (!visible && exists) { if constexpr (!visible && exists) {
m_hitTestVisibleWidgets.removeAll(widget); m_hitTestVisibleWidgets.removeAll(widget);
} }
} }
@ -218,7 +219,7 @@ void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event)
if (!isInTitleBarDraggableArea(scenePos)) { if (!isInTitleBarDraggableArea(scenePos)) {
return; return;
} }
Utils::startSystemMove(q->windowHandle()); Utils::startSystemMove(m_window);
} }
void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
@ -251,7 +252,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event)
const QPoint globalPos = event->globalPos(); const QPoint globalPos = event->globalPos();
# endif # endif
const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint(); const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint();
Utils::showSystemMenu(q->windowHandle(), nativePos); Utils::showSystemMenu(m_window, nativePos);
#endif #endif
} }
@ -261,7 +262,7 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event)
if (!event) { if (!event) {
return; return;
} }
if ((m_options & Option::NoDoubleClickMaximizeToggle) || (m_options & Option::DisableResizing)) { if ((m_options & Option::NoDoubleClickMaximizeToggle) || Utils::isWindowFixedSize(m_window)) {
return; return;
} }
if (event->button() != Qt::LeftButton) { if (event->button() != Qt::LeftButton) {
@ -295,12 +296,12 @@ void FramelessWidgetsHelper::initialize()
// Force the widget become a native window now so that we can deal with its // Force the widget become a native window now so that we can deal with its
// win32 events as soon as possible. // win32 events as soon as possible.
q->setAttribute(Qt::WA_NativeWindow); q->setAttribute(Qt::WA_NativeWindow);
QWindow * const window = q->windowHandle(); m_window = q->windowHandle();
Q_ASSERT(window); Q_ASSERT(m_window);
if (!window) { if (!m_window) {
return; return;
} }
window->setProperty(kInternalOptionsFlag, QVariant::fromValue(m_options)); m_window->setProperty(kInternalOptionsFlag, QVariant::fromValue(m_options));
if (m_options & Option::UseStandardWindowLayout) { if (m_options & Option::UseStandardWindowLayout) {
if (q->inherits(QT_MAINWINDOW_CLASS_NAME)) { if (q->inherits(QT_MAINWINDOW_CLASS_NAME)) {
m_options &= ~Options(Option::UseStandardWindowLayout); m_options &= ~Options(Option::UseStandardWindowLayout);
@ -315,7 +316,7 @@ void FramelessWidgetsHelper::initialize()
true); true);
} }
FramelessWindowsManager *manager = FramelessWindowsManager::instance(); FramelessWindowsManager *manager = FramelessWindowsManager::instance();
manager->addWindow(window); manager->addWindow(m_window);
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){ connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){
if (m_options & Option::UseStandardWindowLayout) { if (m_options & Option::UseStandardWindowLayout) {
updateSystemTitleBarStyleSheet(); updateSystemTitleBarStyleSheet();
@ -327,12 +328,11 @@ void FramelessWidgetsHelper::initialize()
setupInitialUi(); setupInitialUi();
if (!(m_options & Option::DontMoveWindowToDesktopCenter)) { if (!(m_options & Option::DontMoveWindowToDesktopCenter)) {
Utils::moveWindowToDesktopCenter( Utils::moveWindowToDesktopCenter(
[this, window]() -> QScreen * { [this]() -> QScreen * {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
Q_UNUSED(window);
return q->screen(); return q->screen();
#else #else
return window->screen(); return m_window->screen();
#endif #endif
}, },
[this]() -> QSize { return q->size(); }, [this]() -> QSize { return q->size(); },
@ -525,7 +525,7 @@ void FramelessWidgetsHelper::updateSystemButtonsIcon()
void FramelessWidgetsHelper::toggleMaximized() void FramelessWidgetsHelper::toggleMaximized()
{ {
if (m_options & Option::DisableResizing) { if (Utils::isWindowFixedSize(m_window)) {
return; return;
} }
if (isZoomed()) { if (isZoomed()) {
@ -535,4 +535,18 @@ void FramelessWidgetsHelper::toggleMaximized()
} }
} }
void FramelessWidgetsHelper::toggleFullScreen()
{
if (Utils::isWindowFixedSize(m_window)) {
return;
}
const Qt::WindowStates windowState = q->windowState();
if (windowState & Qt::WindowFullScreen) {
q->setWindowState(m_savedWindowState);
} else {
m_savedWindowState = windowState;
q->showFullScreen();
}
}
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE