From 556741cfb17032dbe648adbe3ae2aa808e8913dd Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Sun, 13 Mar 2022 12:14:08 +0800 Subject: [PATCH] wip Signed-off-by: Yuhang Zhao <2546789017@qq.com> --- CMakeLists.txt | 32 +-- examples/CMakeLists.txt | 2 +- examples/mainwindow/CMakeLists.txt | 2 +- examples/quick/CMakeLists.txt | 2 +- examples/quick/main.cpp | 89 ++----- examples/quick/qml.qrc | 2 +- examples/quick/qml/CloseButton.qml | 28 ++- .../quick/qml/{main.qml => MainWindow.qml} | 93 +++----- examples/quick/qml/MaximizeButton.qml | 29 ++- examples/quick/qml/MinimizeButton.qml | 27 ++- examples/widget/CMakeLists.txt | 2 +- examples/widget/widget.cpp | 8 +- framelesshelper.cpp | 3 +- framelesshelper_win32.cpp | 2 +- framelessquickhelper.cpp | 221 +++++++++++++----- framelessquickhelper.h | 68 ++++-- framelesswindowsmanager.cpp | 28 +-- framelesswindowsmanager.h | 1 + 18 files changed, 355 insertions(+), 284 deletions(-) rename examples/quick/qml/{main.qml => MainWindow.qml} (56%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 208c04c..3d99c06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,8 @@ cmake_minimum_required(VERSION 3.20) project(FramelessHelper LANGUAGES CXX) -option(BUILD_EXAMPLES "Build examples." ON) - -if(NOT DEFINED BUILD_SHARED_LIBS) - set(BUILD_SHARED_LIBS ON) -endif() +option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF) +option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -20,8 +17,8 @@ set(CMAKE_AUTORCC ON) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED) -#find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick) -#find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick) set(SOURCES framelesshelper_global.h @@ -34,12 +31,12 @@ set(SOURCES utilities.cpp ) -#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick) +if(TARGET Qt${QT_VERSION_MAJOR}::Quick) list(APPEND SOURCES framelessquickhelper.h framelessquickhelper.cpp ) -endif()]] +endif() if(WIN32) list(APPEND SOURCES @@ -56,15 +53,18 @@ elseif(UNIX) list(APPEND SOURCES utilities_linux.cpp) endif() -if(WIN32 AND BUILD_SHARED_LIBS) +if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC) enable_language(RC) list(APPEND SOURCES framelesshelper.rc) endif() -add_library(${PROJECT_NAME} ${SOURCES}) -add_library(wangwenx190::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +if(FRAMELESSHELPER_BUILD_STATIC) + add_library(${PROJECT_NAME} STATIC ${SOURCES}) +else() + add_library(${PROJECT_NAME} SHARED ${SOURCES}) +endif() -if(NOT BUILD_SHARED_LIBS) +if(FRAMELESSHELPER_BUILD_STATIC) target_compile_definitions(${PROJECT_NAME} PUBLIC FRAMELESSHELPER_STATIC ) @@ -85,16 +85,16 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::GuiPrivate ) -#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick) +if(TARGET Qt${QT_VERSION_MAJOR}::Quick) target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Quick ) -endif()]] +endif() target_include_directories(${PROJECT_NAME} PUBLIC "$" ) -if(BUILD_EXAMPLES) +if(FRAMELESSHELPER_BUILD_EXAMPLES) add_subdirectory(examples) endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3b02bc1..0cef64a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,5 +7,5 @@ if(TARGET Qt${QT_VERSION_MAJOR}::Widgets) endif() if(TARGET Qt${QT_VERSION_MAJOR}::Quick) - #add_subdirectory(quick) + add_subdirectory(quick) endif() diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index aeb24c7..a705851 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -25,7 +25,7 @@ add_executable(MainWindow WIN32 ${SOURCES}) target_link_libraries(MainWindow PRIVATE Qt${QT_VERSION_MAJOR}::Widgets - wangwenx190::FramelessHelper + FramelessHelper ) target_compile_definitions(MainWindow PRIVATE diff --git a/examples/quick/CMakeLists.txt b/examples/quick/CMakeLists.txt index ffb5b2e..1876bbd 100644 --- a/examples/quick/CMakeLists.txt +++ b/examples/quick/CMakeLists.txt @@ -23,7 +23,7 @@ add_executable(Quick WIN32 ${SOURCES}) target_link_libraries(Quick PRIVATE Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::QuickControls2 - wangwenx190::FramelessHelper + FramelessHelper ) target_compile_definitions(Quick PRIVATE diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index d6c49d2..aaf42e0 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -22,77 +22,20 @@ * SOFTWARE. */ -#include "../../utilities.h" -#include "../../framelessquickhelper.h" #include #include #include +#include FRAMELESSHELPER_USE_NAMESPACE -static constexpr const char qtquicknamespace[] = "wangwenx190.Utils"; - -class UtilFunctions : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY_MOVE(UtilFunctions) - Q_PROPERTY(bool isWindowsHost READ isWindowsHost CONSTANT) - Q_PROPERTY(bool isWindows10OrGreater READ isWindows10OrGreater CONSTANT) - Q_PROPERTY(bool isWindows11OrGreater READ isWindows11OrGreater CONSTANT) - Q_PROPERTY(QColor activeFrameBorderColor READ activeFrameBorderColor CONSTANT) - Q_PROPERTY(QColor inactiveFrameBorderColor READ inactiveFrameBorderColor CONSTANT) - Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT) - -public: - explicit UtilFunctions(QObject *parent = nullptr) : QObject(parent) {} - ~UtilFunctions() override = default; - - inline bool isWindowsHost() const { -#ifdef Q_OS_WINDOWS - return true; -#else - return false; -#endif - } - - inline bool isWindows10OrGreater() const { -#ifdef Q_OS_WINDOWS - return Utilities::isWin10OrGreater(); -#else - return false; -#endif - } - - inline bool isWindows11OrGreater() const { -#ifdef Q_OS_WINDOWS - return Utilities::isWin11OrGreater(); -#else - return false; -#endif - } - - inline QColor activeFrameBorderColor() const { - const ColorizationArea area = Utilities::getColorizationArea(); - const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder) - || (area == ColorizationArea::All)); - return (colorizedBorder ? Utilities::getColorizationColor() : Qt::black); - } - - inline QColor inactiveFrameBorderColor() const { - return Qt::darkGray; - } - - inline qreal frameBorderThickness() const { - return 1.0; - } -}; +static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; int main(int argc, char *argv[]) { - QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); @@ -100,6 +43,9 @@ int main(int argc, char *argv[]) QGuiApplication application(argc, argv); + QScopedPointer framelessHelper(new FramelessQuickHelper); + QScopedPointer framelessUtils(new FramelessQuickUtils); + QQmlApplicationEngine engine; #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -108,14 +54,10 @@ int main(int argc, char *argv[]) QQuickStyle::setStyle(QStringLiteral("Default")); #endif - qmlRegisterSingletonType(qtquicknamespace, 1, 0, "Utils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine); - Q_UNUSED(scriptEngine); - return new UtilFunctions(); - }); - qmlRegisterType(qtquicknamespace, 1, 0, "FramelessHelper"); + qmlRegisterSingletonInstance(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessHelper", framelessHelper.data()); + qmlRegisterSingletonInstance(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessUtils", framelessUtils.data()); - const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/main.qml")); + const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/MainWindow.qml")); const QMetaObject::Connection connection = QObject::connect( &engine, &QQmlApplicationEngine::objectCreated, @@ -124,17 +66,16 @@ int main(int argc, char *argv[]) if (url != mainQmlUrl) { return; } - if (!object) { - QGuiApplication::exit(-1); - } else { + if (object) { QObject::disconnect(connection); + + } else { + QCoreApplication::exit(-1); } }, Qt::QueuedConnection); engine.load(mainQmlUrl); - return QGuiApplication::exec(); + return QCoreApplication::exec(); } - -#include "main.moc" diff --git a/examples/quick/qml.qrc b/examples/quick/qml.qrc index d379b89..a1bf191 100644 --- a/examples/quick/qml.qrc +++ b/examples/quick/qml.qrc @@ -1,6 +1,6 @@ - qml/main.qml + qml/MainWindow.qml qml/MinimizeButton.qml qml/MaximizeButton.qml qml/CloseButton.qml diff --git a/examples/quick/qml/CloseButton.qml b/examples/quick/qml/CloseButton.qml index bdb31c3..509591d 100644 --- a/examples/quick/qml/CloseButton.qml +++ b/examples/quick/qml/CloseButton.qml @@ -24,25 +24,33 @@ import QtQuick 2.0 import QtQuick.Controls 2.0 +import org.wangwenx190.FramelessHelper 1.0 Button { id: button - implicitWidth: 45 implicitHeight: 30 + implicitWidth: implicitHeight * 1.5 - ToolTip.visible: hovered && !down - ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval - ToolTip.text: qsTr("Close") + ToolTip { + visible: button.hovered && !button.down + delay: Qt.styleHints.mousePressAndHoldInterval + text: qsTr("Close") + } - contentItem: Image { - anchors.fill: parent - source: button.down - || button.hovered ? "qrc:/images/button_close_white.svg" : "qrc:/images/button_close_black.svg" + contentItem: Item { + implicitWidth: 16 + implicitHeight: implicitWidth + + Image { + anchors.centerIn: parent + source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-close.svg" : "qrc:/images/dark/chrome-close.svg" + } } background: Rectangle { - visible: button.down || button.hovered - color: button.down ? "#8c0a15" : (button.hovered ? "#e81123" : "transparent") + visible: button.hovered + color: "red" + opacity: 0.5 } } diff --git a/examples/quick/qml/main.qml b/examples/quick/qml/MainWindow.qml similarity index 56% rename from examples/quick/qml/main.qml rename to examples/quick/qml/MainWindow.qml index 6d4634d..7cde9ca 100644 --- a/examples/quick/qml/main.qml +++ b/examples/quick/qml/MainWindow.qml @@ -22,10 +22,10 @@ * SOFTWARE. */ -import QtQuick 2.0 -import QtQuick.Window 2.0 -import QtQuick.Controls 2.0 -import wangwenx190.Utils 1.0 +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import org.wangwenx190.FramelessHelper 1.0 Window { id: window @@ -33,14 +33,7 @@ Window { width: 800 height: 600 title: qsTr("Hello, World!") - color: "#f0f0f0" - - property real _flh_margin: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen)) ? 0 : (Utils.frameBorderThickness / Screen.devicePixelRatio) - property var _win_prev_state: null - - FramelessHelper { - id: framelessHelper - } + color: FramelessUtils.darkModeEnabled ? "#202020" : "#f0f0f0" Timer { id: timer @@ -52,35 +45,46 @@ Window { Rectangle { id: titleBar - height: framelessHelper.titleBarHeight - color: "white" + height: 30 + color: window.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor : + (FramelessUtils.darkModeEnabled ? "white" : "black")) : + (FramelessUtils.darkModeEnabled ? "#202020" : "white") anchors { top: parent.top - topMargin: window._flh_margin + topMargin: windowTopBorder.height left: parent.left - leftMargin: window._flh_margin right: parent.right - rightMargin: window._flh_margin } Text { id: titleBarText text: window.title font.pointSize: 13 - color: window.active ? "black" : "gray" + color: window.active ? (FramelessUtils.darkModeEnabled ? "white" : "black") : "darkGray" anchors.left: parent.left anchors.leftMargin: 15 anchors.verticalCenter: parent.verticalCenter } + MouseArea { + anchors.fill: parent + anchors.rightMargin: 30 * 1.5 * 3 + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onDoubleClicked: maximizeButton.clicked() + onPositionChanged: { + if (containsPress && (window.visibility !== Window.FullScreen)) { + FramelessUtils.startSystemMove2(window); + } + } + } + Row { anchors.top: parent.top anchors.right: parent.right MinimizeButton { - id: minimizeButton - onClicked: framelessHelper.showMinimized() - Component.onCompleted: framelessHelper.setHitTestVisible(minimizeButton, true) + onClicked: FramelessUtils.showMinimized2(window) } MaximizeButton { @@ -88,18 +92,15 @@ Window { maximized: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen)) onClicked: { if (maximized) { - window.showNormal() + window.showNormal(); } else { - window.showMaximized() + window.showMaximized(); } } - Component.onCompleted: framelessHelper.setHitTestVisible(maximizeButton, true) } CloseButton { - id: closeButton onClicked: window.close() - Component.onCompleted: framelessHelper.setHitTestVisible(closeButton, true) } } } @@ -111,41 +112,19 @@ Window { pointSize: 70 bold: true } - } - - Button { - id: fullScreenButton - anchors { - horizontalCenter: parent.horizontalCenter - top: timeLabel.bottom - topMargin: 15 - } - property bool _full: window.visibility === Window.FullScreen - text: _full ? qsTr("Exit FullScreen") : qsTr("Enter FullScreen") - onClicked: { - if (_full) { - if (_win_prev_state == Window.Maximized) { - window.showMaximized() - } else if (_win_prev_state == Window.Windowed) { - window.showNormal() - } - } else { - _win_prev_state = window.visibility - window.showFullScreen() - } - } + color: FramelessUtils.darkModeEnabled ? "white" : "black" } Rectangle { - id: windowFrame - anchors.fill: parent - color: "transparent" - visible: !Utils.isWindows11OrGreater - border { - color: window.active ? Utils.activeFrameBorderColor : Utils.inactiveFrameBorderColor - width: window._flh_margin + 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 } - Component.onCompleted: framelessHelper.removeWindowFrame() + Component.onCompleted: FramelessHelper.addWindow(window) } diff --git a/examples/quick/qml/MaximizeButton.qml b/examples/quick/qml/MaximizeButton.qml index cf0d316..ba4dada 100644 --- a/examples/quick/qml/MaximizeButton.qml +++ b/examples/quick/qml/MaximizeButton.qml @@ -24,26 +24,37 @@ import QtQuick 2.0 import QtQuick.Controls 2.0 +import org.wangwenx190.FramelessHelper 1.0 Button { id: button - implicitWidth: 45 implicitHeight: 30 + implicitWidth: implicitHeight * 1.5 property bool maximized: false - ToolTip.visible: hovered && !down - ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval - ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize") + ToolTip { + visible: button.hovered && !button.down + delay: Qt.styleHints.mousePressAndHoldInterval + text: button.maximized ? qsTr("Restore") : qsTr("Maximize") + } - contentItem: Image { - anchors.fill: parent - source: maximized ? "qrc:/images/button_restore_black.svg" : "qrc:/images/button_maximize_black.svg" + contentItem: Item { + implicitWidth: 16 + implicitHeight: implicitWidth + + Image { + anchors.centerIn: parent + source: button.maximized ? + (FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-restore.svg" : "qrc:/images/dark/chrome-restore.svg") : + (FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-maximize.svg" : "qrc:/images/dark/chrome-maximize.svg") + } } background: Rectangle { - visible: button.down || button.hovered - color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent") + visible: button.hovered + color: "gray" + opacity: 0.5 } } diff --git a/examples/quick/qml/MinimizeButton.qml b/examples/quick/qml/MinimizeButton.qml index 496c7f6..821a3e2 100644 --- a/examples/quick/qml/MinimizeButton.qml +++ b/examples/quick/qml/MinimizeButton.qml @@ -24,24 +24,33 @@ import QtQuick 2.0 import QtQuick.Controls 2.0 +import org.wangwenx190.FramelessHelper 1.0 Button { id: button - implicitWidth: 45 implicitHeight: 30 + implicitWidth: implicitHeight * 1.5 - ToolTip.visible: hovered && !down - ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval - ToolTip.text: qsTr("Minimize") + ToolTip { + visible: button.hovered && !button.down + delay: Qt.styleHints.mousePressAndHoldInterval + text: qsTr("Minimize") + } - contentItem: Image { - anchors.fill: parent - source: "qrc:/images/button_minimize_black.svg" + contentItem: Item { + implicitWidth: 16 + implicitHeight: implicitWidth + + Image { + anchors.centerIn: parent + source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-minimize.svg" : "qrc:/images/dark/chrome-minimize.svg" + } } background: Rectangle { - visible: button.down || button.hovered - color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent") + visible: button.hovered + color: "gray" + opacity: 0.5 } } diff --git a/examples/widget/CMakeLists.txt b/examples/widget/CMakeLists.txt index d0e04fe..967477b 100644 --- a/examples/widget/CMakeLists.txt +++ b/examples/widget/CMakeLists.txt @@ -23,7 +23,7 @@ add_executable(Widget WIN32 ${SOURCES}) target_link_libraries(Widget PRIVATE Qt${QT_VERSION_MAJOR}::Widgets - wangwenx190::FramelessHelper + FramelessHelper ) target_compile_definitions(Widget PRIVATE diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index d25a6c7..b1f2c67 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -35,7 +35,7 @@ FRAMELESSHELPER_USE_NAMESPACE static const QColor systemLightColor = QStringLiteral("#f0f0f0"); -static const QColor systemDarkColor = QColor::fromRgb(32, 32, 32); +static const QColor systemDarkColor = QStringLiteral("#202020"); static const QString mainStyleSheet = QStringLiteral(R"(#MainWidget { background-color: %1; @@ -132,7 +132,7 @@ void Widget::paintEvent(QPaintEvent *event) painter.save(); QPen pen = {}; pen.setColor(Utilities::getFrameBorderColor(isActiveWindow())); - const int frameBorderThickness = Utilities::getFrameBorderThickness(winId(), false); + const int frameBorderThickness = 1; pen.setWidth(frameBorderThickness); painter.setPen(pen); painter.drawLine(0, frameBorderThickness, width(), frameBorderThickness); @@ -174,7 +174,7 @@ void Widget::initFramelessHelperOnce() void Widget::setupUi() { - const int titleBarHeight = /*Utilities::getTitleBarHeight(winId(), false)*/30; + const int titleBarHeight = 30; const QSize systemButtonSize = {int(qRound(qreal(titleBarHeight) * 1.5)), titleBarHeight}; const QSize systemIconSize = {16, 16}; setObjectName(QStringLiteral("MainWidget")); @@ -327,7 +327,7 @@ void Widget::resetContentsMargins() { #ifdef Q_OS_WIN if (Utilities::isWin10OrGreater()) { - const int frameBorderThickness = Utilities::getFrameBorderThickness(winId(), false); + const int frameBorderThickness = 1; setContentsMargins(0, frameBorderThickness, 0, 0); } #endif diff --git a/framelesshelper.cpp b/framelesshelper.cpp index ee21e36..cf4a8bb 100644 --- a/framelesshelper.cpp +++ b/framelesshelper.cpp @@ -66,8 +66,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) } const QEvent::Type type = event->type(); // We are only interested in mouse events. - if ((type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseButtonPress) - && (type != QEvent::MouseMove)) { + if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseMove)) { return false; } const auto window = qobject_cast(object); diff --git a/framelesshelper_win32.cpp b/framelesshelper_win32.cpp index da42157..daa409b 100644 --- a/framelesshelper_win32.cpp +++ b/framelesshelper_win32.cpp @@ -67,7 +67,7 @@ void FramelessHelperWin::addWindow(QWindow *window) qApp->installNativeEventFilter(g_helper()->instance.data()); } const WId winId = window->winId(); - Utilities::fixupQtInternals(winId); + //Utilities::fixupQtInternals(winId); Utilities::updateInternalWindowFrameMargins(window, true); Utilities::updateWindowFrameMargins(winId, false); const bool dark = Utilities::shouldAppsUseDarkMode(); diff --git a/framelessquickhelper.cpp b/framelessquickhelper.cpp index be3da62..11a5660 100644 --- a/framelessquickhelper.cpp +++ b/framelessquickhelper.cpp @@ -23,85 +23,180 @@ */ #include "framelessquickhelper.h" +#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) +# include +# include +#endif #include "framelesswindowsmanager.h" -#include +#include "utilities.h" #ifdef Q_OS_WINDOWS -#include "framelesshelper_windows.h" +# include #endif FRAMELESSHELPER_BEGIN_NAMESPACE -FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent) -{ -} +FramelessQuickHelper::FramelessQuickHelper(QObject *parent) : QObject(parent) {} -qreal FramelessQuickHelper::resizeBorderThickness() const -{ - return FramelessWindowsManager::getResizeBorderThickness(window()); -} +FramelessQuickHelper::~FramelessQuickHelper() = default; -void FramelessQuickHelper::setResizeBorderThickness(const qreal val) +void FramelessQuickHelper::addWindow(QWindow *window) { - FramelessWindowsManager::setResizeBorderThickness(window(), qRound(val)); - Q_EMIT resizeBorderThicknessChanged(val); -} - -qreal FramelessQuickHelper::titleBarHeight() const -{ - return FramelessWindowsManager::getTitleBarHeight(window()); -} - -void FramelessQuickHelper::setTitleBarHeight(const qreal val) -{ - FramelessWindowsManager::setTitleBarHeight(window(), qRound(val)); - Q_EMIT titleBarHeightChanged(val); -} - -bool FramelessQuickHelper::resizable() const -{ - return FramelessWindowsManager::getResizable(window()); -} - -void FramelessQuickHelper::setResizable(const bool val) -{ - FramelessWindowsManager::setResizable(window(), val); - Q_EMIT resizableChanged(val); -} - -void FramelessQuickHelper::removeWindowFrame() -{ - FramelessWindowsManager::addWindow(window()); -} - -void FramelessQuickHelper::bringBackWindowFrame() -{ - FramelessWindowsManager::removeWindow(window()); -} - -bool FramelessQuickHelper::isWindowFrameless() const -{ - return FramelessWindowsManager::isWindowFrameless(window()); -} - -void FramelessQuickHelper::setHitTestVisible(QQuickItem *item, const bool visible) -{ - Q_ASSERT(item); - if (!item) { + Q_ASSERT(window); + if (!window) { return; } - FramelessWindowsManager::setHitTestVisible(window(), item, visible); + FramelessWindowsManager::addWindow(window); } -void FramelessQuickHelper::showMinimized() +void FramelessQuickHelper::removeWindow(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } + FramelessWindowsManager::removeWindow(window); +} + +FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent) +{ + connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::themeChanged, this, [this](){ + Q_EMIT frameBorderActiveColorChanged(); + Q_EMIT frameBorderInactiveColorChanged(); + Q_EMIT darkModeEnabledChanged(); + Q_EMIT systemAccentColorChanged(); + Q_EMIT titleBarColorVisibleChanged(); + }); +} + +FramelessQuickUtils::~FramelessQuickUtils() = default; + +qreal FramelessQuickUtils::titleBarHeight() +{ + return 30; +} + +bool FramelessQuickUtils::frameBorderVisible() { #ifdef Q_OS_WINDOWS - // Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711 - // Don't use "SW_SHOWMINIMIZED" because it will activate the current - // window instead of the next window in the Z order, that's not the - // native behavior of Windows applications. - ShowWindow(reinterpret_cast(window()->winId()), SW_MINIMIZE); + return (Utilities::isWin10OrGreater() && !Utilities::isWin11OrGreater()); #else - window()->showMinimized(); + return false; +#endif +} + +qreal FramelessQuickUtils::frameBorderThickness() +{ + return 1; +} + +QColor FramelessQuickUtils::frameBorderActiveColor() +{ +#ifdef Q_OS_WINDOWS + return Utilities::getFrameBorderColor(true); +#else + return {}; +#endif +} + +QColor FramelessQuickUtils::frameBorderInactiveColor() +{ +#ifdef Q_OS_WINDOWS + return Utilities::getFrameBorderColor(false); +#else + return {}; +#endif +} + +bool FramelessQuickUtils::darkModeEnabled() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) + if (const QPlatformTheme * const theme = QGuiApplicationPrivate::platformTheme()) { + return (theme->appearance() == QPlatformTheme::Appearance::Dark); + } + return false; +#else +# ifdef Q_OS_WINDOWS + return Utilities::shouldAppsUseDarkMode(); +# else + return false; +# endif +#endif +} + +QColor FramelessQuickUtils::systemAccentColor() +{ +#ifdef Q_OS_WINDOWS + return Utilities::getDwmColorizationColor(); +#else + return {}; +#endif +} + +bool FramelessQuickUtils::titleBarColorVisible() +{ +#ifdef Q_OS_WINDOWS + if (!Utilities::isWin10OrGreater()) { + return false; + } + const DwmColorizationArea area = Utilities::getDwmColorizationArea(); + return ((area == DwmColorizationArea::TitleBar_WindowBorder) || (area == DwmColorizationArea::All)); +#else + return false; +#endif +} + +void FramelessQuickUtils::showMinimized2(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } +#ifdef Q_OS_WINDOWS + // Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711 + // Don't use "SW_SHOWMINIMIZED" because it will activate the current window + // instead of the next window in the Z order, which is not the default behavior + // of native Win32 applications. + ShowWindow(reinterpret_cast(window->winId()), SW_MINIMIZE); +#else + window->showMinimized(); +#endif +} + +void FramelessQuickUtils::showSystemMenu(const QPointF &pos) +{ + +} + +void FramelessQuickUtils::startSystemMove2(QWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + window->startSystemMove(); +#else +# ifdef Q_OS_WINDOWS + Utilities::startSystemMove(window); +# endif +#endif +} + +void FramelessQuickUtils::startSystemResize2(QWindow *window, const Qt::Edges edges) +{ + Q_ASSERT(window); + if (!window) { + return; + } + if (edges == Qt::Edges{}) { + return; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + window->startSystemResize(edges); +#else +# ifdef Q_OS_WINDOWS + Utilities::startSystemResize(window, edges); +# endif #endif } diff --git a/framelessquickhelper.h b/framelessquickhelper.h index ec66901..c9b5bff 100644 --- a/framelessquickhelper.h +++ b/framelessquickhelper.h @@ -25,46 +25,72 @@ #pragma once #include "framelesshelper_global.h" -#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QWindow; +QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE -class FRAMELESSHELPER_API FramelessQuickHelper : public QQuickItem +class FRAMELESSHELPER_API FramelessQuickHelper : public QObject { Q_OBJECT Q_DISABLE_COPY_MOVE(FramelessQuickHelper) #ifdef QML_NAMED_ELEMENT QML_NAMED_ELEMENT(FramelessHelper) #endif - Q_PROPERTY(qreal resizeBorderThickness READ resizeBorderThickness WRITE setResizeBorderThickness NOTIFY resizeBorderThicknessChanged) - Q_PROPERTY(qreal titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged) - Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged) public: - explicit FramelessQuickHelper(QQuickItem *parent = nullptr); + explicit FramelessQuickHelper(QObject *parent = nullptr); ~FramelessQuickHelper() override; - Q_NODISCARD qreal resizeBorderThickness() const; - void setResizeBorderThickness(const qreal val); + Q_INVOKABLE static void addWindow(QWindow *window); + Q_INVOKABLE static void removeWindow(QWindow *window); +}; - Q_NODISCARD qreal titleBarHeight() const; - void setTitleBarHeight(const qreal val); +class FRAMELESSHELPER_API FramelessQuickUtils : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(FramelessQuickUtils) +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(FramelessUtils) +#endif + 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(QColor frameBorderActiveColor READ frameBorderActiveColor NOTIFY frameBorderActiveColorChanged FINAL) + Q_PROPERTY(QColor frameBorderInactiveColor READ frameBorderInactiveColor NOTIFY frameBorderInactiveColorChanged FINAL) + Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL) + Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL) + Q_PROPERTY(bool titleBarColorVisible READ titleBarColorVisible NOTIFY titleBarColorVisibleChanged FINAL) - Q_NODISCARD bool resizable() const; - void setResizable(const bool val); +public: + explicit FramelessQuickUtils(QObject *parent = nullptr); + ~FramelessQuickUtils() override; - Q_NODISCARD Q_INVOKABLE bool isWindowFrameless() const; + Q_NODISCARD static qreal titleBarHeight(); + Q_NODISCARD static bool frameBorderVisible(); + Q_NODISCARD static qreal frameBorderThickness(); + Q_NODISCARD static QColor frameBorderActiveColor(); + Q_NODISCARD static QColor frameBorderInactiveColor(); + Q_NODISCARD static bool darkModeEnabled(); + Q_NODISCARD static QColor systemAccentColor(); + Q_NODISCARD static bool titleBarColorVisible(); -public Q_SLOTS: - void removeWindowFrame(); - void bringBackWindowFrame(); - void setHitTestVisible(QQuickItem *item, const bool visible); - void showMinimized(); + Q_INVOKABLE static void showMinimized2(QWindow *window); + Q_INVOKABLE static void showSystemMenu(const QPointF &pos); + Q_INVOKABLE static void startSystemMove2(QWindow *window); + Q_INVOKABLE static void startSystemResize2(QWindow *window, const Qt::Edges edges); Q_SIGNALS: - void resizeBorderThicknessChanged(qreal); - void titleBarHeightChanged(qreal); - void resizableChanged(bool); + void frameBorderActiveColorChanged(); + void frameBorderInactiveColorChanged(); + void darkModeEnabledChanged(); + void systemAccentColorChanged(); + void titleBarColorVisibleChanged(); }; FRAMELESSHELPER_END_NAMESPACE diff --git a/framelesswindowsmanager.cpp b/framelesswindowsmanager.cpp index 0a17045..a87457c 100644 --- a/framelesswindowsmanager.cpp +++ b/framelesswindowsmanager.cpp @@ -158,20 +158,22 @@ void FramelessWindowsManager::addWindow(QWindow *window) g_managerPrivate()->qtFramelessHelpers.insert(uuid, qtFramelessHelper); } #ifdef Q_OS_WINDOWS - // Work-around Win32 multi-monitor artifacts. - const QMetaObject::Connection workaroundConnection = + if (!g_usePureQtImplementation) { + // Work-around Win32 multi-monitor artifacts. + const QMetaObject::Connection workaroundConnection = connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){ - Q_UNUSED(screen); - // Force a WM_NCCALCSIZE event to inform Windows about our custom window frame, - // this is only necessary when the window is being moved cross monitors. - Utilities::triggerFrameChange(window->winId()); - // For some reason the window is not repainted correctly when moving cross monitors, - // we workaround this issue by force a re-paint and re-layout of the window by triggering - // a resize event manually. Although the actual size does not change, the issue we - // observed disappeared indeed, amazingly. - window->resize(window->size()); - }); - g_managerPrivate()->win32WorkaroundConnections.insert(uuid, workaroundConnection); + Q_UNUSED(screen); + // Force a WM_NCCALCSIZE event to inform Windows about our custom window frame, + // this is only necessary when the window is being moved cross monitors. + Utilities::triggerFrameChange(window->winId()); + // For some reason the window is not repainted correctly when moving cross monitors, + // we workaround this issue by force a re-paint and re-layout of the window by triggering + // a resize event manually. Although the actual size does not change, the issue we + // observed disappeared indeed, amazingly. + window->resize(window->size()); + }); + g_managerPrivate()->win32WorkaroundConnections.insert(uuid, workaroundConnection); + } #endif g_managerPrivate()->mutex.unlock(); if (g_usePureQtImplementation) { diff --git a/framelesswindowsmanager.h b/framelesswindowsmanager.h index 77a2494..30686dc 100644 --- a/framelesswindowsmanager.h +++ b/framelesswindowsmanager.h @@ -49,6 +49,7 @@ public: Q_SIGNALS: void themeChanged(); + void systemMenuRequested(const QPointF &); }; FRAMELESSHELPER_END_NAMESPACE