OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/gfx/native_theme_android.h" | |
6 | |
7 #include <limits> | |
8 | |
9 #include "base/logging.h" | |
10 #include "grit/gfx_resources.h" | |
11 #include "third_party/skia/include/effects/SkGradientShader.h" | |
12 #include "ui/base/resource/resource_bundle.h" | |
13 #include "ui/gfx/color_utils.h" | |
14 #include "ui/gfx/rect.h" | |
15 #include "ui/gfx/size.h" | |
16 | |
17 namespace gfx { | |
18 | |
19 static const unsigned int button_length_ = 14; | |
sky
2011/11/11 02:41:39
These should now be kConstant style.
michaelbai
2011/11/11 18:16:26
Done.
| |
20 static const unsigned int scrollbar_width_ = 15; | |
21 static const unsigned int thumb_inactive_color_ = 0xeaeaea; | |
22 static const unsigned int thumb_active_color_ = 0xf4f4f4; | |
23 static const unsigned int track_color_ = 0xd3d3d3; | |
24 | |
25 // These are the default dimensions of radio buttons and checkboxes. | |
26 static const int kCheckboxAndRadioWidth = 13; | |
27 static const int kCheckboxAndRadioHeight = 13; | |
28 | |
29 // These sizes match the sizes in Chromium Win. | |
30 static const int kSliderThumbWidth = 11; | |
31 static const int kSliderThumbHeight = 21; | |
32 | |
33 static const SkColor kSliderTrackBackgroundColor = | |
34 SkColorSetRGB(0xe3, 0xdd, 0xd8); | |
35 static const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef); | |
36 static const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0); | |
37 static const SkColor kSliderThumbBorderDarkGrey = | |
38 SkColorSetRGB(0x9d, 0x96, 0x8e); | |
39 | |
40 // Get lightness adjusted color. | |
41 static SkColor BrightenColor(const color_utils::HSL& hsl, | |
42 SkAlpha alpha, | |
43 double lightness_amount) { | |
44 color_utils::HSL adjusted = hsl; | |
45 adjusted.l += lightness_amount; | |
46 if (adjusted.l > 1.0) | |
47 adjusted.l = 1.0; | |
48 if (adjusted.l < 0.0) | |
49 adjusted.l = 0.0; | |
50 | |
51 return color_utils::HSLToSkColor(adjusted, alpha); | |
52 } | |
53 | |
54 // static | |
55 NativeThemeAndroid* NativeThemeAndroid::instance() { | |
56 // The global NativeThemeAndroid instance. | |
57 static NativeThemeAndroid s_native_theme; | |
58 return &s_native_theme; | |
59 } | |
60 | |
61 gfx::Size NativeThemeAndroid::GetPartSize(Part part) const { | |
62 switch (part) { | |
63 case SCROLLBAR_DOWN_ARROW: | |
64 case SCROLLBAR_UP_ARROW: | |
65 return gfx::Size(scrollbar_width_, button_length_); | |
66 case SCROLLBAR_LEFT_ARROW: | |
67 case SCROLLBAR_RIGHT_ARROW: | |
68 return gfx::Size(button_length_, scrollbar_width_); | |
69 case CHECKBOX: | |
70 case RADIO: | |
71 return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight); | |
72 case SLIDER_TNUMB: | |
73 // These sizes match the sizes in Chromium Win. | |
74 return gfx::Size(kSliderThumbWidth, kSliderThumbHeight); | |
75 case INNER_SPIN_BUTTON: | |
76 return gfx::Size(scrollbar_width_, 0); | |
77 case PUSH_BUTTON: | |
78 case TEXTFIELD: | |
79 case MENU_LIST: | |
80 case SLIDER_TRACK: | |
81 case PROGRESS_BAR: | |
82 return gfx::Size(); // No default size. | |
83 } | |
84 return gfx::Size(); | |
85 } | |
86 | |
87 void NativeThemeAndroid::Paint(SkCanvas* canvas, | |
88 Part part, | |
89 State state, | |
90 const gfx::Rect& rect, | |
91 const ExtraParams& extra) { | |
92 switch (part) { | |
93 case SCROLLBAR_DOWN_ARROW: | |
94 case SCROLLBAR_UP_ARROW: | |
95 case SCROLLBAR_LEFT_ARROW: | |
96 case SCROLLBAR_RIGHT_ARROW: | |
97 PaintArrowButton(canvas, rect, part, state); | |
98 break; | |
99 case CHECKBOX: | |
100 PaintCheckbox(canvas, state, rect, extra.button); | |
101 break; | |
102 case RADIO: | |
103 PaintRadio(canvas, state, rect, extra.button); | |
104 break; | |
105 case PUSH_BUTTON: | |
106 PaintButton(canvas, state, rect, extra.button); | |
107 break; | |
108 case TEXTFIELD: | |
109 PaintTextField(canvas, state, rect, extra.text_field); | |
110 break; | |
111 case MENU_LIST: | |
112 PaintMenuList(canvas, state, rect, extra.menu_list); | |
113 break; | |
114 case SLIDER_TRACK: | |
115 PaintSliderTrack(canvas, state, rect, extra.slider); | |
116 break; | |
117 case SLIDER_TNUMB: | |
118 PaintSliderThumb(canvas, state, rect, extra.slider); | |
119 break; | |
120 case INNER_SPIN_BUTTON: | |
121 PaintInnerSpinButton(canvas, state, rect, extra.inner_spin); | |
122 break; | |
123 case PROGRESS_BAR: | |
124 PaintProgressBar(canvas, state, rect, extra.progress_bar); | |
125 break; | |
126 default: | |
127 NOTREACHED(); | |
128 } | |
129 } | |
130 | |
131 NativeThemeAndroid::NativeThemeAndroid() { | |
132 } | |
133 | |
134 NativeThemeAndroid::~NativeThemeAndroid() { | |
135 } | |
136 | |
137 void NativeThemeAndroid::PaintArrowButton(SkCanvas* canvas, | |
138 const gfx::Rect& rect, | |
139 Part direction, | |
140 State state) { | |
141 int widthMiddle; | |
142 int lengthMiddle; | |
143 SkPaint paint; | |
144 if (direction == SCROLLBAR_UP_ARROW || direction == SCROLLBAR_DOWN_ARROW) { | |
145 widthMiddle = rect.width() / 2 + 1; | |
146 lengthMiddle = rect.height() / 2 + 1; | |
147 } else { | |
148 lengthMiddle = rect.width() / 2 + 1; | |
149 widthMiddle = rect.height() / 2 + 1; | |
150 } | |
151 | |
152 // Calculate button color. | |
153 SkScalar trackHSV[3]; | |
154 SkColorToHSV(track_color_, trackHSV); | |
155 SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2); | |
156 SkColor backgroundColor = buttonColor; | |
157 if (state == PRESSED) { | |
158 SkScalar buttonHSV[3]; | |
159 SkColorToHSV(buttonColor, buttonHSV); | |
160 buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1); | |
161 } else if (state == HOVERED) { | |
162 SkScalar buttonHSV[3]; | |
163 SkColorToHSV(buttonColor, buttonHSV); | |
164 buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05); | |
165 } | |
166 | |
167 SkIRect skrect; | |
168 skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() | |
169 + rect.height()); | |
170 // Paint the background (the area visible behind the rounded corners). | |
171 paint.setColor(backgroundColor); | |
172 canvas->drawIRect(skrect, paint); | |
173 | |
174 // Paint the button's outline and fill the middle | |
175 SkPath outline; | |
176 switch (direction) { | |
177 case SCROLLBAR_UP_ARROW: | |
178 outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5); | |
179 outline.rLineTo(0, -(rect.height() - 2)); | |
180 outline.rLineTo(2, -2); | |
181 outline.rLineTo(rect.width() - 5, 0); | |
182 outline.rLineTo(2, 2); | |
183 outline.rLineTo(0, rect.height() - 2); | |
184 break; | |
185 case SCROLLBAR_DOWN_ARROW: | |
186 outline.moveTo(rect.x() + 0.5, rect.y() - 0.5); | |
187 outline.rLineTo(0, rect.height() - 2); | |
188 outline.rLineTo(2, 2); | |
189 outline.rLineTo(rect.width() - 5, 0); | |
190 outline.rLineTo(2, -2); | |
191 outline.rLineTo(0, -(rect.height() - 2)); | |
192 break; | |
193 case SCROLLBAR_RIGHT_ARROW: | |
194 outline.moveTo(rect.x() - 0.5, rect.y() + 0.5); | |
195 outline.rLineTo(rect.width() - 2, 0); | |
196 outline.rLineTo(2, 2); | |
197 outline.rLineTo(0, rect.height() - 5); | |
198 outline.rLineTo(-2, 2); | |
199 outline.rLineTo(-(rect.width() - 2), 0); | |
200 break; | |
201 case SCROLLBAR_LEFT_ARROW: | |
202 outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5); | |
203 outline.rLineTo(-(rect.width() - 2), 0); | |
204 outline.rLineTo(-2, 2); | |
205 outline.rLineTo(0, rect.height() - 5); | |
206 outline.rLineTo(2, 2); | |
207 outline.rLineTo(rect.width() - 2, 0); | |
208 break; | |
209 default: | |
210 break; | |
211 } | |
212 outline.close(); | |
213 | |
214 paint.setStyle(SkPaint::kFill_Style); | |
215 paint.setColor(buttonColor); | |
216 canvas->drawPath(outline, paint); | |
217 | |
218 paint.setAntiAlias(true); | |
219 paint.setStyle(SkPaint::kStroke_Style); | |
220 SkScalar thumbHSV[3]; | |
221 SkColorToHSV(thumb_inactive_color_, thumbHSV); | |
222 paint.setColor(OutlineColor(trackHSV, thumbHSV)); | |
223 canvas->drawPath(outline, paint); | |
224 | |
225 // If the button is disabled or read-only, the arrow is drawn with the | |
226 // outline color. | |
227 if (state != DISABLED) | |
228 paint.setColor(SK_ColorBLACK); | |
229 | |
230 paint.setAntiAlias(false); | |
231 paint.setStyle(SkPaint::kFill_Style); | |
232 | |
233 SkPath path; | |
234 // The constants in this block of code are hand-tailored to produce good | |
235 // looking arrows without anti-aliasing. | |
236 switch (direction) { | |
237 case SCROLLBAR_UP_ARROW: | |
238 path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle + 2); | |
239 path.rLineTo(7, 0); | |
240 path.rLineTo(-4, -4); | |
241 break; | |
242 case SCROLLBAR_DOWN_ARROW: | |
243 path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle - 3); | |
244 path.rLineTo(7, 0); | |
245 path.rLineTo(-4, 4); | |
246 break; | |
247 case SCROLLBAR_RIGHT_ARROW: | |
248 path.moveTo(rect.x() + lengthMiddle - 3, rect.y() + widthMiddle - 4); | |
249 path.rLineTo(0, 7); | |
250 path.rLineTo(4, -4); | |
251 break; | |
252 case SCROLLBAR_LEFT_ARROW: | |
253 path.moveTo(rect.x() + lengthMiddle + 1, rect.y() + widthMiddle - 5); | |
254 path.rLineTo(0, 9); | |
255 path.rLineTo(-4, -4); | |
256 break; | |
257 default: | |
258 break; | |
259 } | |
260 path.close(); | |
261 | |
262 canvas->drawPath(path, paint); | |
263 } | |
264 | |
265 void NativeThemeAndroid::PaintCheckbox(SkCanvas* canvas, | |
266 State state, | |
267 const gfx::Rect& rect, | |
268 const ButtonExtraParams& button) { | |
269 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
270 SkBitmap* image = NULL; | |
271 if (button.indeterminate) { | |
272 image = state == DISABLED ? | |
273 rb.GetBitmapNamed(IDR_CHECKBOX_DISABLED_INDETERMINATE) : | |
274 rb.GetBitmapNamed(IDR_CHECKBOX_INDETERMINATE); | |
275 } else if (button.checked) { | |
276 image = state == DISABLED ? | |
277 rb.GetBitmapNamed(IDR_CHECKBOX_DISABLED_ON) : | |
278 rb.GetBitmapNamed(IDR_CHECKBOX_ON); | |
279 } else { | |
280 image = state == DISABLED ? | |
281 rb.GetBitmapNamed(IDR_CHECKBOX_DISABLED_OFF) : | |
282 rb.GetBitmapNamed(IDR_CHECKBOX_OFF); | |
283 } | |
284 | |
285 gfx::Rect bounds = rect.Center(gfx::Size(image->width(), image->height())); | |
286 DrawBitmapInt(canvas, *image, 0, 0, image->width(), image->height(), | |
287 bounds.x(), bounds.y(), bounds.width(), bounds.height()); | |
288 } | |
289 | |
290 void NativeThemeAndroid::PaintRadio(SkCanvas* canvas, | |
291 State state, | |
292 const gfx::Rect& rect, | |
293 const ButtonExtraParams& button) { | |
294 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
295 SkBitmap* image = NULL; | |
296 if (state == DISABLED) { | |
297 image = button.checked ? | |
298 rb.GetBitmapNamed(IDR_RADIO_DISABLED_ON) : | |
299 rb.GetBitmapNamed(IDR_RADIO_DISABLED_OFF); | |
300 } else { | |
301 image = button.checked ? | |
302 rb.GetBitmapNamed(IDR_RADIO_ON) : | |
303 rb.GetBitmapNamed(IDR_RADIO_OFF); | |
304 } | |
305 | |
306 gfx::Rect bounds = rect.Center(gfx::Size(image->width(), image->height())); | |
307 DrawBitmapInt(canvas, *image, 0, 0, image->width(), image->height(), | |
308 bounds.x(), bounds.y(), bounds.width(), bounds.height()); | |
309 } | |
310 | |
311 void NativeThemeAndroid::PaintButton(SkCanvas* canvas, | |
312 State state, | |
313 const gfx::Rect& rect, | |
314 const ButtonExtraParams& button) { | |
315 SkPaint paint; | |
316 SkRect skrect; | |
317 int kRight = rect.right(); | |
318 int kBottom = rect.bottom(); | |
319 SkColor base_color = button.background_color; | |
320 | |
321 color_utils::HSL base_hsl; | |
322 color_utils::SkColorToHSL(base_color, &base_hsl); | |
323 | |
324 // Our standard gradient is from 0xdd to 0xf8. This is the amount of | |
325 // increased luminance between those values. | |
326 SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105)); | |
327 | |
328 // If the button is too small, fallback to drawing a single, solid color | |
329 if (rect.width() < 5 || rect.height() < 5) { | |
330 paint.setColor(base_color); | |
331 skrect.set(rect.x(), rect.y(), kRight, kBottom); | |
332 canvas->drawRect(skrect, paint); | |
333 return; | |
334 } | |
335 | |
336 if (button.has_border) { | |
337 int kBorderAlpha = state == HOVERED ? 0x80 : 0x55; | |
338 paint.setARGB(kBorderAlpha, 0, 0, 0); | |
339 canvas->drawLine(rect.x() + 1, rect.y(), kRight - 1, rect.y(), paint); | |
340 canvas->drawLine(kRight - 1, rect.y() + 1, kRight - 1, kBottom - 1, paint); | |
341 canvas->drawLine(rect.x() + 1, kBottom - 1, kRight - 1, kBottom - 1, paint); | |
342 canvas->drawLine(rect.x(), rect.y() + 1, rect.x(), kBottom - 1, paint); | |
343 } | |
344 | |
345 paint.setColor(SK_ColorBLACK); | |
346 int kLightEnd = state == PRESSED ? 1 : 0; | |
347 int kDarkEnd = !kLightEnd; | |
348 SkPoint gradient_bounds[2]; | |
349 gradient_bounds[kLightEnd].set(SkIntToScalar(rect.x()), | |
350 SkIntToScalar(rect.y())); | |
351 gradient_bounds[kDarkEnd].set(SkIntToScalar(rect.x()), | |
352 SkIntToScalar(kBottom - 1)); | |
353 SkColor colors[2]; | |
354 colors[0] = light_color; | |
355 colors[1] = base_color; | |
356 | |
357 SkShader* shader = SkGradientShader::CreateLinear( | |
358 gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode, NULL); | |
359 paint.setStyle(SkPaint::kFill_Style); | |
360 paint.setShader(shader); | |
361 shader->unref(); | |
362 | |
363 if (button.has_border) { | |
364 skrect.set(rect.x() + 1, rect.y() + 1, kRight - 1, kBottom - 1); | |
365 } else { | |
366 skrect.set(rect.x(), rect.y(), kRight, kBottom); | |
367 } | |
368 canvas->drawRect(skrect, paint); | |
369 paint.setShader(NULL); | |
370 | |
371 if (button.has_border) { | |
372 paint.setColor(BrightenColor(base_hsl, SkColorGetA(base_color), -0.0588)); | |
373 canvas->drawPoint(rect.x() + 1, rect.y() + 1, paint); | |
374 canvas->drawPoint(kRight - 2, rect.y() + 1, paint); | |
375 canvas->drawPoint(rect.x() + 1, kBottom - 2, paint); | |
376 canvas->drawPoint(kRight - 2, kBottom - 2, paint); | |
377 } | |
378 } | |
379 | |
380 void NativeThemeAndroid::PaintTextField(SkCanvas* canvas, | |
381 State state, | |
382 const gfx::Rect& rect, | |
383 const TextFieldExtraParams& text) { | |
384 // The following drawing code simulates the user-agent css border for | |
385 // text area and text input so that we do not break layout tests. Once we | |
386 // have decided the desired looks, we should update the code here and | |
387 // the layout test expectations. | |
388 SkRect bounds; | |
389 bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1); | |
390 | |
391 SkPaint fill_paint; | |
392 fill_paint.setStyle(SkPaint::kFill_Style); | |
393 fill_paint.setColor(text.background_color); | |
394 canvas->drawRect(bounds, fill_paint); | |
395 | |
396 if (text.is_text_area) { | |
397 // Draw text area border: 1px solid black | |
398 SkPaint stroke_paint; | |
399 fill_paint.setStyle(SkPaint::kStroke_Style); | |
400 fill_paint.setColor(SK_ColorBLACK); | |
401 canvas->drawRect(bounds, fill_paint); | |
402 } else { | |
403 // Draw text input and listbox inset border | |
404 // Text Input: 2px inset #eee | |
405 // Listbox: 1px inset #808080 | |
406 SkColor kLightColor = text.is_listbox ? | |
407 SkColorSetRGB(0x80, 0x80, 0x80) : SkColorSetRGB(0xee, 0xee, 0xee); | |
408 SkColor kDarkColor = text.is_listbox ? | |
409 SkColorSetRGB(0x2c, 0x2c, 0x2c) : SkColorSetRGB(0x9a, 0x9a, 0x9a); | |
410 int kBorderWidth = text.is_listbox ? 1 : 2; | |
411 | |
412 SkPaint dark_paint; | |
413 dark_paint.setAntiAlias(true); | |
414 dark_paint.setStyle(SkPaint::kFill_Style); | |
415 dark_paint.setColor(kDarkColor); | |
416 | |
417 SkPaint light_paint; | |
418 light_paint.setAntiAlias(true); | |
419 light_paint.setStyle(SkPaint::kFill_Style); | |
420 light_paint.setColor(kLightColor); | |
421 | |
422 int left = rect.x(); | |
423 int top = rect.y(); | |
424 int right = rect.right(); | |
425 int bottom = rect.bottom(); | |
426 | |
427 SkPath path; | |
428 path.incReserve(4); | |
429 | |
430 // Top | |
431 path.moveTo(SkIntToScalar(left), SkIntToScalar(top)); | |
432 path.lineTo(SkIntToScalar(left + kBorderWidth), | |
433 SkIntToScalar(top + kBorderWidth)); | |
434 path.lineTo(SkIntToScalar(right - kBorderWidth), | |
435 SkIntToScalar(top + kBorderWidth)); | |
436 path.lineTo(SkIntToScalar(right), SkIntToScalar(top)); | |
437 canvas->drawPath(path, dark_paint); | |
438 | |
439 // Bottom | |
440 path.reset(); | |
441 path.moveTo(SkIntToScalar(left + kBorderWidth), | |
442 SkIntToScalar(bottom - kBorderWidth)); | |
443 path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom)); | |
444 path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom)); | |
445 path.lineTo(SkIntToScalar(right - kBorderWidth), | |
446 SkIntToScalar(bottom - kBorderWidth)); | |
447 canvas->drawPath(path, light_paint); | |
448 | |
449 // Left | |
450 path.reset(); | |
451 path.moveTo(SkIntToScalar(left), SkIntToScalar(top)); | |
452 path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom)); | |
453 path.lineTo(SkIntToScalar(left + kBorderWidth), | |
454 SkIntToScalar(bottom - kBorderWidth)); | |
455 path.lineTo(SkIntToScalar(left + kBorderWidth), | |
456 SkIntToScalar(top + kBorderWidth)); | |
457 canvas->drawPath(path, dark_paint); | |
458 | |
459 // Right | |
460 path.reset(); | |
461 path.moveTo(SkIntToScalar(right - kBorderWidth), | |
462 SkIntToScalar(top + kBorderWidth)); | |
463 path.lineTo(SkIntToScalar(right - kBorderWidth), SkIntToScalar(bottom)); | |
464 path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom)); | |
465 path.lineTo(SkIntToScalar(right), SkIntToScalar(top)); | |
466 canvas->drawPath(path, light_paint); | |
467 } | |
468 } | |
469 | |
470 void NativeThemeAndroid::PaintMenuList(SkCanvas* canvas, | |
471 State state, | |
472 const gfx::Rect& rect, | |
473 const MenuListExtraParams& menu_list) { | |
474 // If a border radius is specified, we let the WebCore paint the background | |
475 // and the border of the control. | |
476 if (!menu_list.has_border_radius) { | |
477 ButtonExtraParams button = { 0 }; | |
478 button.background_color = menu_list.background_color; | |
479 button.has_border = menu_list.has_border; | |
480 PaintButton(canvas, state, rect, button); | |
481 } | |
482 | |
483 SkPaint paint; | |
484 paint.setColor(SK_ColorBLACK); | |
485 paint.setAntiAlias(true); | |
486 paint.setStyle(SkPaint::kFill_Style); | |
487 | |
488 SkPath path; | |
489 path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3); | |
490 path.rLineTo(6, 0); | |
491 path.rLineTo(-3, 6); | |
492 path.close(); | |
493 canvas->drawPath(path, paint); | |
494 } | |
495 | |
496 void NativeThemeAndroid::PaintSliderTrack(SkCanvas* canvas, | |
497 State state, | |
498 const gfx::Rect& rect, | |
499 const SliderExtraParams& slider) { | |
500 int kMidX = rect.x() + rect.width() / 2; | |
501 int kMidY = rect.y() + rect.height() / 2; | |
502 | |
503 SkPaint paint; | |
504 paint.setColor(kSliderTrackBackgroundColor); | |
505 | |
506 SkRect skrect; | |
507 if (slider.vertical) { | |
508 skrect.set(std::max(rect.x(), kMidX - 2), | |
509 rect.y(), | |
510 std::min(rect.right(), kMidX + 2), | |
511 rect.bottom()); | |
512 } else { | |
513 skrect.set(rect.x(), | |
514 std::max(rect.y(), kMidY - 2), | |
515 rect.right(), | |
516 std::min(rect.bottom(), kMidY + 2)); | |
517 } | |
518 canvas->drawRect(skrect, paint); | |
519 } | |
520 | |
521 void NativeThemeAndroid::PaintSliderThumb(SkCanvas* canvas, | |
522 State state, | |
523 const gfx::Rect& rect, | |
524 const SliderExtraParams& slider) { | |
525 bool hovered = (state == HOVERED) || slider.in_drag; | |
526 int kMidX = rect.x() + rect.width() / 2; | |
527 int kMidY = rect.y() + rect.height() / 2; | |
528 | |
529 SkPaint paint; | |
530 paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey); | |
531 | |
532 SkIRect skrect; | |
533 if (slider.vertical) | |
534 skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom()); | |
535 else | |
536 skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1); | |
537 | |
538 canvas->drawIRect(skrect, paint); | |
539 | |
540 paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey); | |
541 | |
542 if (slider.vertical) | |
543 skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom()); | |
544 else | |
545 skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom()); | |
546 | |
547 canvas->drawIRect(skrect, paint); | |
548 | |
549 paint.setColor(kSliderThumbBorderDarkGrey); | |
550 DrawBox(canvas, rect, paint); | |
551 | |
552 if (rect.height() > 10 && rect.width() > 10) { | |
553 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint); | |
554 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint); | |
555 DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint); | |
556 } | |
557 } | |
558 | |
559 void NativeThemeAndroid::PaintInnerSpinButton( | |
560 SkCanvas* canvas, | |
561 State state, | |
562 const gfx::Rect& rect, | |
563 const InnerSpinButtonExtraParams& spin_button) { | |
564 if (spin_button.read_only) | |
565 state = DISABLED; | |
566 | |
567 State north_state = state; | |
568 State south_state = state; | |
569 if (spin_button.spin_up) | |
570 south_state = south_state != DISABLED ? NORMAL : DISABLED; | |
571 else | |
572 north_state = north_state != DISABLED ? NORMAL : DISABLED; | |
573 | |
574 gfx::Rect half = rect; | |
575 half.set_height(rect.height() / 2); | |
576 PaintArrowButton(canvas, half, SCROLLBAR_UP_ARROW, north_state); | |
577 | |
578 half.set_y(rect.y() + rect.height() / 2); | |
579 PaintArrowButton(canvas, half, SCROLLBAR_DOWN_ARROW, south_state); | |
580 } | |
581 | |
582 void NativeThemeAndroid::PaintProgressBar( | |
583 SkCanvas* canvas, | |
584 State state, | |
585 const gfx::Rect& rect, | |
586 const ProgressBarExtraParams& progress_bar) { | |
587 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
588 SkBitmap* bar_image = rb.GetBitmapNamed(IDR_PROGRESS_BAR); | |
589 SkBitmap* left_border_image = rb.GetBitmapNamed(IDR_PROGRESS_BORDER_LEFT); | |
590 SkBitmap* right_border_image = rb.GetBitmapNamed(IDR_PROGRESS_BORDER_RIGHT); | |
591 | |
592 double tile_scale = static_cast<double>(rect.height()) / | |
593 bar_image->height(); | |
594 | |
595 int new_tile_width = static_cast<int>(bar_image->width() * tile_scale); | |
596 double tile_scale_x = static_cast<double>(new_tile_width) / | |
597 bar_image->width(); | |
598 | |
599 DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale, | |
600 rect.x(), rect.y(), rect.width(), rect.height()); | |
601 | |
602 if (progress_bar.value_rect_width) { | |
603 SkBitmap* value_image = rb.GetBitmapNamed(IDR_PROGRESS_VALUE); | |
604 | |
605 new_tile_width = static_cast<int>(value_image->width() * tile_scale); | |
606 tile_scale_x = static_cast<double>(new_tile_width) / | |
607 value_image->width(); | |
608 | |
609 DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale, | |
610 progress_bar.value_rect_x, | |
611 progress_bar.value_rect_y, | |
612 progress_bar.value_rect_width, | |
613 progress_bar.value_rect_height); | |
614 } | |
615 | |
616 int dest_left_border_width = static_cast<int>(left_border_image->width() * | |
617 tile_scale); | |
618 SkRect dest_rect = { | |
619 SkIntToScalar(rect.x()), | |
620 SkIntToScalar(rect.y()), | |
621 SkIntToScalar(rect.x() + dest_left_border_width), | |
622 SkIntToScalar(rect.bottom()) | |
623 }; | |
624 canvas->drawBitmapRect(*left_border_image, NULL, dest_rect); | |
625 | |
626 int dest_right_border_width = static_cast<int>(right_border_image->width() * | |
627 tile_scale); | |
628 dest_rect.set(SkIntToScalar(rect.right() - dest_right_border_width), | |
629 SkIntToScalar(rect.y()), | |
630 SkIntToScalar(rect.right()), | |
631 SkIntToScalar(rect.bottom())); | |
632 canvas->drawBitmapRect(*right_border_image, NULL, dest_rect); | |
633 } | |
634 | |
635 bool NativeThemeAndroid::IntersectsClipRectInt(SkCanvas* canvas, | |
636 int x, | |
637 int y, | |
638 int w, | |
639 int h) { | |
640 SkRect clip; | |
641 return canvas->getClipBounds(&clip) && | |
642 clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w), | |
643 SkIntToScalar(y + h)); | |
644 } | |
645 | |
646 void NativeThemeAndroid::DrawBitmapInt(SkCanvas* canvas, | |
647 const SkBitmap& bitmap, | |
648 int src_x, | |
649 int src_y, | |
650 int src_w, | |
651 int src_h, | |
652 int dest_x, | |
653 int dest_y, | |
654 int dest_w, | |
655 int dest_h) { | |
656 DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() && | |
657 src_y + src_h < std::numeric_limits<int16_t>::max()); | |
658 if (src_w <= 0 || src_h <= 0 || dest_w <= 0 || dest_h <= 0) { | |
659 NOTREACHED() << "Attempting to draw bitmap to/from an empty rect!"; | |
660 return; | |
661 } | |
662 | |
663 if (!IntersectsClipRectInt(canvas, dest_x, dest_y, dest_w, dest_h)) | |
664 return; | |
665 | |
666 SkRect dest_rect = { SkIntToScalar(dest_x), | |
667 SkIntToScalar(dest_y), | |
668 SkIntToScalar(dest_x + dest_w), | |
669 SkIntToScalar(dest_y + dest_h) }; | |
670 | |
671 if (src_w == dest_w && src_h == dest_h) { | |
672 // Workaround for apparent bug in Skia that causes image to occasionally | |
673 // shift. | |
674 SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; | |
675 canvas->drawBitmapRect(bitmap, &src_rect, dest_rect); | |
676 return; | |
677 } | |
678 | |
679 // Make a bitmap shader that contains the bitmap we want to draw. This is | |
680 // basically what SkCanvas.drawBitmap does internally, but it gives us | |
681 // more control over quality and will use the mipmap in the source image if | |
682 // it has one, whereas drawBitmap won't. | |
683 SkShader* shader = SkShader::CreateBitmapShader(bitmap, | |
684 SkShader::kRepeat_TileMode, | |
685 SkShader::kRepeat_TileMode); | |
686 SkMatrix shader_scale; | |
687 shader_scale.setScale(SkFloatToScalar(static_cast<float>(dest_w) / src_w), | |
688 SkFloatToScalar(static_cast<float>(dest_h) / src_h)); | |
689 shader_scale.preTranslate(SkIntToScalar(-src_x), SkIntToScalar(-src_y)); | |
690 shader_scale.postTranslate(SkIntToScalar(dest_x), SkIntToScalar(dest_y)); | |
691 shader->setLocalMatrix(shader_scale); | |
692 | |
693 // The rect will be filled by the bitmap. | |
694 SkPaint p; | |
695 p.setFilterBitmap(true); | |
696 p.setShader(shader); | |
697 shader->unref(); | |
698 canvas->drawRect(dest_rect, p); | |
699 } | |
700 | |
701 void NativeThemeAndroid::DrawTiledImage(SkCanvas* canvas, | |
702 const SkBitmap& bitmap, | |
703 int src_x, | |
704 int src_y, | |
705 double tile_scale_x, | |
706 double tile_scale_y, | |
707 int dest_x, | |
708 int dest_y, | |
709 int w, | |
710 int h) const { | |
711 SkShader* shader = SkShader::CreateBitmapShader(bitmap, | |
712 SkShader::kRepeat_TileMode, | |
713 SkShader::kRepeat_TileMode); | |
714 if (tile_scale_x != 1.0 || tile_scale_y != 1.0) { | |
715 SkMatrix shader_scale; | |
716 shader_scale.setScale(SkDoubleToScalar(tile_scale_x), | |
717 SkDoubleToScalar(tile_scale_y)); | |
718 shader->setLocalMatrix(shader_scale); | |
719 } | |
720 | |
721 SkPaint paint; | |
722 paint.setShader(shader); | |
723 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); | |
724 | |
725 // CreateBitmapShader returns a Shader with a reference count of one, we | |
726 // need to unref after paint takes ownership of the shader. | |
727 shader->unref(); | |
728 canvas->save(); | |
729 canvas->translate(SkIntToScalar(dest_x - src_x), | |
730 SkIntToScalar(dest_y - src_y)); | |
731 canvas->clipRect(SkRect::MakeXYWH(src_x, src_y, w, h)); | |
732 canvas->drawPaint(paint); | |
733 canvas->restore(); | |
734 } | |
735 | |
736 SkColor NativeThemeAndroid::SaturateAndBrighten( | |
737 SkScalar* hsv, | |
738 SkScalar saturate_amount, | |
739 SkScalar brighten_amount) const { | |
740 SkScalar color[3]; | |
741 color[0] = hsv[0]; | |
742 color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0); | |
743 color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0); | |
744 return SkHSVToColor(color); | |
745 } | |
746 | |
747 SkScalar NativeThemeAndroid::Clamp(SkScalar value, | |
748 SkScalar min, | |
749 SkScalar max) const { | |
750 return std::min(std::max(value, min), max); | |
751 } | |
752 | |
753 void NativeThemeAndroid::DrawVertLine(SkCanvas* canvas, | |
754 int x, | |
755 int y1, | |
756 int y2, | |
757 const SkPaint& paint) const { | |
758 SkIRect skrect; | |
759 skrect.set(x, y1, x + 1, y2 + 1); | |
760 canvas->drawIRect(skrect, paint); | |
761 } | |
762 | |
763 void NativeThemeAndroid::DrawHorizLine(SkCanvas* canvas, | |
764 int x1, | |
765 int x2, | |
766 int y, | |
767 const SkPaint& paint) const { | |
768 SkIRect skrect; | |
769 skrect.set(x1, y, x2 + 1, y + 1); | |
770 canvas->drawIRect(skrect, paint); | |
771 } | |
772 | |
773 void NativeThemeAndroid::DrawBox(SkCanvas* canvas, | |
774 const gfx::Rect& rect, | |
775 const SkPaint& paint) const { | |
776 int right = rect.x() + rect.width() - 1; | |
777 int bottom = rect.y() + rect.height() - 1; | |
778 DrawHorizLine(canvas, rect.x(), right, rect.y(), paint); | |
779 DrawVertLine(canvas, right, rect.y(), bottom, paint); | |
780 DrawHorizLine(canvas, rect.x(), right, bottom, paint); | |
781 DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint); | |
782 } | |
783 | |
784 SkColor NativeThemeAndroid::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const { | |
785 // GTK Theme engines have way too much control over the layout of | |
786 // the scrollbar. We might be able to more closely approximate its | |
787 // look-and-feel, if we sent whole images instead of just colors | |
788 // from the browser to the renderer. But even then, some themes | |
789 // would just break. | |
790 // | |
791 // So, instead, we don't even try to 100% replicate the look of | |
792 // the native scrollbar. We render our own version, but we make | |
793 // sure to pick colors that blend in nicely with the system GTK | |
794 // theme. In most cases, we can just sample a couple of pixels | |
795 // from the system scrollbar and use those colors to draw our | |
796 // scrollbar. | |
797 // | |
798 // This works fine for the track color and the overall thumb | |
799 // color. But it fails spectacularly for the outline color used | |
800 // around the thumb piece. Not all themes have a clearly defined | |
801 // outline. For some of them it is partially transparent, and for | |
802 // others the thickness is very unpredictable. | |
803 // | |
804 // So, instead of trying to approximate the system theme, we | |
805 // instead try to compute a reasonable looking choice based on the | |
806 // known color of the track and the thumb piece. This is difficult | |
807 // when trying to deal both with high- and low-contrast themes, | |
808 // and both with positive and inverted themes. | |
809 // | |
810 // The following code has been tested to look OK with all of the | |
811 // default GTK themes. | |
812 SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2, 0.28, 0.5); | |
813 SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5); | |
814 | |
815 if (hsv1[2] + hsv2[2] > 1.0) | |
816 diff = -diff; | |
817 | |
818 return SaturateAndBrighten(hsv2, -0.2, diff); | |
819 } | |
820 | |
821 } // namespace gfx | |
OLD | NEW |