Home | History | Annotate | Download | only in chromium
      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 
     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 
     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 
    115 WebThemeControlDRTWin::~WebThemeControlDRTWin()
    116 {
    117 }
    118 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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