Zhao Yuhang
2023-12-12 5a0e5a9261c251dba54afb8cc0b6b32ae9d95510
src/core/contexts/win32windowcontext.cpp
@@ -29,6 +29,8 @@
#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
@@ -357,44 +359,37 @@
        }
    }
    static inline quint32 getWindowFrameBorderThickness(HWND hwnd) {
        UINT result = 0;
    static inline quint32 getSystemMetricsForDpi(int index, quint32 dpi) {
        const DynamicApis &apis = DynamicApis::instance();
        if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
                                                  &result, sizeof(result)))) {
            return result;
        } else {
            const quint32 dpi = getDpiForWindow(hwnd);
            result = quint32(std::round(qreal(1) * qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI)));
        if (apis.pGetSystemMetricsForDpi) {
            return ::GetSystemMetricsForDpi(index, dpi);
        }
        return ::GetSystemMetrics(index);
    }
    static inline quint32 getWindowFrameBorderThickness(HWND hwnd) {
        const DynamicApis &apis = DynamicApis::instance();
        if (UINT result = 0; SUCCEEDED(apis.pDwmGetWindowAttribute(
                hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &result, sizeof(result)))) {
            return result;
        }
        return getSystemMetricsForDpi(SM_CXBORDER, getDpiForWindow(hwnd));
    }
    static inline quint32 getResizeBorderThickness(HWND hwnd) {
        const DynamicApis &apis = DynamicApis::instance();
        if (apis.pGetSystemMetricsForDpi) {
            const quint32 dpi = getDpiForWindow(hwnd);
            return apis.pGetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) +
                   apis.pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
        } else {
            return ::GetSystemMetrics(SM_CXSIZEFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER);
        }
        const quint32 dpi = getDpiForWindow(hwnd);
        return getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) +
               getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
    }
    static inline quint32 getTitleBarHeight(HWND hwnd) {
        const auto captionHeight = [hwnd]() -> int {
            const DynamicApis &apis = DynamicApis::instance();
            if (apis.pGetSystemMetricsForDpi) {
                const quint32 dpi = getDpiForWindow(hwnd);
                return apis.pGetSystemMetricsForDpi(SM_CYCAPTION, dpi);
            } else {
                return ::GetSystemMetrics(SM_CYCAPTION);
            }
        }();
        return captionHeight + getResizeBorderThickness(hwnd);
        const quint32 dpi = getDpiForWindow(hwnd);
        return getSystemMetricsForDpi(SM_CYCAPTION, dpi) +
               getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) +
               getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
    }
    static inline void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
    static void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
        const auto margins = [hwnd]() -> QMargins {
            const auto titleBarHeight = int(getTitleBarHeight(hwnd));
            if (isWin10OrGreater()) {
@@ -460,7 +455,7 @@
#endif
    }
    static inline void syncPaintEventWithDwm() {
    static void syncPaintEventWithDwm() {
        // No need to sync with DWM if DWM composition is disabled.
        if (!isDwmCompositionEnabled()) {
            return;
@@ -505,9 +500,9 @@
        apis.ptimeEndPeriod(ms_granularity);
    }
    static inline void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry,
                                       const bool fixedSize) {
        const HMENU hMenu = ::GetSystemMenu(hWnd, FALSE);
    static void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry,
                                const bool fixedSize) {
        HMENU hMenu = ::GetSystemMenu(hWnd, FALSE);
        if (!hMenu) {
            // The corresponding window doesn't have a system menu, most likely due to the
            // lack of the "WS_SYSMENU" window style. This situation should not be treated
@@ -809,74 +804,13 @@
                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 &region = *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 = *static_cast<QMap<QString, QColor> *>(data);
                map.clear();
                map.insert(QStringLiteral("activeLight"), kWindowsColorSet.activeLight);
                map.insert(QStringLiteral("activeDark"), kWindowsColorSet.activeDark);
                map.insert(QStringLiteral("inactiveLight"), kWindowsColorSet.inactiveLight);
                map.insert(QStringLiteral("inactiveDark"), kWindowsColorSet.inactiveDark);
                return;
            }
@@ -886,6 +820,16 @@
            }
        }
        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() {
@@ -950,6 +894,10 @@
        // Whether to show system menu
        if (systemMenuHandler(hWnd, message, wParam, lParam, result)) {
            return true;
        }
        if (themeStuffHandler(hWnd, message, wParam, lParam, result)) {
            return true;
        }
@@ -1939,4 +1887,81 @@
        return false;
    }
    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));
                break;
            }
            case WM_THEMECHANGED:
            case WM_SYSCOLORCHANGE: {
                break;
            }
            case WM_DWMCOLORIZATIONCOLORCHANGED: {
                const QColor color = QColor::fromRgba(wParam);
                const auto blendWithOpacity = *reinterpret_cast<LPBOOL>(lParam);
                break;
            }
            case WM_SETTINGCHANGE: {
                if (!wParam && lParam && std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0) {
                    const QColor color = getAccentColor();
                }
                break;
            }
            case WM_SIZE: {
                const bool max = wParam == SIZE_MAXIMIZED;
                const bool min = wParam == SIZE_MINIMIZED;
                const bool full = isFullScreen(hWnd);
                break;
            }
            case WM_ACTIVATE: {
                const auto state = LOWORD(wParam);
                const bool active = state == WA_ACTIVE || state == WA_CLICKACTIVE;
                break;
            }
            default:
                break;
        }
        return false;
    }
    void Win10BorderHandler::paintBorder(QPainter &painter, const QRect &rect,
                                         const QRegion &region) {
        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();
    }
}