1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/native_theme/native_theme_base.h"
6
7 #include <limits>
8 #include <memory>
9
10 #include "base/check.h"
11 #include "base/command_line.h"
12 #include "base/notreached.h"
13 #include "base/numerics/ranges.h"
14 #include "build/build_config.h"
15 #include "cc/paint/paint_flags.h"
16 #include "cc/paint/paint_shader.h"
17 #include "third_party/skia/include/core/SkPath.h"
18 #include "third_party/skia/include/effects/SkGradientShader.h"
19 #include "ui/base/layout.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/base/ui_base_features.h"
22 #include "ui/base/ui_base_switches.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/color_palette.h"
25 #include "ui/gfx/color_utils.h"
26 #include "ui/gfx/geometry/rect.h"
27 #include "ui/gfx/geometry/rect_f.h"
28 #include "ui/gfx/geometry/size.h"
29 #include "ui/gfx/image/image_skia.h"
30 #include "ui/gfx/skia_util.h"
31 #include "ui/native_theme/common_theme.h"
32
33 namespace {
34
35 // These are the default dimensions of radio buttons and checkboxes.
36 const int kCheckboxAndRadioWidth = 13;
37 const int kCheckboxAndRadioHeight = 13;
38
39 // These sizes match the sizes in Chromium Win.
40 const int kSliderThumbWidth = 11;
41 const int kSliderThumbHeight = 21;
42
43 // Color constant pairs for light/default and dark color-schemes below.
44 constexpr SkColor kThumbActiveColor[2] = {SkColorSetRGB(0xF4, 0xF4, 0xF4),
45 gfx::kPlaceholderColor};
46 constexpr SkColor kThumbInactiveColor[2] = {SkColorSetRGB(0xEA, 0xEA, 0xEA),
47 SK_ColorWHITE};
48 constexpr SkColor kTrackColor[2] = {SkColorSetRGB(0xD3, 0xD3, 0xD3),
49 gfx::kPlaceholderColor};
50 constexpr SkColor kSliderTrackBackgroundColor[2] = {
51 SkColorSetRGB(0xE3, 0xDD, 0xD8), SkColorSetRGB(0x44, 0x44, 0x44)};
52 constexpr SkColor kSliderThumbBrightColor[2] = {
53 SkColorSetRGB(0xF4, 0xF2, 0xEF), SkColorSetRGB(0xD0, 0xD0, 0xD0)};
54 constexpr SkColor kSliderThumbShadedColor[2] = {
55 SkColorSetRGB(0xEA, 0xE5, 0xE0), SkColorSetRGB(0xC4, 0xC4, 0xC4)};
56 constexpr SkColor kSliderThumbHoveredBrightColor[2] = {
57 SK_ColorWHITE, SkColorSetRGB(0xDD, 0xDD, 0xDD)};
58 constexpr SkColor kSliderThumbHoveredShadedColor[2] = {
59 SkColorSetRGB(0xF4, 0xF2, 0xEF), SkColorSetRGB(0xD0, 0xD0, 0xD0)};
60 constexpr SkColor kSliderThumbBorder[2] = {SkColorSetRGB(0x9D, 0x96, 0x8E),
61 SkColorSetRGB(0x63, 0x6C, 0x72)};
62 constexpr SkColor kTextBorderColor[2] = {SkColorSetRGB(0xA9, 0xA9, 0xA9),
63 SkColorSetRGB(0x60, 0x60, 0x60)};
64 constexpr SkColor kProgressBorderColor[2] = {SkColorSetRGB(0xA9, 0xA9, 0xA9),
65 SkColorSetRGB(0x60, 0x60, 0x60)};
66 constexpr SkColor kProgressTickColor[2] = {SkColorSetRGB(0xED, 0xED, 0xED),
67 SkColorSetRGB(0x20, 0x20, 0x20)};
68 constexpr SkColor kProgressValueColor[2] = {gfx::kGoogleBlue300,
69 gfx::kGoogleBlue700};
70 // We are currently only painting kMenuPopupBackground with the kDefault
71 // scheme. If that changes, we need to replace gfx::kPlaceholderColor with an
72 // appropriate dark scheme color. See the DCHECK in PaintMenuPopupBackground().
73 constexpr SkColor kMenuPopupBackgroundColor[2] = {SkColorSetRGB(210, 225, 246),
74 gfx::kPlaceholderColor};
75 constexpr SkColor kCheckboxTinyColor[2] = {SK_ColorGRAY, SK_ColorDKGRAY};
76 constexpr SkColor kCheckboxShadowColor[2] = {SkColorSetA(SK_ColorBLACK, 0x15),
77 SkColorSetA(SK_ColorWHITE, 0x15)};
78 constexpr SkColor kCheckboxShadowHoveredColor[2] = {
79 SkColorSetA(SK_ColorBLACK, 0x1F), SkColorSetA(SK_ColorWHITE, 0x1F)};
80 constexpr SkColor kCheckboxShadowDisabledColor[2] = {
81 SK_ColorTRANSPARENT, SkColorSetA(SK_ColorWHITE, 0x1F)};
82 constexpr SkColor kCheckboxGradientStartColor[2] = {
83 SkColorSetRGB(0xED, 0xED, 0xED), SkColorSetRGB(0x13, 0x13, 0x13)};
84 constexpr SkColor kCheckboxGradientEndColor[2] = {
85 SkColorSetRGB(0xDE, 0xDE, 0xDE), SkColorSetRGB(0x20, 0x20, 0x20)};
86 constexpr U8CPU kCheckboxDisabledGradientAlpha = 0x80;
87 constexpr SkColor kCheckboxPressedGradientStartColor[2] = {
88 SkColorSetRGB(0xE7, 0xE7, 0xE7), SkColorSetRGB(0x19, 0x19, 0x19)};
89 constexpr SkColor kCheckboxPressedGradientEndColor[2] = {
90 SkColorSetRGB(0xD7, 0xD7, 0xD7), SkColorSetRGB(0x27, 0x27, 0x27)};
91 const SkColor kCheckboxHoveredGradientStartColor[2] = {
92 SkColorSetRGB(0xF0, 0xF0, 0xF0), SkColorSetRGB(0x16, 0x16, 0x16)};
93 const SkColor kCheckboxHoveredGradientEndColor[2] = {
94 SkColorSetRGB(0xE0, 0xE0, 0xE0), SkColorSetRGB(0x20, 0x20, 0x20)};
95 constexpr SkColor kCheckboxBorderColor[2] = {SkColorSetA(SK_ColorBLACK, 0x40),
96 SkColorSetA(SK_ColorWHITE, 0x40)};
97 constexpr SkColor kCheckboxBorderHoveredColor[2] = {
98 SkColorSetA(SK_ColorBLACK, 0x4D), SkColorSetA(SK_ColorWHITE, 0x4D)};
99 constexpr SkColor kCheckboxBorderDisabledColor[2] = {
100 SkColorSetA(SK_ColorBLACK, 0x20), SkColorSetA(SK_ColorWHITE, 0x20)};
101 constexpr SkColor kCheckboxStrokeColor[2] = {SkColorSetA(SK_ColorBLACK, 0xB3),
102 SkColorSetA(SK_ColorWHITE, 0xB3)};
103 constexpr SkColor kCheckboxStrokeDisabledColor[2] = {
104 SkColorSetA(SK_ColorBLACK, 0x59), SkColorSetA(SK_ColorWHITE, 0x59)};
105 constexpr SkColor kRadioDotColor[2] = {SkColorSetRGB(0x66, 0x66, 0x66),
106 SkColorSetRGB(0xDD, 0xDD, 0xDD)};
107 constexpr SkColor kRadioDotDisabledColor[2] = {
108 SkColorSetARGB(0x80, 0x66, 0x66, 0x66),
109 SkColorSetARGB(0x80, 0xDD, 0xDD, 0xDD)};
110 constexpr SkColor kArrowDisabledColor[2] = {SK_ColorBLACK, SK_ColorWHITE};
111 constexpr SkColor kButtonBorderColor[2] = {SK_ColorBLACK, SK_ColorWHITE};
112 constexpr SkColor kProgressBackgroundColor[2] = {SK_ColorWHITE, SK_ColorBLACK};
113
114 // The "dash" is 8x2 px by default (the checkbox is 13x13 px).
115 const SkScalar kIndeterminateInsetWidthRatio = (13 - 8) / 2.0f / 13;
116 const SkScalar kIndeterminateInsetHeightRatio = (13 - 2) / 2.0f / 13;
117 const SkScalar kBorderWidth = 1.f;
118 const SkScalar kSliderTrackHeight = 8.f;
119 const SkScalar kSliderThumbBorderWidth = 1.f;
120 const SkScalar kSliderThumbBorderHoveredWidth = 1.f;
121 // Default height for progress is 16px and the track is 8px.
122 const SkScalar kTrackHeightRatio = 8.0f / 16;
123 const SkScalar kMenuListArrowStrokeWidth = 2.f;
124 const int kSliderThumbSize = 16;
125
126 // Get a color constant based on color-scheme
GetColor(const SkColor colors[2],ui::NativeTheme::ColorScheme color_scheme)127 SkColor GetColor(const SkColor colors[2],
128 ui::NativeTheme::ColorScheme color_scheme) {
129 return colors[color_scheme == ui::NativeTheme::ColorScheme::kDark ? 1 : 0];
130 }
131
132 // Get lightness adjusted color.
BrightenColor(const color_utils::HSL & hsl,SkAlpha alpha,double lightness_amount)133 SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
134 double lightness_amount) {
135 color_utils::HSL adjusted = hsl;
136 adjusted.l += lightness_amount;
137 if (adjusted.l > 1.0)
138 adjusted.l = 1.0;
139 if (adjusted.l < 0.0)
140 adjusted.l = 0.0;
141
142 return color_utils::HSLToSkColor(adjusted, alpha);
143 }
144
145 } // namespace
146
147 namespace ui {
148
GetPartSize(Part part,State state,const ExtraParams & extra) const149 gfx::Size NativeThemeBase::GetPartSize(Part part,
150 State state,
151 const ExtraParams& extra) const {
152 switch (part) {
153 // Please keep these in the order of NativeTheme::Part.
154 case kCheckbox:
155 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
156 case kInnerSpinButton:
157 return gfx::Size(scrollbar_width_, 0);
158 case kMenuList:
159 return gfx::Size(); // No default size.
160 case kMenuPopupBackground:
161 return gfx::Size(); // No default size.
162 case kMenuItemBackground:
163 case kProgressBar:
164 case kPushButton:
165 return gfx::Size(); // No default size.
166 case kRadio:
167 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
168 case kScrollbarDownArrow:
169 case kScrollbarUpArrow:
170 return gfx::Size(scrollbar_width_, scrollbar_button_length_);
171 case kScrollbarLeftArrow:
172 case kScrollbarRightArrow:
173 return gfx::Size(scrollbar_button_length_, scrollbar_width_);
174 case kScrollbarHorizontalThumb:
175 // This matches Firefox on Linux.
176 return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
177 case kScrollbarVerticalThumb:
178 // This matches Firefox on Linux.
179 return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
180 case kScrollbarHorizontalTrack:
181 return gfx::Size(0, scrollbar_width_);
182 case kScrollbarVerticalTrack:
183 return gfx::Size(scrollbar_width_, 0);
184 case kScrollbarHorizontalGripper:
185 case kScrollbarVerticalGripper:
186 NOTIMPLEMENTED();
187 break;
188 case kSliderTrack:
189 return gfx::Size(); // No default size.
190 case kSliderThumb:
191 // These sizes match the sizes in Chromium Win.
192 if (features::IsFormControlsRefreshEnabled())
193 return gfx::Size(kSliderThumbSize, kSliderThumbSize);
194 return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
195 case kTabPanelBackground:
196 NOTIMPLEMENTED();
197 break;
198 case kTextField:
199 return gfx::Size(); // No default size.
200 case kTrackbarThumb:
201 case kTrackbarTrack:
202 case kWindowResizeGripper:
203 NOTIMPLEMENTED();
204 break;
205 default:
206 NOTREACHED() << "Unknown theme part: " << part;
207 break;
208 }
209 return gfx::Size();
210 }
211
GetBorderRadiusForPart(Part part,float width,float height,float zoom) const212 float NativeThemeBase::GetBorderRadiusForPart(Part part,
213 float width,
214 float height,
215 float zoom) const {
216 if (!features::IsFormControlsRefreshEnabled()) {
217 NOTREACHED() << "GetBorderRadiusForPart only supports FormControlsRefresh.";
218 return 0;
219 }
220
221 switch (part) {
222 case kCheckbox:
223 return 2.f * zoom;
224 case kPushButton:
225 case kTextField:
226 return 2.f;
227 case kRadio:
228 return std::max(width, height) * 0.5;
229 case kProgressBar:
230 case kSliderTrack:
231 // default border radius for progress and range is 40px.
232 return 40.f;
233 case kSliderThumb:
234 return std::max(width, height) * 0.5;
235 default:
236 break;
237 }
238 return 0;
239 }
240
Paint(cc::PaintCanvas * canvas,Part part,State state,const gfx::Rect & rect,const ExtraParams & extra,ColorScheme color_scheme) const241 void NativeThemeBase::Paint(cc::PaintCanvas* canvas,
242 Part part,
243 State state,
244 const gfx::Rect& rect,
245 const ExtraParams& extra,
246 ColorScheme color_scheme) const {
247 if (rect.IsEmpty())
248 return;
249
250 canvas->save();
251 canvas->clipRect(gfx::RectToSkRect(rect));
252
253 switch (part) {
254 // Please keep these in the order of NativeTheme::Part.
255 case kCheckbox:
256 PaintCheckbox(canvas, state, rect, extra.button, color_scheme);
257 break;
258 #if (defined(OS_LINUX) || defined(OS_BSD)) && !defined(OS_CHROMEOS)
259 case kFrameTopArea:
260 PaintFrameTopArea(canvas, state, rect, extra.frame_top_area,
261 color_scheme);
262 break;
263 #endif
264 case kInnerSpinButton:
265 PaintInnerSpinButton(canvas, state, rect, extra.inner_spin, color_scheme);
266 break;
267 case kMenuList:
268 PaintMenuList(canvas, state, rect, extra.menu_list, color_scheme);
269 break;
270 case kMenuPopupBackground:
271 PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background,
272 color_scheme);
273 break;
274 case kMenuPopupSeparator:
275 PaintMenuSeparator(canvas, state, rect, extra.menu_separator,
276 color_scheme);
277 break;
278 case kMenuItemBackground:
279 PaintMenuItemBackground(canvas, state, rect, extra.menu_item,
280 color_scheme);
281 break;
282 case kProgressBar:
283 PaintProgressBar(canvas, state, rect, extra.progress_bar, color_scheme);
284 break;
285 case kPushButton:
286 PaintButton(canvas, state, rect, extra.button, color_scheme);
287 break;
288 case kRadio:
289 PaintRadio(canvas, state, rect, extra.button, color_scheme);
290 break;
291 case kScrollbarDownArrow:
292 case kScrollbarUpArrow:
293 case kScrollbarLeftArrow:
294 case kScrollbarRightArrow:
295 if (scrollbar_button_length_ > 0)
296 PaintArrowButton(canvas, rect, part, state, color_scheme,
297 extra.scrollbar_arrow);
298 break;
299 case kScrollbarHorizontalThumb:
300 case kScrollbarVerticalThumb:
301 PaintScrollbarThumb(canvas, part, state, rect,
302 extra.scrollbar_thumb.scrollbar_theme, color_scheme);
303 break;
304 case kScrollbarHorizontalTrack:
305 case kScrollbarVerticalTrack:
306 PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect,
307 color_scheme);
308 break;
309 case kScrollbarHorizontalGripper:
310 case kScrollbarVerticalGripper:
311 // Invoked by views scrollbar code, don't care about for non-win
312 // implementations, so no NOTIMPLEMENTED.
313 break;
314 case kScrollbarCorner:
315 PaintScrollbarCorner(canvas, state, rect, color_scheme);
316 break;
317 case kSliderTrack:
318 PaintSliderTrack(canvas, state, rect, extra.slider, color_scheme);
319 break;
320 case kSliderThumb:
321 PaintSliderThumb(canvas, state, rect, extra.slider, color_scheme);
322 break;
323 case kTabPanelBackground:
324 NOTIMPLEMENTED();
325 break;
326 case kTextField:
327 PaintTextField(canvas, state, rect, extra.text_field, color_scheme);
328 break;
329 case kTrackbarThumb:
330 case kTrackbarTrack:
331 case kWindowResizeGripper:
332 NOTIMPLEMENTED();
333 break;
334 default:
335 NOTREACHED() << "Unknown theme part: " << part;
336 break;
337 }
338
339 canvas->restore();
340 }
341
SupportsNinePatch(Part part) const342 bool NativeThemeBase::SupportsNinePatch(Part part) const {
343 return false;
344 }
345
GetNinePatchCanvasSize(Part part) const346 gfx::Size NativeThemeBase::GetNinePatchCanvasSize(Part part) const {
347 NOTREACHED() << "NativeThemeBase doesn't support nine-patch resources.";
348 return gfx::Size();
349 }
350
GetNinePatchAperture(Part part) const351 gfx::Rect NativeThemeBase::GetNinePatchAperture(Part part) const {
352 NOTREACHED() << "NativeThemeBase doesn't support nine-patch resources.";
353 return gfx::Rect();
354 }
355
NativeThemeBase()356 NativeThemeBase::NativeThemeBase() : NativeThemeBase(false) {}
357
NativeThemeBase(bool should_only_use_dark_colors)358 NativeThemeBase::NativeThemeBase(bool should_only_use_dark_colors)
359 : NativeTheme(should_only_use_dark_colors) {}
360
361 NativeThemeBase::~NativeThemeBase() = default;
362
PaintArrowButton(cc::PaintCanvas * canvas,const gfx::Rect & rect,Part direction,State state,ColorScheme color_scheme,const ScrollbarArrowExtraParams & arrow) const363 void NativeThemeBase::PaintArrowButton(
364 cc::PaintCanvas* canvas,
365 const gfx::Rect& rect,
366 Part direction,
367 State state,
368 ColorScheme color_scheme,
369 const ScrollbarArrowExtraParams& arrow) const {
370 cc::PaintFlags flags;
371
372 // Calculate button color.
373 SkScalar track_hsv[3];
374 SkColorToHSV(GetColor(kTrackColor, color_scheme), track_hsv);
375 SkColor button_color = SaturateAndBrighten(track_hsv, 0, 0.2f);
376 SkColor background_color = button_color;
377 if (state == kPressed) {
378 SkScalar button_hsv[3];
379 SkColorToHSV(button_color, button_hsv);
380 button_color = SaturateAndBrighten(button_hsv, 0, -0.1f);
381 } else if (state == kHovered) {
382 SkScalar button_hsv[3];
383 SkColorToHSV(button_color, button_hsv);
384 button_color = SaturateAndBrighten(button_hsv, 0, 0.05f);
385 }
386
387 SkIRect skrect;
388 skrect.setXYWH(rect.x(), rect.y(), rect.width(), rect.height());
389 // Paint the background (the area visible behind the rounded corners).
390 flags.setColor(background_color);
391 canvas->drawIRect(skrect, flags);
392
393 // Paint the button's outline and fill the middle
394 SkPath outline;
395 switch (direction) {
396 case kScrollbarUpArrow:
397 outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
398 outline.rLineTo(0, -(rect.height() - 2));
399 outline.rLineTo(2, -2);
400 outline.rLineTo(rect.width() - 5, 0);
401 outline.rLineTo(2, 2);
402 outline.rLineTo(0, rect.height() - 2);
403 break;
404 case kScrollbarDownArrow:
405 outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
406 outline.rLineTo(0, rect.height() - 2);
407 outline.rLineTo(2, 2);
408 outline.rLineTo(rect.width() - 5, 0);
409 outline.rLineTo(2, -2);
410 outline.rLineTo(0, -(rect.height() - 2));
411 break;
412 case kScrollbarRightArrow:
413 outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
414 outline.rLineTo(rect.width() - 2, 0);
415 outline.rLineTo(2, 2);
416 outline.rLineTo(0, rect.height() - 5);
417 outline.rLineTo(-2, 2);
418 outline.rLineTo(-(rect.width() - 2), 0);
419 break;
420 case kScrollbarLeftArrow:
421 outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
422 outline.rLineTo(-(rect.width() - 2), 0);
423 outline.rLineTo(-2, 2);
424 outline.rLineTo(0, rect.height() - 5);
425 outline.rLineTo(2, 2);
426 outline.rLineTo(rect.width() - 2, 0);
427 break;
428 default:
429 break;
430 }
431 outline.close();
432
433 flags.setStyle(cc::PaintFlags::kFill_Style);
434 flags.setColor(button_color);
435 canvas->drawPath(outline, flags);
436
437 flags.setAntiAlias(true);
438 flags.setStyle(cc::PaintFlags::kStroke_Style);
439 SkScalar thumb_hsv[3];
440 SkColorToHSV(GetColor(kThumbInactiveColor, color_scheme), thumb_hsv);
441 flags.setColor(OutlineColor(track_hsv, thumb_hsv));
442 canvas->drawPath(outline, flags);
443
444 PaintArrow(canvas, rect, direction, GetArrowColor(state, color_scheme));
445 }
446
PaintArrow(cc::PaintCanvas * gc,const gfx::Rect & rect,Part direction,SkColor color) const447 void NativeThemeBase::PaintArrow(cc::PaintCanvas* gc,
448 const gfx::Rect& rect,
449 Part direction,
450 SkColor color) const {
451 cc::PaintFlags flags;
452 flags.setColor(color);
453
454 SkPath path = PathForArrow(rect, direction);
455
456 gc->drawPath(path, flags);
457 }
458
PathForArrow(const gfx::Rect & rect,Part direction) const459 SkPath NativeThemeBase::PathForArrow(const gfx::Rect& rect,
460 Part direction) const {
461 gfx::Rect bounding_rect = BoundingRectForArrow(rect);
462 const gfx::PointF center = gfx::RectF(bounding_rect).CenterPoint();
463 SkPath path;
464 SkMatrix transform;
465 transform.setIdentity();
466 if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
467 int arrow_altitude = bounding_rect.height() / 2 + 1;
468 path.moveTo(bounding_rect.x(), bounding_rect.bottom());
469 path.rLineTo(bounding_rect.width(), 0);
470 path.rLineTo(-bounding_rect.width() / 2.0f, -arrow_altitude);
471 path.close();
472 path.offset(0, -arrow_altitude / 2 + 1);
473 if (direction == kScrollbarDownArrow) {
474 transform.setScale(1, -1, center.x(), center.y());
475 }
476 } else {
477 int arrow_altitude = bounding_rect.width() / 2 + 1;
478 path.moveTo(bounding_rect.x(), bounding_rect.y());
479 path.rLineTo(0, bounding_rect.height());
480 path.rLineTo(arrow_altitude, -bounding_rect.height() / 2.0f);
481 path.close();
482 path.offset(arrow_altitude / 2, 0);
483 if (direction == kScrollbarLeftArrow) {
484 transform.setScale(-1, 1, center.x(), center.y());
485 }
486 }
487 path.transform(transform);
488
489 return path;
490 }
491
BoundingRectForArrow(const gfx::Rect & rect) const492 gfx::Rect NativeThemeBase::BoundingRectForArrow(const gfx::Rect& rect) const {
493 const std::pair<int, int> rect_sides =
494 std::minmax(rect.width(), rect.height());
495 const int side_length_inset = 2 * std::ceil(rect_sides.second / 4.f);
496 const int side_length =
497 std::min(rect_sides.first, rect_sides.second - side_length_inset);
498 // When there are an odd number of pixels, put the extra on the top/left.
499 return gfx::Rect(rect.x() + (rect.width() - side_length + 1) / 2,
500 rect.y() + (rect.height() - side_length + 1) / 2,
501 side_length, side_length);
502 }
503
PaintScrollbarTrack(cc::PaintCanvas * canvas,Part part,State state,const ScrollbarTrackExtraParams & extra_params,const gfx::Rect & rect,ColorScheme color_scheme) const504 void NativeThemeBase::PaintScrollbarTrack(
505 cc::PaintCanvas* canvas,
506 Part part,
507 State state,
508 const ScrollbarTrackExtraParams& extra_params,
509 const gfx::Rect& rect,
510 ColorScheme color_scheme) const {
511 cc::PaintFlags flags;
512 SkIRect skrect;
513
514 skrect.setLTRB(rect.x(), rect.y(), rect.right(), rect.bottom());
515 SkScalar track_hsv[3];
516 SkColorToHSV(GetColor(kTrackColor, color_scheme), track_hsv);
517 flags.setColor(SaturateAndBrighten(track_hsv, 0, 0));
518 canvas->drawIRect(skrect, flags);
519
520 SkScalar thumb_hsv[3];
521 SkColorToHSV(GetColor(kThumbInactiveColor, color_scheme), thumb_hsv);
522
523 flags.setColor(OutlineColor(track_hsv, thumb_hsv));
524 DrawBox(canvas, rect, flags);
525 }
526
PaintScrollbarThumb(cc::PaintCanvas * canvas,Part part,State state,const gfx::Rect & rect,ScrollbarOverlayColorTheme,ColorScheme color_scheme) const527 void NativeThemeBase::PaintScrollbarThumb(cc::PaintCanvas* canvas,
528 Part part,
529 State state,
530 const gfx::Rect& rect,
531 ScrollbarOverlayColorTheme,
532 ColorScheme color_scheme) const {
533 const bool hovered = state == kHovered;
534 const int midx = rect.x() + rect.width() / 2;
535 const int midy = rect.y() + rect.height() / 2;
536 const bool vertical = part == kScrollbarVerticalThumb;
537
538 SkScalar thumb[3];
539 SkColorToHSV(
540 GetColor(hovered ? kThumbActiveColor : kThumbInactiveColor, color_scheme),
541 thumb);
542
543 cc::PaintFlags flags;
544 flags.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
545
546 SkIRect skrect;
547 if (vertical)
548 skrect.setLTRB(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
549 else
550 skrect.setLTRB(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
551
552 canvas->drawIRect(skrect, flags);
553
554 flags.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
555
556 if (vertical) {
557 skrect.setLTRB(midx + 1, rect.y(), rect.x() + rect.width(),
558 rect.y() + rect.height());
559 } else {
560 skrect.setLTRB(rect.x(), midy + 1, rect.x() + rect.width(),
561 rect.y() + rect.height());
562 }
563
564 canvas->drawIRect(skrect, flags);
565
566 SkScalar track[3];
567 SkColorToHSV(GetColor(kTrackColor, color_scheme), track);
568 flags.setColor(OutlineColor(track, thumb));
569 DrawBox(canvas, rect, flags);
570
571 if (rect.height() > 10 && rect.width() > 10) {
572 const int grippy_half_width = 2;
573 const int inter_grippy_offset = 3;
574 if (vertical) {
575 DrawHorizLine(canvas, midx - grippy_half_width, midx + grippy_half_width,
576 midy - inter_grippy_offset, flags);
577 DrawHorizLine(canvas, midx - grippy_half_width, midx + grippy_half_width,
578 midy, flags);
579 DrawHorizLine(canvas, midx - grippy_half_width, midx + grippy_half_width,
580 midy + inter_grippy_offset, flags);
581 } else {
582 DrawVertLine(canvas, midx - inter_grippy_offset, midy - grippy_half_width,
583 midy + grippy_half_width, flags);
584 DrawVertLine(canvas, midx, midy - grippy_half_width,
585 midy + grippy_half_width, flags);
586 DrawVertLine(canvas, midx + inter_grippy_offset, midy - grippy_half_width,
587 midy + grippy_half_width, flags);
588 }
589 }
590 }
591
PaintScrollbarCorner(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,ColorScheme color_scheme) const592 void NativeThemeBase::PaintScrollbarCorner(cc::PaintCanvas* canvas,
593 State state,
594 const gfx::Rect& rect,
595 ColorScheme color_scheme) const {}
596
PaintCheckbox(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,ColorScheme color_scheme) const597 void NativeThemeBase::PaintCheckbox(cc::PaintCanvas* canvas,
598 State state,
599 const gfx::Rect& rect,
600 const ButtonExtraParams& button,
601 ColorScheme color_scheme) const {
602 if (features::IsFormControlsRefreshEnabled()) {
603 const float border_radius = GetBorderRadiusForPart(
604 kCheckbox, rect.width(), rect.height(), button.zoom);
605 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, true,
606 border_radius, color_scheme);
607
608 if (!skrect.isEmpty()) {
609 cc::PaintFlags flags;
610 flags.setAntiAlias(true);
611
612 if (button.indeterminate) {
613 // Draw the dash.
614 flags.setColor(ControlsBorderColorForState(state, color_scheme));
615 const auto indeterminate =
616 skrect.makeInset(skrect.width() * kIndeterminateInsetWidthRatio,
617 skrect.height() * kIndeterminateInsetHeightRatio);
618 flags.setStyle(cc::PaintFlags::kFill_Style);
619 canvas->drawRoundRect(indeterminate, border_radius, border_radius,
620 flags);
621 } else if (button.checked) {
622 // Draw the accent background.
623 flags.setStyle(cc::PaintFlags::kFill_Style);
624 flags.setColor(ControlsAccentColorForState(state, color_scheme));
625 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
626
627 // Draw the checkmark.
628 SkPath check;
629 check.moveTo(skrect.x() + skrect.width() * 0.2, skrect.centerY());
630 check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
631 check.lineTo(skrect.right() - skrect.width() * 0.2,
632 skrect.y() + skrect.height() * 0.2);
633 flags.setStyle(cc::PaintFlags::kStroke_Style);
634 flags.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.16));
635 SkColor checkmark_color =
636 ControlsBackgroundColorForState(state, color_scheme);
637 flags.setColor(checkmark_color);
638 canvas->drawPath(check, flags);
639 }
640 }
641 return;
642 }
643
644 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, true,
645 SkIntToScalar(2), color_scheme);
646 if (!skrect.isEmpty()) {
647 // Draw the checkmark / dash.
648 cc::PaintFlags flags;
649 flags.setAntiAlias(true);
650 flags.setStyle(cc::PaintFlags::kStroke_Style);
651 flags.setColor(GetColor(state == kDisabled ? kCheckboxStrokeDisabledColor
652 : kCheckboxStrokeColor,
653 color_scheme));
654 if (button.indeterminate) {
655 SkPath dash;
656 dash.moveTo(skrect.x() + skrect.width() * 0.16,
657 (skrect.y() + skrect.bottom()) / 2);
658 dash.rLineTo(skrect.width() * 0.68, 0);
659 flags.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.2));
660 canvas->drawPath(dash, flags);
661 } else if (button.checked) {
662 SkPath check;
663 check.moveTo(skrect.x() + skrect.width() * 0.2,
664 skrect.y() + skrect.height() * 0.5);
665 check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
666 flags.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.23));
667 check.lineTo(skrect.right() - skrect.width() * 0.2,
668 skrect.y() + skrect.height() * 0.2);
669 canvas->drawPath(check, flags);
670 }
671 }
672 }
673
674 // Draws the common elements of checkboxes and radio buttons.
675 // Returns the rectangle within which any additional decorations should be
676 // drawn, or empty if none.
PaintCheckboxRadioCommon(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,bool is_checkbox,const SkScalar border_radius,ColorScheme color_scheme) const677 SkRect NativeThemeBase::PaintCheckboxRadioCommon(
678 cc::PaintCanvas* canvas,
679 State state,
680 const gfx::Rect& rect,
681 const ButtonExtraParams& button,
682 bool is_checkbox,
683 const SkScalar border_radius,
684 ColorScheme color_scheme) const {
685 if (features::IsFormControlsRefreshEnabled()) {
686 SkRect skrect = gfx::RectToSkRect(rect);
687
688 // Use the largest square that fits inside the provided rectangle.
689 // No other browser seems to support non-square widget, so accidentally
690 // having non-square sizes is common (eg. amazon and webkit dev tools).
691 if (skrect.width() != skrect.height()) {
692 SkScalar size = std::min(skrect.width(), skrect.height());
693 skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
694 }
695
696 // If the rectangle is too small then paint only a rectangle. We don't want
697 // to have to worry about '- 1' and '+ 1' calculations below having overflow
698 // or underflow.
699 if (skrect.width() <= 2) {
700 cc::PaintFlags flags;
701 flags.setColor(GetControlColor(kBorder, color_scheme));
702 flags.setStyle(cc::PaintFlags::kFill_Style);
703 canvas->drawRect(skrect, flags);
704 // Too small to draw anything more.
705 return SkRect::MakeEmpty();
706 }
707
708 cc::PaintFlags flags;
709 flags.setAntiAlias(true);
710
711 // Paint the background (is not visible behind the rounded corners).
712 // Note we need to shrink the rect for background a little bit so we don't
713 // see artifacts introduced by antialiasing between the border and the
714 // background near the rounded corners of checkbox.
715 const auto background_rect =
716 skrect.makeInset(kBorderWidth * 0.2f, kBorderWidth * 0.2f);
717 PaintLightenLayer(canvas, background_rect, state, border_radius,
718 color_scheme);
719 flags.setColor(ControlsBackgroundColorForState(state, color_scheme));
720 flags.setStyle(cc::PaintFlags::kFill_Style);
721 canvas->drawRoundRect(background_rect, border_radius, border_radius, flags);
722
723 // For checkbox the border is drawn only when it is unchecked or
724 // indeterminate. For radio the border is always drawn.
725 if (!(is_checkbox && button.checked && !button.indeterminate)) {
726 // Shrink half border width so the final pixels of the border will be
727 // within the rectangle.
728 const auto border_rect =
729 skrect.makeInset(kBorderWidth / 2, kBorderWidth / 2);
730 SkColor border_color =
731 (button.checked && !button.indeterminate)
732 ? ControlsAccentColorForState(state, color_scheme)
733 : ControlsBorderColorForState(state, color_scheme);
734 flags.setColor(border_color);
735 flags.setStyle(cc::PaintFlags::kStroke_Style);
736 flags.setStrokeWidth(kBorderWidth);
737 canvas->drawRoundRect(border_rect, border_radius, border_radius, flags);
738 }
739
740 // Return the rectangle for drawing any additional decorations.
741 return skrect;
742 }
743
744 SkRect skrect = gfx::RectToSkRect(rect);
745
746 // Use the largest square that fits inside the provided rectangle.
747 // No other browser seems to support non-square widget, so accidentally
748 // having non-square sizes is common (eg. amazon and webkit dev tools).
749 if (skrect.width() != skrect.height()) {
750 SkScalar size = std::min(skrect.width(), skrect.height());
751 skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
752 }
753
754 // If the rectangle is too small then paint only a rectangle. We don't want
755 // to have to worry about '- 1' and '+ 1' calculations below having overflow
756 // or underflow.
757 if (skrect.width() <= 2) {
758 cc::PaintFlags flags;
759 flags.setColor(GetColor(kCheckboxTinyColor, color_scheme));
760 flags.setStyle(cc::PaintFlags::kFill_Style);
761 canvas->drawRect(skrect, flags);
762 // Too small to draw anything more.
763 return SkRect::MakeEmpty();
764 }
765
766 // Make room for padding/drop shadow.
767 AdjustCheckboxRadioRectForPadding(&skrect);
768
769 // Draw the drop shadow below the widget.
770 if (state != kPressed) {
771 cc::PaintFlags flags;
772 flags.setAntiAlias(true);
773 SkRect shadow_rect = skrect;
774 shadow_rect.offset(0, 1);
775 if (state == kDisabled)
776 flags.setColor(GetColor(kCheckboxShadowDisabledColor, color_scheme));
777 else if (state == kHovered)
778 flags.setColor(GetColor(kCheckboxShadowHoveredColor, color_scheme));
779 else
780 flags.setColor(GetColor(kCheckboxShadowColor, color_scheme));
781 flags.setStyle(cc::PaintFlags::kFill_Style);
782 canvas->drawRoundRect(shadow_rect, border_radius, border_radius, flags);
783 }
784
785 // Draw the gradient-filled rectangle
786 SkPoint gradient_bounds[3];
787 gradient_bounds[0].set(skrect.x(), skrect.y());
788 gradient_bounds[1].set(skrect.x(), skrect.y() + skrect.height() * 0.38);
789 gradient_bounds[2].set(skrect.x(), skrect.bottom());
790 SkColor start_color;
791 SkColor end_color;
792 if (state == kPressed) {
793 start_color = GetColor(kCheckboxPressedGradientStartColor, color_scheme);
794 end_color = GetColor(kCheckboxPressedGradientEndColor, color_scheme);
795 } else if (state == kHovered) {
796 start_color = GetColor(kCheckboxHoveredGradientStartColor, color_scheme);
797 end_color = GetColor(kCheckboxHoveredGradientEndColor, color_scheme);
798 } else /* kNormal or kDisabled */ {
799 start_color = GetColor(kCheckboxGradientStartColor, color_scheme);
800 end_color = GetColor(kCheckboxGradientEndColor, color_scheme);
801 if (state == kDisabled) {
802 start_color = SkColorSetA(start_color, kCheckboxDisabledGradientAlpha);
803 end_color = SkColorSetA(end_color, kCheckboxDisabledGradientAlpha);
804 }
805 }
806 SkColor colors[3] = {start_color, start_color, end_color};
807 cc::PaintFlags flags;
808 flags.setAntiAlias(true);
809 flags.setShader(cc::PaintShader::MakeLinearGradient(
810 gradient_bounds, colors, nullptr, 3, SkTileMode::kClamp));
811 flags.setStyle(cc::PaintFlags::kFill_Style);
812 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
813 flags.setShader(nullptr);
814
815 // Draw the border.
816 if (state == kHovered)
817 flags.setColor(GetColor(kCheckboxBorderHoveredColor, color_scheme));
818 else if (state == kDisabled)
819 flags.setColor(GetColor(kCheckboxBorderDisabledColor, color_scheme));
820 else
821 flags.setColor(GetColor(kCheckboxBorderColor, color_scheme));
822 flags.setStyle(cc::PaintFlags::kStroke_Style);
823 flags.setStrokeWidth(SkIntToScalar(1));
824 skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
825 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
826
827 // Return the rectangle excluding the drop shadow for drawing any additional
828 // decorations.
829 return skrect;
830 }
831
PaintRadio(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,ColorScheme color_scheme) const832 void NativeThemeBase::PaintRadio(cc::PaintCanvas* canvas,
833 State state,
834 const gfx::Rect& rect,
835 const ButtonExtraParams& button,
836 ColorScheme color_scheme) const {
837 if (features::IsFormControlsRefreshEnabled()) {
838 // Most of a radio button is the same as a checkbox, except the the rounded
839 // square is a circle (i.e. border radius >= 100%).
840 const float border_radius = GetBorderRadiusForPart(
841 kRadio, rect.width(), rect.height(), button.zoom);
842 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, false,
843 border_radius, color_scheme);
844 if (!skrect.isEmpty() && button.checked) {
845 // Draw the dot.
846 cc::PaintFlags flags;
847 flags.setAntiAlias(true);
848 flags.setStyle(cc::PaintFlags::kFill_Style);
849 flags.setColor(ControlsAccentColorForState(state, color_scheme));
850
851 skrect.inset(skrect.width() * 0.2, skrect.height() * 0.2);
852 // Use drawRoundedRect instead of drawOval to be completely consistent
853 // with the border in PaintCheckboxRadioNewCommon.
854 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
855 }
856 return;
857 }
858
859 // Most of a radio button is the same as a checkbox, except the the rounded
860 // square is a circle (i.e. border radius >= 100%).
861 const SkScalar radius = SkFloatToScalar(
862 static_cast<float>(std::max(rect.width(), rect.height())) / 2);
863 SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, false,
864 radius, color_scheme);
865 if (!skrect.isEmpty() && button.checked) {
866 // Draw the dot.
867 cc::PaintFlags flags;
868 flags.setAntiAlias(true);
869 flags.setStyle(cc::PaintFlags::kFill_Style);
870 flags.setColor(
871 GetColor(state == kDisabled ? kRadioDotDisabledColor : kRadioDotColor,
872 color_scheme));
873 skrect.inset(skrect.width() * 0.25, skrect.height() * 0.25);
874 // Use drawRoundedRect instead of drawOval to be completely consistent
875 // with the border in PaintCheckboxRadioNewCommon.
876 canvas->drawRoundRect(skrect, radius, radius, flags);
877 }
878 }
879
PaintButton(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,ColorScheme color_scheme) const880 void NativeThemeBase::PaintButton(cc::PaintCanvas* canvas,
881 State state,
882 const gfx::Rect& rect,
883 const ButtonExtraParams& button,
884 ColorScheme color_scheme) const {
885 if (features::IsFormControlsRefreshEnabled()) {
886 cc::PaintFlags flags;
887 SkRect skrect = gfx::RectToSkRect(rect);
888
889 flags.setAntiAlias(true);
890 flags.setStyle(cc::PaintFlags::kFill_Style);
891
892 // If the button is too small, fallback to drawing a single, solid color.
893 if (rect.width() < 5 || rect.height() < 5) {
894 flags.setColor(ButtonFillColorForState(state, color_scheme));
895 canvas->drawRect(skrect, flags);
896 return;
897 }
898
899 float border_radius = GetBorderRadiusForPart(kPushButton, rect.width(),
900 rect.height(), button.zoom);
901 // Paint the background (is not visible behind the rounded corners).
902 skrect.inset(kBorderWidth / 2, kBorderWidth / 2);
903 PaintLightenLayer(canvas, skrect, state, border_radius, color_scheme);
904 flags.setColor(ButtonFillColorForState(state, color_scheme));
905 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
906
907 // Paint the border: 1px solid.
908 if (button.has_border) {
909 flags.setStyle(cc::PaintFlags::kStroke_Style);
910 flags.setStrokeWidth(kBorderWidth);
911 flags.setColor(ButtonBorderColorForState(state, color_scheme));
912 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
913 }
914 return;
915 }
916
917 cc::PaintFlags flags;
918 SkRect skrect = gfx::RectToSkRect(rect);
919 SkColor base_color = button.background_color;
920
921 color_utils::HSL base_hsl;
922 color_utils::SkColorToHSL(base_color, &base_hsl);
923
924 // Our standard gradient is from 0xDD to 0xF8. This is the amount of
925 // increased luminance between those values.
926 SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
927
928 // If the button is too small, fallback to drawing a single, solid color
929 if (rect.width() < 5 || rect.height() < 5) {
930 flags.setColor(base_color);
931 canvas->drawRect(skrect, flags);
932 return;
933 }
934
935 flags.setColor(GetColor(kButtonBorderColor, color_scheme));
936 SkPoint gradient_bounds[2] = {
937 gfx::PointToSkPoint(rect.origin()),
938 gfx::PointToSkPoint(rect.bottom_left() - gfx::Vector2d(0, 1))
939 };
940 if (state == kPressed)
941 std::swap(gradient_bounds[0], gradient_bounds[1]);
942 SkColor colors[2] = { light_color, base_color };
943
944 flags.setStyle(cc::PaintFlags::kFill_Style);
945 flags.setAntiAlias(true);
946 flags.setShader(cc::PaintShader::MakeLinearGradient(
947 gradient_bounds, colors, nullptr, 2, SkTileMode::kClamp));
948
949 canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), flags);
950 flags.setShader(nullptr);
951
952 if (button.has_border) {
953 int border_alpha = state == kHovered ? 0x80 : 0x55;
954 if (button.is_focused) {
955 border_alpha = 0xFF;
956 flags.setColor(GetSystemColor(kColorId_FocusedBorderColor, color_scheme));
957 }
958 flags.setStyle(cc::PaintFlags::kStroke_Style);
959 flags.setStrokeWidth(SkIntToScalar(1));
960 flags.setAlpha(border_alpha);
961 skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
962 canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), flags);
963 }
964 }
965
PaintTextField(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const TextFieldExtraParams & text,ColorScheme color_scheme) const966 void NativeThemeBase::PaintTextField(cc::PaintCanvas* canvas,
967 State state,
968 const gfx::Rect& rect,
969 const TextFieldExtraParams& text,
970 ColorScheme color_scheme) const {
971 if (features::IsFormControlsRefreshEnabled()) {
972 SkRect bounds = gfx::RectToSkRect(rect);
973 const SkScalar border_radius = GetBorderRadiusForPart(
974 kTextField, rect.width(), rect.height(), /*zoom level=*/1);
975
976 // Paint the background (is not visible behind the rounded corners).
977 bounds.inset(kBorderWidth / 2, kBorderWidth / 2);
978 cc::PaintFlags fill_flags;
979 fill_flags.setStyle(cc::PaintFlags::kFill_Style);
980 if (text.background_color != 0) {
981 PaintLightenLayer(canvas, bounds, state, border_radius, color_scheme);
982 SkColor text_field_background_color =
983 ControlsBackgroundColorForState(state, color_scheme);
984 if (text.auto_complete_active && state != kDisabled) {
985 text_field_background_color =
986 GetControlColor(kAutoCompleteBackground, color_scheme);
987 }
988 fill_flags.setColor(text_field_background_color);
989 canvas->drawRoundRect(bounds, border_radius, border_radius, fill_flags);
990 }
991
992 // Paint the border: 1px solid.
993 if (text.has_border) {
994 cc::PaintFlags stroke_flags;
995 stroke_flags.setColor(ControlsBorderColorForState(state, color_scheme));
996 stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
997 stroke_flags.setStrokeWidth(kBorderWidth);
998 canvas->drawRoundRect(bounds, border_radius, border_radius, stroke_flags);
999 }
1000
1001 return;
1002 }
1003
1004 SkRect bounds;
1005 bounds.setLTRB(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
1006
1007 cc::PaintFlags fill_flags;
1008 fill_flags.setStyle(cc::PaintFlags::kFill_Style);
1009 fill_flags.setColor(text.background_color);
1010 canvas->drawRect(bounds, fill_flags);
1011
1012 // Text INPUT, listbox SELECT, and TEXTAREA have consistent borders.
1013 // border: 1px solid #a9a9a9
1014 cc::PaintFlags stroke_flags;
1015 stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
1016 stroke_flags.setColor(GetColor(kTextBorderColor, color_scheme));
1017 canvas->drawRect(bounds, stroke_flags);
1018 }
1019
PaintMenuList(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const MenuListExtraParams & menu_list,ColorScheme color_scheme) const1020 void NativeThemeBase::PaintMenuList(cc::PaintCanvas* canvas,
1021 State state,
1022 const gfx::Rect& rect,
1023 const MenuListExtraParams& menu_list,
1024 ColorScheme color_scheme) const {
1025 if (features::IsFormControlsRefreshEnabled()) {
1026 // If a border radius is specified paint the background and the border of
1027 // the menulist, otherwise let the non-theming code paint the background
1028 // and the border of the control. The arrow (menulist button) is always
1029 // painted by the theming code.
1030 if (!menu_list.has_border_radius) {
1031 TextFieldExtraParams text_field = {0};
1032 text_field.background_color = menu_list.background_color;
1033 text_field.has_border = menu_list.has_border;
1034 PaintTextField(canvas, state, rect, text_field, color_scheme);
1035 }
1036
1037 // Paint the arrow.
1038 cc::PaintFlags flags;
1039 flags.setColor(menu_list.arrow_color);
1040 flags.setAntiAlias(true);
1041 flags.setStyle(cc::PaintFlags::kStroke_Style);
1042 flags.setStrokeWidth(kMenuListArrowStrokeWidth);
1043
1044 float arrow_width = menu_list.arrow_size;
1045 int arrow_height = arrow_width * 0.5;
1046 gfx::Rect arrow(menu_list.arrow_x, menu_list.arrow_y - (arrow_height / 2),
1047 arrow_width, arrow_height);
1048 arrow.Intersect(rect);
1049
1050 if (arrow_width != arrow.width() || arrow_height != arrow.height()) {
1051 // The arrow is clipped after being constrained to the paint rect so we
1052 // need to recalculate its size.
1053 int height_clip = arrow_height - arrow.height();
1054 int width_clip = arrow_width - arrow.width();
1055 if (height_clip > width_clip) {
1056 arrow.set_width(arrow.height() * 1.6);
1057 } else {
1058 arrow.set_height(arrow.width() * 0.6);
1059 }
1060 arrow.set_y(menu_list.arrow_y - (arrow.height() / 2));
1061 }
1062
1063 SkPath path;
1064 path.moveTo(arrow.x(), arrow.y());
1065 path.lineTo(arrow.x() + arrow.width() / 2, arrow.y() + arrow.height());
1066 path.lineTo(arrow.x() + arrow.width(), arrow.y());
1067 canvas->drawPath(path, flags);
1068 return;
1069 }
1070
1071 // If a border radius is specified, we let the WebCore paint the background
1072 // and the border of the control.
1073 if (!menu_list.has_border_radius) {
1074 ButtonExtraParams button = { 0 };
1075 button.background_color = menu_list.background_color;
1076 button.has_border = menu_list.has_border;
1077 PaintButton(canvas, state, rect, button, color_scheme);
1078 }
1079
1080 cc::PaintFlags flags;
1081 flags.setColor(menu_list.arrow_color);
1082 flags.setAntiAlias(true);
1083 flags.setStyle(cc::PaintFlags::kFill_Style);
1084
1085 int arrow_size = menu_list.arrow_size;
1086 gfx::Rect arrow(
1087 menu_list.arrow_x,
1088 menu_list.arrow_y - (arrow_size / 2),
1089 arrow_size,
1090 arrow_size);
1091
1092 // Constrain to the paint rect.
1093 arrow.Intersect(rect);
1094
1095 SkPath path;
1096 path.moveTo(arrow.x(), arrow.y());
1097 path.lineTo(arrow.right(), arrow.y());
1098 path.lineTo(arrow.x() + arrow.width() / 2, arrow.bottom());
1099 path.close();
1100 canvas->drawPath(path, flags);
1101 }
1102
PaintMenuPopupBackground(cc::PaintCanvas * canvas,const gfx::Size & size,const MenuBackgroundExtraParams & menu_background,ColorScheme color_scheme) const1103 void NativeThemeBase::PaintMenuPopupBackground(
1104 cc::PaintCanvas* canvas,
1105 const gfx::Size& size,
1106 const MenuBackgroundExtraParams& menu_background,
1107 ColorScheme color_scheme) const {
1108 // We are currently only painting kMenuPopupBackground with the kDefault
1109 // scheme. If that changes, we need to add an appropriate dark scheme color to
1110 // kMenuPopupBackgroundColor.
1111 DCHECK(color_scheme == ColorScheme::kDefault);
1112 canvas->drawColor(GetColor(kMenuPopupBackgroundColor, color_scheme),
1113 SkBlendMode::kSrc);
1114 }
1115
PaintMenuItemBackground(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const MenuItemExtraParams & menu_item,ColorScheme color_scheme) const1116 void NativeThemeBase::PaintMenuItemBackground(
1117 cc::PaintCanvas* canvas,
1118 State state,
1119 const gfx::Rect& rect,
1120 const MenuItemExtraParams& menu_item,
1121 ColorScheme color_scheme) const {
1122 // By default don't draw anything over the normal background.
1123 }
1124
PaintMenuSeparator(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const MenuSeparatorExtraParams & menu_separator,ColorScheme color_scheme) const1125 void NativeThemeBase::PaintMenuSeparator(
1126 cc::PaintCanvas* canvas,
1127 State state,
1128 const gfx::Rect& rect,
1129 const MenuSeparatorExtraParams& menu_separator,
1130 ColorScheme color_scheme) const {
1131 cc::PaintFlags flags;
1132 flags.setColor(GetSystemColor(ui::NativeTheme::kColorId_MenuSeparatorColor,
1133 color_scheme));
1134 canvas->drawRect(gfx::RectToSkRect(*menu_separator.paint_rect), flags);
1135 }
1136
PaintSliderTrack(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const SliderExtraParams & slider,ColorScheme color_scheme) const1137 void NativeThemeBase::PaintSliderTrack(cc::PaintCanvas* canvas,
1138 State state,
1139 const gfx::Rect& rect,
1140 const SliderExtraParams& slider,
1141 ColorScheme color_scheme) const {
1142 if (features::IsFormControlsRefreshEnabled()) {
1143 // Paint the entire slider track.
1144 cc::PaintFlags flags;
1145 flags.setAntiAlias(true);
1146 flags.setColor(ControlsFillColorForState(state, color_scheme));
1147 const float track_height = kSliderTrackHeight * slider.zoom;
1148 SkRect track_rect = AlignSliderTrack(rect, slider, false, track_height);
1149 // Shrink the track by 1 pixel so the thumb can completely cover the track
1150 // on both ends.
1151 if (slider.vertical)
1152 track_rect.inset(0, 1);
1153 else
1154 track_rect.inset(1, 0);
1155 float border_radius = GetBorderRadiusForPart(kSliderTrack, rect.width(),
1156 rect.height(), slider.zoom);
1157 canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1158
1159 // Clip the track to create rounded corners for the value bar.
1160 SkRRect rounded_rect;
1161 rounded_rect.setRectXY(track_rect, border_radius, border_radius);
1162 canvas->clipRRect(rounded_rect, SkClipOp::kIntersect, true);
1163
1164 // Paint the value slider track.
1165 flags.setColor(ControlsSliderColorForState(state, color_scheme));
1166 SkRect value_rect = AlignSliderTrack(rect, slider, true, track_height);
1167 canvas->drawRect(value_rect, flags);
1168
1169 // Paint the border.
1170 flags.setStyle(cc::PaintFlags::kStroke_Style);
1171 flags.setStrokeWidth(kBorderWidth);
1172 SkColor border_color = ControlsBorderColorForState(state, color_scheme);
1173 if (!UsesHighContrastColors() && state != kDisabled &&
1174 color_scheme != ColorScheme::kDark)
1175 border_color = SkColorSetA(border_color, 0x80);
1176 flags.setColor(border_color);
1177 track_rect.inset(kBorderWidth / 2, kBorderWidth / 2);
1178 canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1179 return;
1180 }
1181
1182 const int kMidX = rect.x() + rect.width() / 2;
1183 const int kMidY = rect.y() + rect.height() / 2;
1184
1185 cc::PaintFlags flags;
1186 flags.setColor(GetColor(kSliderTrackBackgroundColor, color_scheme));
1187
1188 SkRect skrect;
1189 if (slider.vertical) {
1190 skrect.setLTRB(std::max(rect.x(), kMidX - 2), rect.y(),
1191 std::min(rect.right(), kMidX + 2), rect.bottom());
1192 } else {
1193 skrect.setLTRB(rect.x(), std::max(rect.y(), kMidY - 2), rect.right(),
1194 std::min(rect.bottom(), kMidY + 2));
1195 }
1196 canvas->drawRect(skrect, flags);
1197 }
1198
PaintSliderThumb(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const SliderExtraParams & slider,ColorScheme color_scheme) const1199 void NativeThemeBase::PaintSliderThumb(cc::PaintCanvas* canvas,
1200 State state,
1201 const gfx::Rect& rect,
1202 const SliderExtraParams& slider,
1203 ColorScheme color_scheme) const {
1204 if (features::IsFormControlsRefreshEnabled()) {
1205 const float radius = GetBorderRadiusForPart(kSliderThumb, rect.width(),
1206 rect.height(), slider.zoom);
1207 SkRect thumb_rect = gfx::RectToSkRect(rect);
1208
1209 cc::PaintFlags flags;
1210 flags.setAntiAlias(true);
1211 SkScalar border_width = kSliderThumbBorderWidth;
1212 if (state == kHovered || state == kPressed) {
1213 border_width = kSliderThumbBorderHoveredWidth;
1214 }
1215
1216 // Paint the background (is not visible behind the rounded corners).
1217 thumb_rect.inset(border_width / 2, border_width / 2);
1218 flags.setColor(ControlsSliderColorForState(state, color_scheme));
1219 flags.setStyle(cc::PaintFlags::kFill_Style);
1220 canvas->drawRoundRect(thumb_rect, radius, radius, flags);
1221 return;
1222 }
1223
1224 const bool hovered = (state == kHovered) || slider.in_drag;
1225 const int kMidX = rect.x() + rect.width() / 2;
1226 const int kMidY = rect.y() + rect.height() / 2;
1227
1228 cc::PaintFlags flags;
1229 flags.setColor(GetColor(
1230 hovered ? kSliderThumbHoveredBrightColor : kSliderThumbBrightColor,
1231 color_scheme));
1232
1233 SkIRect skrect;
1234 if (slider.vertical)
1235 skrect.setLTRB(rect.x(), rect.y(), kMidX + 1, rect.bottom());
1236 else
1237 skrect.setLTRB(rect.x(), rect.y(), rect.right(), kMidY + 1);
1238
1239 canvas->drawIRect(skrect, flags);
1240
1241 flags.setColor(GetColor(
1242 hovered ? kSliderThumbHoveredShadedColor : kSliderThumbShadedColor,
1243 color_scheme));
1244
1245 if (slider.vertical)
1246 skrect.setLTRB(kMidX + 1, rect.y(), rect.right(), rect.bottom());
1247 else
1248 skrect.setLTRB(rect.x(), kMidY + 1, rect.right(), rect.bottom());
1249
1250 canvas->drawIRect(skrect, flags);
1251
1252 flags.setColor(GetColor(kSliderThumbBorder, color_scheme));
1253 DrawBox(canvas, rect, flags);
1254
1255 if (rect.height() > 10 && rect.width() > 10) {
1256 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, flags);
1257 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, flags);
1258 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, flags);
1259 }
1260 }
1261
PaintInnerSpinButton(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const InnerSpinButtonExtraParams & spin_button,ColorScheme color_scheme) const1262 void NativeThemeBase::PaintInnerSpinButton(
1263 cc::PaintCanvas* canvas,
1264 State state,
1265 const gfx::Rect& rect,
1266 const InnerSpinButtonExtraParams& spin_button,
1267 ColorScheme color_scheme) const {
1268 if (spin_button.read_only)
1269 state = kDisabled;
1270
1271 State north_state = state;
1272 State south_state = state;
1273 if (spin_button.spin_up)
1274 south_state = south_state != kDisabled ? kNormal : kDisabled;
1275 else
1276 north_state = north_state != kDisabled ? kNormal : kDisabled;
1277
1278 gfx::Rect half = rect;
1279 half.set_height(rect.height() / 2);
1280 ScrollbarArrowExtraParams arrow = ScrollbarArrowExtraParams();
1281 arrow.zoom = 1.0;
1282 PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state, color_scheme,
1283 arrow);
1284
1285 half.set_y(rect.y() + rect.height() / 2);
1286 PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state, color_scheme,
1287 arrow);
1288 }
1289
PaintProgressBar(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ProgressBarExtraParams & progress_bar,ColorScheme color_scheme) const1290 void NativeThemeBase::PaintProgressBar(
1291 cc::PaintCanvas* canvas,
1292 State state,
1293 const gfx::Rect& rect,
1294 const ProgressBarExtraParams& progress_bar,
1295 ColorScheme color_scheme) const {
1296 if (features::IsFormControlsRefreshEnabled()) {
1297 DCHECK(!rect.IsEmpty());
1298
1299 // Paint the track.
1300 cc::PaintFlags flags;
1301 flags.setAntiAlias(true);
1302 flags.setStyle(cc::PaintFlags::kFill_Style);
1303 flags.setColor(GetControlColor(kFill, color_scheme));
1304 SliderExtraParams slider;
1305 slider.vertical = false;
1306 float track_height = rect.height() * kTrackHeightRatio;
1307 SkRect track_rect = AlignSliderTrack(rect, slider, false, track_height);
1308 float border_radius = GetBorderRadiusForPart(
1309 kProgressBar, rect.width(), rect.height(), /*zoom level=*/1);
1310 canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1311
1312 // Clip the track to create rounded corners for the value bar.
1313 SkRRect rounded_rect;
1314 rounded_rect.setRectXY(track_rect, border_radius, border_radius);
1315 canvas->clipRRect(rounded_rect, SkClipOp::kIntersect, true);
1316
1317 // Paint the progress value bar.
1318 const SkScalar kMinimumProgressValueWidth = 2;
1319 SkScalar adjusted_width = progress_bar.value_rect_width;
1320 if (adjusted_width > 0 && adjusted_width < kMinimumProgressValueWidth)
1321 adjusted_width = kMinimumProgressValueWidth;
1322 gfx::Rect original_value_rect(progress_bar.value_rect_x,
1323 progress_bar.value_rect_y, adjusted_width,
1324 progress_bar.value_rect_height);
1325 SkRect value_rect =
1326 AlignSliderTrack(original_value_rect, slider, false, track_height);
1327 flags.setColor(GetControlColor(kAccent, color_scheme));
1328 if (progress_bar.determinate) {
1329 canvas->drawRect(value_rect, flags);
1330 } else {
1331 canvas->drawRoundRect(value_rect, border_radius, border_radius, flags);
1332 }
1333
1334 // Paint the border.
1335 flags.setStyle(cc::PaintFlags::kStroke_Style);
1336 flags.setStrokeWidth(kBorderWidth);
1337 SkColor border_color = GetControlColor(kBorder, color_scheme);
1338 if (!UsesHighContrastColors() && color_scheme != ColorScheme::kDark)
1339 border_color = SkColorSetA(border_color, 0x80);
1340 flags.setColor(border_color);
1341 track_rect.inset(kBorderWidth / 2, kBorderWidth / 2);
1342 canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1343 return;
1344 }
1345
1346 DCHECK(!rect.IsEmpty());
1347
1348 canvas->drawColor(GetColor(kProgressBackgroundColor, color_scheme));
1349
1350 // Draw the tick marks. The spacing between the tick marks is adjusted to
1351 // evenly divide into the width.
1352 SkPath path;
1353 int stroke_width = std::max(1, rect.height() / 18);
1354 int tick_width = 16 * stroke_width;
1355 int ticks = rect.width() / tick_width + (rect.width() % tick_width ? 1 : 0);
1356 SkScalar tick_spacing = SkIntToScalar(rect.width()) / ticks;
1357 for (int i = 1; i < ticks; ++i) {
1358 path.moveTo(rect.x() + i * tick_spacing, rect.y());
1359 path.rLineTo(0, rect.height());
1360 }
1361 cc::PaintFlags stroke_flags;
1362 stroke_flags.setColor(GetColor(kProgressTickColor, color_scheme));
1363 stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
1364 stroke_flags.setStrokeWidth(stroke_width);
1365 canvas->drawPath(path, stroke_flags);
1366
1367 // Draw progress.
1368 gfx::Rect progress_rect(progress_bar.value_rect_x, progress_bar.value_rect_y,
1369 progress_bar.value_rect_width,
1370 progress_bar.value_rect_height);
1371 cc::PaintFlags progress_flags;
1372 progress_flags.setColor(GetColor(kProgressValueColor, color_scheme));
1373 progress_flags.setStyle(cc::PaintFlags::kFill_Style);
1374 canvas->drawRect(gfx::RectToSkRect(progress_rect), progress_flags);
1375
1376 // Draw the border.
1377 gfx::RectF border_rect(rect);
1378 border_rect.Inset(stroke_width / 2.0f, stroke_width / 2.0f);
1379 stroke_flags.setColor(GetColor(kProgressBorderColor, color_scheme));
1380 canvas->drawRect(gfx::RectFToSkRect(border_rect), stroke_flags);
1381 }
1382
PaintFrameTopArea(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const FrameTopAreaExtraParams & frame_top_area,ColorScheme color_scheme) const1383 void NativeThemeBase::PaintFrameTopArea(
1384 cc::PaintCanvas* canvas,
1385 State state,
1386 const gfx::Rect& rect,
1387 const FrameTopAreaExtraParams& frame_top_area,
1388 ColorScheme color_scheme) const {
1389 cc::PaintFlags flags;
1390 flags.setColor(frame_top_area.default_background_color);
1391 canvas->drawRect(gfx::RectToSkRect(rect), flags);
1392 }
1393
AdjustCheckboxRadioRectForPadding(SkRect * rect) const1394 void NativeThemeBase::AdjustCheckboxRadioRectForPadding(SkRect* rect) const {
1395 // By default we only take 1px from right and bottom for the drop shadow.
1396 rect->setLTRB(static_cast<int>(rect->x()), static_cast<int>(rect->y()),
1397 static_cast<int>(rect->right()) - 1,
1398 static_cast<int>(rect->bottom()) - 1);
1399 }
1400
SaturateAndBrighten(SkScalar * hsv,SkScalar saturate_amount,SkScalar brighten_amount) const1401 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
1402 SkScalar saturate_amount,
1403 SkScalar brighten_amount) const {
1404 SkScalar color[3];
1405 color[0] = hsv[0];
1406 color[1] =
1407 base::ClampToRange(hsv[1] + saturate_amount, SkScalar{0}, SK_Scalar1);
1408 color[2] =
1409 base::ClampToRange(hsv[2] + brighten_amount, SkScalar{0}, SK_Scalar1);
1410 return SkHSVToColor(color);
1411 }
1412
GetArrowColor(State state,ColorScheme color_scheme) const1413 SkColor NativeThemeBase::GetArrowColor(State state,
1414 ColorScheme color_scheme) const {
1415 if (state != kDisabled)
1416 return GetColor(kArrowDisabledColor, color_scheme);
1417
1418 SkScalar track_hsv[3];
1419 SkColorToHSV(GetColor(kTrackColor, color_scheme), track_hsv);
1420
1421 SkScalar thumb_hsv[3];
1422 SkColorToHSV(GetControlColor(kScrollbarThumbInactive, color_scheme),
1423 thumb_hsv);
1424 return OutlineColor(track_hsv, thumb_hsv);
1425 }
1426
DrawVertLine(cc::PaintCanvas * canvas,int x,int y1,int y2,const cc::PaintFlags & flags) const1427 void NativeThemeBase::DrawVertLine(cc::PaintCanvas* canvas,
1428 int x,
1429 int y1,
1430 int y2,
1431 const cc::PaintFlags& flags) const {
1432 SkIRect skrect;
1433 skrect.setLTRB(x, y1, x + 1, y2 + 1);
1434 canvas->drawIRect(skrect, flags);
1435 }
1436
DrawHorizLine(cc::PaintCanvas * canvas,int x1,int x2,int y,const cc::PaintFlags & flags) const1437 void NativeThemeBase::DrawHorizLine(cc::PaintCanvas* canvas,
1438 int x1,
1439 int x2,
1440 int y,
1441 const cc::PaintFlags& flags) const {
1442 SkIRect skrect;
1443 skrect.setLTRB(x1, y, x2 + 1, y + 1);
1444 canvas->drawIRect(skrect, flags);
1445 }
1446
DrawBox(cc::PaintCanvas * canvas,const gfx::Rect & rect,const cc::PaintFlags & flags) const1447 void NativeThemeBase::DrawBox(cc::PaintCanvas* canvas,
1448 const gfx::Rect& rect,
1449 const cc::PaintFlags& flags) const {
1450 const int right = rect.x() + rect.width() - 1;
1451 const int bottom = rect.y() + rect.height() - 1;
1452 DrawHorizLine(canvas, rect.x(), right, rect.y(), flags);
1453 DrawVertLine(canvas, right, rect.y(), bottom, flags);
1454 DrawHorizLine(canvas, rect.x(), right, bottom, flags);
1455 DrawVertLine(canvas, rect.x(), rect.y(), bottom, flags);
1456 }
1457
OutlineColor(SkScalar * hsv1,SkScalar * hsv2) const1458 SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
1459 // GTK Theme engines have way too much control over the layout of
1460 // the scrollbar. We might be able to more closely approximate its
1461 // look-and-feel, if we sent whole images instead of just colors
1462 // from the browser to the renderer. But even then, some themes
1463 // would just break.
1464 //
1465 // So, instead, we don't even try to 100% replicate the look of
1466 // the native scrollbar. We render our own version, but we make
1467 // sure to pick colors that blend in nicely with the system GTK
1468 // theme. In most cases, we can just sample a couple of pixels
1469 // from the system scrollbar and use those colors to draw our
1470 // scrollbar.
1471 //
1472 // This works fine for the track color and the overall thumb
1473 // color. But it fails spectacularly for the outline color used
1474 // around the thumb piece. Not all themes have a clearly defined
1475 // outline. For some of them it is partially transparent, and for
1476 // others the thickness is very unpredictable.
1477 //
1478 // So, instead of trying to approximate the system theme, we
1479 // instead try to compute a reasonable looking choice based on the
1480 // known color of the track and the thumb piece. This is difficult
1481 // when trying to deal both with high- and low-contrast themes,
1482 // and both with positive and inverted themes.
1483 //
1484 // The following code has been tested to look OK with all of the
1485 // default GTK themes.
1486 SkScalar min_diff =
1487 base::ClampToRange((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
1488 SkScalar diff =
1489 base::ClampToRange(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
1490
1491 if (hsv1[2] + hsv2[2] > 1.0)
1492 diff = -diff;
1493
1494 return SaturateAndBrighten(hsv2, -0.2f, diff);
1495 }
1496
ControlsAccentColorForState(State state,ColorScheme color_scheme) const1497 SkColor NativeThemeBase::ControlsAccentColorForState(
1498 State state,
1499 ColorScheme color_scheme) const {
1500 ControlColorId color_id;
1501 if (state == kHovered) {
1502 color_id = kHoveredAccent;
1503 } else if (state == kPressed) {
1504 color_id = kPressedAccent;
1505 } else if (state == kDisabled) {
1506 color_id = kDisabledAccent;
1507 } else {
1508 color_id = kAccent;
1509 }
1510 return GetControlColor(color_id, color_scheme);
1511 }
1512
ControlsSliderColorForState(State state,ColorScheme color_scheme) const1513 SkColor NativeThemeBase::ControlsSliderColorForState(
1514 State state,
1515 ColorScheme color_scheme) const {
1516 ControlColorId color_id;
1517 if (state == kHovered) {
1518 color_id = kHoveredSlider;
1519 } else if (state == kPressed) {
1520 color_id = kPressedSlider;
1521 } else if (state == kDisabled) {
1522 color_id = kDisabledSlider;
1523 } else {
1524 color_id = kSlider;
1525 }
1526 return GetControlColor(color_id, color_scheme);
1527 }
1528
ControlsBorderColorForState(State state,ColorScheme color_scheme) const1529 SkColor NativeThemeBase::ControlsBorderColorForState(
1530 State state,
1531 ColorScheme color_scheme) const {
1532 ControlColorId color_id;
1533 if (state == kHovered) {
1534 color_id = kHoveredBorder;
1535 } else if (state == kPressed) {
1536 color_id = kPressedBorder;
1537 } else if (state == kDisabled) {
1538 color_id = kDisabledBorder;
1539 } else {
1540 color_id = kBorder;
1541 }
1542 return GetControlColor(color_id, color_scheme);
1543 }
1544
ButtonBorderColorForState(State state,ColorScheme color_scheme) const1545 SkColor NativeThemeBase::ButtonBorderColorForState(
1546 State state,
1547 ColorScheme color_scheme) const {
1548 ControlColorId color_id;
1549 if (state == kHovered) {
1550 color_id = kButtonHoveredBorder;
1551 } else if (state == kPressed) {
1552 color_id = kButtonPressedBorder;
1553 } else if (state == kDisabled) {
1554 color_id = kButtonDisabledBorder;
1555 } else {
1556 color_id = kButtonBorder;
1557 }
1558 return GetControlColor(color_id, color_scheme);
1559 }
1560
ControlsFillColorForState(State state,ColorScheme color_scheme) const1561 SkColor NativeThemeBase::ControlsFillColorForState(
1562 State state,
1563 ColorScheme color_scheme) const {
1564 ControlColorId color_id;
1565 if (state == kHovered) {
1566 color_id = kHoveredFill;
1567 } else if (state == kPressed) {
1568 color_id = kPressedFill;
1569 } else if (state == kDisabled) {
1570 color_id = kDisabledFill;
1571 } else {
1572 color_id = kFill;
1573 }
1574 return GetControlColor(color_id, color_scheme);
1575 }
1576
ButtonFillColorForState(State state,ColorScheme color_scheme) const1577 SkColor NativeThemeBase::ButtonFillColorForState(
1578 State state,
1579 ColorScheme color_scheme) const {
1580 ControlColorId color_id;
1581 if (state == kHovered) {
1582 color_id = kButtonHoveredFill;
1583 } else if (state == kPressed) {
1584 color_id = kButtonPressedFill;
1585 } else if (state == kDisabled) {
1586 color_id = kButtonDisabledFill;
1587 } else {
1588 color_id = kButtonFill;
1589 }
1590 return GetControlColor(color_id, color_scheme);
1591 }
1592
ControlsBackgroundColorForState(State state,ColorScheme color_scheme) const1593 SkColor NativeThemeBase::ControlsBackgroundColorForState(
1594 State state,
1595 ColorScheme color_scheme) const {
1596 ControlColorId color_id;
1597 if (state == kDisabled) {
1598 color_id = kDisabledBackground;
1599 } else {
1600 color_id = kBackground;
1601 }
1602 return GetControlColor(color_id, color_scheme);
1603 }
1604
GetControlColor(ControlColorId color_id,ColorScheme color_scheme) const1605 SkColor NativeThemeBase::GetControlColor(ControlColorId color_id,
1606 ColorScheme color_scheme) const {
1607 #if defined(OS_WIN)
1608 if (UsesHighContrastColors() && features::IsForcedColorsEnabled())
1609 return GetHighContrastControlColor(color_id, color_scheme);
1610 #endif
1611
1612 if(color_scheme == ColorScheme::kDark)
1613 return GetDarkModeControlColor(color_id);
1614
1615 switch (color_id) {
1616 case kBorder:
1617 case kButtonBorder:
1618 return SkColorSetRGB(0x76, 0x76, 0x76);
1619 case kHoveredBorder:
1620 case kButtonHoveredBorder:
1621 return SkColorSetRGB(0x4F, 0x4F, 0x4F);
1622 case kPressedBorder:
1623 case kButtonPressedBorder:
1624 return SkColorSetRGB(0x8D, 0x8D, 0x8D);
1625 case kDisabledBorder:
1626 case kButtonDisabledBorder:
1627 return SkColorSetARGB(0x4D, 0x76, 0x76, 0x76);
1628 case kAccent:
1629 return SkColorSetRGB(0x00, 0x75, 0xFF);
1630 case kHoveredAccent:
1631 return SkColorSetRGB(0x00, 0x5C, 0xC8);
1632 case kPressedAccent:
1633 return SkColorSetRGB(0x37, 0x93, 0xFF);
1634 case kDisabledAccent:
1635 return SkColorSetARGB(0x4D, 0x76, 0x76, 0x76);
1636 case kBackground:
1637 return SK_ColorWHITE;
1638 case kDisabledBackground:
1639 return SkColorSetA(SK_ColorWHITE, 0x99);
1640 case kFill:
1641 case kButtonFill:
1642 return SkColorSetRGB(0xEF, 0xEF, 0xEF);
1643 case kHoveredFill:
1644 case kButtonHoveredFill:
1645 return SkColorSetRGB(0xE5, 0xE5, 0xE5);
1646 case kPressedFill:
1647 case kButtonPressedFill:
1648 return SkColorSetRGB(0xF5, 0xF5, 0xF5);
1649 case kDisabledFill:
1650 case kButtonDisabledFill:
1651 return SkColorSetARGB(0x4D, 0xEF, 0xEF, 0xEF);
1652 case kLightenLayer:
1653 return SkColorSetARGB(0x33, 0xA9, 0xA9, 0xA9);
1654 case kProgressValue:
1655 return SkColorSetRGB(0x00, 0x75, 0xFF);
1656 case kSlider:
1657 return SkColorSetRGB(0x00, 0x75, 0xFF);
1658 case kHoveredSlider:
1659 return SkColorSetRGB(0x00, 0x5C, 0xC8);
1660 case kPressedSlider:
1661 return SkColorSetRGB(0x37, 0x93, 0xFF);
1662 case kDisabledSlider:
1663 return SkColorSetRGB(0xCB, 0xCB, 0xCB);
1664 case kAutoCompleteBackground:
1665 return SkColorSetRGB(0xE8, 0xF0, 0xFE);
1666 case kScrollbarArrowBackground:
1667 case kScrollbarTrack:
1668 return SkColorSetRGB(0xF1, 0xF1, 0xF1);
1669 case kScrollbarArrowBackgroundHovered:
1670 return SkColorSetRGB(0xD2, 0xD2, 0xD2);
1671 case kScrollbarArrowBackgroundPressed:
1672 return SkColorSetRGB(0x78, 0x78, 0x78);
1673 case kScrollbarArrowHovered:
1674 case kScrollbarArrow:
1675 return SkColorSetRGB(0x50, 0x50, 0x50);
1676 case kScrollbarArrowPressed:
1677 return SK_ColorWHITE;
1678 case kScrollbarThumbInactive:
1679 return SkColorSetRGB(0xEA, 0xEA, 0xEA);
1680 case kScrollbarThumbHovered:
1681 case kScrollbarThumbPressed:
1682 case kScrollbarThumb:
1683 return SK_ColorBLACK;
1684 }
1685 NOTREACHED();
1686 return gfx::kPlaceholderColor;
1687 }
1688
GetDarkModeControlColor(ControlColorId color_id) const1689 SkColor NativeThemeBase::GetDarkModeControlColor(
1690 ControlColorId color_id) const {
1691 switch (color_id) {
1692 case kAccent:
1693 return SkColorSetRGB(0x99, 0xC8, 0xFF);
1694 case kHoveredAccent:
1695 return SkColorSetRGB(0xD1, 0xE6, 0xFF);
1696 case kPressedAccent:
1697 return SkColorSetRGB(0x61, 0xA9, 0xFF);
1698 case kDisabledAccent:
1699 return SkColorSetRGB(0x75, 0x75, 0x75);
1700 case kProgressValue:
1701 return SkColorSetRGB(0x63, 0xAD, 0xE5);
1702 case kFill:
1703 return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1704 case kButtonBorder:
1705 case kButtonFill:
1706 return SkColorSetRGB(0x6B, 0x6B, 0x6B);
1707 case kLightenLayer:
1708 case kAutoCompleteBackground:
1709 case kBackground:
1710 return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1711 case kBorder:
1712 return SkColorSetRGB(0x85, 0x85, 0x85);
1713 case kSlider:
1714 return SkColorSetRGB(0x99, 0xC8, 0xFF);
1715 case kHoveredSlider:
1716 return SkColorSetRGB(0xD1, 0xE6, 0xFF);
1717 case kPressedSlider:
1718 return SkColorSetRGB(0x61, 0xA9, 0xFF);
1719 case kDisabledSlider:
1720 return SkColorSetRGB(0x75, 0x75, 0x75);
1721 case kDisabledBackground:
1722 return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1723 case kHoveredBorder:
1724 return SkColorSetRGB(0xAC, 0xAC, 0xAC);
1725 case kPressedBorder:
1726 return SkColorSetRGB(0x6E, 0x6E, 0x6E);
1727 case kDisabledBorder:
1728 return SkColorSetRGB(0x62, 0x62, 0x62);
1729 case kHoveredFill:
1730 return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1731 case kButtonHoveredBorder:
1732 case kButtonHoveredFill:
1733 return SkColorSetRGB(0x7B, 0x7B, 0x7B);
1734 case kPressedFill:
1735 return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1736 case kButtonPressedBorder:
1737 case kButtonPressedFill:
1738 return SkColorSetRGB(0x61, 0x61, 0x61);
1739 case kDisabledFill:
1740 case kButtonDisabledBorder:
1741 case kButtonDisabledFill:
1742 return SkColorSetRGB(0x36, 0x36, 0x36);
1743 case kScrollbarArrowBackground:
1744 return SkColorSetRGB(0x42, 0x42, 0x42);
1745 case kScrollbarArrowBackgroundHovered:
1746 return SkColorSetRGB(0x4F, 0x4F, 0x4F);
1747 case kScrollbarArrowBackgroundPressed:
1748 return SkColorSetRGB(0xB1, 0xB1, 0xB1);
1749 case kScrollbarArrowHovered:
1750 case kScrollbarArrow:
1751 return SK_ColorWHITE;
1752 case kScrollbarArrowPressed:
1753 return SK_ColorBLACK;
1754 case kScrollbarTrack:
1755 return SkColorSetRGB(0x42, 0x42, 0x42);
1756 case kScrollbarThumbInactive:
1757 case kScrollbarThumbHovered:
1758 case kScrollbarThumbPressed:
1759 case kScrollbarThumb:
1760 return SK_ColorWHITE;
1761 }
1762 NOTREACHED();
1763 return gfx::kPlaceholderColor;
1764 }
1765
GetHighContrastControlColor(ControlColorId color_id,ColorScheme color_scheme) const1766 SkColor NativeThemeBase::GetHighContrastControlColor(
1767 ControlColorId color_id,
1768 ColorScheme color_scheme) const {
1769 if (!system_colors_.empty()) {
1770 switch (color_id) {
1771 case kDisabledBorder:
1772 case kDisabledAccent:
1773 case kDisabledSlider:
1774 case kButtonDisabledBorder:
1775 return system_colors_[SystemThemeColor::kGrayText];
1776 case kBorder:
1777 case kHoveredBorder:
1778 case kPressedBorder:
1779 case kButtonBorder:
1780 case kButtonHoveredBorder:
1781 case kButtonPressedBorder:
1782 return system_colors_[SystemThemeColor::kButtonText];
1783 case kAccent:
1784 case kHoveredAccent:
1785 case kPressedAccent:
1786 case kProgressValue:
1787 case kSlider:
1788 case kHoveredSlider:
1789 case kPressedSlider:
1790 case kScrollbarThumbHovered:
1791 case kScrollbarThumbPressed:
1792 case kScrollbarArrowBackgroundHovered:
1793 case kScrollbarArrowBackgroundPressed:
1794 return system_colors_[SystemThemeColor::kHighlight];
1795 case kBackground:
1796 case kDisabledBackground:
1797 case kFill:
1798 case kHoveredFill:
1799 case kPressedFill:
1800 case kDisabledFill:
1801 case kButtonFill:
1802 case kButtonHoveredFill:
1803 case kButtonPressedFill:
1804 case kButtonDisabledFill:
1805 case kAutoCompleteBackground:
1806 case kLightenLayer:
1807 case kScrollbarArrowBackground:
1808 case kScrollbarTrack:
1809 return system_colors_[SystemThemeColor::kWindow];
1810 case kScrollbarArrow:
1811 case kScrollbarThumb:
1812 case kScrollbarThumbInactive:
1813 return system_colors_[SystemThemeColor::kWindowText];
1814 case kScrollbarArrowHovered:
1815 case kScrollbarArrowPressed:
1816 return system_colors_[SystemThemeColor::kButtonFace];
1817 }
1818 } else {
1819 // // Default high contrast colors (used in web test mode)
1820 switch (color_id) {
1821 case kDisabledBorder:
1822 case kDisabledAccent:
1823 case kDisabledSlider:
1824 case kButtonDisabledBorder:
1825 return SK_ColorGREEN;
1826 case kBorder:
1827 case kHoveredBorder:
1828 case kPressedBorder:
1829 case kButtonBorder:
1830 case kButtonHoveredBorder:
1831 case kButtonPressedBorder:
1832 case kScrollbarThumbInactive:
1833 case kScrollbarArrowBackground:
1834 case kScrollbarTrack:
1835 return SK_ColorWHITE;
1836 case kAccent:
1837 case kHoveredAccent:
1838 case kPressedAccent:
1839 case kProgressValue:
1840 case kSlider:
1841 case kHoveredSlider:
1842 case kPressedSlider:
1843 return SK_ColorCYAN;
1844 case kBackground:
1845 case kDisabledBackground:
1846 case kFill:
1847 case kHoveredFill:
1848 case kPressedFill:
1849 case kDisabledFill:
1850 case kButtonFill:
1851 case kButtonHoveredFill:
1852 case kButtonPressedFill:
1853 case kButtonDisabledFill:
1854 case kAutoCompleteBackground:
1855 case kLightenLayer:
1856 case kScrollbarThumb:
1857 case kScrollbarArrow:
1858 case kScrollbarArrowHovered:
1859 case kScrollbarArrowPressed:
1860 return SK_ColorBLACK;
1861 case kScrollbarThumbHovered:
1862 case kScrollbarThumbPressed:
1863 case kScrollbarArrowBackgroundHovered:
1864 case kScrollbarArrowBackgroundPressed:
1865 return SkColorSetRGB(0x1A, 0xEB, 0xFF);
1866 }
1867 }
1868 NOTREACHED();
1869 return gfx::kPlaceholderColor;
1870 }
1871
PaintLightenLayer(cc::PaintCanvas * canvas,SkRect skrect,State state,SkScalar border_radius,ColorScheme color_scheme) const1872 void NativeThemeBase::PaintLightenLayer(cc::PaintCanvas* canvas,
1873 SkRect skrect,
1874 State state,
1875 SkScalar border_radius,
1876 ColorScheme color_scheme) const {
1877 if (state == kDisabled) {
1878 cc::PaintFlags flags;
1879 flags.setAntiAlias(true);
1880 flags.setStyle(cc::PaintFlags::kFill_Style);
1881 // Draw the lighten layer to lighten the background so the translucent
1882 // disabled color works regardless of what it's over.
1883 flags.setColor(GetControlColor(kLightenLayer, color_scheme));
1884 canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
1885 }
1886 }
1887
AlignSliderTrack(const gfx::Rect & slider_rect,const NativeTheme::SliderExtraParams & slider,bool is_value,float track_height) const1888 SkRect NativeThemeBase::AlignSliderTrack(
1889 const gfx::Rect& slider_rect,
1890 const NativeTheme::SliderExtraParams& slider,
1891 bool is_value,
1892 float track_height) const {
1893 const float kAlignment = track_height / 2;
1894 const float mid_x = slider_rect.x() + slider_rect.width() / 2.0f;
1895 const float mid_y = slider_rect.y() + slider_rect.height() / 2.0f;
1896 SkRect aligned_rect;
1897
1898 if (slider.vertical) {
1899 const float top = is_value ? slider_rect.y() + slider.thumb_y + kAlignment
1900 : slider_rect.y();
1901 aligned_rect.setLTRB(
1902 std::max(float(slider_rect.x()), mid_x - kAlignment), top,
1903 std::min(float(slider_rect.right()), mid_x + kAlignment),
1904 slider_rect.bottom());
1905 } else {
1906 const float right = is_value && !slider.right_to_left
1907 ? slider_rect.x() + slider.thumb_x + kAlignment
1908 : slider_rect.right();
1909 const float left = is_value && slider.right_to_left
1910 ? slider_rect.x() + slider.thumb_x + kAlignment
1911 : slider_rect.x();
1912
1913 aligned_rect.setLTRB(
1914 left, std::max(float(slider_rect.y()), mid_y - kAlignment), right,
1915 std::min(float(slider_rect.bottom()), mid_y + kAlignment));
1916 }
1917
1918 return aligned_rect;
1919 }
1920
1921 } // namespace ui
1922