Bug 1705532 use async/await instead of callbacks r=annyG
[gecko.git] / widget / ThemeColors.cpp
blob08d1ce972f8bbe81b06ec62f85964d8845bb6936
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ThemeColors.h"
8 #include "mozilla/RelativeLuminanceUtils.h"
9 #include "mozilla/StaticPrefs_widget.h"
10 #include "ThemeDrawing.h"
12 using namespace mozilla;
13 using namespace mozilla::gfx;
14 using namespace mozilla::widget;
16 struct ColorPalette {
17 ColorPalette(nscolor aAccent, nscolor aForeground);
19 constexpr ColorPalette(sRGBColor aAccent, sRGBColor aForeground,
20 sRGBColor aLight, sRGBColor aDark, sRGBColor aDarker)
21 : mAccent(aAccent),
22 mForeground(aForeground),
23 mAccentLight(aLight),
24 mAccentDark(aDark),
25 mAccentDarker(aDarker) {}
27 constexpr static ColorPalette Default() {
28 return ColorPalette(
29 sDefaultAccent, sDefaultAccentForeground,
30 sRGBColor::UnusualFromARGB(0x4d008deb), // Luminance: 25.04791%
31 sRGBColor::UnusualFromARGB(0xff0250bb), // Luminance: 9.33808%
32 sRGBColor::UnusualFromARGB(0xff054096) // Luminance: 5.90106%
36 // Ensure accent color is opaque by blending with white. This serves two
37 // purposes: On one hand, it avoids surprises if we overdraw. On the other, it
38 // makes our math below make more sense, as we want to match the browser
39 // style, which has an opaque accent color.
40 static nscolor EnsureOpaque(nscolor aAccent) {
41 if (NS_GET_A(aAccent) != 0xff) {
42 return NS_ComposeColors(NS_RGB(0xff, 0xff, 0xff), aAccent);
44 return aAccent;
47 static nscolor GetLight(nscolor aAccent) {
48 // The luminance from the light color divided by the one of the accent color
49 // in the default palette.
50 constexpr float kLightLuminanceScale = 25.048f / 13.693f;
51 const float lightLuminanceAdjust = ThemeColors::ScaleLuminanceBy(
52 RelativeLuminanceUtils::Compute(aAccent), kLightLuminanceScale);
53 nscolor lightColor =
54 RelativeLuminanceUtils::Adjust(aAccent, lightLuminanceAdjust);
55 return NS_RGBA(NS_GET_R(lightColor), NS_GET_G(lightColor),
56 NS_GET_B(lightColor), 0x4d);
59 static nscolor GetDark(nscolor aAccent) {
60 // Same deal as above (but without the alpha).
61 constexpr float kDarkLuminanceScale = 9.338f / 13.693f;
62 const float darkLuminanceAdjust = ThemeColors::ScaleLuminanceBy(
63 RelativeLuminanceUtils::Compute(aAccent), kDarkLuminanceScale);
64 return RelativeLuminanceUtils::Adjust(aAccent, darkLuminanceAdjust);
67 static nscolor GetDarker(nscolor aAccent) {
68 // Same deal as above.
69 constexpr float kDarkerLuminanceScale = 5.901f / 13.693f;
70 const float darkerLuminanceAdjust = ThemeColors::ScaleLuminanceBy(
71 RelativeLuminanceUtils::Compute(aAccent), kDarkerLuminanceScale);
72 return RelativeLuminanceUtils::Adjust(aAccent, darkerLuminanceAdjust);
75 sRGBColor mAccent;
76 sRGBColor mForeground;
78 // Note that depending on the exact accent color, lighter/darker might really
79 // be inverted.
80 sRGBColor mAccentLight;
81 sRGBColor mAccentDark;
82 sRGBColor mAccentDarker;
85 static nscolor ThemedAccentColor(bool aBackground) {
86 MOZ_ASSERT(StaticPrefs::widget_non_native_theme_use_theme_accent());
87 // TODO(emilio): In the future we should probably add dark-color-scheme
88 // support for non-native form controls.
89 return ColorPalette::EnsureOpaque(LookAndFeel::Color(
90 aBackground ? LookAndFeel::ColorID::MozAccentColor
91 : LookAndFeel::ColorID::MozAccentColorForeground,
92 LookAndFeel::ColorScheme::Light, LookAndFeel::UseStandins::No));
95 static ColorPalette sDefaultPalette = ColorPalette::Default();
97 ColorPalette::ColorPalette(nscolor aAccent, nscolor aForeground) {
98 mAccent = sRGBColor::FromABGR(aAccent);
99 mForeground = sRGBColor::FromABGR(aForeground);
100 mAccentLight = sRGBColor::FromABGR(GetLight(aAccent));
101 mAccentDark = sRGBColor::FromABGR(GetDark(aAccent));
102 mAccentDarker = sRGBColor::FromABGR(GetDarker(aAccent));
105 ThemeAccentColor::ThemeAccentColor(const ComputedStyle& aStyle) {
106 const auto& color = aStyle.StyleUI()->mAccentColor;
107 if (color.IsColor()) {
108 mAccentColor.emplace(
109 ColorPalette::EnsureOpaque(color.AsColor().CalcColor(aStyle)));
110 } else {
111 MOZ_ASSERT(color.IsAuto());
115 sRGBColor ThemeAccentColor::Get() const {
116 if (!mAccentColor) {
117 return sDefaultPalette.mAccent;
119 return sRGBColor::FromABGR(*mAccentColor);
122 sRGBColor ThemeAccentColor::GetForeground() const {
123 if (!mAccentColor) {
124 return sDefaultPalette.mForeground;
126 return sRGBColor::FromABGR(
127 ThemeColors::ComputeCustomAccentForeground(*mAccentColor));
130 sRGBColor ThemeAccentColor::GetLight() const {
131 if (!mAccentColor) {
132 return sDefaultPalette.mAccentLight;
134 return sRGBColor::FromABGR(ColorPalette::GetLight(*mAccentColor));
137 sRGBColor ThemeAccentColor::GetDark() const {
138 if (!mAccentColor) {
139 return sDefaultPalette.mAccentDark;
141 return sRGBColor::FromABGR(ColorPalette::GetDark(*mAccentColor));
144 sRGBColor ThemeAccentColor::GetDarker() const {
145 if (!mAccentColor) {
146 return sDefaultPalette.mAccentDarker;
148 return sRGBColor::FromABGR(ColorPalette::GetDarker(*mAccentColor));
151 bool ThemeColors::ShouldBeHighContrast(const nsPresContext& aPc) {
152 // We make sure that we're drawing backgrounds, since otherwise layout will
153 // darken our used text colors etc anyways, and that can cause contrast issues
154 // with dark high-contrast themes.
155 return aPc.GetBackgroundColorDraw() &&
156 PreferenceSheet::PrefsFor(*aPc.Document())
157 .NonNativeThemeShouldBeHighContrast();
160 /*static*/
161 void ThemeColors::RecomputeAccentColors() {
162 MOZ_RELEASE_ASSERT(NS_IsMainThread());
164 if (!StaticPrefs::widget_non_native_theme_use_theme_accent()) {
165 sDefaultPalette = ColorPalette::Default();
166 return;
169 sDefaultPalette =
170 ColorPalette(ThemedAccentColor(true), ThemedAccentColor(false));
173 /*static*/
174 nscolor ThemeColors::ComputeCustomAccentForeground(nscolor aColor) {
175 // Contrast ratio is defined in
176 // https://www.w3.org/TR/WCAG20/#contrast-ratiodef as:
178 // (L1 + 0.05) / (L2 + 0.05)
180 // Where L1 is the lighter color, and L2 is the darker one. So we determine
181 // whether we're dark or light and resolve the equation for the target ratio.
183 // So when lightening:
185 // L1 = k * (L2 + 0.05) - 0.05
187 // And when darkening:
189 // L2 = (L1 + 0.05) / k - 0.05
191 const float luminance = RelativeLuminanceUtils::Compute(aColor);
193 // We generally prefer white unless we can't because the color is really light
194 // and we can't provide reasonable contrast.
195 const float ratioWithWhite = 1.05f / (luminance + 0.05f);
196 const bool canBeWhite =
197 ratioWithWhite >=
198 StaticPrefs::layout_css_accent_color_min_contrast_ratio();
199 if (canBeWhite) {
200 return NS_RGB(0xff, 0xff, 0xff);
202 const float targetRatio =
203 StaticPrefs::layout_css_accent_color_darkening_target_contrast_ratio();
204 const float targetLuminance = (luminance + 0.05f) / targetRatio - 0.05f;
205 return RelativeLuminanceUtils::Adjust(aColor, targetLuminance);
208 nscolor ThemeColors::AdjustUnthemedScrollbarThumbColor(nscolor aFaceColor,
209 EventStates aStates) {
210 // In Windows 10, scrollbar thumb has the following colors:
212 // State | Color | Luminance
213 // -------+----------+----------
214 // Normal | Gray 205 | 61.0%
215 // Hover | Gray 166 | 38.1%
216 // Active | Gray 96 | 11.7%
218 // This function is written based on the ratios between the values.
219 bool isActive = aStates.HasState(NS_EVENT_STATE_ACTIVE);
220 bool isHover = aStates.HasState(NS_EVENT_STATE_HOVER);
221 if (!isActive && !isHover) {
222 return aFaceColor;
224 float luminance = RelativeLuminanceUtils::Compute(aFaceColor);
225 if (isActive) {
226 // 11.7 / 61.0
227 luminance = ScaleLuminanceBy(luminance, 0.192f);
228 } else {
229 // 38.1 / 61.0
230 luminance = ScaleLuminanceBy(luminance, 0.625f);
232 return RelativeLuminanceUtils::Adjust(aFaceColor, luminance);