1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 // This file implements a simple generic version of the WebThemeEngine,
32 // which is used to draw all the native controls on a web page. We use this
33 // file when running in layout test mode in order to remove any
34 // platform-specific rendering differences due to themes, colors, etc.
35 //
36
37 #include "config.h"
38 #include "WebThemeControlDRTWin.h"
39
40 #include "skia/ext/platform_canvas.h"
41 #include "skia/ext/skia_utils_win.h"
42 #include "third_party/skia/include/core/SkPaint.h"
43 #include "third_party/skia/include/core/SkPath.h"
44 #include "third_party/skia/include/core/SkRect.h"
45
46 #include <wtf/Assertions.h>
47
48 using namespace std;
49
50 static const SkColor edgeColor = SK_ColorBLACK;
51 static const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
52 static const SkColor fgColor = SK_ColorBLACK;
53 static const SkColor bgColors[] = {
54 SK_ColorBLACK, // Unknown
55 SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled
56 SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly
57 SkColorSetRGB(0x89, 0xc4, 0xff), // Normal
58 SkColorSetRGB(0x43, 0xf9, 0xff), // Hot
59 SkColorSetRGB(0x20, 0xf6, 0xcc), // Focused
60 SkColorSetRGB(0x00, 0xf3, 0xac), // Hover
61 SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed
62 SkColorSetRGB(0xcc, 0xcc, 0xcc) // Indeterminate
63 };
64
validate(const SkIRect & rect,WebThemeControlDRTWin::Type ctype)65 static SkIRect validate(const SkIRect& rect, WebThemeControlDRTWin::Type ctype)
66 {
67 switch (ctype) {
68 case WebThemeControlDRTWin::UncheckedBoxType:
69 case WebThemeControlDRTWin::CheckedBoxType:
70 case WebThemeControlDRTWin::UncheckedRadioType:
71 case WebThemeControlDRTWin::CheckedRadioType: {
72 SkIRect retval = rect;
73
74 // The maximum width and height is 13.
75 // Center the square in the passed rectangle.
76 const int maxControlSize = 13;
77 int controlSize = min(rect.width(), rect.height());
78 controlSize = min(controlSize, maxControlSize);
79
80 retval.fLeft = rect.fLeft + (rect.width() / 2) - (controlSize / 2);
81 retval.fRight = retval.fLeft + controlSize - 1;
82 retval.fTop = rect.fTop + (rect.height() / 2) - (controlSize / 2);
83 retval.fBottom = retval.fTop + controlSize - 1;
84
85 return retval;
86 }
87
88 default:
89 return rect;
90 }
91 }
92
93 // WebThemeControlDRTWin
94
WebThemeControlDRTWin(SkCanvas * canvas,const SkIRect & irect,Type ctype,State cstate)95 WebThemeControlDRTWin::WebThemeControlDRTWin(SkCanvas* canvas,
96 const SkIRect& irect,
97 Type ctype,
98 State cstate)
99 : m_canvas(canvas)
100 , m_irect(validate(irect, ctype))
101 , m_type(ctype)
102 , m_state(cstate)
103 , m_left(m_irect.fLeft)
104 , m_right(m_irect.fRight)
105 , m_top(m_irect.fTop)
106 , m_bottom(m_irect.fBottom)
107 , m_height(m_irect.height())
108 , m_width(m_irect.width())
109 , m_edgeColor(edgeColor)
110 , m_bgColor(bgColors[cstate])
111 , m_fgColor(fgColor)
112 {
113 }
114
~WebThemeControlDRTWin()115 WebThemeControlDRTWin::~WebThemeControlDRTWin()
116 {
117 }
118
box(const SkIRect & rect,SkColor fillColor)119 void WebThemeControlDRTWin::box(const SkIRect& rect, SkColor fillColor)
120 {
121 SkPaint paint;
122
123 paint.setStyle(SkPaint::kFill_Style);
124 paint.setColor(fillColor);
125 m_canvas->drawIRect(rect, paint);
126
127 paint.setColor(m_edgeColor);
128 paint.setStyle(SkPaint::kStroke_Style);
129 m_canvas->drawIRect(rect, paint);
130 }
131
line(int x0,int y0,int x1,int y1,SkColor color)132 void WebThemeControlDRTWin::line(int x0, int y0, int x1, int y1, SkColor color)
133 {
134 SkPaint paint;
135 paint.setColor(color);
136 m_canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0),
137 SkIntToScalar(x1), SkIntToScalar(y1),
138 paint);
139 }
140
triangle(int x0,int y0,int x1,int y1,int x2,int y2,SkColor color)141 void WebThemeControlDRTWin::triangle(int x0, int y0,
142 int x1, int y1,
143 int x2, int y2,
144 SkColor color)
145 {
146 SkPath path;
147 SkPaint paint;
148
149 paint.setColor(color);
150 paint.setStyle(SkPaint::kFill_Style);
151 path.incReserve(4);
152 path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0));
153 path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1));
154 path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2));
155 path.close();
156 m_canvas->drawPath(path, paint);
157
158 paint.setColor(m_edgeColor);
159 paint.setStyle(SkPaint::kStroke_Style);
160 m_canvas->drawPath(path, paint);
161 }
162
roundRect(SkColor color)163 void WebThemeControlDRTWin::roundRect(SkColor color)
164 {
165 SkRect rect;
166 SkScalar radius = SkIntToScalar(5);
167 SkPaint paint;
168
169 rect.set(m_irect);
170 paint.setColor(color);
171 paint.setStyle(SkPaint::kFill_Style);
172 m_canvas->drawRoundRect(rect, radius, radius, paint);
173
174 paint.setColor(m_edgeColor);
175 paint.setStyle(SkPaint::kStroke_Style);
176 m_canvas->drawRoundRect(rect, radius, radius, paint);
177 }
178
oval(SkColor color)179 void WebThemeControlDRTWin::oval(SkColor color)
180 {
181 SkRect rect;
182 SkPaint paint;
183
184 rect.set(m_irect);
185 paint.setColor(color);
186 paint.setStyle(SkPaint::kFill_Style);
187 m_canvas->drawOval(rect, paint);
188
189 paint.setColor(m_edgeColor);
190 paint.setStyle(SkPaint::kStroke_Style);
191 m_canvas->drawOval(rect, paint);
192 }
193
circle(SkScalar radius,SkColor color)194 void WebThemeControlDRTWin::circle(SkScalar radius, SkColor color)
195 {
196 SkScalar cy = SkIntToScalar(m_top + m_height / 2);
197 SkScalar cx = SkIntToScalar(m_left + m_width / 2);
198 SkPaint paint;
199
200 paint.setColor(color);
201 paint.setStyle(SkPaint::kFill_Style);
202 m_canvas->drawCircle(cx, cy, radius, paint);
203
204 paint.setColor(m_edgeColor);
205 paint.setStyle(SkPaint::kStroke_Style);
206 m_canvas->drawCircle(cx, cy, radius, paint);
207 }
208
nestedBoxes(int indentLeft,int indentTop,int indentRight,int indentBottom,SkColor outerColor,SkColor innerColor)209 void WebThemeControlDRTWin::nestedBoxes(int indentLeft,
210 int indentTop,
211 int indentRight,
212 int indentBottom,
213 SkColor outerColor,
214 SkColor innerColor)
215 {
216 SkIRect lirect;
217 box(m_irect, outerColor);
218 lirect.set(m_irect.fLeft + indentLeft,
219 m_irect.fTop + indentTop,
220 m_irect.fRight - indentRight,
221 m_irect.fBottom - indentBottom);
222 box(lirect, innerColor);
223 }
224
markState()225 void WebThemeControlDRTWin::markState()
226 {
227 // The horizontal lines in a read only control are spaced by this amount.
228 const int readOnlyLineOffset = 5;
229
230 // The length of a triangle side for the corner marks.
231 const int triangleSize = 5;
232
233 switch (m_state) {
234 case UnknownState:
235 case DisabledState:
236 case NormalState:
237 // Don't visually mark these states (color is enough).
238 break;
239 case ReadOnlyState:
240 // Drawing lines across the control.
241 for (int i = m_top + readOnlyLineOffset; i < m_bottom; i += readOnlyLineOffset)
242 line(m_left + 1, i, m_right - 1, i, readOnlyColor);
243 break;
244
245 case HotState:
246 // Draw a triangle in the upper left corner of the control.
247 triangle(m_left, m_top,
248 m_left + triangleSize, m_top,
249 m_left, m_top + triangleSize, m_edgeColor);
250 break;
251
252 case HoverState:
253 // Draw a triangle in the upper right corner of the control.
254 triangle(m_right, m_top,
255 m_right, m_top + triangleSize,
256 m_right - triangleSize, m_top, m_edgeColor);
257 break;
258
259 case FocusedState:
260 // Draw a triangle in the bottom right corner of the control.
261 triangle(m_right, m_bottom,
262 m_right - triangleSize, m_bottom,
263 m_right, m_bottom - triangleSize, m_edgeColor);
264 break;
265
266 case PressedState:
267 // Draw a triangle in the bottom left corner of the control.
268 triangle(m_left, m_bottom,
269 m_left, m_bottom - triangleSize,
270 m_left + triangleSize, m_bottom, m_edgeColor);
271 break;
272
273 default:
274 ASSERT_NOT_REACHED();
275 CRASH();
276 break;
277 }
278 }
279
draw()280 void WebThemeControlDRTWin::draw()
281 {
282 int halfWidth = m_width / 2;
283 int halfHeight = m_height / 2;
284 int quarterWidth = m_width / 4;
285 int quarterHeight = m_height / 4;
286
287 // Indent amounts for the check in a checkbox or radio button.
288 const int checkIndent = 3;
289
290 // Indent amounts for short and long sides of the scrollbar notches.
291 const int notchLongOffset = 1;
292 const int notchShortOffset = 4;
293 const int noOffset = 0;
294
295 // Indent amounts for the short and long sides of a scroll thumb box.
296 const int thumbLongIndent = 0;
297 const int thumbShortIndent = 2;
298
299 // Indents for the crosshatch on a scroll grip.
300 const int gripLongIndent = 3;
301 const int gripShortIndent = 5;
302
303 // Indents for the the slider track.
304 const int sliderIndent = 2;
305
306 skia::BeginPlatformPaint(m_canvas);
307
308 switch (m_type) {
309 case UnknownType:
310 ASSERT_NOT_REACHED();
311 CRASH();
312 break;
313
314 case TextFieldType:
315 // We render this by hand outside of this function.
316 ASSERT_NOT_REACHED();
317 CRASH();
318 break;
319
320 case PushButtonType:
321 // push buttons render as a rounded rectangle
322 roundRect(m_bgColor);
323 break;
324
325 case UncheckedBoxType:
326 // Unchecked boxes are simply plain boxes.
327 box(m_irect, m_bgColor);
328 break;
329
330 case CheckedBoxType:
331 nestedBoxes(checkIndent, checkIndent, checkIndent, checkIndent, m_bgColor, m_fgColor);
332 break;
333
334 case IndeterminateCheckboxType:
335 // Indeterminate checkbox is a box containing '-'.
336 nestedBoxes(checkIndent, halfHeight, checkIndent, halfHeight, m_bgColor, m_fgColor);
337 break;
338
339 case UncheckedRadioType:
340 circle(SkIntToScalar(halfHeight), m_bgColor);
341 break;
342
343 case CheckedRadioType:
344 circle(SkIntToScalar(halfHeight), m_bgColor);
345 circle(SkIntToScalar(halfHeight - checkIndent), m_fgColor);
346 break;
347
348 case HorizontalScrollTrackBackType: {
349 // Draw a box with a notch at the left.
350 int longOffset = halfHeight - notchLongOffset;
351 int shortOffset = m_width - notchShortOffset;
352 nestedBoxes(noOffset, longOffset, shortOffset, longOffset, m_bgColor, m_edgeColor);
353 break;
354 }
355
356 case HorizontalScrollTrackForwardType: {
357 // Draw a box with a notch at the right.
358 int longOffset = halfHeight - notchLongOffset;
359 int shortOffset = m_width - notchShortOffset;
360 nestedBoxes(shortOffset, longOffset, noOffset, longOffset, m_bgColor, m_fgColor);
361 break;
362 }
363
364 case VerticalScrollTrackBackType: {
365 // Draw a box with a notch at the top.
366 int longOffset = halfWidth - notchLongOffset;
367 int shortOffset = m_height - notchShortOffset;
368 nestedBoxes(longOffset, noOffset, longOffset, shortOffset, m_bgColor, m_fgColor);
369 break;
370 }
371
372 case VerticalScrollTrackForwardType: {
373 // Draw a box with a notch at the bottom.
374 int longOffset = halfWidth - notchLongOffset;
375 int shortOffset = m_height - notchShortOffset;
376 nestedBoxes(longOffset, shortOffset, longOffset, noOffset, m_bgColor, m_fgColor);
377 break;
378 }
379
380 case HorizontalScrollThumbType:
381 // Draw a narrower box on top of the outside box.
382 nestedBoxes(thumbLongIndent, thumbShortIndent, thumbLongIndent, thumbShortIndent, m_bgColor, m_bgColor);
383 break;
384
385 case VerticalScrollThumbType:
386 // Draw a shorter box on top of the outside box.
387 nestedBoxes(thumbShortIndent, thumbLongIndent, thumbShortIndent, thumbLongIndent, m_bgColor, m_bgColor);
388 break;
389
390 case HorizontalSliderThumbType:
391 // Slider thumbs are ovals.
392 oval(m_bgColor);
393 break;
394
395 case HorizontalScrollGripType: {
396 // Draw a horizontal crosshatch for the grip.
397 int longOffset = halfWidth - gripLongIndent;
398 line(m_left + gripLongIndent, m_top + halfHeight,
399 m_right - gripLongIndent, m_top + halfHeight, m_fgColor);
400 line(m_left + longOffset, m_top + gripShortIndent,
401 m_left + longOffset, m_bottom - gripShortIndent, m_fgColor);
402 line(m_right - longOffset, m_top + gripShortIndent,
403 m_right - longOffset, m_bottom - gripShortIndent, m_fgColor);
404 break;
405 }
406
407 case VerticalScrollGripType: {
408 // Draw a vertical crosshatch for the grip.
409 int longOffset = halfHeight - gripLongIndent;
410 line(m_left + halfWidth, m_top + gripLongIndent,
411 m_left + halfWidth, m_bottom - gripLongIndent, m_fgColor);
412 line(m_left + gripShortIndent, m_top + longOffset,
413 m_right - gripShortIndent, m_top + longOffset, m_fgColor);
414 line(m_left + gripShortIndent, m_bottom - longOffset,
415 m_right - gripShortIndent, m_bottom - longOffset, m_fgColor);
416 break;
417 }
418
419 case LeftArrowType:
420 // Draw a left arrow inside a box.
421 box(m_irect, m_bgColor);
422 triangle(m_right - quarterWidth, m_top + quarterHeight,
423 m_right - quarterWidth, m_bottom - quarterHeight,
424 m_left + quarterWidth, m_top + halfHeight, m_fgColor);
425 break;
426
427 case RightArrowType:
428 // Draw a left arrow inside a box.
429 box(m_irect, m_bgColor);
430 triangle(m_left + quarterWidth, m_top + quarterHeight,
431 m_right - quarterWidth, m_top + halfHeight,
432 m_left + quarterWidth, m_bottom - quarterHeight, m_fgColor);
433 break;
434
435 case UpArrowType:
436 // Draw an up arrow inside a box.
437 box(m_irect, m_bgColor);
438 triangle(m_left + quarterWidth, m_bottom - quarterHeight,
439 m_left + halfWidth, m_top + quarterHeight,
440 m_right - quarterWidth, m_bottom - quarterHeight, m_fgColor);
441 break;
442
443 case DownArrowType:
444 // Draw a down arrow inside a box.
445 box(m_irect, m_bgColor);
446 triangle(m_left + quarterWidth, m_top + quarterHeight,
447 m_right - quarterWidth, m_top + quarterHeight,
448 m_left + halfWidth, m_bottom - quarterHeight, m_fgColor);
449 break;
450
451 case HorizontalSliderTrackType: {
452 // Draw a narrow rect for the track plus box hatches on the ends.
453 SkIRect lirect;
454 lirect = m_irect;
455 lirect.inset(noOffset, halfHeight - sliderIndent);
456 box(lirect, m_bgColor);
457 line(m_left, m_top, m_left, m_bottom, m_edgeColor);
458 line(m_right, m_top, m_right, m_bottom, m_edgeColor);
459 break;
460 }
461
462 case DropDownButtonType:
463 // Draw a box with a big down arrow on top.
464 box(m_irect, m_bgColor);
465 triangle(m_left + quarterWidth, m_top,
466 m_right - quarterWidth, m_top,
467 m_left + halfWidth, m_bottom, m_fgColor);
468 break;
469
470 default:
471 ASSERT_NOT_REACHED();
472 CRASH();
473 break;
474 }
475
476 markState();
477 skia::EndPlatformPaint(m_canvas);
478 }
479
480 // Because rendering a text field is dependent on input
481 // parameters the other controls don't have, we render it directly
482 // rather than trying to overcomplicate draw() further.
drawTextField(bool drawEdges,bool fillContentArea,SkColor color)483 void WebThemeControlDRTWin::drawTextField(bool drawEdges, bool fillContentArea, SkColor color)
484 {
485 SkPaint paint;
486
487 skia::BeginPlatformPaint(m_canvas);
488 if (fillContentArea) {
489 paint.setColor(color);
490 paint.setStyle(SkPaint::kFill_Style);
491 m_canvas->drawIRect(m_irect, paint);
492 }
493 if (drawEdges) {
494 paint.setColor(m_edgeColor);
495 paint.setStyle(SkPaint::kStroke_Style);
496 m_canvas->drawIRect(m_irect, paint);
497 }
498
499 markState();
500 skia::EndPlatformPaint(m_canvas);
501 }
502
drawProgressBar(const SkIRect & fillRect)503 void WebThemeControlDRTWin::drawProgressBar(const SkIRect& fillRect)
504 {
505 SkPaint paint;
506
507 skia::BeginPlatformPaint(m_canvas);
508 paint.setColor(m_bgColor);
509 paint.setStyle(SkPaint::kFill_Style);
510 m_canvas->drawIRect(m_irect, paint);
511
512 // Emulate clipping
513 SkIRect tofill;
514 tofill.intersect(m_irect, fillRect);
515 paint.setColor(m_fgColor);
516 paint.setStyle(SkPaint::kFill_Style);
517 m_canvas->drawIRect(tofill, paint);
518
519 markState();
520 skia::EndPlatformPaint(m_canvas);
521 }
522
523