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