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

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

Powered by Google App Engine
This is Rietveld 408576698