/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This file implements a simple generic version of the WebThemeEngine,
// which is used to draw all the native controls on a web page. We use this
// file when running in layout test mode in order to remove any
// platform-specific rendering differences due to themes, colors, etc.
//
#include "config.h"
#include "WebThemeControlDRTWin.h"
#include "skia/ext/platform_canvas.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkRect.h"
#include <wtf/Assertions.h>
using namespace std;
static const SkColor edgeColor = SK_ColorBLACK;
static const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6);
static const SkColor fgColor = SK_ColorBLACK;
static const SkColor bgColors[] = {
SK_ColorBLACK, // Unknown
SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled
SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly
SkColorSetRGB(0x89, 0xc4, 0xff), // Normal
SkColorSetRGB(0x43, 0xf9, 0xff), // Hot
SkColorSetRGB(0x20, 0xf6, 0xcc), // Focused
SkColorSetRGB(0x00, 0xf3, 0xac), // Hover
SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed
SkColorSetRGB(0xcc, 0xcc, 0xcc) // Indeterminate
};
static SkIRect validate(const SkIRect& rect, WebThemeControlDRTWin::Type ctype)
{
switch (ctype) {
case WebThemeControlDRTWin::UncheckedBoxType:
case WebThemeControlDRTWin::CheckedBoxType:
case WebThemeControlDRTWin::UncheckedRadioType:
case WebThemeControlDRTWin::CheckedRadioType: {
SkIRect retval = rect;
// The maximum width and height is 13.
// Center the square in the passed rectangle.
const int maxControlSize = 13;
int controlSize = min(rect.width(), rect.height());
controlSize = min(controlSize, maxControlSize);
retval.fLeft = rect.fLeft + (rect.width() / 2) - (controlSize / 2);
retval.fRight = retval.fLeft + controlSize - 1;
retval.fTop = rect.fTop + (rect.height() / 2) - (controlSize / 2);
retval.fBottom = retval.fTop + controlSize - 1;
return retval;
}
default:
return rect;
}
}
// WebThemeControlDRTWin
WebThemeControlDRTWin::WebThemeControlDRTWin(SkCanvas* canvas,
const SkIRect& irect,
Type ctype,
State cstate)
: m_canvas(canvas)
, m_irect(validate(irect, ctype))
, m_type(ctype)
, m_state(cstate)
, m_left(m_irect.fLeft)
, m_right(m_irect.fRight)
, m_top(m_irect.fTop)
, m_bottom(m_irect.fBottom)
, m_height(m_irect.height())
, m_width(m_irect.width())
, m_edgeColor(edgeColor)
, m_bgColor(bgColors[cstate])
, m_fgColor(fgColor)
{
}
WebThemeControlDRTWin::~WebThemeControlDRTWin()
{
}
void WebThemeControlDRTWin::box(const SkIRect& rect, SkColor fillColor)
{
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(fillColor);
m_canvas->drawIRect(rect, paint);
paint.setColor(m_edgeColor);
paint.setStyle(SkPaint::kStroke_Style);
m_canvas->drawIRect(rect, paint);
}
void WebThemeControlDRTWin::line(int x0, int y0, int x1, int y1, SkColor color)
{
SkPaint paint;
paint.setColor(color);
m_canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0),
SkIntToScalar(x1), SkIntToScalar(y1),
paint);
}
void WebThemeControlDRTWin::triangle(int x0, int y0,
int x1, int y1,
int x2, int y2,
SkColor color)
{
SkPath path;
SkPaint paint;
paint.setColor(color);
paint.setStyle(SkPaint::kFill_Style);
path.incReserve(4);
path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0));
path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1));
path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2));
path.close();
m_canvas->drawPath(path, paint);
paint.setColor(m_edgeColor);
paint.setStyle(SkPaint::kStroke_Style);
m_canvas->drawPath(path, paint);
}
void WebThemeControlDRTWin::roundRect(SkColor color)
{
SkRect rect;
SkScalar radius = SkIntToScalar(5);
SkPaint paint;
rect.set(m_irect);
paint.setColor(color);
paint.setStyle(SkPaint::kFill_Style);
m_canvas->drawRoundRect(rect, radius, radius, paint);
paint.setColor(m_edgeColor);
paint.setStyle(SkPaint::kStroke_Style);
m_canvas->drawRoundRect(rect, radius, radius, paint);
}
void WebThemeControlDRTWin::oval(SkColor color)
{
SkRect rect;
SkPaint paint;
rect.set(m_irect);
paint.setColor(color);
paint.setStyle(SkPaint::kFill_Style);
m_canvas->drawOval(rect, paint);
paint.setColor(m_edgeColor);
paint.setStyle(SkPaint::kStroke_Style);
m_canvas->drawOval(rect, paint);
}
void WebThemeControlDRTWin::circle(SkScalar radius, SkColor color)
{
SkScalar cy = SkIntToScalar(m_top + m_height / 2);
SkScalar cx = SkIntToScalar(m_left + m_width / 2);
SkPaint paint;
paint.setColor(color);
paint.setStyle(SkPaint::kFill_Style);
m_canvas->drawCircle(cx, cy, radius, paint);
paint.setColor(m_edgeColor);
paint.setStyle(SkPaint::kStroke_Style);
m_canvas->drawCircle(cx, cy, radius, paint);
}
void WebThemeControlDRTWin::nestedBoxes(int indentLeft,
int indentTop,
int indentRight,
int indentBottom,
SkColor outerColor,
SkColor innerColor)
{
SkIRect lirect;
box(m_irect, outerColor);
lirect.set(m_irect.fLeft + indentLeft,
m_irect.fTop + indentTop,
m_irect.fRight - indentRight,
m_irect.fBottom - indentBottom);
box(lirect, innerColor);
}
void WebThemeControlDRTWin::markState()
{
// The horizontal lines in a read only control are spaced by this amount.
const int readOnlyLineOffset = 5;
// The length of a triangle side for the corner marks.
const int triangleSize = 5;
switch (m_state) {
case UnknownState:
case DisabledState:
case NormalState:
// Don't visually mark these states (color is enough).
break;
case ReadOnlyState:
// Drawing lines across the control.
for (int i = m_top + readOnlyLineOffset; i < m_bottom; i += readOnlyLineOffset)
line(m_left + 1, i, m_right - 1, i, readOnlyColor);
break;
case HotState:
// Draw a triangle in the upper left corner of the control.
triangle(m_left, m_top,
m_left + triangleSize, m_top,
m_left, m_top + triangleSize, m_edgeColor);
break;
case HoverState:
// Draw a triangle in the upper right corner of the control.
triangle(m_right, m_top,
m_right, m_top + triangleSize,
m_right - triangleSize, m_top, m_edgeColor);
break;
case FocusedState:
// Draw a triangle in the bottom right corner of the control.
triangle(m_right, m_bottom,
m_right - triangleSize, m_bottom,
m_right, m_bottom - triangleSize, m_edgeColor);
break;
case PressedState:
// Draw a triangle in the bottom left corner of the control.
triangle(m_left, m_bottom,
m_left, m_bottom - triangleSize,
m_left + triangleSize, m_bottom, m_edgeColor);
break;
default:
ASSERT_NOT_REACHED();
CRASH();
break;
}
}
void WebThemeControlDRTWin::draw()
{
int halfWidth = m_width / 2;
int halfHeight = m_height / 2;
int quarterWidth = m_width / 4;
int quarterHeight = m_height / 4;
// Indent amounts for the check in a checkbox or radio button.
const int checkIndent = 3;
// Indent amounts for short and long sides of the scrollbar notches.
const int notchLongOffset = 1;
const int notchShortOffset = 4;
const int noOffset = 0;
// Indent amounts for the short and long sides of a scroll thumb box.
const int thumbLongIndent = 0;
const int thumbShortIndent = 2;
// Indents for the crosshatch on a scroll grip.
const int gripLongIndent = 3;
const int gripShortIndent = 5;
// Indents for the the slider track.
const int sliderIndent = 2;
skia::BeginPlatformPaint(m_canvas);
switch (m_type) {
case UnknownType:
ASSERT_NOT_REACHED();
CRASH();
break;
case TextFieldType:
// We render this by hand outside of this function.
ASSERT_NOT_REACHED();
CRASH();
break;
case PushButtonType:
// push buttons render as a rounded rectangle
roundRect(m_bgColor);
break;
case UncheckedBoxType:
// Unchecked boxes are simply plain boxes.
box(m_irect, m_bgColor);
break;
case CheckedBoxType:
nestedBoxes(checkIndent, checkIndent, checkIndent, checkIndent, m_bgColor, m_fgColor);
break;
case IndeterminateCheckboxType:
// Indeterminate checkbox is a box containing '-'.
nestedBoxes(checkIndent, halfHeight, checkIndent, halfHeight, m_bgColor, m_fgColor);
break;
case UncheckedRadioType:
circle(SkIntToScalar(halfHeight), m_bgColor);
break;
case CheckedRadioType:
circle(SkIntToScalar(halfHeight), m_bgColor);
circle(SkIntToScalar(halfHeight - checkIndent), m_fgColor);
break;
case HorizontalScrollTrackBackType: {
// Draw a box with a notch at the left.
int longOffset = halfHeight - notchLongOffset;
int shortOffset = m_width - notchShortOffset;
nestedBoxes(noOffset, longOffset, shortOffset, longOffset, m_bgColor, m_edgeColor);
break;
}
case HorizontalScrollTrackForwardType: {
// Draw a box with a notch at the right.
int longOffset = halfHeight - notchLongOffset;
int shortOffset = m_width - notchShortOffset;
nestedBoxes(shortOffset, longOffset, noOffset, longOffset, m_bgColor, m_fgColor);
break;
}
case VerticalScrollTrackBackType: {
// Draw a box with a notch at the top.
int longOffset = halfWidth - notchLongOffset;
int shortOffset = m_height - notchShortOffset;
nestedBoxes(longOffset, noOffset, longOffset, shortOffset, m_bgColor, m_fgColor);
break;
}
case VerticalScrollTrackForwardType: {
// Draw a box with a notch at the bottom.
int longOffset = halfWidth - notchLongOffset;
int shortOffset = m_height - notchShortOffset;
nestedBoxes(longOffset, shortOffset, longOffset, noOffset, m_bgColor, m_fgColor);
break;
}
case HorizontalScrollThumbType:
// Draw a narrower box on top of the outside box.
nestedBoxes(thumbLongIndent, thumbShortIndent, thumbLongIndent, thumbShortIndent, m_bgColor, m_bgColor);
break;
case VerticalScrollThumbType:
// Draw a shorter box on top of the outside box.
nestedBoxes(thumbShortIndent, thumbLongIndent, thumbShortIndent, thumbLongIndent, m_bgColor, m_bgColor);
break;
case HorizontalSliderThumbType:
// Slider thumbs are ovals.
oval(m_bgColor);
break;
case HorizontalScrollGripType: {
// Draw a horizontal crosshatch for the grip.
int longOffset = halfWidth - gripLongIndent;
line(m_left + gripLongIndent, m_top + halfHeight,
m_right - gripLongIndent, m_top + halfHeight, m_fgColor);
line(m_left + longOffset, m_top + gripShortIndent,
m_left + longOffset, m_bottom - gripShortIndent, m_fgColor);
line(m_right - longOffset, m_top + gripShortIndent,
m_right - longOffset, m_bottom - gripShortIndent, m_fgColor);
break;
}
case VerticalScrollGripType: {
// Draw a vertical crosshatch for the grip.
int longOffset = halfHeight - gripLongIndent;
line(m_left + halfWidth, m_top + gripLongIndent,
m_left + halfWidth, m_bottom - gripLongIndent, m_fgColor);
line(m_left + gripShortIndent, m_top + longOffset,
m_right - gripShortIndent, m_top + longOffset, m_fgColor);
line(m_left + gripShortIndent, m_bottom - longOffset,
m_right - gripShortIndent, m_bottom - longOffset, m_fgColor);
break;
}
case LeftArrowType:
// Draw a left arrow inside a box.
box(m_irect, m_bgColor);
triangle(m_right - quarterWidth, m_top + quarterHeight,
m_right - quarterWidth, m_bottom - quarterHeight,
m_left + quarterWidth, m_top + halfHeight, m_fgColor);
break;
case RightArrowType:
// Draw a left arrow inside a box.
box(m_irect, m_bgColor);
triangle(m_left + quarterWidth, m_top + quarterHeight,
m_right - quarterWidth, m_top + halfHeight,
m_left + quarterWidth, m_bottom - quarterHeight, m_fgColor);
break;
case UpArrowType:
// Draw an up arrow inside a box.
box(m_irect, m_bgColor);
triangle(m_left + quarterWidth, m_bottom - quarterHeight,
m_left + halfWidth, m_top + quarterHeight,
m_right - quarterWidth, m_bottom - quarterHeight, m_fgColor);
break;
case DownArrowType:
// Draw a down arrow inside a box.
box(m_irect, m_bgColor);
triangle(m_left + quarterWidth, m_top + quarterHeight,
m_right - quarterWidth, m_top + quarterHeight,
m_left + halfWidth, m_bottom - quarterHeight, m_fgColor);
break;
case HorizontalSliderTrackType: {
// Draw a narrow rect for the track plus box hatches on the ends.
SkIRect lirect;
lirect = m_irect;
lirect.inset(noOffset, halfHeight - sliderIndent);
box(lirect, m_bgColor);
line(m_left, m_top, m_left, m_bottom, m_edgeColor);
line(m_right, m_top, m_right, m_bottom, m_edgeColor);
break;
}
case DropDownButtonType:
// Draw a box with a big down arrow on top.
box(m_irect, m_bgColor);
triangle(m_left + quarterWidth, m_top,
m_right - quarterWidth, m_top,
m_left + halfWidth, m_bottom, m_fgColor);
break;
default:
ASSERT_NOT_REACHED();
CRASH();
break;
}
markState();
skia::EndPlatformPaint(m_canvas);
}
// Because rendering a text field is dependent on input
// parameters the other controls don't have, we render it directly
// rather than trying to overcomplicate draw() further.
void WebThemeControlDRTWin::drawTextField(bool drawEdges, bool fillContentArea, SkColor color)
{
SkPaint paint;
skia::BeginPlatformPaint(m_canvas);
if (fillContentArea) {
paint.setColor(color);
paint.setStyle(SkPaint::kFill_Style);
m_canvas->drawIRect(m_irect, paint);
}
if (drawEdges) {
paint.setColor(m_edgeColor);
paint.setStyle(SkPaint::kStroke_Style);
m_canvas->drawIRect(m_irect, paint);
}
markState();
skia::EndPlatformPaint(m_canvas);
}
void WebThemeControlDRTWin::drawProgressBar(const SkIRect& fillRect)
{
SkPaint paint;
skia::BeginPlatformPaint(m_canvas);
paint.setColor(m_bgColor);
paint.setStyle(SkPaint::kFill_Style);
m_canvas->drawIRect(m_irect, paint);
// Emulate clipping
SkIRect tofill;
tofill.intersect(m_irect, fillRect);
paint.setColor(m_fgColor);
paint.setStyle(SkPaint::kFill_Style);
m_canvas->drawIRect(tofill, paint);
markState();
skia::EndPlatformPaint(m_canvas);
}