summaryrefslogtreecommitdiff
path: root/chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-11-21 14:09:57 +0100
committerAndras Becsi <andras.becsi@digia.com>2013-11-29 15:14:36 +0100
commiteb32ba6f51d0c21d58cd7d89785285ff8fa64624 (patch)
tree2c7c940e1dbee81b89d935626110816b494aa32c /chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm
parent9427c1a0222ebd67efef1a2c7990a0fa5c9aac84 (diff)
downloadqtwebengine-chromium-eb32ba6f51d0c21d58cd7d89785285ff8fa64624.tar.gz
Update chromium to branch 1599.
Change-Id: I04e775a946a208bb4500d3b722bcb05c82b9d7cb Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm')
-rw-r--r--chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm396
1 files changed, 396 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm b/chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm
new file mode 100644
index 00000000000..6b7975afda3
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/core/platform/mac/ScrollbarThemeMac.mm
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2008, 2011 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/platform/mac/ScrollbarThemeMac.h"
+
+#include <Carbon/Carbon.h>
+#include "core/page/FrameView.h"
+#include "core/platform/PlatformMouseEvent.h"
+#include "core/platform/ScrollView.h"
+#include "core/platform/graphics/Gradient.h"
+#include "core/platform/graphics/GraphicsContext.h"
+#include "core/platform/graphics/GraphicsContextStateSaver.h"
+#include "core/platform/graphics/GraphicsLayer.h"
+#include "core/platform/graphics/ImageBuffer.h"
+#include "core/platform/graphics/mac/ColorMac.h"
+#include "core/platform/mac/LocalCurrentGraphicsContext.h"
+#include "core/platform/mac/NSScrollerImpDetails.h"
+#include "core/platform/mac/ScrollAnimatorMac.h"
+#include "core/platform/mac/ScrollbarThemeMacNonOverlayAPI.h"
+#include "core/platform/mac/ScrollbarThemeMacOverlayAPI.h"
+#include "public/platform/mac/WebThemeEngine.h"
+#include "public/platform/Platform.h"
+#include "public/platform/WebRect.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "wtf/HashMap.h"
+#include "wtf/StdLibExtras.h"
+#include "wtf/TemporaryChange.h"
+#include "wtf/UnusedParam.h"
+
+// FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
+
+using namespace std;
+using namespace WebCore;
+
+@interface NSColor (WebNSColorDetails)
++ (NSImage *)_linenPatternImage;
+@end
+
+namespace WebCore {
+
+typedef HashMap<ScrollbarThemeClient*, ScrollbarThemeClient*> ScrollbarMap;
+
+static ScrollbarMap* scrollbarMap()
+{
+ static ScrollbarMap* map = new ScrollbarMap;
+ return map;
+}
+
+}
+
+@interface WebScrollbarPrefsObserver : NSObject
+{
+}
+
++ (void)registerAsObserver;
++ (void)appearancePrefsChanged:(NSNotification*)theNotification;
++ (void)behaviorPrefsChanged:(NSNotification*)theNotification;
+
+@end
+
+@implementation WebScrollbarPrefsObserver
+
++ (void)appearancePrefsChanged:(NSNotification*)unusedNotification
+{
+ UNUSED_PARAM(unusedNotification);
+
+ ScrollbarTheme* theme = ScrollbarTheme::theme();
+ if (theme->isMockTheme())
+ return;
+
+ static_cast<ScrollbarThemeMacCommon*>(ScrollbarTheme::theme())->preferencesChanged();
+ if (scrollbarMap()->isEmpty())
+ return;
+ ScrollbarMap::iterator end = scrollbarMap()->end();
+ for (ScrollbarMap::iterator it = scrollbarMap()->begin(); it != end; ++it) {
+ it->key->styleChanged();
+ it->key->invalidate();
+ }
+}
+
++ (void)behaviorPrefsChanged:(NSNotification*)unusedNotification
+{
+ UNUSED_PARAM(unusedNotification);
+
+ ScrollbarTheme* theme = ScrollbarTheme::theme();
+ if (theme->isMockTheme())
+ return;
+
+ static_cast<ScrollbarThemeMacCommon*>(ScrollbarTheme::theme())->preferencesChanged();
+}
+
++ (void)registerAsObserver
+{
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appearancePrefsChanged:) name:@"AppleAquaScrollBarVariantChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(behaviorPrefsChanged:) name:@"AppleNoRedisplayAppearancePreferenceChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
+}
+
+@end
+
+namespace WebCore {
+
+static float gInitialButtonDelay = 0.5f;
+static float gAutoscrollButtonDelay = 0.05f;
+static bool gJumpOnTrackClick = false;
+
+ScrollbarTheme* ScrollbarTheme::nativeTheme()
+{
+ if (isScrollbarOverlayAPIAvailable()) {
+ DEFINE_STATIC_LOCAL(ScrollbarThemeMacOverlayAPI, theme, ());
+ return &theme;
+ } else {
+ DEFINE_STATIC_LOCAL(ScrollbarThemeMacNonOverlayAPI, theme, ());
+ return &theme;
+ }
+ return NULL;
+}
+
+void ScrollbarThemeMacCommon::registerScrollbar(ScrollbarThemeClient* scrollbar)
+{
+ scrollbarMap()->add(scrollbar, scrollbar);
+}
+
+void ScrollbarThemeMacCommon::unregisterScrollbar(ScrollbarThemeClient* scrollbar)
+{
+ scrollbarMap()->remove(scrollbar);
+}
+
+void ScrollbarThemeMacCommon::paintGivenTickmarks(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect, const Vector<IntRect>& tickmarks)
+{
+ if (scrollbar->orientation() != VerticalScrollbar)
+ return;
+
+ if (rect.height() <= 0 || rect.width() <= 0)
+ return; // nothing to draw on.
+
+ if (!tickmarks.size())
+ return;
+
+ GraphicsContextStateSaver stateSaver(*context);
+ context->setShouldAntialias(false);
+ context->setStrokeColor(Color(0xCC, 0xAA, 0x00, 0xFF));
+ context->setFillColor(Color(0xFF, 0xDD, 0x00, 0xFF));
+
+ for (Vector<IntRect>::const_iterator i = tickmarks.begin(); i != tickmarks.end(); ++i) {
+ // Calculate how far down (in %) the tick-mark should appear.
+ const float percent = static_cast<float>(i->y()) / scrollbar->totalSize();
+ if (percent < 0.0 || percent > 1.0)
+ continue;
+
+ // Calculate how far down (in pixels) the tick-mark should appear.
+ const int yPos = rect.y() + (rect.height() * percent);
+
+ // Paint.
+ FloatRect tickRect(rect.x(), yPos, rect.width(), 2);
+ context->fillRect(tickRect);
+ context->strokeRect(tickRect, 1);
+ }
+}
+
+void ScrollbarThemeMacCommon::paintOverhangAreas(ScrollView* view, GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
+{
+ // The extent of each shadow in pixels.
+ const int kShadowSize = 4;
+ // Offset of negative one pixel to make the gradient blend with the toolbar's bottom border.
+ const int kToolbarShadowOffset = -1;
+ const struct {
+ float stop;
+ Color color;
+ } kShadowColors[] = {
+ { 0.000, Color(0, 0, 0, 255) },
+ { 0.125, Color(0, 0, 0, 57) },
+ { 0.375, Color(0, 0, 0, 41) },
+ { 0.625, Color(0, 0, 0, 18) },
+ { 0.875, Color(0, 0, 0, 6) },
+ { 1.000, Color(0, 0, 0, 0) }
+ };
+ const unsigned kNumShadowColors = sizeof(kShadowColors)/sizeof(kShadowColors[0]);
+
+ const bool hasHorizontalOverhang = !horizontalOverhangRect.isEmpty();
+ const bool hasVerticalOverhang = !verticalOverhangRect.isEmpty();
+ // Prefer non-additive shadows, but degrade to additive shadows if there is vertical overhang.
+ const bool useAdditiveShadows = hasVerticalOverhang;
+
+ GraphicsContextStateSaver stateSaver(*context);
+
+ context->setFillPattern(m_overhangPattern);
+ if (hasHorizontalOverhang)
+ context->fillRect(intersection(horizontalOverhangRect, dirtyRect));
+ if (hasVerticalOverhang)
+ context->fillRect(intersection(verticalOverhangRect, dirtyRect));
+
+ IntSize scrollOffset = view->scrollOffset();
+ FloatPoint shadowCornerOrigin;
+ FloatPoint shadowCornerOffset;
+
+ // Draw the shadow for the horizontal overhang.
+ if (hasHorizontalOverhang) {
+ int toolbarShadowHeight = kShadowSize;
+ RefPtr<Gradient> gradient;
+ IntRect shadowRect = horizontalOverhangRect;
+ shadowRect.setHeight(kShadowSize);
+ if (scrollOffset.height() < 0) {
+ if (useAdditiveShadows) {
+ toolbarShadowHeight = std::min(horizontalOverhangRect.height(), kShadowSize);
+ } else if (horizontalOverhangRect.height() < 2 * kShadowSize + kToolbarShadowOffset) {
+ // Split the overhang area between the web content shadow and toolbar shadow if it's too small.
+ shadowRect.setHeight((horizontalOverhangRect.height() + 1) / 2);
+ toolbarShadowHeight = horizontalOverhangRect.height() - shadowRect.height() - kToolbarShadowOffset;
+ }
+ shadowRect.setY(horizontalOverhangRect.maxY() - shadowRect.height());
+ gradient = Gradient::create(FloatPoint(0, shadowRect.maxY()), FloatPoint(0, shadowRect.maxY() - kShadowSize));
+ shadowCornerOrigin.setY(shadowRect.maxY());
+ shadowCornerOffset.setY(-kShadowSize);
+ } else {
+ gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.maxY()));
+ shadowCornerOrigin.setY(shadowRect.y());
+ }
+ if (hasVerticalOverhang) {
+ shadowRect.setWidth(shadowRect.width() - verticalOverhangRect.width());
+ if (scrollOffset.width() < 0) {
+ shadowRect.setX(shadowRect.x() + verticalOverhangRect.width());
+ shadowCornerOrigin.setX(shadowRect.x());
+ shadowCornerOffset.setX(-kShadowSize);
+ } else {
+ shadowCornerOrigin.setX(shadowRect.maxX());
+ }
+ }
+ for (unsigned i = 0; i < kNumShadowColors; i++)
+ gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
+ context->setFillGradient(gradient);
+ context->fillRect(intersection(shadowRect, dirtyRect));
+
+ // Draw a drop-shadow from the toolbar.
+ if (scrollOffset.height() < 0) {
+ shadowRect.setY(kToolbarShadowOffset);
+ shadowRect.setHeight(toolbarShadowHeight);
+ gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.y() + kShadowSize));
+ for (unsigned i = 0; i < kNumShadowColors; i++)
+ gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
+ context->setFillGradient(gradient);
+ context->fillRect(intersection(shadowRect, dirtyRect));
+ }
+ }
+
+ // Draw the shadow for the vertical overhang.
+ if (hasVerticalOverhang) {
+ RefPtr<Gradient> gradient;
+ IntRect shadowRect = verticalOverhangRect;
+ shadowRect.setWidth(kShadowSize);
+ if (scrollOffset.width() < 0) {
+ shadowRect.setX(verticalOverhangRect.maxX() - shadowRect.width());
+ gradient = Gradient::create(FloatPoint(shadowRect.maxX(), 0), FloatPoint(shadowRect.x(), 0));
+ } else {
+ gradient = Gradient::create(FloatPoint(shadowRect.x(), 0), FloatPoint(shadowRect.maxX(), 0));
+ }
+ for (unsigned i = 0; i < kNumShadowColors; i++)
+ gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
+ context->setFillGradient(gradient);
+ context->fillRect(intersection(shadowRect, dirtyRect));
+
+ // Draw a drop-shadow from the toolbar.
+ shadowRect = verticalOverhangRect;
+ shadowRect.setY(kToolbarShadowOffset);
+ shadowRect.setHeight(kShadowSize);
+ gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.maxY()));
+ for (unsigned i = 0; i < kNumShadowColors; i++)
+ gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
+ context->setFillGradient(gradient);
+ context->fillRect(intersection(shadowRect, dirtyRect));
+ }
+
+ // If both rectangles present, draw a radial gradient for the corner.
+ if (hasHorizontalOverhang && hasVerticalOverhang) {
+ RefPtr<Gradient> gradient = Gradient::create(shadowCornerOrigin, 0, shadowCornerOrigin, kShadowSize);
+ for (unsigned i = 0; i < kNumShadowColors; i++)
+ gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
+ context->setFillGradient(gradient);
+ context->fillRect(FloatRect(shadowCornerOrigin.x() + shadowCornerOffset.x(), shadowCornerOrigin.y() + shadowCornerOffset.y(), kShadowSize, kShadowSize));
+ }
+}
+
+void ScrollbarThemeMacCommon::paintTickmarks(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect)
+{
+ // Note: This is only used for css-styled scrollbars on mac.
+ if (scrollbar->orientation() != VerticalScrollbar)
+ return;
+
+ if (rect.height() <= 0 || rect.width() <= 0)
+ return;
+
+ Vector<IntRect> tickmarks;
+ scrollbar->getTickmarks(tickmarks);
+ if (!tickmarks.size())
+ return;
+
+ // Inset a bit.
+ IntRect tickmarkTrackRect = rect;
+ tickmarkTrackRect.setX(tickmarkTrackRect.x() + 1);
+ tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 2);
+ paintGivenTickmarks(context, scrollbar, tickmarkTrackRect, tickmarks);
+}
+
+ScrollbarThemeMacCommon::ScrollbarThemeMacCommon()
+{
+ static bool initialized;
+ if (!initialized) {
+ initialized = true;
+ [WebScrollbarPrefsObserver registerAsObserver];
+ preferencesChanged();
+ }
+
+ // Load the linen pattern image used for overhang drawing.
+ RefPtr<Image> patternImage = Image::loadPlatformResource("overhangPattern");
+ m_overhangPattern = Pattern::create(patternImage, true, true);
+}
+
+ScrollbarThemeMacCommon::~ScrollbarThemeMacCommon()
+{
+}
+
+void ScrollbarThemeMacCommon::preferencesChanged()
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults synchronize];
+ updateButtonPlacement();
+ gInitialButtonDelay = [defaults floatForKey:@"NSScrollerButtonDelay"];
+ gAutoscrollButtonDelay = [defaults floatForKey:@"NSScrollerButtonPeriod"];
+ gJumpOnTrackClick = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
+}
+
+double ScrollbarThemeMacCommon::initialAutoscrollTimerDelay()
+{
+ return gInitialButtonDelay;
+}
+
+double ScrollbarThemeMacCommon::autoscrollTimerDelay()
+{
+ return gAutoscrollButtonDelay;
+}
+
+bool ScrollbarThemeMacCommon::shouldCenterOnThumb(ScrollbarThemeClient*, const PlatformMouseEvent& evt)
+{
+ if (evt.button() != LeftButton)
+ return false;
+ if (gJumpOnTrackClick)
+ return !evt.altKey();
+ return evt.altKey();
+}
+
+bool ScrollbarThemeMacCommon::shouldDragDocumentInsteadOfThumb(ScrollbarThemeClient*, const PlatformMouseEvent& event)
+{
+ return event.altKey();
+}
+
+int ScrollbarThemeMacCommon::scrollbarPartToHIPressedState(ScrollbarPart part)
+{
+ switch (part) {
+ case BackButtonStartPart:
+ return kThemeTopOutsideArrowPressed;
+ case BackButtonEndPart:
+ return kThemeTopOutsideArrowPressed; // This does not make much sense. For some reason the outside constant is required.
+ case ForwardButtonStartPart:
+ return kThemeTopInsideArrowPressed;
+ case ForwardButtonEndPart:
+ return kThemeBottomOutsideArrowPressed;
+ case ThumbPart:
+ return kThemeThumbPressed;
+ default:
+ return 0;
+ }
+}
+
+} // namespace WebCore