diff --git a/docs/changes.txt b/docs/changes.txt index d0c0947739..b4802f94c9 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -50,6 +50,10 @@ Changes in behaviour not resulting in compilation errors you ever used it under MSW (it never did anything in the other ports), you can turn on the native WS_EX_TRANSPARENT extended style if really needed. +- wxSystemAppearance::IsDark() now returns whether this application uses dark + mode under MSW, use the new AreAppsDark() or IsSystemDark() to check if the + other applications or the system are using dark mode. + Changes in behaviour which may result in build errors ----------------------------------------------------- diff --git a/include/wx/settings.h b/include/wx/settings.h index a28947dabb..e6248e96bc 100644 --- a/include/wx/settings.h +++ b/include/wx/settings.h @@ -172,8 +172,15 @@ public: // Return the name if available or empty string otherwise. wxString GetName() const; - // Return true if the current system there is explicitly recognized as - // being a dark theme or if the default window background is dark. + // Return true if the applications on this system use dark theme by default. + bool AreAppsDark() const; + + // Return true if the system elements use dark theme: this can only differ + // from AreAppsDark() under MSW where it's possible to configure the system + // (taskbar etc) to use a different theme. + bool IsSystemDark() const; + + // Return true if this application itself uses a dark theme. bool IsDark() const; // Return true if the background is darker than foreground. This is used by diff --git a/interface/wx/settings.h b/interface/wx/settings.h index 33b4c15fa0..c46cca3f27 100644 --- a/interface/wx/settings.h +++ b/interface/wx/settings.h @@ -271,6 +271,22 @@ enum wxSystemScreenType class wxSystemAppearance { public: + /** + Return true if the applications on this system use dark theme by + default. + + This function returns @true if dark mode is enabled for the + applications system-wide, even if it's not enabled for this particular + application. + + Note that for non-MSW platforms this is currently the same as IsDark(), + but under MSW these two functions can return different values as dark + mode requires to opt-in into it specifically. + + @since 3.3.0 + */ + bool AreAppsDark() const; + /** Return the name if available or empty string otherwise. @@ -291,9 +307,23 @@ public: the other applications on the system, so under MSW, for example, it will return @false even if dark mode is used system-wide unless the application opted in using dark mode using wxApp::MSWEnableDarkMode(). + You can use IsSystemDark() or AreAppsDark() to check if the system is + using dark mode by default. */ bool IsDark() const; + /** + Return true if the system UI uses dark theme. + + This is the same as AreAppsDark() on the non-MSW platforms, but can be + different from the other function under MSW as it is possible to + configure default "Windows mode" and "app mode" to use different colour + schemes under Windows. + + @since 3.3.0 + */ + bool IsSystemDark() const; + /** Return true if the default window background is significantly darker than foreground. diff --git a/samples/drawing/drawing.cpp b/samples/drawing/drawing.cpp index df356c51b6..904cbda0aa 100644 --- a/samples/drawing/drawing.cpp +++ b/samples/drawing/drawing.cpp @@ -1676,21 +1676,31 @@ void MyCanvas::DrawSystemColours(wxDC& dc) wxCoord x(FromDIP(10)); wxRect r(textSize.GetWidth() + x, x, dc.FromDIP(100), lineHeight); - wxString title = "System colours"; + dc.DrawText("System colours", x, r.y); + r.y += 2*lineHeight; const wxSystemAppearance appearance = wxSystemSettings::GetAppearance(); const wxString appearanceName = appearance.GetName(); if ( !appearanceName.empty() ) - title += wxString::Format(" for \"%s\"", appearanceName); - if ( appearance.IsDark() ) - title += " (using dark system theme)"; - dc.DrawText(title, x, r.y); - r.y += 2*lineHeight; - dc.DrawText(wxString::Format("Window background is %s", - appearance.IsUsingDarkBackground() ? "dark" - : "light"), - x, r.y); - r.y += 3*lineHeight; + { + dc.DrawText(wxString::Format("System appearance: %s", appearanceName), + x, r.y); + r.y += lineHeight; + } + + auto const showDarkOrLight = [&](const char* what, bool dark) + { + dc.DrawText(wxString::Format("%s: %s", what, dark ? "dark" : "light"), + x, r.y); + r.y += 1.5*lineHeight; + }; + + showDarkOrLight("System", appearance.IsSystemDark()); + showDarkOrLight("App default", appearance.AreAppsDark()); + showDarkOrLight("Current app", appearance.IsDark()); + showDarkOrLight("Background", appearance.IsUsingDarkBackground()); + + r.y += lineHeight; dc.SetPen(*wxTRANSPARENT_PEN); diff --git a/src/common/settcmn.cpp b/src/common/settcmn.cpp index 0a80c6fac3..ad8165f0d4 100644 --- a/src/common/settcmn.cpp +++ b/src/common/settcmn.cpp @@ -68,6 +68,21 @@ void wxSystemSettings::SetScreenType( wxSystemScreenType screen ) // Trivial wxSystemAppearance implementation // ---------------------------------------------------------------------------- +// wxMSW has its own implementation of these functions. +#if !defined(__WXMSW__) + +bool wxSystemAppearance::AreAppsDark() const +{ + return IsDark(); +} + +bool wxSystemAppearance::IsSystemDark() const +{ + return IsDark(); +} + +#endif // !__WXMSW__ + #if !defined(__WXOSX__) wxString wxSystemAppearance::GetName() const diff --git a/src/msw/settings.cpp b/src/msw/settings.cpp index a2d36f4fe2..c3900332c8 100644 --- a/src/msw/settings.cpp +++ b/src/msw/settings.cpp @@ -33,6 +33,7 @@ #include "wx/msw/missing.h" // for SM_CXCURSOR, SM_CYCURSOR, SM_TABLETPC #include "wx/msw/private/darkmode.h" #include "wx/msw/private/metrics.h" +#include "wx/msw/registry.h" #include "wx/fontutil.h" #include "wx/fontenum.h" @@ -369,6 +370,32 @@ extern wxFont wxGetCCDefaultFont() #endif // wxUSE_LISTCTRL || wxUSE_TREECTRL +// There is no official API for determining whether dark mode is being used, +// but HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize +// contains AppsUseLightTheme and SystemUsesLightTheme values determining +// whether the applications/system use light or dark mode, so use them. +// +// Adapted from https://stackoverflow.com/a/51336913/15275 ("How to detect +// Windows 10 light/dark mode in Win32 application?"). +namespace +{ + +// Return false unless we are sure we're using the dark mode. +bool IsUsingDarkTheme(const wxString& forWhat) +{ + wxRegKey rk(wxRegKey::HKCU, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"); + if ( rk.Exists() && rk.HasValue(forWhat) ) + { + long value = -1; + if ( rk.QueryValue(forWhat, &value) ) + return value <= 0; + } + + return false; +} + +} // anonymous namespace + bool wxSystemAppearance::IsDark() const { // If the application opted in using dark mode, use the undocumented API @@ -381,3 +408,13 @@ bool wxSystemAppearance::IsDark() const // application itself uses dark colour schema or not. return IsUsingDarkBackground(); } + +bool wxSystemAppearance::AreAppsDark() const +{ + return IsUsingDarkTheme("AppsUseLightTheme"); +} + +bool wxSystemAppearance::IsSystemDark() const +{ + return IsUsingDarkTheme("SystemUsesLightTheme"); +}