macOS: Introduce QMacKeyValueObserver and use in theme and style
Change-Id: I2d21c883628933543ae5a66b694ff7503119bc4a Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
parent
99636127d1
commit
2dfe0f8f68
|
|
@ -514,5 +514,36 @@ Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version);
|
|||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void QMacKeyValueObserver::addObserver()
|
||||
{
|
||||
[object addObserver:observer forKeyPath:keyPath
|
||||
options:NSKeyValueObservingOptionNew context:callback.get()];
|
||||
}
|
||||
|
||||
void QMacKeyValueObserver::removeObserver() {
|
||||
if (object)
|
||||
[object removeObserver:observer forKeyPath:keyPath context:callback.get()];
|
||||
object = nil;
|
||||
}
|
||||
|
||||
KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
|
||||
|
||||
QT_END_NAMESPACE
|
||||
@implementation KeyValueObserver
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
|
||||
change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
|
||||
{
|
||||
Q_UNUSED(keyPath);
|
||||
Q_UNUSED(object);
|
||||
Q_UNUSED(change);
|
||||
|
||||
(*reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
|
||||
}
|
||||
@end
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
|
||||
#ifdef __OBJC__
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#include "qstring.h"
|
||||
|
|
@ -334,6 +335,60 @@ public:
|
|||
private:
|
||||
id observer = nil;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
@interface QT_MANGLE_NAMESPACE(KeyValueObserver) : NSObject
|
||||
@end
|
||||
QT_NAMESPACE_ALIAS_OBJC_CLASS(KeyValueObserver);
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_CORE_EXPORT QMacKeyValueObserver
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
QMacKeyValueObserver() {}
|
||||
|
||||
// Note: QMacKeyValueObserver must not outlive the object observed!
|
||||
QMacKeyValueObserver(id object, NSString *keyPath, Callback callback)
|
||||
: object(object), keyPath(keyPath), callback(new Callback(callback)) { addObserver(); }
|
||||
|
||||
QMacKeyValueObserver(const QMacKeyValueObserver &other)
|
||||
: QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {}
|
||||
|
||||
QMacKeyValueObserver(QMacKeyValueObserver &&other) { swap(other, *this); }
|
||||
|
||||
~QMacKeyValueObserver() { removeObserver(); }
|
||||
|
||||
QMacKeyValueObserver &operator=(const QMacKeyValueObserver &other) {
|
||||
QMacKeyValueObserver tmp(other);
|
||||
swap(tmp, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
QMacKeyValueObserver &operator=(QMacKeyValueObserver &&other) {
|
||||
QMacKeyValueObserver tmp(std::move(other));
|
||||
swap(tmp, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void removeObserver();
|
||||
|
||||
private:
|
||||
void swap(QMacKeyValueObserver &first, QMacKeyValueObserver &second) {
|
||||
std::swap(first.object, second.object);
|
||||
std::swap(first.keyPath, second.keyPath);
|
||||
std::swap(first.callback, second.callback);
|
||||
}
|
||||
|
||||
void addObserver();
|
||||
|
||||
id object = nil;
|
||||
NSString *keyPath = nullptr;
|
||||
std::unique_ptr<Callback> callback;
|
||||
|
||||
static KeyValueObserver *observer;
|
||||
};
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ private:
|
|||
QMacNotificationObserver m_systemColorObserver;
|
||||
mutable QHash<QPlatformTheme::Palette, QPalette*> m_palettes;
|
||||
mutable QHash<QPlatformTheme::Font, QFont*> m_fonts;
|
||||
QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) *m_appearanceObserver;
|
||||
QMacKeyValueObserver m_appearanceObserver;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -80,57 +80,22 @@
|
|||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
@interface QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) : NSObject
|
||||
@property (readonly, nonatomic) QCocoaTheme *theme;
|
||||
- (instancetype)initWithTheme:(QCocoaTheme *)theme;
|
||||
@end
|
||||
|
||||
QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaThemeAppAppearanceObserver);
|
||||
|
||||
@implementation QCocoaThemeAppAppearanceObserver
|
||||
- (instancetype)initWithTheme:(QCocoaTheme *)theme
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_theme = theme;
|
||||
[NSApp addObserver:self forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew context:nullptr];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[NSApp removeObserver:self forKeyPath:@"effectiveAppearance"];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
|
||||
change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
|
||||
{
|
||||
Q_UNUSED(change);
|
||||
Q_UNUSED(context);
|
||||
|
||||
Q_ASSERT(object == NSApp);
|
||||
Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]);
|
||||
|
||||
if (__builtin_available(macOS 10.14, *))
|
||||
NSAppearance.currentAppearance = NSApp.effectiveAppearance;
|
||||
|
||||
self.theme->handleSystemThemeChange();
|
||||
}
|
||||
@end
|
||||
#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
const char *QCocoaTheme::name = "cocoa";
|
||||
|
||||
QCocoaTheme::QCocoaTheme()
|
||||
: m_systemPalette(nullptr), m_appearanceObserver(nil)
|
||||
: m_systemPalette(nullptr)
|
||||
{
|
||||
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave)
|
||||
m_appearanceObserver = [[QCocoaThemeAppAppearanceObserver alloc] initWithTheme:this];
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) {
|
||||
m_appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] {
|
||||
if (__builtin_available(macOS 10.14, *))
|
||||
NSAppearance.currentAppearance = NSApp.effectiveAppearance;
|
||||
|
||||
handleSystemThemeChange();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
m_systemColorObserver = QMacNotificationObserver(nil,
|
||||
|
|
@ -141,9 +106,6 @@ QCocoaTheme::QCocoaTheme()
|
|||
|
||||
QCocoaTheme::~QCocoaTheme()
|
||||
{
|
||||
if (m_appearanceObserver)
|
||||
[m_appearanceObserver release];
|
||||
|
||||
reset();
|
||||
qDeleteAll(m_fonts);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,41 +144,6 @@ static QWindow *qt_getWindow(const QWidget *widget)
|
|||
return widget ? widget->window()->windowHandle() : 0;
|
||||
}
|
||||
|
||||
@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject
|
||||
@end
|
||||
|
||||
QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver);
|
||||
|
||||
@implementation NotificationReceiver
|
||||
{
|
||||
QMacStylePrivate *privateStyle;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPrivateStyle:(QMacStylePrivate *)style
|
||||
{
|
||||
if (self = [super init])
|
||||
privateStyle = style;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
|
||||
change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
|
||||
{
|
||||
Q_UNUSED(keyPath);
|
||||
Q_UNUSED(object);
|
||||
Q_UNUSED(change);
|
||||
Q_UNUSED(context);
|
||||
|
||||
Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]);
|
||||
Q_ASSERT(object == NSApp);
|
||||
|
||||
for (NSView *b : privateStyle->cocoaControls)
|
||||
[b release];
|
||||
privateStyle->cocoaControls.clear();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator
|
||||
|
||||
@property (readonly, nonatomic) NSInteger animators;
|
||||
|
|
@ -2080,7 +2045,6 @@ void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const
|
|||
QMacStyle::QMacStyle()
|
||||
: QCommonStyle(*new QMacStylePrivate)
|
||||
{
|
||||
Q_D(QMacStyle);
|
||||
QMacAutoReleasePool pool;
|
||||
|
||||
static QMacNotificationObserver scrollbarStyleObserver(nil,
|
||||
|
|
@ -2093,26 +2057,21 @@ QMacStyle::QMacStyle()
|
|||
QCoreApplication::sendEvent(o, &event);
|
||||
});
|
||||
|
||||
d->receiver = [[NotificationReceiver alloc] initWithPrivateStyle:d];
|
||||
|
||||
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
Q_D(QMacStyle);
|
||||
// FIXME: Tie this logic into theme change, or even polish/unpolish
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) {
|
||||
[NSApplication.sharedApplication addObserver:d->receiver forKeyPath:@"effectiveAppearance"
|
||||
options:NSKeyValueObservingOptionNew context:nullptr];
|
||||
d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [&d] {
|
||||
for (NSView *b : d->cocoaControls)
|
||||
[b release];
|
||||
d->cocoaControls.clear();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QMacStyle::~QMacStyle()
|
||||
{
|
||||
Q_D(QMacStyle);
|
||||
QMacAutoReleasePool pool;
|
||||
|
||||
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave)
|
||||
[NSApplication.sharedApplication removeObserver:d->receiver forKeyPath:@"effectiveAppearance"];
|
||||
#endif
|
||||
[d->receiver release];
|
||||
}
|
||||
|
||||
void QMacStyle::polish(QPalette &)
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGContext);
|
|||
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSView);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(NSCell);
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(NotificationReceiver));
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -288,13 +287,14 @@ public:
|
|||
static QVector<QPointer<QObject> > scrollBars;
|
||||
|
||||
mutable QPointer<QFocusFrame> focusWidget;
|
||||
QT_MANGLE_NAMESPACE(NotificationReceiver) *receiver;
|
||||
mutable NSView *backingStoreNSView;
|
||||
mutable QHash<CocoaControl, NSView *> cocoaControls;
|
||||
mutable QHash<CocoaControl, NSCell *> cocoaCells;
|
||||
|
||||
QFont smallSystemFont;
|
||||
QFont miniSystemFont;
|
||||
|
||||
QMacKeyValueObserver appearanceObserver;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
Loading…
Reference in New Issue