From f2f2868de55901c0b99d1e4a804063767f258c0d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 26 May 2023 00:55:28 +0100 Subject: [PATCH] Add support for dark mode colours to XRC Allow specifying a different colour to use in the dark mode in XRC colour properties. Closes #23571. --- docs/doxygen/overviews/xrc_format.h | 14 +++++++++-- include/wx/xrc/xmlres.h | 8 ++++-- include/wx/xrc/xmlreshandler.h | 8 +++--- interface/wx/xrc/xmlres.h | 10 ++++++-- misc/schema/xrc_schema.rnc | 3 +-- samples/xrc/rc/controls.xrc | 2 +- src/xrc/xh_clrpicker.cpp | 2 +- src/xrc/xmlres.cpp | 39 ++++++++++++++++++++++++++--- 8 files changed, 69 insertions(+), 17 deletions(-) diff --git a/docs/doxygen/overviews/xrc_format.h b/docs/doxygen/overviews/xrc_format.h index e15ae5a876..235dcd927d 100644 --- a/docs/doxygen/overviews/xrc_format.h +++ b/docs/doxygen/overviews/xrc_format.h @@ -222,7 +222,7 @@ is "." regardless of the locale. @subsection overview_xrcformat_type_colour Colour -Colour specification can be either any string colour representation accepted +A single colour can be either any string colour representation accepted by wxColour::Set() or any wxSYS_COLOUR_XXX symbolic name accepted by wxSystemSettings::GetColour(). In particular, the following forms are supported: @@ -231,11 +231,21 @@ wxSystemSettings::GetColour(). In particular, the following forms are supported: @li CSS-style "rgb(r,g,b)" and "rgba(r,g,b,a)" @li wxSYS_COLOUR_XXX symbolic names +Moreover, a single colour definition in XRC may contain more than one colour, +separated by `|` (pipe symbol), with the first colour used by default and the +subsequent colours in specific situations. Currently the only supported +alternative colour is the colour to be used in dark mode, which must be +prefixed with "dark:". + +It is recommended to provide both light and dark values when not using system +colour names (that already adapt to the dark mode), as it's rare for the same +colour to look well in both light and dark mode. + Some examples: @code red #ff0000 -rgb(255,0,0) +rgb(192,192,192)|dark:#404040 wxSYS_COLOUR_HIGHLIGHT @endcode diff --git a/include/wx/xrc/xmlres.h b/include/wx/xrc/xmlres.h index fac667a5fe..8cc356b9cc 100644 --- a/include/wx/xrc/xmlres.h +++ b/include/wx/xrc/xmlres.h @@ -558,8 +558,12 @@ public: // Gets a float value from the parameter. float GetFloat(const wxString& param, float defaultv = 0) override; - // Gets colour in HTML syntax (#RRGGBB). - wxColour GetColour(const wxString& param, const wxColour& defaultv = wxNullColour) override; + // Gets colour from the parameter, returning one of the provided default + // values if it's not specified depending on whether we're using light or + // dark mode. + wxColour GetColour(const wxString& param, + const wxColour& defaultLight = wxNullColour, + const wxColour& defaultDark = wxNullColour) override; // Gets the size (may be in dialog units). wxSize GetSize(const wxString& param = wxT("size"), diff --git a/include/wx/xrc/xmlreshandler.h b/include/wx/xrc/xmlreshandler.h index 74c0e087fb..be71ad7af9 100644 --- a/include/wx/xrc/xmlreshandler.h +++ b/include/wx/xrc/xmlreshandler.h @@ -76,7 +76,8 @@ public: virtual long GetLong(const wxString& param, long defaultv = 0) = 0; virtual float GetFloat(const wxString& param, float defaultv = 0) = 0; virtual wxColour GetColour(const wxString& param, - const wxColour& defaultv = wxNullColour) = 0; + const wxColour& defaultLight = wxNullColour, + const wxColour& defaultDark = wxNullColour) = 0; virtual wxSize GetSize(const wxString& param = wxT("size"), wxWindow *windowToUse = nullptr) = 0; virtual wxPoint GetPosition(const wxString& param = wxT("pos"), @@ -301,9 +302,10 @@ protected: return GetImpl()->GetFloat(param, defaultv); } wxColour GetColour(const wxString& param, - const wxColour& defaultv = wxNullColour) + const wxColour& defaultLight = wxNullColour, + const wxColour& defaultDark = wxNullColour) { - return GetImpl()->GetColour(param, defaultv); + return GetImpl()->GetColour(param, defaultLight, defaultDark); } wxSize GetSize(const wxString& param = wxT("size"), wxWindow *windowToUse = nullptr) diff --git a/interface/wx/xrc/xmlres.h b/interface/wx/xrc/xmlres.h index 2fefb75a4e..ea43e57a48 100644 --- a/interface/wx/xrc/xmlres.h +++ b/interface/wx/xrc/xmlres.h @@ -654,10 +654,16 @@ protected: bool GetBool(const wxString& param, bool defaultv = false); /** - Gets colour in HTML syntax (\#RRGGBB). + Gets colour from the given parameter. + + If the colour is not specified, returns @a defaultLight or @a + defaultDark if the application is using dark mode. + + Parameter @a defaultDark is only available since wxWidgets 3.3.0. */ wxColour GetColour(const wxString& param, - const wxColour& defaultColour = wxNullColour); + const wxColour& defaultLight = wxNullColour, + const wxColour& defaultDark = wxNullColour); /** Returns the current file system. diff --git a/misc/schema/xrc_schema.rnc b/misc/schema/xrc_schema.rnc index 88c3e69d3f..cab7948175 100644 --- a/misc/schema/xrc_schema.rnc +++ b/misc/schema/xrc_schema.rnc @@ -458,8 +458,7 @@ t_showeffect = "wxSHOW_EFFECT_NONE" | "wxSHOW_EFFECT_ROLL_TO_LEFT" | "wxSHOW_EFFECT_EXPAND" t_url = string -t_colour = xsd:string { pattern = "#[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]" } | - xsd:string { pattern = "[^#].*" } +t_colour = xsd:string { pattern = "(#[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]|[a-zA-Z0-9 ]+)(\|dark:(#[0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]|[a-zA-Z0-9 ]+))?" } t_position = t_size t_size = xsd:string { pattern = "(-?\d+),(-?\d+)d?" } t_pair_ints = xsd:string { pattern = "(-?\d+),(-?\d+)" } diff --git a/samples/xrc/rc/controls.xrc b/samples/xrc/rc/controls.xrc index 638f91a167..9617db25e6 100644 --- a/samples/xrc/rc/controls.xrc +++ b/samples/xrc/rc/controls.xrc @@ -1396,7 +1396,7 @@ lay them out using wxSizers, absolute positioning, everything you like! 5 - #bbbbff + #bbbbff|dark:#ffbbbb 2,2 diff --git a/src/xrc/xh_clrpicker.cpp b/src/xrc/xh_clrpicker.cpp index 8f094e11d5..0f9260840d 100644 --- a/src/xrc/xh_clrpicker.cpp +++ b/src/xrc/xh_clrpicker.cpp @@ -32,7 +32,7 @@ wxObject *wxColourPickerCtrlXmlHandler::DoCreateResource() picker->Create(m_parentAsWindow, GetID(), - GetColour(wxT("value"), *wxBLACK), + GetColour(wxT("value"), *wxBLACK, *wxWHITE), GetPosition(), GetSize(), GetStyle(wxT("style"), wxCLRP_DEFAULT_STYLE), wxDefaultValidator, diff --git a/src/xrc/xmlres.cpp b/src/xrc/xmlres.cpp index 8c321f2117..af86c3efbd 100644 --- a/src/xrc/xmlres.cpp +++ b/src/xrc/xmlres.cpp @@ -1780,12 +1780,43 @@ static wxColour GetSystemColour(const wxString& name) return wxNullColour; } -wxColour wxXmlResourceHandlerImpl::GetColour(const wxString& param, const wxColour& defaultv) +wxColour +wxXmlResourceHandlerImpl::GetColour(const wxString& param, + const wxColour& defaultLight, + const wxColour& defaultDark) { - wxString v = GetParamValue(param); + const wxString& values = GetParamValue(param); - if ( v.empty() ) - return defaultv; + if ( values.empty() ) + return wxSystemSettings::SelectLightDark(defaultLight, defaultDark); + + wxStringTokenizer tk(values, "|"); + wxString v = tk.GetNextToken(); + + while ( tk.HasMoreTokens() ) + { + wxString altCol = tk.GetNextToken(); + wxString altV; + if ( altCol.StartsWith("dark:", &altV) ) + { + if ( wxSystemSettings::GetAppearance().IsDark() ) + v = altV; + //else: just ignore it, it's valid but not used + } + else + { + ReportParamError + ( + param, + wxString::Format + ( + "unrecognized alternative colour specification \"%s\"", + altCol + ) + ); + return wxNullColour; + } + } wxColour clr;