Refactor the Quick implementation, interface is now simpler

Now we use attached properties instead, it make things simpler.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-05-02 12:26:57 +08:00
parent d6fa6a163e
commit 306ebfc717
30 changed files with 1223 additions and 830 deletions

View File

@ -6,8 +6,9 @@
- Quick: Restored some 1.x interfaces which may be convenient for Qt Quick users.
- Examples: Added QtWebEngine based demo projects for both Qt Widgets and Qt Quick.
- Common: Added cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme.
- Common: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed.
- Common: Bug fix and internal refactoring, improved stability on all supported platforms.
- Misc: Removed bundled Qt internal classes that are licensed under Commercial/GPL/LGPL. This library is now pure MIT licensed.
- Misc: Migrate to categorized logging output.
- Misc: Bug fixes and internal refactorings, improved stability on all supported platforms.
## Highlights compared to 1.x

View File

@ -25,6 +25,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="org.wangwenx190.framelesshelper.demo" version="1.0.0.0"/>
<description>FramelessHelper Demo</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
@ -39,6 +40,8 @@
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 Version 1809 (October 2018 Update) -->
<maxversiontested Id="10.0.17763.0"/>
<!-- Windows Vista and Windows Server 2008 -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 and Windows Server 2008 R2 -->
@ -55,6 +58,11 @@
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM, True</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor, System</dpiAwareness>
<printerDriverIsolation xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">True</printerDriverIsolation>
<disableWindowFiltering xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">True</disableWindowFiltering>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">True</longPathAware>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
</windowsSettings>
</application>
</assembly>

View File

@ -28,6 +28,8 @@ set(SOURCES
mainwindow.h
mainwindow.cpp
main.cpp
systembutton.h
systembutton.cpp
)
if(WIN32)

View File

@ -151,7 +151,7 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="minimizeButton">
<widget class="SystemButton" name="minimizeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -188,7 +188,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="maximizeButton">
<widget class="SystemButton" name="maximizeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -225,7 +225,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<widget class="SystemButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -264,6 +264,13 @@
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>SystemButton</class>
<extends>QPushButton</extends>
<header>systembutton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,43 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "systembutton.h"
SystemButton::SystemButton(QWidget *parent) : QPushButton(parent)
{
}
SystemButton::~SystemButton() = default;
void SystemButton::setHovered(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}
void SystemButton::setPressed(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}

View File

@ -0,0 +1,40 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QtWidgets/qpushbutton.h>
class SystemButton : public QPushButton
{
Q_OBJECT
public:
explicit SystemButton(QWidget *parent = nullptr);
~SystemButton() override;
public Q_SLOTS:
void setHovered(const bool value);
void setPressed(const bool value);
};

View File

@ -39,6 +39,8 @@ set(SOURCES
mainwindow.h
mainwindow.cpp
main.cpp
systembutton.h
systembutton.cpp
)
if(WIN32)

View File

@ -24,9 +24,9 @@
#include "mainwindow.h"
#include "glwidget.h"
#include "systembutton.h"
#include <QtWidgets/qboxlayout.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qpushbutton.h>
FRAMELESSHELPER_USE_NAMESPACE
@ -51,16 +51,16 @@ void MainWindow::setupUi()
f.setPointSize(kDefaultTitleBarFontPointSize);
m_titleLabel->setFont(f);
connect(this, &MainWindow::windowTitleChanged, m_titleLabel, &QLabel::setText);
m_minBtn = new QPushButton(this);
m_minBtn = new SystemButton(this);
m_minBtn->setText(tr("MINIMIZE"));
connect(m_minBtn, &QPushButton::clicked, this, &MainWindow::showMinimized);
m_maxBtn = new QPushButton(this);
connect(m_minBtn, &SystemButton::clicked, this, &MainWindow::showMinimized);
m_maxBtn = new SystemButton(this);
updateMaximizeButton();
connect(m_maxBtn, &QPushButton::clicked, this, &MainWindow::toggleMaximized);
connect(m_maxBtn, &SystemButton::clicked, this, &MainWindow::toggleMaximized);
connect(this, &MainWindow::zoomedChanged, this, &MainWindow::updateMaximizeButton);
m_closeBtn = new QPushButton(this);
m_closeBtn = new SystemButton(this);
m_closeBtn->setText(tr("CLOSE"));
connect(m_closeBtn, &QPushButton::clicked, this, &MainWindow::close);
connect(m_closeBtn, &SystemButton::clicked, this, &MainWindow::close);
m_titleBarWidget = new QWidget(this);
m_titleBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_titleBarWidget->setFixedHeight(kDefaultTitleBarHeight);

View File

@ -28,10 +28,10 @@
QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
QT_END_NAMESPACE
class GLWidget;
class SystemButton;
class MainWindow : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)
{
@ -50,9 +50,9 @@ private:
private:
QLabel *m_titleLabel = nullptr;
QPushButton *m_minBtn = nullptr;
QPushButton *m_maxBtn = nullptr;
QPushButton *m_closeBtn = nullptr;
SystemButton *m_minBtn = nullptr;
SystemButton *m_maxBtn = nullptr;
SystemButton *m_closeBtn = nullptr;
QWidget *m_titleBarWidget = nullptr;
GLWidget *m_glWidget = nullptr;
};

View File

@ -0,0 +1,43 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "systembutton.h"
SystemButton::SystemButton(QWidget *parent) : QPushButton(parent)
{
}
SystemButton::~SystemButton() = default;
void SystemButton::setHovered(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}
void SystemButton::setPressed(const bool value)
{
Q_UNUSED(value);
Q_UNIMPLEMENTED();
}

View File

@ -0,0 +1,40 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QtWidgets/qpushbutton.h>
class SystemButton : public QPushButton
{
Q_OBJECT
public:
explicit SystemButton(QWidget *parent = nullptr);
~SystemButton() override;
public Q_SLOTS:
void setHovered(const bool value);
void setPressed(const bool value);
};

View File

@ -29,11 +29,12 @@ import org.wangwenx190.FramelessHelper 1.0
FramelessWindow {
id: window
visible: true
visible: true // Default is false, so won't be visible unless explicitly set to true.
width: 800
height: 600
title: qsTr("Hello, World! - Qt Quick")
color: FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor
title: qsTr("FramelessHelper demo application - Qt Quick")
color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark)
? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor
Timer {
interval: 500
@ -49,7 +50,7 @@ FramelessWindow {
pointSize: 70
bold: true
}
color: FramelessUtils.darkModeEnabled ? "white" : "black"
color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? "white" : "black"
}
StandardTitleBar {
@ -75,15 +76,15 @@ FramelessWindow {
}
Component.onCompleted: {
// Make our homemade title bar snap to the window top frame border.
window.snapToTopBorder(titleBar, FramelessHelper.Top, FramelessHelper.Bottom);
window.snapToTopBorder(titleBar, FramelessHelperConstants.Top, FramelessHelperConstants.Bottom);
// Make our homemade title bar draggable, and open the system menu
// when the user right clicks on the title bar area.
window.titleBarItem = titleBar;
FramelessHelper.titleBarItem = titleBar;
// Make our own items visible to the hit test and on Windows, enable
// the snap layout feature (available since Windows 11).
window.setSystemButton(minimizeButton, FramelessHelper.Minimize);
window.setSystemButton(maximizeButton, FramelessHelper.Maximize);
window.setSystemButton(closeButton, FramelessHelper.Close);
FramelessHelper.setSystemButton(minimizeButton, FramelessHelperConstants.Minimize);
FramelessHelper.setSystemButton(maximizeButton, FramelessHelperConstants.Maximize);
FramelessHelper.setSystemButton(closeButton, FramelessHelperConstants.Close);
}
}
}

View File

@ -26,7 +26,7 @@
#include <QtQml/qqmlapplicationengine.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuickControls2/qquickstyle.h>
#include <framelessquickhelper.h>
#include <framelessquickmodule.h>
FRAMELESSHELPER_USE_NAMESPACE

View File

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

View File

@ -25,33 +25,55 @@
#pragma once
#include "framelesshelperquick_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
# include <QtQml/qqmlextensionplugin.h>
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QT_BEGIN_NAMESPACE
class QQmlEngine;
QT_END_NAMESPACE
#include <QtQuick/qquickitem.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
namespace FramelessHelper::Quick
{
FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine);
} // namespace FramelessHelper::Quick
class FramelessQuickHelperPrivate;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
class FRAMELESSHELPER_QUICK_API FramelessHelperQuickPlugin : public QQmlEngineExtensionPlugin
class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessHelper)
#endif
Q_DECLARE_PRIVATE(FramelessQuickHelper)
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL)
public:
explicit FramelessHelperQuickPlugin(QObject *parent = nullptr);
~FramelessHelperQuickPlugin() override;
explicit FramelessQuickHelper(QQuickItem *parent = nullptr, const Global::UserSettings &settings = {});
~FramelessQuickHelper() override;
void initializeEngine(QQmlEngine *engine, const char *uri) override;
Q_NODISCARD static FramelessQuickHelper *qmlAttachedProperties(QObject *parentObject);
Q_NODISCARD QQuickItem *titleBarItem() const;
Q_NODISCARD bool isWindowFixedSize() const;
public Q_SLOTS:
void setTitleBarItem(QQuickItem *value);
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
void setHitTestVisible(QQuickItem *item);
void showSystemMenu(const QPoint &pos);
void windowStartSystemMove2(const QPoint &pos);
void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos);
void moveWindowToDesktopCenter();
void bringWindowToFront();
void setWindowFixedSize(const bool value, const bool force = false);
protected:
void itemChange(const ItemChange change, const ItemChangeData &data) override;
Q_SIGNALS:
void titleBarItemChanged();
private:
QScopedPointer<FramelessQuickHelperPrivate> d_ptr;
};
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
FRAMELESSHELPER_END_NAMESPACE
QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper))
QML_DECLARE_TYPEINFO(FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessQuickHelper), QML_HAS_ATTACHED_PROPERTIES)

View File

@ -0,0 +1,57 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelperquick_global.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
# include <QtQml/qqmlextensionplugin.h>
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QT_BEGIN_NAMESPACE
class QQmlEngine;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
namespace FramelessHelper::Quick
{
FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine);
} // namespace FramelessHelper::Quick
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
class FRAMELESSHELPER_QUICK_API FramelessHelperQuickPlugin : public QQmlEngineExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
public:
explicit FramelessHelperQuickPlugin(QObject *parent = nullptr);
~FramelessHelperQuickPlugin() override;
void initializeEngine(QQmlEngine *engine, const char *uri) override;
};
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
FRAMELESSHELPER_END_NAMESPACE

View File

@ -31,7 +31,6 @@
QT_BEGIN_NAMESPACE
class QQuickWindow;
class QQuickItem;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -49,7 +48,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL)
Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL)
Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL)
Q_PROPERTY(QuickGlobal::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL)
Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL)
Q_PROPERTY(bool titleBarColorized READ titleBarColorized NOTIFY titleBarColorizedChanged FINAL)
Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL)
@ -63,24 +62,24 @@ public:
explicit FramelessQuickUtils(QObject *parent = nullptr);
~FramelessQuickUtils() override;
Q_NODISCARD static qreal titleBarHeight();
Q_NODISCARD static bool frameBorderVisible();
Q_NODISCARD static qreal frameBorderThickness();
Q_NODISCARD static bool darkModeEnabled();
Q_NODISCARD static QColor systemAccentColor();
Q_NODISCARD static bool titleBarColorized();
Q_NODISCARD static QColor defaultSystemLightColor();
Q_NODISCARD static QColor defaultSystemDarkColor();
Q_NODISCARD static QSizeF defaultSystemButtonSize();
Q_NODISCARD static QSizeF defaultSystemButtonIconSize();
Q_NODISCARD static QColor defaultSystemButtonBackgroundColor();
Q_NODISCARD static QColor defaultSystemCloseButtonBackgroundColor();
Q_NODISCARD qreal titleBarHeight() const;
Q_NODISCARD bool frameBorderVisible() const;
Q_NODISCARD qreal frameBorderThickness() const;
Q_NODISCARD QuickGlobal::SystemTheme systemTheme() const;
Q_NODISCARD QColor systemAccentColor() const;
Q_NODISCARD bool titleBarColorized() const;
Q_NODISCARD QColor defaultSystemLightColor() const;
Q_NODISCARD QColor defaultSystemDarkColor() const;
Q_NODISCARD QSizeF defaultSystemButtonSize() const;
Q_NODISCARD QSizeF defaultSystemButtonIconSize() const;
Q_NODISCARD QColor defaultSystemButtonBackgroundColor() const;
Q_NODISCARD QColor defaultSystemCloseButtonBackgroundColor() const;
Q_NODISCARD Q_INVOKABLE static QColor getSystemButtonBackgroundColor(
Q_NODISCARD Q_INVOKABLE QColor getSystemButtonBackgroundColor(
const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state);
Q_SIGNALS:
void darkModeEnabledChanged();
void systemThemeChanged();
void systemAccentColorChanged();
void titleBarColorizedChanged();
};

View File

@ -25,7 +25,6 @@
#pragma once
#include "framelesshelperquick_global.h"
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickwindow.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
@ -45,10 +44,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow
Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL)
Q_PROPERTY(bool zoomed READ isZoomed NOTIFY zoomedChanged FINAL)
Q_PROPERTY(bool fullScreen READ isFullScreen NOTIFY fullScreenChanged FINAL)
Q_PROPERTY(bool fixedSize READ isFixedSize WRITE setFixedSize NOTIFY fixedSizeChanged FINAL)
Q_PROPERTY(QColor frameBorderColor READ frameBorderColor NOTIFY frameBorderColorChanged FINAL)
Q_PROPERTY(QuickGlobal::Options options READ options WRITE setOptions NOTIFY optionsChanged FINAL)
Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL)
public:
explicit FramelessQuickWindow(QWindow *parent = nullptr, const Global::UserSettings &settings = {});
@ -59,30 +55,13 @@ public:
Q_NODISCARD bool isMinimized() const;
Q_NODISCARD bool isZoomed() const;
Q_NODISCARD bool isFullScreen() const;
Q_NODISCARD bool isFixedSize() const;
void setFixedSize(const bool value);
Q_NODISCARD QColor frameBorderColor() const;
Q_NODISCARD QuickGlobal::Options options() const;
void setOptions(const QuickGlobal::Options value);
Q_NODISCARD QQuickItem *titleBarItem() const;
void setTitleBarItem(QQuickItem *item);
public Q_SLOTS:
void showMinimized2();
void toggleMaximized();
void toggleFullScreen();
void showSystemMenu(const QPoint &pos);
void startSystemMove2(const QPoint &pos);
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
void setHitTestVisible(QQuickItem *item);
void moveToDesktopCenter();
void bringToFront();
void snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor);
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
Q_SIGNALS:
void hiddenChanged();
@ -90,11 +69,7 @@ Q_SIGNALS:
void minimizedChanged();
void zoomedChanged();
void fullScreenChanged();
void fixedSizeChanged();
void frameBorderColorChanged();
void systemButtonStateChanged(const QuickGlobal::SystemButtonType, const QuickGlobal::ButtonState);
void optionsChanged();
void titleBarItemChanged();
private:
QScopedPointer<FramelessQuickWindowPrivate> d_ptr;

View File

@ -72,7 +72,6 @@ Q_SIGNALS:
void fixedSizeChanged();
void titleBarWidgetChanged();
void systemThemeChanged();
void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState);
private:
QScopedPointer<FramelessWidgetsHelper> d_ptr;

View File

@ -77,7 +77,6 @@ Q_SIGNALS:
void titleBarWidgetChanged();
void contentWidgetChanged();
void systemThemeChanged();
void systemButtonStateChanged(const Global::SystemButtonType, const Global::ButtonState);
private:
QScopedPointer<FramelessWidgetsHelper> d_ptr;

View File

@ -28,6 +28,7 @@
#include <QtCore/qmutex.h>
#include <QtCore/qvariant.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/quuid.h>
#include <QtGui/qwindow.h>
#include "framelesswindowsmanager.h"
#include "framelesswindowsmanager_p.h"
@ -58,7 +59,6 @@ Q_GLOBAL_STATIC(Win32Helper, g_win32Helper)
FRAMELESSHELPER_BYTEARRAY_CONSTANT2(Win32MessageTypeName, "windows_generic_MSG")
static const QString qThemeSettingChangeEventName = QString::fromWCharArray(kThemeSettingChangeEventName);
static constexpr const wchar_t kDragBarWindowClassName[] = L"FRAMELESSHELPER@DRAG_BAR_WINDOW_CLASS";
FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow)
FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW)
FRAMELESSHELPER_STRING_CONSTANT(ScreenToClient)
@ -140,6 +140,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
if (data.params.isInsideSystemButtons(qtScenePos, &buttonType)) {
switch (buttonType) {
case SystemButtonType::Unknown:
Q_ASSERT(false);
break;
case SystemButtonType::WindowIcon:
return HTSYSMENU;
@ -156,7 +157,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
}
// The parent window has quite some logic in the hit test handler, we
// should forward this message to the parent window and return what it
// returns to make sure our homemade titlebar is still functional.
// returns to make sure our homemade title bar is still functional.
return SendMessageW(parentWindowHandle, WM_NCHITTEST, 0, lParam);
}
case WM_NCMOUSEMOVE: {
@ -197,7 +198,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
}
// If we haven't previously asked for mouse tracking, request mouse
// tracking. We need to do this so we can get the WM_NCMOUSELEAVE
// message when the mouse leave the titlebar. Otherwise, we won't always
// message when the mouse leave the title bar. Otherwise, we won't always
// get that message (especially if the user moves the mouse _real
// fast_).
if (!data.trackingMouse && ((wParam == HTSYSMENU) || (wParam == HTHELP)
@ -233,7 +234,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONDBLCLK: {
// Manual handling for mouse clicks in the drag bar. If it's in a
// caption button, then tell the titlebar to "press" the button, which
// caption button, then tell the title bar to "press" the button, which
// should change its visual state.
//
// If it's not in a caption button, then just forward the message along
@ -309,7 +310,7 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
}
// Forward all the mouse events we don't handled here to the parent window,
// this is a necessary step to make sure the child widgets/quick items can still
// receive mouse events from our homemade titlebar.
// receive mouse events from our homemade title bar.
if (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) ||
((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))) {
SendMessageW(parentWindowHandle, uMsg, wParam, lParam);
@ -332,6 +333,12 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
}
const int titleBarHeight = Utils::getTitleBarHeight(parentWindowId, true);
const auto dragBarWindowHandle = reinterpret_cast<HWND>(dragBarWindowId);
// As you can see from the code, we only use the drag bar window to activate the
// snap layout feature introduced in Windows 11. So you may wonder, why not just
// limit it to the rectangle of the three system buttons, instead of covering the
// whole title bar area? Well, I've tried that solution already and unfortunately
// it doesn't work. Since our current solution works well, I have no plan to dig
// into all the magic behind it.
if (SetWindowPos(dragBarWindowHandle, HWND_TOP, 0, 0, parentWindowClientRect.right,
titleBarHeight, (SWP_NOACTIVATE | SWP_SHOWWINDOW)) == FALSE) {
qWarning() << Utils::getSystemErrorMessage(kSetWindowPos);
@ -358,12 +365,18 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
qWarning() << Utils::getSystemErrorMessage(kGetModuleHandleW);
return false;
}
static const QString dragBarWindowClassName = QUuid::createUuid().toString();
Q_ASSERT(!dragBarWindowClassName.isEmpty());
if (dragBarWindowClassName.isEmpty()) {
qWarning() << "Failed to generate a new UUID.";
return false;
}
static const ATOM dragBarWindowClass = [instance]() -> ATOM {
WNDCLASSEXW wcex;
SecureZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS);
wcex.lpszClassName = kDragBarWindowClassName;
wcex.lpszClassName = qUtf16Printable(dragBarWindowClassName);
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wcex.lpfnWndProc = DragBarWindowProc;
@ -376,7 +389,8 @@ FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent)
return false;
}
const HWND dragBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP),
kDragBarWindowClassName, nullptr, WS_CHILD, 0, 0, 0, 0, parentWindowHandle, nullptr, instance, nullptr);
qUtf16Printable(dragBarWindowClassName), nullptr, WS_CHILD, 0, 0, 0, 0,
parentWindowHandle, nullptr, instance, nullptr);
Q_ASSERT(dragBarWindowHandle);
if (!dragBarWindowHandle) {
qWarning() << Utils::getSystemErrorMessage(kCreateWindowExW);

View File

@ -22,8 +22,8 @@
SOFTWARE.
]]
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Qml QuickTemplates2 QuickControls2)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Qml QuickTemplates2 QuickControls2)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS QuickTemplates2 QuickControls2)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS QuickTemplates2 QuickControls2)
set(SUB_PROJ_NAME FramelessHelperQuick)
@ -32,9 +32,10 @@ set(INCLUDE_PREFIX ../../include/FramelessHelper/Quick)
set(SOURCES
framelesshelperquick.qrc
${INCLUDE_PREFIX}/framelesshelperquick_global.h
${INCLUDE_PREFIX}/framelessquickutils.h
${INCLUDE_PREFIX}/framelessquickhelper.h
${INCLUDE_PREFIX}/framelessquickmodule.h
${INCLUDE_PREFIX}/framelessquickwindow.h
${INCLUDE_PREFIX}/framelessquickhelper.h
${INCLUDE_PREFIX}/framelessquickutils.h
quickstandardminimizebutton_p.h
quickstandardminimizebutton.cpp
quickstandardmaximizebutton_p.h
@ -43,10 +44,12 @@ set(SOURCES
quickstandardclosebutton.cpp
quickstandardtitlebar_p.h
quickstandardtitlebar.cpp
framelessquickhelper_p.h
framelessquickwindow_p.h
framelessquickhelper.cpp
framelessquickutils.cpp
framelessquickmodule.cpp
framelessquickwindow.cpp
framelessquickhelper.cpp
)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
@ -91,7 +94,7 @@ if(MSVC)
_WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO}
)
target_compile_options(${SUB_PROJ_NAME} PRIVATE
/utf-8 /W4 /WX
/utf-8 /W3 /WX # Can't use /W4 here due to the Q_D macro causes warnings.
)
else()
target_compile_options(${SUB_PROJ_NAME} PRIVATE
@ -100,7 +103,6 @@ else()
endif()
target_link_libraries(${SUB_PROJ_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::QmlPrivate
Qt${QT_VERSION_MAJOR}::QuickPrivate
Qt${QT_VERSION_MAJOR}::QuickTemplates2Private
Qt${QT_VERSION_MAJOR}::QuickControls2Private

View File

@ -23,97 +23,641 @@
*/
#include "framelessquickhelper.h"
#include "framelessquickutils.h"
#include "framelessquickwindow.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include "quickstandardminimizebutton_p.h"
# include "quickstandardmaximizebutton_p.h"
# include "quickstandardclosebutton_p.h"
# include "quickstandardtitlebar_p.h"
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#ifndef QUICK_URI_SHORT
# define QUICK_URI_SHORT FRAMELESSHELPER_QUICK_URI, 1
#endif
#ifndef QUICK_URI_FULL
# define QUICK_URI_FULL QUICK_URI_SHORT, 0
#endif
#ifndef QUICK_URI_EXPAND
# define QUICK_URI_EXPAND(name) QUICK_URI_FULL, name
#endif
#include "framelessquickhelper_p.h"
#include <QtCore/qmutex.h>
#include <QtGui/qpa/qplatformwindow.h> // For QWINDOWSIZE_MAX
#include <QtQuick/qquickwindow.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <framelesswindowsmanager.h>
#include <utils.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
using namespace Global;
struct QuickHelperData
{
Q_ASSERT(engine);
if (!engine) {
bool attached = false;
UserSettings settings = {};
SystemParameters params = {};
QPointer<QQuickItem> titleBarItem = nullptr;
QList<QQuickItem *> hitTestVisibleItems = {};
};
struct QuickHelper
{
QMutex mutex;
QHash<WId, QuickHelperData> data = {};
};
Q_GLOBAL_STATIC(QuickHelper, g_quickHelper)
static constexpr const char QTQUICK_ITEM_CLASS_NAME[] = "QQuickItem";
static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton";
FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q, const UserSettings &settings) : QObject(q)
{
Q_ASSERT(q);
if (!q) {
return;
}
static bool inited = false;
if (inited) {
return;
}
inited = true;
qRegisterMetaType<QuickGlobal::Options>();
qRegisterMetaType<QuickGlobal::SystemTheme>();
qRegisterMetaType<QuickGlobal::SystemButtonType>();
qRegisterMetaType<QuickGlobal::ResourceType>();
qRegisterMetaType<QuickGlobal::DwmColorizationArea>();
qRegisterMetaType<QuickGlobal::Anchor>();
qRegisterMetaType<QuickGlobal::ButtonState>();
qmlRegisterUncreatableType<QuickGlobal>(QUICK_URI_FULL, "FramelessHelper",
FRAMELESSHELPER_STRING_LITERAL("The FramelessHelper namespace is not creatable, you can only use it to access it's enums."));
qmlRegisterSingletonType<FramelessQuickUtils>(QUICK_URI_EXPAND("FramelessUtils"),
[](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
return new FramelessQuickUtils;
});
qmlRegisterRevision<QWindow, 254>(QUICK_URI_FULL);
qmlRegisterRevision<QQuickWindow, 254>(QUICK_URI_FULL);
qmlRegisterRevision<QQuickItem, 254>(QUICK_URI_FULL);
qmlRegisterType<FramelessQuickWindow>(QUICK_URI_EXPAND("FramelessWindow"));
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterType<QuickStandardMinimizeButton>(QUICK_URI_EXPAND("StandardMinimizeButton"));
qmlRegisterType<QuickStandardMaximizeButton>(QUICK_URI_EXPAND("StandardMaximizeButton"));
qmlRegisterType<QuickStandardCloseButton>(QUICK_URI_EXPAND("StandardCloseButton"));
qmlRegisterType<QuickStandardTitleBar>(QUICK_URI_EXPAND("StandardTitleBar"));
#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMinimizeButton"),
FRAMELESSHELPER_STRING_LITERAL("StandardMinimizeButton is not available until Qt6."));
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMaximizeButton"),
FRAMELESSHELPER_STRING_LITERAL("StandardMaximizeButton is not available until Qt6."));
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardCloseButton"),
FRAMELESSHELPER_STRING_LITERAL("StandardCloseButton is not available until Qt6."));
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardTitleBar"),
FRAMELESSHELPER_STRING_LITERAL("StandardTitleBar is not available until Qt6."));
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterModule(QUICK_URI_FULL);
q_ptr = q;
m_cachedSettings = settings;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
FramelessHelperQuickPlugin::FramelessHelperQuickPlugin(QObject *parent) : QQmlEngineExtensionPlugin(parent)
FramelessQuickHelperPrivate::~FramelessQuickHelperPrivate() = default;
FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(FramelessQuickHelper *pub)
{
Q_ASSERT(pub);
if (!pub) {
return nullptr;
}
return pub->d_func();
}
const FramelessQuickHelperPrivate *FramelessQuickHelperPrivate::get(const FramelessQuickHelper *pub)
{
Q_ASSERT(pub);
if (!pub) {
return nullptr;
}
return pub->d_func();
}
QQuickItem *FramelessQuickHelperPrivate::getTitleBarItem() const
{
return getWindowData().titleBarItem;
}
void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value)
{
Q_ASSERT(value);
if (!value) {
return;
}
QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
if (data->titleBarItem == value) {
return;
}
data->titleBarItem = value;
Q_Q(FramelessQuickHelper);
Q_EMIT q->titleBarItemChanged();
}
void FramelessQuickHelperPrivate::attachToWindow()
{
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
Q_ASSERT(window);
if (!window) {
return;
}
g_quickHelper()->mutex.lock();
QuickHelperData *data = getWindowDataMutable();
const bool attached = data->attached;
g_quickHelper()->mutex.unlock();
if (attached) {
return;
}
UserSettings settings = m_cachedSettings;
m_cachedSettings = {};
SystemParameters params = {};
params.getWindowId = [window]() -> WId { return window->winId(); };
params.getWindowFlags = [window]() -> Qt::WindowFlags { return window->flags(); };
params.setWindowFlags = [window](const Qt::WindowFlags flags) -> void { window->setFlags(flags); };
params.getWindowSize = [window]() -> QSize { return window->size(); };
params.setWindowSize = [window](const QSize &size) -> void { window->resize(size); };
params.getWindowPosition = [window]() -> QPoint { return window->position(); };
params.setWindowPosition = [window](const QPoint &pos) -> void { window->setX(pos.x()); window->setY(pos.y()); };
params.getWindowScreen = [window]() -> QScreen * { return window->screen(); };
params.isWindowFixedSize = [this]() -> bool { return isWindowFixedSize(); };
params.setWindowFixedSize = [this](const bool value) -> void { setWindowFixedSize(value); };
params.getWindowState = [window]() -> Qt::WindowState { return window->windowState(); };
params.setWindowState = [window](const Qt::WindowState state) -> void { window->setWindowState(state); };
params.getWindowHandle = [q]() -> QWindow * { return q->window(); };
params.windowToScreen = [window](const QPoint &pos) -> QPoint { return window->mapToGlobal(pos); };
params.screenToWindow = [window](const QPoint &pos) -> QPoint { return window->mapFromGlobal(pos); };
params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool {
QuickGlobal::SystemButtonType button2 = QuickGlobal::SystemButtonType::Unknown;
const bool result = isInSystemButtons(pos, &button2);
*button = FRAMELESSHELPER_ENUM_QUICK_TO_CORE(SystemButtonType, button2);
return result;
};
params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
params.getWindowDevicePixelRatio = [window]() -> qreal { return window->effectiveDevicePixelRatio(); };
params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void {
setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button),
FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state));
};
params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); };
params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); };
g_quickHelper()->mutex.lock();
data->params = params;
data->attached = true;
g_quickHelper()->mutex.unlock();
FramelessWindowsManager::instance()->addWindow(settings, params);
}
void FramelessQuickHelperPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
{
Q_ASSERT(item);
Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown);
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
return;
}
QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
switch (buttonType) {
case QuickGlobal::SystemButtonType::Unknown:
Q_ASSERT(false);
break;
case QuickGlobal::SystemButtonType::WindowIcon:
data->settings.windowIconButton = item;
break;
case QuickGlobal::SystemButtonType::Help:
data->settings.contextHelpButton = item;
break;
case QuickGlobal::SystemButtonType::Minimize:
data->settings.minimizeButton = item;
break;
case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore:
data->settings.maximizeButton = item;
break;
case QuickGlobal::SystemButtonType::Close:
data->settings.closeButton = item;
break;
}
}
void FramelessQuickHelperPrivate::setHitTestVisible(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
QMutexLocker locker(&g_quickHelper()->mutex);
QuickHelperData *data = getWindowDataMutable();
static constexpr const bool visible = true;
const bool exists = data->hitTestVisibleItems.contains(item);
if (visible && !exists) {
data->hitTestVisibleItems.append(item);
}
if constexpr (!visible && exists) {
data->hitTestVisibleItems.removeAll(item);
}
}
void FramelessQuickHelperPrivate::showSystemMenu(const QPoint &pos)
{
#ifdef Q_OS_WINDOWS
Q_Q(FramelessQuickHelper);
const QQuickWindow * const window = q->window();
if (!window) {
return;
}
const QPoint globalPos = window->mapToGlobal(pos);
const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint();
Utils::showSystemMenu(window->winId(), nativePos, {}, false, {}, [this]() -> bool { return isWindowFixedSize(); });
#else
Q_UNUSED(pos);
#endif
}
void FramelessQuickHelperPrivate::windowStartSystemMove2(const QPoint &pos)
{
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
Utils::startSystemMove(window, pos);
}
void FramelessQuickHelperPrivate::windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos)
{
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
Utils::startSystemResize(window, edges, pos);
}
void FramelessQuickHelperPrivate::moveWindowToDesktopCenter()
{
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
Utils::moveWindowToDesktopCenter([window]() -> QScreen * { return window->screen(); },
[window]() -> QSize { return window->size(); },
[window](const QPoint &pos) -> void { window->setX(pos.x()); window->setY(pos.y()); }, true);
}
void FramelessQuickHelperPrivate::bringWindowToFront()
{
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
const QQuickWindow::Visibility visibility = window->visibility();
if (visibility == QQuickWindow::Hidden) {
window->show();
}
if (visibility == QQuickWindow::Minimized) {
window->showNormal(); // ### FIXME
}
window->raise();
window->requestActivate();
}
bool FramelessQuickHelperPrivate::isWindowFixedSize() const
{
const QuickHelperData data = getWindowData();
if (data.settings.options & Option::DisableResizing) {
return true;
}
Q_Q(const FramelessQuickHelper);
const QQuickWindow * const window = q->window();
if (!window) {
return false;
}
if (window->flags() & Qt::MSWindowsFixedSizeDialogHint) {
return true;
}
const QSize minSize = window->minimumSize();
const QSize maxSize = window->maximumSize();
if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) {
return true;
}
return false;
}
void FramelessQuickHelperPrivate::setWindowFixedSize(const bool value, const bool force)
{
if ((isWindowFixedSize() == value) && !force) {
return;
}
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
if (value) {
const QSize size = window->size();
window->setMinimumSize(size);
window->setMaximumSize(size);
window->setFlags(window->flags() | Qt::MSWindowsFixedSizeDialogHint);
} else {
window->setFlags(window->flags() & ~Qt::MSWindowsFixedSizeDialogHint);
window->setMinimumSize(kDefaultWindowSize);
window->setMaximumSize(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX));
}
#ifdef Q_OS_WINDOWS
Utils::setAeroSnappingEnabled(window->winId(), !value);
#endif
}
QRect FramelessQuickHelperPrivate::mapItemGeometryToScene(const QQuickItem * const item) const
{
Q_ASSERT(item);
if (!item) {
return {};
}
const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
const QSizeF size = item->size();
#else
const QSizeF size = {item->width(), item->height()};
#endif
return QRectF(originPoint, size).toRect();
}
bool FramelessQuickHelperPrivate::isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const
{
Q_ASSERT(button);
if (!button) {
return false;
}
*button = QuickGlobal::SystemButtonType::Unknown;
const QuickHelperData data = getWindowData();
if (data.settings.windowIconButton && data.settings.windowIconButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto iconBtn = qobject_cast<QQuickItem *>(data.settings.windowIconButton);
if (mapItemGeometryToScene(iconBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::WindowIcon;
return true;
}
}
if (data.settings.contextHelpButton && data.settings.contextHelpButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto helpBtn = qobject_cast<QQuickItem *>(data.settings.contextHelpButton);
if (mapItemGeometryToScene(helpBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Help;
return true;
}
}
if (data.settings.minimizeButton && data.settings.minimizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto minBtn = qobject_cast<QQuickItem *>(data.settings.minimizeButton);
if (mapItemGeometryToScene(minBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Minimize;
return true;
}
}
if (data.settings.maximizeButton && data.settings.maximizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto maxBtn = qobject_cast<QQuickItem *>(data.settings.maximizeButton);
if (mapItemGeometryToScene(maxBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Maximize;
return true;
}
}
if (data.settings.closeButton && data.settings.closeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto closeBtn = qobject_cast<QQuickItem *>(data.settings.closeButton);
if (mapItemGeometryToScene(closeBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Close;
return true;
}
}
return false;
}
bool FramelessQuickHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos) const
{
const QuickHelperData data = getWindowData();
if (!data.titleBarItem) {
return false;
}
QRegion region = mapItemGeometryToScene(data.titleBarItem);
const auto systemButtons = {data.settings.windowIconButton, data.settings.contextHelpButton,
data.settings.minimizeButton, data.settings.maximizeButton, data.settings.closeButton};
for (auto &&button : qAsConst(systemButtons)) {
if (button && button->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto quickButton = qobject_cast<QQuickItem *>(button);
region -= mapItemGeometryToScene(quickButton);
}
}
if (!data.hitTestVisibleItems.isEmpty()) {
for (auto &&item : qAsConst(data.hitTestVisibleItems)) {
Q_ASSERT(item);
if (item) {
region -= mapItemGeometryToScene(item);
}
}
}
return region.contains(pos);
}
bool FramelessQuickHelperPrivate::shouldIgnoreMouseEvents(const QPoint &pos) const
{
Q_Q(const FramelessQuickHelper);
const QQuickWindow * const window = q->window();
if (!window) {
return false;
}
const bool withinFrameBorder = [&pos, window]() -> bool {
if (pos.y() < kDefaultResizeBorderThickness) {
return true;
}
#ifdef Q_OS_WINDOWS
if (Utils::isWindowFrameBorderVisible()) {
return false;
}
#endif
return ((pos.x() < kDefaultResizeBorderThickness)
|| (pos.x() >= (window->width() - kDefaultResizeBorderThickness)));
}();
return ((window->visibility() == QQuickWindow::Windowed) && withinFrameBorder);
}
void FramelessQuickHelperPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button,
const QuickGlobal::ButtonState state)
{
Q_ASSERT(button != QuickGlobal::SystemButtonType::Unknown);
if (button == QuickGlobal::SystemButtonType::Unknown) {
return;
}
const QuickHelperData data = getWindowData();
QQuickAbstractButton *quickButton = nullptr;
switch (button) {
case QuickGlobal::SystemButtonType::Unknown: {
Q_ASSERT(false);
} break;
case QuickGlobal::SystemButtonType::WindowIcon: {
if (data.settings.windowIconButton && data.settings.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.windowIconButton);
}
} break;
case QuickGlobal::SystemButtonType::Help: {
if (data.settings.contextHelpButton && data.settings.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.contextHelpButton);
}
} break;
case QuickGlobal::SystemButtonType::Minimize: {
if (data.settings.minimizeButton && data.settings.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.minimizeButton);
}
} break;
case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore: {
if (data.settings.maximizeButton && data.settings.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.maximizeButton);
}
} break;
case QuickGlobal::SystemButtonType::Close: {
if (data.settings.closeButton && data.settings.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(data.settings.closeButton);
}
} break;
}
if (quickButton) {
const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void {
Q_ASSERT(btn);
if (!btn) {
return;
}
switch (state) {
case QuickGlobal::ButtonState::Unspecified: {
btn->setPressed(false);
btn->setHovered(false);
} break;
case QuickGlobal::ButtonState::Hovered: {
btn->setPressed(false);
btn->setHovered(true);
} break;
case QuickGlobal::ButtonState::Pressed: {
btn->setHovered(true);
btn->setPressed(true);
} break;
case QuickGlobal::ButtonState::Clicked: {
// Clicked: pressed --> released, so behave like hovered.
btn->setPressed(false);
btn->setHovered(true);
// "QQuickAbstractButtonPrivate::click()"'s implementation is nothing but
// emits the "clicked" signal of the public interface, so we just emit
// the signal directly to avoid accessing the private implementation.
Q_EMIT btn->clicked();
} break;
}
};
updateButtonState(quickButton);
}
}
QuickHelperData FramelessQuickHelperPrivate::getWindowData() const
{
Q_Q(const FramelessQuickHelper);
const QQuickWindow * const window = q->window();
Q_ASSERT(window);
if (!window) {
return {};
}
const WId windowId = window->winId();
QMutexLocker locker(&g_quickHelper()->mutex);
if (!g_quickHelper()->data.contains(windowId)) {
g_quickHelper()->data.insert(windowId, {});
}
return g_quickHelper()->data.value(windowId);
}
QuickHelperData *FramelessQuickHelperPrivate::getWindowDataMutable() const
{
Q_Q(const FramelessQuickHelper);
const QQuickWindow * const window = q->window();
Q_ASSERT(window);
if (!window) {
return nullptr;
}
const WId windowId = window->winId();
if (!g_quickHelper()->data.contains(windowId)) {
g_quickHelper()->data.insert(windowId, {});
}
return &g_quickHelper()->data[windowId];
}
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent, const UserSettings &settings)
: QQuickItem(parent), d_ptr(new FramelessQuickHelperPrivate(this, settings))
{
}
FramelessHelperQuickPlugin::~FramelessHelperQuickPlugin() = default;
FramelessQuickHelper::~FramelessQuickHelper() = default;
void FramelessHelperQuickPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
FramelessQuickHelper *FramelessQuickHelper::qmlAttachedProperties(QObject *parentObject)
{
Q_ASSERT(engine);
Q_ASSERT(uri);
if (!engine || !uri) {
return;
Q_ASSERT(parentObject);
if (!parentObject) {
return nullptr;
}
Q_ASSERT(QLatin1String(uri) == QLatin1String(FRAMELESSHELPER_QUICK_URI));
if (QLatin1String(uri) != QLatin1String(FRAMELESSHELPER_QUICK_URI)) {
return;
const auto item = new FramelessQuickHelper;
const auto parentItem = qobject_cast<QQuickItem *>(parentObject);
if (parentItem) {
item->setParentItem(parentItem);
} else {
item->setParent(parentObject);
}
return item;
}
QQuickItem *FramelessQuickHelper::titleBarItem() const
{
Q_D(const FramelessQuickHelper);
return d->getTitleBarItem();
}
bool FramelessQuickHelper::isWindowFixedSize() const
{
Q_D(const FramelessQuickHelper);
return d->isWindowFixedSize();
}
void FramelessQuickHelper::setTitleBarItem(QQuickItem *value)
{
Q_ASSERT(value);
if (!value) {
return;
}
Q_D(FramelessQuickHelper);
d->setTitleBarItem(value);
}
void FramelessQuickHelper::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
{
Q_ASSERT(item);
Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown);
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
return;
}
Q_D(FramelessQuickHelper);
d->setSystemButton(item, buttonType);
}
void FramelessQuickHelper::setHitTestVisible(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_D(FramelessQuickHelper);
d->setHitTestVisible(item);
}
void FramelessQuickHelper::showSystemMenu(const QPoint &pos)
{
Q_D(FramelessQuickHelper);
d->showSystemMenu(pos);
}
void FramelessQuickHelper::windowStartSystemMove2(const QPoint &pos)
{
Q_D(FramelessQuickHelper);
d->windowStartSystemMove2(pos);
}
void FramelessQuickHelper::windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos)
{
if (edges == Qt::Edges{}) {
return;
}
Q_D(FramelessQuickHelper);
d->windowStartSystemResize2(edges, pos);
}
void FramelessQuickHelper::moveWindowToDesktopCenter()
{
Q_D(FramelessQuickHelper);
d->moveWindowToDesktopCenter();
}
void FramelessQuickHelper::bringWindowToFront()
{
Q_D(FramelessQuickHelper);
d->bringWindowToFront();
}
void FramelessQuickHelper::setWindowFixedSize(const bool value, const bool force)
{
Q_D(FramelessQuickHelper);
d->setWindowFixedSize(value, force);
}
void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeData &data)
{
QQuickItem::itemChange(change, data);
if ((change == FramelessQuickHelper::ItemSceneChange) && data.window) {
Q_D(FramelessQuickHelper);
d->attachToWindow();
}
FramelessHelper::Quick::registerTypes(engine);
}
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,82 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "framelesshelperquick_global.h"
#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
class QQuickItem;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
struct QuickHelperData;
class FramelessQuickHelper;
class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject
{
Q_OBJECT
Q_DECLARE_PUBLIC(FramelessQuickHelper)
Q_DISABLE_COPY_MOVE(FramelessQuickHelperPrivate)
public:
explicit FramelessQuickHelperPrivate(FramelessQuickHelper *q, const Global::UserSettings &settings = {});
~FramelessQuickHelperPrivate() override;
Q_NODISCARD static FramelessQuickHelperPrivate *get(FramelessQuickHelper *pub);
Q_NODISCARD static const FramelessQuickHelperPrivate *get(const FramelessQuickHelper *pub);
Q_NODISCARD QQuickItem *getTitleBarItem() const;
void setTitleBarItem(QQuickItem *value);
void attachToWindow();
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
void setHitTestVisible(QQuickItem *item);
void showSystemMenu(const QPoint &pos);
void windowStartSystemMove2(const QPoint &pos);
void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos);
void moveWindowToDesktopCenter();
void bringWindowToFront();
Q_NODISCARD bool isWindowFixedSize() const;
void setWindowFixedSize(const bool value, const bool force = false);
private:
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state);
Q_NODISCARD QuickHelperData getWindowData() const;
Q_NODISCARD QuickHelperData *getWindowDataMutable() const;
private:
FramelessQuickHelper *q_ptr = nullptr;
Global::UserSettings m_cachedSettings = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelessquickmodule.h"
#include "framelessquickhelper.h"
#include "framelessquickutils.h"
#include "framelessquickwindow.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
# include "quickstandardminimizebutton_p.h"
# include "quickstandardmaximizebutton_p.h"
# include "quickstandardclosebutton_p.h"
# include "quickstandardtitlebar_p.h"
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#ifndef QUICK_URI_SHORT
# define QUICK_URI_SHORT FRAMELESSHELPER_QUICK_URI, 1
#endif
#ifndef QUICK_URI_FULL
# define QUICK_URI_FULL QUICK_URI_SHORT, 0
#endif
#ifndef QUICK_URI_EXPAND
# define QUICK_URI_EXPAND(name) QUICK_URI_FULL, name
#endif
FRAMELESSHELPER_BEGIN_NAMESPACE
void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
{
Q_ASSERT(engine);
if (!engine) {
return;
}
static bool inited = false;
if (inited) {
return;
}
inited = true;
qRegisterMetaType<QuickGlobal::Options>();
qRegisterMetaType<QuickGlobal::SystemTheme>();
qRegisterMetaType<QuickGlobal::SystemButtonType>();
qRegisterMetaType<QuickGlobal::ResourceType>();
qRegisterMetaType<QuickGlobal::DwmColorizationArea>();
qRegisterMetaType<QuickGlobal::Anchor>();
qRegisterMetaType<QuickGlobal::ButtonState>();
qmlRegisterUncreatableType<QuickGlobal>(QUICK_URI_FULL, "FramelessHelperConstants",
FRAMELESSHELPER_STRING_LITERAL("The FramelessHelperConstants namespace is not creatable, you can only use it to access it's enums."));
qmlRegisterSingletonType<FramelessQuickUtils>(QUICK_URI_EXPAND("FramelessUtils"),
[](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
return new FramelessQuickUtils;
});
qmlRegisterRevision<QWindow, 254>(QUICK_URI_FULL);
qmlRegisterRevision<QQuickWindow, 254>(QUICK_URI_FULL);
qmlRegisterRevision<QQuickItem, 254>(QUICK_URI_FULL);
qmlRegisterType<FramelessQuickHelper>(QUICK_URI_EXPAND("FramelessHelper"));
qmlRegisterType<FramelessQuickWindow>(QUICK_URI_EXPAND("FramelessWindow"));
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterType<QuickStandardMinimizeButton>(QUICK_URI_EXPAND("StandardMinimizeButton"));
qmlRegisterType<QuickStandardMaximizeButton>(QUICK_URI_EXPAND("StandardMaximizeButton"));
qmlRegisterType<QuickStandardCloseButton>(QUICK_URI_EXPAND("StandardCloseButton"));
qmlRegisterType<QuickStandardTitleBar>(QUICK_URI_EXPAND("StandardTitleBar"));
#else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMinimizeButton"),
FRAMELESSHELPER_STRING_LITERAL("StandardMinimizeButton is not available until Qt6."));
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardMaximizeButton"),
FRAMELESSHELPER_STRING_LITERAL("StandardMaximizeButton is not available until Qt6."));
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardCloseButton"),
FRAMELESSHELPER_STRING_LITERAL("StandardCloseButton is not available until Qt6."));
qmlRegisterTypeNotAvailable(QUICK_URI_EXPAND("StandardTitleBar"),
FRAMELESSHELPER_STRING_LITERAL("StandardTitleBar is not available until Qt6."));
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterModule(QUICK_URI_FULL);
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
FramelessHelperQuickPlugin::FramelessHelperQuickPlugin(QObject *parent) : QQmlEngineExtensionPlugin(parent)
{
}
FramelessHelperQuickPlugin::~FramelessHelperQuickPlugin() = default;
void FramelessHelperQuickPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
Q_ASSERT(engine);
Q_ASSERT(uri);
if (!engine || !uri) {
return;
}
Q_ASSERT(qstrcmp(uri, FRAMELESSHELPER_QUICK_URI) == 0);
if (qstrcmp(uri, FRAMELESSHELPER_QUICK_URI) != 0) {
return;
}
FramelessHelper::Quick::registerTypes(engine);
}
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,25 @@
/*
* MIT License
*
* Copyright (C) 2022 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "../../include/FramelessHelper/Quick/framelessquickmodule.h"

View File

@ -33,7 +33,7 @@ using namespace Global;
FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
{
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this](){
Q_EMIT darkModeEnabledChanged();
Q_EMIT systemThemeChanged();
Q_EMIT systemAccentColorChanged();
Q_EMIT titleBarColorizedChanged();
});
@ -41,12 +41,12 @@ FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
FramelessQuickUtils::~FramelessQuickUtils() = default;
qreal FramelessQuickUtils::titleBarHeight()
qreal FramelessQuickUtils::titleBarHeight() const
{
return kDefaultTitleBarHeight;
}
bool FramelessQuickUtils::frameBorderVisible()
bool FramelessQuickUtils::frameBorderVisible() const
{
#ifdef Q_OS_WINDOWS
return (Utils::isWindowFrameBorderVisible() && !Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2));
@ -55,7 +55,7 @@ bool FramelessQuickUtils::frameBorderVisible()
#endif
}
qreal FramelessQuickUtils::frameBorderThickness()
qreal FramelessQuickUtils::frameBorderThickness() const
{
#ifdef Q_OS_WINDOWS
return kDefaultWindowFrameBorderThickness;
@ -64,12 +64,12 @@ qreal FramelessQuickUtils::frameBorderThickness()
#endif
}
bool FramelessQuickUtils::darkModeEnabled()
QuickGlobal::SystemTheme FramelessQuickUtils::systemTheme() const
{
return Utils::shouldAppsUseDarkMode();
return FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemTheme, Utils::getSystemTheme());
}
QColor FramelessQuickUtils::systemAccentColor()
QColor FramelessQuickUtils::systemAccentColor() const
{
#ifdef Q_OS_WINDOWS
return Utils::getDwmColorizationColor();
@ -82,37 +82,37 @@ QColor FramelessQuickUtils::systemAccentColor()
#endif
}
bool FramelessQuickUtils::titleBarColorized()
bool FramelessQuickUtils::titleBarColorized() const
{
return Utils::isTitleBarColorized();
}
QColor FramelessQuickUtils::defaultSystemLightColor()
QColor FramelessQuickUtils::defaultSystemLightColor() const
{
return kDefaultSystemLightColor;
}
QColor FramelessQuickUtils::defaultSystemDarkColor()
QColor FramelessQuickUtils::defaultSystemDarkColor() const
{
return kDefaultSystemDarkColor;
}
QSizeF FramelessQuickUtils::defaultSystemButtonSize()
QSizeF FramelessQuickUtils::defaultSystemButtonSize() const
{
return kDefaultSystemButtonSize;
}
QSizeF FramelessQuickUtils::defaultSystemButtonIconSize()
QSizeF FramelessQuickUtils::defaultSystemButtonIconSize() const
{
return kDefaultSystemButtonIconSize;
}
QColor FramelessQuickUtils::defaultSystemButtonBackgroundColor()
QColor FramelessQuickUtils::defaultSystemButtonBackgroundColor() const
{
return kDefaultSystemButtonBackgroundColor;
}
QColor FramelessQuickUtils::defaultSystemCloseButtonBackgroundColor()
QColor FramelessQuickUtils::defaultSystemCloseButtonBackgroundColor() const
{
return kDefaultSystemCloseButtonBackgroundColor;
}

View File

@ -27,7 +27,6 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQuick/private/qquickanchors_p.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <framelesswindowsmanager.h>
#include <utils.h>
@ -35,67 +34,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
using namespace Global;
static constexpr const char QTQUICK_ITEM_CLASS_NAME[] = "QQuickItem";
static constexpr const char QTQUICK_BUTTON_CLASS_NAME[] = "QQuickAbstractButton";
[[nodiscard]] static inline QuickGlobal::Options optionsCoreToQuick(const Options value)
{
QuickGlobal::Options result = {};
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, ForceHideWindowFrameBorder, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, ForceShowWindowFrameBorder, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontDrawTopWindowFrameBorder, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontForceSquareWindowCorners, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, TransparentWindowBackground, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableWindowsSnapLayout, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, CreateStandardWindowLayout, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, BeCompatibleWithQtFramelessWindowHint, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchQtInternals, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchWindowFrameBorderColor, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontInstallSystemMenuHook, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableSystemMenu, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, NoDoubleClickMaximizeToggle, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableResizing, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DisableDragging, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchCursorShape, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontMoveWindowToDesktopCenter, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTreatFullScreenAsZoomed, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchHighDpiScalingPolicy, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchScaleFactorRoundingPolicy, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontTouchProcessDpiAwarenessLevel, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, DontEnsureNonNativeWidgetSiblings, value, result)
FRAMELESSHELPER_FLAGS_CORE_TO_QUICK(Option, SyncNativeControlsThemeWithSystem, value, result)
return result;
}
[[nodiscard]] static inline Options optionsQuickToCore(const QuickGlobal::Options value)
{
Options result = {};
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, ForceHideWindowFrameBorder, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, ForceShowWindowFrameBorder, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontDrawTopWindowFrameBorder, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontForceSquareWindowCorners, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, TransparentWindowBackground, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableWindowsSnapLayout, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, CreateStandardWindowLayout, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, BeCompatibleWithQtFramelessWindowHint, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchQtInternals, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchWindowFrameBorderColor, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontInstallSystemMenuHook, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableSystemMenu, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, NoDoubleClickMaximizeToggle, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableResizing, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DisableDragging, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchCursorShape, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontMoveWindowToDesktopCenter, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTreatFullScreenAsZoomed, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchHighDpiScalingPolicy, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchScaleFactorRoundingPolicy, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontTouchProcessDpiAwarenessLevel, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, DontEnsureNonNativeWidgetSiblings, value, result)
FRAMELESSHELPER_FLAGS_QUICK_TO_CORE(Option, SyncNativeControlsThemeWithSystem, value, result)
return result;
}
FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q, const UserSettings &settings) : QObject(q)
{
Q_ASSERT(q);
@ -160,23 +98,6 @@ bool FramelessQuickWindowPrivate::isFullScreen() const
return (q->visibility() == FramelessQuickWindow::FullScreen);
}
bool FramelessQuickWindowPrivate::isFixedSize() const
{
if (m_settings.options & Option::DisableResizing) {
return true;
}
Q_Q(const FramelessQuickWindow);
if (q->flags() & Qt::MSWindowsFixedSizeDialogHint) {
return true;
}
const QSize minSize = q->minimumSize();
const QSize maxSize = q->maximumSize();
if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize == maxSize)) {
return true;
}
return false;
}
QColor FramelessQuickWindowPrivate::getFrameBorderColor() const
{
#ifdef Q_OS_WINDOWS
@ -217,77 +138,6 @@ QQuickAnchorLine FramelessQuickWindowPrivate::getTopBorderVerticalCenter() const
return QQuickAnchorLine(m_topBorderRectangle.data(), QQuickAnchors::VCenterAnchor);
}
void FramelessQuickWindowPrivate::setTitleBarItem(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
if (m_titleBarItem == item) {
return;
}
m_titleBarItem = item;
Q_Q(FramelessQuickWindow);
Q_EMIT q->titleBarItemChanged();
}
void FramelessQuickWindowPrivate::setHitTestVisible(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
static constexpr const bool visible = true;
const bool exists = m_hitTestVisibleItems.contains(item);
if (visible && !exists) {
m_hitTestVisibleItems.append(item);
}
if constexpr (!visible && exists) {
m_hitTestVisibleItems.removeAll(item);
}
}
void FramelessQuickWindowPrivate::moveToDesktopCenter()
{
Utils::moveWindowToDesktopCenter(m_params.getWindowScreen, m_params.getWindowSize,
m_params.setWindowPosition, true);
}
void FramelessQuickWindowPrivate::setFixedSize(const bool value, const bool force)
{
if ((isFixedSize() == value) && !force) {
return;
}
Q_Q(FramelessQuickWindow);
if (value) {
const QSize size = q->size();
q->setMinimumSize(size);
q->setMaximumSize(size);
q->setFlags(q->flags() | Qt::MSWindowsFixedSizeDialogHint);
} else {
q->setFlags(q->flags() & ~Qt::MSWindowsFixedSizeDialogHint);
q->setMinimumSize(kDefaultWindowSize);
q->setMaximumSize(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX));
}
#ifdef Q_OS_WINDOWS
Utils::setAeroSnappingEnabled(q->winId(), !value);
#endif
Q_EMIT q->fixedSizeChanged();
}
void FramelessQuickWindowPrivate::bringToFront()
{
Q_Q(FramelessQuickWindow);
if (isHidden()) {
q->show();
}
if (isMinimized()) {
q->showNormal(); // ### FIXME !!!
}
q->raise();
q->requestActivate();
}
void FramelessQuickWindowPrivate::snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor)
{
Q_ASSERT(item);
@ -340,74 +190,6 @@ void FramelessQuickWindowPrivate::snapToTopBorder(QQuickItem *item, const QuickG
}
}
void FramelessQuickWindowPrivate::setOptions(const QuickGlobal::Options value)
{
Q_Q(FramelessQuickWindow);
if (m_quickOptions == value) {
return;
}
// ### TODO: re-evaluate some usable options.
m_quickOptions = value;
m_settings.options = optionsQuickToCore(m_quickOptions);
Q_EMIT q->optionsChanged();
}
void FramelessQuickWindowPrivate::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
{
Q_ASSERT(item);
Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown);
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
return;
}
switch (buttonType) {
case QuickGlobal::SystemButtonType::Unknown:
Q_ASSERT(false);
break;
case QuickGlobal::SystemButtonType::WindowIcon:
m_settings.windowIconButton = item;
break;
case QuickGlobal::SystemButtonType::Help:
m_settings.contextHelpButton = item;
break;
case QuickGlobal::SystemButtonType::Minimize:
m_settings.minimizeButton = item;
break;
case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore:
m_settings.maximizeButton = item;
break;
case QuickGlobal::SystemButtonType::Close:
m_settings.closeButton = item;
break;
}
}
bool FramelessQuickWindowPrivate::eventFilter(QObject *object, QEvent *event)
{
Q_ASSERT(object);
Q_ASSERT(event);
if (!object || !event) {
return false;
}
if (!object->isWindowType()) {
return QObject::eventFilter(object, event);
}
Q_Q(FramelessQuickWindow);
const auto window = qobject_cast<FramelessQuickWindow *>(object);
if (window != q) {
return QObject::eventFilter(object, event);
}
switch (event->type()) {
case QEvent::Show: {
const auto showEvent = static_cast<QShowEvent *>(event);
showEventHandler(showEvent);
} break;
default:
break;
}
return QObject::eventFilter(object, event);
}
void FramelessQuickWindowPrivate::showMinimized2()
{
Q_Q(FramelessQuickWindow);
@ -424,9 +206,6 @@ void FramelessQuickWindowPrivate::showMinimized2()
void FramelessQuickWindowPrivate::toggleMaximized()
{
if (isFixedSize()) {
return;
}
Q_Q(FramelessQuickWindow);
if (isZoomed()) {
q->showNormal();
@ -437,9 +216,6 @@ void FramelessQuickWindowPrivate::toggleMaximized()
void FramelessQuickWindowPrivate::toggleFullScreen()
{
if (isFixedSize()) {
return;
}
Q_Q(FramelessQuickWindow);
if (isFullScreen()) {
q->setVisibility(m_savedVisibility);
@ -449,79 +225,12 @@ void FramelessQuickWindowPrivate::toggleFullScreen()
}
}
void FramelessQuickWindowPrivate::showSystemMenu(const QPoint &pos)
{
#ifdef Q_OS_WINDOWS
Q_Q(FramelessQuickWindow);
const QPoint globalPos = q->mapToGlobal(pos);
const QPoint nativePos = QPointF(QPointF(globalPos) * q->effectiveDevicePixelRatio()).toPoint();
Utils::showSystemMenu(q->winId(), nativePos, m_settings.systemMenuOffset,
false, m_settings.options, m_params.isWindowFixedSize);
#else
Q_UNUSED(pos);
#endif
}
void FramelessQuickWindowPrivate::startSystemMove2(const QPoint &pos)
{
Q_Q(FramelessQuickWindow);
Utils::startSystemMove(q, pos);
}
void FramelessQuickWindowPrivate::startSystemResize2(const Qt::Edges edges, const QPoint &pos)
{
if (isFixedSize()) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
Q_Q(FramelessQuickWindow);
Utils::startSystemResize(q, edges, pos);
}
void FramelessQuickWindowPrivate::initialize()
{
Q_Q(FramelessQuickWindow);
m_params.getWindowId = [q]() -> WId { return q->winId(); };
m_params.getWindowFlags = [q]() -> Qt::WindowFlags { return q->flags(); };
m_params.setWindowFlags = [q](const Qt::WindowFlags flags) -> void { q->setFlags(flags); };
m_params.getWindowSize = [q]() -> QSize { return q->size(); };
m_params.setWindowSize = [q](const QSize &size) -> void { q->resize(size); };
m_params.getWindowPosition = [q]() -> QPoint { return q->position(); };
m_params.setWindowPosition = [q](const QPoint &pos) -> void { q->setX(pos.x()); q->setY(pos.y()); };
m_params.getWindowScreen = [q]() -> QScreen * { return q->screen(); };
m_params.isWindowFixedSize = [this]() -> bool { return isFixedSize(); };
m_params.setWindowFixedSize = [this](const bool value) -> void { setFixedSize(value); };
m_params.getWindowState = [q]() -> Qt::WindowState { return q->windowState(); };
m_params.setWindowState = [q](const Qt::WindowState state) -> void { q->setWindowState(state); };
m_params.getWindowHandle = [q]() -> QWindow * { return q; };
m_params.windowToScreen = [q](const QPoint &pos) -> QPoint { return q->mapToGlobal(pos); };
m_params.screenToWindow = [q](const QPoint &pos) -> QPoint { return q->mapFromGlobal(pos); };
m_params.isInsideSystemButtons = [this](const QPoint &pos, SystemButtonType *button) -> bool {
QuickGlobal::SystemButtonType button2 = QuickGlobal::SystemButtonType::Unknown;
const bool result = isInSystemButtons(pos, &button2);
*button = FRAMELESSHELPER_ENUM_QUICK_TO_CORE(SystemButtonType, button2);
return result;
};
m_params.isInsideTitleBarDraggableArea = [this](const QPoint &pos) -> bool { return isInTitleBarDraggableArea(pos); };
m_params.getWindowDevicePixelRatio = [q]() -> qreal { return q->effectiveDevicePixelRatio(); };
m_params.setSystemButtonState = [this](const SystemButtonType button, const ButtonState state) -> void {
setSystemButtonState(FRAMELESSHELPER_ENUM_CORE_TO_QUICK(SystemButtonType, button),
FRAMELESSHELPER_ENUM_CORE_TO_QUICK(ButtonState, state));
};
m_params.shouldIgnoreMouseEvents = [this](const QPoint &pos) -> bool { return shouldIgnoreMouseEvents(pos); };
m_params.showSystemMenu = [this](const QPoint &pos) -> void { showSystemMenu(pos); };
if (m_settings.options & Option::DisableResizing) {
setFixedSize(true, true);
}
if (m_settings.options & Option::TransparentWindowBackground) {
q->setColor(kDefaultTransparentColor);
}
m_quickOptions = optionsCoreToQuick(m_settings.options);
FramelessWindowsManager * const manager = FramelessWindowsManager::instance();
manager->addWindow(m_settings, m_params);
q->installEventFilter(this);
QQuickItem * const rootItem = q->contentItem();
const QQuickItemPrivate * const rootItemPrivate = QQuickItemPrivate::get(rootItem);
m_topBorderRectangle.reset(new QQuickRectangle(rootItem));
@ -545,115 +254,12 @@ void FramelessQuickWindowPrivate::initialize()
Q_EMIT q->fullScreenChanged();
});
connect(q, &FramelessQuickWindow::activeChanged, this, &FramelessQuickWindowPrivate::updateTopBorderColor);
connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this, q](){
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::systemThemeChanged, this, [this, q](){
updateTopBorderColor();
Q_EMIT q->frameBorderColorChanged();
});
}
QRect FramelessQuickWindowPrivate::mapItemGeometryToScene(const QQuickItem * const item) const
{
Q_ASSERT(item);
if (!item) {
return {};
}
const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
const QSizeF size = item->size();
#else
const QSizeF size = {item->width(), item->height()};
#endif
return QRectF(originPoint, size).toRect();
}
bool FramelessQuickWindowPrivate::isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const
{
Q_ASSERT(button);
if (!button) {
return false;
}
*button = QuickGlobal::SystemButtonType::Unknown;
if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto iconBtn = qobject_cast<QQuickItem *>(m_settings.windowIconButton);
if (mapItemGeometryToScene(iconBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::WindowIcon;
return true;
}
}
if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto helpBtn = qobject_cast<QQuickItem *>(m_settings.contextHelpButton);
if (mapItemGeometryToScene(helpBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Help;
return true;
}
}
if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto minBtn = qobject_cast<QQuickItem *>(m_settings.minimizeButton);
if (mapItemGeometryToScene(minBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Minimize;
return true;
}
}
if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto maxBtn = qobject_cast<QQuickItem *>(m_settings.maximizeButton);
if (mapItemGeometryToScene(maxBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Maximize;
return true;
}
}
if (m_settings.closeButton && m_settings.closeButton->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto closeBtn = qobject_cast<QQuickItem *>(m_settings.closeButton);
if (mapItemGeometryToScene(closeBtn).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Close;
return true;
}
}
return false;
}
bool FramelessQuickWindowPrivate::isInTitleBarDraggableArea(const QPoint &pos) const
{
if (!m_titleBarItem) {
return false;
}
QRegion region = mapItemGeometryToScene(m_titleBarItem);
const auto systemButtons = {m_settings.windowIconButton, m_settings.contextHelpButton,
m_settings.minimizeButton, m_settings.maximizeButton, m_settings.closeButton};
for (auto &&button : qAsConst(systemButtons)) {
if (button && button->inherits(QTQUICK_ITEM_CLASS_NAME)) {
const auto quickButton = qobject_cast<QQuickItem *>(button);
region -= mapItemGeometryToScene(quickButton);
}
}
if (!m_hitTestVisibleItems.isEmpty()) {
for (auto &&item : qAsConst(m_hitTestVisibleItems)) {
Q_ASSERT(item);
if (item) {
region -= mapItemGeometryToScene(item);
}
}
}
return region.contains(pos);
}
bool FramelessQuickWindowPrivate::shouldIgnoreMouseEvents(const QPoint &pos) const
{
Q_Q(const FramelessQuickWindow);
const bool withinFrameBorder = [&pos, q]() -> bool {
if (pos.y() < kDefaultResizeBorderThickness) {
return true;
}
#ifdef Q_OS_WINDOWS
if (Utils::isWindowFrameBorderVisible()) {
return false;
}
#endif
return ((pos.x() < kDefaultResizeBorderThickness)
|| (pos.x() >= (q->width() - kDefaultResizeBorderThickness)));
}();
return (isNormal() && withinFrameBorder);
}
bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const
{
#ifdef Q_OS_WINDOWS
@ -664,120 +270,6 @@ bool FramelessQuickWindowPrivate::shouldDrawFrameBorder() const
#endif
}
void FramelessQuickWindowPrivate::setSystemButtonState(const QuickGlobal::SystemButtonType button,
const QuickGlobal::ButtonState state)
{
Q_ASSERT(button != QuickGlobal::SystemButtonType::Unknown);
if (button == QuickGlobal::SystemButtonType::Unknown) {
return;
}
QQuickAbstractButton *quickButton = nullptr;
switch (button) {
case QuickGlobal::SystemButtonType::Unknown: {
Q_ASSERT(false);
} break;
case QuickGlobal::SystemButtonType::WindowIcon: {
if (m_settings.windowIconButton && m_settings.windowIconButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(m_settings.windowIconButton);
}
} break;
case QuickGlobal::SystemButtonType::Help: {
if (m_settings.contextHelpButton && m_settings.contextHelpButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(m_settings.contextHelpButton);
}
} break;
case QuickGlobal::SystemButtonType::Minimize: {
if (m_settings.minimizeButton && m_settings.minimizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(m_settings.minimizeButton);
}
} break;
case QuickGlobal::SystemButtonType::Maximize:
case QuickGlobal::SystemButtonType::Restore: {
if (m_settings.maximizeButton && m_settings.maximizeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(m_settings.maximizeButton);
}
} break;
case QuickGlobal::SystemButtonType::Close: {
if (m_settings.closeButton && m_settings.closeButton->inherits(QTQUICK_BUTTON_CLASS_NAME)) {
quickButton = qobject_cast<QQuickAbstractButton *>(m_settings.closeButton);
}
} break;
}
if (quickButton) {
const auto updateButtonState = [state](QQuickAbstractButton *btn) -> void {
Q_ASSERT(btn);
if (!btn) {
return;
}
switch (state) {
case QuickGlobal::ButtonState::Unspecified: {
btn->setDown(false);
btn->setPressed(false);
btn->setHovered(false);
} break;
case QuickGlobal::ButtonState::Hovered: {
btn->setDown(false);
btn->setPressed(false);
btn->setHovered(true);
} break;
case QuickGlobal::ButtonState::Pressed: {
btn->setHovered(true);
btn->setDown(true);
btn->setPressed(true);
} break;
case QuickGlobal::ButtonState::Clicked: {
// Clicked: pressed --> released, so behave like hovered.
btn->setDown(false);
btn->setPressed(false);
btn->setHovered(true);
// "QQuickAbstractButtonPrivate::click()"'s implementation is nothing but
// emits the "clicked" signal of the public interface, so we just emit
// the signal directly to avoid accessing the private implementation.
Q_EMIT btn->clicked();
} break;
}
};
updateButtonState(quickButton);
}
Q_Q(FramelessQuickWindow);
Q_EMIT q->systemButtonStateChanged(button, state);
}
void FramelessQuickWindowPrivate::showEventHandler(QShowEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
if (m_windowExposed) {
return;
}
m_windowExposed = true;
if (m_settings.options & Option::DontMoveWindowToDesktopCenter) {
if (!m_settings.startupPosition.isNull()) {
m_params.setWindowPosition(m_settings.startupPosition);
}
if (!m_settings.startupSize.isEmpty()) {
m_params.setWindowSize(m_settings.startupSize);
}
if (m_settings.startupState != Qt::WindowNoState) {
m_params.setWindowState(m_settings.startupState);
}
} else {
moveToDesktopCenter();
}
}
QuickGlobal::Options FramelessQuickWindowPrivate::getOptions() const
{
return m_quickOptions;
}
QQuickItem *FramelessQuickWindowPrivate::getTitleBarItem() const
{
return m_titleBarItem;
}
void FramelessQuickWindowPrivate::updateTopBorderColor()
{
#ifdef Q_OS_WINDOWS
@ -799,9 +291,9 @@ void FramelessQuickWindowPrivate::updateTopBorderHeight()
#endif
}
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings) : QQuickWindow(parent)
FramelessQuickWindow::FramelessQuickWindow(QWindow *parent, const UserSettings &settings)
: QQuickWindow(parent), d_ptr(new FramelessQuickWindowPrivate(this, settings))
{
d_ptr.reset(new FramelessQuickWindowPrivate(this, settings));
}
FramelessQuickWindow::~FramelessQuickWindow() = default;
@ -836,74 +328,12 @@ bool FramelessQuickWindow::isFullScreen() const
return d->isFullScreen();
}
bool FramelessQuickWindow::isFixedSize() const
{
Q_D(const FramelessQuickWindow);
return d->isFixedSize();
}
void FramelessQuickWindow::setFixedSize(const bool value)
{
Q_D(FramelessQuickWindow);
d->setFixedSize(value);
}
QColor FramelessQuickWindow::frameBorderColor() const
{
Q_D(const FramelessQuickWindow);
return d->getFrameBorderColor();
}
QuickGlobal::Options FramelessQuickWindow::options() const
{
Q_D(const FramelessQuickWindow);
return d->getOptions();
}
void FramelessQuickWindow::setOptions(const QuickGlobal::Options value)
{
Q_D(FramelessQuickWindow);
d->setOptions(value);
}
QQuickItem *FramelessQuickWindow::titleBarItem() const
{
Q_D(const FramelessQuickWindow);
return d->getTitleBarItem();
}
void FramelessQuickWindow::setTitleBarItem(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_D(FramelessQuickWindow);
d->setTitleBarItem(item);
}
void FramelessQuickWindow::setHitTestVisible(QQuickItem *item)
{
Q_ASSERT(item);
if (!item) {
return;
}
Q_D(FramelessQuickWindow);
d->setHitTestVisible(item);
}
void FramelessQuickWindow::moveToDesktopCenter()
{
Q_D(FramelessQuickWindow);
d->moveToDesktopCenter();
}
void FramelessQuickWindow::bringToFront()
{
Q_D(FramelessQuickWindow);
d->bringToFront();
}
void FramelessQuickWindow::snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor)
{
Q_ASSERT(item);
@ -914,17 +344,6 @@ void FramelessQuickWindow::snapToTopBorder(QQuickItem *item, const QuickGlobal::
d->snapToTopBorder(item, itemAnchor, topBorderAnchor);
}
void FramelessQuickWindow::setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType)
{
Q_ASSERT(item);
Q_ASSERT(buttonType != QuickGlobal::SystemButtonType::Unknown);
if (!item || (buttonType == QuickGlobal::SystemButtonType::Unknown)) {
return;
}
Q_D(FramelessQuickWindow);
d->setSystemButton(item, buttonType);
}
void FramelessQuickWindow::showMinimized2()
{
Q_D(FramelessQuickWindow);
@ -943,25 +362,4 @@ void FramelessQuickWindow::toggleFullScreen()
d->toggleFullScreen();
}
void FramelessQuickWindow::showSystemMenu(const QPoint &pos)
{
Q_D(FramelessQuickWindow);
d->showSystemMenu(pos);
}
void FramelessQuickWindow::startSystemMove2(const QPoint &pos)
{
Q_D(FramelessQuickWindow);
d->startSystemMove2(pos);
}
void FramelessQuickWindow::startSystemResize2(const Qt::Edges edges, const QPoint &pos)
{
if (edges == Qt::Edges{}) {
return;
}
Q_D(FramelessQuickWindow);
d->startSystemResize2(edges, pos);
}
FRAMELESSHELPER_END_NAMESPACE

View File

@ -26,7 +26,7 @@
#include "framelesshelperquick_global.h"
#include <QtCore/qobject.h>
#include <QtGui/qwindow.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qquickanchors_p_p.h>
QT_BEGIN_NAMESPACE
@ -55,7 +55,6 @@ public:
Q_INVOKABLE Q_NODISCARD bool isMinimized() const;
Q_INVOKABLE Q_NODISCARD bool isZoomed() const;
Q_INVOKABLE Q_NODISCARD bool isFullScreen() const;
Q_INVOKABLE Q_NODISCARD bool isFixedSize() const;
Q_INVOKABLE Q_NODISCARD QColor getFrameBorderColor() const;
Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderTop() const;
@ -65,39 +64,15 @@ public:
Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderHorizontalCenter() const;
Q_INVOKABLE Q_NODISCARD QQuickAnchorLine getTopBorderVerticalCenter() const;
Q_INVOKABLE void showEventHandler(QShowEvent *event);
Q_INVOKABLE Q_NODISCARD QuickGlobal::Options getOptions() const;
Q_INVOKABLE Q_NODISCARD QQuickItem *getTitleBarItem() const;
public Q_SLOTS:
void showMinimized2();
void toggleMaximized();
void toggleFullScreen();
void showSystemMenu(const QPoint &pos);
void startSystemMove2(const QPoint &pos);
void startSystemResize2(const Qt::Edges edges, const QPoint &pos);
void setTitleBarItem(QQuickItem *item);
void setHitTestVisible(QQuickItem *item);
void moveToDesktopCenter();
void setFixedSize(const bool value, const bool force = false);
void bringToFront();
void snapToTopBorder(QQuickItem *item, const QuickGlobal::Anchor itemAnchor, const QuickGlobal::Anchor topBorderAnchor);
void setOptions(const QuickGlobal::Options value);
void setSystemButton(QQuickItem *item, const QuickGlobal::SystemButtonType buttonType);
protected:
Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override;
private:
void initialize();
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const;
Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const;
Q_NODISCARD bool shouldIgnoreMouseEvents(const QPoint &pos) const;
Q_NODISCARD bool shouldDrawFrameBorder() const;
void setSystemButtonState(const QuickGlobal::SystemButtonType button, const QuickGlobal::ButtonState state);
private Q_SLOTS:
void updateTopBorderColor();
@ -107,13 +82,9 @@ private:
FramelessQuickWindow *q_ptr = nullptr;
QScopedPointer<QQuickRectangle> m_topBorderRectangle;
QScopedPointer<QQuickAnchors> m_topBorderAnchors;
QWindow::Visibility m_savedVisibility = QWindow::Windowed;
QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed;
Global::UserSettings m_settings = {};
Global::SystemParameters m_params = {};
bool m_windowExposed = false;
QPointer<QQuickItem> m_titleBarItem = nullptr;
QList<QQuickItem *> m_hitTestVisibleItems = {};
QuickGlobal::Options m_quickOptions = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -39,7 +39,6 @@ using namespace Global;
static constexpr const char FRAMELESSHELPER_PROP_NAME[] = "__wwx190_FramelessWidgetsHelper_instance";
static constexpr const char QTWIDGETS_MAINWINDOW_CLASS_NAME[] = "QMainWindow";
static constexpr const char FRAMELESSHELPER_SYSTEMBUTTON_CLASS_NAME[] = "StandardSystemButton";
FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetColorTemplate, "color: %1;")
FRAMELESSHELPER_STRING_CONSTANT2(StyleSheetBackgroundColorTemplate, "background-color: %1;")
@ -602,8 +601,6 @@ void FramelessWidgetsHelper::setSystemButtonState(const SystemButtonType button,
};
updateButtonState(widgetButton);
}
QMetaObject::invokeMethod(q, "systemButtonStateChanged",
Q_ARG(Global::SystemButtonType, button), Q_ARG(Global::ButtonState, state));
}
void FramelessWidgetsHelper::updateContentsMargins()