| @@ -503,11 +503,18 @@ DECLARE_JNI_CLASS (AndroidRect, "android/graphics/Rect") | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| METHOD (getIdentifier, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I") \ | |||
| METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;") | |||
| METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;") \ | |||
| METHOD (getConfiguration, "getConfiguration", "()Landroid/content/res/Configuration;") | |||
| DECLARE_JNI_CLASS (AndroidResources, "android/content/res/Resources") | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| FIELD (uiMode, "uiMode", "I") \ | |||
| DECLARE_JNI_CLASS (AndroidConfiguration, "android/content/res/Configuration") | |||
| #undef JNI_CLASS_MEMBERS | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ | |||
| METHOD (getHeight, "getHeight", "()I") \ | |||
| METHOD (getWidth, "getWidth", "()I") | |||
| @@ -28,7 +28,8 @@ namespace juce | |||
| Desktop::Desktop() | |||
| : mouseSources (new MouseInputSource::SourceList()), | |||
| masterScaleFactor ((float) getDefaultMasterScale()) | |||
| masterScaleFactor ((float) getDefaultMasterScale()), | |||
| nativeDarkModeChangeDetectorImpl (createNativeDarkModeChangeDetectorImpl()) | |||
| { | |||
| displays.reset (new Displays (*this)); | |||
| } | |||
| @@ -198,6 +199,12 @@ void Desktop::handleAsyncUpdate() | |||
| }); | |||
| } | |||
| //============================================================================== | |||
| void Desktop::addDarkModeSettingListener (DarkModeSettingListener* l) { darkModeSettingListeners.add (l); } | |||
| void Desktop::removeDarkModeSettingListener (DarkModeSettingListener* l) { darkModeSettingListeners.remove (l); } | |||
| void Desktop::darkModeChanged() { darkModeSettingListeners.call ([] (DarkModeSettingListener& l) { l.darkModeSettingChanged(); }); } | |||
| //============================================================================== | |||
| void Desktop::resetTimer() | |||
| { | |||
| @@ -45,6 +45,26 @@ public: | |||
| virtual void globalFocusChanged (Component* focusedComponent) = 0; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Classes can implement this interface and register themselves with the Desktop class | |||
| to receive callbacks when the operating system dark mode setting changes. The | |||
| Desktop::isDarkModeActive() method can then be used to query the current setting. | |||
| @see Desktop::addDarkModeSettingListener, Desktop::removeDarkModeSettingListener, | |||
| Desktop::isDarkModeActive | |||
| @tags{GUI} | |||
| */ | |||
| class JUCE_API DarkModeSettingListener | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~DarkModeSettingListener() = default; | |||
| /** Callback to indicate that the dark mode setting has changed. */ | |||
| virtual void darkModeSettingChanged() = 0; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| @@ -135,8 +155,7 @@ public: | |||
| */ | |||
| void addGlobalMouseListener (MouseListener* listener); | |||
| /** Unregisters a MouseListener that was added with the addGlobalMouseListener() | |||
| method. | |||
| /** Unregisters a MouseListener that was added with addGlobalMouseListener(). | |||
| @see addGlobalMouseListener | |||
| */ | |||
| @@ -150,13 +169,36 @@ public: | |||
| */ | |||
| void addFocusChangeListener (FocusChangeListener* listener); | |||
| /** Unregisters a FocusChangeListener that was added with the addFocusChangeListener() | |||
| method. | |||
| /** Unregisters a FocusChangeListener that was added with addFocusChangeListener(). | |||
| @see addFocusChangeListener | |||
| */ | |||
| void removeFocusChangeListener (FocusChangeListener* listener); | |||
| //============================================================================== | |||
| /** Registers a DarkModeSettingListener that will receive a callback when the | |||
| operating system dark mode setting changes. To query whether dark mode is on | |||
| use the isDarkModeActive() method. | |||
| @see isDarkModeActive, removeDarkModeSettingListener | |||
| */ | |||
| void addDarkModeSettingListener (DarkModeSettingListener* listener); | |||
| /** Unregisters a DarkModeSettingListener that was added with addDarkModeSettingListener(). | |||
| @see addDarkModeSettingListener | |||
| */ | |||
| void removeDarkModeSettingListener (DarkModeSettingListener* listener); | |||
| /** True if the operating system "dark mode" is active. | |||
| To receive a callback when this setting changes implement the DarkModeSettingListener | |||
| interface and use the addDarkModeSettingListener() to register a listener. | |||
| @see addDarkModeSettingListener, removeDarkModeSettingListener | |||
| */ | |||
| bool isDarkModeActive() const; | |||
| //============================================================================== | |||
| /** Takes a component and makes it full-screen, removing the taskbar, dock, etc. | |||
| @@ -352,9 +394,10 @@ public: | |||
| /** True if the OS supports semitransparent windows */ | |||
| static bool canUseSemiTransparentWindows() noexcept; | |||
| #if JUCE_MAC | |||
| /** OSX-specific function to check for the "dark" title-bar and menu mode. */ | |||
| static bool isOSXDarkModeActive(); | |||
| #if JUCE_MAC && ! defined (DOXYGEN) | |||
| [[deprecated ("This macOS-specific method has been deprecated in favour of the cross-platform " | |||
| " isDarkModeActive() method.")]] | |||
| static bool isOSXDarkModeActive() { return Desktop::getInstance().isDarkModeActive(); } | |||
| #endif | |||
| //============================================================================== | |||
| @@ -376,6 +419,7 @@ private: | |||
| ListenerList<MouseListener> mouseListeners; | |||
| ListenerList<FocusChangeListener> focusListeners; | |||
| ListenerList<DarkModeSettingListener> darkModeSettingListeners; | |||
| Array<Component*> desktopComponents; | |||
| Array<ComponentPeer*> peers; | |||
| @@ -423,6 +467,14 @@ private: | |||
| Desktop(); | |||
| ~Desktop() override; | |||
| //============================================================================== | |||
| class NativeDarkModeChangeDetectorImpl; | |||
| std::unique_ptr<NativeDarkModeChangeDetectorImpl> nativeDarkModeChangeDetectorImpl; | |||
| static std::unique_ptr<NativeDarkModeChangeDetectorImpl> createNativeDarkModeChangeDetectorImpl(); | |||
| void darkModeChanged(); | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop) | |||
| }; | |||
| @@ -1236,6 +1236,86 @@ bool Desktop::canUseSemiTransparentWindows() noexcept | |||
| return true; | |||
| } | |||
| class Desktop::NativeDarkModeChangeDetectorImpl : public ActivityLifecycleCallbacks | |||
| { | |||
| public: | |||
| NativeDarkModeChangeDetectorImpl() | |||
| { | |||
| LocalRef<jobject> appContext (getAppContext()); | |||
| if (appContext != nullptr) | |||
| { | |||
| auto* env = getEnv(); | |||
| myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks")); | |||
| env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get()); | |||
| } | |||
| } | |||
| ~NativeDarkModeChangeDetectorImpl() override | |||
| { | |||
| LocalRef<jobject> appContext (getAppContext()); | |||
| if (appContext != nullptr && myself != nullptr) | |||
| { | |||
| auto* env = getEnv(); | |||
| env->CallVoidMethod (appContext.get(), | |||
| AndroidApplication.unregisterActivityLifecycleCallbacks, | |||
| myself.get()); | |||
| clear(); | |||
| myself.clear(); | |||
| } | |||
| } | |||
| bool isDarkModeEnabled() const noexcept { return darkModeEnabled; } | |||
| void onActivityStarted (jobject /*activity*/) override | |||
| { | |||
| const auto isEnabled = getDarkModeSetting(); | |||
| if (darkModeEnabled != isEnabled) | |||
| { | |||
| darkModeEnabled = isEnabled; | |||
| Desktop::getInstance().darkModeChanged(); | |||
| } | |||
| } | |||
| private: | |||
| static bool getDarkModeSetting() | |||
| { | |||
| auto* env = getEnv(); | |||
| const LocalRef<jobject> resources (env->CallObjectMethod (getAppContext().get(), AndroidContext.getResources)); | |||
| const LocalRef<jobject> configuration (env->CallObjectMethod (resources, AndroidResources.getConfiguration)); | |||
| const auto uiMode = env->GetIntField (configuration, AndroidConfiguration.uiMode); | |||
| return ((uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES); | |||
| } | |||
| static constexpr int UI_MODE_NIGHT_MASK = 0x00000030, | |||
| UI_MODE_NIGHT_NO = 0x00000010, | |||
| UI_MODE_NIGHT_UNDEFINED = 0x00000000, | |||
| UI_MODE_NIGHT_YES = 0x00000020; | |||
| GlobalRef myself; | |||
| bool darkModeEnabled = getDarkModeSetting(); | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) | |||
| }; | |||
| std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl() | |||
| { | |||
| return std::make_unique<NativeDarkModeChangeDetectorImpl>(); | |||
| } | |||
| bool Desktop::isDarkModeActive() const | |||
| { | |||
| return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled(); | |||
| } | |||
| double Desktop::getDefaultMasterScale() | |||
| { | |||
| return 1.0; | |||
| @@ -137,6 +137,8 @@ using namespace juce; | |||
| - (BOOL) textView: (UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text; | |||
| - (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection; | |||
| - (BOOL) isAccessibilityElement; | |||
| - (CGRect) accessibilityFrame; | |||
| - (NSArray*) accessibilityElements; | |||
| @@ -270,6 +272,11 @@ public: | |||
| return getMouseTime ([e timestamp]); | |||
| } | |||
| static NSString* getDarkModeNotificationName() | |||
| { | |||
| return @"ViewDarkModeChanged"; | |||
| } | |||
| static MultiTouchMapper<UITouch*> currentTouches; | |||
| private: | |||
| @@ -296,25 +303,27 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer) | |||
| }; | |||
| static void sendScreenBoundsUpdate (JuceUIViewController* c) | |||
| static UIViewComponentPeer* getViewPeer (JuceUIViewController* c) | |||
| { | |||
| JuceUIView* juceView = (JuceUIView*) [c view]; | |||
| if (JuceUIView* juceView = (JuceUIView*) [c view]) | |||
| return juceView->owner; | |||
| if (juceView != nil && juceView->owner != nullptr) | |||
| juceView->owner->updateScreenBounds(); | |||
| jassertfalse; | |||
| return nullptr; | |||
| } | |||
| static bool isKioskModeView (JuceUIViewController* c) | |||
| static void sendScreenBoundsUpdate (JuceUIViewController* c) | |||
| { | |||
| JuceUIView* juceView = (JuceUIView*) [c view]; | |||
| if (auto* peer = getViewPeer (c)) | |||
| peer->updateScreenBounds(); | |||
| } | |||
| if (juceView == nil || juceView->owner == nullptr) | |||
| { | |||
| jassertfalse; | |||
| return false; | |||
| } | |||
| static bool isKioskModeView (JuceUIViewController* c) | |||
| { | |||
| if (auto* peer = getViewPeer (c)) | |||
| return Desktop::getInstance().getKioskModeComponent() == &(peer->getComponent()); | |||
| return Desktop::getInstance().getKioskModeComponent() == &(juceView->owner->getComponent()); | |||
| return false; | |||
| } | |||
| MultiTouchMapper<UITouch*> UIViewComponentPeer::currentTouches; | |||
| @@ -544,6 +553,22 @@ MultiTouchMapper<UITouch*> UIViewComponentPeer::currentTouches; | |||
| nsStringToJuce (text)); | |||
| } | |||
| - (void) traitCollectionDidChange: (UITraitCollection*) previousTraitCollection | |||
| { | |||
| [super traitCollectionDidChange: previousTraitCollection]; | |||
| #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 | |||
| if (@available (iOS 12.0, *)) | |||
| { | |||
| const auto wasDarkModeActive = ([previousTraitCollection userInterfaceStyle] == UIUserInterfaceStyleDark); | |||
| if (wasDarkModeActive != Desktop::getInstance().isDarkModeActive()) | |||
| [[NSNotificationCenter defaultCenter] postNotificationName: UIViewComponentPeer::getDarkModeNotificationName() | |||
| object: nil]; | |||
| } | |||
| #endif | |||
| } | |||
| - (BOOL) isAccessibilityElement | |||
| { | |||
| return NO; | |||
| @@ -678,6 +678,77 @@ bool Desktop::canUseSemiTransparentWindows() noexcept | |||
| return true; | |||
| } | |||
| bool Desktop::isDarkModeActive() const | |||
| { | |||
| #if defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 | |||
| if (@available (iOS 12.0, *)) | |||
| return [[[UIScreen mainScreen] traitCollection] userInterfaceStyle] == UIUserInterfaceStyleDark; | |||
| #endif | |||
| return false; | |||
| } | |||
| class Desktop::NativeDarkModeChangeDetectorImpl | |||
| { | |||
| public: | |||
| NativeDarkModeChangeDetectorImpl() | |||
| { | |||
| static DelegateClass delegateClass; | |||
| delegate = [delegateClass.createInstance() init]; | |||
| object_setInstanceVariable (delegate, "owner", this); | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | |||
| [[NSNotificationCenter defaultCenter] addObserver: delegate | |||
| selector: @selector (darkModeChanged:) | |||
| name: UIViewComponentPeer::getDarkModeNotificationName() | |||
| object: nil]; | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| } | |||
| ~NativeDarkModeChangeDetectorImpl() | |||
| { | |||
| object_setInstanceVariable (delegate, "owner", nullptr); | |||
| [[NSNotificationCenter defaultCenter] removeObserver: delegate]; | |||
| [delegate release]; | |||
| } | |||
| void darkModeChanged() | |||
| { | |||
| Desktop::getInstance().darkModeChanged(); | |||
| } | |||
| private: | |||
| struct DelegateClass : public ObjCClass<NSObject> | |||
| { | |||
| DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_") | |||
| { | |||
| addIvar<NativeDarkModeChangeDetectorImpl*> ("owner"); | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | |||
| addMethod (@selector (darkModeChanged:), darkModeChanged, "v@:@"); | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| registerClass(); | |||
| } | |||
| static void darkModeChanged (id self, SEL, NSNotification*) | |||
| { | |||
| if (auto* owner = getIvar<NativeDarkModeChangeDetectorImpl*> (self, "owner")) | |||
| owner->darkModeChanged(); | |||
| } | |||
| }; | |||
| id delegate = nil; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) | |||
| }; | |||
| std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl() | |||
| { | |||
| return std::make_unique<NativeDarkModeChangeDetectorImpl>(); | |||
| } | |||
| Point<float> MouseInputSource::getCurrentRawMousePosition() | |||
| { | |||
| return juce_lastMousePos; | |||
| @@ -534,6 +534,18 @@ bool Desktop::canUseSemiTransparentWindows() noexcept | |||
| return XWindowSystem::getInstance()->canUseSemiTransparentWindows(); | |||
| } | |||
| bool Desktop::isDarkModeActive() const | |||
| { | |||
| return false; | |||
| } | |||
| class Desktop::NativeDarkModeChangeDetectorImpl { public: NativeDarkModeChangeDetectorImpl() = default; }; | |||
| std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl() | |||
| { | |||
| return nullptr; | |||
| } | |||
| static bool screenSaverAllowed = true; | |||
| void Desktop::setScreenSaverEnabled (bool isEnabled) | |||
| @@ -430,6 +430,73 @@ Desktop::DisplayOrientation Desktop::getCurrentOrientation() const | |||
| return upright; | |||
| } | |||
| bool Desktop::isDarkModeActive() const | |||
| { | |||
| return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")] | |||
| isEqualToString: nsStringLiteral ("Dark")]; | |||
| } | |||
| class Desktop::NativeDarkModeChangeDetectorImpl | |||
| { | |||
| public: | |||
| NativeDarkModeChangeDetectorImpl() | |||
| { | |||
| static DelegateClass delegateClass; | |||
| delegate = [delegateClass.createInstance() init]; | |||
| object_setInstanceVariable (delegate, "owner", this); | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | |||
| [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate | |||
| selector: @selector (darkModeChanged:) | |||
| name: @"AppleInterfaceThemeChangedNotification" | |||
| object: nil]; | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| } | |||
| ~NativeDarkModeChangeDetectorImpl() | |||
| { | |||
| object_setInstanceVariable (delegate, "owner", nullptr); | |||
| [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate]; | |||
| [delegate release]; | |||
| } | |||
| void darkModeChanged() | |||
| { | |||
| Desktop::getInstance().darkModeChanged(); | |||
| } | |||
| private: | |||
| struct DelegateClass : public ObjCClass<NSObject> | |||
| { | |||
| DelegateClass() : ObjCClass<NSObject> ("JUCEDelegate_") | |||
| { | |||
| addIvar<NativeDarkModeChangeDetectorImpl*> ("owner"); | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | |||
| addMethod (@selector (darkModeChanged:), darkModeChanged, "v@:@"); | |||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
| registerClass(); | |||
| } | |||
| static void darkModeChanged (id self, SEL, NSNotification*) | |||
| { | |||
| if (auto* owner = getIvar<NativeDarkModeChangeDetectorImpl*> (self, "owner")) | |||
| owner->darkModeChanged(); | |||
| } | |||
| }; | |||
| id delegate = nil; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) | |||
| }; | |||
| std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl() | |||
| { | |||
| return std::make_unique<NativeDarkModeChangeDetectorImpl>(); | |||
| } | |||
| //============================================================================== | |||
| class ScreenSaverDefeater : public Timer | |||
| { | |||
| @@ -674,10 +741,4 @@ void Process::setDockIconVisible (bool isVisible) | |||
| ignoreUnused (err); | |||
| } | |||
| bool Desktop::isOSXDarkModeActive() | |||
| { | |||
| return [[[NSUserDefaults standardUserDefaults] stringForKey: nsStringLiteral ("AppleInterfaceStyle")] | |||
| isEqualToString: nsStringLiteral ("Dark")]; | |||
| } | |||
| } // namespace juce | |||
| @@ -712,6 +712,8 @@ static void setWindowZOrder (HWND hwnd, HWND insertAfter) | |||
| } | |||
| //============================================================================== | |||
| extern RTL_OSVERSIONINFOW getWindowsVersionInfo(); | |||
| double Desktop::getDefaultMasterScale() | |||
| { | |||
| if (! JUCEApplicationBase::isStandaloneApp() || isPerMonitorDPIAwareProcess()) | |||
| @@ -720,7 +722,95 @@ double Desktop::getDefaultMasterScale() | |||
| return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI; | |||
| } | |||
| bool Desktop::canUseSemiTransparentWindows() noexcept { return true; } | |||
| bool Desktop::canUseSemiTransparentWindows() noexcept | |||
| { | |||
| return true; | |||
| } | |||
| class Desktop::NativeDarkModeChangeDetectorImpl | |||
| { | |||
| public: | |||
| NativeDarkModeChangeDetectorImpl() | |||
| { | |||
| const auto winVer = getWindowsVersionInfo(); | |||
| if (winVer.dwMajorVersion >= 10 && winVer.dwBuildNumber >= 17763) | |||
| { | |||
| const auto uxtheme = "uxtheme.dll"; | |||
| LoadLibraryA (uxtheme); | |||
| const auto uxthemeModule = GetModuleHandleA (uxtheme); | |||
| if (uxthemeModule != nullptr) | |||
| { | |||
| shouldAppsUseDarkMode = (ShouldAppsUseDarkModeFunc) GetProcAddress (uxthemeModule, MAKEINTRESOURCEA (132)); | |||
| if (shouldAppsUseDarkMode != nullptr) | |||
| darkModeEnabled = shouldAppsUseDarkMode() && ! isHighContrast(); | |||
| } | |||
| } | |||
| } | |||
| bool isDarkModeEnabled() const noexcept { return darkModeEnabled; } | |||
| private: | |||
| static bool isHighContrast() | |||
| { | |||
| HIGHCONTRASTW highContrast { 0 }; | |||
| if (SystemParametersInfoW (SPI_GETHIGHCONTRAST, sizeof (highContrast), &highContrast, false)) | |||
| return highContrast.dwFlags & HCF_HIGHCONTRASTON; | |||
| return false; | |||
| } | |||
| static LRESULT CALLBACK callWndProc (int nCode, WPARAM wParam, LPARAM lParam) | |||
| { | |||
| auto* params = reinterpret_cast<CWPSTRUCT*> (lParam); | |||
| if (nCode >= 0 | |||
| && params != nullptr | |||
| && params->message == WM_SETTINGCHANGE | |||
| && params->lParam != 0 | |||
| && CompareStringOrdinal (reinterpret_cast<LPWCH> (params->lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) | |||
| { | |||
| Desktop::getInstance().nativeDarkModeChangeDetectorImpl->colourSetChanged(); | |||
| } | |||
| return CallNextHookEx ({}, nCode, wParam, lParam); | |||
| } | |||
| void colourSetChanged() | |||
| { | |||
| if (shouldAppsUseDarkMode != nullptr) | |||
| { | |||
| const auto wasDarkModeEnabled = std::exchange (darkModeEnabled, shouldAppsUseDarkMode() && ! isHighContrast()); | |||
| if (darkModeEnabled != wasDarkModeEnabled) | |||
| Desktop::getInstance().darkModeChanged(); | |||
| } | |||
| } | |||
| using ShouldAppsUseDarkModeFunc = bool (WINAPI*)(); | |||
| ShouldAppsUseDarkModeFunc shouldAppsUseDarkMode = nullptr; | |||
| bool darkModeEnabled = false; | |||
| HHOOK hook { SetWindowsHookEx (WH_CALLWNDPROC, | |||
| callWndProc, | |||
| (HINSTANCE) juce::Process::getCurrentModuleInstanceHandle(), | |||
| GetCurrentThreadId()) }; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeDarkModeChangeDetectorImpl) | |||
| }; | |||
| std::unique_ptr<Desktop::NativeDarkModeChangeDetectorImpl> Desktop::createNativeDarkModeChangeDetectorImpl() | |||
| { | |||
| return std::make_unique<NativeDarkModeChangeDetectorImpl>(); | |||
| } | |||
| bool Desktop::isDarkModeActive() const | |||
| { | |||
| return nativeDarkModeChangeDetectorImpl->isDarkModeEnabled(); | |||
| } | |||
| Desktop::DisplayOrientation Desktop::getCurrentOrientation() const | |||
| { | |||