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()