diff -r 000000000000 -r 4f2f89ce4247 WebCore/platform/ScrollbarThemeComposite.cpp --- /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(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(scrollbar->visibleSize()) / scrollbar->totalSize(); + float value = scrollbar->currentPos() / static_cast(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(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); + } +} + +}