diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index ba77cb1cccc..024bcfab426 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -395,6 +395,7 @@ qt_internal_extend_target(Gui CONDITION QT_FEATURE_opengl qt_internal_extend_target(Gui CONDITION MACOS SOURCES platform/macos/qcocoanativeinterface.mm + platform/macos/qmacstyle_p.h LIBRARIES ${FWAppKit} ${FWCarbon} diff --git a/src/gui/platform/macos/qmacstyle_p.h b/src/gui/platform/macos/qmacstyle_p.h new file mode 100644 index 00000000000..bbfbc1a06e3 --- /dev/null +++ b/src/gui/platform/macos/qmacstyle_p.h @@ -0,0 +1,81 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QMACSTYLE_P_H +#define QMACSTYLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#if defined(QT_WIDGETS_LIB) && defined(QT_QUICK_LIB) +# error "Cannot use QtGui Mac style with both Widgets and Quick" +#endif + +#if defined(QT_WIDGETS_LIB) +# define OPTIONAL_WIDGET_ARGUMENT , const QWidget *w = nullptr +# define FORWARD_OPTIONAL_WIDGET_ARGUMENT , w +# else +# define OPTIONAL_WIDGET_ARGUMENT +# define FORWARD_OPTIONAL_WIDGET_ARGUMENT +#endif + +#include + +QT_BEGIN_NAMESPACE + +/* + Helper class to ensure that the Mac style in Widgets and Quick + draw their NSViews and NSCells with the correct appearance, + as the native controls use NSAppearance.currentDrawingAppearance + instead of NSApp.effectiveAppearance when drawing. + + Due to the duplicated class hierarchies between Widgets and Quick + for the styles, with the Quick styles missing the QWidget pointer + in the function parameters, we have to opt for an awkward macro + to solve this. +*/ +template +class QMacApperanceStyle : public Style +{ +public: + void drawPrimitive(typename Style::PrimitiveElement pe, const QStyleOption *opt, QPainter *p + OPTIONAL_WIDGET_ARGUMENT) const override + { + [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{ + Style::drawPrimitive(pe, opt, p + FORWARD_OPTIONAL_WIDGET_ARGUMENT); + }]; + } + + void drawControl(typename Style::ControlElement element, const QStyleOption *opt, QPainter *p + OPTIONAL_WIDGET_ARGUMENT) const override + { + [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{ + Style::drawControl(element, opt, p + FORWARD_OPTIONAL_WIDGET_ARGUMENT); + }]; + } + + void drawComplexControl(typename Style::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p + OPTIONAL_WIDGET_ARGUMENT) const override + { + [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{ + Style::drawComplexControl(cc, opt, p + FORWARD_OPTIONAL_WIDGET_ARGUMENT); + }]; + } +}; + +QT_END_NAMESPACE + +#endif // QMACSTYLE_P_H diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 3e7e01d8048..e3ff518b4e1 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -214,7 +214,6 @@ QCocoaTheme::QCocoaTheme() : m_systemPalette(nullptr) { m_appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] { - NSAppearance.currentAppearance = NSApp.effectiveAppearance; handleSystemThemeChange(); }); @@ -291,13 +290,23 @@ QPlatformSystemTrayIcon *QCocoaTheme::createPlatformSystemTrayIcon() const const QPalette *QCocoaTheme::palette(Palette type) const { + // Note: NSColor resolves its RGB values based on the current + // drawing appearance, so we need to propagate the effective + // appearance when (re)creating the palettes. + if (type == SystemPalette) { - if (!m_systemPalette) - m_systemPalette = qt_mac_createSystemPalette(); + if (!m_systemPalette) { + [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{ + m_systemPalette = qt_mac_createSystemPalette(); + }]; + } return m_systemPalette; } else { - if (m_palettes.isEmpty()) - m_palettes = qt_mac_createRolePalettes(); + if (m_palettes.isEmpty()) { + [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{ + m_palettes = qt_mac_createRolePalettes(); + }]; + } return m_palettes.value(type, nullptr); } return nullptr; diff --git a/src/plugins/styles/mac/main.mm b/src/plugins/styles/mac/main.mm index 5f4fbd9eb8e..ef6ca46045d 100644 --- a/src/plugins/styles/mac/main.mm +++ b/src/plugins/styles/mac/main.mm @@ -20,7 +20,7 @@ QStyle *QMacStylePlugin::create(const QString &key) { QMacAutoReleasePool pool; if (key.compare(QLatin1String("macos"), Qt::CaseInsensitive) == 0) - return new QMacStyle(); + return QMacStyle::create(); return 0; } diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index bc411eb72d8..83b2d284e86 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -41,6 +41,8 @@ #include #endif +#include + #include QT_USE_NAMESPACE @@ -338,40 +340,6 @@ static const int toolButtonArrowMargin = 2; static const qreal focusRingWidth = 3.5; -// An application can force 'Aqua' theme while the system theme is one of -// the 'Dark' variants. Since in Qt we sometimes use NSControls and even -// NSCells directly without attaching them to any view hierarchy, we have -// to set NSAppearance.currentAppearance to 'Aqua' manually, to make sure -// the correct rendering path is triggered. Apple recommends us to un-set -// the current appearance back after we finished with drawing. This is what -// AppearanceSync is for. - -class AppearanceSync { -public: - AppearanceSync() - { - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !isDarkMode()) { - auto requiredAppearanceName = NSApplication.sharedApplication.effectiveAppearance.name; - if (![NSAppearance.currentAppearance.name isEqualToString:requiredAppearanceName]) { - previous = NSAppearance.currentAppearance; - NSAppearance.currentAppearance = [NSAppearance appearanceNamed:requiredAppearanceName]; - } - } - } - - ~AppearanceSync() - { - if (previous) - NSAppearance.currentAppearance = previous; - } - -private: - NSAppearance *previous = nil; - - Q_DISABLE_COPY(AppearanceSync) -}; - static bool setupScroller(NSScroller *scroller, const QStyleOptionSlider *sb) { const qreal length = sb->maximum - sb->minimum + sb->pageStep; @@ -1995,6 +1963,11 @@ void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const backingStoreNSView = window ? (NSView *)window->winId() : nil; } +QMacStyle *QMacStyle::create() +{ + return new QMacApperanceStyle; +} + QMacStyle::QMacStyle() : QCommonStyle(*new QMacStylePrivate) { @@ -2886,7 +2859,6 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai const QWidget *w) const { Q_D(const QMacStyle); - const AppearanceSync appSync; QMacCGContext cg(p); QWindow *window = w && w->window() ? w->window()->windowHandle() : nullptr; d->resolveCurrentNSView(window); @@ -3414,7 +3386,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const QWidget *w) const { Q_D(const QMacStyle); - const AppearanceSync sync; const QMacAutoReleasePool pool; QMacCGContext cg(p); QWindow *window = w && w->window() ? w->window()->windowHandle() : nullptr; @@ -5086,7 +5057,6 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const QWidget *widget) const { Q_D(const QMacStyle); - const AppearanceSync sync; QMacCGContext cg(p); QWindow *window = widget && widget->window() ? widget->window()->windowHandle() : nullptr; d->resolveCurrentNSView(window); diff --git a/src/plugins/styles/mac/qmacstyle_mac_p.h b/src/plugins/styles/mac/qmacstyle_mac_p.h index 99c8d1fec42..15ced48e21d 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p.h @@ -28,10 +28,13 @@ class QMacStylePrivate; class QMacStyle : public QCommonStyle { Q_OBJECT -public: +protected: QMacStyle(); +public: virtual ~QMacStyle(); + static QMacStyle *create(); + void polish(QWidget *w); void unpolish(QWidget *w);