From 4139cf0ab2a0035a485a0bf130cb5d9e6cbe0a15 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Mon, 31 May 2021 10:33:54 +0800 Subject: [PATCH] redesign API --- CMakeLists.txt | 31 ++++--- examples/mainwindow/mainwindow.cpp | 10 +-- examples/quick/qml/main.qml | 6 +- examples/widget/widget.cpp | 9 ++- framelesshelper.cpp | 78 ++++++++---------- framelesshelper.h | 8 +- framelesshelper.rc | 2 +- framelesshelper_global.h | 26 +++--- framelesshelper_win32.cpp | 41 ++-------- framelesshelper_win32.h | 5 +- framelessquickhelper.cpp | 8 +- framelessquickhelper.h | 6 +- framelesswindowsmanager.cpp | 125 +++++++++++++++-------------- framelesswindowsmanager.h | 16 ++-- lib.pro | 2 +- utilities.cpp | 101 +++++++++++++++-------- utilities.h | 50 +++++++----- 17 files changed, 266 insertions(+), 258 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48b0d9b..00498be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,9 @@ -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.15) project(FramelessHelper LANGUAGES CXX) option(BUILD_EXAMPLES "Build examples." ON) +option(TEST_UNIX "Test UNIX version (from Win32)." OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -14,7 +15,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -if(NOT CMAKE_DEBUG_POSTFIX) +if(NOT DEFINED CMAKE_DEBUG_POSTFIX) if(WIN32) set(CMAKE_DEBUG_POSTFIX d) else() @@ -22,15 +23,23 @@ if(NOT CMAKE_DEBUG_POSTFIX) endif() endif() -if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) +if(NOT DEFINED CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +endif() + +if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) endif() -if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) +if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) endif() -if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) +if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) endif() @@ -41,6 +50,8 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick) set(SOURCES framelesshelper_global.h + framelesshelper.h + framelesshelper.cpp framelesswindowsmanager.h framelesswindowsmanager.cpp utilities.h @@ -61,10 +72,6 @@ if(WIN32) framelesshelper_win32.cpp ) else() - list(APPEND SOURCES - framelesshelper.h - framelesshelper.cpp - ) if(MACOS) list(APPEND SOURCES utilities_macos.mm) else() @@ -99,10 +106,14 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE QT_NO_CAST_TO_ASCII QT_NO_KEYWORDS QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060000 + QT_DISABLE_DEPRECATED_BEFORE=0x060100 FRAMELESSHELPER_BUILD_LIBRARY ) +if(TEST_UNIX) + target_compile_definitions(${PROJECT_NAME} PRIVATE FRAMELESSHELPER_TEST_UNIX) +endif() + if(WIN32) target_compile_definitions(${PROJECT_NAME} PRIVATE WIN32_LEAN_AND_MEAN diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index c8e510f..2c19ae4 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -79,11 +79,11 @@ void MainWindow::showEvent(QShowEvent *event) const auto win = windowHandle(); if (win) { FramelessWindowsManager::addWindow(win); - FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->iconButton); - FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->minimizeButton); - FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->maximizeButton); - FramelessWindowsManager::addIgnoreObject(win, titleBarWidget->closeButton); - FramelessWindowsManager::addIgnoreObject(win, appMainWindow->menubar); + FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->iconButton, true); + FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->minimizeButton, true); + FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->maximizeButton, true); + FramelessWindowsManager::setHitTestVisible(win, titleBarWidget->closeButton, true); + FramelessWindowsManager::setHitTestVisible(win, appMainWindow->menubar, true); inited = true; } } diff --git a/examples/quick/qml/main.qml b/examples/quick/qml/main.qml index 84d20a6..d2c1f7c 100644 --- a/examples/quick/qml/main.qml +++ b/examples/quick/qml/main.qml @@ -79,7 +79,7 @@ Window { MinimizeButton { id: minimizeButton onClicked: window.showMinimized() - Component.onCompleted: framelessHelper.addIgnoreObject(minimizeButton) + Component.onCompleted: framelessHelper.setHitTestVisible(minimizeButton, true) } MaximizeButton { @@ -92,13 +92,13 @@ Window { window.showMaximized() } } - Component.onCompleted: framelessHelper.addIgnoreObject(maximizeButton) + Component.onCompleted: framelessHelper.setHitTestVisible(maximizeButton, true) } CloseButton { id: closeButton onClicked: window.close() - Component.onCompleted: framelessHelper.addIgnoreObject(closeButton) + Component.onCompleted: framelessHelper.setHitTestVisible(closeButton, true) } } } diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index c4ae775..d6d1678 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -46,7 +46,11 @@ void Widget::showEvent(QShowEvent *event) QWidget::showEvent(event); static bool inited = false; if (!inited) { - FramelessWindowsManager::addWindow(windowHandle()); + QWindow *win = windowHandle(); + FramelessWindowsManager::addWindow(win); + FramelessWindowsManager::setHitTestVisible(win, m_minimizeButton, true); + FramelessWindowsManager::setHitTestVisible(win, m_maximizeButton, true); + FramelessWindowsManager::setHitTestVisible(win, m_closeButton, true); inited = true; } } @@ -134,9 +138,6 @@ void Widget::setupUi() m_closeButton->setIcon(QIcon{QStringLiteral(":/images/button_close_black.svg")}); m_closeButton->setIconSize(systemButtonSize); connect(m_closeButton, &QPushButton::clicked, this, &Widget::close); - FramelessWindowsManager::addIgnoreObject(win, m_minimizeButton); - FramelessWindowsManager::addIgnoreObject(win, m_maximizeButton); - FramelessWindowsManager::addIgnoreObject(win, m_closeButton); const auto systemButtonLayout = new QHBoxLayout; systemButtonLayout->setContentsMargins(0, 0, 0, 0); systemButtonLayout->setSpacing(0); diff --git a/framelesshelper.cpp b/framelesshelper.cpp index 94dbf4d..62c33c5 100644 --- a/framelesshelper.cpp +++ b/framelesshelper.cpp @@ -63,44 +63,30 @@ void FramelessHelper::setTitleBarHeight(const int val) m_titleBarHeight = val; } -QObjectList FramelessHelper::getIgnoreObjects(const QWindow *window) const -{ - Q_ASSERT(window); - QObjectList ret{}; - const QObjectList objs = m_ignoreObjects.value(window); - if (!objs.isEmpty()) { - for (auto &&_obj : qAsConst(objs)) { - if (_obj) { - ret.append(_obj); - } - } - } - return ret; -} - -void FramelessHelper::addIgnoreObject(const QWindow *window, QObject *val) -{ - Q_ASSERT(window); - QObjectList objs = m_ignoreObjects[window]; - objs.append(val); - m_ignoreObjects[window] = objs; -} - bool FramelessHelper::getResizable(const QWindow *window) const { Q_ASSERT(window); + if (!window) { + return false; + } return !m_fixedSize.value(window); } void FramelessHelper::setResizable(const QWindow *window, const bool val) { Q_ASSERT(window); - m_fixedSize[window] = !val; + if (!window) { + return; + } + m_fixedSize.insert(window, !val); } void FramelessHelper::removeWindowFrame(QWindow *window) { Q_ASSERT(window); + if (!window) { + return; + } // TODO: check whether these flags are correct for Linux and macOS. window->setFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint); @@ -112,6 +98,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) { Q_ASSERT(object); Q_ASSERT(event); + if (!object || !event) { + return false; + } if (!object->isWindowType()) { return false; } @@ -165,22 +154,22 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) } return Qt::CursorShape::ArrowCursor; }; - const auto isInTitlebarArea = [this](const QPointF &globalPoint, - const QPointF &point, - const QWindow *window) -> bool { + const auto isInTitlebarArea = [this](const QPointF &point, const QWindow *window) -> bool { Q_ASSERT(window); - return (point.y() <= m_titleBarHeight) - && !Utilities::isMouseInSpecificObjects(globalPoint, getIgnoreObjects(window)); + if (!window) { + return false; + } + return (point.y() <= m_titleBarHeight) && !Utilities::isHitTestVisible(window); }; const auto moveOrResize = - [this, &getWindowEdges, &isInTitlebarArea](const QPointF &globalPoint, - const QPointF &point, - QWindow *window) { + [this, &getWindowEdges, &isInTitlebarArea](const QPointF &point, QWindow *window) { Q_ASSERT(window); - //const QPointF deltaPoint = globalPoint - m_pOldMousePos; + if (!window) { + return; + } const Qt::Edges edges = getWindowEdges(point, window->width(), window->height()); if (edges == Qt::Edges{}) { - if (isInTitlebarArea(globalPoint, point, window)) { + if (isInTitlebarArea(point, window)) { if (!window->startSystemMove()) { // ### FIXME: TO BE IMPLEMENTED! qWarning() << "Current OS doesn't support QWindow::startSystemMove()."; @@ -188,8 +177,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) } } else { if ((window->windowState() == Qt::WindowState::WindowNoState) - && !Utilities::isMouseInSpecificObjects(globalPoint, getIgnoreObjects(window)) - && getResizable(window)) { + && !Utilities::isHitTestVisible(window) && getResizable(window)) { if (!window->startSystemResize(edges)) { // ### FIXME: TO BE IMPLEMENTED! qWarning() << "Current OS doesn't support QWindow::startSystemResize()."; @@ -199,6 +187,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) }; const auto getMousePos = [](const QMouseEvent *e, const bool global) -> QPointF { Q_ASSERT(e); + if (!e) { + return {}; + } #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) return global ? e->globalPosition() : e->scenePosition(); #else @@ -212,9 +203,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) if (mouseEvent->button() != Qt::MouseButton::LeftButton) { break; } - if (isInTitlebarArea(getMousePos(mouseEvent, true), - getMousePos(mouseEvent, false), - currentWindow)) { + if (isInTitlebarArea(getMousePos(mouseEvent, false), currentWindow)) { if (currentWindow->windowState() == Qt::WindowState::WindowFullScreen) { break; } @@ -235,9 +224,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) } m_bIsMRBPressed = true; m_pOldMousePos = getMousePos(mouseEvent, true); - moveOrResize(getMousePos(mouseEvent, true), - getMousePos(mouseEvent, false), - currentWindow); + moveOrResize(getMousePos(mouseEvent, false), currentWindow); } } break; case QEvent::MouseMove: { @@ -265,12 +252,11 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) case QEvent::TouchBegin: case QEvent::TouchUpdate: { #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - const auto point = static_cast(event)->points().first(); - moveOrResize(point.globalPosition(), point.position(), currentWindow); + const auto point = static_cast(event)->points().first().position(); #else - const auto point = static_cast(event)->touchPoints().first(); - moveOrResize(point.screenPos(), point.pos(), currentWindow); + const auto point = static_cast(event)->touchPoints().first().pos(); #endif + moveOrResize(point, currentWindow); } break; default: break; diff --git a/framelesshelper.h b/framelesshelper.h index 572885f..ecb6145 100644 --- a/framelesshelper.h +++ b/framelesshelper.h @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (C) 2020 by wangwenx190 (Yuhang Zhao) + * Copyright (C) 2021 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 @@ -34,7 +34,7 @@ QT_BEGIN_NAMESPACE QT_FORWARD_DECLARE_CLASS(QWindow) QT_END_NAMESPACE -class FRAMELESSHELPER_EXPORT FramelessHelper : public QObject +class FRAMELESSHELPER_API FramelessHelper : public QObject { Q_OBJECT Q_DISABLE_COPY_MOVE(FramelessHelper) @@ -54,9 +54,6 @@ public: int getTitleBarHeight() const; void setTitleBarHeight(const int val); - void addIgnoreObject(const QWindow *window, QObject *val); - QObjectList getIgnoreObjects(const QWindow *window) const; - bool getResizable(const QWindow *window) const; void setResizable(const QWindow *window, const bool val); @@ -68,7 +65,6 @@ private: // the scale factor is 1.0. Don't know how to acquire these values on UNIX // platforms through native API. int m_borderWidth = 8, m_borderHeight = 8, m_titleBarHeight = 30; - QHash m_ignoreObjects = {}; QHash m_fixedSize = {}; }; #endif diff --git a/framelesshelper.rc b/framelesshelper.rc index 2bea3b4..3f00019 100644 --- a/framelesshelper.rc +++ b/framelesshelper.rc @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (C) 2020 by wangwenx190 (Yuhang Zhao) + * Copyright (C) 2021 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 diff --git a/framelesshelper_global.h b/framelesshelper_global.h index 74d0dd8..88e65f4 100644 --- a/framelesshelper_global.h +++ b/framelesshelper_global.h @@ -26,14 +26,14 @@ #include -#ifndef FRAMELESSHELPER_EXPORT +#ifndef FRAMELESSHELPER_API #ifdef FRAMELESSHELPER_STATIC -#define FRAMELESSHELPER_EXPORT +#define FRAMELESSHELPER_API #else #ifdef FRAMELESSHELPER_BUILD_LIBRARY -#define FRAMELESSHELPER_EXPORT Q_DECL_EXPORT +#define FRAMELESSHELPER_API Q_DECL_EXPORT #else -#define FRAMELESSHELPER_EXPORT Q_DECL_IMPORT +#define FRAMELESSHELPER_API Q_DECL_IMPORT #endif #endif #endif @@ -42,31 +42,31 @@ #define Q_OS_WINDOWS #endif -#ifndef Q_DISABLE_MOVE -#define Q_DISABLE_MOVE(Class) \ +#ifndef Q_DISABLE_COPY_MOVE +#define Q_DISABLE_COPY_MOVE(Class) \ + Q_DISABLE_COPY(Class) \ Class(Class &&) = delete; \ Class &operator=(Class &&) = delete; #endif -#ifndef Q_DISABLE_COPY_MOVE -#define Q_DISABLE_COPY_MOVE(Class) \ - Q_DISABLE_COPY(Class) \ - Q_DISABLE_MOVE(Class) -#endif - #if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0)) #define qAsConst(i) std::as_const(i) #endif +#if !defined(Q_OS_WINDOWS) || defined(FRAMELESSHELPER_TEST_UNIX) +#define FRAMELESSHELPER_USE_UNIX_VERSION +#endif + namespace _flh_global { [[maybe_unused]] const char _flh_framelessEnabled_flag[] = "_FRAMELESSHELPER_FRAMELESS_MODE_ENABLED"; [[maybe_unused]] const char _flh_borderWidth_flag[] = "_FRAMELESSHELPER_WINDOW_BORDER_WIDTH"; [[maybe_unused]] const char _flh_borderHeight_flag[] = "_FRAMELESSHELPER_WINDOW_BORDER_HEIGHT"; [[maybe_unused]] const char _flh_titleBarHeight_flag[] = "_FRAMELESSHELPER_WINDOW_TITLE_BAR_HEIGHT"; -[[maybe_unused]] const char _flh_ignoredObjects_flag[] = "_FRAMELESSHELPER_WINDOW_TITLE_BAR_IGNORED_OBJECTS"; +[[maybe_unused]] const char _flh_hitTestVisible_flag[] = "_FRAMELESSHELPER_HIT_TEST_VISIBLE"; [[maybe_unused]] const char _flh_useNativeTitleBar_flag[] = "_FRAMELESSHELPER_USE_NATIVE_TITLE_BAR"; [[maybe_unused]] const char _flh_preserveNativeFrame_flag[] = "_FRAMELESSHELPER_PRESERVE_NATIVE_WINDOW_FRAME"; [[maybe_unused]] const char _flh_forcePreserveNativeFrame_flag[] = "_FRAMELESSHELPER_FORCE_PRESERVE_NATIVE_WINDOW_FRAME"; +[[maybe_unused]] const char _flh_nativeParent_flag[] = "_FRAMELESSHELPER_NATIVE_PARENT"; } diff --git a/framelesshelper_win32.cpp b/framelesshelper_win32.cpp index 03656b4..f3625be 100644 --- a/framelesshelper_win32.cpp +++ b/framelesshelper_win32.cpp @@ -156,42 +156,13 @@ void FramelessHelperWin::removeFramelessWindow(QWindow *window) installHelper(window, false); } -void FramelessHelperWin::setIgnoredObjects(QWindow *window, const QObjectList &objects) -{ - Q_ASSERT(window); - if (!window) { - return; - } - window->setProperty(_flh_global::_flh_ignoredObjects_flag, QVariant::fromValue(objects)); -} - -QObjectList FramelessHelperWin::getIgnoredObjects(const QWindow *window) -{ - Q_ASSERT(window); - if (!window) { - return {}; - } - return qvariant_cast(window->property(_flh_global::_flh_ignoredObjects_flag)); -} - #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) #else bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *message, long *result) #endif { - // "result" can't be null in theory and I don't see any projects check - // this, everyone is assuming it will never be null, including Microsoft, - // but according to Lucas, frameless applications crashed on many Win7 - // machines because it's null. The temporary solution is also strange: - // upgrade drivers or switch to the basic theme. - if (!result) { - return false; - } - // The example code in Qt's documentation has this check. I don't know - // whether we really need this check or not, but adding this check won't - // bring us harm anyway. - if (eventType != "windows_generic_MSG") { + if ((eventType != QByteArrayLiteral("windows_generic_MSG")) || !message || !result) { return false; } #if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1)) @@ -200,13 +171,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me #else const auto msg = static_cast(message); #endif - if (!msg || (msg && !msg->hwnd)) { + if (!msg->hwnd) { // Why sometimes the window handle is null? Is it designed to be? // Anyway, we should skip it in this case. return false; } const QWindow *window = Utilities::findWindow(reinterpret_cast(msg->hwnd)); - if (!window || (window && !window->property(_flh_global::_flh_framelessEnabled_flag).toBool())) { + if (!window || !window->property(_flh_global::_flh_framelessEnabled_flag).toBool()) { return false; } switch (msg->message) { @@ -551,15 +522,13 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me break; } - const qreal dpr = window->devicePixelRatio(); - const QPointF globalMouse = QCursor::pos(window->screen()) * dpr; + const QPointF globalMouse = Utilities::getGlobalMousePosition(window); POINT winLocalMouse = {qRound(globalMouse.x()), qRound(globalMouse.y())}; ScreenToClient(msg->hwnd, &winLocalMouse); const QPointF localMouse = {static_cast(winLocalMouse.x), static_cast(winLocalMouse.y)}; - const bool isInIgnoreObjects = Utilities::isMouseInSpecificObjects(globalMouse, getIgnoredObjects(window), dpr); const int bh = getSystemMetric(window, Utilities::SystemMetric::BorderHeight, true); const int tbh = getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, true); - const bool isTitleBar = (localMouse.y() <= tbh) && !isInIgnoreObjects; + const bool isTitleBar = (localMouse.y() <= tbh) && !Utilities::isHitTestVisible(window); const bool isTop = localMouse.y() <= bh; if (shouldHaveWindowFrame()) { // This will handle the left, right and bottom parts of the frame diff --git a/framelesshelper_win32.h b/framelesshelper_win32.h index 2fa79fe..57e4411 100644 --- a/framelesshelper_win32.h +++ b/framelesshelper_win32.h @@ -32,7 +32,7 @@ QT_BEGIN_NAMESPACE QT_FORWARD_DECLARE_CLASS(QWindow) QT_END_NAMESPACE -class FRAMELESSHELPER_EXPORT FramelessHelperWin : public QAbstractNativeEventFilter +class FRAMELESSHELPER_API FramelessHelperWin : public QAbstractNativeEventFilter { Q_DISABLE_COPY_MOVE(FramelessHelperWin) @@ -44,9 +44,6 @@ public: static bool isWindowFrameless(const QWindow *window); static void removeFramelessWindow(QWindow *window); - static void setIgnoredObjects(QWindow *window, const QObjectList &objects); - static QObjectList getIgnoredObjects(const QWindow *window); - static void setBorderWidth(QWindow *window, const int bw); static void setBorderHeight(QWindow *window, const int bh); static void setTitleBarHeight(QWindow *window, const int tbh); diff --git a/framelessquickhelper.cpp b/framelessquickhelper.cpp index 1ed8e25..e1c5ae6 100644 --- a/framelessquickhelper.cpp +++ b/framelessquickhelper.cpp @@ -97,11 +97,11 @@ void FramelessQuickHelper::removeWindowFrame() FramelessWindowsManager::addWindow(window()); } -void FramelessQuickHelper::addIgnoreObject(QQuickItem *val) +void FramelessQuickHelper::setHitTestVisible(QQuickItem *item, const bool visible) { - Q_ASSERT(val); - if (!val) { + Q_ASSERT(item); + if (!item) { return; } - FramelessWindowsManager::addIgnoreObject(window(), val); + FramelessWindowsManager::setHitTestVisible(window(), item, visible); } diff --git a/framelessquickhelper.h b/framelessquickhelper.h index 13c7029..8945e9c 100644 --- a/framelessquickhelper.h +++ b/framelessquickhelper.h @@ -27,11 +27,11 @@ #include "framelesshelper_global.h" #include -class FRAMELESSHELPER_EXPORT FramelessQuickHelper : public QQuickItem +class FRAMELESSHELPER_API FramelessQuickHelper : public QQuickItem { Q_OBJECT Q_DISABLE_COPY_MOVE(FramelessQuickHelper) -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) +#ifdef QML_NAMED_ELEMENT QML_NAMED_ELEMENT(FramelessHelper) #endif Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged) @@ -60,7 +60,7 @@ public: public Q_SLOTS: void removeWindowFrame(); - void addIgnoreObject(QQuickItem *val); + void setHitTestVisible(QQuickItem *item, const bool visible); Q_SIGNALS: void borderWidthChanged(); diff --git a/framelesswindowsmanager.cpp b/framelesswindowsmanager.cpp index 74c355d..9ed6066 100644 --- a/framelesswindowsmanager.cpp +++ b/framelesswindowsmanager.cpp @@ -23,136 +23,141 @@ */ #include "framelesswindowsmanager.h" +#include #include #include "utilities.h" -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION +#include "framelesshelper.h" +#else #include #include "framelesshelper_win32.h" -#else -#include "framelesshelper.h" #endif -#ifndef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION Q_GLOBAL_STATIC(FramelessHelper, framelessHelper) #endif FramelessWindowsManager::FramelessWindowsManager() = default; -void FramelessWindowsManager::addWindow(const QWindow *window) +void FramelessWindowsManager::addWindow(QWindow *window) { Q_ASSERT(window); if (!window) { return; } -#ifdef Q_OS_WINDOWS - const auto win = const_cast(window); - FramelessHelperWin::addFramelessWindow(win); - // Work-around a Win32 multi-monitor bug. - QObject::connect(win, &QWindow::screenChanged, [win](QScreen *screen){ - Q_UNUSED(screen); - win->resize(win->size()); - }); +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + framelessHelper()->removeWindowFrame(window); #else - framelessHelper()->removeWindowFrame(const_cast(window)); + FramelessHelperWin::addFramelessWindow(window); + // Work-around a Win32 multi-monitor bug. + QObject::connect(window, &QWindow::screenChanged, [window](QScreen *screen){ + Q_UNUSED(screen); + window->resize(window->size()); + }); #endif } -void FramelessWindowsManager::addIgnoreObject(const QWindow *window, QObject *object) +void FramelessWindowsManager::setHitTestVisible(QWindow *window, QObject *object, const bool value) { Q_ASSERT(window); - if (!window) { + Q_ASSERT(object); + if (!window || !object) { return; } -#ifdef Q_OS_WINDOWS - QObjectList objects = FramelessHelperWin::getIgnoredObjects(window); - objects.append(object); - FramelessHelperWin::setIgnoredObjects(const_cast(window), objects); -#else - framelessHelper()->addIgnoreObject(window, object); -#endif + if (!object->isWidgetType() && !object->inherits("QQuickItem")) { + qWarning() << object << "is not a QWidget or QQuickItem."; + return; + } + const QObject *nativeParent = Utilities::getNativeParent(object); + Q_ASSERT(nativeParent); + if (!nativeParent) { + return; + } + window->setProperty(_flh_global::_flh_nativeParent_flag, QVariant::fromValue(nativeParent)); + object->setProperty(_flh_global::_flh_hitTestVisible_flag, value); } int FramelessWindowsManager::getBorderWidth(const QWindow *window) { -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + Q_UNUSED(window); + return framelessHelper()->getBorderWidth(); +#else Q_ASSERT(window); if (!window) { return 0; } return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderWidth, false); -#else - Q_UNUSED(window); - return framelessHelper()->getBorderWidth(); #endif } -void FramelessWindowsManager::setBorderWidth(const QWindow *window, const int value) +void FramelessWindowsManager::setBorderWidth(QWindow *window, const int value) { -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + Q_UNUSED(window); + framelessHelper()->setBorderWidth(value); +#else Q_ASSERT(window); if (!window) { return; } - FramelessHelperWin::setBorderWidth(const_cast(window), value); -#else - Q_UNUSED(window); - framelessHelper()->setBorderWidth(value); + FramelessHelperWin::setBorderWidth(window, value); #endif } int FramelessWindowsManager::getBorderHeight(const QWindow *window) { -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + Q_UNUSED(window); + return framelessHelper()->getBorderHeight(); +#else Q_ASSERT(window); if (!window) { return 0; } return Utilities::getSystemMetric(window, Utilities::SystemMetric::BorderHeight, false); -#else - Q_UNUSED(window); - return framelessHelper()->getBorderHeight(); #endif } -void FramelessWindowsManager::setBorderHeight(const QWindow *window, const int value) +void FramelessWindowsManager::setBorderHeight(QWindow *window, const int value) { -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + Q_UNUSED(window); + framelessHelper()->setBorderHeight(value); +#else Q_ASSERT(window); if (!window) { return; } - FramelessHelperWin::setBorderHeight(const_cast(window), value); -#else - Q_UNUSED(window); - framelessHelper()->setBorderHeight(value); + FramelessHelperWin::setBorderHeight(window, value); #endif } int FramelessWindowsManager::getTitleBarHeight(const QWindow *window) { -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + Q_UNUSED(window); + return framelessHelper()->getTitleBarHeight(); +#else Q_ASSERT(window); if (!window) { return 0; } return Utilities::getSystemMetric(window, Utilities::SystemMetric::TitleBarHeight, false); -#else - Q_UNUSED(window); - return framelessHelper()->getTitleBarHeight(); #endif } -void FramelessWindowsManager::setTitleBarHeight(const QWindow *window, const int value) +void FramelessWindowsManager::setTitleBarHeight(QWindow *window, const int value) { -#ifdef Q_OS_WINDOWS +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION + Q_UNUSED(window); + framelessHelper()->setTitleBarHeight(value); +#else Q_ASSERT(window); if (!window) { return; } - FramelessHelperWin::setTitleBarHeight(const_cast(window), value); -#else - Q_UNUSED(window); - framelessHelper()->setTitleBarHeight(value); + FramelessHelperWin::setTitleBarHeight(window, value); #endif } @@ -162,22 +167,22 @@ bool FramelessWindowsManager::getResizable(const QWindow *window) if (!window) { return false; } -#ifdef Q_OS_WINDOWS - return !Utilities::isWindowFixedSize(window); -#else +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION return framelessHelper()->getResizable(window); +#else + return !Utilities::isWindowFixedSize(window); #endif } -void FramelessWindowsManager::setResizable(const QWindow *window, const bool value) +void FramelessWindowsManager::setResizable(QWindow *window, const bool value) { Q_ASSERT(window); if (!window) { return; } -#ifdef Q_OS_WINDOWS - const_cast(window)->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value); -#else +#ifdef FRAMELESSHELPER_USE_UNIX_VERSION framelessHelper()->setResizable(window, value); +#else + window->setFlag(Qt::MSWindowsFixedSizeDialogHint, !value); #endif } diff --git a/framelesswindowsmanager.h b/framelesswindowsmanager.h index d6f1810..20ec23a 100644 --- a/framelesswindowsmanager.h +++ b/framelesswindowsmanager.h @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (C) 2020 by wangwenx190 (Yuhang Zhao) + * Copyright (C) 2021 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 @@ -32,7 +32,7 @@ QT_FORWARD_DECLARE_CLASS(QObject) QT_FORWARD_DECLARE_CLASS(QWindow) QT_END_NAMESPACE -class FRAMELESSHELPER_EXPORT FramelessWindowsManager +class FRAMELESSHELPER_API FramelessWindowsManager { Q_DISABLE_COPY_MOVE(FramelessWindowsManager) @@ -40,19 +40,19 @@ public: explicit FramelessWindowsManager(); ~FramelessWindowsManager() = default; - static void addWindow(const QWindow *window); + static void addWindow(QWindow *window); - static void addIgnoreObject(const QWindow *window, QObject *object); + static void setHitTestVisible(QWindow *window, QObject *object, const bool value = true); static int getBorderWidth(const QWindow *window); - static void setBorderWidth(const QWindow *window, const int value); + static void setBorderWidth(QWindow *window, const int value); static int getBorderHeight(const QWindow *window); - static void setBorderHeight(const QWindow *window, const int value); + static void setBorderHeight(QWindow *window, const int value); static int getTitleBarHeight(const QWindow *window); - static void setTitleBarHeight(const QWindow *window, const int value); + static void setTitleBarHeight(QWindow *window, const int value); static bool getResizable(const QWindow *window); - static void setResizable(const QWindow *window, const bool value = true); + static void setResizable(QWindow *window, const bool value = true); }; diff --git a/lib.pro b/lib.pro index ee2c15e..4e34d84 100644 --- a/lib.pro +++ b/lib.pro @@ -9,7 +9,7 @@ DEFINES += \ QT_NO_CAST_TO_ASCII \ QT_NO_KEYWORDS \ QT_DEPRECATED_WARNINGS \ - QT_DISABLE_DEPRECATED_BEFORE=0x060000 \ + QT_DISABLE_DEPRECATED_BEFORE=0x060100 \ FRAMELESSHELPER_BUILD_LIBRARY HEADERS += \ framelesshelper_global.h \ diff --git a/utilities.cpp b/utilities.cpp index 1659deb..f9f01eb 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -67,50 +67,87 @@ bool Utilities::isWindowFixedSize(const QWindow *window) return false; } -bool Utilities::isMouseInSpecificObjects(const QPointF &mousePos, const QObjectList &objects, const qreal dpr) +QPointF Utilities::getGlobalMousePosition(const QWindow *window) { - if (mousePos.isNull()) { - qWarning() << "Mouse position point is null."; + if (window) { + return (QCursor::pos(window->screen()) * window->devicePixelRatio()); + } else { + const qreal dpr = 1.0; // TODO + return (QCursor::pos() * dpr); + } +} + +bool Utilities::isHitTestVisible(const QWindow *window) +{ + Q_ASSERT(window); + if (!window) { return false; } - if (objects.isEmpty()) { - qWarning() << "Object list is empty."; + QObjectList objs = {}; + const auto target = qvariant_cast(window->property(_flh_global::_flh_nativeParent_flag)); + objs = target ? target->findChildren() : window->findChildren(); + if (objs.isEmpty()) { return false; } - for (auto &&object : qAsConst(objects)) { - if (!object) { - qWarning() << "Object pointer is null."; + for (auto &&obj : qAsConst(objs)) { + if (!obj || !(obj->isWidgetType() || obj->inherits("QQuickItem"))) { continue; } - if (!object->isWidgetType() && !object->inherits("QQuickItem")) { - qWarning() << object << "is not a QWidget or QQuickItem!"; + if (!obj->property(_flh_global::_flh_hitTestVisible_flag).toBool() || !obj->property("visible").toBool()) { continue; } - if (!object->property("visible").toBool()) { - qDebug() << "Skipping invisible object" << object; - continue; - } - const auto mapOriginPointToWindow = [](const QObject *obj) -> QPointF { - Q_ASSERT(obj); - if (!obj) { - return {}; - } - QPointF point = {obj->property("x").toReal(), obj->property("y").toReal()}; - for (QObject *parent = obj->parent(); parent; parent = parent->parent()) { - point += {parent->property("x").toReal(), parent->property("y").toReal()}; - if (parent->isWindowType()) { - break; - } - } - return point; - }; - const QPointF originPoint = mapOriginPointToWindow(object); - const qreal width = object->property("width").toReal(); - const qreal height = object->property("height").toReal(); + const QPointF originPoint = mapOriginPointToWindow(obj); + const qreal width = obj->property("width").toReal(); + const qreal height = obj->property("height").toReal(); + const qreal dpr = window->devicePixelRatio(); const QRectF rect = {originPoint.x() * dpr, originPoint.y() * dpr, width * dpr, height * dpr}; - if (rect.contains(mousePos)) { + if (rect.contains(getGlobalMousePosition(window))) { return true; } } return false; } + +QObject *Utilities::getNativeParent(const QObject *object) +{ + Q_ASSERT(object); + if (!object) { + return nullptr; + } + if (!object->isWidgetType() && !object->inherits("QQuickItem")) { + qWarning() << object << "is not a QWidget or a QQuickItem."; + return nullptr; + } + QObject *parent = object->parent(); + while (parent) { + QObject *p = parent->parent(); + if (!p) { + return parent; + } + if (p->isWindowType()) { + return parent; + } + parent = p; + } + return parent; +} + +QPointF Utilities::mapOriginPointToWindow(const QObject *object) +{ + Q_ASSERT(object); + if (!object) { + return {}; + } + if (!object->isWidgetType() && !object->inherits("QQuickItem")) { + qWarning() << object << "is not a QWidget or a QQuickItem."; + return {}; + } + QPointF point = {object->property("x").toReal(), object->property("y").toReal()}; + for (QObject *parent = object->parent(); parent; parent = parent->parent()) { + point += {parent->property("x").toReal(), parent->property("y").toReal()}; + if (parent->isWindowType()) { + break; + } + } + return point; +} diff --git a/utilities.h b/utilities.h index 08df5e2..56a74b3 100644 --- a/utilities.h +++ b/utilities.h @@ -38,42 +38,48 @@ enum class SystemMetric }; // Common -FRAMELESSHELPER_EXPORT int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue = false); +FRAMELESSHELPER_API int getSystemMetric(const QWindow *window, const SystemMetric metric, const bool dpiAware, const bool forceSystemValue = false); -FRAMELESSHELPER_EXPORT bool isLightThemeEnabled(); -FRAMELESSHELPER_EXPORT bool isDarkThemeEnabled(); +FRAMELESSHELPER_API bool isLightThemeEnabled(); +FRAMELESSHELPER_API bool isDarkThemeEnabled(); -FRAMELESSHELPER_EXPORT QWindow *findWindow(const WId winId); +FRAMELESSHELPER_API QWindow *findWindow(const WId winId); -FRAMELESSHELPER_EXPORT bool shouldUseNativeTitleBar(); +FRAMELESSHELPER_API bool shouldUseNativeTitleBar(); -FRAMELESSHELPER_EXPORT bool isWindowFixedSize(const QWindow *window); +FRAMELESSHELPER_API bool isWindowFixedSize(const QWindow *window); -FRAMELESSHELPER_EXPORT bool isMouseInSpecificObjects(const QPointF &mousePos, const QObjectList &objects, const qreal dpr = 1.0); +FRAMELESSHELPER_API QPointF getGlobalMousePosition(const QWindow *window); -FRAMELESSHELPER_EXPORT QColor getNativeWindowFrameColor(const bool isActive = true); +FRAMELESSHELPER_API bool isHitTestVisible(const QWindow *window); + +FRAMELESSHELPER_API QColor getNativeWindowFrameColor(const bool isActive = true); + +FRAMELESSHELPER_API QObject *getNativeParent(const QObject *object); + +FRAMELESSHELPER_API QPointF mapOriginPointToWindow(const QObject *object); #ifdef Q_OS_WINDOWS // Windows specific -FRAMELESSHELPER_EXPORT bool isWin7OrGreater(); -FRAMELESSHELPER_EXPORT bool isWin8OrGreater(); -FRAMELESSHELPER_EXPORT bool isWin8Point1OrGreater(); -FRAMELESSHELPER_EXPORT bool isWin10OrGreater(); -FRAMELESSHELPER_EXPORT bool isWin10OrGreater(const int subVer); +FRAMELESSHELPER_API bool isWin7OrGreater(); +FRAMELESSHELPER_API bool isWin8OrGreater(); +FRAMELESSHELPER_API bool isWin8Point1OrGreater(); +FRAMELESSHELPER_API bool isWin10OrGreater(); +FRAMELESSHELPER_API bool isWin10OrGreater(const int subVer); -FRAMELESSHELPER_EXPORT bool isDwmBlurAvailable(); +FRAMELESSHELPER_API bool isDwmBlurAvailable(); -FRAMELESSHELPER_EXPORT bool isColorizationEnabled(); -FRAMELESSHELPER_EXPORT QColor getColorizationColor(); +FRAMELESSHELPER_API bool isColorizationEnabled(); +FRAMELESSHELPER_API QColor getColorizationColor(); -FRAMELESSHELPER_EXPORT void triggerFrameChange(const QWindow *window); -FRAMELESSHELPER_EXPORT void updateFrameMargins(const QWindow *window, const bool reset); -FRAMELESSHELPER_EXPORT void updateQtFrameMargins(QWindow *window, const bool enable); +FRAMELESSHELPER_API void triggerFrameChange(const QWindow *window); +FRAMELESSHELPER_API void updateFrameMargins(const QWindow *window, const bool reset); +FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable); -FRAMELESSHELPER_EXPORT quint32 getWindowDpi(const QWindow *window); -FRAMELESSHELPER_EXPORT QMargins getWindowNativeFrameMargins(const QWindow *window); +FRAMELESSHELPER_API quint32 getWindowDpi(const QWindow *window); +FRAMELESSHELPER_API QMargins getWindowNativeFrameMargins(const QWindow *window); -FRAMELESSHELPER_EXPORT void displaySystemMenu(const QWindow *window, const QPoint &pos = {}); +FRAMELESSHELPER_API void displaySystemMenu(const QWindow *window, const QPoint &pos = {}); #endif }