diff --git a/core/framelesshelper_qt.cpp b/core/framelesshelper_qt.cpp index c6d6982..da38016 100644 --- a/core/framelesshelper_qt.cpp +++ b/core/framelesshelper_qt.cpp @@ -115,22 +115,32 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) } const auto mouseEvent = static_cast(event); #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPointF localPos = mouseEvent->position(); + const QPointF scenePos = mouseEvent->scenePosition(); #else - const QPointF localPos = mouseEvent->windowPos(); + const QPointF scenePos = mouseEvent->windowPos(); #endif - if (type == QEvent::MouseMove) { - const Qt::CursorShape cs = Utils::calculateCursorShape(window, localPos); + switch (type) { + case QEvent::MouseMove: { + const Qt::CursorShape cs = Utils::calculateCursorShape(window, scenePos); if (cs == Qt::ArrowCursor) { window->unsetCursor(); } else { window->setCursor(cs); } - } else if (type == QEvent::MouseButtonPress) { - const Qt::Edges edges = Utils::calculateWindowEdges(window, localPos); - if (edges != Qt::Edges{}) { - Utils::startSystemResize(window, edges); + } break; + case QEvent::MouseButtonPress: { + if (mouseEvent->button() != Qt::LeftButton) { + return false; } + const Qt::Edges edges = Utils::calculateWindowEdges(window, scenePos); + if (edges == Qt::Edges{}) { + return false; + } + Utils::startSystemResize(window, edges); + return true; + } + default: + break; } return false; } diff --git a/examples/mainwindow/TitleBar.ui b/examples/mainwindow/TitleBar.ui index d2aa2b1..1e2f975 100644 --- a/examples/mainwindow/TitleBar.ui +++ b/examples/mainwindow/TitleBar.ui @@ -222,9 +222,6 @@ 16 - - true - diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 8169bc2..1ea893b 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -25,6 +25,9 @@ #include "mainwindow.h" #include "ui_MainWindow.h" #include "ui_TitleBar.h" +#include + +FRAMELESSHELPER_USE_NAMESPACE MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : FramelessMainWindow(parent, flags) { @@ -43,6 +46,14 @@ MainWindow::~MainWindow() } } +void MainWindow::changeEvent(QEvent *event) +{ + FramelessMainWindow::changeEvent(event); + if (event->type() == QEvent::WindowStateChange) { + Q_EMIT windowStateChanged(); + } +} + void MainWindow::setupUi() { mainWindow = new Ui::MainWindow; @@ -52,6 +63,12 @@ void MainWindow::setupUi() titleBar = new Ui::TitleBar; titleBar->setupUi(titleBarWidget); + const SystemTheme theme = SystemTheme::Light; + const ResourceType resource = ResourceType::Icon; + titleBar->minimizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Minimize, theme, resource))); + titleBar->maximizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Maximize, theme, resource))); + titleBar->closeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Close, theme, resource))); + QMenuBar *mb = menuBar(); titleBar->horizontalLayout->insertWidget(1, mb); @@ -65,19 +82,18 @@ void MainWindow::setupUi() setHitTestVisible(titleBar->closeButton, true); connect(titleBar->minimizeButton, &QPushButton::clicked, this, &MainWindow::showMinimized); - connect(titleBar->maximizeButton, &QPushButton::clicked, this, [this](){ - if (isZoomed()) { - showNormal(); - } else { - showMaximized(); - } - }); + connect(titleBar->maximizeButton, &QPushButton::clicked, this, &MainWindow::toggleMaximized); connect(titleBar->closeButton, &QPushButton::clicked, this, &MainWindow::close); connect(this, &MainWindow::windowIconChanged, titleBar->iconButton, &QPushButton::setIcon); connect(this, &MainWindow::windowTitleChanged, titleBar->titleLabel, &QLabel::setText); connect(this, &MainWindow::windowStateChanged, this, [this](){ const bool zoomed = isZoomed(); - titleBar->maximizeButton->setChecked(zoomed); + const SystemTheme theme = SystemTheme::Light; + const SystemButtonType button = (zoomed ? SystemButtonType::Restore : SystemButtonType::Maximize); + const ResourceType resource = ResourceType::Icon; + titleBar->maximizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(button, theme, resource))); titleBar->maximizeButton->setToolTip(zoomed ? tr("Restore") : tr("Maximize")); }); + + setWindowTitle(tr("Hello, World! - Qt MainWindow")); } diff --git a/examples/mainwindow/mainwindow.h b/examples/mainwindow/mainwindow.h index 86c75f5..65909c4 100644 --- a/examples/mainwindow/mainwindow.h +++ b/examples/mainwindow/mainwindow.h @@ -41,6 +41,9 @@ public: explicit MainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = {}); ~MainWindow() override; +protected: + void changeEvent(QEvent *event) override; + private: void setupUi(); diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index 9172047..7968699 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include FRAMELESSHELPER_USE_NAMESPACE @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) QQuickStyle::setStyle(QStringLiteral("Default")); #endif - FramelessQuickHelper::registerTypes(&engine); + FramelessHelper::Quick::registerTypes(&engine); const QUrl homepageUrl(QStringLiteral("qrc:///qml/MainWindow.qml")); const QMetaObject::Connection connection = QObject::connect( diff --git a/examples/quick/qml/MainWindow.qml b/examples/quick/qml/MainWindow.qml index 6901ea5..b98754e 100644 --- a/examples/quick/qml/MainWindow.qml +++ b/examples/quick/qml/MainWindow.qml @@ -67,33 +67,12 @@ Window { anchors.verticalCenter: parent.verticalCenter } - MouseArea { - anchors.fill: parent - anchors.rightMargin: 30 * 1.5 * 3 - acceptedButtons: Qt.LeftButton | Qt.RightButton - hoverEnabled: true - onClicked: { - if (mouse.button === Qt.RightButton) { - FramelessUtils.showSystemMenu(window, Qt.point(mouse.x, mouse.y)); - } - } - onDoubleClicked: { - if (mouse.button === Qt.LeftButton) { - 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: FramelessUtils.showMinimized2(window) } @@ -110,6 +89,7 @@ Window { } CloseButton { + id: closeButton onClicked: window.close() } } @@ -136,5 +116,11 @@ Window { color: window.active ? FramelessUtils.frameBorderActiveColor : FramelessUtils.frameBorderInactiveColor } - Component.onCompleted: FramelessHelper.addWindow(window) + Component.onCompleted: { + FramelessHelper.addWindow(window); + FramelessHelper.setTitleBarItem(window, titleBar); + FramelessHelper.setHitTestVisible(window, minimizeButton, true); + FramelessHelper.setHitTestVisible(window, maximizeButton, true); + FramelessHelper.setHitTestVisible(window, closeButton, true); + } } diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index d935866..e99e59e 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -30,11 +30,11 @@ FRAMELESSHELPER_USE_NAMESPACE -Widget::Widget(QWidget *parent) : FramelessWidget(parent) +Widget::Widget(QWidget *parent) : FramelessWidget(parent, WindowLayout::Standard) { setupUi(); - connect(this, &Widget::systemThemeChanged, this, &Widget::updateStyleSheet); startTimer(500); + connect(this, &Widget::systemThemeChanged, this, &Widget::updateStyleSheet); } Widget::~Widget() = default; diff --git a/quick/CMakeLists.txt b/quick/CMakeLists.txt index 9d9df18..3d0d0f4 100644 --- a/quick/CMakeLists.txt +++ b/quick/CMakeLists.txt @@ -8,6 +8,10 @@ set(SOURCES framelessquickutils.cpp framelesshelperimageprovider.h framelesshelperimageprovider.cpp + framelessquickeventfilter.h + framelessquickeventfilter.cpp + framelesshelper_quick.h + framelesshelper_quick.cpp ) if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC) diff --git a/quick/framelesshelper_quick.cpp b/quick/framelesshelper_quick.cpp new file mode 100644 index 0000000..a842e0d --- /dev/null +++ b/quick/framelesshelper_quick.cpp @@ -0,0 +1,54 @@ +/* + * 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 "framelesshelper_quick.h" +#include +#include "framelesshelperimageprovider.h" +#include "framelessquickhelper.h" +#include "framelessquickutils.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; + +void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) +{ + Q_ASSERT(engine); + if (!engine) { + return; + } + engine->addImageProvider(QStringLiteral("framelesshelper"), new FramelessHelperImageProvider); + qmlRegisterSingletonType(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessHelper", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + return new FramelessQuickHelper; + }); + qmlRegisterSingletonType(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessUtils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + return new FramelessQuickUtils; + }); +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelesshelper_quick.h b/quick/framelesshelper_quick.h new file mode 100644 index 0000000..9b7bf2a --- /dev/null +++ b/quick/framelesshelper_quick.h @@ -0,0 +1,42 @@ +/* + * 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" + +QT_BEGIN_NAMESPACE +class QQmlEngine; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +namespace FramelessHelper::Quick +{ + +FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine); + +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelesshelperimageprovider.cpp b/quick/framelesshelperimageprovider.cpp index d6ac989..1038d73 100644 --- a/quick/framelesshelperimageprovider.cpp +++ b/quick/framelesshelperimageprovider.cpp @@ -94,7 +94,7 @@ QPixmap FramelessHelperImageProvider::requestPixmap(const QString &id, QSize *si if (!pixmapVar.isValid()) { return {}; } - if (static_cast(pixmapVar.typeId()) != QMetaType::QPixmap) { + if (static_cast(pixmapVar.userType()) != QMetaType::QPixmap) { return {}; } if (size) { diff --git a/quick/framelesshelperimageprovider.h b/quick/framelesshelperimageprovider.h index 4a9d140..f9a044a 100644 --- a/quick/framelesshelperimageprovider.h +++ b/quick/framelesshelperimageprovider.h @@ -31,14 +31,16 @@ FRAMELESSHELPER_BEGIN_NAMESPACE class FRAMELESSHELPER_QUICK_API FramelessHelperImageProvider : public QQuickImageProvider { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) Q_OBJECT +#endif Q_DISABLE_COPY_MOVE(FramelessHelperImageProvider) public: explicit FramelessHelperImageProvider(); ~FramelessHelperImageProvider() override; - Q_NODISCARD QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) override; + Q_NODISCARD QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelessquickeventfilter.cpp b/quick/framelessquickeventfilter.cpp new file mode 100644 index 0000000..af02747 --- /dev/null +++ b/quick/framelessquickeventfilter.cpp @@ -0,0 +1,252 @@ +/* + * MIT License + * + * Copyright (C) 2022 by wangwenx190 (Yuhang Zhao) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "framelessquickeventfilter.h" +#include +#include +#include +#include +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +struct EventFilterDataInternal +{ + QQuickWindow *window = nullptr; + FramelessQuickEventFilter *eventFilter = nullptr; + QQuickItem *titleBarItem = nullptr; + QList hitTestVisibleItems = {}; +}; + +struct EventFilterData +{ + QMutex mutex = {}; + QHash data = {}; + + explicit EventFilterData() = default; + ~EventFilterData() = default; + +private: + Q_DISABLE_COPY_MOVE(EventFilterData) +}; + +Q_GLOBAL_STATIC(EventFilterData, g_data) + +[[nodiscard]] static inline bool isInTitleBarDraggableArea(QQuickWindow *window, const QPointF &pos) +{ + Q_ASSERT(window); + if (!window) { + return false; + } + g_data()->mutex.lock(); + if (!g_data()->data.contains(window)) { + g_data()->mutex.unlock(); + return false; + } + const EventFilterDataInternal data = g_data()->data.value(window); + g_data()->mutex.unlock(); + if (!data.titleBarItem) { + return false; + } + const auto mapGeometryToScene = [](const QQuickItem * const item) -> QRectF { + Q_ASSERT(item); + if (!item) { + return {}; + } + return QRectF(item->mapToScene(QPointF(0.0, 0.0)), item->size()); + }; + QRegion region = mapGeometryToScene(data.titleBarItem).toRect(); + if (!data.hitTestVisibleItems.isEmpty()) { + for (auto &&item : qAsConst(data.hitTestVisibleItems)) { + Q_ASSERT(item); + if (item) { + region -= mapGeometryToScene(item).toRect(); + } + } + } + return region.contains(pos.toPoint()); +} + +FramelessQuickEventFilter::FramelessQuickEventFilter(QObject *parent) : QObject(parent) {} + +FramelessQuickEventFilter::~FramelessQuickEventFilter() = default; + +void FramelessQuickEventFilter::addWindow(QQuickWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } + g_data()->mutex.lock(); + if (g_data()->data.contains(window)) { + g_data()->mutex.unlock(); + return; + } + auto data = EventFilterDataInternal{}; + data.window = window; + data.eventFilter = new FramelessQuickEventFilter(window); + g_data()->data.insert(window, data); + g_data()->mutex.unlock(); + window->installEventFilter(data.eventFilter); +} + +void FramelessQuickEventFilter::removeWindow(QQuickWindow *window) +{ + Q_ASSERT(window); + if (!window) { + return; + } + g_data()->mutex.lock(); + if (!g_data()->data.contains(window)) { + g_data()->mutex.unlock(); + return; + } + const EventFilterDataInternal data = g_data()->data.value(window); + g_data()->data.remove(window); + g_data()->mutex.unlock(); + window->removeEventFilter(data.eventFilter); + delete data.eventFilter; +} + +void FramelessQuickEventFilter::setTitleBarItem(QQuickWindow *window, QQuickItem *item) +{ + Q_ASSERT(window); + Q_ASSERT(item); + if (!window || !item) { + return; + } + QMutexLocker locker(&g_data()->mutex); + if (!g_data()->data.contains(window)) { + return; + } + g_data()->data[window].titleBarItem = item; +} + +void FramelessQuickEventFilter::setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible) +{ + Q_ASSERT(window); + Q_ASSERT(item); + if (!window || !item) { + return; + } + QMutexLocker locker(&g_data()->mutex); + if (!g_data()->data.contains(window)) { + return; + } + auto &items = g_data()->data[window].hitTestVisibleItems; + const bool exists = items.contains(item); + if (visible && !exists) { + items.append(item); + } + if (!visible && exists) { + items.removeAll(item); + } +} + +bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event) +{ + Q_ASSERT(object); + Q_ASSERT(event); + if (!object || !event) { + return false; + } + if (!object->isWindowType()) { + return false; + } + const auto window = qobject_cast(object); + if (!window) { + return false; + } + g_data()->mutex.lock(); + if (!g_data()->data.contains(window)) { + g_data()->mutex.unlock(); + return false; + } + g_data()->mutex.unlock(); + const QEvent::Type eventType = event->type(); + if ((eventType != QEvent::MouseButtonPress) + && (eventType != QEvent::MouseButtonRelease) && (eventType != QEvent::MouseButtonDblClick)) { + return false; + } + const auto mouseEvent = static_cast(event); + const Qt::MouseButton button = mouseEvent->button(); + if ((button != Qt::LeftButton) && (button != Qt::RightButton)) { + return false; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPointF scenePos = mouseEvent->scenePosition(); +#else + const QPointF scenePos = mouseEvent->windowPos(); +#endif + const bool titleBar = isInTitleBarDraggableArea(window, scenePos); + switch (eventType) { + case QEvent::MouseButtonPress: { + if (button != Qt::LeftButton) { + return false; + } + if (!titleBar) { + return false; + } + Utils::startSystemMove(window); + return true; + } + case QEvent::MouseButtonRelease: { + if (button != Qt::RightButton) { + return false; + } + if (!titleBar) { + return false; + } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPointF globalPos = mouseEvent->globalPosition(); +#else + const QPointF globalPos = mouseEvent->globalPos(); +#endif + const QPointF nativePos = QPointF(globalPos * window->effectiveDevicePixelRatio()); + Utils::showSystemMenu(window->winId(), nativePos); + return true; + } + case QEvent::MouseButtonDblClick: { + if (button != Qt::LeftButton) { + return false; + } + if (!titleBar) { + return false; + } + const QQuickWindow::Visibility visibility = window->visibility(); + if ((visibility == QQuickWindow::Maximized) || (visibility == QQuickWindow::FullScreen)) { + window->showNormal(); + } else { + window->showMaximized(); + } + return true; + } + default: + break; + } + return false; +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelessquickeventfilter.h b/quick/framelessquickeventfilter.h new file mode 100644 index 0000000..cffbeeb --- /dev/null +++ b/quick/framelessquickeventfilter.h @@ -0,0 +1,55 @@ +/* + * 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 + +QT_BEGIN_NAMESPACE +class QQuickWindow; +class QQuickItem; +QT_END_NAMESPACE + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FRAMELESSHELPER_QUICK_API FramelessQuickEventFilter : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(FramelessQuickEventFilter) + +public: + explicit FramelessQuickEventFilter(QObject *parent = nullptr); + ~FramelessQuickEventFilter() override; + + Q_INVOKABLE static void addWindow(QQuickWindow *window); + Q_INVOKABLE static void removeWindow(QQuickWindow *window); + Q_INVOKABLE static void setTitleBarItem(QQuickWindow *window, QQuickItem *item); + Q_INVOKABLE static void setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible); + +protected: + Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; +}; + +FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelessquickhelper.cpp b/quick/framelessquickhelper.cpp index 273ccbd..de9a6c8 100644 --- a/quick/framelessquickhelper.cpp +++ b/quick/framelessquickhelper.cpp @@ -23,56 +23,54 @@ */ #include "framelessquickhelper.h" -#include -#include "framelesshelperimageprovider.h" -#include "framelessquickutils.h" +#include #include +#include "framelessquickeventfilter.h" FRAMELESSHELPER_BEGIN_NAMESPACE -static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; - FramelessQuickHelper::FramelessQuickHelper(QObject *parent) : QObject(parent) {} FramelessQuickHelper::~FramelessQuickHelper() = default; -void FramelessQuickHelper::registerTypes(QQmlEngine *engine) -{ - Q_ASSERT(engine); - if (!engine) { - return; - } - engine->addImageProvider(QStringLiteral("framelesshelper"), new FramelessHelperImageProvider); - qmlRegisterSingletonType(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessHelper", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine); - Q_UNUSED(scriptEngine); - const auto framelessHelper = new FramelessQuickHelper; - return framelessHelper; - }); - qmlRegisterSingletonType(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessUtils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine); - Q_UNUSED(scriptEngine); - const auto framelessUtils = new FramelessQuickUtils; - return framelessUtils; - }); -} - -void FramelessQuickHelper::addWindow(QWindow *window) +void FramelessQuickHelper::addWindow(QQuickWindow *window) { Q_ASSERT(window); if (!window) { return; } FramelessWindowsManager::instance()->addWindow(window); + FramelessQuickEventFilter::addWindow(window); } -void FramelessQuickHelper::removeWindow(QWindow *window) +void FramelessQuickHelper::removeWindow(QQuickWindow *window) { Q_ASSERT(window); if (!window) { return; } + FramelessQuickEventFilter::removeWindow(window); FramelessWindowsManager::instance()->removeWindow(window); } +void FramelessQuickHelper::setTitleBarItem(QQuickWindow *window, QQuickItem *item) +{ + Q_ASSERT(window); + Q_ASSERT(item); + if (!window || !item) { + return; + } + FramelessQuickEventFilter::setTitleBarItem(window, item); +} + +void FramelessQuickHelper::setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible) +{ + Q_ASSERT(window); + Q_ASSERT(item); + if (!window || !item) { + return; + } + FramelessQuickEventFilter::setHitTestVisible(window, item, visible); +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelessquickhelper.h b/quick/framelessquickhelper.h index e0db127..aa98880 100644 --- a/quick/framelessquickhelper.h +++ b/quick/framelessquickhelper.h @@ -28,8 +28,8 @@ #include QT_BEGIN_NAMESPACE -class QWindow; -class QQmlEngine; +class QQuickWindow; +class QQuickItem; QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE @@ -49,10 +49,10 @@ public: explicit FramelessQuickHelper(QObject *parent = nullptr); ~FramelessQuickHelper() override; - Q_INVOKABLE static void registerTypes(QQmlEngine *engine); - - Q_INVOKABLE static void addWindow(QWindow *window); - Q_INVOKABLE static void removeWindow(QWindow *window); + Q_INVOKABLE static void addWindow(QQuickWindow *window); + Q_INVOKABLE static void removeWindow(QQuickWindow *window); + Q_INVOKABLE static void setTitleBarItem(QQuickWindow *window, QQuickItem *item); + Q_INVOKABLE static void setHitTestVisible(QQuickWindow *window, QQuickItem *item, const bool visible); }; FRAMELESSHELPER_END_NAMESPACE diff --git a/quick/framelessquickutils.cpp b/quick/framelessquickutils.cpp index a418bc9..dc76f41 100644 --- a/quick/framelessquickutils.cpp +++ b/quick/framelessquickutils.cpp @@ -23,6 +23,7 @@ */ #include "framelessquickutils.h" +#include #if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) # include # include @@ -119,7 +120,7 @@ bool FramelessQuickUtils::titleBarColorVisible() #endif } -void FramelessQuickUtils::showMinimized2(QWindow *window) +void FramelessQuickUtils::showMinimized2(QQuickWindow *window) { Q_ASSERT(window); if (!window) { @@ -136,7 +137,7 @@ void FramelessQuickUtils::showMinimized2(QWindow *window) #endif } -void FramelessQuickUtils::showSystemMenu(QWindow *window, const QPointF &pos) +void FramelessQuickUtils::showSystemMenu(QQuickWindow *window, const QPointF &pos) { Q_ASSERT(window); if (!window) { @@ -144,15 +145,16 @@ void FramelessQuickUtils::showSystemMenu(QWindow *window, const QPointF &pos) } #ifdef Q_OS_WINDOWS # if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPointF globalPos = window->mapToGlobal(pos) * window->devicePixelRatio(); + const QPointF globalPos = window->mapToGlobal(pos); # else - const QPointF globalPos = QPointF(window->mapToGlobal(pos.toPoint())) * window->devicePixelRatio(); + const QPointF globalPos = window->mapToGlobal(pos.toPoint()); # endif - Utils::showSystemMenu(window->winId(), globalPos); + const QPointF nativePos = QPointF(globalPos * window->effectiveDevicePixelRatio()); + Utils::showSystemMenu(window->winId(), nativePos); #endif } -void FramelessQuickUtils::startSystemMove2(QWindow *window) +void FramelessQuickUtils::startSystemMove2(QQuickWindow *window) { Q_ASSERT(window); if (!window) { @@ -165,7 +167,7 @@ void FramelessQuickUtils::startSystemMove2(QWindow *window) #endif } -void FramelessQuickUtils::startSystemResize2(QWindow *window, const Qt::Edges edges) +void FramelessQuickUtils::startSystemResize2(QQuickWindow *window, const Qt::Edges edges) { Q_ASSERT(window); if (!window) { diff --git a/quick/framelessquickutils.h b/quick/framelessquickutils.h index 85805e7..a1e0c13 100644 --- a/quick/framelessquickutils.h +++ b/quick/framelessquickutils.h @@ -29,7 +29,7 @@ #include QT_BEGIN_NAMESPACE -class QWindow; +class QQuickWindow; QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE @@ -66,10 +66,10 @@ public: Q_NODISCARD static QColor systemAccentColor(); Q_NODISCARD static bool titleBarColorVisible(); - Q_INVOKABLE static void showMinimized2(QWindow *window); - Q_INVOKABLE static void showSystemMenu(QWindow *window, const QPointF &pos); - Q_INVOKABLE static void startSystemMove2(QWindow *window); - Q_INVOKABLE static void startSystemResize2(QWindow *window, const Qt::Edges edges); + Q_INVOKABLE static void showMinimized2(QQuickWindow *window); + Q_INVOKABLE static void showSystemMenu(QQuickWindow *window, const QPointF &pos); + Q_INVOKABLE static void startSystemMove2(QQuickWindow *window); + Q_INVOKABLE static void startSystemResize2(QQuickWindow *window, const Qt::Edges edges); Q_SIGNALS: void frameBorderActiveColorChanged(); diff --git a/widgets/framelessmainwindow.cpp b/widgets/framelessmainwindow.cpp index 39532db..651e6c6 100644 --- a/widgets/framelessmainwindow.cpp +++ b/widgets/framelessmainwindow.cpp @@ -29,8 +29,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE FramelessMainWindow::FramelessMainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) { - m_helper.reset(new FramelessWidgetsHelper(this, this)); - m_helper->initialize(); + m_helper.reset(new FramelessWidgetsHelper(this, WindowLayout::Custom)); } FramelessMainWindow::~FramelessMainWindow() = default; @@ -60,6 +59,11 @@ void FramelessMainWindow::setHitTestVisible(QWidget *widget, const bool visible) m_helper->setHitTestVisible(widget, visible); } +void FramelessMainWindow::toggleMaximized() +{ + m_helper->toggleMaximized(); +} + void FramelessMainWindow::showEvent(QShowEvent *event) { QMainWindow::showEvent(event); @@ -84,6 +88,12 @@ void FramelessMainWindow::mousePressEvent(QMouseEvent *event) m_helper->mousePressEventHandler(event); } +void FramelessMainWindow::mouseReleaseEvent(QMouseEvent *event) +{ + QMainWindow::mouseReleaseEvent(event); + m_helper->mouseReleaseEventHandler(event); +} + void FramelessMainWindow::mouseDoubleClickEvent(QMouseEvent *event) { QMainWindow::mouseDoubleClickEvent(event); diff --git a/widgets/framelessmainwindow.h b/widgets/framelessmainwindow.h index 4e60d9a..ec892cc 100644 --- a/widgets/framelessmainwindow.h +++ b/widgets/framelessmainwindow.h @@ -38,23 +38,25 @@ class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL) public: - explicit FramelessMainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = {}, - const WindowLayout wl = WindowLayout::Standard); + explicit FramelessMainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = {}); ~FramelessMainWindow() override; - Q_NODISCARD bool isNormal() const; - Q_NODISCARD bool isZoomed() const; + Q_NODISCARD Q_INVOKABLE bool isNormal() const; + Q_NODISCARD Q_INVOKABLE bool isZoomed() const; void setTitleBarWidget(QWidget *widget); Q_NODISCARD QWidget *titleBarWidget() const; Q_INVOKABLE void setHitTestVisible(QWidget *widget, const bool visible); + Q_INVOKABLE void toggleMaximized(); + protected: void showEvent(QShowEvent *event) override; void changeEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; Q_SIGNALS: diff --git a/widgets/framelesswidget.cpp b/widgets/framelesswidget.cpp index 3bc8cb3..365c426 100644 --- a/widgets/framelesswidget.cpp +++ b/widgets/framelesswidget.cpp @@ -27,10 +27,9 @@ FRAMELESSHELPER_BEGIN_NAMESPACE -FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent) +FramelessWidget::FramelessWidget(QWidget *parent, const WindowLayout wl) : QWidget(parent) { - m_helper.reset(new FramelessWidgetsHelper(this, this)); - m_helper->initialize(); + m_helper.reset(new FramelessWidgetsHelper(this, wl)); } FramelessWidget::~FramelessWidget() = default; @@ -70,6 +69,11 @@ void FramelessWidget::setHitTestVisible(QWidget *widget, const bool visible) m_helper->setHitTestVisible(widget, visible); } +void FramelessWidget::toggleMaximized() +{ + m_helper->toggleMaximized(); +} + void FramelessWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); @@ -94,6 +98,12 @@ void FramelessWidget::mousePressEvent(QMouseEvent *event) m_helper->mousePressEventHandler(event); } +void FramelessWidget::mouseReleaseEvent(QMouseEvent *event) +{ + QWidget::mouseReleaseEvent(event); + m_helper->mouseReleaseEventHandler(event); +} + void FramelessWidget::mouseDoubleClickEvent(QMouseEvent *event) { QWidget::mouseDoubleClickEvent(event); diff --git a/widgets/framelesswidget.h b/widgets/framelesswidget.h index 4dd0fc7..7c6fa2b 100644 --- a/widgets/framelesswidget.h +++ b/widgets/framelesswidget.h @@ -42,8 +42,8 @@ public: explicit FramelessWidget(QWidget *parent = nullptr, const WindowLayout wl = WindowLayout::Standard); ~FramelessWidget() override; - Q_NODISCARD bool isNormal() const; - Q_NODISCARD bool isZoomed() const; + Q_NODISCARD Q_INVOKABLE bool isNormal() const; + Q_NODISCARD Q_INVOKABLE bool isZoomed() const; void setTitleBarWidget(QWidget *widget); Q_NODISCARD QWidget *titleBarWidget() const; @@ -53,11 +53,14 @@ public: Q_INVOKABLE void setHitTestVisible(QWidget *widget, const bool visible); + Q_INVOKABLE void toggleMaximized(); + protected: void showEvent(QShowEvent *event) override; void changeEvent(QEvent *event) override; void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; Q_SIGNALS: diff --git a/widgets/framelesswidgetshelper.cpp b/widgets/framelesswidgetshelper.cpp index 6f094da..86ca17b 100644 --- a/widgets/framelesswidgetshelper.cpp +++ b/widgets/framelesswidgetshelper.cpp @@ -34,8 +34,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE -static constexpr const char QT_MAINWINDOW_CLASS_NAME[] = "QMainWindow"; - static const QString kSystemButtonStyleSheet = QStringLiteral(R"( QPushButton { border-style: none; @@ -82,18 +80,19 @@ void FramelessWidgetsHelper::setTitleBarWidget(QWidget *widget) if (m_userTitleBarWidget == widget) { return; } - if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) { - m_systemTitleBarWidget->hide(); - } - if (isMainWindow()) { - m_userTitleBarWidget = widget; - } else { + if (isStandardLayout()) { + if (m_systemTitleBarWidget && m_systemTitleBarWidget->isVisible()) { + m_mainLayout->removeWidget(m_systemTitleBarWidget); + m_systemTitleBarWidget->hide(); + } if (m_userTitleBarWidget) { m_mainLayout->removeWidget(m_userTitleBarWidget); m_userTitleBarWidget = nullptr; } m_userTitleBarWidget = widget; m_mainLayout->insertWidget(0, m_userTitleBarWidget); + } else { + m_userTitleBarWidget = widget; } QMetaObject::invokeMethod(q, "titleBarWidgetChanged"); } @@ -109,7 +108,7 @@ void FramelessWidgetsHelper::setContentWidget(QWidget *widget) if (!widget) { return; } - if (isMainWindow()) { + if (isCustomLayout()) { return; } if (m_userContentWidget == widget) { @@ -126,9 +125,6 @@ void FramelessWidgetsHelper::setContentWidget(QWidget *widget) QWidget *FramelessWidgetsHelper::contentWidget() const { - if (isMainWindow()) { - return nullptr; - } return m_userContentWidget; } @@ -149,33 +145,46 @@ void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget, const bool visib void FramelessWidgetsHelper::showEventHandler(QShowEvent *event) { - Q_UNUSED(event); + Q_ASSERT(event); + if (!event) { + return; + } setupFramelessHelperOnce(); } void FramelessWidgetsHelper::changeEventHandler(QEvent *event) { - bool shouldUpdate = false; - if (event->type() == QEvent::WindowStateChange) { - if (isZoomed()) { - m_systemMaximizeButton->setToolTip(tr("Restore")); - } else { - m_systemMaximizeButton->setToolTip(tr("Maximize")); + Q_ASSERT(event); + if (!event) { + return; + } + const QEvent::Type type = event->type(); + if ((type != QEvent::WindowStateChange) && (type != QEvent::ActivationChange)) { + return; + } + if (type == QEvent::WindowStateChange) { + if (isStandardLayout()) { + if (isZoomed()) { + m_systemMaximizeButton->setToolTip(tr("Restore")); + } else { + m_systemMaximizeButton->setToolTip(tr("Maximize")); + } + updateSystemButtonsIcon(); } updateContentsMargins(); - updateSystemButtonsIcon(); - shouldUpdate = true; - } else if (event->type() == QEvent::ActivationChange) { - shouldUpdate = true; } - if (shouldUpdate) { + if (isStandardLayout()) { updateSystemTitleBarStyleSheet(); } + q->update(); } void FramelessWidgetsHelper::paintEventHandler(QPaintEvent *event) { - Q_UNUSED(event); + Q_ASSERT(event); + if (!event) { + return; + } if (!shouldDrawFrameBorder()) { return; } @@ -191,39 +200,63 @@ void FramelessWidgetsHelper::paintEventHandler(QPaintEvent *event) void FramelessWidgetsHelper::mousePressEventHandler(QMouseEvent *event) { - const Qt::MouseButton button = event->button(); - if ((button != Qt::LeftButton) && (button != Qt::RightButton)) { + Q_ASSERT(event); + if (!event) { + return; + } + if (event->button() != Qt::LeftButton) { + return; + } + if (!isInTitleBarDraggableArea(event->pos())) { + return; + } + Utils::startSystemMove(q->windowHandle()); +} + +void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) +{ + Q_ASSERT(event); + if (!event) { + return; + } + if (event->button() != Qt::RightButton) { + return; + } + if (!isInTitleBarDraggableArea(event->pos())) { return; } - if (isInTitleBarDraggableArea(event->pos())) { - if (button == Qt::LeftButton) { - Utils::startSystemMove(q->windowHandle()); - } else { #ifdef Q_OS_WINDOWS # if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const QPointF globalPos = event->globalPosition(); + const QPointF globalPos = event->globalPosition(); # else - const QPointF globalPos = event->globalPos(); + const QPointF globalPos = event->globalPos(); # endif - const QPointF pos = globalPos * q->devicePixelRatioF(); - Utils::showSystemMenu(q->winId(), pos); + const QPointF nativePos = QPointF(globalPos * q->devicePixelRatioF()); + Utils::showSystemMenu(q->winId(), nativePos); #endif - } - } } void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) { + Q_ASSERT(event); + if (!event) { + return; + } if (event->button() != Qt::LeftButton) { return; } - if (isInTitleBarDraggableArea(event->pos())) { - m_systemMaximizeButton->click(); + if (!isInTitleBarDraggableArea(event->pos())) { + return; } + toggleMaximized(); } void FramelessWidgetsHelper::initialize() { + if (m_initialized) { + return; + } + m_initialized = true; q->setAttribute(Qt::WA_DontCreateNativeAncestors); q->createWinId(); setupInitialUi(); @@ -231,15 +264,18 @@ void FramelessWidgetsHelper::initialize() void FramelessWidgetsHelper::setupFramelessHelperOnce() { - if (m_framelessHelperInited) { + if (m_framelessHelperSetup) { return; } - m_framelessHelperInited = true; + m_framelessHelperSetup = true; FramelessWindowsManager *manager = FramelessWindowsManager::instance(); manager->addWindow(q->windowHandle()); connect(manager, &FramelessWindowsManager::systemThemeChanged, this, [this](){ - updateSystemTitleBarStyleSheet(); - updateSystemButtonsIcon(); + if (isStandardLayout()) { + updateSystemTitleBarStyleSheet(); + updateSystemButtonsIcon(); + q->update(); + } QMetaObject::invokeMethod(q, "systemThemeChanged"); }); connect(manager, &FramelessWindowsManager::systemMenuRequested, this, [this](const QPointF &pos){ @@ -249,7 +285,7 @@ void FramelessWidgetsHelper::setupFramelessHelperOnce() void FramelessWidgetsHelper::createSystemTitleBar() { - if (isCustomWindow()) { + if (isCustomLayout()) { return; } m_systemTitleBarWidget = new QWidget(q); @@ -271,14 +307,7 @@ void FramelessWidgetsHelper::createSystemTitleBar() m_systemMaximizeButton->setFixedSize(kDefaultSystemButtonSize); m_systemMaximizeButton->setIconSize(kDefaultSystemButtonIconSize); m_systemMaximizeButton->setToolTip(tr("Maximize")); - connect(m_systemMaximizeButton, &QPushButton::clicked, this, [this](){ - if (isZoomed()) { - q->showNormal(); - } else { - q->showMaximized(); - } - updateSystemButtonsIcon(); - }); + connect(m_systemMaximizeButton, &QPushButton::clicked, this, &FramelessWidgetsHelper::toggleMaximized); m_systemCloseButton = new QPushButton(m_systemTitleBarWidget); m_systemCloseButton->setFixedSize(kDefaultSystemButtonSize); m_systemCloseButton->setIconSize(kDefaultSystemButtonIconSize); @@ -299,7 +328,7 @@ void FramelessWidgetsHelper::createSystemTitleBar() void FramelessWidgetsHelper::createUserContentContainer() { - if (isCustomWindow() || isMainWindow()) { + if (isCustomLayout()) { return; } m_userContentContainerWidget = new QWidget(q); @@ -312,20 +341,17 @@ void FramelessWidgetsHelper::createUserContentContainer() void FramelessWidgetsHelper::setupInitialUi() { - if (isStandardWindow()) { + if (isStandardLayout()) { createSystemTitleBar(); - if (isMainWindow()) { - // ### TODO - } else { - createUserContentContainer(); - m_mainLayout = new QVBoxLayout(q); - m_mainLayout->setContentsMargins(0, 0, 0, 0); - m_mainLayout->setSpacing(0); - m_mainLayout->addWidget(m_systemTitleBarWidget); - m_mainLayout->addWidget(m_userContentContainerWidget); - q->setLayout(m_mainLayout); - } + createUserContentContainer(); + m_mainLayout = new QVBoxLayout(q); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + m_mainLayout->setSpacing(0); + m_mainLayout->addWidget(m_systemTitleBarWidget); + m_mainLayout->addWidget(m_userContentContainerWidget); + q->setLayout(m_mainLayout); updateSystemTitleBarStyleSheet(); + q->update(); } updateContentsMargins(); } @@ -333,23 +359,30 @@ void FramelessWidgetsHelper::setupInitialUi() bool FramelessWidgetsHelper::isInTitleBarDraggableArea(const QPoint &pos) const { const QRegion draggableRegion = [this]() -> QRegion { + const auto mapGeometryToScene = [this](const QWidget * const widget) -> QRect { + Q_ASSERT(widget); + if (!widget) { + return {}; + } + return QRect(widget->mapTo(q, QPoint(0, 0)), widget->size()); + }; if (m_userTitleBarWidget) { - QRegion region = {QRect(QPoint(0, 0), m_userTitleBarWidget->size())}; + QRegion region = mapGeometryToScene(m_userTitleBarWidget); if (!m_hitTestVisibleWidgets.isEmpty()) { for (auto &&widget : qAsConst(m_hitTestVisibleWidgets)) { Q_ASSERT(widget); if (widget) { - region -= widget->geometry(); + region -= mapGeometryToScene(widget); } } } return region; } - if (isStandardWindow()) { - QRegion region = {QRect(QPoint(0, 0), m_systemTitleBarWidget->size())}; - region -= m_systemMinimizeButton->geometry(); - region -= m_systemMaximizeButton->geometry(); - region -= m_systemCloseButton->geometry(); + if (isStandardLayout()) { + QRegion region = mapGeometryToScene(m_systemTitleBarWidget); + region -= mapGeometryToScene(m_systemMinimizeButton); + region -= mapGeometryToScene(m_systemMaximizeButton); + region -= mapGeometryToScene(m_systemCloseButton); return region; } return {}; @@ -366,20 +399,12 @@ bool FramelessWidgetsHelper::shouldDrawFrameBorder() const #endif } -bool FramelessWidgetsHelper::isMainWindow() const -{ - if (!q) { - return false; - } - return q->inherits(QT_MAINWINDOW_CLASS_NAME); -} - -bool FramelessWidgetsHelper::isStandardWindow() const +bool FramelessWidgetsHelper::isStandardLayout() const { return (m_windowLayout == WindowLayout::Standard); } -bool FramelessWidgetsHelper::isCustomWindow() +bool FramelessWidgetsHelper::isCustomLayout() const { return (m_windowLayout == WindowLayout::Custom); } @@ -393,7 +418,7 @@ void FramelessWidgetsHelper::updateContentsMargins() void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() { - if (isCustomWindow()) { + if (isCustomLayout()) { return; } const bool active = q->isActiveWindow(); @@ -424,22 +449,31 @@ void FramelessWidgetsHelper::updateSystemTitleBarStyleSheet() m_systemMaximizeButton->setStyleSheet(kSystemButtonStyleSheet); m_systemCloseButton->setStyleSheet(kSystemButtonStyleSheet); m_systemTitleBarWidget->setStyleSheet(QStringLiteral("background-color: %1;").arg(systemTitleBarWidgetBackgroundColor.name())); - q->update(); } void FramelessWidgetsHelper::updateSystemButtonsIcon() { - if (isCustomWindow()) { + if (isCustomLayout()) { return; } const SystemTheme theme = ((Utils::shouldAppsUseDarkMode() || Utils::isTitleBarColorized()) ? SystemTheme::Dark : SystemTheme::Light); - m_systemMinimizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Minimize, theme, ResourceType::Icon))); + const ResourceType resource = ResourceType::Icon; + m_systemMinimizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Minimize, theme, resource))); if (isZoomed()) { - m_systemMaximizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Restore, theme, ResourceType::Icon))); + m_systemMaximizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Restore, theme, resource))); } else { - m_systemMaximizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Maximize, theme, ResourceType::Icon))); + m_systemMaximizeButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Maximize, theme, resource))); + } + m_systemCloseButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Close, theme, resource))); +} + +void FramelessWidgetsHelper::toggleMaximized() +{ + if (isZoomed()) { + q->showNormal(); + } else { + q->showMaximized(); } - m_systemCloseButton->setIcon(qvariant_cast(Utils::getSystemButtonIconResource(SystemButtonType::Close, theme, ResourceType::Icon))); } FRAMELESSHELPER_END_NAMESPACE diff --git a/widgets/framelesswidgetshelper.h b/widgets/framelesswidgetshelper.h index eb4f687..dd79dcc 100644 --- a/widgets/framelesswidgetshelper.h +++ b/widgets/framelesswidgetshelper.h @@ -59,11 +59,14 @@ public: Q_INVOKABLE void setHitTestVisible(QWidget *widget, const bool visible); - void showEventHandler(QShowEvent *event); - void changeEventHandler(QEvent *event); - void paintEventHandler(QPaintEvent *event); - void mousePressEventHandler(QMouseEvent *event); - void mouseDoubleClickEventHandler(QMouseEvent *event); + Q_INVOKABLE void toggleMaximized(); + + Q_INVOKABLE void showEventHandler(QShowEvent *event); + Q_INVOKABLE void changeEventHandler(QEvent *event); + Q_INVOKABLE void paintEventHandler(QPaintEvent *event); + Q_INVOKABLE void mousePressEventHandler(QMouseEvent *event); + Q_INVOKABLE void mouseReleaseEventHandler(QMouseEvent *event); + Q_INVOKABLE void mouseDoubleClickEventHandler(QMouseEvent *event); private: void initialize(); @@ -73,9 +76,8 @@ private: void setupInitialUi(); Q_NODISCARD bool isInTitleBarDraggableArea(const QPoint &pos) const; Q_NODISCARD bool shouldDrawFrameBorder() const; - Q_NODISCARD bool isMainWindow() const; - Q_NODISCARD bool isStandardWindow() const; - Q_NODISCARD bool isCustomWindow(); + Q_NODISCARD bool isStandardLayout() const; + Q_NODISCARD bool isCustomLayout() const; private Q_SLOTS: void updateContentsMargins(); @@ -84,7 +86,8 @@ private Q_SLOTS: private: QWidget *q = nullptr; - bool m_framelessHelperInited = false; + bool m_initialized = false; + bool m_framelessHelperSetup = false; WindowLayout m_windowLayout = WindowLayout::Standard; QWidget *m_systemTitleBarWidget = nullptr; QLabel *m_systemWindowTitleLabel = nullptr;