From f2685a36e1604c3239dd522b54a256077ef24947 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 26 Jul 2026 09:58:36 -0400 Subject: [PATCH 1/5] Fix white pressed-button text becoming illegible in dark ribbon themes Also, refactors duplicate ternary code. --- include/wx/ribbon/art.h | 4 ++++ include/wx/ribbon/art_internal.h | 4 ++++ src/ribbon/art_aui.cpp | 6 +++--- src/ribbon/art_internal.cpp | 5 +++++ src/ribbon/art_msw.cpp | 25 ++++++++++++++++++------- src/ribbon/art_msw_flat.cpp | 4 +--- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/include/wx/ribbon/art.h b/include/wx/ribbon/art.h index 1077a560eb..46de0b7386 100644 --- a/include/wx/ribbon/art.h +++ b/include/wx/ribbon/art.h @@ -644,6 +644,9 @@ protected: void DrawDropdownArrow(wxDC& dc, int x, int y, const wxColour& colour); void DrawGalleryBackgroundCommon(wxDC& dc, wxRibbonGallery* wnd, const wxRect& rect); + // Picks the button bar label colour appropriate for the given + // wxRIBBON_BUTTONBAR_BUTTON_* state flags. + wxColour GetButtonBarLabelColour(long state) const; virtual void DrawGalleryButton(wxDC& dc, wxRect rect, wxRibbonGalleryButtonState state, wxBitmapBundle* bundles, wxWindow* wnd); void DrawButtonBarButtonForeground( @@ -674,6 +677,7 @@ protected: wxColour m_button_bar_label_colour; wxColour m_button_bar_label_disabled_colour; + wxColour m_button_bar_active_label_colour; wxColour m_tab_label_colour; wxColour m_tab_active_label_colour; wxColour m_tab_hover_label_colour; diff --git a/include/wx/ribbon/art_internal.h b/include/wx/ribbon/art_internal.h index 63f8614720..07b31a3169 100644 --- a/include/wx/ribbon/art_internal.h +++ b/include/wx/ribbon/art_internal.h @@ -27,6 +27,10 @@ WXDLLIMPEXP_RIBBON bool wxRibbonCanLabelBreakAtPosition( const wxString& label, size_t pos); +// Returns white or black, whichever contrasts better against background. +WXDLLIMPEXP_RIBBON wxColour wxRibbonGetContrastingLabelColour( + const wxColour& background); + WXDLLIMPEXP_RIBBON void wxRibbonDrawParallelGradientLines( wxDC& dc, int nlines, diff --git a/src/ribbon/art_aui.cpp b/src/ribbon/art_aui.cpp index 33121f7bb2..1df252f18a 100644 --- a/src/ribbon/art_aui.cpp +++ b/src/ribbon/art_aui.cpp @@ -286,6 +286,8 @@ void wxRibbonAUIArtProvider::SetColourScheme( m_button_bar_hover_border_pen = secondary_hsl.ToRGB(); m_button_bar_hover_background_brush = LikeSecondary(1.7f); m_button_bar_active_background_brush = LikeSecondary(1.4f); + m_button_bar_active_label_colour = wxRibbonGetContrastingLabelColour( + m_button_bar_active_background_brush.GetColour()); m_button_bar_label_colour = m_tab_label_colour; #ifdef __WXOSX__ m_button_bar_label_disabled_colour = wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTIONTEXT); @@ -1145,9 +1147,7 @@ void wxRibbonAUIArtProvider::DrawButtonBarButton( } dc.SetFont(m_button_bar_label_font); - dc.SetTextForeground(state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED - ? m_button_bar_label_disabled_colour - : m_button_bar_label_colour); + dc.SetTextForeground(GetButtonBarLabelColour(state)); DrawButtonBarButtonForeground(dc, rect, kind, state, label, bitmap_large, bitmap_small); } diff --git a/src/ribbon/art_internal.cpp b/src/ribbon/art_internal.cpp index 9c538f24ce..f4e83cc69f 100644 --- a/src/ribbon/art_internal.cpp +++ b/src/ribbon/art_internal.cpp @@ -59,6 +59,11 @@ bool wxRibbonCanLabelBreakAtPosition(const wxString& label, size_t pos) return label[pos] == ' '; } +wxColour wxRibbonGetContrastingLabelColour(const wxColour& background) +{ + return background.GetLuminance() < 0.5 ? *wxWHITE : *wxBLACK; +} + void wxRibbonDrawParallelGradientLines(wxDC& dc, int nlines, const wxPoint* line_origins, diff --git a/src/ribbon/art_msw.cpp b/src/ribbon/art_msw.cpp index d68a6583c7..4eb010058f 100644 --- a/src/ribbon/art_msw.cpp +++ b/src/ribbon/art_msw.cpp @@ -293,6 +293,12 @@ void wxRibbonMSWArtProvider::SetColourScheme( m_button_bar_active_background_colour = LikeSecondary(-9.9f, 0.14f, -0.14f); m_button_bar_active_background_gradient_colour = LikeSecondary(-8.7f, 0.17f, -0.03f); + // The active/pressed background can end up much lighter (or darker) + // than the normal button label colour was chosen for, so pick a label + // colour that stays legible against it specifically. + m_button_bar_active_label_colour = + wxRibbonGetContrastingLabelColour(m_button_bar_active_background_colour); + m_toolbar_border_pen = LikePrimary(1.4f, -0.21f, -0.16f); SetColour(wxRIBBON_ART_TOOLBAR_FACE_COLOUR, LikePrimary(1.4f, -0.17f, -0.22f)); m_tool_background_top_colour = LikePrimary(-1.9f, -0.07f, 0.06f); @@ -366,6 +372,7 @@ void wxRibbonMSWArtProvider::CloneTo(wxRibbonMSWArtProvider* copy) const copy->m_button_bar_label_colour = m_button_bar_label_colour; copy->m_button_bar_label_disabled_colour = m_button_bar_label_disabled_colour; + copy->m_button_bar_active_label_colour = m_button_bar_active_label_colour; copy->m_tab_label_colour = m_tab_label_colour; copy->m_tab_active_label_colour = m_tab_active_label_colour; copy->m_tab_hover_label_colour = m_tab_hover_label_colour; @@ -2305,6 +2312,15 @@ void wxRibbonMSWArtProvider::DrawPartialPageBackground( dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height); } +wxColour wxRibbonMSWArtProvider::GetButtonBarLabelColour(long state) const +{ + if(state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED) + return m_button_bar_label_disabled_colour; + if(state & wxRIBBON_BUTTONBAR_BUTTON_ACTIVE_MASK) + return m_button_bar_active_label_colour; + return m_button_bar_label_colour; +} + void wxRibbonMSWArtProvider::DrawButtonBarButton( wxDC& dc, wxWindow* WXUNUSED(wnd), @@ -2425,9 +2441,7 @@ void wxRibbonMSWArtProvider::DrawButtonBarButton( } dc.SetFont(m_button_bar_label_font); - dc.SetTextForeground(state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED - ? m_button_bar_label_disabled_colour - : m_button_bar_label_colour); + dc.SetTextForeground(GetButtonBarLabelColour(state)); DrawButtonBarButtonForeground(dc, rect, kind, state, label, bitmap_large, bitmap_small); } @@ -2441,10 +2455,7 @@ void wxRibbonMSWArtProvider::DrawButtonBarButtonForeground( const wxBitmap& bitmap_large, const wxBitmap& bitmap_small) { - const wxColour - arrowColour(state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED - ? m_button_bar_label_disabled_colour - : m_button_bar_label_colour); + const wxColour arrowColour(GetButtonBarLabelColour(state)); switch(state & wxRIBBON_BUTTONBAR_BUTTON_SIZE_MASK) { diff --git a/src/ribbon/art_msw_flat.cpp b/src/ribbon/art_msw_flat.cpp index 9c72277b04..2f9819bd90 100644 --- a/src/ribbon/art_msw_flat.cpp +++ b/src/ribbon/art_msw_flat.cpp @@ -782,9 +782,7 @@ void wxRibbonMSWFlatArtProvider::DrawButtonBarButton( } dc.SetFont(m_button_bar_label_font); - dc.SetTextForeground(state & wxRIBBON_BUTTONBAR_BUTTON_DISABLED - ? m_button_bar_label_disabled_colour - : m_button_bar_label_colour); + dc.SetTextForeground(GetButtonBarLabelColour(state)); DrawButtonBarButtonForeground(dc, rect, kind, state, label, bitmap_large, bitmap_small); } From d20d50bfbed22329a6116b3bfe73bef19d3100ee Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:14:25 -0400 Subject: [PATCH 2/5] Use proper contrast calculation to pick pressed-button label colour Uses calculations from AUI. --- include/wx/ribbon/art_internal.h | 3 ++- src/ribbon/art_aui.cpp | 4 ++-- src/ribbon/art_internal.cpp | 32 ++++++++++++++++++++++++++++++-- src/ribbon/art_msw.cpp | 4 ++-- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/include/wx/ribbon/art_internal.h b/include/wx/ribbon/art_internal.h index 07b31a3169..b135315add 100644 --- a/include/wx/ribbon/art_internal.h +++ b/include/wx/ribbon/art_internal.h @@ -27,8 +27,9 @@ WXDLLIMPEXP_RIBBON bool wxRibbonCanLabelBreakAtPosition( const wxString& label, size_t pos); -// Returns white or black, whichever contrasts better against background. +// Returns foreground as-is if it's readable on background, else black or white. WXDLLIMPEXP_RIBBON wxColour wxRibbonGetContrastingLabelColour( + const wxColour& foreground, const wxColour& background); WXDLLIMPEXP_RIBBON void wxRibbonDrawParallelGradientLines( diff --git a/src/ribbon/art_aui.cpp b/src/ribbon/art_aui.cpp index 1df252f18a..569338640a 100644 --- a/src/ribbon/art_aui.cpp +++ b/src/ribbon/art_aui.cpp @@ -286,9 +286,9 @@ void wxRibbonAUIArtProvider::SetColourScheme( m_button_bar_hover_border_pen = secondary_hsl.ToRGB(); m_button_bar_hover_background_brush = LikeSecondary(1.7f); m_button_bar_active_background_brush = LikeSecondary(1.4f); - m_button_bar_active_label_colour = wxRibbonGetContrastingLabelColour( - m_button_bar_active_background_brush.GetColour()); m_button_bar_label_colour = m_tab_label_colour; + m_button_bar_active_label_colour = wxRibbonGetContrastingLabelColour( + m_button_bar_label_colour, m_button_bar_active_background_brush.GetColour()); #ifdef __WXOSX__ m_button_bar_label_disabled_colour = wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTIONTEXT); #else diff --git a/src/ribbon/art_internal.cpp b/src/ribbon/art_internal.cpp index f4e83cc69f..92e113b970 100644 --- a/src/ribbon/art_internal.cpp +++ b/src/ribbon/art_internal.cpp @@ -9,6 +9,7 @@ #include "wx/wxprec.h" +#include #if wxUSE_RIBBON @@ -59,9 +60,36 @@ bool wxRibbonCanLabelBreakAtPosition(const wxString& label, size_t pos) return label[pos] == ' '; } -wxColour wxRibbonGetContrastingLabelColour(const wxColour& background) +// Same contrast formula as wxAuiGetColourContrast() +static float wxRibbonGetSRGBComponent(float c) { - return background.GetLuminance() < 0.5 ? *wxWHITE : *wxBLACK; + return c <= 0.03928f ? c / 12.92f : std::pow((c + 0.055f) / 1.055f, 2.4f); +} + +static float wxRibbonGetRelativeLuminance(const wxColour& c) +{ + return 0.2126f * wxRibbonGetSRGBComponent(c.Red() / 255.0f) + + 0.7152f * wxRibbonGetSRGBComponent(c.Green() / 255.0f) + + 0.0722f * wxRibbonGetSRGBComponent(c.Blue() / 255.0f); +} + +static float wxRibbonGetColourContrast(const wxColour& c1, const wxColour& c2) +{ + const float l1 = wxRibbonGetRelativeLuminance(c1); + const float l2 = wxRibbonGetRelativeLuminance(c2); + return l1 > l2 ? (l1 + 0.05f) / (l2 + 0.05f) + : (l2 + 0.05f) / (l1 + 0.05f); +} + +wxColour wxRibbonGetContrastingLabelColour(const wxColour& foreground, + const wxColour& background) +{ + if (wxRibbonGetColourContrast(foreground, background) >= 4.5f) + return foreground; + + return wxRibbonGetColourContrast(*wxWHITE, background) + > wxRibbonGetColourContrast(*wxBLACK, background) + ? *wxWHITE : *wxBLACK; } void wxRibbonDrawParallelGradientLines(wxDC& dc, diff --git a/src/ribbon/art_msw.cpp b/src/ribbon/art_msw.cpp index 4eb010058f..0df7be3ca9 100644 --- a/src/ribbon/art_msw.cpp +++ b/src/ribbon/art_msw.cpp @@ -296,8 +296,8 @@ void wxRibbonMSWArtProvider::SetColourScheme( // The active/pressed background can end up much lighter (or darker) // than the normal button label colour was chosen for, so pick a label // colour that stays legible against it specifically. - m_button_bar_active_label_colour = - wxRibbonGetContrastingLabelColour(m_button_bar_active_background_colour); + m_button_bar_active_label_colour = wxRibbonGetContrastingLabelColour( + m_button_bar_label_colour, m_button_bar_active_background_colour); m_toolbar_border_pen = LikePrimary(1.4f, -0.21f, -0.16f); SetColour(wxRIBBON_ART_TOOLBAR_FACE_COLOUR, LikePrimary(1.4f, -0.17f, -0.22f)); From 08ae56c96d8822a86f4b48b15d13fef39376a616 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 29 Jul 2026 19:31:55 +0200 Subject: [PATCH 3/5] Extract colour-related functions to a reusable header Don't duplicate the same code using different names in wxAUI and wxRibbon, put it in a header which can be included from both. No real changes, this is a pure refactoring. --- include/wx/private/colour.h | 69 ++++++++++++++++++++++++++++++++ include/wx/ribbon/art_internal.h | 5 --- src/aui/dockart.cpp | 24 +---------- src/aui/tabart.cpp | 22 ++-------- src/ribbon/art_aui.cpp | 4 +- src/ribbon/art_internal.cpp | 34 ---------------- src/ribbon/art_msw.cpp | 4 +- 7 files changed, 79 insertions(+), 83 deletions(-) create mode 100644 include/wx/private/colour.h diff --git a/include/wx/private/colour.h b/include/wx/private/colour.h new file mode 100644 index 0000000000..dc63c934a6 --- /dev/null +++ b/include/wx/private/colour.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/private/colour.h +// Purpose: Private colour-related helper functions. +// Author: Vadim Zeitlin +// Created: 2026-07-29 (extracted src/aui/tabart.cpp) +// Copyright: (c) 2026 wxWidgets team +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_COLOUR_H_ +#define _WX_PRIVATE_COLOUR_H_ + +#include "wx/colour.h" + +#include + +// Helpers used only in this file itself. +namespace wxPrivate +{ + +inline float wxGetSRGB(float r) { + return r <= 0.03928f ? r / 12.92f : std::pow((r + 0.055f) / 1.055f, 2.4f); +} + +inline float wxGetRelativeLuminance(const wxColour& c) +{ + // based on https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + return + 0.2126f * wxGetSRGB(c.Red() / 255.0f) + + 0.7152f * wxGetSRGB(c.Green() / 255.0f) + + 0.0722f * wxGetSRGB(c.Blue() / 255.0f); +} + +inline float wxComputeContrast(const wxColour& c1, const wxColour& c2) +{ + // based on https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html + float L1 = wxGetRelativeLuminance(c1); + float L2 = wxGetRelativeLuminance(c2); + return L1 > L2 ? (L1 + 0.05f) / (L2 + 0.05f) : (L2 + 0.05f) / (L1 + 0.05f); +} + +} // anonymous namespace + +// Return the colour if it has sufficient contrast ratio (4.5 recommended) +// with the background or return either white or black if it doesn't. +// (based on https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html) +inline wxColour +wxGetContrastingFgColour(const wxColour& fg, const wxColour& bg) +{ + using namespace wxPrivate; + + // No need to change the colour if it has sufficient contrast. + if ( wxComputeContrast(*fg, bg) >= 4.5f ) + return fg; + + // Otherwise pick the colour that provides better contrast. + return wxComputeContrast(*wxWHITE, bg) > wxComputeContrast(*wxBLACK, bg) + ? *wxWHITE + : *wxBLACK; +} + +// Version of the above modifying the foregorund colour in place. +inline void +wxEnsureSufficientContrast(wxColour* fg, const wxColour& bg) +{ + *fg = wxGetContrastingFgColour(*fg, bg); +} + +#endif // _WX_PRIVATE_COLOUR_H_ diff --git a/include/wx/ribbon/art_internal.h b/include/wx/ribbon/art_internal.h index b135315add..63f8614720 100644 --- a/include/wx/ribbon/art_internal.h +++ b/include/wx/ribbon/art_internal.h @@ -27,11 +27,6 @@ WXDLLIMPEXP_RIBBON bool wxRibbonCanLabelBreakAtPosition( const wxString& label, size_t pos); -// Returns foreground as-is if it's readable on background, else black or white. -WXDLLIMPEXP_RIBBON wxColour wxRibbonGetContrastingLabelColour( - const wxColour& foreground, - const wxColour& background); - WXDLLIMPEXP_RIBBON void wxRibbonDrawParallelGradientLines( wxDC& dc, int nlines, diff --git a/src/aui/dockart.cpp b/src/aui/dockart.cpp index 96b76f5b6a..b404710104 100644 --- a/src/aui/dockart.cpp +++ b/src/aui/dockart.cpp @@ -48,8 +48,7 @@ #endif #include "wx/private/aui.h" - -#include +#include "wx/private/colour.h" wxColor wxAuiLightContrastColour(const wxColour& c) { @@ -63,27 +62,6 @@ wxColor wxAuiLightContrastColour(const wxColour& c) return c.ChangeLightness(amount); } -inline float wxAuiGetSRGB(float r) { - return r <= 0.03928f ? r / 12.92f : std::pow((r + 0.055f) / 1.055f, 2.4f); -} - -float wxAuiGetRelativeLuminance(const wxColour& c) -{ - // based on https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef - return - 0.2126f * wxAuiGetSRGB(c.Red() / 255.0f) + - 0.7152f * wxAuiGetSRGB(c.Green() / 255.0f) + - 0.0722f * wxAuiGetSRGB(c.Blue() / 255.0f); -} - -float wxAuiGetColourContrast(const wxColour& c1, const wxColour& c2) -{ - // based on https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html - float L1 = wxAuiGetRelativeLuminance(c1); - float L2 = wxAuiGetRelativeLuminance(c2); - return L1 > L2 ? (L1 + 0.05f) / (L2 + 0.05f) : (L2 + 0.05f) / (L1 + 0.05f); -} - #ifdef wxHAS_SVG wxBitmapBundle wxAuiCreateBitmap(const char* svgData, int w, int h, const wxColour& color) diff --git a/src/aui/tabart.cpp b/src/aui/tabart.cpp index 8855e5a503..30469ad5e3 100644 --- a/src/aui/tabart.cpp +++ b/src/aui/tabart.cpp @@ -36,31 +36,15 @@ #endif #include "wx/private/aui.h" +#include "wx/private/colour.h" // -- GUI helper classes and functions -- // these functions live in dockart.cpp -- they'll eventually // be moved to a new utility cpp file -float wxAuiGetColourContrast(const wxColour& c1, const wxColour& c2); - wxString wxAuiChopText(wxDC& dc, const wxString& text, int max_size); -// Check if the given colour has sufficient contrast ratio (4.5 recommended) -// with the background and replace it with either white or black if it doesn't. -// (based on https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html) -static void -wxAuiEnsureSufficientContrast(wxColour* fg, const wxColour& bg) -{ - // No need to change the colour if it has sufficient contrast. - if ( wxAuiGetColourContrast(*fg, bg) >= 4.5f ) - return; - - // Otherwise pick the colour that provides better contrast. - *fg = wxAuiGetColourContrast(*wxWHITE, bg) - > wxAuiGetColourContrast(*wxBLACK, bg) ? *wxWHITE : *wxBLACK; -} - // Create a "disabled" version of the given colour by adjusting its lightness // in the direction depending on the theme. static wxColour wxAuiDimColour(wxColour colour, int delta = 30) @@ -1263,7 +1247,7 @@ int wxAuiGenericTabArt::DrawPageTab( // draw tab text wxColor font_color = wxSystemSettings::GetColour( page.active ? wxSYS_COLOUR_CAPTIONTEXT : wxSYS_COLOUR_INACTIVECAPTIONTEXT); - wxAuiEnsureSufficientContrast(&font_color, back_color); + wxEnsureSufficientContrast(&font_color, back_color); dc.SetTextForeground(font_color); dc.DrawText(draw_text, text_offset, @@ -1617,7 +1601,7 @@ void wxAuiSimpleTabArt::DrawTab(wxDC& dc, wxColor back_color = dc.GetBrush().GetColour(); wxColor font_color = wxSystemSettings::GetColour( page.active ? wxSYS_COLOUR_CAPTIONTEXT : wxSYS_COLOUR_INACTIVECAPTIONTEXT); - wxAuiEnsureSufficientContrast(&font_color, back_color); + wxEnsureSufficientContrast(&font_color, back_color); dc.SetTextForeground(font_color); const auto textY = tab_y + (tab_height - texty) / 2 + 1; diff --git a/src/ribbon/art_aui.cpp b/src/ribbon/art_aui.cpp index 569338640a..11d83eba97 100644 --- a/src/ribbon/art_aui.cpp +++ b/src/ribbon/art_aui.cpp @@ -29,6 +29,8 @@ #include "wx/osx/private.h" #endif +#include "wx/private/colour.h" + wxRibbonAUIArtProvider::wxRibbonAUIArtProvider() : wxRibbonMSWArtProvider(false) { @@ -287,7 +289,7 @@ void wxRibbonAUIArtProvider::SetColourScheme( m_button_bar_hover_background_brush = LikeSecondary(1.7f); m_button_bar_active_background_brush = LikeSecondary(1.4f); m_button_bar_label_colour = m_tab_label_colour; - m_button_bar_active_label_colour = wxRibbonGetContrastingLabelColour( + m_button_bar_active_label_colour = wxGetContrastingFgColour( m_button_bar_label_colour, m_button_bar_active_background_brush.GetColour()); #ifdef __WXOSX__ m_button_bar_label_disabled_colour = wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTIONTEXT); diff --git a/src/ribbon/art_internal.cpp b/src/ribbon/art_internal.cpp index 92e113b970..6f0053a8b8 100644 --- a/src/ribbon/art_internal.cpp +++ b/src/ribbon/art_internal.cpp @@ -9,8 +9,6 @@ #include "wx/wxprec.h" -#include - #if wxUSE_RIBBON #include "wx/ribbon/art.h" @@ -60,38 +58,6 @@ bool wxRibbonCanLabelBreakAtPosition(const wxString& label, size_t pos) return label[pos] == ' '; } -// Same contrast formula as wxAuiGetColourContrast() -static float wxRibbonGetSRGBComponent(float c) -{ - return c <= 0.03928f ? c / 12.92f : std::pow((c + 0.055f) / 1.055f, 2.4f); -} - -static float wxRibbonGetRelativeLuminance(const wxColour& c) -{ - return 0.2126f * wxRibbonGetSRGBComponent(c.Red() / 255.0f) + - 0.7152f * wxRibbonGetSRGBComponent(c.Green() / 255.0f) + - 0.0722f * wxRibbonGetSRGBComponent(c.Blue() / 255.0f); -} - -static float wxRibbonGetColourContrast(const wxColour& c1, const wxColour& c2) -{ - const float l1 = wxRibbonGetRelativeLuminance(c1); - const float l2 = wxRibbonGetRelativeLuminance(c2); - return l1 > l2 ? (l1 + 0.05f) / (l2 + 0.05f) - : (l2 + 0.05f) / (l1 + 0.05f); -} - -wxColour wxRibbonGetContrastingLabelColour(const wxColour& foreground, - const wxColour& background) -{ - if (wxRibbonGetColourContrast(foreground, background) >= 4.5f) - return foreground; - - return wxRibbonGetColourContrast(*wxWHITE, background) - > wxRibbonGetColourContrast(*wxBLACK, background) - ? *wxWHITE : *wxBLACK; -} - void wxRibbonDrawParallelGradientLines(wxDC& dc, int nlines, const wxPoint* line_origins, diff --git a/src/ribbon/art_msw.cpp b/src/ribbon/art_msw.cpp index 0df7be3ca9..f6b8094acd 100644 --- a/src/ribbon/art_msw.cpp +++ b/src/ribbon/art_msw.cpp @@ -26,6 +26,8 @@ #include "wx/msw/private.h" #endif +#include "wx/private/colour.h" + static const char* const gallery_up_xpm[] = { "5 5 2 1", " c None", @@ -296,7 +298,7 @@ void wxRibbonMSWArtProvider::SetColourScheme( // The active/pressed background can end up much lighter (or darker) // than the normal button label colour was chosen for, so pick a label // colour that stays legible against it specifically. - m_button_bar_active_label_colour = wxRibbonGetContrastingLabelColour( + m_button_bar_active_label_colour = wxGetContrastingFgColour( m_button_bar_label_colour, m_button_bar_active_background_colour); m_toolbar_border_pen = LikePrimary(1.4f, -0.21f, -0.16f); From c4fe911d197d14b318456012ba4e495ea3c8bca4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 29 Jul 2026 19:36:29 +0200 Subject: [PATCH 4/5] Link to the current WCAG21 instead of the old 2008 version Also update the coefficient in wxGetSRGB() to use the correct value, quoting the linked document: Note 2 Before May 2021 the value of 0.04045 in the definition was different (0.03928). It was taken from an older version of the specification and has been updated. It has no practical effect on the calculations in the context of these guidelines. --- include/wx/private/colour.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/wx/private/colour.h b/include/wx/private/colour.h index dc63c934a6..61f13a38ab 100644 --- a/include/wx/private/colour.h +++ b/include/wx/private/colour.h @@ -18,13 +18,14 @@ namespace wxPrivate { -inline float wxGetSRGB(float r) { - return r <= 0.03928f ? r / 12.92f : std::pow((r + 0.055f) / 1.055f, 2.4f); +inline float wxGetSRGB(float r) +{ + return r <= 0.04045f ? r / 12.92f : std::pow((r + 0.055f) / 1.055f, 2.4f); } inline float wxGetRelativeLuminance(const wxColour& c) { - // based on https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + // See https://www.w3.org/TR/WCAG21/#dfn-relative-luminance return 0.2126f * wxGetSRGB(c.Red() / 255.0f) + 0.7152f * wxGetSRGB(c.Green() / 255.0f) + @@ -33,7 +34,7 @@ inline float wxGetRelativeLuminance(const wxColour& c) inline float wxComputeContrast(const wxColour& c1, const wxColour& c2) { - // based on https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html + // See https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio float L1 = wxGetRelativeLuminance(c1); float L2 = wxGetRelativeLuminance(c2); return L1 > L2 ? (L1 + 0.05f) / (L2 + 0.05f) : (L2 + 0.05f) / (L1 + 0.05f); @@ -43,7 +44,7 @@ inline float wxComputeContrast(const wxColour& c1, const wxColour& c2) // Return the colour if it has sufficient contrast ratio (4.5 recommended) // with the background or return either white or black if it doesn't. -// (based on https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html) +// (see https://www.w3.org/TR/WCAG21/#contrast-minimum) inline wxColour wxGetContrastingFgColour(const wxColour& fg, const wxColour& bg) { From 75a928259869f4e572bbf050d183f662721af70f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 29 Jul 2026 19:47:29 +0200 Subject: [PATCH 5/5] Fix typo in wxGetContrastingFgColour() The code dereferencing wxColour wrongly compiled when using wxGTK due to the existence of operator*() there. --- include/wx/private/colour.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wx/private/colour.h b/include/wx/private/colour.h index 61f13a38ab..85f2514b22 100644 --- a/include/wx/private/colour.h +++ b/include/wx/private/colour.h @@ -51,7 +51,7 @@ wxGetContrastingFgColour(const wxColour& fg, const wxColour& bg) using namespace wxPrivate; // No need to change the colour if it has sufficient contrast. - if ( wxComputeContrast(*fg, bg) >= 4.5f ) + if ( wxComputeContrast(fg, bg) >= 4.5f ) return fg; // Otherwise pick the colour that provides better contrast.