blob: 7a1260db0a139f9f3f8a823af2c220f36162812a [file] [log] [blame]
/*
* Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009 Google, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#import "third_party/blink/renderer/core/paint/theme_painter_mac.h"
#import <AvailabilityMacros.h>
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#import <math.h>
#import "third_party/blink/renderer/core/frame/local_frame_view.h"
#import "third_party/blink/renderer/core/layout/layout_progress.h"
#import "third_party/blink/renderer/core/layout/layout_theme_mac.h"
#import "third_party/blink/renderer/core/layout/layout_view.h"
#import "third_party/blink/renderer/core/paint/paint_info.h"
#import "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
#import "third_party/blink/renderer/platform/graphics/bitmap_image.h"
#import "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
#import "third_party/blink/renderer/platform/graphics/image.h"
#import "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
#import "third_party/blink/renderer/platform/mac/block_exceptions.h"
#import "third_party/blink/renderer/platform/mac/color_mac.h"
#import "third_party/blink/renderer/platform/mac/local_current_graphics_context.h"
#import "third_party/blink/renderer/platform/mac/theme_mac.h"
#import "third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h"
// The methods in this file are specific to the Mac OS X platform.
// Forward declare Mac SPIs.
extern "C" {
void _NSDrawCarbonThemeBezel(NSRect frame, BOOL enabled, BOOL flipped);
// Request for public API: rdar://13787640
void _NSDrawCarbonThemeListBox(NSRect frame,
BOOL enabled,
BOOL flipped,
BOOL always_yes);
}
namespace blink {
ThemePainterMac::ThemePainterMac(LayoutThemeMac& layout_theme)
: ThemePainter(), layout_theme_(layout_theme) {}
bool ThemePainterMac::PaintTextField(const Node* node,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& r) {
LocalCurrentGraphicsContext local_context(paint_info.context, r);
bool use_ns_text_field_cell =
style.HasAppearance() &&
style.VisitedDependentColor(GetCSSPropertyBackgroundColor()) ==
Color::kWhite &&
!style.HasBackgroundImage();
// We do not use NSTextFieldCell to draw styled text fields since it induces a
// behavior change while remaining a fragile solution.
// https://bugs.chromium.org/p/chromium/issues/detail?id=658085#c3
if (!use_ns_text_field_cell) {
_NSDrawCarbonThemeBezel(
CGRect(r),
LayoutTheme::IsEnabled(node) && !LayoutTheme::IsReadOnlyControl(node),
YES);
return false;
}
NSTextFieldCell* text_field = layout_theme_.TextField();
GraphicsContextStateSaver state_saver(paint_info.context);
[text_field setEnabled:(LayoutTheme::IsEnabled(node) &&
!LayoutTheme::IsReadOnlyControl(node))];
[text_field drawWithFrame:NSRect(r) inView:layout_theme_.DocumentView()];
[text_field setControlView:nil];
return false;
}
bool ThemePainterMac::PaintCapsLockIndicator(const LayoutObject& o,
const PaintInfo& paint_info,
const IntRect& r) {
// This draws the caps lock indicator as it was done by
// WKDrawCapsLockIndicator.
LocalCurrentGraphicsContext local_context(paint_info.context, r);
CGContextRef c = local_context.CgContext();
CGMutablePathRef shape = CGPathCreateMutable();
// To draw the caps lock indicator, draw the shape into a small
// square that is then scaled to the size of r.
const CGFloat kSquareSize = 17;
// Create a rounted square shape.
CGPathMoveToPoint(shape, NULL, 16.5, 4.5);
CGPathAddArc(shape, NULL, 12.5, 12.5, 4, 0, M_PI_2, false);
CGPathAddArc(shape, NULL, 4.5, 12.5, 4, M_PI_2, M_PI, false);
CGPathAddArc(shape, NULL, 4.5, 4.5, 4, M_PI, 3 * M_PI / 2, false);
CGPathAddArc(shape, NULL, 12.5, 4.5, 4, 3 * M_PI / 2, 0, false);
// Draw the arrow - note this is drawing in a flipped coordinate system, so
// the arrow is pointing down.
CGPathMoveToPoint(shape, NULL, 8.5, 2); // Tip point.
CGPathAddLineToPoint(shape, NULL, 4, 7);
CGPathAddLineToPoint(shape, NULL, 6.25, 7);
CGPathAddLineToPoint(shape, NULL, 6.25, 10.25);
CGPathAddLineToPoint(shape, NULL, 10.75, 10.25);
CGPathAddLineToPoint(shape, NULL, 10.75, 7);
CGPathAddLineToPoint(shape, NULL, 13, 7);
CGPathAddLineToPoint(shape, NULL, 8.5, 2);
// Draw the rectangle that underneath (or above in the flipped system) the
// arrow.
CGPathAddLineToPoint(shape, NULL, 10.75, 12);
CGPathAddLineToPoint(shape, NULL, 6.25, 12);
CGPathAddLineToPoint(shape, NULL, 6.25, 14.25);
CGPathAddLineToPoint(shape, NULL, 10.75, 14.25);
CGPathAddLineToPoint(shape, NULL, 10.75, 12);
// Scale and translate the shape.
CGRect cgr = CGRect(r);
CGFloat max_x = CGRectGetMaxX(cgr);
CGFloat min_x = CGRectGetMinX(cgr);
CGFloat min_y = CGRectGetMinY(cgr);
CGFloat height_scale = r.Height() / kSquareSize;
const bool is_rtl = o.StyleRef().Direction() == TextDirection::kRtl;
CGAffineTransform transform = CGAffineTransformMake(
height_scale, 0, // A B
0, height_scale, // C D
is_rtl ? min_x : max_x - r.Height(), min_y); // Tx Ty
CGMutablePathRef paint_path = CGPathCreateMutable();
CGPathAddPath(paint_path, &transform, shape);
CGPathRelease(shape);
CGContextSetRGBFillColor(c, 0, 0, 0, 0.4);
CGContextBeginPath(c);
CGContextAddPath(c, paint_path);
CGContextFillPath(c);
CGPathRelease(paint_path);
return false;
}
bool ThemePainterMac::PaintTextArea(const Node* node,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& r) {
LocalCurrentGraphicsContext local_context(paint_info.context, r);
_NSDrawCarbonThemeListBox(
CGRect(r),
LayoutTheme::IsEnabled(node) && !LayoutTheme::IsReadOnlyControl(node),
YES, YES);
return false;
}
bool ThemePainterMac::PaintMenuList(const Node* node,
const Document&,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& r) {
layout_theme_.SetPopupButtonCellState(node, style, r);
NSPopUpButtonCell* popup_button = layout_theme_.PopupButton();
float zoom_level = style.EffectiveZoom();
IntSize size = layout_theme_.PopupButtonSizes()[[popup_button controlSize]];
size.SetHeight(size.Height() * zoom_level);
size.SetWidth(r.Width());
// Now inflate it to account for the shadow.
IntRect inflated_rect = r;
if (r.Width() >= layout_theme_.MinimumMenuListSize(style))
inflated_rect = ThemeMac::InflateRect(
inflated_rect, size, layout_theme_.PopupButtonMargins(), zoom_level);
LocalCurrentGraphicsContext local_context(
paint_info.context, ThemeMac::InflateRectForFocusRing(inflated_rect));
if (zoom_level != 1.0f) {
inflated_rect.SetWidth(inflated_rect.Width() / zoom_level);
inflated_rect.SetHeight(inflated_rect.Height() / zoom_level);
paint_info.context.Translate(inflated_rect.X(), inflated_rect.Y());
paint_info.context.Scale(zoom_level, zoom_level);
paint_info.context.Translate(-inflated_rect.X(), -inflated_rect.Y());
}
NSView* view = layout_theme_.DocumentView();
[popup_button drawWithFrame:CGRect(inflated_rect) inView:view];
if (LayoutTheme::IsFocused(node) && style.OutlineStyleIsAuto())
[popup_button cr_drawFocusRingWithFrame:CGRect(inflated_rect) inView:view];
[popup_button setControlView:nil];
return false;
}
bool ThemePainterMac::PaintProgressBar(const LayoutObject& layout_object,
const PaintInfo& paint_info,
const IntRect& rect) {
if (!layout_object.IsProgress())
return true;
const LayoutProgress& layout_progress = ToLayoutProgress(layout_object);
HIThemeTrackDrawInfo track_info;
track_info.version = 0;
NSControlSize control_size =
layout_theme_.ControlSizeForFont(layout_object.StyleRef());
if (control_size == NSRegularControlSize)
track_info.kind = layout_progress.GetPosition() < 0
? kThemeLargeIndeterminateBar
: kThemeLargeProgressBar;
else
track_info.kind = layout_progress.GetPosition() < 0
? kThemeMediumIndeterminateBar
: kThemeMediumProgressBar;
track_info.bounds = CGRect(IntRect(IntPoint(), rect.Size()));
track_info.min = 0;
track_info.max = std::numeric_limits<SInt32>::max();
track_info.value =
lround(layout_progress.GetPosition() * nextafter(track_info.max, 0));
track_info.trackInfo.progress.phase =
lround(layout_progress.AnimationProgress() *
nextafter(LayoutThemeMac::kProgressAnimationNumFrames, 0));
track_info.attributes = kThemeTrackHorizontal;
track_info.enableState = LayoutTheme::IsActive(layout_object.GetNode())
? kThemeTrackActive
: kThemeTrackInactive;
track_info.reserved = 0;
track_info.filler1 = 0;
GraphicsContextStateSaver state_saver(paint_info.context);
paint_info.context.Translate(rect.X(), rect.Y());
if (!layout_progress.StyleRef().IsLeftToRightDirection()) {
paint_info.context.Translate(rect.Width(), 0);
paint_info.context.Scale(-1, 1);
}
IntRect clip_rect = IntRect(IntPoint(), rect.Size());
LocalCurrentGraphicsContext local_context(paint_info.context, clip_rect);
CGContextRef cg_context = local_context.CgContext();
HIThemeDrawTrack(&track_info, 0, cg_context, kHIThemeOrientationNormal);
return false;
}
bool ThemePainterMac::PaintMenuListButton(const Node* node,
const Document&,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& r) {
IntRect bounds =
IntRect(r.X() + style.BorderLeftWidth(), r.Y() + style.BorderTopWidth(),
r.Width() - style.BorderLeftWidth() - style.BorderRightWidth(),
r.Height() - style.BorderTopWidth() - style.BorderBottomWidth());
// Since we actually know the size of the control here, we restrict the font
// scale to make sure the arrows will fit vertically in the bounds
float font_scale = std::min(
style.FontSize() / LayoutThemeMac::kBaseFontSize,
bounds.Height() / (LayoutThemeMac::kMenuListBaseArrowHeight * 2 +
LayoutThemeMac::kMenuListBaseSpaceBetweenArrows));
float center_y = bounds.Y() + bounds.Height() / 2.0f;
float arrow_height = LayoutThemeMac::kMenuListBaseArrowHeight * font_scale;
float arrow_width = LayoutThemeMac::kMenuListBaseArrowWidth * font_scale;
float space_between_arrows =
LayoutThemeMac::kMenuListBaseSpaceBetweenArrows * font_scale;
float scaled_padding_end =
LayoutThemeMac::kMenuListArrowPaddingEnd * style.EffectiveZoom();
float left_edge;
if (style.Direction() == TextDirection::kLtr) {
left_edge = bounds.MaxX() - scaled_padding_end - arrow_width;
} else {
left_edge = bounds.X() + scaled_padding_end;
}
if (bounds.Width() < arrow_width + scaled_padding_end)
return false;
Color color = style.VisitedDependentColor(GetCSSPropertyColor());
PaintFlags flags = paint_info.context.FillFlags();
flags.setAntiAlias(true);
flags.setColor(color.Rgb());
SkPath arrow1;
arrow1.moveTo(left_edge, center_y - space_between_arrows / 2.0f);
arrow1.lineTo(left_edge + arrow_width,
center_y - space_between_arrows / 2.0f);
arrow1.lineTo(left_edge + arrow_width / 2.0f,
center_y - space_between_arrows / 2.0f - arrow_height);
// Draw the top arrow.
paint_info.context.DrawPath(arrow1, flags);
SkPath arrow2;
arrow2.moveTo(left_edge, center_y + space_between_arrows / 2.0f);
arrow2.lineTo(left_edge + arrow_width,
center_y + space_between_arrows / 2.0f);
arrow2.lineTo(left_edge + arrow_width / 2.0f,
center_y + space_between_arrows / 2.0f + arrow_height);
// Draw the bottom arrow.
paint_info.context.DrawPath(arrow2, flags);
return false;
}
bool ThemePainterMac::PaintSliderTrack(const LayoutObject& o,
const PaintInfo& paint_info,
const IntRect& r) {
PaintSliderTicks(o, paint_info, r);
float zoom_level = o.StyleRef().EffectiveZoom();
FloatRect unzoomed_rect(r);
if (o.StyleRef().Appearance() == kSliderHorizontalPart ||
o.StyleRef().Appearance() == kMediaSliderPart) {
unzoomed_rect.SetY(
ceilf(unzoomed_rect.Y() + unzoomed_rect.Height() / 2 -
zoom_level * LayoutThemeMac::kSliderTrackWidth / 2));
unzoomed_rect.SetHeight(zoom_level * LayoutThemeMac::kSliderTrackWidth);
} else if (o.StyleRef().Appearance() == kSliderVerticalPart) {
unzoomed_rect.SetX(
ceilf(unzoomed_rect.X() + unzoomed_rect.Width() / 2 -
zoom_level * LayoutThemeMac::kSliderTrackWidth / 2));
unzoomed_rect.SetWidth(zoom_level * LayoutThemeMac::kSliderTrackWidth);
}
if (zoom_level != 1) {
unzoomed_rect.SetWidth(unzoomed_rect.Width() / zoom_level);
unzoomed_rect.SetHeight(unzoomed_rect.Height() / zoom_level);
}
GraphicsContextStateSaver state_saver(paint_info.context);
if (zoom_level != 1) {
paint_info.context.Translate(unzoomed_rect.X(), unzoomed_rect.Y());
paint_info.context.Scale(zoom_level, zoom_level);
paint_info.context.Translate(-unzoomed_rect.X(), -unzoomed_rect.Y());
}
Color fill_color(205, 205, 205);
Color border_gradient_top_color(109, 109, 109);
Color border_gradient_bottom_color(181, 181, 181);
Color shadow_color(0, 0, 0, 118);
if (!LayoutTheme::IsEnabled(o.GetNode())) {
Color tint_color(255, 255, 255, 128);
fill_color = fill_color.Blend(tint_color);
border_gradient_top_color = border_gradient_top_color.Blend(tint_color);
border_gradient_bottom_color =
border_gradient_bottom_color.Blend(tint_color);
shadow_color = shadow_color.Blend(tint_color);
}
Color tint_color;
if (!LayoutTheme::IsEnabled(o.GetNode()))
tint_color = Color(255, 255, 255, 128);
bool is_vertical_slider = o.StyleRef().Appearance() == kSliderVerticalPart;
float fill_radius_size = (LayoutThemeMac::kSliderTrackWidth -
LayoutThemeMac::kSliderTrackBorderWidth) /
2;
FloatSize fill_radius(fill_radius_size, fill_radius_size);
FloatRect fill_bounds(EnclosedIntRect(unzoomed_rect));
FloatRoundedRect fill_rect(fill_bounds, fill_radius, fill_radius, fill_radius,
fill_radius);
paint_info.context.FillRoundedRect(fill_rect, fill_color);
FloatSize shadow_offset(is_vertical_slider ? 1 : 0,
is_vertical_slider ? 0 : 1);
float shadow_blur = 3;
float shadow_spread = 0;
paint_info.context.Save();
paint_info.context.DrawInnerShadow(fill_rect, shadow_color, shadow_offset,
shadow_blur, shadow_spread);
paint_info.context.Restore();
scoped_refptr<Gradient> border_gradient =
Gradient::CreateLinear(fill_bounds.MinXMinYCorner(),
is_vertical_slider ? fill_bounds.MaxXMinYCorner()
: fill_bounds.MinXMaxYCorner());
border_gradient->AddColorStop(0.0, border_gradient_top_color);
border_gradient->AddColorStop(1.0, border_gradient_bottom_color);
FloatRect border_rect(unzoomed_rect);
border_rect.Inflate(-LayoutThemeMac::kSliderTrackBorderWidth / 2.0);
float border_radius_size =
(is_vertical_slider ? border_rect.Width() : border_rect.Height()) / 2;
FloatSize border_radius(border_radius_size, border_radius_size);
FloatRoundedRect border_r_rect(border_rect, border_radius, border_radius,
border_radius, border_radius);
paint_info.context.SetStrokeThickness(
LayoutThemeMac::kSliderTrackBorderWidth);
PaintFlags border_flags(paint_info.context.StrokeFlags());
border_gradient->ApplyToFlags(border_flags, SkMatrix::I());
paint_info.context.DrawRRect(border_r_rect, border_flags);
return false;
}
bool ThemePainterMac::PaintSliderThumb(const Node* node,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& r) {
GraphicsContextStateSaver state_saver(paint_info.context);
float zoom_level = style.EffectiveZoom();
FloatRect unzoomed_rect(r.X(), r.Y(), LayoutThemeMac::kSliderThumbWidth,
LayoutThemeMac::kSliderThumbHeight);
if (zoom_level != 1.0f) {
paint_info.context.Translate(unzoomed_rect.X(), unzoomed_rect.Y());
paint_info.context.Scale(zoom_level, zoom_level);
paint_info.context.Translate(-unzoomed_rect.X(), -unzoomed_rect.Y());
}
Color fill_gradient_top_color(250, 250, 250);
Color fill_gradient_upper_middle_color(244, 244, 244);
Color fill_gradient_lower_middle_color(236, 236, 236);
Color fill_gradient_bottom_color(238, 238, 238);
Color border_gradient_top_color(151, 151, 151);
Color border_gradient_bottom_color(128, 128, 128);
Color shadow_color(0, 0, 0, 36);
if (!LayoutTheme::IsEnabled(node)) {
Color tint_color(255, 255, 255, 128);
fill_gradient_top_color = fill_gradient_top_color.Blend(tint_color);
fill_gradient_upper_middle_color =
fill_gradient_upper_middle_color.Blend(tint_color);
fill_gradient_lower_middle_color =
fill_gradient_lower_middle_color.Blend(tint_color);
fill_gradient_bottom_color = fill_gradient_bottom_color.Blend(tint_color);
border_gradient_top_color = border_gradient_top_color.Blend(tint_color);
border_gradient_bottom_color =
border_gradient_bottom_color.Blend(tint_color);
shadow_color = shadow_color.Blend(tint_color);
} else if (LayoutTheme::IsPressed(node)) {
Color tint_color(0, 0, 0, 32);
fill_gradient_top_color = fill_gradient_top_color.Blend(tint_color);
fill_gradient_upper_middle_color =
fill_gradient_upper_middle_color.Blend(tint_color);
fill_gradient_lower_middle_color =
fill_gradient_lower_middle_color.Blend(tint_color);
fill_gradient_bottom_color = fill_gradient_bottom_color.Blend(tint_color);
border_gradient_top_color = border_gradient_top_color.Blend(tint_color);
border_gradient_bottom_color =
border_gradient_bottom_color.Blend(tint_color);
shadow_color = shadow_color.Blend(tint_color);
}
FloatRect border_bounds = unzoomed_rect;
border_bounds.Inflate(LayoutThemeMac::kSliderThumbBorderWidth / 2.0);
border_bounds.Inflate(-LayoutThemeMac::kSliderThumbBorderWidth);
FloatSize shadow_offset(0, 1);
paint_info.context.SetShadow(
shadow_offset, LayoutThemeMac::kSliderThumbShadowBlur, shadow_color);
paint_info.context.SetFillColor(Color::kBlack);
paint_info.context.FillEllipse(border_bounds);
paint_info.context.SetDrawLooper(nullptr);
FloatRect fill_bounds = FloatRect(EnclosedIntRect(unzoomed_rect));
scoped_refptr<Gradient> fill_gradient = Gradient::CreateLinear(
fill_bounds.MinXMinYCorner(), fill_bounds.MinXMaxYCorner());
fill_gradient->AddColorStop(0.0, fill_gradient_top_color);
fill_gradient->AddColorStop(0.52, fill_gradient_upper_middle_color);
fill_gradient->AddColorStop(0.52, fill_gradient_lower_middle_color);
fill_gradient->AddColorStop(1.0, fill_gradient_bottom_color);
PaintFlags fill_flags(paint_info.context.FillFlags());
fill_gradient->ApplyToFlags(fill_flags, SkMatrix::I());
paint_info.context.DrawOval(border_bounds, fill_flags);
scoped_refptr<Gradient> border_gradient = Gradient::CreateLinear(
fill_bounds.MinXMinYCorner(), fill_bounds.MinXMaxYCorner());
border_gradient->AddColorStop(0.0, border_gradient_top_color);
border_gradient->AddColorStop(1.0, border_gradient_bottom_color);
paint_info.context.SetStrokeThickness(
LayoutThemeMac::kSliderThumbBorderWidth);
PaintFlags border_flags(paint_info.context.StrokeFlags());
border_gradient->ApplyToFlags(border_flags, SkMatrix::I());
paint_info.context.DrawOval(border_bounds, border_flags);
if (LayoutTheme::IsFocused(node)) {
Path border_path;
border_path.AddEllipse(border_bounds);
paint_info.context.DrawFocusRing(border_path, 5, -2,
layout_theme_.FocusRingColor());
}
return false;
}
// We don't use controlSizeForFont() for search field decorations because it
// needs to fit into the search field. The font size will already be modified by
// setFontFromControlSize() called on the search field.
static NSControlSize SearchFieldControlSizeForFont(const ComputedStyle& style) {
int font_size = style.FontSize();
if (font_size >= 13)
return NSRegularControlSize;
if (font_size >= 11)
return NSSmallControlSize;
return NSMiniControlSize;
}
bool ThemePainterMac::PaintSearchField(const Node* node,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& r) {
LocalCurrentGraphicsContext local_context(paint_info.context, r);
NSSearchFieldCell* search = layout_theme_.Search();
layout_theme_.SetSearchCellState(node, style, r);
[search setControlSize:SearchFieldControlSizeForFont(style)];
GraphicsContextStateSaver state_saver(paint_info.context);
float zoom_level = style.EffectiveZoom();
IntRect unzoomed_rect = r;
if (zoom_level != 1.0f) {
unzoomed_rect.SetWidth(unzoomed_rect.Width() / zoom_level);
unzoomed_rect.SetHeight(unzoomed_rect.Height() / zoom_level);
paint_info.context.Translate(unzoomed_rect.X(), unzoomed_rect.Y());
paint_info.context.Scale(zoom_level, zoom_level);
paint_info.context.Translate(-unzoomed_rect.X(), -unzoomed_rect.Y());
}
// Set the search button to nil before drawing. Then reset it so we can
// draw it later.
[search setSearchButtonCell:nil];
[search drawWithFrame:NSRect(unzoomed_rect)
inView:layout_theme_.DocumentView()];
[search setControlView:nil];
[search resetSearchButtonCell];
return false;
}
bool ThemePainterMac::PaintSearchFieldCancelButton(
const LayoutObject& cancel_button,
const PaintInfo& paint_info,
const IntRect& r) {
if (!cancel_button.GetNode())
return false;
GraphicsContextStateSaver state_saver(paint_info.context);
float zoom_level = cancel_button.StyleRef().EffectiveZoom();
FloatRect unzoomed_rect(r);
if (zoom_level != 1.0f) {
unzoomed_rect.SetWidth(unzoomed_rect.Width() / zoom_level);
unzoomed_rect.SetHeight(unzoomed_rect.Height() / zoom_level);
paint_info.context.Translate(unzoomed_rect.X(), unzoomed_rect.Y());
paint_info.context.Scale(zoom_level, zoom_level);
paint_info.context.Translate(-unzoomed_rect.X(), -unzoomed_rect.Y());
}
Color fill_color(200, 200, 200);
if (LayoutTheme::IsPressed(cancel_button.GetNode())) {
Color tint_color(0, 0, 0, 32);
fill_color = fill_color.Blend(tint_color);
}
float center_x = unzoomed_rect.X() + unzoomed_rect.Width() / 2;
float center_y = unzoomed_rect.Y() + unzoomed_rect.Height() / 2;
// The line width is 3px on a regular sized, high DPI NSCancelButtonCell
// (which is 28px wide).
float line_width = unzoomed_rect.Width() * 3 / 28;
// The line length is 16px on a regular sized, high DPI NSCancelButtonCell.
float line_length = unzoomed_rect.Width() * 16 / 28;
Path x_path;
FloatSize line_rect_radius(line_width / 2, line_width / 2);
x_path.AddRoundedRect(
FloatRect(-line_length / 2, -line_width / 2, line_length, line_width),
line_rect_radius, line_rect_radius, line_rect_radius, line_rect_radius);
x_path.AddRoundedRect(
FloatRect(-line_width / 2, -line_length / 2, line_width, line_length),
line_rect_radius, line_rect_radius, line_rect_radius, line_rect_radius);
paint_info.context.Translate(center_x, center_y);
paint_info.context.Rotate(deg2rad(45.0));
paint_info.context.ClipOut(x_path);
paint_info.context.Rotate(deg2rad(-45.0));
paint_info.context.Translate(-center_x, -center_y);
paint_info.context.SetFillColor(fill_color);
paint_info.context.FillEllipse(unzoomed_rect);
return false;
}
// FIXME: Share more code with radio buttons.
bool ThemePainterMac::PaintCheckbox(const Node* node,
const Document& document,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& zoomed_rect) {
BEGIN_BLOCK_OBJC_EXCEPTIONS
ControlStates states = LayoutTheme::ControlStatesForNode(node, style);
float zoom_factor = style.EffectiveZoom();
// Determine the width and height needed for the control and prepare the cell
// for painting.
NSButtonCell* checkbox_cell =
ThemeMac::Checkbox(states, zoomed_rect, zoom_factor);
GraphicsContextStateSaver state_saver(paint_info.context);
NSControlSize control_size = [checkbox_cell controlSize];
IntSize zoomed_size = ThemeMac::CheckboxSizes()[control_size];
zoomed_size.SetWidth(zoomed_size.Width() * zoom_factor);
zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
IntRect inflated_rect = ThemeMac::InflateRect(
zoomed_rect, zoomed_size, ThemeMac::CheckboxMargins(control_size),
zoom_factor);
if (zoom_factor != 1.0f) {
inflated_rect.SetWidth(inflated_rect.Width() / zoom_factor);
inflated_rect.SetHeight(inflated_rect.Height() / zoom_factor);
paint_info.context.Translate(inflated_rect.X(), inflated_rect.Y());
paint_info.context.Scale(zoom_factor, zoom_factor);
paint_info.context.Translate(-inflated_rect.X(), -inflated_rect.Y());
}
LocalCurrentGraphicsContext local_context(
paint_info.context, ThemeMac::InflateRectForFocusRing(inflated_rect));
NSView* view = ThemeMac::EnsuredView(document.View()->Size());
[checkbox_cell drawWithFrame:NSRect(inflated_rect) inView:view];
if (states & kFocusControlState)
[checkbox_cell cr_drawFocusRingWithFrame:NSRect(inflated_rect) inView:view];
[checkbox_cell setControlView:nil];
END_BLOCK_OBJC_EXCEPTIONS
return false;
}
bool ThemePainterMac::PaintRadio(const Node* node,
const Document& document,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& zoomed_rect) {
ControlStates states = LayoutTheme::ControlStatesForNode(node, style);
float zoom_factor = style.EffectiveZoom();
// Determine the width and height needed for the control and prepare the cell
// for painting.
NSButtonCell* radio_cell = ThemeMac::Radio(states, zoomed_rect, zoom_factor);
GraphicsContextStateSaver state_saver(paint_info.context);
NSControlSize control_size = [radio_cell controlSize];
IntSize zoomed_size = ThemeMac::RadioSizes()[control_size];
zoomed_size.SetWidth(zoomed_size.Width() * zoom_factor);
zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
IntRect inflated_rect =
ThemeMac::InflateRect(zoomed_rect, zoomed_size,
ThemeMac::RadioMargins(control_size), zoom_factor);
if (zoom_factor != 1.0f) {
inflated_rect.SetWidth(inflated_rect.Width() / zoom_factor);
inflated_rect.SetHeight(inflated_rect.Height() / zoom_factor);
paint_info.context.Translate(inflated_rect.X(), inflated_rect.Y());
paint_info.context.Scale(zoom_factor, zoom_factor);
paint_info.context.Translate(-inflated_rect.X(), -inflated_rect.Y());
}
LocalCurrentGraphicsContext local_context(
paint_info.context, ThemeMac::InflateRectForFocusRing(inflated_rect));
BEGIN_BLOCK_OBJC_EXCEPTIONS
NSView* view = ThemeMac::EnsuredView(document.View()->Size());
[radio_cell drawWithFrame:NSRect(inflated_rect) inView:view];
if (states & kFocusControlState)
[radio_cell cr_drawFocusRingWithFrame:NSRect(inflated_rect) inView:view];
[radio_cell setControlView:nil];
END_BLOCK_OBJC_EXCEPTIONS
return false;
}
bool ThemePainterMac::PaintButton(const Node* node,
const Document& document,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& zoomed_rect) {
BEGIN_BLOCK_OBJC_EXCEPTIONS
ControlStates states = LayoutTheme::ControlStatesForNode(node, style);
float zoom_factor = style.EffectiveZoom();
// Determine the width and height needed for the control and prepare the cell
// for painting.
NSButtonCell* button_cell =
ThemeMac::Button(style.Appearance(), states, zoomed_rect, zoom_factor);
GraphicsContextStateSaver state_saver(paint_info.context);
NSControlSize control_size = [button_cell controlSize];
IntSize zoomed_size = ThemeMac::ButtonSizes()[control_size];
// Buttons don't ever constrain width, so the zoomed width can just be
// honored.
zoomed_size.SetWidth(zoomed_rect.Width());
zoomed_size.SetHeight(zoomed_size.Height() * zoom_factor);
IntRect inflated_rect = zoomed_rect;
if ([button_cell bezelStyle] == NSRoundedBezelStyle) {
// Center the button within the available space.
if (inflated_rect.Height() > zoomed_size.Height()) {
inflated_rect.SetY(inflated_rect.Y() +
(inflated_rect.Height() - zoomed_size.Height()) / 2);
inflated_rect.SetHeight(zoomed_size.Height());
}
// Now inflate it to account for the shadow.
inflated_rect = ThemeMac::InflateRect(inflated_rect, zoomed_size,
ThemeMac::ButtonMargins(control_size),
zoom_factor);
if (zoom_factor != 1.0f) {
inflated_rect.SetWidth(inflated_rect.Width() / zoom_factor);
inflated_rect.SetHeight(inflated_rect.Height() / zoom_factor);
paint_info.context.Translate(inflated_rect.X(), inflated_rect.Y());
paint_info.context.Scale(zoom_factor, zoom_factor);
paint_info.context.Translate(-inflated_rect.X(), -inflated_rect.Y());
}
}
LocalCurrentGraphicsContext local_context(
paint_info.context, ThemeMac::InflateRectForFocusRing(inflated_rect));
NSView* view = ThemeMac::EnsuredView(document.View()->Size());
[button_cell drawWithFrame:NSRect(inflated_rect) inView:view];
if (states & kFocusControlState)
[button_cell cr_drawFocusRingWithFrame:NSRect(inflated_rect) inView:view];
[button_cell setControlView:nil];
END_BLOCK_OBJC_EXCEPTIONS
return false;
}
static ThemeDrawState ConvertControlStatesToThemeDrawState(
ThemeButtonKind kind,
ControlStates states) {
if (states & kReadOnlyControlState)
return kThemeStateUnavailableInactive;
if (!(states & kEnabledControlState))
return kThemeStateUnavailableInactive;
// Do not process PressedState if !EnabledControlState or
// ReadOnlyControlState.
if (states & kPressedControlState) {
if (kind == kThemeIncDecButton || kind == kThemeIncDecButtonSmall ||
kind == kThemeIncDecButtonMini)
return states & kSpinUpControlState ? kThemeStatePressedUp
: kThemeStatePressedDown;
return kThemeStatePressed;
}
return kThemeStateActive;
}
bool ThemePainterMac::PaintInnerSpinButton(const Node* node,
const ComputedStyle& style,
const PaintInfo& paint_info,
const IntRect& zoomed_rect) {
ControlStates states = LayoutTheme::ControlStatesForNode(node, style);
float zoom_factor = style.EffectiveZoom();
// We don't use NSStepperCell because there are no ways to draw an
// NSStepperCell with the up button highlighted.
HIThemeButtonDrawInfo draw_info;
draw_info.version = 0;
draw_info.state =
ConvertControlStatesToThemeDrawState(kThemeIncDecButton, states);
draw_info.adornment = kThemeAdornmentDefault;
ControlSize control_size = ThemeMac::ControlSizeFromPixelSize(
ThemeMac::StepperSizes(), zoomed_rect.Size(), zoom_factor);
if (control_size == NSSmallControlSize)
draw_info.kind = kThemeIncDecButtonSmall;
else if (control_size == NSMiniControlSize)
draw_info.kind = kThemeIncDecButtonMini;
else
draw_info.kind = kThemeIncDecButton;
IntRect rect(zoomed_rect);
GraphicsContextStateSaver state_saver(paint_info.context);
if (zoom_factor != 1.0f) {
rect.SetWidth(rect.Width() / zoom_factor);
rect.SetHeight(rect.Height() / zoom_factor);
paint_info.context.Translate(rect.X(), rect.Y());
paint_info.context.Scale(zoom_factor, zoom_factor);
paint_info.context.Translate(-rect.X(), -rect.Y());
}
CGRect bounds(rect);
CGRect background_bounds;
HIThemeGetButtonBackgroundBounds(&bounds, &draw_info, &background_bounds);
// Center the stepper rectangle in the specified area.
background_bounds.origin.x =
bounds.origin.x + (bounds.size.width - background_bounds.size.width) / 2;
if (background_bounds.size.height < bounds.size.height) {
int height_diff =
clampTo<int>(bounds.size.height - background_bounds.size.height);
background_bounds.origin.y = bounds.origin.y + (height_diff / 2) + 1;
}
LocalCurrentGraphicsContext local_context(paint_info.context, rect);
HIThemeDrawButton(&background_bounds, &draw_info, local_context.CgContext(),
kHIThemeOrientationNormal, 0);
return false;
}
} // namespace blink