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
48using namespace std;
49
50static const SkColor edgeColor     = SK_ColorBLACK;
51static const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
52static const SkColor fgColor       = SK_ColorBLACK;
53static 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
65static 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
95WebThemeControlDRTWin::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
115WebThemeControlDRTWin::~WebThemeControlDRTWin()
116{
117}
118
119void 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
132void 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
141void 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
163void 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
179void 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
194void 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
209void 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
225void 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
280void 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.
483void 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
503void 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