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/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); 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