From 9a9c845289c4186bea9246068c0373e0b845399a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 16 Apr 2019 01:50:41 +0200 Subject: [PATCH 1/2] Add wxSystemAppearance to check for dark mode under macOS Provide a way to retrieve the name of the current system appearance (mostly for diagnostic purposes) and check if it uses predominantly dark colours. Currently this class has a non-trivial (but still very simple) implementation under macOS only and simply checks whether the default text colour is brighter than the default background colour under the other platforms, but other platform-specific implementations could be added later. Also update the drawing sample "system colours" page to show the system appearance as well. --- include/wx/settings.h | 36 ++++++++++++++++++++++++ interface/wx/settings.h | 56 +++++++++++++++++++++++++++++++++++++ samples/drawing/drawing.cpp | 16 +++++++++++ src/common/settcmn.cpp | 33 ++++++++++++++++++++++ src/osx/cocoa/settings.mm | 33 ++++++++++++++++++++++ 5 files changed, 174 insertions(+) diff --git a/include/wx/settings.h b/include/wx/settings.h index cf5602ccd8..f66acfe179 100644 --- a/include/wx/settings.h +++ b/include/wx/settings.h @@ -162,6 +162,39 @@ enum wxSystemScreenType wxSYS_SCREEN_DESKTOP // >= 800x600 }; +// ---------------------------------------------------------------------------- +// wxSystemAppearance: describes the global appearance used for the UI +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_CORE wxSystemAppearance +{ +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. + bool IsDark() const; + + // Return true if the background is darker than foreground. This is used by + // IsDark() if there is no platform-specific way to determine whether a + // dark mode is being used. + bool IsUsingDarkBackground() const; + +private: + friend class wxSystemSettingsNative; + + // Ctor is private, even though it's trivial, because objects of this type + // are only supposed to be created by wxSystemSettingsNative. + wxSystemAppearance() { } + + // Currently this class doesn't have any internal state because the only + // available implementation doesn't need it. If we do need it later, we + // could add some "wxSystemAppearanceImpl* const m_impl" here, which we'd + // forward our public functions to (we'd also need to add the copy ctor and + // dtor to clone/free it). +}; + // ---------------------------------------------------------------------------- // wxSystemSettingsNative: defines the API for wxSystemSettings class // ---------------------------------------------------------------------------- @@ -185,6 +218,9 @@ public: // get a system-dependent metric static int GetMetric(wxSystemMetric index, wxWindow * win = NULL); + // get the object describing the current system appearance + static wxSystemAppearance GetAppearance(); + // return true if the port has certain feature static bool HasFeature(wxSystemFeature index); }; diff --git a/interface/wx/settings.h b/interface/wx/settings.h index 297d777372..89c3d8049a 100644 --- a/interface/wx/settings.h +++ b/interface/wx/settings.h @@ -254,6 +254,55 @@ enum wxSystemScreenType }; +/** + Provides information about the current system appearance. + + An object of this class can be retrieved using + wxSystemSettings::GetAppearance() and can then be queried for some aspects + of the current system appearance, notably whether the system is using a + dark theme, i.e. a theme with predominantly dark background. + + This is useful for custom controls that don't use the standard system + colours, as they need to adjust the colours used for drawing them to fit in + the system look. + + @since 3.1.3 + */ +class wxSystemAppearance +{ +public: + /** + Return the name if available or empty string otherwise. + + This is currently only implemented for macOS 10.9 or later and returns + a not necessarily user-readable string such as "NSAppearanceNameAqua" + there and an empty string under all the other platforms. + */ + 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. + + This method should be used to check whether custom colours more + appropriate for the default (light) or dark appearance should be used. + */ + bool IsDark() const; + + /** + Return true if the default window background is significantly darker + than foreground. + + This is used by IsDark() if there is no platform-specific way to + determine whether a dark mode is being used and is generally not very + useful to call directly. + + @see wxColour::GetLuminance() + */ + bool IsUsingDarkBackground() const; +}; + + /** @class wxSystemSettings @@ -327,6 +376,13 @@ public: */ static wxSystemScreenType GetScreenType(); + /** + Returns the object describing the current system appearance. + + @since 3.1.3 + */ + static wxSystemAppearance GetAppearance(); + /** Returns @true if the port has certain feature. See the ::wxSystemFeature enum values. diff --git a/samples/drawing/drawing.cpp b/samples/drawing/drawing.cpp index 2b266b9231..49a2778356 100644 --- a/samples/drawing/drawing.cpp +++ b/samples/drawing/drawing.cpp @@ -1623,6 +1623,22 @@ void MyCanvas::DrawSystemColours(wxDC& dc) int lineHeight = textSize.GetHeight(); wxRect r(textSize.GetWidth() + 10, 10, 100, lineHeight); + wxString title = "System colours"; + + 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, 10, r.y); + r.y += 2*lineHeight; + dc.DrawText(wxString::Format("Window background is %s", + appearance.IsUsingDarkBackground() ? "dark" + : "light"), + 10, r.y); + r.y += 3*lineHeight; + dc.SetPen(*wxTRANSPARENT_PEN); static const struct { diff --git a/src/common/settcmn.cpp b/src/common/settcmn.cpp index c9c011d5f7..c02d3e5f11 100644 --- a/src/common/settcmn.cpp +++ b/src/common/settcmn.cpp @@ -66,3 +66,36 @@ void wxSystemSettings::SetScreenType( wxSystemScreenType screen ) { ms_screen = screen; } + +// ---------------------------------------------------------------------------- +// Trivial wxSystemAppearance implementation +// ---------------------------------------------------------------------------- + +#if !defined(__WXOSX__) + +wxString wxSystemAppearance::GetName() const +{ + return wxString(); +} + +bool wxSystemAppearance::IsDark() const +{ + return IsUsingDarkBackground(); +} + +#endif // !__WXOSX__ + +bool wxSystemAppearance::IsUsingDarkBackground() const +{ + const wxColour bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + const wxColour fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + + // The threshold here is rather arbitrary, but it seems that using just + // inequality would be wrong as it could result in false positivies. + return fg.GetLuminance() - bg.GetLuminance() > 0.2; +} + +wxSystemAppearance wxSystemSettingsNative::GetAppearance() +{ + return wxSystemAppearance(); +} diff --git a/src/osx/cocoa/settings.mm b/src/osx/cocoa/settings.mm index 7f410583ae..fc39244e18 100644 --- a/src/osx/cocoa/settings.mm +++ b/src/osx/cocoa/settings.mm @@ -42,6 +42,39 @@ static int wxOSXGetUserDefault(NSString* key, int defaultValue) return [setting intValue]; } +// ---------------------------------------------------------------------------- +// wxSystemAppearance +// ---------------------------------------------------------------------------- + +wxString wxSystemAppearance::GetName() const +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 + if ( WX_IS_MACOS_AVAILABLE(10, 9) ) + { + return wxStringWithNSString([[NSApp effectiveAppearance] name]); + } +#endif + + return wxString(); +} + +bool wxSystemAppearance::IsDark() const +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 + if ( WX_IS_MACOS_AVAILABLE(10, 14) ) + { + const NSAppearanceName + appearanceName = [[NSApp effectiveAppearance] + bestMatchFromAppearancesWithNames: + @[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]]; + + return [appearanceName isEqualToString:NSAppearanceNameDarkAqua]; + } +#endif + + // Fall back on the generic method when not running under 10.14. + return IsUsingDarkBackground(); +} // ---------------------------------------------------------------------------- // wxSystemSettingsNative From 16af76e542d781eaa59b0305714e4f94095d35c9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 18 Apr 2019 19:15:23 +0200 Subject: [PATCH 2/2] Use wxSystemAppearance::IsDark() to check for dark mode in wxOSX This should be more reliable than simply checking the red component of the default window background colour. --- src/osx/carbon/renderer.cpp | 16 ++++------------ src/osx/carbon/statbrma.cpp | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/osx/carbon/renderer.cpp b/src/osx/carbon/renderer.cpp index 360931a421..1bfe9ac733 100644 --- a/src/osx/carbon/renderer.cpp +++ b/src/osx/carbon/renderer.cpp @@ -173,11 +173,9 @@ int wxRendererMac::DrawHeaderButton( wxWindow *win, wxHeaderSortIconType sortArrow, wxHeaderButtonParams* params ) { - if ( WX_IS_MACOS_AVAILABLE(10, 14) ) - { - if ( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW).Red() < 128 ) - return wxRendererNative::GetGeneric().DrawHeaderButton(win, dc, rect, flags, sortArrow, params); - } + if ( wxSystemSettings::GetAppearance().IsDark() ) + return wxRendererNative::GetGeneric().DrawHeaderButton(win, dc, rect, flags, sortArrow, params); + const wxCoord x = rect.x; const wxCoord y = rect.y; const wxCoord w = rect.width; @@ -391,13 +389,7 @@ void wxRendererMac::DrawSplitterSash( wxWindow *win, if ( win->HasFlag(wxSP_3DSASH) ) { - bool doDraw; - if ( WX_IS_MACOS_AVAILABLE(10, 14) ) - doDraw = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW).Red() > 128; - else - doDraw = true; - - if ( doDraw ) + if ( !wxSystemSettings::GetAppearance().IsDark() ) { HIThemeSplitterDrawInfo drawInfo; drawInfo.version = 0; diff --git a/src/osx/carbon/statbrma.cpp b/src/osx/carbon/statbrma.cpp index 3d63fa31a5..ba19399a55 100644 --- a/src/osx/carbon/statbrma.cpp +++ b/src/osx/carbon/statbrma.cpp @@ -88,7 +88,7 @@ void wxStatusBarMac::InitColours() m_textActive = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT); m_textInactive = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); - if ( bg.Red() < 128 ) + if ( wxSystemSettings::GetAppearance().IsDark() ) { // dark mode appearance m_textActive = wxColour(0xB0, 0xB0, 0xB0);