include/svtools/colorcfg.hxx       |    4 ++
 svtools/source/config/colorcfg.cxx |   18 ++++++++++
 vcl/inc/osx/salframeview.h         |    4 ++
 vcl/osx/salframeview.mm            |   25 ++++++++++++++
 vcl/osx/salinst.cxx                |    5 ++
 vcl/osx/vclnsapp.mm                |    8 ++--
 vcl/source/app/settings.cxx        |   64 ++++++++++++++++++++++++++++++-------
 7 files changed, 113 insertions(+), 15 deletions(-)

New commits:
commit 86bc37a40be72539f98198bc6c1c92fed20a3fd7
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Tue Mar 4 08:47:00 2025 -0500
Commit:     Patrick Luby <guibomac...@gmail.com>
CommitDate: Tue Mar 11 22:14:34 2025 +0100

    Partial: tdf#156855 update native and LibreOffice dark mode states
    
    Updating the dark mode state of everything all at once does not
    solve all failures to update colors when the light/dark mode
    changes, but it eliminates enough failures that the UI is now
    generally readble without restarting LibreOffice.
    
    Also, update the application's color mode when the macOS light/dark
    mode changes while LibreOffice is running.
    
    Change-Id: Iffe3ca3789373135f06f7fbe22429cc59438d4f6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182785
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>
    Reviewed-by: Sahil <sahil.gautam.ext...@allotropia.de>
    Tested-by: Jenkins

diff --git a/include/svtools/colorcfg.hxx b/include/svtools/colorcfg.hxx
index c757eeb20abd..0275cfda26b8 100644
--- a/include/svtools/colorcfg.hxx
+++ b/include/svtools/colorcfg.hxx
@@ -25,6 +25,8 @@
 #include <unotools/options.hxx>
 #include <memory>
 
+#include <tools/link.hxx>
+#include <vcl/vclevent.hxx>
 
 namespace svtools{
 enum ColorConfigEntry : int
@@ -300,6 +302,8 @@ public:
 
     void                    LoadThemeColorsFromRegistry();
     void                    SetupTheme();
+
+    DECL_LINK(DataChangedHdl, VclSimpleEvent&, void);
 };
 
 class SVT_DLLPUBLIC EditableColorConfig
diff --git a/svtools/source/config/colorcfg.cxx 
b/svtools/source/config/colorcfg.cxx
index 154862518676..4934200d97ee 100644
--- a/svtools/source/config/colorcfg.cxx
+++ b/svtools/source/config/colorcfg.cxx
@@ -507,10 +507,14 @@ ColorConfig::ColorConfig()
     ++nColorRefCount_Impl;
     m_pImpl->AddListener(this);
     SetupTheme();
+
+    ::Application::AddEventListener( LINK(this, ColorConfig, DataChangedHdl) );
 }
 
 ColorConfig::~ColorConfig()
 {
+    ::Application::RemoveEventListener( LINK(this, ColorConfig, 
DataChangedHdl) );
+
     if (comphelper::IsFuzzing())
         return;
     std::unique_lock aGuard( ColorMutex_Impl() );
@@ -752,6 +756,20 @@ const OUString& ColorConfig::GetCurrentSchemeName()
     return m_pImpl->GetLoadedScheme();
 }
 
+IMPL_LINK( ColorConfig, DataChangedHdl, VclSimpleEvent&, rEvent, void )
+{
+    if (rEvent.GetId() == VclEventId::ApplicationDataChanged)
+    {
+        DataChangedEvent* pData = 
static_cast<DataChangedEvent*>(static_cast<VclWindowEvent&>(rEvent).GetData());
+        if (pData->GetType() == DataChangedEventType::SETTINGS &&
+            pData->GetFlags() & AllSettingsFlags::STYLE)
+        {
+            ThemeColors::SetThemeLoaded(false);
+            SetupTheme();
+        }
+    }
+}
+
 EditableColorConfig::EditableColorConfig() :
     m_pImpl(new ColorConfig_Impl),
     m_bModified(false)
diff --git a/vcl/inc/osx/salframeview.h b/vcl/inc/osx/salframeview.h
index 287ccaedbc06..8b141f7aafe5 100644
--- a/vcl/inc/osx/salframeview.h
+++ b/vcl/inc/osx/salframeview.h
@@ -112,6 +112,8 @@ enum class SalEvent;
     NSAttributedString* mpLastMarkedText;
     BOOL            mbTextInputWantsNonRepeatKeyDown;
     NSTrackingArea* mpLastTrackingArea;
+
+    BOOL            mbInViewDidChangeEffectiveAppearance;
 }
 +(void)unsetMouseFrame: (AquaSalFrame*)pFrame;
 -(id)initWithSalFrame: (AquaSalFrame*)pFrame;
@@ -270,6 +272,8 @@ enum class SalEvent;
 -(NSArray *)accessibilityChildren;
 -(NSArray <id<NSAccessibilityElement>> 
*)accessibilityChildrenInNavigationOrder;
 
+-(void)viewDidChangeEffectiveAppearance;
+
 @end
 
 @interface SalFrameViewA11yWrapper : AquaA11yWrapper
diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
index a5cb9270600b..7590acf31ebb 100644
--- a/vcl/osx/salframeview.mm
+++ b/vcl/osx/salframeview.mm
@@ -936,6 +936,8 @@ static void updateMenuBarVisibility( const AquaSalFrame 
*pFrame )
         mpLastMarkedText = nil;
         mbTextInputWantsNonRepeatKeyDown = NO;
         mpLastTrackingArea = nil;
+
+        mbInViewDidChangeEffectiveAppearance = NO;
     }
 
     return self;
@@ -2836,6 +2838,29 @@ static void updateMenuBarVisibility( const AquaSalFrame 
*pFrame )
     return [self accessibilityChildren];
 }
 
+-(void)viewDidChangeEffectiveAppearance
+{
+    if (mbInViewDidChangeEffectiveAppearance)
+        return;
+
+    mbInViewDidChangeEffectiveAppearance = YES;
+
+    // Related: tdf#156855 force the current theme to reload its colors
+    // This call is called when the macOS light/dark mode changes while
+    // LibreOffice is running. Send a SalEvent::SettingsChanged event
+    // but do it in an idle timer. Otherwise, an infinite recursion
+    // can occur.
+    NSWindow *pWindow = [self window];
+    if (pWindow && ([pWindow isVisible] || [pWindow isMiniaturized]))
+    {
+        SolarMutexGuard aGuard;
+
+        GetSalData()->mpInstance->delayedSettingsChanged(true);
+    }
+
+    mbInViewDidChangeEffectiveAppearance = NO;
+}
+
 @end
 
 @implementation SalFrameViewA11yWrapper
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index 3983d32241a3..6d6cb6d49989 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -107,6 +107,11 @@ public:
 
     virtual void Invoke() override
     {
+        // Related: tdf#156855 force reload of both native and theme colors
+        int nMode = MiscSettings::GetAppColorMode();
+        if (!nMode)
+            MiscSettings::SetAppColorMode(nMode);
+
         AquaSalInstance *pInst = GetSalData()->mpInstance;
         SalFrame *pAnyFrame = pInst->anyFrame();
         if( pAnyFrame )
diff --git a/vcl/osx/vclnsapp.mm b/vcl/osx/vclnsapp.mm
index 19c90cbb323e..731a93e4805f 100644
--- a/vcl/osx/vclnsapp.mm
+++ b/vcl/osx/vclnsapp.mm
@@ -353,10 +353,10 @@
     (void)pNotification;
     SolarMutexGuard aGuard;
 
-    AquaSalInstance *pInst = GetSalData()->mpInstance;
-    SalFrame *pAnyFrame = pInst->anyFrame();
-    if(  pAnyFrame )
-        pAnyFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
+    // Related: tdf#156855 delay SalEvent::SettingsChanged event
+    // -[SalFrameView viewDidChangeEffectiveAppearance] needs to delay
+    // so be safe and do the same here.
+    GetSalData()->mpInstance->delayedSettingsChanged( true );
 }
 
 -(void)screenParametersChanged: (NSNotification*) pNotification
diff --git a/vcl/source/app/settings.cxx b/vcl/source/app/settings.cxx
index 6c69bda88a37..d1d48c0f3efc 100644
--- a/vcl/source/app/settings.cxx
+++ b/vcl/source/app/settings.cxx
@@ -2314,21 +2314,14 @@ bool MiscSettings::GetEnableLocalizedDecimalSep() const
 
 int MiscSettings::GetDarkMode()
 {
-    return officecfg::Office::Common::Appearance::ApplicationAppearance::get();
+    // MiscSettings::GetAppColorMode() replaces MiscSettings::GetDarkMode()
+    return MiscSettings::GetAppColorMode();
 }
 
 void MiscSettings::SetDarkMode(int nMode)
 {
-    std::shared_ptr<comphelper::ConfigurationChanges> 
batch(comphelper::ConfigurationChanges::create());
-    officecfg::Office::Common::Appearance::ApplicationAppearance::set(nMode, 
batch);
-    batch->commit();
-
-    vcl::Window *pWin = Application::GetFirstTopLevelWindow();
-    while (pWin)
-    {
-        pWin->ImplGetFrame()->UpdateDarkMode();
-        pWin = Application::GetNextTopLevelWindow(pWin);
-    }
+    // MiscSettings::SetAppColorMode() replaces MiscSettings::SetDarkMode()
+    MiscSettings::SetAppColorMode(nMode);
 }
 
 bool MiscSettings::GetUseDarkMode()
@@ -2348,9 +2341,58 @@ int MiscSettings::GetAppColorMode()
 
 void MiscSettings::SetAppColorMode(int nMode)
 {
+    // Partial: tdf#156855 update native and LibreOffice dark mode states
+    // Updating the dark mode state of everything all at once does not
+    // solve all failures to update colors when the light/dark mode
+    // changes, but it eliminates enough failures that the UI is now
+    // generally readble without restarting LibreOffice.
+    // Important: all of the following steps must be done. Otherwise,
+    // changing the macOS light/dark mode preference while LibreOffice
+    // is running will cause the color mode state change to fail.
+
+    // 1. Save the new mode.
     std::shared_ptr<comphelper::ConfigurationChanges> 
batch(comphelper::ConfigurationChanges::create());
     officecfg::Office::Common::Appearance::ApplicationAppearance::set(nMode, 
batch);
     batch->commit();
+
+    // 2. Force the native windows to update their dark mode state so
+    //    that we can fetch the correct native colors.
+    vcl::Window *pWin = Application::GetFirstTopLevelWindow();
+    while (pWin)
+    {
+        pWin->ImplGetFrame()->UpdateDarkMode();
+        pWin = Application::GetNextTopLevelWindow(pWin);
+    }
+
+#ifdef MACOSX
+    // 3. Reset the native colors in AllSettings. Note: the current theme
+    //    is disabled during this step to stop SalFrame::UpdateSettings()
+    //    from adding the current theme's colors which are still set to
+    //    the previous light/dark mode's colors.
+    if (ThemeColors::IsThemeLoaded())
+        ThemeColors::SetThemeLoaded(false);
+    AllSettings aSettings = Application::GetSettings();
+    Application::MergeSystemSettings(aSettings);
+    Application *pApp = GetpApp();
+    if (pApp)
+        pApp->OverrideSystemSettings(aSettings);
+    Application::SetSettings(aSettings);
+
+    // 4. Force the current theme's ColorConfig to reload itself
+    //    with the correct light/dark mode colors. It will also
+    //    merge the native colors updated in the previous step.
+    DataChangedEvent aDCEvt(DataChangedEventType::SETTINGS);
+    Application::ImplCallEventListenersApplicationDataChanged(&aDCEvt);
+    Application::NotifyAllWindows(aDCEvt);
+#else
+    // Note for Windows and Linux: the above macOS code doesn't appear
+    // to work as expected on Windows and Linux. One thing that might
+    // make the above code work on those platforms is by delaying the
+    // firing of the SalEvent::SettingsChanged event. macos uses the
+    // AquaSalInstance::delayedSettingsChanged() method to delay firing
+    // and also invalidate all the open windows so that may need to be
+    // moved to the SalInstance base class.
+#endif
 }
 
 bool MiscSettings::GetUseReducedAnimation()

Reply via email to