| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file implements a simple generic version of the WebKitThemeEngine, |
| // 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 "webkit/tools/test_shell/test_shell_webthemecontrol.h" |
| |
| #include "base/logging.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" |
| |
| namespace TestShellWebTheme { |
| |
| const SkColor kEdgeColor = SK_ColorBLACK; |
| const SkColor kReadOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6); |
| const SkColor kFgColor = SK_ColorBLACK; |
| |
| const SkColor kBgColors[] = { |
| 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 |
| }; |
| |
| SkIRect Validate(const SkIRect& rect, Control::Type ctype) { |
| SkIRect retval = rect; |
| if (ctype == Control::kUncheckedBox_Type || |
| ctype == Control::kCheckedBox_Type || |
| ctype == Control::kUncheckedRadio_Type || |
| ctype == Control::kCheckedRadio_Type) { |
| // The maximum width and height is 13. Center the square in the passed |
| // rectangle. |
| const int kMaxControlSize = 13; |
| int control_size = std::min(rect.width(), rect.height()); |
| control_size = std::min(control_size, kMaxControlSize); |
| |
| retval.fLeft = rect.fLeft + (rect.width() / 2) - (control_size / 2); |
| retval.fRight = retval.fLeft + control_size - 1; |
| retval.fTop = rect.fTop + (rect.height() / 2) - (control_size / 2); |
| retval.fBottom = retval.fTop + control_size - 1; |
| } |
| return retval; |
| } |
| |
| Control::Control(SkCanvas* canvas, const SkIRect& irect, |
| Type ctype, State cstate) |
| : canvas_(canvas), |
| irect_(Validate(irect, ctype)), |
| type_(ctype), |
| state_(cstate), |
| left_(irect_.fLeft), |
| right_(irect_.fRight), |
| top_(irect_.fTop), |
| bottom_(irect_.fBottom), |
| height_(irect_.height()), |
| width_(irect_.width()), |
| edge_color_(kEdgeColor), |
| bg_color_(kBgColors[cstate]), |
| fg_color_(kFgColor) { |
| } |
| |
| Control::~Control() { |
| } |
| |
| void Control::box(const SkIRect& rect, SkColor fill_color) { |
| SkPaint paint; |
| |
| paint.setStyle(SkPaint::kFill_Style); |
| paint.setColor(fill_color); |
| canvas_->drawIRect(rect, paint); |
| |
| paint.setColor(edge_color_); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas_->drawIRect(rect, paint); |
| } |
| |
| void Control::line(int x0, int y0, int x1, int y1, SkColor color) { |
| SkPaint paint; |
| paint.setColor(color); |
| canvas_->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), |
| SkIntToScalar(x1), SkIntToScalar(y1), |
| paint); |
| } |
| |
| void Control::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(); |
| canvas_->drawPath(path, paint); |
| |
| paint.setColor(edge_color_); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas_->drawPath(path, paint); |
| } |
| |
| void Control::roundRect(SkColor color) { |
| SkRect rect; |
| SkScalar radius = SkIntToScalar(5); |
| SkPaint paint; |
| |
| rect.set(irect_); |
| paint.setColor(color); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas_->drawRoundRect(rect, radius, radius, paint); |
| |
| paint.setColor(edge_color_); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas_->drawRoundRect(rect, radius, radius, paint); |
| } |
| |
| void Control::oval(SkColor color) { |
| SkRect rect; |
| SkPaint paint; |
| |
| rect.set(irect_); |
| paint.setColor(color); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas_->drawOval(rect, paint); |
| |
| paint.setColor(edge_color_); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas_->drawOval(rect, paint); |
| } |
| |
| void Control::circle(SkScalar radius, SkColor color) { |
| SkScalar cy = SkIntToScalar(top_ + height_ / 2); |
| SkScalar cx = SkIntToScalar(left_ + width_ / 2); |
| SkPaint paint; |
| |
| paint.setColor(color); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas_->drawCircle(cx, cy, radius, paint); |
| |
| paint.setColor(edge_color_); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas_->drawCircle(cx, cy, radius, paint); |
| } |
| |
| void Control::nested_boxes(int indent_left, int indent_top, |
| int indent_right, int indent_bottom, |
| SkColor outer_color, SkColor inner_color) { |
| SkIRect lirect; |
| box(irect_, outer_color); |
| lirect.set(irect_.fLeft + indent_left, irect_.fTop + indent_top, |
| irect_.fRight - indent_right, irect_.fBottom - indent_bottom); |
| box(lirect, inner_color); |
| } |
| |
| |
| void Control::markState() { |
| // The horizontal lines in a read only control are spaced by this amount. |
| const int kReadOnlyLineOffset = 5; |
| |
| // The length of a triangle side for the corner marks. |
| const int kTriangleSize = 5; |
| |
| switch (state_) { |
| case kUnknown_State: // FALLTHROUGH |
| case kDisabled_State: // FALLTHROUGH |
| case kNormal_State: |
| // Don't visually mark these states (color is enough). |
| break; |
| case kReadOnly_State: |
| // Drawing lines across the control. |
| for (int i = top_ + kReadOnlyLineOffset; i < bottom_ ; |
| i += kReadOnlyLineOffset) { |
| line(left_ + 1, i, right_ - 1, i, kReadOnlyColor); |
| } |
| break; |
| case kHot_State: |
| // Draw a triangle in the upper left corner of the control. |
| triangle(left_, top_, |
| left_ + kTriangleSize, top_, |
| left_, top_ + kTriangleSize, edge_color_); |
| break; |
| case kHover_State: |
| // Draw a triangle in the upper right corner of the control. |
| triangle(right_, top_, |
| right_, top_ + kTriangleSize, |
| right_ - kTriangleSize, top_, edge_color_); |
| break; |
| case kFocused_State: |
| // Draw a triangle in the bottom right corner of the control. |
| triangle(right_, bottom_, |
| right_ - kTriangleSize, bottom_, |
| right_, bottom_ - kTriangleSize, edge_color_); |
| break; |
| case kPressed_State: |
| // Draw a triangle in the bottom left corner of the control. |
| triangle(left_, bottom_, |
| left_, bottom_ - kTriangleSize, |
| left_ + kTriangleSize, bottom_, edge_color_); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| void Control::draw() { |
| int half_width = width_ / 2; |
| int half_height = height_ / 2; |
| int quarter_width = width_ / 4; |
| int quarter_height = height_ / 4; |
| |
| // Indent amounts for the check in a checkbox or radio button. |
| const int kCheckIndent = 3; |
| |
| // Indent amounts for short and long sides of the scrollbar notches. |
| const int kNotchLongOffset = 1; |
| const int kNotchShortOffset = 4; |
| const int kNoOffset = 0; |
| int short_offset; |
| int long_offset; |
| |
| // Indent amounts for the short and long sides of a scroll thumb box. |
| const int kThumbLongIndent = 0; |
| const int kThumbShortIndent = 2; |
| |
| // Indents for the crosshatch on a scroll grip. |
| const int kGripLongIndent = 3; |
| const int kGripShortIndent = 5; |
| |
| // Indents for the the slider track. |
| const int kSliderIndent = 2; |
| |
| skia::ScopedPlatformPaint scoped_platform_paint(canvas_); |
| switch (type_) { |
| case kUnknown_Type: |
| NOTREACHED(); |
| break; |
| case kTextField_Type: |
| // We render this by hand outside of this function. |
| NOTREACHED(); |
| break; |
| case kPushButton_Type: |
| // push buttons render as a rounded rectangle |
| roundRect(bg_color_); |
| break; |
| case kUncheckedBox_Type: |
| // Unchecked boxes are simply plain boxes. |
| box(irect_, bg_color_); |
| break; |
| case kCheckedBox_Type: |
| nested_boxes(kCheckIndent, kCheckIndent, kCheckIndent, kCheckIndent, |
| bg_color_, fg_color_); |
| break; |
| case kIndeterminateCheckBox_Type: |
| // Indeterminate checkbox is a box containing '-'. |
| nested_boxes(kCheckIndent, height_ / 2, kCheckIndent, height_ / 2, |
| bg_color_, fg_color_); |
| break; |
| case kUncheckedRadio_Type: |
| circle(SkIntToScalar(half_height), bg_color_); |
| break; |
| case kCheckedRadio_Type: |
| circle(SkIntToScalar(half_height), bg_color_); |
| circle(SkIntToScalar(half_height - kCheckIndent), fg_color_); |
| break; |
| case kHorizontalScrollTrackBack_Type: |
| // Draw a box with a notch at the left. |
| long_offset = half_height - kNotchLongOffset; |
| short_offset = width_ - kNotchShortOffset; |
| nested_boxes(kNoOffset, long_offset, short_offset, long_offset, |
| bg_color_, edge_color_); |
| break; |
| case kHorizontalScrollTrackForward_Type: |
| // Draw a box with a notch at the right. |
| long_offset = half_height - kNotchLongOffset; |
| short_offset = width_ - kNotchShortOffset; |
| nested_boxes(short_offset, long_offset, kNoOffset, long_offset, |
| bg_color_, fg_color_); |
| break; |
| case kVerticalScrollTrackBack_Type: |
| // Draw a box with a notch at the top. |
| long_offset = half_width - kNotchLongOffset; |
| short_offset = height_ - kNotchShortOffset; |
| nested_boxes(long_offset, kNoOffset, long_offset, short_offset, |
| bg_color_, fg_color_); |
| break; |
| case kVerticalScrollTrackForward_Type: |
| // Draw a box with a notch at the bottom. |
| long_offset = half_width - kNotchLongOffset; |
| short_offset = height_ - kNotchShortOffset; |
| nested_boxes(long_offset, short_offset, long_offset, kNoOffset, |
| bg_color_, fg_color_); |
| break; |
| case kHorizontalScrollThumb_Type: |
| // Draw a narrower box on top of the outside box. |
| nested_boxes(kThumbLongIndent, kThumbShortIndent, kThumbLongIndent, |
| kThumbShortIndent, bg_color_, bg_color_); |
| break; |
| case kVerticalScrollThumb_Type: |
| // Draw a shorter box on top of the outside box. |
| nested_boxes(kThumbShortIndent, kThumbLongIndent, kThumbShortIndent, |
| kThumbLongIndent, bg_color_, bg_color_); |
| break; |
| case kHorizontalSliderThumb_Type: |
| // Slider thumbs are ovals. |
| oval(bg_color_); |
| break; |
| case kHorizontalScrollGrip_Type: |
| // Draw a horizontal crosshatch for the grip. |
| long_offset = half_width - kGripLongIndent; |
| line(left_ + kGripLongIndent, top_ + half_height, |
| right_ - kGripLongIndent, top_ + half_height, fg_color_); |
| line(left_ + long_offset, top_ + kGripShortIndent, |
| left_ + long_offset, bottom_ - kGripShortIndent, fg_color_); |
| line(right_ - long_offset, top_ + kGripShortIndent, |
| right_ - long_offset, bottom_ - kGripShortIndent, fg_color_); |
| break; |
| case kVerticalScrollGrip_Type: |
| // Draw a vertical crosshatch for the grip. |
| long_offset = half_height - kGripLongIndent; |
| line(left_ + half_width, top_ + kGripLongIndent, |
| left_ + half_width, bottom_ - kGripLongIndent, fg_color_); |
| line(left_ + kGripShortIndent, top_ + long_offset, |
| right_ - kGripShortIndent, top_ + long_offset, fg_color_); |
| line(left_ + kGripShortIndent, bottom_ - long_offset, |
| right_ - kGripShortIndent, bottom_ - long_offset, fg_color_); |
| break; |
| case kLeftArrow_Type: |
| // Draw a left arrow inside a box. |
| box(irect_, bg_color_); |
| triangle(right_ - quarter_width, top_ + quarter_height, |
| right_ - quarter_width, bottom_ - quarter_height, |
| left_ + quarter_width, top_ + half_height, fg_color_); |
| break; |
| case kRightArrow_Type: |
| // Draw a left arrow inside a box. |
| box(irect_, bg_color_); |
| triangle(left_ + quarter_width, top_ + quarter_height, |
| right_ - quarter_width, top_ + half_height, |
| left_ + quarter_width, bottom_ - quarter_height, fg_color_); |
| break; |
| case kUpArrow_Type: |
| // Draw an up arrow inside a box. |
| box(irect_, bg_color_); |
| triangle(left_ + quarter_width, bottom_ - quarter_height, |
| left_ + half_width, top_ + quarter_height, |
| right_ - quarter_width, bottom_ - quarter_height, fg_color_); |
| break; |
| case kDownArrow_Type: |
| // Draw a down arrow inside a box. |
| box(irect_, bg_color_); |
| triangle(left_ + quarter_width, top_ + quarter_height, |
| right_ - quarter_width, top_ + quarter_height, |
| left_ + half_width, bottom_ - quarter_height, fg_color_); |
| break; |
| case kHorizontalSliderTrack_Type: |
| // Draw a narrow rect for the track plus box hatches on the ends. |
| SkIRect lirect; |
| lirect = irect_; |
| lirect.inset(kNoOffset, half_height - kSliderIndent); |
| box(lirect, bg_color_); |
| line(left_, top_, left_, bottom_, edge_color_); |
| line(right_, top_, right_, bottom_, edge_color_); |
| break; |
| case kDropDownButton_Type: |
| // Draw a box with a big down arrow on top. |
| box(irect_, bg_color_); |
| triangle(left_ + quarter_width, top_, |
| right_ - quarter_width, top_, |
| left_ + half_width, bottom_, fg_color_); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| |
| markState(); |
| } |
| |
| // 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 Control::drawTextField(bool draw_edges, bool fill_content_area, |
| SkColor color) { |
| SkPaint paint; |
| |
| skia::ScopedPlatformPaint scoped_platform_paint(canvas_); |
| if (fill_content_area) { |
| paint.setColor(color); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas_->drawIRect(irect_, paint); |
| } |
| if (draw_edges) { |
| paint.setColor(edge_color_); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas_->drawIRect(irect_, paint); |
| } |
| |
| markState(); |
| } |
| |
| void |
| Control::drawProgressBar(const SkIRect& fill_rect) { |
| SkPaint paint; |
| |
| skia::ScopedPlatformPaint scoped_platform_paint(canvas_); |
| paint.setColor(bg_color_); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas_->drawIRect(irect_, paint); |
| |
| // Emulate clipping |
| SkIRect tofill; |
| tofill.intersect(irect_, fill_rect); |
| paint.setColor(fg_color_); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas_->drawIRect(tofill, paint); |
| |
| markState(); |
| } |
| |
| } // namespace TestShellWebTheme |
| |