| /* |
| * 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 |