README.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
examples/mainwindow/CMakeLists.txt | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
qmsetup @ 513c04 | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/win32windowcontext.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/shared/windows10borderhandler_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/quick/quickwindowagent_win.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetwindowagent_win.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
README.md
@@ -255,6 +255,12 @@ + QWindowKit Internals [TODO] + [FramelessHelper Related](docs/framelesshelper-related.md) ### Vulnerabilities + Once you have made the window frameless, it will not be able to switch back to the system border. + There must not be any child widget with `Qt::WA_NativeWindow` property enabled, otherwise the native features and display may be abnormal. Therefore, do not set any widget that has called `QWidget::winId()` or `QWidget::setAttribute(Qt::WA_NativeWindow)` as a descendant of a frameless window. If you really need to move widgets between different windows, make sure that the widget is not a top-level window and wrap it with a frameless container. ## TODO + Fix 5.15 window abnormal behavior examples/mainwindow/CMakeLists.txt
@@ -4,11 +4,6 @@ qwk_add_example(${PROJECT_NAME} SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc QT_LINKS Core Gui Widgets # WebEngineWidgets QT_LINKS Core Gui Widgets # MultimediaWidgets LINKS QWKWidgets WidgetFrame ) set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE ) qmsetup
@@ -1 +1 @@ Subproject commit 961f074a5bc9e6cfd0ecaf790be2734d08702a9f Subproject commit 513c04beb24f77813fedf96c8c9d215f41646a00 src/core/contexts/win32windowcontext.cpp
@@ -33,14 +33,6 @@ #include "qwkglobal_p.h" #include "qwkwindowsextra_p.h" // https://github.com/qt/qtbase/blob/6.6.1/src/plugins/platforms/windows/qwindowswindow.cpp#L2791 // https://github.com/qt/qtbase/blob/6.6.1/src/plugins/platforms/windows/qwindowswindow.cpp#L3321 // This issue exists only in Qt 6.6.1, and was introduced by QTBUG-113736 patch and fixed by // QTBUG-117704 patch. #if !QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) && QT_VERSION == QT_VERSION_CHECK(6, 6, 1) # define QTBUG_113736_WORKAROUND #endif namespace QWK { // The thickness of an auto-hide taskbar in pixels. @@ -490,17 +482,38 @@ return false; } // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546 // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been processed, so we // have to process it after Qt acquires the initial data. auto msg = static_cast<const MSG *>(message); if (msg->message == WM_NCCALCSIZE && lastMessageContext) { switch (msg->message) { case WM_NCCALCSIZE: { // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546 // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been // processed, so we have to process it after Qt acquires the initial data. if (lastMessageContext) { LRESULT res; if (lastMessageContext->nonClientCalcSizeHandler(msg->hwnd, msg->message, msg->wParam, msg->lParam, &res)) { if (lastMessageContext->nonClientCalcSizeHandler( msg->hwnd, msg->message, msg->wParam, msg->lParam, &res)) { *result = decltype(*result)(res); return true; } } break; } // case WM_NCHITTEST: { // // The child window must return HTTRANSPARENT when processing // WM_NCHITTEST for // // the parent window to receive WM_NCHITTEST. // if (!lastMessageContext) { // auto rootHWnd = ::GetAncestor(msg->hwnd, GA_ROOT); // if (rootHWnd != msg->hwnd) { // if (auto ctx = g_wndProcHash->value(rootHWnd)) { // *result = HTTRANSPARENT; // return true; // } // } // } // break; // } } return false; } @@ -589,13 +602,15 @@ return ::DefWindowProcW(hWnd, message, wParam, lParam); } WindowsNativeEventFilter::lastMessageContext = ctx; const auto &contextCleaner = qScopeGuard([]() { WindowsNativeEventFilter::lastMessageContext = nullptr; // }); // Since Qt does the necessary processing of the WM_NCCALCSIZE message, we need to // forward it right away and process it in our native event filter. if (message == WM_NCCALCSIZE) { WindowsNativeEventFilter::lastMessageContext = ctx; LRESULT result = ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); WindowsNativeEventFilter::lastMessageContext = nullptr; return result; return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); } // Try hooked procedure and save result @@ -613,20 +628,10 @@ } static inline void addManagedWindow(QWindow *window, HWND hWnd, Win32WindowContext *ctx) { #ifndef QTBUG_113736_WORKAROUND const auto margins = [](HWND hWnd) -> QMargins { const auto titleBarHeight = int(getTitleBarHeight(hWnd)); if (isSystemBorderEnabled()) { return {0, -titleBarHeight, 0, 0}; } else { const auto frameSize = int(getResizeBorderThickness(hWnd)); return {-frameSize, -titleBarHeight, -frameSize, -frameSize}; } }(hWnd); // Inform Qt we want and have set custom margins setInternalWindowFrameMargins(window, margins); #endif setInternalWindowFrameMargins(window, QMargins(0, -int(getTitleBarHeight(hWnd)), 0, 0)); } // Store original window proc if (!g_qtWindowProc) { @@ -817,10 +822,10 @@ mouseLeaveBlocked = false; lastHitTestResult = WindowPart::Outside; #ifdef QTBUG_113736_WORKAROUND m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) | Qt::FramelessWindowHint); #endif if (!isSystemBorderEnabled()) { m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) | Qt::FramelessWindowHint); } // If the original window id is valid, remove all resources related if (oldWinId) { src/core/shared/windows10borderhandler_p.h
@@ -1,6 +1,19 @@ // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) // SPDX-License-Identifier: Apache-2.0 #ifndef WINDOWS10BORDERHANDLER_P_H #define WINDOWS10BORDERHANDLER_P_H // // W A R N I N G !!! // ----------------- // // This file is not part of the QWindowKit API. It is used purely as an // implementation detail. This header file may change from version to // version without notice, or may even be removed. // #include <QtGui/QWindow> #include <QtGui/QMouseEvent> src/quick/quickwindowagent_win.cpp
@@ -14,8 +14,6 @@ #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) class DeferredDeleteHook; class BorderItem : public QQuickPaintedItem, public Windows10BorderHandler { public: explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context); @@ -30,29 +28,16 @@ protected: bool sharedEventFilter(QObject *obj, QEvent *event) override; std::unique_ptr<DeferredDeleteHook> paintHook; }; # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) volatile bool needPaint = false; class DeferredDeleteHook : public QObject { public: DeferredDeleteHook(BorderItem *item) : item(item) { } // Override deferred delete handler bool event(QEvent *event) override { if (event->type() == QEvent::DeferredDelete) { item->drawBorder(); return true; } return QObject::event(event); } BorderItem *item; private: void _q_afterSynchronizing(); # endif }; BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context) : QQuickPaintedItem(parent), Windows10BorderHandler(context), paintHook(std::make_unique<DeferredDeleteHook>(this)) { : QQuickPaintedItem(parent), Windows10BorderHandler(context) { setAntialiasing(true); // We need anti-aliasing to give us better result. setFillColor({}); // Will improve the performance a little bit. setOpaquePainting(true); // Will also improve the performance, we don't draw @@ -66,6 +51,11 @@ setZ(std::numeric_limits<qreal>::max()); // Make sure our fake border always above // everything in the window. # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) connect(window(), &QQuickWindow::afterSynchronizing, this, &BorderItem::_q_afterSynchronizing, Qt::DirectConnection); # endif // First update if (context->windowId()) { @@ -84,12 +74,23 @@ void BorderItem::paint(QPainter *painter) { Q_UNUSED(painter) // https://github.com/qt/qtdeclarative/blob/cc04afbb382fd4b1f65173d71f44d3372c47b0e1/src/quick/scenegraph/qsgthreadedrenderloop.cpp#L551 // https://github.com/qt/qtdeclarative/blob/cc04afbb382fd4b1f65173d71f44d3372c47b0e1/src/quick/scenegraph/qsgthreadedrenderloop.cpp#L561 // QCoreApplication must process all DeferredDelay events right away after rendering, we can // hook it by overriding a specifiy QObject's event handler and draw the native border in // it. paintHook->deleteLater(); # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (auto api = window()->rendererInterface()->graphicsApi(); !(api == QSGRendererInterface::Direct3D11 || api == QSGRendererInterface::Direct3D12)) { # endif QRect rect(QPoint(0, 0), size().toSize()); QRegion region(rect); void *args[] = { painter, &rect, ®ion, }; ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args); # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) } else { needPaint = true; } # endif } void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) { @@ -111,7 +112,6 @@ switch (event->type()) { case QEvent::WindowStateChange: { updateGeometry(); break; } default: break; @@ -119,6 +119,15 @@ return Windows10BorderHandler::sharedEventFilter(obj, event); } # if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void BorderItem::_q_afterSynchronizing() { if (needPaint) { needPaint = false; drawBorder(); } } # endif void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() { // Install painting hook auto ctx = context.get(); src/widgets/widgetwindowagent_win.cpp
@@ -84,7 +84,6 @@ bool sharedEventFilter(QObject *obj, QEvent *event) override { Q_UNUSED(obj) auto window = widget->windowHandle(); switch (event->type()) { case QEvent::Expose: { // Qt will absolutely send a QExposeEvent or QResizeEvent to the QWindow when it @@ -95,6 +94,7 @@ // Since a QExposeEvent will be sent immediately after the QResizeEvent, we can // simply ignore it. auto ee = static_cast<QExposeEvent *>(event); auto window = widget->windowHandle(); if (window->isExposed() && isNormalWindow() && !ee->region().isNull()) { forwardEventToWindowAndDraw(window, event); return true;