Home | History | Annotate | Download | only in scroll
      1 /*
      2  * Copyright (c) 2010, Google Inc. All rights reserved.
      3  * Copyright (C) 2008, 2011 Apple Inc. All Rights Reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "config.h"
     33 #include "platform/scroll/ScrollableArea.h"
     34 
     35 #include "platform/graphics/GraphicsLayer.h"
     36 #include "platform/geometry/FloatPoint.h"
     37 #include "platform/scroll//ScrollbarTheme.h"
     38 #include "wtf/PassOwnPtr.h"
     39 
     40 #include "platform/TraceEvent.h"
     41 
     42 static const int kPixelsPerLineStep = 40;
     43 static const float kMinFractionToStepWhenPaging = 0.875f;
     44 
     45 namespace WebCore {
     46 
     47 struct SameSizeAsScrollableArea {
     48     virtual ~SameSizeAsScrollableArea();
     49     void* pointer;
     50     unsigned bitfields : 16;
     51     IntPoint origin;
     52 };
     53 
     54 COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
     55 
     56 int ScrollableArea::pixelsPerLineStep()
     57 {
     58     return kPixelsPerLineStep;
     59 }
     60 
     61 float ScrollableArea::minFractionToStepWhenPaging()
     62 {
     63     return kMinFractionToStepWhenPaging;
     64 }
     65 
     66 int ScrollableArea::maxOverlapBetweenPages()
     67 {
     68     static int maxOverlapBetweenPages = ScrollbarTheme::theme()->maxOverlapBetweenPages();
     69     return maxOverlapBetweenPages;
     70 }
     71 
     72 ScrollableArea::ScrollableArea()
     73     : m_constrainsScrollingToContentEdge(true)
     74     , m_inLiveResize(false)
     75     , m_verticalScrollElasticity(ScrollElasticityNone)
     76     , m_horizontalScrollElasticity(ScrollElasticityNone)
     77     , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
     78     , m_scrollOriginChanged(false)
     79 {
     80 }
     81 
     82 ScrollableArea::~ScrollableArea()
     83 {
     84 }
     85 
     86 ScrollAnimator* ScrollableArea::scrollAnimator() const
     87 {
     88     if (!m_scrollAnimator)
     89         m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
     90 
     91     return m_scrollAnimator.get();
     92 }
     93 
     94 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
     95 {
     96     if (m_scrollOrigin != origin) {
     97         m_scrollOrigin = origin;
     98         m_scrollOriginChanged = true;
     99     }
    100 }
    101 
    102 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
    103 {
    104     ScrollbarOrientation orientation;
    105 
    106     if (direction == ScrollUp || direction == ScrollDown)
    107         orientation = VerticalScrollbar;
    108     else
    109         orientation = HorizontalScrollbar;
    110 
    111     if (!userInputScrollable(orientation))
    112         return false;
    113 
    114     float step = 0;
    115     switch (granularity) {
    116     case ScrollByLine:
    117         step = lineStep(orientation);
    118         break;
    119     case ScrollByPage:
    120         step = pageStep(orientation);
    121         break;
    122     case ScrollByDocument:
    123         step = documentStep(orientation);
    124         break;
    125     case ScrollByPixel:
    126     case ScrollByPrecisePixel:
    127         step = pixelStep(orientation);
    128         break;
    129     }
    130 
    131     if (direction == ScrollUp || direction == ScrollLeft)
    132         multiplier = -multiplier;
    133 
    134     return scrollAnimator()->scroll(orientation, granularity, step, multiplier);
    135 }
    136 
    137 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
    138 {
    139     scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
    140 }
    141 
    142 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
    143 {
    144     if (orientation == HorizontalScrollbar)
    145         scrollToOffsetWithoutAnimation(FloatPoint(offset, scrollAnimator()->currentPosition().y()));
    146     else
    147         scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), offset));
    148 }
    149 
    150 void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
    151 {
    152     scrollPositionChanged(position);
    153     scrollAnimator()->setCurrentPosition(position);
    154 }
    155 
    156 void ScrollableArea::scrollPositionChanged(const IntPoint& position)
    157 {
    158     TRACE_EVENT0("webkit", "ScrollableArea::scrollPositionChanged");
    159 
    160     IntPoint oldPosition = scrollPosition();
    161     // Tell the derived class to scroll its contents.
    162     setScrollOffset(position);
    163 
    164     Scrollbar* verticalScrollbar = this->verticalScrollbar();
    165 
    166     // Tell the scrollbars to update their thumb postions.
    167     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
    168         horizontalScrollbar->offsetDidChange();
    169         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
    170             if (!verticalScrollbar)
    171                 horizontalScrollbar->invalidate();
    172             else {
    173                 // If there is both a horizontalScrollbar and a verticalScrollbar,
    174                 // then we must also invalidate the corner between them.
    175                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
    176                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
    177                 horizontalScrollbar->invalidateRect(boundsAndCorner);
    178             }
    179         }
    180     }
    181     if (verticalScrollbar) {
    182         verticalScrollbar->offsetDidChange();
    183         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
    184             verticalScrollbar->invalidate();
    185     }
    186 
    187     if (scrollPosition() != oldPosition)
    188         scrollAnimator()->notifyContentAreaScrolled(scrollPosition() - oldPosition);
    189 }
    190 
    191 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
    192 {
    193     return scrollAnimator()->handleWheelEvent(wheelEvent);
    194 }
    195 
    196 // NOTE: Only called from Internals for testing.
    197 void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
    198 {
    199     setScrollOffsetFromAnimation(offset);
    200 }
    201 
    202 void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
    203 {
    204     scrollPositionChanged(offset);
    205 }
    206 
    207 void ScrollableArea::willStartLiveResize()
    208 {
    209     if (m_inLiveResize)
    210         return;
    211     m_inLiveResize = true;
    212     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    213         scrollAnimator->willStartLiveResize();
    214 }
    215 
    216 void ScrollableArea::willEndLiveResize()
    217 {
    218     if (!m_inLiveResize)
    219         return;
    220     m_inLiveResize = false;
    221     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    222         scrollAnimator->willEndLiveResize();
    223 }
    224 
    225 void ScrollableArea::contentAreaWillPaint() const
    226 {
    227     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    228         scrollAnimator->contentAreaWillPaint();
    229 }
    230 
    231 void ScrollableArea::mouseEnteredContentArea() const
    232 {
    233     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    234         scrollAnimator->mouseEnteredContentArea();
    235 }
    236 
    237 void ScrollableArea::mouseExitedContentArea() const
    238 {
    239     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    240         scrollAnimator->mouseEnteredContentArea();
    241 }
    242 
    243 void ScrollableArea::mouseMovedInContentArea() const
    244 {
    245     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    246         scrollAnimator->mouseMovedInContentArea();
    247 }
    248 
    249 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
    250 {
    251     scrollAnimator()->mouseEnteredScrollbar(scrollbar);
    252 }
    253 
    254 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
    255 {
    256     scrollAnimator()->mouseExitedScrollbar(scrollbar);
    257 }
    258 
    259 void ScrollableArea::contentAreaDidShow() const
    260 {
    261     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    262         scrollAnimator->contentAreaDidShow();
    263 }
    264 
    265 void ScrollableArea::contentAreaDidHide() const
    266 {
    267     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    268         scrollAnimator->contentAreaDidHide();
    269 }
    270 
    271 void ScrollableArea::finishCurrentScrollAnimations() const
    272 {
    273     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    274         scrollAnimator->finishCurrentScrollAnimations();
    275 }
    276 
    277 void ScrollableArea::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
    278 {
    279     if (orientation == VerticalScrollbar)
    280         scrollAnimator()->didAddVerticalScrollbar(scrollbar);
    281     else
    282         scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
    283 
    284     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
    285     setScrollbarOverlayStyle(scrollbarOverlayStyle());
    286 }
    287 
    288 void ScrollableArea::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
    289 {
    290     if (orientation == VerticalScrollbar)
    291         scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
    292     else
    293         scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
    294 }
    295 
    296 void ScrollableArea::contentsResized()
    297 {
    298     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    299         scrollAnimator->contentsResized();
    300 }
    301 
    302 bool ScrollableArea::hasOverlayScrollbars() const
    303 {
    304     return (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
    305         || (horizontalScrollbar() && horizontalScrollbar()->isOverlayScrollbar());
    306 }
    307 
    308 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
    309 {
    310     m_scrollbarOverlayStyle = overlayStyle;
    311 
    312     if (horizontalScrollbar()) {
    313         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(horizontalScrollbar());
    314         horizontalScrollbar()->invalidate();
    315     }
    316 
    317     if (verticalScrollbar()) {
    318         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(verticalScrollbar());
    319         verticalScrollbar()->invalidate();
    320     }
    321 }
    322 
    323 void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
    324 {
    325     if (scrollbar == horizontalScrollbar()) {
    326         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
    327             graphicsLayer->setNeedsDisplay();
    328             graphicsLayer->setContentsNeedsDisplay();
    329             return;
    330         }
    331     } else if (scrollbar == verticalScrollbar()) {
    332         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
    333             graphicsLayer->setNeedsDisplay();
    334             graphicsLayer->setContentsNeedsDisplay();
    335             return;
    336         }
    337     }
    338     invalidateScrollbarRect(scrollbar, rect);
    339 }
    340 
    341 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
    342 {
    343     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
    344         graphicsLayer->setNeedsDisplay();
    345         return;
    346     }
    347     invalidateScrollCornerRect(rect);
    348 }
    349 
    350 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
    351 {
    352     return layerForHorizontalScrollbar();
    353 }
    354 
    355 bool ScrollableArea::hasLayerForVerticalScrollbar() const
    356 {
    357     return layerForVerticalScrollbar();
    358 }
    359 
    360 bool ScrollableArea::hasLayerForScrollCorner() const
    361 {
    362     return layerForScrollCorner();
    363 }
    364 
    365 void ScrollableArea::serviceScrollAnimations()
    366 {
    367     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    368         scrollAnimator->serviceScrollAnimations();
    369 }
    370 
    371 IntRect ScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
    372 {
    373     int verticalScrollbarWidth = 0;
    374     int horizontalScrollbarHeight = 0;
    375 
    376     if (scrollbarInclusion == IncludeScrollbars) {
    377         if (Scrollbar* verticalBar = verticalScrollbar())
    378             verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
    379         if (Scrollbar* horizontalBar = horizontalScrollbar())
    380             horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
    381     }
    382 
    383     return IntRect(scrollPosition().x(),
    384                    scrollPosition().y(),
    385                    std::max(0, visibleWidth() + verticalScrollbarWidth),
    386                    std::max(0, visibleHeight() + horizontalScrollbarHeight));
    387 }
    388 
    389 IntPoint ScrollableArea::clampScrollPosition(const IntPoint& scrollPosition) const
    390 {
    391     return scrollPosition.shrunkTo(maximumScrollPosition()).expandedTo(minimumScrollPosition());
    392 }
    393 
    394 int ScrollableArea::lineStep(ScrollbarOrientation) const
    395 {
    396     return pixelsPerLineStep();
    397 }
    398 
    399 int ScrollableArea::documentStep(ScrollbarOrientation orientation) const
    400 {
    401     return scrollSize(orientation);
    402 }
    403 
    404 float ScrollableArea::pixelStep(ScrollbarOrientation) const
    405 {
    406     return 1;
    407 }
    408 
    409 } // namespace WebCore
    410