2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "platform/scroll/ScrollView.h"
29 #include "platform/graphics/GraphicsContextStateSaver.h"
30 #include "platform/graphics/GraphicsLayer.h"
31 #include "platform/HostWindow.h"
32 #include "platform/scroll/ScrollbarTheme.h"
33 #include "wtf/StdLibExtras.h"
39 ScrollView::ScrollView()
40 : m_horizontalScrollbarMode(ScrollbarAuto)
41 , m_verticalScrollbarMode(ScrollbarAuto)
42 , m_horizontalScrollbarLock(false)
43 , m_verticalScrollbarLock(false)
44 , m_scrollbarsAvoidingResizer(0)
45 , m_scrollbarsSuppressed(false)
46 , m_inUpdateScrollbars(false)
47 , m_updateScrollbarsPass(0)
48 , m_drawPanScrollIcon(false)
49 , m_paintsEntireContents(false)
50 , m_clipsRepaints(true)
54 ScrollView::~ScrollView()
58 void ScrollView::addChild(PassRefPtr<Widget> prpChild)
60 Widget* child = prpChild.get();
61 ASSERT(child != this && !child->parent());
62 child->setParent(this);
63 m_children.add(prpChild);
66 void ScrollView::removeChild(Widget* child)
68 ASSERT(child->parent() == this);
70 m_children.remove(child);
73 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
75 if (hasBar && !m_horizontalScrollbar) {
76 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
77 addChild(m_horizontalScrollbar.get());
78 didAddScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
79 m_horizontalScrollbar->styleChanged();
80 } else if (!hasBar && m_horizontalScrollbar) {
81 willRemoveScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
82 removeChild(m_horizontalScrollbar.get());
83 m_horizontalScrollbar = nullptr;
87 void ScrollView::setHasVerticalScrollbar(bool hasBar)
89 if (hasBar && !m_verticalScrollbar) {
90 m_verticalScrollbar = createScrollbar(VerticalScrollbar);
91 addChild(m_verticalScrollbar.get());
92 didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
93 m_verticalScrollbar->styleChanged();
94 } else if (!hasBar && m_verticalScrollbar) {
95 willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
96 removeChild(m_verticalScrollbar.get());
97 m_verticalScrollbar = nullptr;
101 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
103 return Scrollbar::create(this, orientation, RegularScrollbar);
106 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
107 bool horizontalLock, bool verticalLock)
109 bool needsUpdate = false;
111 if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
112 m_horizontalScrollbarMode = horizontalMode;
116 if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
117 m_verticalScrollbarMode = verticalMode;
122 setHorizontalScrollbarLock();
125 setVerticalScrollbarLock();
130 updateScrollbars(scrollOffset());
132 if (!layerForScrolling())
134 blink::WebLayer* layer = layerForScrolling()->platformLayer();
137 layer->setUserScrollable(userInputScrollable(HorizontalScrollbar), userInputScrollable(VerticalScrollbar));
140 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
142 horizontalMode = m_horizontalScrollbarMode;
143 verticalMode = m_verticalScrollbarMode;
146 void ScrollView::setCanHaveScrollbars(bool canScroll)
148 ScrollbarMode newHorizontalMode;
149 ScrollbarMode newVerticalMode;
151 scrollbarModes(newHorizontalMode, newVerticalMode);
153 if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
154 newVerticalMode = ScrollbarAuto;
156 newVerticalMode = ScrollbarAlwaysOff;
158 if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
159 newHorizontalMode = ScrollbarAuto;
161 newHorizontalMode = ScrollbarAlwaysOff;
163 setScrollbarModes(newHorizontalMode, newVerticalMode);
166 bool ScrollView::shouldAttemptToScrollUsingFastPath() const
171 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
173 m_paintsEntireContents = paintsEntireContents;
176 void ScrollView::setClipsRepaints(bool clipsRepaints)
178 m_clipsRepaints = clipsRepaints;
181 IntSize ScrollView::unscaledVisibleContentSize(IncludeScrollbarsInRect scrollbarInclusion) const
183 return scrollbarInclusion == ExcludeScrollbars ? excludeScrollbars(frameRect().size()) : frameRect().size();
186 IntSize ScrollView::excludeScrollbars(const IntSize& size) const
188 int verticalScrollbarWidth = 0;
189 int horizontalScrollbarHeight = 0;
191 if (Scrollbar* verticalBar = verticalScrollbar())
192 verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
193 if (Scrollbar* horizontalBar = horizontalScrollbar())
194 horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
196 return IntSize(max(0, size.width() - verticalScrollbarWidth),
197 max(0, size.height() - horizontalScrollbarHeight));
201 IntRect ScrollView::visibleContentRect(IncludeScrollbarsInRect scollbarInclusion) const
203 FloatSize visibleContentSize = unscaledVisibleContentSize(scollbarInclusion);
204 visibleContentSize.scale(1 / visibleContentScaleFactor());
205 return IntRect(IntPoint(m_scrollOffset), expandedIntSize(visibleContentSize));
208 IntSize ScrollView::contentsSize() const
210 return m_contentsSize;
213 void ScrollView::setContentsSize(const IntSize& newSize)
215 if (contentsSize() == newSize)
217 m_contentsSize = newSize;
218 updateScrollbars(scrollOffset());
219 updateOverhangAreas();
222 IntPoint ScrollView::maximumScrollPosition() const
224 IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), contentsHeight() - visibleHeight() - scrollOrigin().y());
225 maximumOffset.clampNegativeToZero();
226 return maximumOffset;
229 IntPoint ScrollView::minimumScrollPosition() const
231 return IntPoint(-scrollOrigin().x(), -scrollOrigin().y());
234 IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
236 if (!constrainsScrollingToContentEdge())
239 IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
240 newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
241 return newScrollPosition;
244 int ScrollView::scrollSize(ScrollbarOrientation orientation) const
246 Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
248 // If no scrollbars are present, the content may still be scrollable.
250 IntSize scrollSize = m_contentsSize - visibleContentRect().size();
251 scrollSize.clampNegativeToZero();
252 return orientation == HorizontalScrollbar ? scrollSize.width() : scrollSize.height();
255 return scrollbar->totalSize() - scrollbar->visibleSize();
258 void ScrollView::notifyPageThatContentAreaWillPaint() const
262 void ScrollView::setScrollOffset(const IntPoint& offset)
264 scrollTo(toIntSize(adjustScrollPositionWithinRange(offset)));
267 void ScrollView::scrollTo(const IntSize& newOffset)
269 IntSize scrollDelta = newOffset - m_scrollOffset;
270 if (scrollDelta == IntSize())
272 m_scrollOffset = newOffset;
274 if (scrollbarsSuppressed())
277 m_pendingScrollDelta += scrollDelta;
280 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
282 IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
284 if (newScrollPosition == scrollPosition())
287 updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
290 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
292 ScrollDirection physicalDirection =
293 toPhysicalDirection(direction, isVerticalDocument(), isFlippedDocument());
295 return ScrollableArea::scroll(physicalDirection, granularity);
298 IntSize ScrollView::overhangAmount() const
302 IntPoint currentScrollPosition = scrollPosition();
303 IntPoint minScrollPosition = minimumScrollPosition();
304 IntPoint maxScrollPosition = maximumScrollPosition();
306 if (currentScrollPosition.x() < minScrollPosition.x())
307 stretch.setWidth(currentScrollPosition.x() - minScrollPosition.x());
308 if (currentScrollPosition.x() > maxScrollPosition.x())
309 stretch.setWidth(currentScrollPosition.x() - maxScrollPosition.x());
311 if (currentScrollPosition.y() < minScrollPosition.y())
312 stretch.setHeight(currentScrollPosition.y() - minScrollPosition.y());
313 if (currentScrollPosition.y() > maxScrollPosition.y())
314 stretch.setHeight(currentScrollPosition.y() - maxScrollPosition.y());
319 void ScrollView::windowResizerRectChanged()
321 updateScrollbars(scrollOffset());
324 static const unsigned cMaxUpdateScrollbarsPass = 2;
326 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
328 if (m_inUpdateScrollbars)
331 // If we came in here with the view already needing a layout, then go ahead and do that
332 // first. (This will be the common case, e.g., when the page changes due to window resizing for example).
333 // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
334 if (!m_scrollbarsSuppressed) {
335 m_inUpdateScrollbars = true;
336 scrollbarExistenceDidChange();
337 m_inUpdateScrollbars = false;
340 IntRect oldScrollCornerRect = scrollCornerRect();
342 bool hasHorizontalScrollbar = m_horizontalScrollbar;
343 bool hasVerticalScrollbar = m_verticalScrollbar;
345 bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
346 bool newHasVerticalScrollbar = hasVerticalScrollbar;
348 ScrollbarMode hScroll = m_horizontalScrollbarMode;
349 ScrollbarMode vScroll = m_verticalScrollbarMode;
351 if (hScroll != ScrollbarAuto)
352 newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
353 if (vScroll != ScrollbarAuto)
354 newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
356 if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
357 if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
358 setHasHorizontalScrollbar(newHasHorizontalScrollbar);
359 if (hasVerticalScrollbar != newHasVerticalScrollbar)
360 setHasVerticalScrollbar(newHasVerticalScrollbar);
362 bool scrollbarExistenceChanged = false;
364 IntSize docSize = contentsSize();
365 IntSize fullVisibleSize = visibleContentRect(IncludeScrollbars).size();
367 bool scrollbarsAreOverlay = ScrollbarTheme::theme()->usesOverlayScrollbars();
369 if (hScroll == ScrollbarAuto) {
370 newHasHorizontalScrollbar = docSize.width() > visibleWidth();
371 if (!scrollbarsAreOverlay && newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height())
372 newHasHorizontalScrollbar = false;
374 if (vScroll == ScrollbarAuto) {
375 newHasVerticalScrollbar = docSize.height() > visibleHeight();
376 if (!scrollbarsAreOverlay && newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height())
377 newHasVerticalScrollbar = false;
380 if (!scrollbarsAreOverlay) {
381 // If we ever turn one scrollbar off, always turn the other one off too. Never ever
382 // try to both gain/lose a scrollbar in the same pass.
383 if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
384 newHasVerticalScrollbar = false;
385 if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
386 newHasHorizontalScrollbar = false;
389 if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
390 scrollbarExistenceChanged = true;
391 if (scrollOrigin().y() && !newHasHorizontalScrollbar && !scrollbarsAreOverlay)
392 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->height()));
393 if (hasHorizontalScrollbar)
394 m_horizontalScrollbar->invalidate();
395 setHasHorizontalScrollbar(newHasHorizontalScrollbar);
398 if (hasVerticalScrollbar != newHasVerticalScrollbar) {
399 scrollbarExistenceChanged = true;
400 if (scrollOrigin().x() && !newHasVerticalScrollbar && !scrollbarsAreOverlay)
401 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->width(), scrollOrigin().y()));
402 if (hasVerticalScrollbar)
403 m_verticalScrollbar->invalidate();
404 setHasVerticalScrollbar(newHasVerticalScrollbar);
407 if (scrollbarExistenceChanged) {
408 if (scrollbarsAreOverlay) {
409 // Synchronize status of scrollbar layers if necessary.
410 m_inUpdateScrollbars = true;
411 scrollbarExistenceDidChange();
412 m_inUpdateScrollbars = false;
413 } else if (m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
414 m_updateScrollbarsPass++;
416 scrollbarExistenceDidChange();
417 IntSize newDocSize = contentsSize();
418 if (newDocSize == docSize) {
419 // The layout with the new scroll state had no impact on
420 // the document's overall size, so updateScrollbars didn't get called.
422 updateScrollbars(desiredOffset);
424 m_updateScrollbarsPass--;
429 // Set up the range, but only do this if we're not in a nested call (to avoid
430 // doing it multiple times).
431 if (m_updateScrollbarsPass)
434 m_inUpdateScrollbars = true;
436 if (m_horizontalScrollbar) {
437 int clientWidth = visibleWidth();
438 IntRect oldRect(m_horizontalScrollbar->frameRect());
439 IntRect hBarRect((shouldPlaceVerticalScrollbarOnLeft() && m_verticalScrollbar) ? m_verticalScrollbar->width() : 0,
440 height() - m_horizontalScrollbar->height(),
441 width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
442 m_horizontalScrollbar->height());
443 m_horizontalScrollbar->setFrameRect(hBarRect);
444 if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
445 m_horizontalScrollbar->invalidate();
447 if (m_scrollbarsSuppressed)
448 m_horizontalScrollbar->setSuppressInvalidation(true);
449 m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
450 m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
451 if (m_scrollbarsSuppressed)
452 m_horizontalScrollbar->setSuppressInvalidation(false);
455 if (m_verticalScrollbar) {
456 int clientHeight = visibleHeight();
457 IntRect oldRect(m_verticalScrollbar->frameRect());
458 IntRect vBarRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
460 m_verticalScrollbar->width(),
461 height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
462 m_verticalScrollbar->setFrameRect(vBarRect);
463 if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
464 m_verticalScrollbar->invalidate();
466 if (m_scrollbarsSuppressed)
467 m_verticalScrollbar->setSuppressInvalidation(true);
468 m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
469 m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
470 if (m_scrollbarsSuppressed)
471 m_verticalScrollbar->setSuppressInvalidation(false);
474 if (hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar) {
475 // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed?
477 positionScrollbarLayers();
478 updateScrollCorner();
479 if (!m_horizontalScrollbar && !m_verticalScrollbar)
480 invalidateScrollCornerRect(oldScrollCornerRect);
483 IntPoint adjustedScrollPosition = IntPoint(desiredOffset);
484 if (!isRubberBandInProgress())
485 adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
487 if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
488 ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition);
489 resetScrollOriginChanged();
492 // Make sure the scrollbar offsets are up to date.
493 if (m_horizontalScrollbar)
494 m_horizontalScrollbar->offsetDidChange();
495 if (m_verticalScrollbar)
496 m_verticalScrollbar->offsetDidChange();
498 m_inUpdateScrollbars = false;
501 const int panIconSizeLength = 16;
503 IntRect ScrollView::rectToCopyOnScroll() const
505 IntRect scrollViewRect = convertToRootView(IntRect((shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar()) ? verticalScrollbar()->width() : 0, 0, visibleWidth(), visibleHeight()));
506 if (hasOverlayScrollbars()) {
507 int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0;
508 int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0;
510 scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
511 scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
513 return scrollViewRect;
516 void ScrollView::scrollContentsIfNeeded()
518 if (m_pendingScrollDelta.isZero())
520 IntSize scrollDelta = m_pendingScrollDelta;
521 m_pendingScrollDelta = IntSize();
522 scrollContents(scrollDelta);
525 void ScrollView::scrollContents(const IntSize& scrollDelta)
527 HostWindow* window = hostWindow();
531 // Since scrolling is double buffered, we will be blitting the scroll view's intersection
532 // with the clip rect every time to keep it smooth.
533 IntRect clipRect = windowClipRect();
534 IntRect scrollViewRect = rectToCopyOnScroll();
535 IntRect updateRect = clipRect;
536 updateRect.intersect(scrollViewRect);
538 if (m_drawPanScrollIcon) {
539 // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
540 // https://bugs.webkit.org/show_bug.cgi?id=47837
541 int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
542 IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
543 IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
544 panScrollIconDirtyRect.intersect(clipRect);
545 window->invalidateContentsAndRootView(panScrollIconDirtyRect);
548 if (!shouldAttemptToScrollUsingFastPath() || !scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
549 scrollContentsSlowPath(updateRect);
551 // Invalidate the overhang areas if they are visible.
552 updateOverhangAreas();
554 // This call will move children with native widgets (plugins) and invalidate them as well.
558 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
560 hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
564 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
566 hostWindow()->invalidateContentsForSlowScroll(updateRect);
569 IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
571 IntPoint viewPoint = convertFromRootView(rootViewPoint);
572 return viewPoint + scrollOffset();
575 IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
577 IntPoint viewPoint = contentsPoint - scrollOffset();
578 return convertToRootView(viewPoint);
581 IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
583 IntRect viewRect = convertFromRootView(rootViewRect);
584 viewRect.move(scrollOffset());
588 IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
590 IntRect viewRect = contentsRect;
591 viewRect.move(-scrollOffset());
592 return convertToRootView(viewRect);
595 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
597 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
598 return viewPoint + scrollOffset();
601 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
603 IntPoint viewPoint = contentsPoint - scrollOffset();
604 return convertToContainingWindow(viewPoint);
607 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
609 IntRect viewRect = convertFromContainingWindow(windowRect);
610 viewRect.move(scrollOffset());
614 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
616 IntRect viewRect = contentsRect;
617 viewRect.move(-scrollOffset());
618 return convertToContainingWindow(viewRect);
621 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
623 HostWindow* window = hostWindow();
626 return window->rootViewToScreen(contentsToRootView(rect));
629 bool ScrollView::containsScrollbarsAvoidingResizer() const
631 return !m_scrollbarsAvoidingResizer;
634 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
636 int oldCount = m_scrollbarsAvoidingResizer;
637 m_scrollbarsAvoidingResizer += overlapDelta;
639 toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(overlapDelta);
640 else if (!scrollbarsSuppressed()) {
641 // If we went from n to 0 or from 0 to n and we're the outermost view,
642 // we need to invalidate the windowResizerRect(), since it will now need to paint
644 if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
645 (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
646 invalidateRect(windowResizerRect());
650 void ScrollView::setParent(Widget* parentView)
652 if (parentView == parent())
655 if (m_scrollbarsAvoidingResizer && parent())
656 toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
658 Widget::setParent(parentView);
660 if (m_scrollbarsAvoidingResizer && parent())
661 toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
664 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
666 if (suppressed == m_scrollbarsSuppressed)
669 m_scrollbarsSuppressed = suppressed;
671 if (repaintOnUnsuppress && !suppressed) {
672 if (m_horizontalScrollbar)
673 m_horizontalScrollbar->invalidate();
674 if (m_verticalScrollbar)
675 m_verticalScrollbar->invalidate();
677 // Invalidate the scroll corner too on unsuppress.
678 invalidateRect(scrollCornerRect());
682 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
684 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
685 if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint))
686 return m_horizontalScrollbar.get();
687 if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint))
688 return m_verticalScrollbar.get();
692 void ScrollView::setFrameRect(const IntRect& newRect)
694 IntRect oldRect = frameRect();
696 if (newRect == oldRect)
699 Widget::setFrameRect(newRect);
701 updateScrollbars(scrollOffset());
706 void ScrollView::frameRectsChanged()
708 HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
709 for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
710 (*current)->frameRectsChanged();
713 static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
715 if (!graphicsLayer || !scrollbar)
718 IntRect scrollbarRect = scrollbar->frameRect();
719 graphicsLayer->setPosition(scrollbarRect.location());
721 if (scrollbarRect.size() == graphicsLayer->size())
724 graphicsLayer->setSize(scrollbarRect.size());
726 if (graphicsLayer->hasContentsLayer()) {
727 graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height()));
731 graphicsLayer->setDrawsContent(true);
732 graphicsLayer->setNeedsDisplay();
735 static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect)
739 graphicsLayer->setDrawsContent(!cornerRect.isEmpty());
740 graphicsLayer->setPosition(cornerRect.location());
741 if (cornerRect.size() != graphicsLayer->size())
742 graphicsLayer->setNeedsDisplay();
743 graphicsLayer->setSize(cornerRect.size());
746 void ScrollView::positionScrollbarLayers()
748 positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar());
749 positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar());
750 positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect());
753 bool ScrollView::userInputScrollable(ScrollbarOrientation orientation) const
755 ScrollbarMode mode = (orientation == HorizontalScrollbar) ?
756 m_horizontalScrollbarMode : m_verticalScrollbarMode;
758 return mode == ScrollbarAuto || mode == ScrollbarAlwaysOn;
761 bool ScrollView::shouldPlaceVerticalScrollbarOnLeft() const
766 void ScrollView::repaintContentRectangle(const IntRect& rect)
768 IntRect paintRect = rect;
769 if (clipsRepaints() && !paintsEntireContents())
770 paintRect.intersect(visibleContentRect());
771 if (paintRect.isEmpty())
774 if (HostWindow* window = hostWindow())
775 window->invalidateContentsAndRootView(contentsToWindow(paintRect));
778 IntRect ScrollView::scrollCornerRect() const
782 if (hasOverlayScrollbars())
785 if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
786 cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(),
787 height() - m_horizontalScrollbar->height(),
788 width() - m_horizontalScrollbar->width(),
789 m_horizontalScrollbar->height()));
792 if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
793 cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
794 m_verticalScrollbar->height(),
795 m_verticalScrollbar->width(),
796 height() - m_verticalScrollbar->height()));
802 bool ScrollView::isScrollCornerVisible() const
804 return !scrollCornerRect().isEmpty();
807 void ScrollView::scrollbarStyleChanged(int, bool forceUpdate)
813 updateScrollbars(scrollOffset());
814 positionScrollbarLayers();
817 void ScrollView::updateScrollCorner()
821 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
823 ScrollbarTheme::theme()->paintScrollCorner(context, cornerRect);
826 void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
828 bar->paint(context, rect);
831 void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
833 invalidateRect(rect);
836 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
838 if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
839 paintScrollbar(context, m_horizontalScrollbar.get(), rect);
840 if (m_verticalScrollbar && !layerForVerticalScrollbar())
841 paintScrollbar(context, m_verticalScrollbar.get(), rect);
843 if (layerForScrollCorner())
845 paintScrollCorner(context, scrollCornerRect());
848 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
850 DEFINE_STATIC_REF(Image, panScrollIcon, (Image::loadPlatformResource("panIcon")));
851 IntPoint iconGCPoint = m_panScrollIconPoint;
853 iconGCPoint = toScrollView(parent())->windowToContents(iconGCPoint);
854 context->drawImage(panScrollIcon, iconGCPoint);
857 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
859 if (context->paintingDisabled() && !context->updatingControlTints())
862 notifyPageThatContentAreaWillPaint();
864 IntRect documentDirtyRect = rect;
865 if (!paintsEntireContents()) {
866 IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect().size());
867 documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
870 if (!documentDirtyRect.isEmpty()) {
871 GraphicsContextStateSaver stateSaver(*context);
873 context->translate(x(), y());
874 documentDirtyRect.moveBy(-location());
876 if (!paintsEntireContents()) {
877 context->translate(-scrollX(), -scrollY());
878 documentDirtyRect.moveBy(scrollPosition());
880 context->clip(visibleContentRect());
883 paintContents(context, documentDirtyRect);
886 calculateAndPaintOverhangAreas(context, rect);
888 // Now paint the scrollbars.
889 if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
890 GraphicsContextStateSaver stateSaver(*context);
891 IntRect scrollViewDirtyRect = rect;
892 IntRect visibleAreaWithScrollbars(location(), visibleContentRect(IncludeScrollbars).size());
893 scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
894 context->translate(x(), y());
895 scrollViewDirtyRect.moveBy(-location());
897 paintScrollbars(context, scrollViewDirtyRect);
900 // Paint the panScroll Icon
901 if (m_drawPanScrollIcon)
902 paintPanScrollIcon(context);
905 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
907 int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
908 ? verticalScrollbar()->width() : 0;
909 int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar())
910 ? horizontalScrollbar()->height() : 0;
912 int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
913 if (physicalScrollY < 0) {
914 horizontalOverhangRect = frameRect();
915 horizontalOverhangRect.setHeight(-physicalScrollY);
916 horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
917 } else if (contentsHeight() && physicalScrollY > contentsHeight() - visibleHeight()) {
918 int height = physicalScrollY - (contentsHeight() - visibleHeight());
919 horizontalOverhangRect = frameRect();
920 horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
921 horizontalOverhangRect.setHeight(height);
922 horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
925 int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
926 if (physicalScrollX < 0) {
927 verticalOverhangRect.setWidth(-physicalScrollX);
928 verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
929 verticalOverhangRect.setX(frameRect().x());
930 if (horizontalOverhangRect.y() == frameRect().y())
931 verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
933 verticalOverhangRect.setY(frameRect().y());
934 } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) {
935 int width = physicalScrollX - (contentsWidth() - visibleWidth());
936 verticalOverhangRect.setWidth(width);
937 verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
938 verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
939 if (horizontalOverhangRect.y() == frameRect().y())
940 verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
942 verticalOverhangRect.setY(frameRect().y());
946 void ScrollView::updateOverhangAreas()
948 HostWindow* window = hostWindow();
952 IntRect horizontalOverhangRect;
953 IntRect verticalOverhangRect;
954 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
955 if (!horizontalOverhangRect.isEmpty())
956 window->invalidateContentsAndRootView(horizontalOverhangRect);
957 if (!verticalOverhangRect.isEmpty())
958 window->invalidateContentsAndRootView(verticalOverhangRect);
961 void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
963 ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
964 ScrollbarTheme::theme()->paintOverhangShadows(context, scrollOffset(), horizontalOverhangRect, verticalOverhangRect, dirtyRect);
967 void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect)
969 IntRect horizontalOverhangRect;
970 IntRect verticalOverhangRect;
971 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
973 if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
974 paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
977 void ScrollView::calculateAndPaintOverhangBackground(GraphicsContext* context, const IntRect& dirtyRect)
979 IntRect horizontalOverhangRect;
980 IntRect verticalOverhangRect;
981 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
983 if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
984 ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
987 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
989 if (!scrollbarCornerPresent())
992 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
994 if (m_horizontalScrollbar) {
995 int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
996 int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
997 int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
999 return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
1002 int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
1003 int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
1004 int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
1006 return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1009 bool ScrollView::scrollbarCornerPresent() const
1011 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0)
1012 || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1015 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
1017 // Scrollbars won't be transformed within us
1018 IntRect newRect = localRect;
1019 newRect.moveBy(scrollbar->location());
1023 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
1025 IntRect newRect = parentRect;
1026 // Scrollbars won't be transformed within us
1027 newRect.moveBy(-scrollbar->location());
1031 // FIXME: test these on windows
1032 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1034 // Scrollbars won't be transformed within us
1035 IntPoint newPoint = localPoint;
1036 newPoint.moveBy(scrollbar->location());
1040 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1042 IntPoint newPoint = parentPoint;
1043 // Scrollbars won't be transformed within us
1044 newPoint.moveBy(-scrollbar->location());
1048 void ScrollView::setParentVisible(bool visible)
1050 if (isParentVisible() == visible)
1053 Widget::setParentVisible(visible);
1055 if (!isSelfVisible())
1058 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1059 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1060 (*it)->setParentVisible(visible);
1063 void ScrollView::show()
1065 if (!isSelfVisible()) {
1066 setSelfVisible(true);
1067 if (isParentVisible()) {
1068 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1069 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1070 (*it)->setParentVisible(true);
1077 void ScrollView::hide()
1079 if (isSelfVisible()) {
1080 if (isParentVisible()) {
1081 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1082 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1083 (*it)->setParentVisible(false);
1085 setSelfVisible(false);
1091 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1093 HostWindow* window = hostWindow();
1096 m_drawPanScrollIcon = true;
1097 m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1098 window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1101 void ScrollView::removePanScrollIcon()
1103 HostWindow* window = hostWindow();
1106 m_drawPanScrollIcon = false;
1107 window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1110 void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1112 if (scrollOrigin() == origin)
1115 ScrollableArea::setScrollOrigin(origin);
1117 // Update if the scroll origin changes, since our position will be different if the content size did not change.
1118 if (updatePositionAtAll && updatePositionSynchronously)
1119 updateScrollbars(scrollOffset());
1122 } // namespace WebCore