qmsetup
@@ -1 +1 @@ Subproject commit 54a068c54436d87329e483dedc63b594be1762e0 Subproject commit 8afc40a5443899b779957fb006098ce8155aacee src/core/CMakeLists.txt
@@ -12,12 +12,12 @@ windowagentbase.cpp windowitemdelegate_p.h windowitemdelegate.cpp kernel/nativeeventfilter.h kernel/eventobserver_p.h kernel/eventobserver.cpp kernel/nativeeventfilter_p.h kernel/nativeeventfilter.cpp contexts/abstractwindowcontext_p.h contexts/abstractwindowcontext.cpp platforms/win10borderhandler_p.h platforms/win10borderhandler.cpp ) if(WIN32) @@ -26,7 +26,6 @@ qwindowkit_windows.cpp contexts/win32windowcontext_p.h contexts/win32windowcontext.cpp platforms ) else() list(APPEND _src src/core/contexts/abstractwindowcontext.cpp
@@ -98,10 +98,6 @@ return true; } void AbstractWindowContext::showSystemMenu(const QPoint &pos) { virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos)); } QRegion AbstractWindowContext::hitTestShape() const { if (hitTestVisibleShapeDirty) { hitTestVisibleShape = {}; @@ -204,4 +200,8 @@ } } void AbstractWindowContext::showSystemMenu(const QPoint &pos) { virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos)); } } src/core/contexts/abstractwindowcontext_p.h
@@ -9,11 +9,12 @@ #include <QtGui/QPolygon> #include <QWKCore/windowagentbase.h> #include <QWKCore/private/eventobserver_p.h> #include <QWKCore/private/windowitemdelegate_p.h> namespace QWK { class QWK_CORE_EXPORT AbstractWindowContext : public QObject { class QWK_CORE_EXPORT AbstractWindowContext : public QObject, public EventDispatcher { Q_OBJECT public: AbstractWindowContext(); @@ -35,8 +36,6 @@ inline QObject *titleBar() const; bool setTitleBar(QObject *obj); void showSystemMenu(const QPoint &pos); QRegion hitTestShape() const; bool isInSystemButtons(const QPoint &pos, WindowAgentBase::SystemButton *button) const; bool isInTitleBarDraggableArea(const QPoint &pos) const; @@ -47,9 +46,12 @@ CentralizeHook = 1, ShowSystemMenuHook, DefaultColorsHook, DrawWindows10BorderHook, // Only works on Windows 10 }; virtual void virtual_hook(int id, void *data); void showSystemMenu(const QPoint &pos); protected: virtual bool setupHost() = 0; src/core/contexts/win32windowcontext.cpp
@@ -4,6 +4,7 @@ #include <QtCore/QHash> #include <QtCore/QScopeGuard> #include <QtCore/QTimer> #include <QtGui/QGuiApplication> #include <QtGui/QPainter> #include <QtGui/QPalette> @@ -26,10 +27,8 @@ #include <dwmapi.h> #include <timeapi.h> #include "nativeeventfilter.h" #include "nativeeventfilter_p.h" #include "qwkglobal_p.h" #include "win10borderhandler_p.h" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_DECLARE_METATYPE(QMargins) @@ -814,6 +813,45 @@ return; } case DrawWindows10BorderHook: { auto args = static_cast<void **>(data); auto &painter = *static_cast<QPainter *>(args[0]); const auto &rect = *static_cast<const QRect *>(args[1]); const auto ®ion = *static_cast<const QRegion *>(args[2]); const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); QPen pen; pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2); const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd); if (m_delegate->isWindowActive(m_host)) { if (isWindowFrameBorderColorized()) { pen.setColor(getAccentColor()); } else { static QColor frameBorderActiveColorLight(kWindowsColorSet.activeLight); static QColor frameBorderActiveColorDark(kWindowsColorSet.activeDark); pen.setColor(dark ? frameBorderActiveColorDark : frameBorderActiveColorLight); } } else { static QColor frameBorderInactiveColorLight(kWindowsColorSet.inactiveLight); static QColor frameBorderInactiveColorDark(kWindowsColorSet.inactiveDark); pen.setColor(dark ? frameBorderInactiveColorDark : frameBorderInactiveColorLight); } painter.save(); // ### TODO: do we need to enable or disable it? painter.setRenderHint(QPainter::Antialiasing); painter.setPen(pen); painter.drawLine(QLine{ QPoint{0, 0}, QPoint{m_windowHandle->width(), 0} }); painter.restore(); } default: { // unreachable break; @@ -822,14 +860,12 @@ AbstractWindowContext::virtual_hook(id, data); } bool Win32WindowContext::needWin10BorderHandler() const { bool Win32WindowContext::needBorderPainter() const { return isWin10OrGreater() && !isWin11OrGreater(); } void Win32WindowContext::setWin10BorderHandler(Win10BorderHandler *handler) { win10BorderHandler.reset(handler); handler->setBorderThickness( int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)))); int Win32WindowContext::borderThickness() const { return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)); } bool Win32WindowContext::setupHost() { @@ -905,8 +941,8 @@ } QWK_USED static constexpr const struct { const WPARAM wParam = 0xF1C9ADD4; const LPARAM lParam = 0xAFB6F4C6; const WPARAM wParam = MAKEWPARAM(44500, 61897); const LPARAM lParam = MAKELPARAM(62662, 44982); // Not used. Reserve for future use. } kMessageTag; static inline quint64 getKeyState() { @@ -1887,81 +1923,83 @@ return false; } bool Win32WindowContext::themeStuffHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *resul) { bool Win32WindowContext::themeStuffHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *resul) { switch (message) { case WM_DPICHANGED: { const auto dpiX = UINT(LOWORD(wParam)); const auto dpiY = UINT(HIWORD(wParam)); QEvent e(QEvent::ScreenChangeInternal); dispatch(&e); break; } case WM_THEMECHANGED: case WM_SYSCOLORCHANGE: { QEvent e(QEvent::UpdateRequest); dispatch(&e); break; } case WM_DWMCOLORIZATIONCOLORCHANGED: { const QColor color = QColor::fromRgba(wParam); const auto blendWithOpacity = *reinterpret_cast<LPBOOL>(lParam); QEvent e(QEvent::UpdateRequest); dispatch(&e); break; } case WM_SETTINGCHANGE: { if (!wParam && lParam && std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0) { if (!wParam && lParam && std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0) { const QColor color = getAccentColor(); } QEvent e(QEvent::UpdateRequest); dispatch(&e); break; } case WM_SIZE: { const bool max = wParam == SIZE_MAXIMIZED; const bool min = wParam == SIZE_MINIMIZED; const bool full = isFullScreen(hWnd); Qt::WindowStates states{}; if (max) { states |= Qt::WindowMaximized; } if (min) { states |= Qt::WindowMinimized; } if (full) { states |= Qt::WindowFullScreen; } QTimer::singleShot(0, this, [this, states] { QWindowStateChangeEvent e(states); dispatch(&e); }); break; } case WM_ACTIVATE: { const auto state = LOWORD(wParam); const bool active = state == WA_ACTIVE || state == WA_CLICKACTIVE; Q_UNUSED(state) QTimer::singleShot(0, this, [this, active] { QEvent e(active ? QEvent::WindowActivate : QEvent::WindowDeactivate); dispatch(&e); }); break; } default: break; } return false; } void Win10BorderHandler::paintBorder(QPainter &painter, const QRect &rect, const QRegion ®ion) { Q_UNUSED(rect) Q_UNUSED(region) QPen pen; pen.setWidth(m_borderThickness * 2); const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(reinterpret_cast<HWND>(m_window->winId())); if (isActive()) { if (isWindowFrameBorderColorized()) { pen.setColor(getAccentColor()); } else { static QColor frameBorderActiveColorLight(kWindowsColorSet.activeLight); static QColor frameBorderActiveColorDark(kWindowsColorSet.activeDark); pen.setColor(dark ? frameBorderActiveColorDark : frameBorderActiveColorLight); } } else { static QColor frameBorderInactiveColorLight(kWindowsColorSet.inactiveLight); static QColor frameBorderInactiveColorDark(kWindowsColorSet.inactiveDark); pen.setColor(dark ? frameBorderInactiveColorDark : frameBorderInactiveColorLight); } painter.save(); // ### TODO: do we need to enable or disable it? painter.setRenderHint(QPainter::Antialiasing); painter.setPen(pen); painter.drawLine(QLine{ QPoint{0, 0}, QPoint{m_window->width(), 0} }); painter.restore(); } } src/core/contexts/win32windowcontext_p.h
@@ -6,10 +6,10 @@ namespace QWK { class Win10BorderHandler; class QWK_CORE_EXPORT Win32WindowContext : public AbstractWindowContext { Q_OBJECT Q_PROPERTY(bool needBorderPainter READ needBorderPainter FINAL) Q_PROPERTY(int borderThickness READ borderThickness FINAL) public: Win32WindowContext(); ~Win32WindowContext() override; @@ -26,8 +26,8 @@ QString key() const override; void virtual_hook(int id, void *data) override; Q_INVOKABLE bool needWin10BorderHandler() const; Q_INVOKABLE void setWin10BorderHandler(Win10BorderHandler *handler); bool needBorderPainter() const; int borderThickness() const; protected: bool setupHost() override; @@ -65,8 +65,6 @@ bool mouseLeaveBlocked = false; bool centered = false; std::unique_ptr<Win10BorderHandler> win10BorderHandler; }; } src/core/kernel/eventobserver.cpp
New file @@ -0,0 +1,44 @@ #include "eventobserver_p.h" namespace QWK { EventObserver::EventObserver() : m_dispatcher(nullptr) { } EventObserver::~EventObserver() { if (m_dispatcher) m_dispatcher->removeObserver(this); } EventDispatcher::EventDispatcher() = default; EventDispatcher::~EventDispatcher() { for (const auto &observer : qAsConst(m_observers)) { observer->m_dispatcher = nullptr; } } bool EventDispatcher::dispatch(QEvent *event) { for (const auto &observer : qAsConst(m_observers)) { if (observer->observe(event)) return true; } return true; } void EventDispatcher::addObserver(EventObserver *observer) { if (!observer || observer->m_dispatcher) return; m_observers.append(observer); observer->m_dispatcher = this; } void EventDispatcher::removeObserver(EventObserver *observer) { if (!m_observers.removeOne(observer)) { return; } observer->m_dispatcher = nullptr; } } src/core/kernel/eventobserver_p.h
New file @@ -0,0 +1,47 @@ #ifndef EVENTFILTER_P_H #define EVENTFILTER_P_H #include <QtGui/QtEvents> #include <QWKCore/qwkglobal.h> namespace QWK { class EventDispatcher; class QWK_CORE_EXPORT EventObserver { public: EventObserver(); virtual ~EventObserver(); protected: virtual bool observe(QEvent *event) = 0; protected: EventDispatcher *m_dispatcher; Q_DISABLE_COPY(EventObserver) friend class EventDispatcher; }; class QWK_CORE_EXPORT EventDispatcher { public: EventDispatcher(); virtual ~EventDispatcher(); virtual bool dispatch(QEvent *event); public: void addObserver(EventObserver *observer); void removeObserver(EventObserver *observer); protected: QVector<EventObserver *> m_observers; Q_DISABLE_COPY(EventDispatcher) }; } #endif // EVENTFILTER_P_H src/core/kernel/nativeeventfilter.cpp
@@ -1,4 +1,4 @@ #include "nativeeventfilter.h" #include "nativeeventfilter_p.h" #include <QtCore/QAbstractNativeEventFilter> #include <QtCore/QCoreApplication> src/core/kernel/nativeeventfilter_p.h
File was renamed from src/core/kernel/nativeeventfilter.h @@ -1,5 +1,5 @@ #ifndef NATIVEEVENTFILTER_H #define NATIVEEVENTFILTER_H #ifndef NATIVEEVENTFILTER_P_H #define NATIVEEVENTFILTER_P_H #include <QWKCore/qwkglobal.h> @@ -20,4 +20,4 @@ } #endif // NATIVEEVENTFILTER_H #endif // NATIVEEVENTFILTER_P_H src/core/platforms/win10borderhandler.cpp
File was deleted src/core/platforms/win10borderhandler_p.h
File was deleted src/quick/CMakeLists.txt
@@ -13,6 +13,10 @@ quickwindowagent.cpp ) if(WIN32) list(APPEND _src quickwindowagent_win.cpp) endif() qwk_add_library(${PROJECT_NAME} AUTOGEN SOURCES ${_src} LINKS QWKCore src/quick/quickitemdelegate.cpp
@@ -39,7 +39,7 @@ } bool QuickItemDelegate::isWindowActive(const QObject *host) const { return static_cast<QQuickWindow *>(const_cast<QObject *>(host))->isActive(); return static_cast<const QQuickWindow *>(host)->isActive(); } } src/quick/quickwindowagent.cpp
@@ -2,69 +2,11 @@ #include "quickwindowagent_p.h" #include <QtQuick/QQuickWindow> #include <QtQuick/QQuickPaintedItem> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickanchors_p.h> #ifdef Q_OS_WINDOWS # include <QWKCore/private/win10borderhandler_p.h> #endif #include "quickitemdelegate_p.h" namespace QWK { class BorderItem : public QQuickPaintedItem, public Win10BorderHandler { Q_OBJECT public: explicit BorderItem(QQuickItem *parent = nullptr); ~BorderItem() override; void updateGeometry() override; void requestUpdate() override; bool isActive() const override; public: void paint(QPainter *painter) override; }; BorderItem::BorderItem(QQuickItem *parent) : Win10BorderHandler(parent->window()), QQuickPaintedItem(parent) { setAntialiasing(true); // ### FIXME: do we need to enable or disable this? setMipmap(true); // ### FIXME: do we need to enable or disable this? setFillColor({}); // Will improve the performance a little bit. setOpaquePainting(true); // Will also improve the performance, we don't draw // semi-transparent borders of course. auto parentPri = QQuickItemPrivate::get(parent); auto anchors = QQuickItemPrivate::get(this)->anchors(); anchors->setTop(parentPri->top()); anchors->setLeft(parentPri->left()); anchors->setRight(parentPri->right()); setZ(10); } BorderItem::~BorderItem() = default; void BorderItem::updateGeometry() { setHeight(m_borderThickness); } void BorderItem::requestUpdate() { update(); } bool BorderItem::isActive() const { return static_cast<QQuickWindow *>(m_window)->isActive(); } void BorderItem::paint(QPainter *painter) { QRect rect(QPoint(0, 0), size().toSize()); QRegion region(rect); Win10BorderHandler::paintBorder(*painter, rect, region); } QuickWindowAgentPrivate::QuickWindowAgentPrivate() { } @@ -99,15 +41,7 @@ d->hostWindow = window; #ifdef Q_OS_WINDOWS // Install painting hook if (bool needPaintBorder; QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler", Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)), needPaintBorder) { QMetaObject::invokeMethod( d->context.get(), "setWin10BorderHandler", Qt::DirectConnection, Q_ARG(Win10BorderHandler *, new BorderItem(window->contentItem()))); } d->setupWindows10BorderWorkaround(); #endif return true; } @@ -158,6 +92,4 @@ d.init(); } } #include "quickwindowagent.moc" } src/quick/quickwindowagent_p.h
@@ -16,6 +16,10 @@ // Host QQuickWindow *hostWindow{}; #ifdef Q_OS_WINDOWS void setupWindows10BorderWorkaround(); #endif }; } src/quick/quickwindowagent_win.cpp
New file @@ -0,0 +1,110 @@ #include "quickwindowagent_p.h" #include <QtQuick/QQuickPaintedItem> #include <QtQuick/private/qquickitem_p.h> #include <QWKCore/private/eventobserver_p.h> namespace QWK { class BorderItem : public QQuickPaintedItem, public EventObserver { Q_OBJECT public: explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context); ~BorderItem() override; void updateGeometry(); public: void paint(QPainter *painter) override; void itemChange(ItemChange change, const ItemChangeData &data) override; protected: bool observe(QEvent *event) override; AbstractWindowContext *context; private: void _q_windowActivityChanged(); }; BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context) : QQuickPaintedItem(parent), context(context) { setAntialiasing(true); // ### FIXME: do we need to enable or disable this? setMipmap(true); // ### FIXME: do we need to enable or disable this? setFillColor({}); // Will improve the performance a little bit. setOpaquePainting(true); // Will also improve the performance, we don't draw // semi-transparent borders of course. auto parentPri = QQuickItemPrivate::get(parent); auto anchors = QQuickItemPrivate::get(this)->anchors(); anchors->setTop(parentPri->top()); anchors->setLeft(parentPri->left()); anchors->setRight(parentPri->right()); setZ(10); context->addObserver(this); connect(window(), &QQuickWindow::activeChanged, this, &BorderItem::_q_windowActivityChanged); updateGeometry(); } BorderItem::~BorderItem() = default; void BorderItem::updateGeometry() { setHeight(context->property("borderThickness").toInt()); } void BorderItem::paint(QPainter *painter) { QRect rect(QPoint(0, 0), size().toSize()); QRegion region(rect); void *args[] = { painter, &rect, ®ion, }; context->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args); } void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) { QQuickPaintedItem::itemChange(change, data); switch (change) { case ItemVisibleHasChanged: case ItemDevicePixelRatioHasChanged: { updateGeometry(); break; } default: break; } } bool BorderItem::observe(QEvent *event) { switch (event->type()) { case QEvent::UpdateRequest: { update(); break; } default: break; } return false; } void BorderItem::_q_windowActivityChanged() { update(); } void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() { // Install painting hook auto ctx = context.get(); if (ctx->property("needBorderPainter").toBool()) { std::ignore = new BorderItem(hostWindow->contentItem(), ctx); } } } #include "quickwindowagent_win.moc" src/quick/qwkquickglobal.cpp
@@ -1,7 +1,9 @@ #include "qwkquickglobal.h" #include "quickwindowagent.h" #include <QtQml/QQmlEngine> #include "quickwindowagent.h" namespace QWK { static constexpr const char kModuleUri[] = "QWindowKit"; src/widgets/CMakeLists.txt
@@ -12,6 +12,10 @@ widgetwindowagent.cpp ) if(WIN32) list(APPEND _src widgetwindowagent_win.cpp) endif() qwk_add_library(${PROJECT_NAME} AUTOGEN SOURCES ${_src} LINKS QWKCore src/widgets/widgetwindowagent.cpp
@@ -5,58 +5,9 @@ #include <QtGui/QPainter> #include <QtCore/QDebug> #ifdef Q_OS_WINDOWS # include <QWKCore/private/win10borderhandler_p.h> #endif #include "widgetitemdelegate_p.h" namespace QWK { #ifdef Q_OS_WINDOWS class WidgetBorderHandler : public QObject, public Win10BorderHandler { public: explicit WidgetBorderHandler(QWidget *widget) : Win10BorderHandler(widget->windowHandle()), widget(widget) { widget->installEventFilter(this); } void updateGeometry() override { if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) { widget->setContentsMargins({}); } else { widget->setContentsMargins({0, int(m_borderThickness), 0, 0}); } } void requestUpdate() override { widget->update(); } bool isActive() const override { return widget->isActiveWindow(); } protected: bool eventFilter(QObject *obj, QEvent *event) override { switch (event->type()) { case QEvent::Paint: { if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) break; auto paintEvent = static_cast<QPaintEvent *>(event); QPainter painter(widget); paintBorder(painter, paintEvent->rect(), paintEvent->region()); return true; } default: break; } return false; } QWidget *widget; }; #endif WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() { } @@ -94,15 +45,7 @@ d->hostWidget = w; #ifdef Q_OS_WINDOWS // Install painting hook if (bool needPaintBorder; QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler", Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)), needPaintBorder) { QMetaObject::invokeMethod(d->context.get(), "setWin10BorderHandler", Qt::DirectConnection, Q_ARG(Win10BorderHandler *, new WidgetBorderHandler(w))); } d->setupWindows10BorderWorkaround(); #endif return true; } src/widgets/widgetwindowagent_p.h
@@ -16,6 +16,10 @@ // Host QWidget *hostWidget{}; #ifdef Q_OS_WINDOWS void setupWindows10BorderWorkaround(); #endif }; } src/widgets/widgetwindowagent_win.cpp
New file @@ -0,0 +1,104 @@ #include "widgetwindowagent_p.h" #include <QtGui/QPainter> #include <QWKCore/private/eventobserver_p.h> namespace QWK { class WidgetBorderHandler : public QObject, public EventObserver { Q_OBJECT public: explicit WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx, QObject *parent = nullptr) : QObject(parent), widget(widget), ctx(ctx) { widget->installEventFilter(this); ctx->addObserver(this); updateGeometry(); } void updateGeometry() { if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) { widget->setContentsMargins({}); } else { widget->setContentsMargins({ 0, ctx->property("borderThickness").toInt(), 0, 0, }); } } protected: bool observe(QEvent *event) override { switch (event->type()) { case QEvent::UpdateRequest: { widget->update(); break; } case QEvent::ScreenChangeInternal: { updateGeometry(); break; } default: break; } return false; } bool eventFilter(QObject *obj, QEvent *event) override { switch (event->type()) { case QEvent::Paint: { if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) break; auto paintEvent = static_cast<QPaintEvent *>(event); auto rect = paintEvent->rect(); auto region = paintEvent->region(); QPainter painter(widget); void *args[] = { &painter, &rect, ®ion, }; ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args); return true; } case QEvent::WindowStateChange: { updateGeometry(); break; } case QEvent::WindowActivate: case QEvent::WindowDeactivate: { widget->update(); break; } default: break; } return false; } QWidget *widget; AbstractWindowContext *ctx; }; void WidgetWindowAgentPrivate::setupWindows10BorderWorkaround() { // Install painting hook auto ctx = context.get(); if (ctx->property("needBorderPainter").toBool()) { std::ignore = new WidgetBorderHandler(hostWidget, ctx, ctx); } } } #include "widgetwindowagent_win.moc"