diff --git a/examples/quick/qml.qrc b/examples/quick/qml.qrc index a1bf191..5c3ce85 100644 --- a/examples/quick/qml.qrc +++ b/examples/quick/qml.qrc @@ -1,8 +1,5 @@ qml/MainWindow.qml - qml/MinimizeButton.qml - qml/MaximizeButton.qml - qml/CloseButton.qml diff --git a/examples/quick/qml/MainWindow.qml b/examples/quick/qml/MainWindow.qml index b98754e..583b13a 100644 --- a/examples/quick/qml/MainWindow.qml +++ b/examples/quick/qml/MainWindow.qml @@ -22,79 +22,32 @@ * SOFTWARE. */ -import QtQuick 2.12 -import QtQuick.Window 2.12 -import QtQuick.Controls 2.12 +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Controls 2.0 import org.wangwenx190.FramelessHelper 1.0 -Window { +FramelessWindow { id: window visible: true width: 800 height: 600 title: qsTr("Hello, World! - Qt Quick") - color: FramelessUtils.darkModeEnabled ? "#202020" : "#f0f0f0" + color: FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor + Component.onCompleted: { + FramelessHelper.setTitleBarItem(window, titleBar); + FramelessHelper.setHitTestVisible(window, minimizeButton, true); + FramelessHelper.setHitTestVisible(window, maximizeButton, true); + FramelessHelper.setHitTestVisible(window, closeButton, true); + } Timer { - id: timer interval: 500 running: true repeat: true onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") } - Rectangle { - id: titleBar - height: 30 - color: window.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor - : (FramelessUtils.darkModeEnabled ? "black" : "white")) - : (FramelessUtils.darkModeEnabled ? "#202020" : "white") - anchors { - top: parent.top - topMargin: windowTopBorder.height - left: parent.left - right: parent.right - } - - Text { - id: titleBarText - text: window.title - font.pointSize: 11 - color: window.active ? ((FramelessUtils.darkModeEnabled - || FramelessUtils.titleBarColorVisible) ? "white" : "black") : "darkGray" - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.verticalCenter: parent.verticalCenter - } - - Row { - anchors.top: parent.top - anchors.right: parent.right - - MinimizeButton { - id: minimizeButton - onClicked: FramelessUtils.showMinimized2(window) - } - - MaximizeButton { - id: maximizeButton - maximized: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen)) - onClicked: { - if (maximized) { - window.showNormal(); - } else { - window.showMaximized(); - } - } - } - - CloseButton { - id: closeButton - onClicked: window.close() - } - } - } - Label { id: timeLabel anchors.centerIn: parent @@ -105,22 +58,28 @@ Window { color: FramelessUtils.darkModeEnabled ? "white" : "black" } - Rectangle { - id: windowTopBorder + StandardTitleBar { + id: titleBar anchors { top: parent.top + topMargin: window.windowTopBorder.height left: parent.left right: parent.right } - height: ((window.visibility === Window.Windowed) && FramelessUtils.frameBorderVisible) ? 1 : 0 - color: window.active ? FramelessUtils.frameBorderActiveColor : FramelessUtils.frameBorderInactiveColor - } - - Component.onCompleted: { - FramelessHelper.addWindow(window); - FramelessHelper.setTitleBarItem(window, titleBar); - FramelessHelper.setHitTestVisible(window, minimizeButton, true); - FramelessHelper.setHitTestVisible(window, maximizeButton, true); - FramelessHelper.setHitTestVisible(window, closeButton, true); + active: window.active + maximized: (window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen) + title: window.title + minimizeButton { + id: minimizeButton + onClicked: FramelessUtils.showMinimized2(window) + } + maximizeButton { + id: maximizeButton + onClicked: FramelessUtils.toggleMaximize(window) + } + closeButton { + id: closeButton + onClicked: window.close() + } } } diff --git a/include/FramelessHelper/Quick/framelesshelperquick_global.h b/include/FramelessHelper/Quick/framelesshelperquick_global.h index 8f7c103..02b1e23 100644 --- a/include/FramelessHelper/Quick/framelesshelperquick_global.h +++ b/include/FramelessHelper/Quick/framelesshelperquick_global.h @@ -37,3 +37,7 @@ # endif # endif #endif + +FRAMELESSHELPER_BEGIN_NAMESPACE +[[maybe_unused]] static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; +FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Quick/framelessquickutils.h b/include/FramelessHelper/Quick/framelessquickutils.h index e5ff7ed..d893918 100644 --- a/include/FramelessHelper/Quick/framelessquickutils.h +++ b/include/FramelessHelper/Quick/framelessquickutils.h @@ -52,6 +52,10 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL) Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL) Q_PROPERTY(bool titleBarColorVisible READ titleBarColorVisible NOTIFY titleBarColorVisibleChanged FINAL) + Q_PROPERTY(QColor defaultSystemLightColor READ defaultSystemLightColor CONSTANT FINAL) + Q_PROPERTY(QColor defaultSystemDarkColor READ defaultSystemDarkColor CONSTANT FINAL) + Q_PROPERTY(QSizeF defaultSystemButtonSize READ defaultSystemButtonSize CONSTANT FINAL) + Q_PROPERTY(QSizeF defaultSystemButtonIconSize READ defaultSystemButtonIconSize CONSTANT FINAL) public: explicit FramelessQuickUtils(QObject *parent = nullptr); @@ -65,8 +69,13 @@ public: Q_NODISCARD static bool darkModeEnabled(); Q_NODISCARD static QColor systemAccentColor(); Q_NODISCARD static bool titleBarColorVisible(); + Q_NODISCARD static QColor defaultSystemLightColor(); + Q_NODISCARD static QColor defaultSystemDarkColor(); + Q_NODISCARD static QSizeF defaultSystemButtonSize(); + Q_NODISCARD static QSizeF defaultSystemButtonIconSize(); Q_INVOKABLE static void showMinimized2(QQuickWindow *window); + Q_INVOKABLE static void toggleMaximize(QQuickWindow *window); Q_INVOKABLE static void showSystemMenu(QQuickWindow *window, const QPoint &pos); Q_INVOKABLE static void startSystemMove2(QQuickWindow *window); Q_INVOKABLE static void startSystemResize2(QQuickWindow *window, const Qt::Edges edges); diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt index 203ed0b..31bf7d0 100644 --- a/src/quick/CMakeLists.txt +++ b/src/quick/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES ${INCLUDE_PREFIX}/framelesshelperimageprovider.h ${INCLUDE_PREFIX}/framelessquickeventfilter.h ${INCLUDE_PREFIX}/framelesshelper_quick.h + framelesshelperquick.qrc framelesshelper_quick.cpp framelessquickhelper.cpp framelessquickutils.cpp diff --git a/src/quick/framelesshelper_quick.cpp b/src/quick/framelesshelper_quick.cpp index a842e0d..501b7a3 100644 --- a/src/quick/framelesshelper_quick.cpp +++ b/src/quick/framelesshelper_quick.cpp @@ -28,9 +28,15 @@ #include "framelessquickhelper.h" #include "framelessquickutils.h" -FRAMELESSHELPER_BEGIN_NAMESPACE +// The "Q_INIT_RESOURCE()" macro can't be used inside a namespace, +// the official workaround is to wrap it into a global function +// and call the wrapper function inside the namespace. +static inline void initResource() +{ + Q_INIT_RESOURCE(framelesshelperquick); +} -static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; +FRAMELESSHELPER_BEGIN_NAMESPACE void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) { @@ -49,6 +55,12 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) Q_UNUSED(scriptEngine); return new FramelessQuickUtils; }); + initResource(); + qmlRegisterType(QUrl(QStringLiteral("qrc:///qml/MinimizeButton.qml")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton"); + qmlRegisterType(QUrl(QStringLiteral("qrc:///qml/MaximizeButton.qml")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton"); + qmlRegisterType(QUrl(QStringLiteral("qrc:///qml/CloseButton.qml")), FRAMELESSHELPER_QUICK_URI, 1, 0, "CloseButton"); + qmlRegisterType(QUrl(QStringLiteral("qrc:///qml/StandardTitleBar.qml")), FRAMELESSHELPER_QUICK_URI, 1, 0, "StandardTitleBar"); + qmlRegisterType(QUrl(QStringLiteral("qrc:///qml/FramelessWindow.qml")), FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessWindow"); } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/framelesshelperquick.qrc b/src/quick/framelesshelperquick.qrc new file mode 100644 index 0000000..9282694 --- /dev/null +++ b/src/quick/framelesshelperquick.qrc @@ -0,0 +1,10 @@ + + + + qml/CloseButton.qml + qml/FramelessWindow.qml + qml/MaximizeButton.qml + qml/MinimizeButton.qml + qml/StandardTitleBar.qml + + diff --git a/src/quick/framelessquickutils.cpp b/src/quick/framelessquickutils.cpp index 04cba61..3fde495 100644 --- a/src/quick/framelessquickutils.cpp +++ b/src/quick/framelessquickutils.cpp @@ -120,6 +120,26 @@ bool FramelessQuickUtils::titleBarColorVisible() #endif } +QColor FramelessQuickUtils::defaultSystemLightColor() +{ + return kDefaultSystemLightColor; +} + +QColor FramelessQuickUtils::defaultSystemDarkColor() +{ + return kDefaultSystemDarkColor; +} + +QSizeF FramelessQuickUtils::defaultSystemButtonSize() +{ + return kDefaultSystemButtonSize; +} + +QSizeF FramelessQuickUtils::defaultSystemButtonIconSize() +{ + return kDefaultSystemButtonIconSize; +} + void FramelessQuickUtils::showMinimized2(QQuickWindow *window) { Q_ASSERT(window); @@ -137,6 +157,20 @@ void FramelessQuickUtils::showMinimized2(QQuickWindow *window) #endif } +void FramelessQuickUtils::toggleMaximize(QQuickWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } + const QQuickWindow::Visibility visibility = window->visibility(); + if ((visibility == QQuickWindow::Maximized) || (visibility == QQuickWindow::FullScreen)) { + window->showNormal(); + } else { + window->showMaximized(); + } +} + void FramelessQuickUtils::showSystemMenu(QQuickWindow *window, const QPoint &pos) { Q_ASSERT(window); diff --git a/examples/quick/qml/CloseButton.qml b/src/quick/qml/CloseButton.qml similarity index 82% rename from examples/quick/qml/CloseButton.qml rename to src/quick/qml/CloseButton.qml index 9618cc7..2fd1c6b 100644 --- a/examples/quick/qml/CloseButton.qml +++ b/src/quick/qml/CloseButton.qml @@ -28,30 +28,27 @@ import org.wangwenx190.FramelessHelper 1.0 Button { id: button + implicitWidth: FramelessUtils.defaultSystemButtonSize.width + implicitHeight: FramelessUtils.defaultSystemButtonSize.height + contentItem: Item { + implicitWidth: FramelessUtils.defaultSystemButtonIconSize.width + implicitHeight: FramelessUtils.defaultSystemButtonIconSize.height - implicitHeight: 30 - implicitWidth: implicitHeight * 1.5 + Image { + anchors.centerIn: parent + source: (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) + ? "image://framelesshelper/dark/close" : "image://framelesshelper/light/close" + } + } + background: Rectangle { + visible: button.hovered + color: "red" + opacity: 0.5 + } ToolTip { visible: button.hovered && !button.down delay: Qt.styleHints.mousePressAndHoldInterval text: qsTr("Close") } - - contentItem: Item { - implicitWidth: 16 - implicitHeight: implicitWidth - - Image { - anchors.centerIn: parent - source: FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible - ? "image://framelesshelper/dark/close" : "image://framelesshelper/light/close" - } - } - - background: Rectangle { - visible: button.hovered - color: "red" - opacity: 0.5 - } } diff --git a/src/quick/qml/FramelessWindow.qml b/src/quick/qml/FramelessWindow.qml new file mode 100644 index 0000000..2ac0e62 --- /dev/null +++ b/src/quick/qml/FramelessWindow.qml @@ -0,0 +1,45 @@ +/* + * 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. + */ + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import org.wangwenx190.FramelessHelper 1.0 + +Window { + property alias windowTopBorder: windowTopBorder + + id: window + Component.onCompleted: FramelessHelper.addWindow(window) + + Rectangle { + id: windowTopBorder + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: ((window.visibility === Window.Windowed) && FramelessUtils.frameBorderVisible) ? 1 : 0 + color: window.active ? FramelessUtils.frameBorderActiveColor : FramelessUtils.frameBorderInactiveColor + } +} diff --git a/examples/quick/qml/MaximizeButton.qml b/src/quick/qml/MaximizeButton.qml similarity index 80% rename from examples/quick/qml/MaximizeButton.qml rename to src/quick/qml/MaximizeButton.qml index 5598b96..33ddfc1 100644 --- a/examples/quick/qml/MaximizeButton.qml +++ b/src/quick/qml/MaximizeButton.qml @@ -27,36 +27,33 @@ import QtQuick.Controls 2.0 import org.wangwenx190.FramelessHelper 1.0 Button { - id: button - - implicitHeight: 30 - implicitWidth: implicitHeight * 1.5 - property bool maximized: false + id: button + implicitWidth: FramelessUtils.defaultSystemButtonSize.width + implicitHeight: FramelessUtils.defaultSystemButtonSize.height + contentItem: Item { + implicitWidth: FramelessUtils.defaultSystemButtonIconSize.width + implicitHeight: FramelessUtils.defaultSystemButtonIconSize.height + + Image { + anchors.centerIn: parent + source: button.maximized ? + ((FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) + ? "image://framelesshelper/dark/restore" : "image://framelesshelper/light/restore") : + ((FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) + ? "image://framelesshelper/dark/maximize" : "image://framelesshelper/light/maximize") + } + } + background: Rectangle { + visible: button.hovered + color: "gray" + opacity: 0.5 + } + ToolTip { visible: button.hovered && !button.down delay: Qt.styleHints.mousePressAndHoldInterval text: button.maximized ? qsTr("Restore") : qsTr("Maximize") } - - contentItem: Item { - implicitWidth: 16 - implicitHeight: implicitWidth - - Image { - anchors.centerIn: parent - source: button.maximized ? - (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible - ? "image://framelesshelper/dark/restore" : "image://framelesshelper/light/restore") : - (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible - ? "image://framelesshelper/dark/maximize" : "image://framelesshelper/light/maximize") - } - } - - background: Rectangle { - visible: button.hovered - color: "gray" - opacity: 0.5 - } } diff --git a/examples/quick/qml/MinimizeButton.qml b/src/quick/qml/MinimizeButton.qml similarity index 82% rename from examples/quick/qml/MinimizeButton.qml rename to src/quick/qml/MinimizeButton.qml index bb8c83f..6e53e98 100644 --- a/examples/quick/qml/MinimizeButton.qml +++ b/src/quick/qml/MinimizeButton.qml @@ -28,30 +28,27 @@ import org.wangwenx190.FramelessHelper 1.0 Button { id: button + implicitWidth: FramelessUtils.defaultSystemButtonSize.width + implicitHeight: FramelessUtils.defaultSystemButtonSize.height + contentItem: Item { + implicitWidth: FramelessUtils.defaultSystemButtonIconSize.width + implicitHeight: FramelessUtils.defaultSystemButtonIconSize.height - implicitHeight: 30 - implicitWidth: implicitHeight * 1.5 + Image { + anchors.centerIn: parent + source: (FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible) + ? "image://framelesshelper/dark/minimize" : "image://framelesshelper/light/minimize" + } + } + background: Rectangle { + visible: button.hovered + color: "gray" + opacity: 0.5 + } ToolTip { visible: button.hovered && !button.down delay: Qt.styleHints.mousePressAndHoldInterval text: qsTr("Minimize") } - - contentItem: Item { - implicitWidth: 16 - implicitHeight: implicitWidth - - Image { - anchors.centerIn: parent - source: FramelessUtils.darkModeEnabled || FramelessUtils.titleBarColorVisible - ? "image://framelesshelper/dark/minimize" : "image://framelesshelper/light/minimize" - } - } - - background: Rectangle { - visible: button.hovered - color: "gray" - opacity: 0.5 - } } diff --git a/src/quick/qml/StandardTitleBar.qml b/src/quick/qml/StandardTitleBar.qml new file mode 100644 index 0000000..8d22071 --- /dev/null +++ b/src/quick/qml/StandardTitleBar.qml @@ -0,0 +1,74 @@ +/* + * 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. + */ + +import QtQuick 2.0 +import QtQuick.Controls 2.0 +import org.wangwenx190.FramelessHelper 1.0 + +Rectangle { + property bool active: true + property bool maximized: false + property alias title: windowTitleLabel.text + property alias minimizeButton: minimizeButton + property alias maximizeButton: maximizeButton + property alias closeButton: closeButton + + id: titleBar + height: FramelessUtils.titleBarHeight + color: titleBar.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor + : (FramelessUtils.darkModeEnabled ? "black" : "white")) + : (FramelessUtils.darkModeEnabled ? FramelessUtils.defaultSystemDarkColor : "white") + + Text { + id: windowTitleLabel + font.pointSize: 11 + color: titleBar.active ? ((FramelessUtils.darkModeEnabled + || FramelessUtils.titleBarColorVisible) ? "white" : "black") : "darkGray" + anchors { + left: parent.left + leftMargin: 10 + verticalCenter: parent.verticalCenter + } + } + + Row { + anchors { + top: parent.top + right: parent.right + } + + MinimizeButton { + id: minimizeButton + } + + MaximizeButton { + id: maximizeButton + maximized: titleBar.maximized + } + + CloseButton { + id: closeButton + } + } +}