WebCore/platform/ScrollbarThemeComposite.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/platform/ScrollbarThemeComposite.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2008 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 "ScrollbarThemeComposite.h"
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "Page.h"
+#include "PlatformMouseEvent.h"
+#include "Scrollbar.h"
+#include "ScrollbarClient.h"
+#include "Settings.h"
+
+namespace WebCore {
+
+#if PLATFORM(WIN)
+static Page* pageForScrollView(ScrollView* view)
+{
+    if (!view)
+        return 0;
+    if (!view->isFrameView())
+        return 0;
+    FrameView* frameView = static_cast<FrameView*>(view);
+    if (!frameView->frame())
+        return 0;
+    return frameView->frame()->page();
+}
+#endif
+
+bool ScrollbarThemeComposite::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
+{
+    // Create the ScrollbarControlPartMask based on the damageRect
+    ScrollbarControlPartMask scrollMask = NoPart;
+
+    IntRect backButtonStartPaintRect;
+    IntRect backButtonEndPaintRect;
+    IntRect forwardButtonStartPaintRect;
+    IntRect forwardButtonEndPaintRect;
+    if (hasButtons(scrollbar)) {
+        backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
+        if (damageRect.intersects(backButtonStartPaintRect))
+            scrollMask |= BackButtonStartPart;
+        backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
+        if (damageRect.intersects(backButtonEndPaintRect))
+            scrollMask |= BackButtonEndPart;
+        forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
+        if (damageRect.intersects(forwardButtonStartPaintRect))
+            scrollMask |= ForwardButtonStartPart;
+        forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
+        if (damageRect.intersects(forwardButtonEndPaintRect))
+            scrollMask |= ForwardButtonEndPart;
+    }
+
+    IntRect startTrackRect;
+    IntRect thumbRect;
+    IntRect endTrackRect;
+    IntRect trackPaintRect = trackRect(scrollbar, true);
+    if (damageRect.intersects(trackPaintRect))
+        scrollMask |= TrackBGPart;
+    bool thumbPresent = hasThumb(scrollbar);
+    if (thumbPresent) {
+        IntRect track = trackRect(scrollbar);
+        splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect);
+        if (damageRect.intersects(thumbRect))
+            scrollMask |= ThumbPart;
+        if (damageRect.intersects(startTrackRect))
+            scrollMask |= BackTrackPart;
+        if (damageRect.intersects(endTrackRect))
+            scrollMask |= ForwardTrackPart;
+    } 
+
+#if PLATFORM(WIN)
+    // FIXME: This API makes the assumption that the custom scrollbar's metrics will match
+    // the theme's metrics.  This is not a valid assumption.  The ability for a client to paint
+    // custom scrollbars should be removed once scrollbars can be styled via CSS.
+    if (Page* page = pageForScrollView(scrollbar->parent())) {
+        if (page->settings()->shouldPaintCustomScrollbars()) {
+            float proportion = static_cast<float>(scrollbar->visibleSize()) / scrollbar->totalSize();
+            float value = scrollbar->currentPos() / static_cast<float>(scrollbar->maximum());
+            ScrollbarControlState s = 0;
+            if (scrollbar->client()->isActive())
+                s |= ActiveScrollbarState;
+            if (scrollbar->enabled())
+                s |= EnabledScrollbarState;
+            if (scrollbar->pressedPart() != NoPart)
+                s |= PressedScrollbarState;
+            if (page->chrome()->client()->paintCustomScrollbar(graphicsContext,
+                                                               scrollbar->frameRect(), 
+                                                               scrollbar->controlSize(),
+                                                               s, 
+                                                               scrollbar->pressedPart(), 
+                                                               scrollbar->orientation() == VerticalScrollbar,
+                                                               value,
+                                                               proportion,
+                                                               scrollMask))
+                return true;
+        }
+    }
+#endif
+
+    // Paint the scrollbar background (only used by custom CSS scrollbars).
+    paintScrollbarBackground(graphicsContext, scrollbar);
+
+    // Paint the back and forward buttons.
+    if (scrollMask & BackButtonStartPart)
+        paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
+    if (scrollMask & BackButtonEndPart)
+        paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
+    if (scrollMask & ForwardButtonStartPart)
+        paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
+    if (scrollMask & ForwardButtonEndPart)
+        paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
+    
+    if (scrollMask & TrackBGPart)
+        paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
+    
+    if ((scrollMask & ForwardTrackPart) || (scrollMask & BackTrackPart)) {
+        // Paint the track pieces above and below the thumb.
+        if (scrollMask & BackTrackPart)
+            paintTrackPiece(graphicsContext, scrollbar, startTrackRect, BackTrackPart);
+        if (scrollMask & ForwardTrackPart)
+            paintTrackPiece(graphicsContext, scrollbar, endTrackRect, ForwardTrackPart);
+
+        paintTickmarks(graphicsContext, scrollbar, trackPaintRect);
+    }
+
+    // Paint the thumb.
+    if (scrollMask & ThumbPart)
+        paintThumb(graphicsContext, scrollbar, thumbRect);
+
+    return true;
+}
+
+ScrollbarPart ScrollbarThemeComposite::hitTest(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
+{
+    ScrollbarPart result = NoPart;
+    if (!scrollbar->enabled())
+        return result;
+
+    IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
+    mousePosition.move(scrollbar->x(), scrollbar->y());
+    
+    if (!scrollbar->frameRect().contains(mousePosition))
+        return NoPart;
+
+    result = ScrollbarBGPart;
+
+    IntRect track = trackRect(scrollbar);
+    if (track.contains(mousePosition)) {
+        IntRect beforeThumbRect;
+        IntRect thumbRect;
+        IntRect afterThumbRect;
+        splitTrack(scrollbar, track, beforeThumbRect, thumbRect, afterThumbRect);
+        if (thumbRect.contains(mousePosition))
+            result = ThumbPart;
+        else if (beforeThumbRect.contains(mousePosition))
+            result = BackTrackPart;
+        else if (afterThumbRect.contains(mousePosition))
+            result = ForwardTrackPart;
+        else
+            result = TrackBGPart;
+    } else if (backButtonRect(scrollbar, BackButtonStartPart).contains(mousePosition))
+        result = BackButtonStartPart;
+    else if (backButtonRect(scrollbar, BackButtonEndPart).contains(mousePosition))
+        result = BackButtonEndPart;
+    else if (forwardButtonRect(scrollbar, ForwardButtonStartPart).contains(mousePosition))
+        result = ForwardButtonStartPart;
+    else if (forwardButtonRect(scrollbar, ForwardButtonEndPart).contains(mousePosition))
+        result = ForwardButtonEndPart;
+    return result;
+}
+
+void ScrollbarThemeComposite::invalidatePart(Scrollbar* scrollbar, ScrollbarPart part)
+{
+    if (part == NoPart)
+        return;
+
+    IntRect result;    
+    switch (part) {
+        case BackButtonStartPart:
+            result = backButtonRect(scrollbar, BackButtonStartPart, true);
+            break;
+        case BackButtonEndPart:
+            result = backButtonRect(scrollbar, BackButtonEndPart, true);
+            break;
+        case ForwardButtonStartPart:
+            result = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
+            break;
+        case ForwardButtonEndPart:
+            result = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
+            break;
+        case TrackBGPart:
+            result = trackRect(scrollbar, true);
+            break;
+        case ScrollbarBGPart:
+            result = scrollbar->frameRect();
+            break;
+        default: {
+            IntRect beforeThumbRect, thumbRect, afterThumbRect;
+            splitTrack(scrollbar, trackRect(scrollbar), beforeThumbRect, thumbRect, afterThumbRect);
+            if (part == BackTrackPart)
+                result = beforeThumbRect;
+            else if (part == ForwardTrackPart)
+                result = afterThumbRect;
+            else
+                result = thumbRect;
+        }
+    }
+    result.move(-scrollbar->x(), -scrollbar->y());
+    scrollbar->invalidateRect(result);
+}
+
+void ScrollbarThemeComposite::splitTrack(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect)
+{
+    // This function won't even get called unless we're big enough to have some combination of these three rects where at least
+    // one of them is non-empty.
+    IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
+    int thickness = scrollbar->orientation() == HorizontalScrollbar ? scrollbar->height() : scrollbar->width();
+    int thumbPos = thumbPosition(scrollbar);
+    if (scrollbar->orientation() == HorizontalScrollbar) {
+        thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - thickness) / 2, thumbLength(scrollbar), thickness); 
+        beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumbRect.width() / 2, trackRect.height()); 
+        afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.right() - beforeThumbRect.right(), trackRect.height());
+    } else {
+        thumbRect = IntRect(trackRect.x() + (trackRect.width() - thickness) / 2, trackRect.y() + thumbPos, thickness, thumbLength(scrollbar));
+        beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos + thumbRect.height() / 2); 
+        afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.bottom() - beforeThumbRect.bottom());
+    }
+}
+
+int ScrollbarThemeComposite::thumbPosition(Scrollbar* scrollbar)
+{
+    if (scrollbar->enabled())
+        return scrollbar->currentPos() * (trackLength(scrollbar) - thumbLength(scrollbar)) / scrollbar->maximum();
+    return 0;
+}
+
+int ScrollbarThemeComposite::thumbLength(Scrollbar* scrollbar)
+{
+    if (!scrollbar->enabled())
+        return 0;
+
+    float proportion = (float)scrollbar->visibleSize() / scrollbar->totalSize();
+    int trackLen = trackLength(scrollbar);
+    int length = proportion * trackLen;
+    length = max(length, minimumThumbLength(scrollbar));
+    if (length > trackLen)
+        length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track).
+    return length;
+}
+
+int ScrollbarThemeComposite::minimumThumbLength(Scrollbar* scrollbar)
+{
+    return scrollbarThickness(scrollbar->controlSize());
+}
+
+int ScrollbarThemeComposite::trackPosition(Scrollbar* scrollbar)
+{
+    IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
+    return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.x() - scrollbar->x() : constrainedTrackRect.y() - scrollbar->y();
+}
+
+int ScrollbarThemeComposite::trackLength(Scrollbar* scrollbar)
+{
+    IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
+    return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.width() : constrainedTrackRect.height();
+}
+
+void ScrollbarThemeComposite::paintScrollCorner(ScrollView* view, GraphicsContext* context, const IntRect& cornerRect)
+{
+    FrameView* frameView = static_cast<FrameView*>(view);
+    Page* page = frameView->frame() ? frameView->frame()->page() : 0;
+    if (page && page->settings()->shouldPaintCustomScrollbars()) {
+        if (!page->chrome()->client()->paintCustomScrollCorner(context, cornerRect))
+            context->fillRect(cornerRect, Color::white, DeviceColorSpace);
+    }
+}
+
+}