API for finding contrasting colours

Visto 168 veces
Saltar al primer mensaje no leído

Vadim Zeitlin

no leída,
13 jun 2021, 10:57:5313/6/21
a wx-dev
Hello,

As you may have noticed from my repeated (but I think justified)
complaints about it, we have a big problem with choosing custom colours for
the UI elements that don't result in making the UI unusable, due to lack of
contrast, on at least some platforms/with some themes. We've seen it with
wxAUI and even with the (much simpler) wxGrid, but it can also easily occur
in the user code with any other control.

E.g. consider a simple example of wanting to use red/pink background for
some control (e.g. wxTextCtrl or wxSpinCtrl) which uses white background by
default. This obviously doesn't work well if the background in the current
theme is not white, and doesn't work at all if the current text colour is
some shade of red, as it would then become completely invisible.

To solve this problem we need to find a colour which is noticeably
different from the default background, but also provides good contrast with
the default foreground colour. Or, alternatively, find 2 colours and change
both the background and the foreground. I suspect the latter solution is
actually better, but I have no idea how to find the colour to use for the
foreground.

So I wonder how do others do this and whether there are native APIs that
could help us with this and that we could, potentially, emulate on the
other platforms. AFAIK there is nothing like this in Win32 API and I
believe GTK way is to use CSS for everything, which is fine if you change
all the colours, but I'm not sure if it really helps if you just want to
change some of them. And, as usual, I don't know about Mac, does Cocoa
provide some way to use theme-compatible "accent colours" or anything like
this by chance?

Even if there are no native APIs for this, I think we still need to
provide something in wx itself, if only for its own use -- but hopefully
also useful for the applications using it. Currently we only have
wxSystemAppearance::IsDark(), which is required for choosing the custom
colours to use, but is insufficient and it would be great to add some
minimal new API to at least allow implementing the simple task described
above, i.e. selecting a decently looking colour (or colours) for
highlighting errors.

Thanks in advance for any ideas!
VZ

Eran Ifrah

no leída,
13 jun 2021, 11:34:4913/6/21
a wx-...@googlegroups.com
Hi,

I wrote many custom controls over the years.
Usually, my method of selecting colours is based on the background colour.

I have written a simple function that simply checks if a colour is "Dark".
So if for example, a colour is considered dark, I will pick white and then change the lightness to a bit, something like wxColour(*wWHITE).ChangeLightness(70) to get a readable foreground colour.
I do the same for light colours, I will take the a *wxBLACK and lighten it up a bit.

This works for any colour I have tried and proven to be very reliable
In CodeLite IDE that I maintain, the colours are configured: user configures the background colour and the rest is done automatically by the method I mentioned above

This is the method I am using to check if a given colour is dark: 


HTH

--

Eran Ifrah
Author of CodeLite IDE https://codelite.org

Eran Ifrah

no leída,
13 jun 2021, 11:51:0713/6/21
a wx-...@googlegroups.com
Just to give more reference on the implementation of "IsDark" method, it was based on this stackoverflow question/answer:

New Pagodi

no leída,
13 jun 2021, 13:55:1613/6/21
a wx-dev
The W3 (https://www.w3.org/TR/WCAG20-TECHS/G17.html) recommends using a 7:1 relative contrast ratio (which they show how to calculate on that page).

If the two colors don't have that ratio, as an initial guess, I think it might be possible to use HSV space to try to find the "closest" color that does. That is: first move away on the V axis to try to find a color with the needed ration.  If that fails, move away on the S axis.  If that fails, move away on the H axis.

So I think my initial suggestion for a function that does this would look something like
wxColour FindContrastingColor(const wxColour c1, wxColour c2);
which would try to find the "closest" color to c2 that has a relative contrast of 7:1 with c1 using the algorithm above.

Vadim Zeitlin

no leída,
13 jun 2021, 17:36:2413/6/21
a wx-...@googlegroups.com
On Sun, 13 Jun 2021 10:55:16 -0700 (PDT) New Pagodi wrote:

NP> So I think my initial suggestion for a function that does this would look
NP> something like
NP> wxColour FindContrastingColor(const wxColour c1, wxColour c2);
NP> which would try to find the "closest" color to c2 that has a relative
NP> contrast of 7:1 with c1 using the algorithm above.

Thanks, this looks straightforward to implement (although we'd need to add
yet another function for computing relative luminance as I don't think we
can just use the existing wxColour::GetLuminance()), but I'm not sure how
exactly would it be used: would c1 be just the foreground colour here and
c2 the desired background or do I misunderstand the intention?

VZ

Vadim Zeitlin

no leída,
13 jun 2021, 17:48:0513/6/21
a wx-...@googlegroups.com
On Sun, 13 Jun 2021 18:34:37 +0300 Eran Ifrah wrote:

EI> I wrote many custom controls over the years.

Thanks for your answer, this is exactly the kind of feedback I was looking
for!

EI> Usually, my method of selecting colours is based on the background colour.

Sorry if I'm missing something obvious, but it looks like it's simpler to
select the colours that work well together if you change both the
background and the foreground. Ideally I'd like to use the default colours
(i.e. fixed by the system/theme) but have some way of finding readable
"accent colours" for things that should stand out.

EI> I have written a simple function that simply checks if a colour is "Dark".

FWIW we already have wxSystemAppearance::IsUsingDarkBackground(), which is
different in details but should be relatively similar in practice.

EI> This works for any colour I have tried and proven to be very reliable
EI> In CodeLite IDE that I maintain, the colours are configured: user
EI> configures the background colour and the rest is done automatically by the
EI> method I mentioned above

Looking at https://docs.codelite.org/settings/colours_and_fonts/, do you
mean that the syntax highlight colours are derived from the base colour
(black) and the same theme would also work with light background? This
would have been perfect, but I don't really see how could this work, so I
guess this probably only works for the IDE colours (which seem to be based
on various shades of grey) only?

Thanks again,
VZ

Eran Ifrah

no leída,
14 jun 2021, 12:43:2414/6/21
a wx-...@googlegroups.com
In the docs you referred to, its a guide on how to change the colours and fonts of the editor itself (wxStyledTextCtrl)
usually, the user selects a pre-built theme. This will change the colours for the various styles in wxStyledTextCtrl.

However, an event is then sent that the colours have been updated and all the custom controls (most of CodeLite is using custom controls, on all platforms)
are adjusting automatically (I have my own clSystemSettings class, similar to wxSystemSettings, which returns a different colour palette)

If by "shades of grey" you mean: CodeLite changes the lightness of the base colour to get a decent text colour etc, than yes.
I am attaching 3 screenshots on how changing just the base colour, CodeLite adjusts the custom control colours

 Thanks again,
VZ
1-dark.png
1-green.png
1-light.png

Mart Raudsepp

no leída,
14 jun 2021, 16:22:5414/6/21
a wx-...@googlegroups.com
Ühel kenal päeval, P, 13.06.2021 kell 16:57, kirjutas Vadim Zeitlin:
> So I wonder how do others do this and whether there are native APIs
> that could help us with this and that we could, potentially, emulate on
> the other platforms. AFAIK there is nothing like this in Win32 API and
> I believe GTK way is to use CSS for everything, which is fine if you
> change all the colours, but I'm not sure if it really helps if you just
> want to change some of them.

GNOME apps typically rely on a set of predefined CSS color names and
apply CSS methods like darken/lighten/desaturate/mix/etc on those as
appropriate for declaring their own custom colors.

The current set of public color names in GTK 4 can be seen here:


https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gtk/theme/Default/_colors-public.scss

For GTK 3 here:


https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss

They appear identical.

As the README in the folder says at
https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gtk/theme/Default/README
those are meant for use by 3rd party apps for color mixing.

It seems the mix CSS function could be particularly useful, if you want
to get some color that is inbetween two other colors based on the theme
used.

So these things could be useful for if you really do need a color code
to work with.

However custom controls within wxGTK should be doing things different.
Apply CSS classes as appropriate on the components of the control,
define some custom CSS if needed (e.g. if it isn't purely a composition
of widgets which you might just want to show together through applying
a "linked" CSS class name where appropriate) and have the whole
machinery do everything for you. In those cases these names aren't
used, but rather the generated full CSS will apply to them, with what
you'd know more in the web.
And then you get all that rendered with OpenGL quickly with GTK 4.


Mart

Vadim Zeitlin

no leída,
14 jun 2021, 17:50:2214/6/21
a wx-...@googlegroups.com
On Mon, 14 Jun 2021 23:22:46 +0300 Mart Raudsepp wrote:

MR> GNOME apps typically rely on a set of predefined CSS color names and
MR> apply CSS methods like darken/lighten/desaturate/mix/etc on those as
MR> appropriate for declaring their own custom colors.
MR>
MR> The current set of public color names in GTK 4 can be seen here:
MR>
MR> https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gtk/theme/Default/_colors-public.scss
MR>
MR> For GTK 3 here:
MR>
MR> https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
MR>
MR> They appear identical.

Thanks! Would you know how to get the values of these colours using the
API? I think we need to allow retrieving them using wxSystemSettings in any
case.

MR> However custom controls within wxGTK should be doing things different.
MR> Apply CSS classes as appropriate on the components of the control,
MR> define some custom CSS if needed (e.g. if it isn't purely a composition
MR> of widgets which you might just want to show together through applying
MR> a "linked" CSS class name where appropriate) and have the whole
MR> machinery do everything for you. In those cases these names aren't
MR> used, but rather the generated full CSS will apply to them, with what
MR> you'd know more in the web.
MR> And then you get all that rendered with OpenGL quickly with GTK 4.

This is undoubtedly great for GTK 4 applications (until you need to
rewrite them using GTK 5 API, anyhow), but is not very compatible with the
existing wx API. It would be nice if could add some new API to make using
theme colours easier at wx level, but I'm still not sure how to do it...

Regards,
VZ

Vadim Zeitlin

no leída,
14 jun 2021, 17:53:0114/6/21
a wx-...@googlegroups.com
On Mon, 14 Jun 2021 19:43:08 +0300 Eran Ifrah wrote:

EI> If by "shades of grey" you mean: CodeLite changes the lightness of the base
EI> colour to get a decent text colour etc, than yes.
EI> I am attaching 3 screenshots on how changing just the base colour, CodeLite
EI> adjusts the custom control colours

I see, thanks. Something like this could perhaps be useful for wxAUI,
wxRibbon etc, but I'm not sure if we need, or should have, public API for
this. Presumably you don't really need it in CodeLite, as it works just
fine without wx support. Do you use any platform-specific code to implement
this? If yes, and if you'd like to suggest any additions to wx API
abstracting it between platforms, perhaps we should add it to wx itself.

Regards,
VZ

Mart Raudsepp

no leída,
14 jun 2021, 18:18:0714/6/21
a wx-...@googlegroups.com
Ühel kenal päeval, E, 14.06.2021 kell 23:50, kirjutas Vadim Zeitlin:
> On Mon, 14 Jun 2021 23:22:46 +0300 Mart Raudsepp wrote:
>
> MR> GNOME apps typically rely on a set of predefined CSS color names
> and
> MR> apply CSS methods like darken/lighten/desaturate/mix/etc on those
> as
> MR> appropriate for declaring their own custom colors.
> MR>
> MR> The current set of public color names in GTK 4 can be seen here:
> MR>
> MR>
> https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gtk/theme/Default/_colors-public.scss
> MR>
> MR> For GTK 3 here:
> MR>
> MR>
> https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
> MR>
> MR> They appear identical.
>
>  Thanks! Would you know how to get the values of these colours using
> the
> API? I think we need to allow retrieving them using wxSystemSettings in
> any
> case.

Something like this is one way:

GdkRGBA text_color = { 0.0, 0.0, 0.0, 1.0 }; /* Fallback to black */
gtk_style_context_lookup_color (gtk_widget_get_style_context (widget),
"theme_text_color", &text_color);

which is roughly equivalent of the legacy way of getting this:

gtk_widget_get_style (widget)->text[GTK_STATE_NORMAL]

You could maybe use that for gtk2 if wx still cares about (is somewhat
mapped into gtk3 too with heavy deprecation warnings). I thought it
already does, though.
It's hard to map any of this to wxSystemColour though. I vaguely recall
trying it in the past.


MR> However custom controls within wxGTK should be doing things
different.
MR> Apply CSS classes as appropriate on the components of the control,
MR> define some custom CSS if needed (e.g. if it isn't purely a
composition
MR> of widgets which you might just want to show together through
applying
MR> a "linked" CSS class name where appropriate) and have the whole
MR> machinery do everything for you. In those cases these names aren't
MR> used, but rather the generated full CSS will apply to them, with
what
MR> you'd know more in the web.
MR> And then you get all that rendered with OpenGL quickly with GTK 4.

 This is undoubtedly great for GTK 4 applications (until you need to
rewrite them using GTK 5 API, anyhow), but is not very compatible with
the
existing wx API. It would be nice if could add some new API to make
using
theme colours easier at wx level, but I'm still not sure how to do
it...

I meant if you need a custom implementation for something like
wxDatePickerCtrl, wxDataViewCtrl, wxSearchCtrl or some such, then those
could be implemented as a "native" gtk control with appropriate styling
applied, so it would all be themed as appropriate more or less
automatically, instead of picking colors through the API discussed here
and using them directly.
Like all well-written native gtk apps would do their custom widget
needs.

But yeah, can't really have all consumers of wx write a native GUI
toolkit version of controls and wrap it in a common API just for their
own use, and then these colors can come in handy.


Mart

Responder a todos
Responder al autor
Reenviar
0 mensajes nuevos