Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: ui/gfx/native_theme_android.cc

Issue 8497054: Upstream: ui implementation in Android (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/native_theme_android.h ('k') | ui/gfx/platform_font_android.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « ui/gfx/native_theme_android.h ('k') | ui/gfx/platform_font_android.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698