OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/base/native_theme/native_theme_android.h" | 5 #include "ui/base/native_theme/native_theme_android.h" |
6 | 6 |
7 #include <limits> | |
8 | |
9 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
10 #include "base/logging.h" | 8 #include "base/logging.h" |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "grit/ui_resources.h" | |
13 #include "third_party/skia/include/effects/SkGradientShader.h" | |
14 #include "ui/base/layout.h" | |
15 #include "ui/base/resource/resource_bundle.h" | |
16 #include "ui/gfx/canvas.h" | |
17 #include "ui/gfx/color_utils.h" | |
18 #include "ui/gfx/rect.h" | |
19 #include "ui/gfx/size.h" | |
20 | 9 |
21 namespace ui { | 10 namespace ui { |
22 | 11 |
23 const unsigned int kButtonLength = 14; | |
24 const unsigned int kScrollbarWidth = 15; | |
25 const unsigned int kThumbInactiveColor = 0xeaeaea; | |
26 const unsigned int kTrackColor= 0xd3d3d3; | |
27 | |
28 // These are the default dimensions of radio buttons and checkboxes. | |
29 const int kCheckboxAndRadioWidth = 13; | |
30 const int kCheckboxAndRadioHeight = 13; | |
31 | |
32 // These sizes match the sizes in Chromium Win. | |
33 const int kSliderThumbWidth = 11; | |
34 const int kSliderThumbHeight = 21; | |
35 | |
36 const SkColor kSliderTrackBackgroundColor = SkColorSetRGB(0xe3, 0xdd, 0xd8); | |
37 const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef); | |
38 const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0); | |
39 const SkColor kSliderThumbBorderDarkGrey = SkColorSetRGB(0x9d, 0x96, 0x8e); | |
40 | |
41 // Get lightness adjusted color. | |
42 SkColor BrightenColor(const color_utils::HSL& hsl, | |
43 SkAlpha alpha, | |
44 double lightness_amount) { | |
45 color_utils::HSL adjusted = hsl; | |
46 adjusted.l += lightness_amount; | |
47 if (adjusted.l > 1.0) | |
48 adjusted.l = 1.0; | |
49 if (adjusted.l < 0.0) | |
50 adjusted.l = 0.0; | |
51 | |
52 return color_utils::HSLToSkColor(adjusted, alpha); | |
53 } | |
54 | |
55 // static | 12 // static |
56 const NativeTheme* NativeTheme::instance() { | 13 const NativeTheme* NativeTheme::instance() { |
57 return NativeThemeAndroid::instance(); | 14 return NativeThemeAndroid::instance(); |
58 } | 15 } |
59 | 16 |
60 // static | 17 // static |
61 const NativeThemeAndroid* NativeThemeAndroid::instance() { | 18 const NativeThemeAndroid* NativeThemeAndroid::instance() { |
62 CR_DEFINE_STATIC_LOCAL(NativeThemeAndroid, s_native_theme, ()); | 19 CR_DEFINE_STATIC_LOCAL(NativeThemeAndroid, s_native_theme, ()); |
63 return &s_native_theme; | 20 return &s_native_theme; |
64 } | 21 } |
65 | 22 |
66 gfx::Size NativeThemeAndroid::GetPartSize(Part part, | |
67 State state, | |
68 const ExtraParams& extra) const { | |
69 switch (part) { | |
70 case kScrollbarDownArrow: | |
71 case kScrollbarUpArrow: | |
72 return gfx::Size(kScrollbarWidth, kButtonLength); | |
73 case kScrollbarLeftArrow: | |
74 case kScrollbarRightArrow: | |
75 return gfx::Size(kButtonLength, kScrollbarWidth); | |
76 case kCheckbox: | |
77 case kRadio: | |
78 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight); | |
79 case kSliderThumb: | |
80 // These sizes match the sizes in Chromium Win. | |
81 return gfx::Size(kSliderThumbWidth, kSliderThumbHeight); | |
82 case kInnerSpinButton: | |
83 return gfx::Size(kScrollbarWidth, 0); | |
84 case kPushButton: | |
85 case kTextField: | |
86 case kMenuList: | |
87 case kSliderTrack: | |
88 case kProgressBar: | |
89 return gfx::Size(); // No default size. | |
90 default: | |
91 NOTREACHED(); | |
92 } | |
93 return gfx::Size(); | |
94 } | |
95 | |
96 void NativeThemeAndroid::Paint(SkCanvas* canvas, | |
97 Part part, | |
98 State state, | |
99 const gfx::Rect& rect, | |
100 const ExtraParams& extra) const { | |
101 switch (part) { | |
102 case kScrollbarDownArrow: | |
103 case kScrollbarUpArrow: | |
104 case kScrollbarLeftArrow: | |
105 case kScrollbarRightArrow: | |
106 PaintArrowButton(canvas, rect, part, state); | |
107 break; | |
108 case kCheckbox: | |
109 PaintCheckbox(canvas, state, rect, extra.button); | |
110 break; | |
111 case kRadio: | |
112 PaintRadio(canvas, state, rect, extra.button); | |
113 break; | |
114 case kPushButton: | |
115 PaintButton(canvas, state, rect, extra.button); | |
116 break; | |
117 case kTextField: | |
118 PaintTextField(canvas, state, rect, extra.text_field); | |
119 break; | |
120 case kMenuList: | |
121 PaintMenuList(canvas, state, rect, extra.menu_list); | |
122 break; | |
123 case kSliderTrack: | |
124 PaintSliderTrack(canvas, state, rect, extra.slider); | |
125 break; | |
126 case kSliderThumb: | |
127 PaintSliderThumb(canvas, state, rect, extra.slider); | |
128 break; | |
129 case kInnerSpinButton: | |
130 PaintInnerSpinButton(canvas, state, rect, extra.inner_spin); | |
131 break; | |
132 case kProgressBar: | |
133 PaintProgressBar(canvas, state, rect, extra.progress_bar); | |
134 break; | |
135 default: | |
136 NOTREACHED(); | |
137 } | |
138 } | |
139 | |
140 SkColor NativeThemeAndroid::GetSystemColor(ColorId color_id) const { | 23 SkColor NativeThemeAndroid::GetSystemColor(ColorId color_id) const { |
141 NOTIMPLEMENTED(); | 24 NOTIMPLEMENTED(); |
142 return SK_ColorBLACK; | 25 return SK_ColorBLACK; |
143 } | 26 } |
144 | 27 |
145 NativeThemeAndroid::NativeThemeAndroid() { | 28 NativeThemeAndroid::NativeThemeAndroid() { |
146 } | 29 } |
147 | 30 |
148 NativeThemeAndroid::~NativeThemeAndroid() { | 31 NativeThemeAndroid::~NativeThemeAndroid() { |
149 } | 32 } |
150 | 33 |
151 void NativeThemeAndroid::PaintArrowButton(SkCanvas* canvas, | |
152 const gfx::Rect& rect, | |
153 Part direction, | |
154 State state) const { | |
155 int widthMiddle; | |
156 int lengthMiddle; | |
157 SkPaint paint; | |
158 if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) { | |
159 widthMiddle = rect.width() / 2 + 1; | |
160 lengthMiddle = rect.height() / 2 + 1; | |
161 } else { | |
162 lengthMiddle = rect.width() / 2 + 1; | |
163 widthMiddle = rect.height() / 2 + 1; | |
164 } | |
165 | |
166 // Calculate button color. | |
167 SkScalar trackHSV[3]; | |
168 SkColorToHSV(kTrackColor, trackHSV); | |
169 SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2); | |
170 SkColor backgroundColor = buttonColor; | |
171 if (state == kPressed) { | |
172 SkScalar buttonHSV[3]; | |
173 SkColorToHSV(buttonColor, buttonHSV); | |
174 buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1); | |
175 } else if (state == kHovered) { | |
176 SkScalar buttonHSV[3]; | |
177 SkColorToHSV(buttonColor, buttonHSV); | |
178 buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05); | |
179 } | |
180 | |
181 SkIRect skrect; | |
182 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() | |
183 + rect.height()); | |
184 // Paint the background (the area visible behind the rounded corners). | |
185 paint.setColor(backgroundColor); | |
186 canvas->drawIRect(skrect, paint); | |
187 | |
188 // Paint the button's outline and fill the middle | |
189 SkPath outline; | |
190 switch (direction) { | |
191 case kScrollbarUpArrow: | |
192 outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5); | |
193 outline.rLineTo(0, -(rect.height() - 2)); | |
194 outline.rLineTo(2, -2); | |
195 outline.rLineTo(rect.width() - 5, 0); | |
196 outline.rLineTo(2, 2); | |
197 outline.rLineTo(0, rect.height() - 2); | |
198 break; | |
199 case kScrollbarDownArrow: | |
200 outline.moveTo(rect.x() + 0.5, rect.y() - 0.5); | |
201 outline.rLineTo(0, rect.height() - 2); | |
202 outline.rLineTo(2, 2); | |
203 outline.rLineTo(rect.width() - 5, 0); | |
204 outline.rLineTo(2, -2); | |
205 outline.rLineTo(0, -(rect.height() - 2)); | |
206 break; | |
207 case kScrollbarRightArrow: | |
208 outline.moveTo(rect.x() - 0.5, rect.y() + 0.5); | |
209 outline.rLineTo(rect.width() - 2, 0); | |
210 outline.rLineTo(2, 2); | |
211 outline.rLineTo(0, rect.height() - 5); | |
212 outline.rLineTo(-2, 2); | |
213 outline.rLineTo(-(rect.width() - 2), 0); | |
214 break; | |
215 case kScrollbarLeftArrow: | |
216 outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5); | |
217 outline.rLineTo(-(rect.width() - 2), 0); | |
218 outline.rLineTo(-2, 2); | |
219 outline.rLineTo(0, rect.height() - 5); | |
220 outline.rLineTo(2, 2); | |
221 outline.rLineTo(rect.width() - 2, 0); | |
222 break; | |
223 default: | |
224 break; | |
225 } | |
226 outline.close(); | |
227 | |
228 paint.setStyle(SkPaint::kFill_Style); | |
229 paint.setColor(buttonColor); | |
230 canvas->drawPath(outline, paint); | |
231 | |
232 paint.setAntiAlias(true); | |
233 paint.setStyle(SkPaint::kStroke_Style); | |
234 SkScalar thumbHSV[3]; | |
235 SkColorToHSV(kThumbInactiveColor, thumbHSV); | |
236 paint.setColor(OutlineColor(trackHSV, thumbHSV)); | |
237 canvas->drawPath(outline, paint); | |
238 | |
239 // If the button is disabled or read-only, the arrow is drawn with the | |
240 // outline color. | |
241 if (state != kDisabled) | |
242 paint.setColor(SK_ColorBLACK); | |
243 | |
244 paint.setAntiAlias(false); | |
245 paint.setStyle(SkPaint::kFill_Style); | |
246 | |
247 SkPath path; | |
248 // The constants in this block of code are hand-tailored to produce good | |
249 // looking arrows without anti-aliasing. | |
250 switch (direction) { | |
251 case kScrollbarUpArrow: | |
252 path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle + 2); | |
253 path.rLineTo(7, 0); | |
254 path.rLineTo(-4, -4); | |
255 break; | |
256 case kScrollbarDownArrow: | |
257 path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle - 3); | |
258 path.rLineTo(7, 0); | |
259 path.rLineTo(-4, 4); | |
260 break; | |
261 case kScrollbarRightArrow: | |
262 path.moveTo(rect.x() + lengthMiddle - 3, rect.y() + widthMiddle - 4); | |
263 path.rLineTo(0, 7); | |
264 path.rLineTo(4, -4); | |
265 break; | |
266 case kScrollbarLeftArrow: | |
267 path.moveTo(rect.x() + lengthMiddle + 1, rect.y() + widthMiddle - 5); | |
268 path.rLineTo(0, 9); | |
269 path.rLineTo(-4, -4); | |
270 break; | |
271 default: | |
272 break; | |
273 } | |
274 path.close(); | |
275 | |
276 canvas->drawPath(path, paint); | |
277 } | |
278 | |
279 void NativeThemeAndroid::PaintCheckbox(SkCanvas* canvas, | |
280 State state, | |
281 const gfx::Rect& rect, | |
282 const ButtonExtraParams& button) const { | |
283 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
284 gfx::ImageSkia* image = NULL; | |
285 if (button.indeterminate) { | |
286 image = state == kDisabled ? | |
287 rb.GetImageSkiaNamed(IDR_CHECKBOX_DISABLED_INDETERMINATE) : | |
288 rb.GetImageSkiaNamed(IDR_CHECKBOX_INDETERMINATE); | |
289 } else if (button.checked) { | |
290 image = state == kDisabled ? | |
291 rb.GetImageSkiaNamed(IDR_CHECKBOX_DISABLED_ON) : | |
292 rb.GetImageSkiaNamed(IDR_CHECKBOX_ON); | |
293 } else { | |
294 image = state == kDisabled ? | |
295 rb.GetImageSkiaNamed(IDR_CHECKBOX_DISABLED_OFF) : | |
296 rb.GetImageSkiaNamed(IDR_CHECKBOX_OFF); | |
297 } | |
298 | |
299 gfx::Rect bounds = rect.Center(gfx::Size(image->width(), image->height())); | |
300 DrawImageInt(canvas, *image, 0, 0, image->width(), image->height(), | |
301 bounds.x(), bounds.y(), bounds.width(), bounds.height()); | |
302 } | |
303 | |
304 void NativeThemeAndroid::PaintRadio(SkCanvas* canvas, | |
305 State state, | |
306 const gfx::Rect& rect, | |
307 const ButtonExtraParams& button) const { | |
308 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
309 gfx::ImageSkia* image = NULL; | |
310 if (state == kDisabled) { | |
311 image = button.checked ? | |
312 rb.GetImageSkiaNamed(IDR_RADIO_DISABLED_ON) : | |
313 rb.GetImageSkiaNamed(IDR_RADIO_DISABLED_OFF); | |
314 } else { | |
315 image = button.checked ? | |
316 rb.GetImageSkiaNamed(IDR_RADIO_ON) : | |
317 rb.GetImageSkiaNamed(IDR_RADIO_OFF); | |
318 } | |
319 | |
320 gfx::Rect bounds = rect.Center(gfx::Size(image->width(), image->height())); | |
321 DrawImageInt(canvas, *image, 0, 0, image->width(), image->height(), | |
322 bounds.x(), bounds.y(), bounds.width(), bounds.height()); | |
323 } | |
324 | |
325 void NativeThemeAndroid::PaintButton(SkCanvas* canvas, | |
326 State state, | |
327 const gfx::Rect& rect, | |
328 const ButtonExtraParams& button) const { | |
329 SkPaint paint; | |
330 SkRect skrect; | |
331 int kRight = rect.right(); | |
332 int kBottom = rect.bottom(); | |
333 SkColor base_color = button.background_color; | |
334 | |
335 color_utils::HSL base_hsl; | |
336 color_utils::SkColorToHSL(base_color, &base_hsl); | |
337 | |
338 // Our standard gradient is from 0xdd to 0xf8. This is the amount of | |
339 // increased luminance between those values. | |
340 SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105)); | |
341 | |
342 // If the button is too small, fallback to drawing a single, solid color | |
343 if (rect.width() < 5 || rect.height() < 5) { | |
344 paint.setColor(base_color); | |
345 skrect.set(rect.x(), rect.y(), kRight, kBottom); | |
346 canvas->drawRect(skrect, paint); | |
347 return; | |
348 } | |
349 | |
350 if (button.has_border) { | |
351 int kBorderAlpha = state == kHovered ? 0x80 : 0x55; | |
352 paint.setARGB(kBorderAlpha, 0, 0, 0); | |
353 canvas->drawLine(rect.x() + 1, rect.y(), kRight - 1, rect.y(), paint); | |
354 canvas->drawLine(kRight - 1, rect.y() + 1, kRight - 1, kBottom - 1, paint); | |
355 canvas->drawLine(rect.x() + 1, kBottom - 1, kRight - 1, kBottom - 1, paint); | |
356 canvas->drawLine(rect.x(), rect.y() + 1, rect.x(), kBottom - 1, paint); | |
357 } | |
358 | |
359 paint.setColor(SK_ColorBLACK); | |
360 int kLightEnd = state == kPressed ? 1 : 0; | |
361 int kDarkEnd = !kLightEnd; | |
362 SkPoint gradient_bounds[2]; | |
363 gradient_bounds[kLightEnd].iset(rect.x(), rect.y()); | |
364 gradient_bounds[kDarkEnd].iset(rect.x(), kBottom - 1); | |
365 SkColor colors[2]; | |
366 colors[0] = light_color; | |
367 colors[1] = base_color; | |
368 | |
369 SkShader* shader = SkGradientShader::CreateLinear( | |
370 gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode, NULL); | |
371 paint.setStyle(SkPaint::kFill_Style); | |
372 paint.setShader(shader); | |
373 shader->unref(); | |
374 | |
375 if (button.has_border) { | |
376 skrect.set(rect.x() + 1, rect.y() + 1, kRight - 1, kBottom - 1); | |
377 } else { | |
378 skrect.set(rect.x(), rect.y(), kRight, kBottom); | |
379 } | |
380 canvas->drawRect(skrect, paint); | |
381 paint.setShader(NULL); | |
382 | |
383 if (button.has_border) { | |
384 paint.setColor(BrightenColor(base_hsl, SkColorGetA(base_color), -0.0588)); | |
385 canvas->drawPoint(rect.x() + 1, rect.y() + 1, paint); | |
386 canvas->drawPoint(kRight - 2, rect.y() + 1, paint); | |
387 canvas->drawPoint(rect.x() + 1, kBottom - 2, paint); | |
388 canvas->drawPoint(kRight - 2, kBottom - 2, paint); | |
389 } | |
390 } | |
391 | |
392 void NativeThemeAndroid::PaintTextField( | |
393 SkCanvas* canvas, | |
394 State state, | |
395 const gfx::Rect& rect, | |
396 const TextFieldExtraParams& text) const { | |
397 // The following drawing code simulates the user-agent css border for | |
398 // text area and text input so that we do not break layout tests. Once we | |
399 // have decided the desired looks, we should update the code here and | |
400 // the layout test expectations. | |
401 SkRect bounds; | |
402 bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1); | |
403 | |
404 SkPaint fill_paint; | |
405 fill_paint.setStyle(SkPaint::kFill_Style); | |
406 fill_paint.setColor(text.background_color); | |
407 canvas->drawRect(bounds, fill_paint); | |
408 | |
409 if (text.is_text_area) { | |
410 // Draw text area border: 1px solid black | |
411 SkPaint stroke_paint; | |
412 fill_paint.setStyle(SkPaint::kStroke_Style); | |
413 fill_paint.setColor(SK_ColorBLACK); | |
414 canvas->drawRect(bounds, fill_paint); | |
415 } else { | |
416 // Draw text input and listbox inset border | |
417 // Text Input: 2px inset #eee | |
418 // Listbox: 1px inset #808080 | |
419 SkColor kLightColor = text.is_listbox ? | |
420 SkColorSetRGB(0x80, 0x80, 0x80) : SkColorSetRGB(0xee, 0xee, 0xee); | |
421 SkColor kDarkColor = text.is_listbox ? | |
422 SkColorSetRGB(0x2c, 0x2c, 0x2c) : SkColorSetRGB(0x9a, 0x9a, 0x9a); | |
423 int kBorderWidth = text.is_listbox ? 1 : 2; | |
424 | |
425 SkPaint dark_paint; | |
426 dark_paint.setAntiAlias(true); | |
427 dark_paint.setStyle(SkPaint::kFill_Style); | |
428 dark_paint.setColor(kDarkColor); | |
429 | |
430 SkPaint light_paint; | |
431 light_paint.setAntiAlias(true); | |
432 light_paint.setStyle(SkPaint::kFill_Style); | |
433 light_paint.setColor(kLightColor); | |
434 | |
435 int left = rect.x(); | |
436 int top = rect.y(); | |
437 int right = rect.right(); | |
438 int bottom = rect.bottom(); | |
439 | |
440 SkPath path; | |
441 path.incReserve(4); | |
442 | |
443 // Top | |
444 path.moveTo(SkIntToScalar(left), SkIntToScalar(top)); | |
445 path.lineTo(SkIntToScalar(left + kBorderWidth), | |
446 SkIntToScalar(top + kBorderWidth)); | |
447 path.lineTo(SkIntToScalar(right - kBorderWidth), | |
448 SkIntToScalar(top + kBorderWidth)); | |
449 path.lineTo(SkIntToScalar(right), SkIntToScalar(top)); | |
450 canvas->drawPath(path, dark_paint); | |
451 | |
452 // Bottom | |
453 path.reset(); | |
454 path.moveTo(SkIntToScalar(left + kBorderWidth), | |
455 SkIntToScalar(bottom - kBorderWidth)); | |
456 path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom)); | |
457 path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom)); | |
458 path.lineTo(SkIntToScalar(right - kBorderWidth), | |
459 SkIntToScalar(bottom - kBorderWidth)); | |
460 canvas->drawPath(path, light_paint); | |
461 | |
462 // Left | |
463 path.reset(); | |
464 path.moveTo(SkIntToScalar(left), SkIntToScalar(top)); | |
465 path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom)); | |
466 path.lineTo(SkIntToScalar(left + kBorderWidth), | |
467 SkIntToScalar(bottom - kBorderWidth)); | |
468 path.lineTo(SkIntToScalar(left + kBorderWidth), | |
469 SkIntToScalar(top + kBorderWidth)); | |
470 canvas->drawPath(path, dark_paint); | |
471 | |
472 // Right | |
473 path.reset(); | |
474 path.moveTo(SkIntToScalar(right - kBorderWidth), | |
475 SkIntToScalar(top + kBorderWidth)); | |
476 path.lineTo(SkIntToScalar(right - kBorderWidth), SkIntToScalar(bottom)); | |
477 path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom)); | |
478 path.lineTo(SkIntToScalar(right), SkIntToScalar(top)); | |
479 canvas->drawPath(path, light_paint); | |
480 } | |
481 } | |
482 | |
483 void NativeThemeAndroid::PaintMenuList( | |
484 SkCanvas* canvas, | |
485 State state, | |
486 const gfx::Rect& rect, | |
487 const MenuListExtraParams& menu_list) const { | |
488 // If a border radius is specified, we let the WebCore paint the background | |
489 // and the border of the control. | |
490 if (!menu_list.has_border_radius) { | |
491 ButtonExtraParams button = { 0 }; | |
492 button.background_color = menu_list.background_color; | |
493 button.has_border = menu_list.has_border; | |
494 PaintButton(canvas, state, rect, button); | |
495 } | |
496 | |
497 SkPaint paint; | |
498 paint.setColor(SK_ColorBLACK); | |
499 paint.setAntiAlias(true); | |
500 paint.setStyle(SkPaint::kFill_Style); | |
501 | |
502 SkPath path; | |
503 path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3); | |
504 path.rLineTo(6, 0); | |
505 path.rLineTo(-3, 6); | |
506 path.close(); | |
507 canvas->drawPath(path, paint); | |
508 } | |
509 | |
510 void NativeThemeAndroid::PaintSliderTrack( | |
511 SkCanvas* canvas, | |
512 State state, | |
513 const gfx::Rect& rect, | |
514 const SliderExtraParams& slider) const { | |
515 int kMidX = rect.x() + rect.width() / 2; | |
516 int kMidY = rect.y() + rect.height() / 2; | |
517 | |
518 SkPaint paint; | |
519 paint.setColor(kSliderTrackBackgroundColor); | |
520 | |
521 SkRect skrect; | |
522 if (slider.vertical) { | |
523 skrect.set(std::max(rect.x(), kMidX - 2), | |
524 rect.y(), | |
525 std::min(rect.right(), kMidX + 2), | |
526 rect.bottom()); | |
527 } else { | |
528 skrect.set(rect.x(), | |
529 std::max(rect.y(), kMidY - 2), | |
530 rect.right(), | |
531 std::min(rect.bottom(), kMidY + 2)); | |
532 } | |
533 canvas->drawRect(skrect, paint); | |
534 } | |
535 | |
536 void NativeThemeAndroid::PaintSliderThumb( | |
537 SkCanvas* canvas, | |
538 State state, | |
539 const gfx::Rect& rect, | |
540 const SliderExtraParams& slider) const { | |
541 bool hovered = (state == kHovered) || slider.in_drag; | |
542 int kMidX = rect.x() + rect.width() / 2; | |
543 int kMidY = rect.y() + rect.height() / 2; | |
544 | |
545 SkPaint paint; | |
546 paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey); | |
547 | |
548 SkIRect skrect; | |
549 if (slider.vertical) | |
550 skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom()); | |
551 else | |
552 skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1); | |
553 | |
554 canvas->drawIRect(skrect, paint); | |
555 | |
556 paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey); | |
557 | |
558 if (slider.vertical) | |
559 skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom()); | |
560 else | |
561 skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom()); | |
562 | |
563 canvas->drawIRect(skrect, paint); | |
564 | |
565 paint.setColor(kSliderThumbBorderDarkGrey); | |
566 DrawBox(canvas, rect, paint); | |
567 | |
568 if (rect.height() > 10 && rect.width() > 10) { | |
569 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint); | |
570 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint); | |
571 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint); | |
572 } | |
573 } | |
574 | |
575 void NativeThemeAndroid::PaintInnerSpinButton( | |
576 SkCanvas* canvas, | |
577 State state, | |
578 const gfx::Rect& rect, | |
579 const InnerSpinButtonExtraParams& spin_button) const { | |
580 if (spin_button.read_only) | |
581 state = kDisabled; | |
582 | |
583 State north_state = state; | |
584 State south_state = state; | |
585 if (spin_button.spin_up) | |
586 south_state = south_state != kDisabled ? kNormal : kDisabled; | |
587 else | |
588 north_state = north_state != kDisabled ? kNormal : kDisabled; | |
589 | |
590 gfx::Rect half = rect; | |
591 half.set_height(rect.height() / 2); | |
592 PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state); | |
593 | |
594 half.set_y(rect.y() + rect.height() / 2); | |
595 PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state); | |
596 } | |
597 | |
598 void NativeThemeAndroid::PaintProgressBar( | |
599 SkCanvas* canvas, | |
600 State state, | |
601 const gfx::Rect& rect, | |
602 const ProgressBarExtraParams& progress_bar) const { | |
603 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
604 gfx::ImageSkia* bar_image = rb.GetImageSkiaNamed(IDR_PROGRESS_BAR); | |
605 gfx::ImageSkia* left_border_image = rb.GetImageSkiaNamed( | |
606 IDR_PROGRESS_BORDER_LEFT); | |
607 gfx::ImageSkia* right_border_image = rb.GetImageSkiaNamed( | |
608 IDR_PROGRESS_BORDER_RIGHT); | |
609 | |
610 float tile_scale = static_cast<float>(rect.height()) / | |
611 bar_image->height(); | |
612 | |
613 int new_tile_width = static_cast<int>(bar_image->width() * tile_scale); | |
614 float tile_scale_x = static_cast<float>(new_tile_width) / | |
615 bar_image->width(); | |
616 | |
617 DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale, | |
618 rect.x(), rect.y(), rect.width(), rect.height()); | |
619 | |
620 if (progress_bar.value_rect_width) { | |
621 gfx::ImageSkia* value_image = rb.GetImageSkiaNamed(IDR_PROGRESS_VALUE); | |
622 | |
623 new_tile_width = static_cast<int>(value_image->width() * tile_scale); | |
624 tile_scale_x = static_cast<float>(new_tile_width) / | |
625 value_image->width(); | |
626 | |
627 DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale, | |
628 progress_bar.value_rect_x, | |
629 progress_bar.value_rect_y, | |
630 progress_bar.value_rect_width, | |
631 progress_bar.value_rect_height); | |
632 } | |
633 | |
634 int dest_left_border_width = static_cast<int>(left_border_image->width() * | |
635 tile_scale); | |
636 DrawImageInt(canvas, *left_border_image, 0, 0, left_border_image->width(), | |
637 left_border_image->height(), rect.x(), rect.y(), dest_left_border_width, | |
638 rect.height()); | |
639 | |
640 int dest_right_border_width = static_cast<int>(right_border_image->width() * | |
641 tile_scale); | |
642 int dest_x = rect.right() - dest_right_border_width; | |
643 DrawImageInt(canvas, *right_border_image, 0, 0, right_border_image->width(), | |
644 right_border_image->height(), dest_x, rect.y(), dest_right_border_width, | |
645 rect.height()); | |
646 } | |
647 | |
648 bool NativeThemeAndroid::IntersectsClipRectInt(SkCanvas* canvas, | |
649 int x, | |
650 int y, | |
651 int w, | |
652 int h) const { | |
653 SkRect clip; | |
654 return canvas->getClipBounds(&clip) && | |
655 clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w), | |
656 SkIntToScalar(y + h)); | |
657 } | |
658 | |
659 void NativeThemeAndroid::DrawImageInt(SkCanvas* sk_canvas, | |
660 const gfx::ImageSkia& image, | |
661 int src_x, | |
662 int src_y, | |
663 int src_w, | |
664 int src_h, | |
665 int dest_x, | |
666 int dest_y, | |
667 int dest_w, | |
668 int dest_h) const { | |
669 // TODO(pkotwicz): Do something better and don't infer device | |
670 // scale factor from canvas scale. | |
671 SkMatrix m = sk_canvas->getTotalMatrix(); | |
672 ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale( | |
673 SkScalarAbs(m.getScaleX())); | |
674 scoped_ptr<gfx::Canvas> canvas(gfx::Canvas::CreateCanvasWithoutScaling( | |
675 sk_canvas, device_scale_factor)); | |
676 canvas->DrawImageInt(image, src_x, src_y, src_w, src_h, | |
677 dest_x, dest_y, dest_w, dest_h, true); | |
678 } | |
679 | |
680 void NativeThemeAndroid::DrawTiledImage(SkCanvas* sk_canvas, | |
681 const gfx::ImageSkia& image, | |
682 int src_x, | |
683 int src_y, | |
684 float tile_scale_x, | |
685 float tile_scale_y, | |
686 int dest_x, | |
687 int dest_y, | |
688 int w, | |
689 int h) const { | |
690 // TODO(pkotwicz): Do something better and don't infer device | |
691 // scale factor from canvas scale. | |
692 SkMatrix m = sk_canvas->getTotalMatrix(); | |
693 ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale( | |
694 SkScalarAbs(m.getScaleX())); | |
695 scoped_ptr<gfx::Canvas> canvas(gfx::Canvas::CreateCanvasWithoutScaling( | |
696 sk_canvas, device_scale_factor)); | |
697 canvas->TileImageInt(image, src_x, src_y, tile_scale_x, | |
698 tile_scale_y, dest_x, dest_y, w, h); | |
699 } | |
700 | |
701 SkColor NativeThemeAndroid::SaturateAndBrighten( | |
702 SkScalar* hsv, | |
703 SkScalar saturate_amount, | |
704 SkScalar brighten_amount) const { | |
705 SkScalar color[3]; | |
706 color[0] = hsv[0]; | |
707 color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0); | |
708 color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0); | |
709 return SkHSVToColor(color); | |
710 } | |
711 | |
712 SkScalar NativeThemeAndroid::Clamp(SkScalar value, | |
713 SkScalar min, | |
714 SkScalar max) const { | |
715 return std::min(std::max(value, min), max); | |
716 } | |
717 | |
718 void NativeThemeAndroid::DrawVertLine(SkCanvas* canvas, | |
719 int x, | |
720 int y1, | |
721 int y2, | |
722 const SkPaint& paint) const { | |
723 SkIRect skrect; | |
724 skrect.set(x, y1, x + 1, y2 + 1); | |
725 canvas->drawIRect(skrect, paint); | |
726 } | |
727 | |
728 void NativeThemeAndroid::DrawHorizLine(SkCanvas* canvas, | |
729 int x1, | |
730 int x2, | |
731 int y, | |
732 const SkPaint& paint) const { | |
733 SkIRect skrect; | |
734 skrect.set(x1, y, x2 + 1, y + 1); | |
735 canvas->drawIRect(skrect, paint); | |
736 } | |
737 | |
738 void NativeThemeAndroid::DrawBox(SkCanvas* canvas, | |
739 const gfx::Rect& rect, | |
740 const SkPaint& paint) const { | |
741 int right = rect.x() + rect.width() - 1; | |
742 int bottom = rect.y() + rect.height() - 1; | |
743 DrawHorizLine(canvas, rect.x(), right, rect.y(), paint); | |
744 DrawVertLine(canvas, right, rect.y(), bottom, paint); | |
745 DrawHorizLine(canvas, rect.x(), right, bottom, paint); | |
746 DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint); | |
747 } | |
748 | |
749 SkColor NativeThemeAndroid::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const { | |
750 SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2, 0.28, 0.5); | |
751 SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5); | |
752 | |
753 if (hsv1[2] + hsv2[2] > 1.0) | |
754 diff = -diff; | |
755 | |
756 return SaturateAndBrighten(hsv2, -0.2, diff); | |
757 } | |
758 | |
759 } // namespace ui | 34 } // namespace ui |
OLD | NEW |