diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 5470396..f5f32ba 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -78,6 +78,7 @@ FRAMELESSHELPER_CORE_API void startSystemResize(QWindow *window, const Qt::Edges [[nodiscard]] FRAMELESSHELPER_CORE_API QVariant getSystemButtonIconResource (const SystemButtonType button, const SystemTheme theme, const ResourceType type); FRAMELESSHELPER_CORE_API void sendMouseReleaseEvent(); +[[nodiscard]] FRAMELESSHELPER_CORE_API QWindow *findWindow(const WId winId); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8OrGreater(); @@ -93,7 +94,7 @@ FRAMELESSHELPER_CORE_API void updateInternalWindowFrameMargins(QWindow *window, [[nodiscard]] FRAMELESSHELPER_CORE_API bool isFullScreen(const WId winId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowNoState(const WId winId); FRAMELESSHELPER_CORE_API void syncWmPaintWithDwm(); -FRAMELESSHELPER_CORE_API void showSystemMenu(const WId winId, const QPoint &pos); +FRAMELESSHELPER_CORE_API void showSystemMenu(const QWindow *window, const QPoint &pos); [[nodiscard]] FRAMELESSHELPER_CORE_API QColor getDwmColorizationColor(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode(); [[nodiscard]] FRAMELESSHELPER_CORE_API DwmColorizationArea getDwmColorizationArea(); @@ -110,7 +111,7 @@ FRAMELESSHELPER_CORE_API void fixupQtInternals(const WId winId); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowFrameBorderVisible(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isFrameBorderColorized(); -FRAMELESSHELPER_CORE_API void installSystemMenuHook(const WId winId); +FRAMELESSHELPER_CORE_API void installSystemMenuHook(const QWindow *window); FRAMELESSHELPER_CORE_API void uninstallSystemMenuHook(const WId winId); FRAMELESSHELPER_CORE_API void tryToBeCompatibleWithQtFramelessWindowHint(QWindow *window, const bool enable); #endif // Q_OS_WINDOWS diff --git a/include/FramelessHelper/Quick/framelessquickwindow.h b/include/FramelessHelper/Quick/framelessquickwindow.h index f00cb06..c0450de 100644 --- a/include/FramelessHelper/Quick/framelessquickwindow.h +++ b/include/FramelessHelper/Quick/framelessquickwindow.h @@ -23,3 +23,20 @@ */ #pragma once + +#include "framelesshelperquick_global.h" +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class FRAMELESSHELPER_QUICK_API FramelessQuickWindow : public QQuickWindow +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(FramelessQuickWindow) + +public: + explicit FramelessQuickWindow(QWindow *parent = nullptr); + ~FramelessQuickWindow() override; +}; + +FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 8cee0eb..fb2a5dc 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -487,7 +487,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const bool full = Utils::isFullScreen(winId); const int frameSizeY = Utils::getResizeBorderThickness(winId, false, true); const bool isTop = (localPos.y < frameSizeY); - const bool isTitleBar = (false || (options & Option::DisableDragging)); + const bool isTitleBar = (false && !(options & Option::DisableDragging)); if (frameBorderVisible) { // This will handle the left, right and bottom parts of the frame // because we didn't change them. diff --git a/src/core/framelesswindowsmanager.cpp b/src/core/framelesswindowsmanager.cpp index 0b11c82..f341623 100644 --- a/src/core/framelesswindowsmanager.cpp +++ b/src/core/framelesswindowsmanager.cpp @@ -140,6 +140,9 @@ WId FramelessWindowsManagerPrivate::findWinIdById(const QUuid &value) const FramelessWindowsManager::FramelessWindowsManager(QObject *parent) : QObject(parent) { d_ptr.reset(new FramelessWindowsManagerPrivate(this)); + if (!QCoreApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) { + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + } } FramelessWindowsManager::~FramelessWindowsManager() = default; @@ -194,7 +197,7 @@ void FramelessWindowsManager::addWindow(QWindow *window) } const auto options = qvariant_cast(window->property(kInternalOptionsFlag)); if (!(options & Option::DontInstallSystemMenuHook)) { - Utils::installSystemMenuHook(winId); + Utils::installSystemMenuHook(window); } #endif } diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 74bda13..46070cd 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -25,6 +25,7 @@ #include "utils.h" #include #include +#include // The "Q_INIT_RESOURCE()" macro can't be used within a namespace, // so we wrap it into a separate function outside of the namespace and @@ -150,4 +151,24 @@ QVariant Utils::getSystemButtonIconResource return {}; } +QWindow *Utils::findWindow(const WId winId) +{ + Q_ASSERT(winId); + if (!winId) { + return nullptr; + } + const QWindowList windows = QGuiApplication::topLevelWindows(); + if (windows.isEmpty()) { + return nullptr; + } + for (auto &&window : qAsConst(windows)) { + if (window && window->handle()) { + if (window->winId() == winId) { + return window; + } + } + } + return nullptr; +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index bbd298e..63b86f9 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -57,6 +57,7 @@ struct Win32UtilsHelper { QMutex mutex = {}; QHash qtWindowProcs = {}; + QHash windowMapping = {}; explicit Win32UtilsHelper() = default; ~Win32UtilsHelper() = default; @@ -181,6 +182,12 @@ static const QString successErrorText = QStringLiteral("The operation completed g_utilsHelper()->mutex.unlock(); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } + const QWindow * const window = g_utilsHelper()->windowMapping.value(hWnd); + Q_ASSERT(window); + if (!window) { + g_utilsHelper()->mutex.unlock(); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } g_utilsHelper()->mutex.unlock(); const auto winId = reinterpret_cast(hWnd); const auto getGlobalPosFromMouse = [lParam]() -> QPoint { @@ -237,7 +244,7 @@ static const QString successErrorText = QStringLiteral("The operation completed } } if (shouldShowSystemMenu) { - Utils::showSystemMenu(winId, globalPos); + Utils::showSystemMenu(window, globalPos); // QPA's internal code will handle system menu events separately, and its // behavior is not what we would want to see because it doesn't know our // window doesn't have any window frame now, so return early here to avoid @@ -487,13 +494,13 @@ DwmColorizationArea Utils::getDwmColorizationArea() return DwmColorizationArea::None; } -void Utils::showSystemMenu(const WId winId, const QPoint &pos) +void Utils::showSystemMenu(const QWindow *window, const QPoint &pos) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(window); + if (!window) { return; } - const auto hWnd = reinterpret_cast(winId); + const auto hWnd = reinterpret_cast(window->winId()); const HMENU menu = GetSystemMenu(hWnd, FALSE); if (!menu) { qWarning() << getSystemErrorMessage(QStringLiteral("GetSystemMenu")); @@ -514,19 +521,21 @@ void Utils::showSystemMenu(const WId winId, const QPoint &pos) return true; }; const bool maxOrFull = (IsMaximized(hWnd) || isFullScreen(reinterpret_cast(hWnd))); - if (!setState(SC_RESTORE, maxOrFull, true)) { + const auto options = qvariant_cast(window->property(kInternalOptionsFlag)); + const bool fixedSize = (isWindowFixedSize(window) || (options & Option::DisableResizing)); + if (!setState(SC_RESTORE, (maxOrFull && !fixedSize), true)) { return; } - if (!setState(SC_MOVE, !maxOrFull, false)) { + if (!setState(SC_MOVE, (!maxOrFull && !(options & Option::DisableDragging)), false)) { return; } - if (!setState(SC_SIZE, !maxOrFull, false)) { + if (!setState(SC_SIZE, (!maxOrFull && !fixedSize), false)) { return; } if (!setState(SC_MINIMIZE, true, false)) { return; } - if (!setState(SC_MAXIMIZE, !maxOrFull, false)) { + if (!setState(SC_MAXIMIZE, (!maxOrFull && !fixedSize), false)) { return; } if (!setState(SC_CLOSE, true, false)) { @@ -557,7 +566,8 @@ bool Utils::isFullScreen(const WId winId) qWarning() << getSystemErrorMessage(QStringLiteral("GetWindowRect")); return false; } - // According to Microsoft Docs, we should compare to primary screen's geometry. + // According to Microsoft Docs, we should compare to the primary screen's geometry + // (if we can't determine the correct screen of our window). const HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); if (!mon) { qWarning() << getSystemErrorMessage(QStringLiteral("MonitorFromWindow")); @@ -1007,13 +1017,13 @@ bool Utils::isFrameBorderColorized() return isTitleBarColorized(); } -void Utils::installSystemMenuHook(const WId winId) +void Utils::installSystemMenuHook(const QWindow *window) { - Q_ASSERT(winId); - if (!winId) { + Q_ASSERT(window); + if (!window) { return; } - const auto hwnd = reinterpret_cast(winId); + const auto hwnd = reinterpret_cast(window->winId()); QMutexLocker locker(&g_utilsHelper()->mutex); if (g_utilsHelper()->qtWindowProcs.contains(hwnd)) { return; @@ -1030,8 +1040,9 @@ void Utils::installSystemMenuHook(const WId winId) qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - g_utilsHelper()->qtWindowProcs.insert(hwnd, originalWindowProc); //triggerFrameChange(winId); + g_utilsHelper()->qtWindowProcs.insert(hwnd, originalWindowProc); + g_utilsHelper()->windowMapping.insert(hwnd, const_cast(window)); } void Utils::uninstallSystemMenuHook(const WId winId) @@ -1055,8 +1066,9 @@ void Utils::uninstallSystemMenuHook(const WId winId) qWarning() << getSystemErrorMessage(QStringLiteral("SetWindowLongPtrW")); return; } - g_utilsHelper()->qtWindowProcs.remove(hwnd); //triggerFrameChange(winId); + g_utilsHelper()->qtWindowProcs.remove(hwnd); + g_utilsHelper()->windowMapping.remove(hwnd); } void Utils::sendMouseReleaseEvent() diff --git a/src/quick/framelesshelper_quick.cpp b/src/quick/framelesshelper_quick.cpp index 754e771..574089e 100644 --- a/src/quick/framelesshelper_quick.cpp +++ b/src/quick/framelesshelper_quick.cpp @@ -27,6 +27,7 @@ #include "framelesshelperimageprovider.h" #include "framelessquickhelper.h" #include "framelessquickutils.h" +#include "framelessquickwindow.h" // The "Q_INIT_RESOURCE()" macro can't be used inside a namespace, // the official workaround is to wrap it into a global function @@ -64,6 +65,8 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) Q_UNUSED(scriptEngine); return new FramelessQuickUtils; }); + qmlRegisterType(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessWindow"); + qmlRegisterAnonymousType(FRAMELESSHELPER_QUICK_URI, 1); initResource(); qmlRegisterType(getQmlFileUrl(QStringLiteral("MinimizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MinimizeButton"); qmlRegisterType(getQmlFileUrl(QStringLiteral("MaximizeButton")), FRAMELESSHELPER_QUICK_URI, 1, 0, "MaximizeButton"); diff --git a/src/quick/framelessquickeventfilter.cpp b/src/quick/framelessquickeventfilter.cpp index d3a0aee..d7546db 100644 --- a/src/quick/framelessquickeventfilter.cpp +++ b/src/quick/framelessquickeventfilter.cpp @@ -242,11 +242,11 @@ bool FramelessQuickEventFilter::eventFilter(QObject *object, QEvent *event) const QPoint globalPos = mouseEvent->globalPos(); #endif const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint(); - Utils::showSystemMenu(window->winId(), nativePos); + Utils::showSystemMenu(window, nativePos); return true; } case QEvent::MouseButtonDblClick: { - if (options & Option::NoDoubleClickMaximizeToggle) { + if ((options & Option::NoDoubleClickMaximizeToggle) || (options & Option::DisableResizing)) { return false; } if (button != Qt::LeftButton) { diff --git a/src/quick/framelessquickutils.cpp b/src/quick/framelessquickutils.cpp index 3fde495..e9a2461 100644 --- a/src/quick/framelessquickutils.cpp +++ b/src/quick/framelessquickutils.cpp @@ -184,7 +184,7 @@ void FramelessQuickUtils::showSystemMenu(QQuickWindow *window, const QPoint &pos const QPoint globalPos = window->mapToGlobal(pos); # endif const QPoint nativePos = QPointF(QPointF(globalPos) * window->effectiveDevicePixelRatio()).toPoint(); - Utils::showSystemMenu(window->winId(), nativePos); + Utils::showSystemMenu(window, nativePos); #endif } diff --git a/src/quick/framelessquickwindow.cpp b/src/quick/framelessquickwindow.cpp index ad5201a..09161a8 100644 --- a/src/quick/framelessquickwindow.cpp +++ b/src/quick/framelessquickwindow.cpp @@ -23,3 +23,11 @@ */ #include "framelessquickwindow.h" + +FRAMELESSHELPER_BEGIN_NAMESPACE + +FramelessQuickWindow::FramelessQuickWindow(QWindow *parent) : QQuickWindow(parent) {} + +FramelessQuickWindow::~FramelessQuickWindow() = default; + +FRAMELESSHELPER_END_NAMESPACE diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index edde5b2..7ff6408 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -248,7 +248,7 @@ void FramelessWidgetsHelper::mouseReleaseEventHandler(QMouseEvent *event) const QPoint globalPos = event->globalPos(); # endif const QPoint nativePos = QPointF(QPointF(globalPos) * q->devicePixelRatioF()).toPoint(); - Utils::showSystemMenu(q->winId(), nativePos); + Utils::showSystemMenu(q->windowHandle(), nativePos); #endif } @@ -258,7 +258,7 @@ void FramelessWidgetsHelper::mouseDoubleClickEventHandler(QMouseEvent *event) if (!event) { return; } - if (m_options & Option::NoDoubleClickMaximizeToggle) { + if ((m_options & Option::NoDoubleClickMaximizeToggle) || (m_options & Option::DisableResizing)) { return; } if (event->button() != Qt::LeftButton) { @@ -291,7 +291,7 @@ void FramelessWidgetsHelper::initialize() q->setAttribute(Qt::WA_DontCreateNativeAncestors); // Force the widget become a native window now so that we can deal with its // win32 events as soon as possible. - q->createWinId(); + q->setAttribute(Qt::WA_NativeWindow); QWindow * const window = q->windowHandle(); Q_ASSERT(window); if (!window) { @@ -506,6 +506,9 @@ void FramelessWidgetsHelper::updateSystemButtonsIcon() void FramelessWidgetsHelper::toggleMaximized() { + if (m_options & Option::DisableResizing) { + return; + } if (isZoomed()) { q->showNormal(); } else {