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 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 | |
OLD | NEW |