macOS: Port from deprecated NSAppearance.currentAppearance

When the system appearance changes, it will be reflected through
NSApp.effectiveAppearance, but NSAppearance.currentAppearance
and NSAppearance.currentDrawingAppearance will remain as is for
some reason (perhaps as a compatibility for apps that were not
ready to handle dark mode).

The original way to deal with this was to explicitly set the
NSAppearance.currentAppearance to the new effective appearance,
which would take effect for the thread from that point on. But
this API has been deprecated, most likely because overriding it
globally could mess up views or logic that was not prepared for
dark mode).

The replacement API, NSAppearance performAsCurrentDrawingAppearance
is given a block, and will only override NSAppearance.currentAppearance
during the block, which isolates it from other fragile components.

We now use a helper from QtGui that takes care of wrapping our
style drawing in performAsCurrentDrawingAppearance.

Task-number: QTBUG-135789
Change-Id: I0eb5dfc05734cf75181e6a3ed8cf2d23185f425f
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Tor Arne Vestbø 2025-04-15 23:52:01 +02:00
parent 6ae7a5a89e
commit fa4568622c
3 changed files with 14 additions and 42 deletions

View File

@ -31,6 +31,9 @@
#include <QtGui/private/qcoregraphics_p.h>
#include <QtGui/private/qguiapplication_p.h>
using namespace QQC2;
#include <QtGui/private/qmacstyle_p.h>
#include <cmath>
QT_USE_NAMESPACE
@ -265,42 +268,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 QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
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];
}
}
#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
}
~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;
@ -1615,6 +1582,11 @@ void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const
backingStoreNSView = window ? (NSView *)window->winId() : nil;
}
QMacStyle *QMacStyle::create()
{
return new QMacApperanceStyle<QMacStyle>;
}
QMacStyle::QMacStyle()
: QCommonStyle(*new QMacStylePrivate)
{
@ -2408,7 +2380,6 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
{
Q_D(const QMacStyle);
const AppearanceSync appSync;
QMacCGContext cg(p);
d->resolveCurrentNSView(opt->window);
@ -2903,7 +2874,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
{
Q_D(const QMacStyle);
const AppearanceSync sync;
const QMacAutoReleasePool pool;
QMacCGContext cg(p);
@ -4431,7 +4401,6 @@ void QMacStylePrivate::restoreNSGraphicsContext(CGContextRef cg) const
void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const
{
Q_D(const QMacStyle);
const AppearanceSync sync;
QMacCGContext cg(p);
d->resolveCurrentNSView(opt->window);

View File

@ -32,10 +32,13 @@ class QMacStylePrivate;
class QMacStyle : public QCommonStyle
{
Q_OBJECT
public:
protected:
QMacStyle();
public:
~QMacStyle();
static QMacStyle *create();
void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const override;
void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const override;
void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const override;

View File

@ -98,7 +98,7 @@ void QtQuickControls2NativeStylePlugin::initializeEngine(QQmlEngine *engine, con
style = new QCommonStyle;
#if defined(Q_OS_MACOS)
else if (envStyle == QLatin1String("mac"))
style = new QMacStyle;
style = QMacStyle::create();
#endif
#if defined(Q_OS_WINDOWS)
else if (envStyle == QLatin1String("windows"))
@ -109,7 +109,7 @@ void QtQuickControls2NativeStylePlugin::initializeEngine(QQmlEngine *engine, con
}
if (!style) {
#if defined(Q_OS_MACOS)
style = new QMacStyle;
style = QMacStyle::create();
#elif defined(Q_OS_WINDOWS)
style = new QWindowsXPStyle;
if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark)