| | |
| | | kernel/nativeeventfilter.cpp |
| | | contexts/abstractwindowcontext_p.h |
| | | contexts/abstractwindowcontext.cpp |
| | | platforms/win10borderhandler_p.h |
| | | platforms/win10borderhandler.cpp |
| | | ) |
| | | |
| | | if(WIN32) |
| | |
| | | qwindowkit_windows.cpp |
| | | contexts/win32windowcontext_p.h |
| | | contexts/win32windowcontext.cpp |
| | | platforms |
| | | ) |
| | | else() |
| | | list(APPEND _src |
| | |
| | | LINKS |
| | | QT_LINKS Core Gui |
| | | QT_INCLUDE_PRIVATE Core Gui |
| | | INCLUDE_PRIVATE kernel contexts |
| | | INCLUDE_PRIVATE kernel contexts platforms |
| | | PREFIX QWK_CORE |
| | | ) |
| | | |
| | |
| | | |
| | | void AbstractWindowContext::virtual_hook(int id, void *data) { |
| | | switch (id) { |
| | | case NeedsDrawBordersHook: { |
| | | auto &result = *static_cast<bool *>(data); |
| | | result = false; |
| | | return; |
| | | case CentralizeHook: { |
| | | // TODO: Qt |
| | | break; |
| | | } |
| | | |
| | | case BorderThicknessHook: { |
| | | auto args = static_cast<void **>(data); |
| | | const bool requireNative = *static_cast<const bool *>(args[0]); |
| | | quint32 &thickness = *static_cast<quint32 *>(args[1]); |
| | | std::ignore = requireNative; |
| | | thickness = 1; |
| | | return; |
| | | case ShowSystemMenuHook: { |
| | | // TODO: Qt |
| | | break; |
| | | } |
| | | |
| | | case BorderColorsHook: { |
| | | auto arr = *reinterpret_cast<QList<QColor> *>(data); |
| | | arr.clear(); |
| | | arr.push_back(kSampleColorSet.activeLight); |
| | | arr.push_back(kSampleColorSet.activeDark); |
| | | arr.push_back(kSampleColorSet.inactiveLight); |
| | | arr.push_back(kSampleColorSet.inactiveDark); |
| | | case DefaultColorsHook: { |
| | | auto map = *reinterpret_cast<QMap<QString, QColor> *>(data); |
| | | map.clear(); |
| | | map.insert("activeLight", kSampleColorSet.activeLight); |
| | | map.insert("activeDark", kSampleColorSet.activeDark); |
| | | map.insert("inactiveLight", kSampleColorSet.inactiveLight); |
| | | map.insert("inactiveDark", kSampleColorSet.inactiveDark); |
| | | return; |
| | | } |
| | | |
| | | default: |
| | | break; |
| | | } |
| | |
| | | enum WindowContextHook { |
| | | CentralizeHook = 1, |
| | | ShowSystemMenuHook, |
| | | NeedsDrawBordersHook, |
| | | BorderThicknessHook, |
| | | BorderColorsHook, |
| | | DrawBordersHook, |
| | | DefaultColorsHook, |
| | | }; |
| | | virtual void virtual_hook(int id, void *data); |
| | | |
| | |
| | | #include "nativeeventfilter.h" |
| | | #include "qwkglobal_p.h" |
| | | |
| | | #include "win10borderhandler_p.h" |
| | | |
| | | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| | | Q_DECLARE_METATYPE(QMargins) |
| | | #endif |
| | |
| | | return; |
| | | } |
| | | |
| | | case NeedsDrawBordersHook: { |
| | | auto &result = *static_cast<bool *>(data); |
| | | result = isWin10OrGreater() && !isWin11OrGreater(); |
| | | return; |
| | | } |
| | | |
| | | case BorderThicknessHook: { |
| | | auto args = static_cast<void **>(data); |
| | | const bool requireNative = *static_cast<const bool *>(args[0]); |
| | | quint32 &thickness = *static_cast<quint32 *>(args[1]); |
| | | const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); |
| | | const auto nativeThickness = getWindowFrameBorderThickness(hwnd); |
| | | thickness = requireNative |
| | | ? nativeThickness |
| | | : QHighDpi::fromNativePixels(nativeThickness, m_windowHandle); |
| | | return; |
| | | } |
| | | |
| | | case BorderColorsHook: { |
| | | auto arr = *reinterpret_cast<QList<QColor> *>(data); |
| | | arr.clear(); |
| | | arr.push_back(kWindowsColorSet.activeLight); |
| | | arr.push_back(kWindowsColorSet.activeDark); |
| | | arr.push_back(kWindowsColorSet.inactiveLight); |
| | | arr.push_back(kWindowsColorSet.inactiveDark); |
| | | return; |
| | | } |
| | | |
| | | case DrawBordersHook: { |
| | | 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; |
| | | const auto borderThickness = int(QHighDpi::fromNativePixels( |
| | | getWindowFrameBorderThickness(hwnd), m_windowHandle)); |
| | | pen.setWidth(borderThickness * 2); |
| | | const bool active = m_delegate->isWindowActive(m_host); |
| | | const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd); |
| | | |
| | | if (active) { |
| | | 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{rect.width(), 0} |
| | | }); |
| | | painter.restore(); |
| | | case DefaultColorsHook: { |
| | | auto map = *reinterpret_cast<QMap<QString, QColor> *>(data); |
| | | map.clear(); |
| | | map.insert("activeLight", kWindowsColorSet.activeLight); |
| | | map.insert("activeDark", kWindowsColorSet.activeDark); |
| | | map.insert("inactiveLight", kWindowsColorSet.inactiveLight); |
| | | map.insert("inactiveDark", kWindowsColorSet.inactiveDark); |
| | | return; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | AbstractWindowContext::virtual_hook(id, data); |
| | | } |
| | | |
| | | bool Win32WindowContext::needWin10BorderHandler() const { |
| | | return isWin10OrGreater() && !isWin11OrGreater(); |
| | | } |
| | | |
| | | void Win32WindowContext::setWin10BorderHandler(Win10BorderHandler *handler) { |
| | | win10BorderHandler.reset(handler); |
| | | handler->setBorderThickness( |
| | | int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)))); |
| | | } |
| | | |
| | | bool Win32WindowContext::setupHost() { |
| | |
| | | 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(); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | namespace QWK { |
| | | |
| | | class Win10BorderHandler; |
| | | |
| | | class QWK_CORE_EXPORT Win32WindowContext : public AbstractWindowContext { |
| | | Q_OBJECT |
| | | public: |
| | |
| | | |
| | | QString key() const override; |
| | | void virtual_hook(int id, void *data) override; |
| | | |
| | | Q_INVOKABLE bool needWin10BorderHandler() const; |
| | | Q_INVOKABLE void setWin10BorderHandler(Win10BorderHandler *handler); |
| | | |
| | | protected: |
| | | bool setupHost() override; |
| | |
| | | bool mouseLeaveBlocked = false; |
| | | |
| | | bool centered = false; |
| | | |
| | | std::unique_ptr<Win10BorderHandler> win10BorderHandler; |
| | | }; |
| | | |
| | | } |
New file |
| | |
| | | #include "win10borderhandler_p.h" |
| | | |
| | | namespace QWK { |
| | | |
| | | Win10BorderHandler::Win10BorderHandler(QWindow *window) : m_window(window), m_borderThickness(0) { |
| | | } |
| | | |
| | | Win10BorderHandler::~Win10BorderHandler() = default; |
| | | |
| | | } |
New file |
| | |
| | | #ifndef WIN10BORDERHANDLER_P_H |
| | | #define WIN10BORDERHANDLER_P_H |
| | | |
| | | #include <QtGui/QPainter> |
| | | |
| | | #include <QWKCore/qwkcoreglobal.h> |
| | | |
| | | namespace QWK { |
| | | |
| | | class QWK_CORE_EXPORT Win10BorderHandler { |
| | | public: |
| | | Win10BorderHandler(QWindow *window); |
| | | virtual ~Win10BorderHandler(); |
| | | |
| | | public: |
| | | virtual void updateGeometry() = 0; |
| | | virtual void requestUpdate() = 0; |
| | | |
| | | virtual bool isActive() const = 0; |
| | | |
| | | inline int borderThickness() const; |
| | | inline void setBorderThickness(int borderThickness); |
| | | |
| | | protected: |
| | | // implemented in `win32windowcontext.cpp` |
| | | void paintBorder(QPainter &painter, const QRect &rect, const QRegion ®ion); |
| | | |
| | | protected: |
| | | QWindow *m_window; |
| | | int m_borderThickness; |
| | | |
| | | Q_DISABLE_COPY(Win10BorderHandler) |
| | | }; |
| | | |
| | | inline int Win10BorderHandler::borderThickness() const { |
| | | return m_borderThickness; |
| | | } |
| | | |
| | | inline void Win10BorderHandler::setBorderThickness(int borderThickness) { |
| | | m_borderThickness = borderThickness; |
| | | updateGeometry(); |
| | | } |
| | | |
| | | } |
| | | |
| | | #endif // WIN10BORDERHANDLER_P_H |
| | |
| | | #include "quickwindowagent.h" |
| | | #include "quickwindowagent_p.h" |
| | | #include "quickitemdelegate_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 { |
| | | class BorderItem : public QQuickPaintedItem, public Win10BorderHandler { |
| | | Q_OBJECT |
| | | public: |
| | | explicit BorderItem(AbstractWindowContext *ctx, QQuickItem *parent = nullptr); |
| | | explicit BorderItem(QQuickItem *parent = nullptr); |
| | | ~BorderItem() override; |
| | | |
| | | void updateHeight(); |
| | | void updateGeometry() override; |
| | | void requestUpdate() override; |
| | | |
| | | bool isActive() const override; |
| | | |
| | | public: |
| | | void paint(QPainter *painter) override; |
| | | void itemChange(ItemChange change, const ItemChangeData &data) override; |
| | | |
| | | private: |
| | | AbstractWindowContext *context; |
| | | |
| | | void _q_windowActivityChanged(); |
| | | }; |
| | | |
| | | BorderItem::BorderItem(AbstractWindowContext *ctx, QQuickItem *parent) |
| | | : QQuickPaintedItem(parent), context(ctx) { |
| | | 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. |
| | |
| | | |
| | | BorderItem::~BorderItem() = default; |
| | | |
| | | void BorderItem::updateHeight() { |
| | | bool native = false; |
| | | quint32 thickness = 0; |
| | | void *args[] = { |
| | | &native, |
| | | &thickness, |
| | | }; |
| | | context->virtual_hook(AbstractWindowContext::BorderThicknessHook, &args); |
| | | setHeight(thickness); |
| | | 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); |
| | | void *args[] = { |
| | | painter, |
| | | &rect, |
| | | ®ion, |
| | | }; |
| | | context->virtual_hook(AbstractWindowContext::DrawBordersHook, args); |
| | | } |
| | | |
| | | void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) { |
| | | QQuickPaintedItem::itemChange(change, data); |
| | | switch (change) { |
| | | case ItemSceneChange: |
| | | if (data.window) { |
| | | connect(data.window, &QQuickWindow::activeChanged, this, |
| | | &BorderItem::_q_windowActivityChanged); |
| | | } |
| | | Q_FALLTHROUGH(); |
| | | case ItemVisibleHasChanged: |
| | | case ItemDevicePixelRatioHasChanged: |
| | | updateHeight(); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | void BorderItem::_q_windowActivityChanged() { |
| | | update(); |
| | | Win10BorderHandler::paintBorder(*painter, rect, region); |
| | | } |
| | | |
| | | QuickWindowAgentPrivate::QuickWindowAgentPrivate() { |
| | |
| | | } |
| | | d->hostWindow = window; |
| | | |
| | | if (bool needPaintBorder = false; |
| | | d->context->virtual_hook(AbstractWindowContext::NeedsDrawBordersHook, &needPaintBorder), |
| | | #ifdef Q_OS_WINDOWS |
| | | // Install painting hook |
| | | if (bool needPaintBorder; |
| | | QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler", |
| | | Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)), |
| | | needPaintBorder) { |
| | | d->borderItem = std::make_unique<BorderItem>(d->context.get(), window->contentItem()); |
| | | QMetaObject::invokeMethod( |
| | | d->context.get(), "setWin10BorderHandler", Qt::DirectConnection, |
| | | Q_ARG(Win10BorderHandler *, new BorderItem(window->contentItem()))); |
| | | } |
| | | #endif |
| | | return true; |
| | | } |
| | | |
| | |
| | | |
| | | namespace QWK { |
| | | |
| | | class BorderItem; |
| | | |
| | | class QuickWindowAgentPrivate : public WindowAgentBasePrivate { |
| | | Q_DECLARE_PUBLIC(QuickWindowAgent) |
| | | public: |
| | |
| | | |
| | | // Host |
| | | QQuickWindow *hostWindow{}; |
| | | |
| | | std::unique_ptr<BorderItem> borderItem; |
| | | }; |
| | | |
| | | } |
| | |
| | | #include <QtGui/QPainter> |
| | | #include <QtCore/QDebug> |
| | | |
| | | #ifdef Q_OS_WINDOWS |
| | | # include <QWKCore/private/win10borderhandler_p.h> |
| | | #endif |
| | | |
| | | #include "widgetitemdelegate_p.h" |
| | | |
| | | namespace QWK { |
| | | |
| | | class WidgetBorderHandler : public QObject { |
| | | #ifdef Q_OS_WINDOWS |
| | | class WidgetBorderHandler : public QObject, public Win10BorderHandler { |
| | | public: |
| | | WidgetBorderHandler(QWidget *widget, AbstractWindowContext *ctx) |
| | | : widget(widget), ctx(ctx), m_thickness(0) { |
| | | updateThickness(); |
| | | explicit WidgetBorderHandler(QWidget *widget) |
| | | : Win10BorderHandler(widget->windowHandle()), widget(widget) { |
| | | widget->installEventFilter(this); |
| | | } |
| | | |
| | | void updateThickness() { |
| | | // Query thickness |
| | | bool native = false; |
| | | void *a[] = { |
| | | &native, |
| | | &m_thickness, |
| | | }; |
| | | ctx->virtual_hook(AbstractWindowContext::BorderThicknessHook, &a); |
| | | } |
| | | |
| | | void updateMargins() { |
| | | void updateGeometry() override { |
| | | if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) { |
| | | widget->setContentsMargins({}); |
| | | } else { |
| | | widget->setContentsMargins({0, int(m_thickness), 0, 0}); |
| | | widget->setContentsMargins({0, int(m_borderThickness), 0, 0}); |
| | | } |
| | | } |
| | | |
| | | void requestUpdate() override { |
| | | widget->update(); |
| | | } |
| | | |
| | | bool isActive() const override { |
| | | return widget->isActiveWindow(); |
| | | } |
| | | |
| | | protected: |
| | |
| | | case QEvent::Paint: { |
| | | if (widget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) |
| | | break; |
| | | |
| | | auto paintEvent = static_cast<QPaintEvent *>(event); |
| | | QPainter painter(widget); |
| | | QRect rect = paintEvent->rect(); |
| | | QRegion region = paintEvent->region(); |
| | | void *args[] = { |
| | | &painter, |
| | | &rect, |
| | | ®ion, |
| | | }; |
| | | ctx->virtual_hook(AbstractWindowContext::DrawBordersHook, args); |
| | | paintBorder(painter, paintEvent->rect(), paintEvent->region()); |
| | | return true; |
| | | } |
| | | |
| | | case QEvent::WindowStateChange: { |
| | | updateMargins(); |
| | | break; |
| | | } |
| | | |
| | | case QEvent::WindowActivate: |
| | | case QEvent::WindowDeactivate: { |
| | | widget->update(); |
| | | break; |
| | | } |
| | | |
| | | // TODO: Handle DPI Change |
| | | |
| | | default: |
| | | break; |
| | | } |
| | |
| | | } |
| | | |
| | | QWidget *widget; |
| | | AbstractWindowContext *ctx; |
| | | quint32 m_thickness; |
| | | }; |
| | | #endif |
| | | |
| | | WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() { |
| | | } |
| | |
| | | } |
| | | d->hostWidget = w; |
| | | |
| | | #ifdef Q_OS_WINDOWS |
| | | // Install painting hook |
| | | if (bool needPaintBorder = false; |
| | | d->context->virtual_hook(AbstractWindowContext::NeedsDrawBordersHook, &needPaintBorder), |
| | | if (bool needPaintBorder; |
| | | QMetaObject::invokeMethod(d->context.get(), "needWin10BorderHandler", |
| | | Qt::DirectConnection, Q_RETURN_ARG(bool, needPaintBorder)), |
| | | needPaintBorder) { |
| | | auto borderHandler = std::make_unique<WidgetBorderHandler>(w, d->context.get()); |
| | | borderHandler->updateMargins(); |
| | | d->borderHandler = std::move(borderHandler); |
| | | QMetaObject::invokeMethod(d->context.get(), "setWin10BorderHandler", |
| | | Qt::DirectConnection, |
| | | Q_ARG(Win10BorderHandler *, new WidgetBorderHandler(w))); |
| | | } |
| | | #endif |
| | | return true; |
| | | } |
| | | |
| | |
| | | |
| | | namespace QWK { |
| | | |
| | | class WidgetBorderHandler; |
| | | |
| | | class WidgetWindowAgentPrivate : public WindowAgentBasePrivate { |
| | | Q_DECLARE_PUBLIC(WidgetWindowAgent) |
| | | public: |
| | |
| | | |
| | | // Host |
| | | QWidget *hostWidget{}; |
| | | |
| | | std::unique_ptr<WidgetBorderHandler> borderHandler; |
| | | }; |
| | | |
| | | } |