blob: 54f9b7eac56f0e0df350f119e0b290b6ce7972ee [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 "core/paint/ThemePainterMac.h"
#import "core/frame/FrameView.h"
#import "core/layout/LayoutProgress.h"
#import "core/layout/LayoutThemeMac.h"
#import "core/layout/LayoutView.h"
#import "core/paint/PaintInfo.h"
#import "platform/geometry/FloatRoundedRect.h"
#import "platform/graphics/BitmapImage.h"
#import "platform/graphics/GraphicsContextStateSaver.h"
#import "platform/graphics/Image.h"
#import "platform/graphics/ImageBuffer.h"
#import "platform/mac/BlockExceptions.h"
#import "platform/mac/ColorMac.h"
#import "platform/mac/LocalCurrentGraphicsContext.h"
#import "platform/mac/ThemeMac.h"
#import "platform/mac/WebCoreNSCellExtras.h"
#import <AvailabilityMacros.h>
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#import <math.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& layoutTheme)
: ThemePainter(), m_layoutTheme(layoutTheme) {}
bool ThemePainterMac::paintTextField(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
LocalCurrentGraphicsContext localContext(paintInfo.context, r);
bool useNSTextFieldCell = o.styleRef().hasAppearance() &&
o.styleRef().visitedDependentColor(
CSSPropertyBackgroundColor) == Color::white &&
!o.styleRef().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 (!useNSTextFieldCell) {
_NSDrawCarbonThemeBezel(
r, LayoutTheme::isEnabled(o) && !LayoutTheme::isReadOnlyControl(o),
YES);
return false;
}
NSTextFieldCell* textField = m_layoutTheme.textField();
GraphicsContextStateSaver stateSaver(paintInfo.context);
[textField setEnabled:(LayoutTheme::isEnabled(o) &&
!LayoutTheme::isReadOnlyControl(o))];
[textField drawWithFrame:NSRect(r) inView:m_layoutTheme.documentViewFor(o)];
[textField setControlView:nil];
return false;
}
bool ThemePainterMac::paintCapsLockIndicator(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
// This draws the caps lock indicator as it was done by
// WKDrawCapsLockIndicator.
LocalCurrentGraphicsContext localContext(paintInfo.context, r);
CGContextRef c = localContext.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 = r;
CGFloat maxX = CGRectGetMaxX(cgr);
CGFloat minX = CGRectGetMinX(cgr);
CGFloat minY = CGRectGetMinY(cgr);
CGFloat heightScale = r.height() / kSquareSize;
const bool isRTL = o.styleRef().direction() == TextDirection::Rtl;
CGAffineTransform transform =
CGAffineTransformMake(heightScale, 0, // A B
0, heightScale, // C D
isRTL ? minX : maxX - r.height(), minY); // Tx Ty
CGMutablePathRef paintPath = CGPathCreateMutable();
CGPathAddPath(paintPath, &transform, shape);
CGPathRelease(shape);
CGContextSetRGBFillColor(c, 0, 0, 0, 0.4);
CGContextBeginPath(c);
CGContextAddPath(c, paintPath);
CGContextFillPath(c);
CGPathRelease(paintPath);
return false;
}
bool ThemePainterMac::paintTextArea(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
LocalCurrentGraphicsContext localContext(paintInfo.context, r);
_NSDrawCarbonThemeListBox(
r, LayoutTheme::isEnabled(o) && !LayoutTheme::isReadOnlyControl(o), YES,
YES);
return false;
}
bool ThemePainterMac::paintMenuList(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
m_layoutTheme.setPopupButtonCellState(o, r);
NSPopUpButtonCell* popupButton = m_layoutTheme.popupButton();
float zoomLevel = o.styleRef().effectiveZoom();
IntSize size = m_layoutTheme.popupButtonSizes()[[popupButton controlSize]];
size.setHeight(size.height() * zoomLevel);
size.setWidth(r.width());
// Now inflate it to account for the shadow.
IntRect inflatedRect = r;
if (r.width() >= m_layoutTheme.minimumMenuListSize(o.styleRef()))
inflatedRect = ThemeMac::inflateRect(
inflatedRect, size, m_layoutTheme.popupButtonMargins(), zoomLevel);
LocalCurrentGraphicsContext localContext(
paintInfo.context, ThemeMac::inflateRectForFocusRing(inflatedRect));
if (zoomLevel != 1.0f) {
inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
paintInfo.context.translate(inflatedRect.x(), inflatedRect.y());
paintInfo.context.scale(zoomLevel, zoomLevel);
paintInfo.context.translate(-inflatedRect.x(), -inflatedRect.y());
}
NSView* view = m_layoutTheme.documentViewFor(o);
[popupButton drawWithFrame:inflatedRect inView:view];
if (LayoutTheme::isFocused(o) && o.styleRef().outlineStyleIsAuto())
[popupButton cr_drawFocusRingWithFrame:inflatedRect inView:view];
[popupButton setControlView:nil];
return false;
}
bool ThemePainterMac::paintProgressBar(const LayoutObject& layoutObject,
const PaintInfo& paintInfo,
const IntRect& rect) {
if (!layoutObject.isProgress())
return true;
const LayoutProgress& layoutProgress = toLayoutProgress(layoutObject);
HIThemeTrackDrawInfo trackInfo;
trackInfo.version = 0;
NSControlSize controlSize =
m_layoutTheme.controlSizeForFont(layoutObject.styleRef());
if (controlSize == NSRegularControlSize)
trackInfo.kind = layoutProgress.position() < 0 ? kThemeLargeIndeterminateBar
: kThemeLargeProgressBar;
else
trackInfo.kind = layoutProgress.position() < 0
? kThemeMediumIndeterminateBar
: kThemeMediumProgressBar;
trackInfo.bounds = IntRect(IntPoint(), rect.size());
trackInfo.min = 0;
trackInfo.max = std::numeric_limits<SInt32>::max();
trackInfo.value =
lround(layoutProgress.position() * nextafter(trackInfo.max, 0));
trackInfo.trackInfo.progress.phase =
lround(layoutProgress.animationProgress() *
nextafter(LayoutThemeMac::progressAnimationNumFrames, 0));
trackInfo.attributes = kThemeTrackHorizontal;
trackInfo.enableState = LayoutTheme::isActive(layoutObject)
? kThemeTrackActive
: kThemeTrackInactive;
trackInfo.reserved = 0;
trackInfo.filler1 = 0;
std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(rect.size());
if (!imageBuffer)
return true;
IntRect clipRect = IntRect(IntPoint(), rect.size());
LocalCurrentGraphicsContext localContext(imageBuffer->canvas(), 1, clipRect);
CGContextRef cgContext = localContext.cgContext();
HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
GraphicsContextStateSaver stateSaver(paintInfo.context);
if (!layoutProgress.styleRef().isLeftToRightDirection()) {
paintInfo.context.translate(2 * rect.x() + rect.width(), 0);
paintInfo.context.scale(-1, 1);
}
if (!paintInfo.context.contextDisabled())
imageBuffer->draw(
paintInfo.context,
FloatRect(rect.location(), FloatSize(imageBuffer->size())), nullptr,
SkBlendMode::kSrcOver);
return false;
}
bool ThemePainterMac::paintMenuListButton(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
IntRect bounds = IntRect(r.x() + o.styleRef().borderLeftWidth(),
r.y() + o.styleRef().borderTopWidth(),
r.width() - o.styleRef().borderLeftWidth() -
o.styleRef().borderRightWidth(),
r.height() - o.styleRef().borderTopWidth() -
o.styleRef().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 fontScale = std::min(
o.styleRef().fontSize() / LayoutThemeMac::baseFontSize,
bounds.height() / (LayoutThemeMac::menuListBaseArrowHeight * 2 +
LayoutThemeMac::menuListBaseSpaceBetweenArrows));
float centerY = bounds.y() + bounds.height() / 2.0f;
float arrowHeight = LayoutThemeMac::menuListBaseArrowHeight * fontScale;
float arrowWidth = LayoutThemeMac::menuListBaseArrowWidth * fontScale;
float spaceBetweenArrows =
LayoutThemeMac::menuListBaseSpaceBetweenArrows * fontScale;
float scaledPaddingEnd =
LayoutThemeMac::menuListArrowPaddingEnd * o.styleRef().effectiveZoom();
float leftEdge;
if (o.styleRef().direction() == TextDirection::Ltr) {
leftEdge = bounds.maxX() - scaledPaddingEnd - arrowWidth;
} else {
leftEdge = bounds.x() + scaledPaddingEnd;
}
if (bounds.width() < arrowWidth + scaledPaddingEnd)
return false;
Color color = o.styleRef().visitedDependentColor(CSSPropertyColor);
SkPaint paint = paintInfo.context.fillPaint();
paint.setAntiAlias(true);
paint.setColor(color.rgb());
SkPath arrow1;
arrow1.moveTo(leftEdge, centerY - spaceBetweenArrows / 2.0f);
arrow1.lineTo(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
arrow1.lineTo(leftEdge + arrowWidth / 2.0f,
centerY - spaceBetweenArrows / 2.0f - arrowHeight);
// Draw the top arrow.
paintInfo.context.drawPath(arrow1, paint);
SkPath arrow2;
arrow2.moveTo(leftEdge, centerY + spaceBetweenArrows / 2.0f);
arrow2.lineTo(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
arrow2.lineTo(leftEdge + arrowWidth / 2.0f,
centerY + spaceBetweenArrows / 2.0f + arrowHeight);
// Draw the bottom arrow.
paintInfo.context.drawPath(arrow2, paint);
return false;
}
bool ThemePainterMac::paintSliderTrack(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
paintSliderTicks(o, paintInfo, r);
float zoomLevel = o.styleRef().effectiveZoom();
FloatRect unzoomedRect = r;
if (o.styleRef().appearance() == SliderHorizontalPart ||
o.styleRef().appearance() == MediaSliderPart) {
unzoomedRect.setY(ceilf(unzoomedRect.y() + unzoomedRect.height() / 2 -
zoomLevel * LayoutThemeMac::sliderTrackWidth / 2));
unzoomedRect.setHeight(zoomLevel * LayoutThemeMac::sliderTrackWidth);
} else if (o.styleRef().appearance() == SliderVerticalPart) {
unzoomedRect.setX(ceilf(unzoomedRect.x() + unzoomedRect.width() / 2 -
zoomLevel * LayoutThemeMac::sliderTrackWidth / 2));
unzoomedRect.setWidth(zoomLevel * LayoutThemeMac::sliderTrackWidth);
}
if (zoomLevel != 1) {
unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
}
GraphicsContextStateSaver stateSaver(paintInfo.context);
if (zoomLevel != 1) {
paintInfo.context.translate(unzoomedRect.x(), unzoomedRect.y());
paintInfo.context.scale(zoomLevel, zoomLevel);
paintInfo.context.translate(-unzoomedRect.x(), -unzoomedRect.y());
}
Color fillColor(205, 205, 205);
Color borderGradientTopColor(109, 109, 109);
Color borderGradientBottomColor(181, 181, 181);
Color shadowColor(0, 0, 0, 118);
if (!LayoutTheme::isEnabled(o)) {
Color tintColor(255, 255, 255, 128);
fillColor = fillColor.blend(tintColor);
borderGradientTopColor = borderGradientTopColor.blend(tintColor);
borderGradientBottomColor = borderGradientBottomColor.blend(tintColor);
shadowColor = shadowColor.blend(tintColor);
}
Color tintColor;
if (!LayoutTheme::isEnabled(o))
tintColor = Color(255, 255, 255, 128);
bool isVerticalSlider = o.styleRef().appearance() == SliderVerticalPart;
float fillRadiusSize = (LayoutThemeMac::sliderTrackWidth -
LayoutThemeMac::sliderTrackBorderWidth) /
2;
FloatSize fillRadius(fillRadiusSize, fillRadiusSize);
FloatRect fillBounds(enclosedIntRect(unzoomedRect));
FloatRoundedRect fillRect(fillBounds, fillRadius, fillRadius, fillRadius,
fillRadius);
paintInfo.context.fillRoundedRect(fillRect, fillColor);
FloatSize shadowOffset(isVerticalSlider ? 1 : 0, isVerticalSlider ? 0 : 1);
float shadowBlur = 3;
float shadowSpread = 0;
paintInfo.context.save();
paintInfo.context.drawInnerShadow(fillRect, shadowColor, shadowOffset,
shadowBlur, shadowSpread);
paintInfo.context.restore();
RefPtr<Gradient> borderGradient =
Gradient::create(fillBounds.minXMinYCorner(),
isVerticalSlider ? fillBounds.maxXMinYCorner()
: fillBounds.minXMaxYCorner());
borderGradient->addColorStop(0.0, borderGradientTopColor);
borderGradient->addColorStop(1.0, borderGradientBottomColor);
FloatRect borderRect(unzoomedRect);
borderRect.inflate(-LayoutThemeMac::sliderTrackBorderWidth / 2.0);
float borderRadiusSize =
(isVerticalSlider ? borderRect.width() : borderRect.height()) / 2;
FloatSize borderRadius(borderRadiusSize, borderRadiusSize);
FloatRoundedRect borderRRect(borderRect, borderRadius, borderRadius,
borderRadius, borderRadius);
paintInfo.context.setStrokeThickness(LayoutThemeMac::sliderTrackBorderWidth);
SkPaint borderPaint(paintInfo.context.strokePaint());
borderGradient->applyToPaint(borderPaint, SkMatrix::I());
paintInfo.context.drawRRect(borderRRect, borderPaint);
return false;
}
bool ThemePainterMac::paintSliderThumb(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
GraphicsContextStateSaver stateSaver(paintInfo.context);
float zoomLevel = o.styleRef().effectiveZoom();
FloatRect unzoomedRect(r.x(), r.y(), LayoutThemeMac::sliderThumbWidth,
LayoutThemeMac::sliderThumbHeight);
if (zoomLevel != 1.0f) {
paintInfo.context.translate(unzoomedRect.x(), unzoomedRect.y());
paintInfo.context.scale(zoomLevel, zoomLevel);
paintInfo.context.translate(-unzoomedRect.x(), -unzoomedRect.y());
}
Color fillGradientTopColor(250, 250, 250);
Color fillGradientUpperMiddleColor(244, 244, 244);
Color fillGradientLowerMiddleColor(236, 236, 236);
Color fillGradientBottomColor(238, 238, 238);
Color borderGradientTopColor(151, 151, 151);
Color borderGradientBottomColor(128, 128, 128);
Color shadowColor(0, 0, 0, 36);
if (!LayoutTheme::isEnabled(o)) {
Color tintColor(255, 255, 255, 128);
fillGradientTopColor = fillGradientTopColor.blend(tintColor);
fillGradientUpperMiddleColor =
fillGradientUpperMiddleColor.blend(tintColor);
fillGradientLowerMiddleColor =
fillGradientLowerMiddleColor.blend(tintColor);
fillGradientBottomColor = fillGradientBottomColor.blend(tintColor);
borderGradientTopColor = borderGradientTopColor.blend(tintColor);
borderGradientBottomColor = borderGradientBottomColor.blend(tintColor);
shadowColor = shadowColor.blend(tintColor);
} else if (LayoutTheme::isPressed(o)) {
Color tintColor(0, 0, 0, 32);
fillGradientTopColor = fillGradientTopColor.blend(tintColor);
fillGradientUpperMiddleColor =
fillGradientUpperMiddleColor.blend(tintColor);
fillGradientLowerMiddleColor =
fillGradientLowerMiddleColor.blend(tintColor);
fillGradientBottomColor = fillGradientBottomColor.blend(tintColor);
borderGradientTopColor = borderGradientTopColor.blend(tintColor);
borderGradientBottomColor = borderGradientBottomColor.blend(tintColor);
shadowColor = shadowColor.blend(tintColor);
}
FloatRect borderBounds = unzoomedRect;
borderBounds.inflate(LayoutThemeMac::sliderThumbBorderWidth / 2.0);
borderBounds.inflate(-LayoutThemeMac::sliderThumbBorderWidth);
FloatSize shadowOffset(0, 1);
paintInfo.context.setShadow(
shadowOffset, LayoutThemeMac::sliderThumbShadowBlur, shadowColor);
paintInfo.context.setFillColor(Color::black);
paintInfo.context.fillEllipse(borderBounds);
paintInfo.context.setDrawLooper(nullptr);
IntRect fillBounds = enclosedIntRect(unzoomedRect);
RefPtr<Gradient> fillGradient = Gradient::create(fillBounds.minXMinYCorner(),
fillBounds.minXMaxYCorner());
fillGradient->addColorStop(0.0, fillGradientTopColor);
fillGradient->addColorStop(0.52, fillGradientUpperMiddleColor);
fillGradient->addColorStop(0.52, fillGradientLowerMiddleColor);
fillGradient->addColorStop(1.0, fillGradientBottomColor);
SkPaint fillPaint(paintInfo.context.fillPaint());
fillGradient->applyToPaint(fillPaint, SkMatrix::I());
paintInfo.context.drawOval(borderBounds, fillPaint);
RefPtr<Gradient> borderGradient = Gradient::create(
fillBounds.minXMinYCorner(), fillBounds.minXMaxYCorner());
borderGradient->addColorStop(0.0, borderGradientTopColor);
borderGradient->addColorStop(1.0, borderGradientBottomColor);
paintInfo.context.setStrokeThickness(LayoutThemeMac::sliderThumbBorderWidth);
SkPaint borderPaint(paintInfo.context.strokePaint());
borderGradient->applyToPaint(borderPaint, SkMatrix::I());
paintInfo.context.drawOval(borderBounds, borderPaint);
if (LayoutTheme::isFocused(o)) {
Path borderPath;
borderPath.addEllipse(borderBounds);
paintInfo.context.drawFocusRing(borderPath, 5, -2,
m_layoutTheme.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 fontSize = style.fontSize();
if (fontSize >= 13)
return NSRegularControlSize;
if (fontSize >= 11)
return NSSmallControlSize;
return NSMiniControlSize;
}
bool ThemePainterMac::paintSearchField(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
LocalCurrentGraphicsContext localContext(paintInfo.context, r);
NSSearchFieldCell* search = m_layoutTheme.search();
m_layoutTheme.setSearchCellState(o, r);
[search setControlSize:searchFieldControlSizeForFont(o.styleRef())];
GraphicsContextStateSaver stateSaver(paintInfo.context);
float zoomLevel = o.styleRef().effectiveZoom();
IntRect unzoomedRect = r;
if (zoomLevel != 1.0f) {
unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
paintInfo.context.translate(unzoomedRect.x(), unzoomedRect.y());
paintInfo.context.scale(zoomLevel, zoomLevel);
paintInfo.context.translate(-unzoomedRect.x(), -unzoomedRect.y());
}
// Set the search button to nil before drawing. Then reset it so we can
// draw it later.
[search setSearchButtonCell:nil];
[search drawWithFrame:NSRect(unzoomedRect)
inView:m_layoutTheme.documentViewFor(o)];
[search setControlView:nil];
[search resetSearchButtonCell];
return false;
}
bool ThemePainterMac::paintSearchFieldCancelButton(const LayoutObject& o,
const PaintInfo& paintInfo,
const IntRect& r) {
if (!o.node())
return false;
Element* input = o.node()->ownerShadowHost();
if (!input)
input = toElement(o.node());
if (!input->layoutObject()->isBox())
return false;
GraphicsContextStateSaver stateSaver(paintInfo.context);
float zoomLevel = o.styleRef().effectiveZoom();
FloatRect unzoomedRect(r);
if (zoomLevel != 1.0f) {
unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
paintInfo.context.translate(unzoomedRect.x(), unzoomedRect.y());
paintInfo.context.scale(zoomLevel, zoomLevel);
paintInfo.context.translate(-unzoomedRect.x(), -unzoomedRect.y());
}
Color fillColor(200, 200, 200);
if (LayoutTheme::isPressed(o)) {
Color tintColor(0, 0, 0, 32);
fillColor = fillColor.blend(tintColor);
}
float centerX = unzoomedRect.x() + unzoomedRect.width() / 2;
float centerY = unzoomedRect.y() + unzoomedRect.height() / 2;
// The line width is 3px on a regular sized, high DPI NSCancelButtonCell
// (which is 28px wide).
float lineWidth = unzoomedRect.width() * 3 / 28;
// The line length is 16px on a regular sized, high DPI NSCancelButtonCell.
float lineLength = unzoomedRect.width() * 16 / 28;
Path xPath;
FloatSize lineRectRadius(lineWidth / 2, lineWidth / 2);
xPath.addRoundedRect(
FloatRect(-lineLength / 2, -lineWidth / 2, lineLength, lineWidth),
lineRectRadius, lineRectRadius, lineRectRadius, lineRectRadius);
xPath.addRoundedRect(
FloatRect(-lineWidth / 2, -lineLength / 2, lineWidth, lineLength),
lineRectRadius, lineRectRadius, lineRectRadius, lineRectRadius);
paintInfo.context.translate(centerX, centerY);
paintInfo.context.rotate(deg2rad(45.0));
paintInfo.context.clipOut(xPath);
paintInfo.context.rotate(deg2rad(-45.0));
paintInfo.context.translate(-centerX, -centerY);
paintInfo.context.setFillColor(fillColor);
paintInfo.context.fillEllipse(unzoomedRect);
return false;
}
// FIXME: Share more code with radio buttons.
bool ThemePainterMac::paintCheckbox(const LayoutObject& object,
const PaintInfo& paintInfo,
const IntRect& zoomedRect) {
BEGIN_BLOCK_OBJC_EXCEPTIONS
ControlStates states = LayoutTheme::controlStatesForLayoutObject(object);
float zoomFactor = object.styleRef().effectiveZoom();
// Determine the width and height needed for the control and prepare the cell
// for painting.
NSButtonCell* checkboxCell =
ThemeMac::checkbox(states, zoomedRect, zoomFactor);
GraphicsContextStateSaver stateSaver(paintInfo.context);
NSControlSize controlSize = [checkboxCell controlSize];
IntSize zoomedSize = ThemeMac::checkboxSizes()[controlSize];
zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
IntRect inflatedRect =
ThemeMac::inflateRect(zoomedRect, zoomedSize,
ThemeMac::checkboxMargins(controlSize), zoomFactor);
if (zoomFactor != 1.0f) {
inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
paintInfo.context.translate(inflatedRect.x(), inflatedRect.y());
paintInfo.context.scale(zoomFactor, zoomFactor);
paintInfo.context.translate(-inflatedRect.x(), -inflatedRect.y());
}
LocalCurrentGraphicsContext localContext(
paintInfo.context, ThemeMac::inflateRectForFocusRing(inflatedRect));
NSView* view = ThemeMac::ensuredView(object.view()->frameView());
[checkboxCell drawWithFrame:NSRect(inflatedRect) inView:view];
if (states & FocusControlState)
[checkboxCell cr_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
[checkboxCell setControlView:nil];
END_BLOCK_OBJC_EXCEPTIONS
return false;
}
bool ThemePainterMac::paintRadio(const LayoutObject& object,
const PaintInfo& paintInfo,
const IntRect& zoomedRect) {
ControlStates states = LayoutTheme::controlStatesForLayoutObject(object);
float zoomFactor = object.styleRef().effectiveZoom();
// Determine the width and height needed for the control and prepare the cell
// for painting.
NSButtonCell* radioCell = ThemeMac::radio(states, zoomedRect, zoomFactor);
GraphicsContextStateSaver stateSaver(paintInfo.context);
NSControlSize controlSize = [radioCell controlSize];
IntSize zoomedSize = ThemeMac::radioSizes()[controlSize];
zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
IntRect inflatedRect = ThemeMac::inflateRect(
zoomedRect, zoomedSize, ThemeMac::radioMargins(controlSize), zoomFactor);
if (zoomFactor != 1.0f) {
inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
paintInfo.context.translate(inflatedRect.x(), inflatedRect.y());
paintInfo.context.scale(zoomFactor, zoomFactor);
paintInfo.context.translate(-inflatedRect.x(), -inflatedRect.y());
}
LocalCurrentGraphicsContext localContext(
paintInfo.context, ThemeMac::inflateRectForFocusRing(inflatedRect));
BEGIN_BLOCK_OBJC_EXCEPTIONS
NSView* view = ThemeMac::ensuredView(object.view()->frameView());
[radioCell drawWithFrame:NSRect(inflatedRect) inView:view];
if (states & FocusControlState)
[radioCell cr_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
[radioCell setControlView:nil];
END_BLOCK_OBJC_EXCEPTIONS
return false;
}
bool ThemePainterMac::paintButton(const LayoutObject& object,
const PaintInfo& paintInfo,
const IntRect& zoomedRect) {
BEGIN_BLOCK_OBJC_EXCEPTIONS
ControlStates states = LayoutTheme::controlStatesForLayoutObject(object);
float zoomFactor = object.styleRef().effectiveZoom();
// Determine the width and height needed for the control and prepare the cell
// for painting.
NSButtonCell* buttonCell = ThemeMac::button(object.styleRef().appearance(),
states, zoomedRect, zoomFactor);
GraphicsContextStateSaver stateSaver(paintInfo.context);
NSControlSize controlSize = [buttonCell controlSize];
IntSize zoomedSize = ThemeMac::buttonSizes()[controlSize];
// Buttons don't ever constrain width, so the zoomed width can just be
// honored.
zoomedSize.setWidth(zoomedRect.width());
zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
IntRect inflatedRect = zoomedRect;
if ([buttonCell bezelStyle] == NSRoundedBezelStyle) {
// Center the button within the available space.
if (inflatedRect.height() > zoomedSize.height()) {
inflatedRect.setY(inflatedRect.y() +
(inflatedRect.height() - zoomedSize.height()) / 2);
inflatedRect.setHeight(zoomedSize.height());
}
// Now inflate it to account for the shadow.
inflatedRect =
ThemeMac::inflateRect(inflatedRect, zoomedSize,
ThemeMac::buttonMargins(controlSize), zoomFactor);
if (zoomFactor != 1.0f) {
inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
paintInfo.context.translate(inflatedRect.x(), inflatedRect.y());
paintInfo.context.scale(zoomFactor, zoomFactor);
paintInfo.context.translate(-inflatedRect.x(), -inflatedRect.y());
}
}
LocalCurrentGraphicsContext localContext(
paintInfo.context, ThemeMac::inflateRectForFocusRing(inflatedRect));
NSView* view = ThemeMac::ensuredView(object.view()->frameView());
[buttonCell drawWithFrame:NSRect(inflatedRect) inView:view];
if (states & FocusControlState)
[buttonCell cr_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
[buttonCell setControlView:nil];
END_BLOCK_OBJC_EXCEPTIONS
return false;
}
static ThemeDrawState convertControlStatesToThemeDrawState(
ThemeButtonKind kind,
ControlStates states) {
if (states & ReadOnlyControlState)
return kThemeStateUnavailableInactive;
if (!(states & EnabledControlState))
return kThemeStateUnavailableInactive;
// Do not process PressedState if !EnabledControlState or
// ReadOnlyControlState.
if (states & PressedControlState) {
if (kind == kThemeIncDecButton || kind == kThemeIncDecButtonSmall ||
kind == kThemeIncDecButtonMini)
return states & SpinUpControlState ? kThemeStatePressedUp
: kThemeStatePressedDown;
return kThemeStatePressed;
}
return kThemeStateActive;
}
bool ThemePainterMac::paintInnerSpinButton(const LayoutObject& object,
const PaintInfo& paintInfo,
const IntRect& zoomedRect) {
ControlStates states = LayoutTheme::controlStatesForLayoutObject(object);
float zoomFactor = object.styleRef().effectiveZoom();
// We don't use NSStepperCell because there are no ways to draw an
// NSStepperCell with the up button highlighted.
HIThemeButtonDrawInfo drawInfo;
drawInfo.version = 0;
drawInfo.state =
convertControlStatesToThemeDrawState(kThemeIncDecButton, states);
drawInfo.adornment = kThemeAdornmentDefault;
ControlSize controlSize = ThemeMac::controlSizeFromPixelSize(
ThemeMac::stepperSizes(), zoomedRect.size(), zoomFactor);
if (controlSize == NSSmallControlSize)
drawInfo.kind = kThemeIncDecButtonSmall;
else if (controlSize == NSMiniControlSize)
drawInfo.kind = kThemeIncDecButtonMini;
else
drawInfo.kind = kThemeIncDecButton;
IntRect rect(zoomedRect);
GraphicsContextStateSaver stateSaver(paintInfo.context);
if (zoomFactor != 1.0f) {
rect.setWidth(rect.width() / zoomFactor);
rect.setHeight(rect.height() / zoomFactor);
paintInfo.context.translate(rect.x(), rect.y());
paintInfo.context.scale(zoomFactor, zoomFactor);
paintInfo.context.translate(-rect.x(), -rect.y());
}
CGRect bounds(rect);
CGRect backgroundBounds;
HIThemeGetButtonBackgroundBounds(&bounds, &drawInfo, &backgroundBounds);
// Center the stepper rectangle in the specified area.
backgroundBounds.origin.x =
bounds.origin.x + (bounds.size.width - backgroundBounds.size.width) / 2;
if (backgroundBounds.size.height < bounds.size.height) {
int heightDiff =
clampTo<int>(bounds.size.height - backgroundBounds.size.height);
backgroundBounds.origin.y = bounds.origin.y + (heightDiff / 2) + 1;
}
LocalCurrentGraphicsContext localContext(paintInfo.context, rect);
HIThemeDrawButton(&backgroundBounds, &drawInfo, localContext.cgContext(),
kHIThemeOrientationNormal, 0);
return false;
}
} // namespace blink