2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Google Inc. All Rights Reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "ScrollbarThemeChromiumMac.h"
30 #include "BitmapImage.h"
31 #include "FrameView.h"
33 #include "ImageBuffer.h"
34 #include "LocalCurrentGraphicsContext.h"
35 #include "PlatformMouseEvent.h"
36 #include "PlatformSupport.h"
37 #include "ScrollAnimatorChromiumMac.h"
38 #include "ScrollView.h"
39 #include <Carbon/Carbon.h>
40 #include <wtf/HashMap.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/UnusedParam.h>
45 #include "PlatformContextSkia.h"
46 #include "skia/ext/skia_utils_mac.h"
51 // FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
54 using namespace WebCore;
56 // This file (and its associated .h file) is a clone of ScrollbarThemeMac.mm.
57 // Because we want to draw tickmarks in the scrollbar, we must maintain a fork.
58 // Please maintain this file by performing parallel changes to it.
60 // The only changes from ScrollbarThemeMac should be:
61 // - The classname change from ScrollbarThemeMac to ScrollbarThemeChromiumMac.
62 // - In paint() the code to paint the track, tickmarks, and thumb separately.
63 // - In paint() the thumb is drawn via ChromeBridge/WebThemeEngine.
64 // - Various functions that were split using #if USE(WK_SCROLLBAR_PAINTER)
65 // have been combined using runtime checks instead.
67 // For all other differences, if it was introduced in this file, then the
68 // maintainer forgot to include it in the list; otherwise it is an update that
69 // should have been applied to this file but was not.
73 typedef HashMap<Scrollbar*, RetainPtr<WKScrollbarPainterRef> > ScrollbarPainterMap;
75 static ScrollbarPainterMap* scrollbarMap()
77 static ScrollbarPainterMap* map = new ScrollbarPainterMap;
83 @interface ScrollbarPrefsObserver : NSObject
87 + (void)registerAsObserver;
88 + (void)appearancePrefsChanged:(NSNotification*)theNotification;
89 + (void)behaviorPrefsChanged:(NSNotification*)theNotification;
93 @implementation ScrollbarPrefsObserver
95 + (void)appearancePrefsChanged:(NSNotification*)unusedNotification
97 UNUSED_PARAM(unusedNotification);
99 static_cast<ScrollbarThemeChromiumMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
100 if (scrollbarMap()->isEmpty())
102 ScrollbarPainterMap::iterator end = scrollbarMap()->end();
103 for (ScrollbarPainterMap::iterator it = scrollbarMap()->begin(); it != end; ++it) {
104 it->first->styleChanged();
105 it->first->invalidate();
109 + (void)behaviorPrefsChanged:(NSNotification*)unusedNotification
111 UNUSED_PARAM(unusedNotification);
113 static_cast<ScrollbarThemeChromiumMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
116 + (void)registerAsObserver
118 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appearancePrefsChanged:) name:@"AppleAquaScrollBarVariantChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
119 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(behaviorPrefsChanged:) name:@"AppleNoRedisplayAppearancePreferenceChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
126 ScrollbarTheme* ScrollbarTheme::nativeTheme()
128 DEFINE_STATIC_LOCAL(ScrollbarThemeChromiumMac, theme, ());
132 // FIXME: Get these numbers from CoreUI.
133 static int cScrollbarThickness[] = { 15, 11 };
134 static int cRealButtonLength[] = { 28, 21 };
135 static int cButtonInset[] = { 14, 11 };
136 static int cButtonHitInset[] = { 3, 2 };
137 // cRealButtonLength - cButtonInset
138 static int cButtonLength[] = { 14, 10 };
139 static int cThumbMinLength[] = { 26, 20 };
141 static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
142 static int cOuterButtonOverlap = 2;
144 static float gInitialButtonDelay = 0.5f;
145 static float gAutoscrollButtonDelay = 0.05f;
146 static bool gJumpOnTrackClick = false;
147 static ScrollbarButtonsPlacement gButtonPlacement = isScrollbarOverlayAPIAvailable() ? ScrollbarButtonsNone : ScrollbarButtonsDoubleEnd;
149 static void updateArrowPlacement()
151 if (isScrollbarOverlayAPIAvailable())
154 NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
155 if ([buttonPlacement isEqualToString:@"Single"])
156 gButtonPlacement = ScrollbarButtonsSingle;
157 else if ([buttonPlacement isEqualToString:@"DoubleMin"])
158 gButtonPlacement = ScrollbarButtonsDoubleStart;
159 else if ([buttonPlacement isEqualToString:@"DoubleBoth"])
160 gButtonPlacement = ScrollbarButtonsDoubleBoth;
162 gButtonPlacement = ScrollbarButtonsDoubleEnd;
165 void ScrollbarThemeChromiumMac::registerScrollbar(Scrollbar* scrollbar)
167 bool isHorizontal = scrollbar->orientation() == HorizontalScrollbar;
168 WKScrollbarPainterRef scrollbarPainter = wkMakeScrollbarPainter(scrollbar->controlSize(), isHorizontal);
169 scrollbarMap()->add(scrollbar, scrollbarPainter);
170 updateEnabledState(scrollbar);
171 updateScrollbarOverlayStyle(scrollbar);
174 void ScrollbarThemeChromiumMac::unregisterScrollbar(Scrollbar* scrollbar)
176 scrollbarMap()->remove(scrollbar);
179 void ScrollbarThemeChromiumMac::setNewPainterForScrollbar(Scrollbar* scrollbar, WKScrollbarPainterRef newPainter)
181 scrollbarMap()->set(scrollbar, newPainter);
182 updateEnabledState(scrollbar);
183 updateScrollbarOverlayStyle(scrollbar);
186 WKScrollbarPainterRef ScrollbarThemeChromiumMac::painterForScrollbar(Scrollbar* scrollbar)
188 return scrollbarMap()->get(scrollbar).get();
191 ScrollbarThemeChromiumMac::ScrollbarThemeChromiumMac()
193 static bool initialized;
197 // Load the linen pattern image used for overhang drawing.
198 RefPtr<Image> patternImage = Image::loadPlatformResource("overhangPattern");
199 m_overhangPattern = Pattern::create(patternImage, true, true);
201 [ScrollbarPrefsObserver registerAsObserver];
202 preferencesChanged();
206 ScrollbarThemeChromiumMac::~ScrollbarThemeChromiumMac()
210 void ScrollbarThemeChromiumMac::preferencesChanged()
212 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
213 [defaults synchronize];
214 updateArrowPlacement();
215 gInitialButtonDelay = [defaults floatForKey:@"NSScrollerButtonDelay"];
216 gAutoscrollButtonDelay = [defaults floatForKey:@"NSScrollerButtonPeriod"];
217 gJumpOnTrackClick = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
220 int ScrollbarThemeChromiumMac::scrollbarThickness(ScrollbarControlSize controlSize)
222 if (isScrollbarOverlayAPIAvailable())
223 return wkScrollbarThickness(controlSize);
225 return cScrollbarThickness[controlSize];
228 bool ScrollbarThemeChromiumMac::usesOverlayScrollbars() const
230 if (isScrollbarOverlayAPIAvailable())
231 return wkScrollbarPainterUsesOverlayScrollers();
236 static inline wkScrollerKnobStyle toScrollbarPainterKnobStyle(ScrollbarOverlayStyle style)
239 case ScrollbarOverlayStyleDark:
240 return wkScrollerKnobStyleDark;
241 case ScrollbarOverlayStyleLight:
242 return wkScrollerKnobStyleLight;
244 return wkScrollerKnobStyleDefault;
248 void ScrollbarThemeChromiumMac::updateScrollbarOverlayStyle(Scrollbar* scrollbar)
250 if (isScrollbarOverlayAPIAvailable()) {
251 wkSetScrollbarPainterKnobStyle(painterForScrollbar(scrollbar), toScrollbarPainterKnobStyle(scrollbar->scrollableArea()->scrollbarOverlayStyle()));
255 double ScrollbarThemeChromiumMac::initialAutoscrollTimerDelay()
257 return gInitialButtonDelay;
260 double ScrollbarThemeChromiumMac::autoscrollTimerDelay()
262 return gAutoscrollButtonDelay;
265 ScrollbarButtonsPlacement ScrollbarThemeChromiumMac::buttonsPlacement() const
267 return gButtonPlacement;
270 bool ScrollbarThemeChromiumMac::hasButtons(Scrollbar* scrollbar)
272 return scrollbar->enabled() && gButtonPlacement != ScrollbarButtonsNone
273 && (scrollbar->orientation() == HorizontalScrollbar
275 : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
278 bool ScrollbarThemeChromiumMac::hasThumb(Scrollbar* scrollbar)
280 int minLengthForThumb;
281 if (isScrollbarOverlayAPIAvailable())
282 minLengthForThumb = wkScrollbarMinimumTotalLengthNeededForThumb(scrollbarMap()->get(scrollbar).get());
284 minLengthForThumb = 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
285 return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
287 scrollbar->height()) >= minLengthForThumb;
290 static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
292 ASSERT(gButtonPlacement != ScrollbarButtonsNone);
294 IntRect paintRect(buttonRect);
295 if (orientation == HorizontalScrollbar) {
296 paintRect.setWidth(cRealButtonLength[controlSize]);
298 paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
300 paintRect.setHeight(cRealButtonLength[controlSize]);
302 paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
308 IntRect ScrollbarThemeChromiumMac::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
312 if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
315 if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
318 int thickness = scrollbarThickness(scrollbar->controlSize());
319 bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
321 if (scrollbar->orientation() == HorizontalScrollbar)
322 result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0), thickness);
324 result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0));
328 // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
329 if (scrollbar->orientation() == HorizontalScrollbar) {
330 int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
331 result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
333 int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
334 result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
338 return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
342 IntRect ScrollbarThemeChromiumMac::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
346 if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
349 if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
352 int thickness = scrollbarThickness(scrollbar->controlSize());
353 int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
354 int buttonLength = cButtonLength[scrollbar->controlSize()];
356 bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
358 if (scrollbar->orientation() == HorizontalScrollbar) {
359 result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
361 result.inflateX(cOuterButtonOverlap);
363 result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
365 result.inflateY(cOuterButtonOverlap);
370 if (scrollbar->orientation() == HorizontalScrollbar) {
371 int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
372 result = IntRect(start, scrollbar->y(), buttonLength, thickness);
374 int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
375 result = IntRect(scrollbar->x(), start, thickness, buttonLength);
378 return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
382 IntRect ScrollbarThemeChromiumMac::trackRect(Scrollbar* scrollbar, bool painting)
384 if (painting || !hasButtons(scrollbar))
385 return scrollbar->frameRect();
388 int thickness = scrollbarThickness(scrollbar->controlSize());
391 int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
392 int buttonLength = cButtonLength[scrollbar->controlSize()];
393 int doubleButtonLength = outerButtonLength + buttonLength;
394 switch (buttonsPlacement()) {
395 case ScrollbarButtonsSingle:
396 startWidth = buttonLength;
397 endWidth = buttonLength;
399 case ScrollbarButtonsDoubleStart:
400 startWidth = doubleButtonLength;
402 case ScrollbarButtonsDoubleEnd:
403 endWidth = doubleButtonLength;
405 case ScrollbarButtonsDoubleBoth:
406 startWidth = doubleButtonLength;
407 endWidth = doubleButtonLength;
413 int totalWidth = startWidth + endWidth;
414 if (scrollbar->orientation() == HorizontalScrollbar)
415 return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
416 return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
419 int ScrollbarThemeChromiumMac::minimumThumbLength(Scrollbar* scrollbar)
421 if (isScrollbarOverlayAPIAvailable())
422 return wkScrollbarMinimumThumbLength(scrollbarMap()->get(scrollbar).get());
424 return cThumbMinLength[scrollbar->controlSize()];
427 bool ScrollbarThemeChromiumMac::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
429 if (evt.button() != LeftButton)
431 if (gJumpOnTrackClick)
432 return !evt.altKey();
436 bool ScrollbarThemeChromiumMac::shouldDragDocumentInsteadOfThumb(Scrollbar*, const PlatformMouseEvent& event)
438 return event.altKey();
441 static int scrollbarPartToHIPressedState(ScrollbarPart part)
444 case BackButtonStartPart:
445 return kThemeTopOutsideArrowPressed;
446 case BackButtonEndPart:
447 return kThemeTopOutsideArrowPressed; // This does not make much sense. For some reason the outside constant is required.
448 case ForwardButtonStartPart:
449 return kThemeTopInsideArrowPressed;
450 case ForwardButtonEndPart:
451 return kThemeBottomOutsideArrowPressed;
453 return kThemeThumbPressed;
459 static PlatformSupport::ThemePaintState scrollbarStateToThemeState(Scrollbar* scrollbar)
461 if (!scrollbar->enabled())
462 return PlatformSupport::StateDisabled;
463 if (!scrollbar->scrollableArea()->isActive())
464 return PlatformSupport::StateInactive;
465 if (scrollbar->pressedPart() == ThumbPart)
466 return PlatformSupport::StatePressed;
468 return PlatformSupport::StateActive;
471 void ScrollbarThemeChromiumMac::updateEnabledState(Scrollbar* scrollbar)
473 if (isScrollbarOverlayAPIAvailable()) {
474 wkScrollbarPainterSetEnabled(scrollbarMap()->get(scrollbar).get(), scrollbar->enabled());
478 bool ScrollbarThemeChromiumMac::paint(Scrollbar* scrollbar, GraphicsContext* context, const IntRect& damageRect)
480 // Get the tickmarks for the frameview.
481 Vector<IntRect> tickmarks;
482 scrollbar->scrollableArea()->getTickmarks(tickmarks);
484 if (isScrollbarOverlayAPIAvailable()) {
488 if (scrollbar->currentPos() < 0) {
489 // Scrolled past the top.
491 overhang = -scrollbar->currentPos();
492 } else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->totalSize()) {
493 // Scrolled past the bottom.
495 overhang = scrollbar->currentPos() + scrollbar->visibleSize() - scrollbar->totalSize();
497 // Within the bounds of the scrollable area.
498 int maximum = scrollbar->maximum();
500 value = scrollbar->currentPos() / maximum;
505 ScrollAnimatorChromiumMac* scrollAnimator = static_cast<ScrollAnimatorChromiumMac*>(scrollbar->scrollableArea()->scrollAnimator());
507 scrollAnimator->setIsDrawingIntoLayer(context->isCALayerContext());
509 scrollAnimator->setIsDrawingIntoLayer(false);
512 CGFloat oldKnobAlpha = 0;
513 CGFloat oldTrackAlpha = 0;
514 bool hasTickmarks = tickmarks.size() > 0 && scrollbar->orientation() == VerticalScrollbar;
516 oldKnobAlpha = wkScrollbarPainterKnobAlpha(painterForScrollbar(scrollbar));
517 wkSetScrollbarPainterKnobAlpha(painterForScrollbar(scrollbar), 1.0);
518 oldTrackAlpha = wkScrollbarPainterTrackAlpha(painterForScrollbar(scrollbar));
519 wkSetScrollbarPainterTrackAlpha(painterForScrollbar(scrollbar), 1.0);
522 GraphicsContextStateSaver stateSaver(*context);
523 context->clip(damageRect);
524 context->translate(scrollbar->frameRect().x(), scrollbar->frameRect().y());
525 LocalCurrentGraphicsContext localContext(context);
526 WKScrollbarPainterRef scrollbarPainter = scrollbarMap()->get(scrollbar).get();
527 wkScrollbarPainterPaintTrack(scrollbarPainter,
528 scrollbar->enabled(),
530 (static_cast<CGFloat>(scrollbar->visibleSize()) - overhang) / scrollbar->totalSize(),
531 scrollbar->frameRect());
533 IntRect tickmarkTrackRect(IntPoint(), trackRect(scrollbar, false).size());
534 if (tickmarkTrackRect.width() <= 10) {
535 // For narrow scrollbars inset by 1 on the left and 3 on the right.
536 tickmarkTrackRect.setX(tickmarkTrackRect.x() + 1);
537 tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 4);
539 // For wide scrollbars inset by 2 on the left and 3 on the right.
540 tickmarkTrackRect.setX(tickmarkTrackRect.x() + 2);
541 tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 5);
543 paintGivenTickmarks(context, scrollbar, tickmarkTrackRect, tickmarks);
545 wkScrollbarPainterPaintKnob(scrollbarPainter);
547 scrollAnimator->setIsDrawingIntoLayer(false);
550 wkSetScrollbarPainterKnobAlpha(scrollbarPainter, oldKnobAlpha);
551 wkSetScrollbarPainterTrackAlpha(scrollbarPainter, oldTrackAlpha);
557 HIThemeTrackDrawInfo trackInfo;
558 trackInfo.version = 0;
559 trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar;
560 trackInfo.bounds = scrollbar->frameRect();
562 trackInfo.max = scrollbar->maximum();
563 trackInfo.value = scrollbar->currentPos();
564 trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize();
565 trackInfo.attributes = 0;
566 if (scrollbar->orientation() == HorizontalScrollbar)
567 trackInfo.attributes |= kThemeTrackHorizontal;
569 if (!scrollbar->enabled())
570 trackInfo.enableState = kThemeTrackDisabled;
572 trackInfo.enableState = scrollbar->scrollableArea()->isActive() ? kThemeTrackActive : kThemeTrackInactive;
574 if (!hasButtons(scrollbar))
575 trackInfo.enableState = kThemeTrackNothingToScroll;
576 trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart());
579 SkCanvas* canvas = context->platformContext()->canvas();
580 CGAffineTransform currentCTM = gfx::SkMatrixToCGAffineTransform(canvas->getTotalMatrix());
582 CGAffineTransform currentCTM = CGContextGetCTM(context->platformContext());
585 // The Aqua scrollbar is buggy when rotated and scaled. We will just draw into a bitmap if we detect a scale or rotation.
586 bool canDrawDirectly = currentCTM.a == 1.0f && currentCTM.b == 0.0f && currentCTM.c == 0.0f && (currentCTM.d == 1.0f || currentCTM.d == -1.0f);
587 GraphicsContext* drawingContext = context;
588 OwnPtr<ImageBuffer> imageBuffer;
589 if (!canDrawDirectly) {
590 trackInfo.bounds = IntRect(IntPoint(), scrollbar->frameRect().size());
592 IntRect bufferRect(scrollbar->frameRect());
593 bufferRect.intersect(damageRect);
594 bufferRect.move(-scrollbar->frameRect().x(), -scrollbar->frameRect().y());
596 imageBuffer = ImageBuffer::create(bufferRect.size());
600 drawingContext = imageBuffer->context();
605 gfx::SkiaBitLocker bitLocker(drawingContext->platformContext()->canvas());
606 CGContextRef cgContext = bitLocker.cgContext();
608 CGContextRef cgContext = drawingContext->platformContext();
610 HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
612 IntRect tickmarkTrackRect = trackRect(scrollbar, false);
613 if (!canDrawDirectly) {
614 tickmarkTrackRect.setX(0);
615 tickmarkTrackRect.setY(0);
617 // The ends are rounded and the thumb doesn't go there.
618 tickmarkTrackRect.inflateY(-tickmarkTrackRect.width());
619 // Inset by 2 on the left and 3 on the right.
620 tickmarkTrackRect.setX(tickmarkTrackRect.x() + 2);
621 tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 5);
622 paintGivenTickmarks(drawingContext, scrollbar, tickmarkTrackRect, tickmarks);
624 if (hasThumb(scrollbar)) {
625 PlatformSupport::ThemePaintScrollbarInfo scrollbarInfo;
626 scrollbarInfo.orientation = scrollbar->orientation() == HorizontalScrollbar ? PlatformSupport::ScrollbarOrientationHorizontal : PlatformSupport::ScrollbarOrientationVertical;
627 scrollbarInfo.parent = scrollbar->parent() && scrollbar->parent()->isFrameView() && static_cast<FrameView*>(scrollbar->parent())->isScrollViewScrollbar(scrollbar) ? PlatformSupport::ScrollbarParentScrollView : PlatformSupport::ScrollbarParentRenderLayer;
628 scrollbarInfo.maxValue = scrollbar->maximum();
629 scrollbarInfo.currentValue = scrollbar->currentPos();
630 scrollbarInfo.visibleSize = scrollbar->visibleSize();
631 scrollbarInfo.totalSize = scrollbar->totalSize();
633 PlatformSupport::paintScrollbarThumb(
635 scrollbarStateToThemeState(scrollbar),
636 scrollbar->controlSize() == RegularScrollbar ? PlatformSupport::SizeRegular : PlatformSupport::SizeSmall,
637 scrollbar->frameRect(),
641 if (!canDrawDirectly)
642 context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, scrollbar->frameRect().location());
647 void ScrollbarThemeChromiumMac::paintGivenTickmarks(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, const Vector<IntRect>& tickmarks)
649 if (scrollbar->orientation() != VerticalScrollbar)
652 if (rect.height() <= 0 || rect.width() <= 0)
653 return; // nothing to draw on.
655 if (!tickmarks.size())
658 GraphicsContextStateSaver stateSaver(*context);
659 context->setShouldAntialias(false);
660 context->setStrokeColor(Color(0xCC, 0xAA, 0x00, 0xFF), ColorSpaceDeviceRGB);
661 context->setFillColor(Color(0xFF, 0xDD, 0x00, 0xFF), ColorSpaceDeviceRGB);
663 for (Vector<IntRect>::const_iterator i = tickmarks.begin(); i != tickmarks.end(); ++i) {
664 // Calculate how far down (in %) the tick-mark should appear.
665 const float percent = static_cast<float>(i->y()) / scrollbar->totalSize();
666 if (percent < 0.0 || percent > 1.0)
669 // Calculate how far down (in pixels) the tick-mark should appear.
670 const int yPos = rect.y() + (rect.height() * percent);
673 FloatRect tickRect(rect.x(), yPos, rect.width(), 2);
674 context->fillRect(tickRect);
675 context->strokeRect(tickRect, 1);
679 void ScrollbarThemeChromiumMac::paintOverhangAreas(ScrollView* view, GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
681 // The extent of each shadow in pixels.
682 const int kShadowSize = 4;
683 // Offset of negative one pixel to make the gradient blend with the toolbar's bottom border.
684 const int kToolbarShadowOffset = -1;
688 } kShadowColors[] = {
689 { 0.000, Color(0, 0, 0, 255) },
690 { 0.125, Color(0, 0, 0, 57) },
691 { 0.375, Color(0, 0, 0, 41) },
692 { 0.625, Color(0, 0, 0, 18) },
693 { 0.875, Color(0, 0, 0, 6) },
694 { 1.000, Color(0, 0, 0, 0) }
696 const unsigned kNumShadowColors = sizeof(kShadowColors)/sizeof(kShadowColors[0]);
698 const bool hasHorizontalOverhang = !horizontalOverhangRect.isEmpty();
699 const bool hasVerticalOverhang = !verticalOverhangRect.isEmpty();
700 // Prefer non-additive shadows, but degrade to additive shadows if there is vertical overhang.
701 const bool useAdditiveShadows = hasVerticalOverhang;
703 GraphicsContextStateSaver stateSaver(*context);
705 context->setFillPattern(m_overhangPattern);
706 if (hasHorizontalOverhang)
707 context->fillRect(intersection(horizontalOverhangRect, dirtyRect));
708 if (hasVerticalOverhang)
709 context->fillRect(intersection(verticalOverhangRect, dirtyRect));
711 IntSize scrollOffset = view->scrollOffset();
712 FloatPoint shadowCornerOrigin;
713 FloatPoint shadowCornerOffset;
715 // Draw the shadow for the horizontal overhang.
716 if (hasHorizontalOverhang) {
717 int toolbarShadowHeight = kShadowSize;
718 RefPtr<Gradient> gradient;
719 IntRect shadowRect = horizontalOverhangRect;
720 shadowRect.setHeight(kShadowSize);
721 if (scrollOffset.height() < 0) {
722 if (useAdditiveShadows) {
723 toolbarShadowHeight = std::min(horizontalOverhangRect.height(), kShadowSize);
724 } else if (horizontalOverhangRect.height() < 2 * kShadowSize + kToolbarShadowOffset) {
725 // Split the overhang area between the web content shadow and toolbar shadow if it's too small.
726 shadowRect.setHeight((horizontalOverhangRect.height() + 1) / 2);
727 toolbarShadowHeight = horizontalOverhangRect.height() - shadowRect.height() - kToolbarShadowOffset;
729 shadowRect.setY(horizontalOverhangRect.maxY() - shadowRect.height());
730 gradient = Gradient::create(FloatPoint(0, shadowRect.maxY()), FloatPoint(0, shadowRect.maxY() - kShadowSize));
731 shadowCornerOrigin.setY(shadowRect.maxY());
732 shadowCornerOffset.setY(-kShadowSize);
734 gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.maxY()));
735 shadowCornerOrigin.setY(shadowRect.y());
737 if (hasVerticalOverhang) {
738 shadowRect.setWidth(shadowRect.width() - verticalOverhangRect.width());
739 if (scrollOffset.width() < 0) {
740 shadowRect.setX(shadowRect.x() + verticalOverhangRect.width());
741 shadowCornerOrigin.setX(shadowRect.x());
742 shadowCornerOffset.setX(-kShadowSize);
744 shadowCornerOrigin.setX(shadowRect.maxX());
747 for (unsigned i = 0; i < kNumShadowColors; i++)
748 gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
749 context->setFillGradient(gradient);
750 context->fillRect(intersection(shadowRect, dirtyRect));
752 // Draw a drop-shadow from the toolbar.
753 if (scrollOffset.height() < 0) {
754 shadowRect.setY(kToolbarShadowOffset);
755 shadowRect.setHeight(toolbarShadowHeight);
756 gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.y() + kShadowSize));
757 for (unsigned i = 0; i < kNumShadowColors; i++)
758 gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
759 context->setFillGradient(gradient);
760 context->fillRect(intersection(shadowRect, dirtyRect));
764 // Draw the shadow for the vertical overhang.
765 if (hasVerticalOverhang) {
766 RefPtr<Gradient> gradient;
767 IntRect shadowRect = verticalOverhangRect;
768 shadowRect.setWidth(kShadowSize);
769 if (scrollOffset.width() < 0) {
770 shadowRect.setX(verticalOverhangRect.maxX() - shadowRect.width());
771 gradient = Gradient::create(FloatPoint(shadowRect.maxX(), 0), FloatPoint(shadowRect.x(), 0));
773 gradient = Gradient::create(FloatPoint(shadowRect.x(), 0), FloatPoint(shadowRect.maxX(), 0));
775 for (unsigned i = 0; i < kNumShadowColors; i++)
776 gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
777 context->setFillGradient(gradient);
778 context->fillRect(intersection(shadowRect, dirtyRect));
780 // Draw a drop-shadow from the toolbar.
781 shadowRect = verticalOverhangRect;
782 shadowRect.setY(kToolbarShadowOffset);
783 shadowRect.setHeight(kShadowSize);
784 gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.maxY()));
785 for (unsigned i = 0; i < kNumShadowColors; i++)
786 gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
787 context->setFillGradient(gradient);
788 context->fillRect(intersection(shadowRect, dirtyRect));
791 // If both rectangles present, draw a radial gradient for the corner.
792 if (hasHorizontalOverhang && hasVerticalOverhang) {
793 RefPtr<Gradient> gradient = Gradient::create(shadowCornerOrigin, 0, shadowCornerOrigin, kShadowSize);
794 for (unsigned i = 0; i < kNumShadowColors; i++)
795 gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
796 context->setFillGradient(gradient);
797 context->fillRect(FloatRect(shadowCornerOrigin.x() + shadowCornerOffset.x(), shadowCornerOrigin.y() + shadowCornerOffset.y(), kShadowSize, kShadowSize));