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 "ScrollView.h"
29 #include "GraphicsContext.h"
30 #include "HostWindow.h"
31 #include "PlatformMouseEvent.h"
32 #include "PlatformWheelEvent.h"
33 #include "Scrollbar.h"
34 #include "ScrollbarTheme.h"
35 #include <wtf/StdLibExtras.h>
42 ScrollView::ScrollView()
43 : m_horizontalScrollbarMode(ScrollbarAuto)
44 , m_verticalScrollbarMode(ScrollbarAuto)
45 , m_horizontalScrollbarLock(false)
46 , m_verticalScrollbarLock(false)
47 , m_prohibitsScrolling(false)
48 , m_canBlitOnScroll(true)
49 , m_scrollbarsAvoidingResizer(0)
50 , m_scrollbarsSuppressed(false)
51 , m_inUpdateScrollbars(false)
52 , m_updateScrollbarsPass(0)
53 , m_drawPanScrollIcon(false)
54 , m_useFixedLayout(false)
55 , m_paintsEntireContents(false)
60 ScrollView::~ScrollView()
65 void ScrollView::addChild(PassRefPtr<Widget> prpChild)
67 Widget* child = prpChild.get();
68 ASSERT(child != this && !child->parent());
69 child->setParent(this);
70 m_children.add(prpChild);
71 if (child->platformWidget())
72 platformAddChild(child);
75 void ScrollView::removeChild(Widget* child)
77 ASSERT(child->parent() == this);
79 m_children.remove(child);
80 if (child->platformWidget())
81 platformRemoveChild(child);
84 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
86 if (hasBar && avoidScrollbarCreation())
89 if (hasBar && !m_horizontalScrollbar) {
90 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
91 addChild(m_horizontalScrollbar.get());
92 m_horizontalScrollbar->styleChanged();
93 } else if (!hasBar && m_horizontalScrollbar) {
94 removeChild(m_horizontalScrollbar.get());
95 m_horizontalScrollbar = 0;
99 void ScrollView::setHasVerticalScrollbar(bool hasBar)
101 if (hasBar && avoidScrollbarCreation())
104 if (hasBar && !m_verticalScrollbar) {
105 m_verticalScrollbar = createScrollbar(VerticalScrollbar);
106 addChild(m_verticalScrollbar.get());
107 m_verticalScrollbar->styleChanged();
108 } else if (!hasBar && m_verticalScrollbar) {
109 removeChild(m_verticalScrollbar.get());
110 m_verticalScrollbar = 0;
115 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
117 return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
120 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
121 bool horizontalLock, bool verticalLock)
123 bool needsUpdate = false;
125 if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
126 m_horizontalScrollbarMode = horizontalMode;
130 if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
131 m_verticalScrollbarMode = verticalMode;
136 setHorizontalScrollbarLock();
139 setVerticalScrollbarLock();
144 if (platformWidget())
145 platformSetScrollbarModes();
147 updateScrollbars(scrollOffset());
151 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
153 if (platformWidget()) {
154 platformScrollbarModes(horizontalMode, verticalMode);
157 horizontalMode = m_horizontalScrollbarMode;
158 verticalMode = m_verticalScrollbarMode;
161 void ScrollView::setCanHaveScrollbars(bool canScroll)
163 ScrollbarMode newHorizontalMode;
164 ScrollbarMode newVerticalMode;
166 scrollbarModes(newHorizontalMode, newVerticalMode);
168 if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
169 newVerticalMode = ScrollbarAuto;
171 newVerticalMode = ScrollbarAlwaysOff;
173 if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
174 newHorizontalMode = ScrollbarAuto;
176 newHorizontalMode = ScrollbarAlwaysOff;
178 setScrollbarModes(newHorizontalMode, newVerticalMode);
181 void ScrollView::setCanBlitOnScroll(bool b)
183 if (platformWidget()) {
184 platformSetCanBlitOnScroll(b);
188 m_canBlitOnScroll = b;
191 bool ScrollView::canBlitOnScroll() const
193 if (platformWidget())
194 return platformCanBlitOnScroll();
196 return m_canBlitOnScroll;
199 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
201 m_paintsEntireContents = paintsEntireContents;
205 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
207 if (platformWidget())
208 return platformVisibleContentRect(includeScrollbars);
209 return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
210 IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)),
211 max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
215 int ScrollView::layoutWidth() const
217 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
220 int ScrollView::layoutHeight() const
222 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
225 IntSize ScrollView::fixedLayoutSize() const
227 return m_fixedLayoutSize;
230 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
232 if (fixedLayoutSize() == newSize)
234 m_fixedLayoutSize = newSize;
235 updateScrollbars(scrollOffset());
238 bool ScrollView::useFixedLayout() const
240 return m_useFixedLayout;
243 void ScrollView::setUseFixedLayout(bool enable)
245 if (useFixedLayout() == enable)
247 m_useFixedLayout = enable;
248 updateScrollbars(scrollOffset());
251 IntSize ScrollView::contentsSize() const
253 if (platformWidget())
254 return platformContentsSize();
255 return m_contentsSize;
258 void ScrollView::setContentsSize(const IntSize& newSize)
260 if (contentsSize() == newSize)
262 m_contentsSize = newSize;
263 if (platformWidget())
264 platformSetContentsSize();
266 updateScrollbars(scrollOffset());
269 #if PLATFORM(ANDROID)
270 int ScrollView::actualWidth() const
272 if (platformWidget())
273 return platformActualWidth();
277 int ScrollView::actualHeight() const
279 if (platformWidget())
280 return platformActualHeight();
284 int ScrollView::actualScrollX() const
286 if (platformWidget())
287 return platformActualScrollX();
291 int ScrollView::actualScrollY() const
293 if (platformWidget())
294 return platformActualScrollY();
299 IntPoint ScrollView::maximumScrollPosition() const
301 IntSize maximumOffset = contentsSize() - visibleContentRect().size();
302 maximumOffset.clampNegativeToZero();
303 return IntPoint(maximumOffset.width(), maximumOffset.height());
306 int ScrollView::scrollSize(ScrollbarOrientation orientation) const
308 Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
309 return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
312 void ScrollView::setScrollOffsetFromAnimation(const IntPoint& offset)
314 if (m_horizontalScrollbar)
315 m_horizontalScrollbar->setValue(offset.x(), Scrollbar::FromScrollAnimator);
316 if (m_verticalScrollbar)
317 m_verticalScrollbar->setValue(offset.y(), Scrollbar::FromScrollAnimator);
320 void ScrollView::valueChanged(Scrollbar* scrollbar)
322 // Figure out if we really moved.
323 IntSize newOffset = m_scrollOffset;
325 if (scrollbar->orientation() == HorizontalScrollbar)
326 newOffset.setWidth(scrollbar->value());
327 else if (scrollbar->orientation() == VerticalScrollbar)
328 newOffset.setHeight(scrollbar->value());
331 IntSize scrollDelta = newOffset - m_scrollOffset;
332 if (scrollDelta == IntSize())
334 m_scrollOffset = newOffset;
336 if (scrollbarsSuppressed())
339 repaintFixedElementsAfterScrolling();
340 scrollContents(scrollDelta);
343 void ScrollView::valueChanged(const IntSize& scrollDelta)
345 if (scrollbarsSuppressed())
348 repaintFixedElementsAfterScrolling();
349 scrollContents(scrollDelta);
352 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
354 if (prohibitsScrolling())
357 if (platformWidget()) {
358 platformSetScrollPosition(scrollPoint);
362 IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
363 newScrollPosition.clampNegativeToZero();
365 if (newScrollPosition == scrollPosition())
368 updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
371 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
373 if (platformWidget())
374 return platformScroll(direction, granularity);
376 if (direction == ScrollUp || direction == ScrollDown) {
377 if (m_verticalScrollbar)
378 return m_verticalScrollbar->scroll(direction, granularity);
380 if (m_horizontalScrollbar)
381 return m_horizontalScrollbar->scroll(direction, granularity);
386 void ScrollView::windowResizerRectChanged()
388 if (platformWidget())
391 updateScrollbars(scrollOffset());
394 static const unsigned cMaxUpdateScrollbarsPass = 2;
396 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
398 if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
401 // If we came in here with the view already needing a layout, then go ahead and do that
402 // first. (This will be the common case, e.g., when the page changes due to window resizing for example).
403 // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
404 if (!m_scrollbarsSuppressed) {
405 m_inUpdateScrollbars = true;
406 visibleContentsResized();
407 m_inUpdateScrollbars = false;
410 bool hasHorizontalScrollbar = m_horizontalScrollbar;
411 bool hasVerticalScrollbar = m_verticalScrollbar;
413 bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
414 bool newHasVerticalScrollbar = hasVerticalScrollbar;
416 ScrollbarMode hScroll = m_horizontalScrollbarMode;
417 ScrollbarMode vScroll = m_verticalScrollbarMode;
419 if (hScroll != ScrollbarAuto)
420 newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
421 if (vScroll != ScrollbarAuto)
422 newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
424 if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
425 if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
426 setHasHorizontalScrollbar(newHasHorizontalScrollbar);
427 if (hasVerticalScrollbar != newHasVerticalScrollbar)
428 setHasVerticalScrollbar(newHasVerticalScrollbar);
430 bool sendContentResizedNotification = false;
432 IntSize docSize = contentsSize();
433 IntSize frameSize = frameRect().size();
435 if (hScroll == ScrollbarAuto) {
436 newHasHorizontalScrollbar = docSize.width() > visibleWidth();
437 if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
438 newHasHorizontalScrollbar = false;
440 if (vScroll == ScrollbarAuto) {
441 newHasVerticalScrollbar = docSize.height() > visibleHeight();
442 if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
443 newHasVerticalScrollbar = false;
446 // If we ever turn one scrollbar off, always turn the other one off too. Never ever
447 // try to both gain/lose a scrollbar in the same pass.
448 if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
449 newHasVerticalScrollbar = false;
450 if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
451 newHasHorizontalScrollbar = false;
453 if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
454 setHasHorizontalScrollbar(newHasHorizontalScrollbar);
455 sendContentResizedNotification = true;
458 if (hasVerticalScrollbar != newHasVerticalScrollbar) {
459 setHasVerticalScrollbar(newHasVerticalScrollbar);
460 sendContentResizedNotification = true;
463 if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
464 m_updateScrollbarsPass++;
466 visibleContentsResized();
467 IntSize newDocSize = contentsSize();
468 if (newDocSize == docSize) {
469 // The layout with the new scroll state had no impact on
470 // the document's overall size, so updateScrollbars didn't get called.
472 updateScrollbars(desiredOffset);
474 m_updateScrollbarsPass--;
478 // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
479 // doing it multiple times).
480 if (m_updateScrollbarsPass)
483 m_inUpdateScrollbars = true;
484 IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
485 IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
486 scroll.clampNegativeToZero();
488 if (m_horizontalScrollbar) {
489 int clientWidth = visibleWidth();
490 m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
491 int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
492 IntRect oldRect(m_horizontalScrollbar->frameRect());
493 IntRect hBarRect = IntRect(0,
494 height() - m_horizontalScrollbar->height(),
495 width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
496 m_horizontalScrollbar->height());
497 m_horizontalScrollbar->setFrameRect(hBarRect);
498 if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
499 m_horizontalScrollbar->invalidate();
501 if (m_scrollbarsSuppressed)
502 m_horizontalScrollbar->setSuppressInvalidation(true);
503 m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
504 m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
505 m_horizontalScrollbar->setValue(scroll.width(), Scrollbar::NotFromScrollAnimator);
506 if (m_scrollbarsSuppressed)
507 m_horizontalScrollbar->setSuppressInvalidation(false);
510 if (m_verticalScrollbar) {
511 int clientHeight = visibleHeight();
512 m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
513 int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
514 IntRect oldRect(m_verticalScrollbar->frameRect());
515 IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(),
517 m_verticalScrollbar->width(),
518 height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
519 m_verticalScrollbar->setFrameRect(vBarRect);
520 if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
521 m_verticalScrollbar->invalidate();
523 if (m_scrollbarsSuppressed)
524 m_verticalScrollbar->setSuppressInvalidation(true);
525 m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
526 m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
527 m_verticalScrollbar->setValue(scroll.height(), Scrollbar::NotFromScrollAnimator);
528 if (m_scrollbarsSuppressed)
529 m_verticalScrollbar->setSuppressInvalidation(false);
532 if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
534 updateScrollCorner();
537 // See if our offset has changed in a situation where we might not have scrollbars.
538 // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
539 // It can also happen when maximizing a window that has scrollbars (but the new maximized result
541 IntSize scrollDelta = scroll - m_scrollOffset;
542 if (scrollDelta != IntSize()) {
543 m_scrollOffset = scroll;
544 valueChanged(scrollDelta);
547 m_inUpdateScrollbars = false;
550 const int panIconSizeLength = 16;
552 void ScrollView::scrollContents(const IntSize& scrollDelta)
557 // Since scrolling is double buffered, we will be blitting the scroll view's intersection
558 // with the clip rect every time to keep it smooth.
559 IntRect clipRect = windowClipRect();
560 IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
561 IntRect updateRect = clipRect;
562 updateRect.intersect(scrollViewRect);
564 // Invalidate the window (not the backing store).
565 hostWindow()->invalidateWindow(updateRect, false /*immediate*/);
567 if (m_drawPanScrollIcon) {
568 int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
569 IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
570 IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
571 panScrollIconDirtyRect.intersect(clipRect);
572 hostWindow()->invalidateContentsAndWindow(panScrollIconDirtyRect, false /*immediate*/);
575 if (canBlitOnScroll()) { // The main frame can just blit the WebView window
576 // FIXME: Find a way to scroll subframes with this faster path
577 if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
578 hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
580 // We need to go ahead and repaint the entire backing store. Do it now before moving the
582 hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
585 // This call will move children with native widgets (plugins) and invalidate them as well.
588 // Now blit the backingstore into the window which should be very fast.
589 hostWindow()->invalidateWindow(IntRect(), true);
592 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
594 hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
598 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
600 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
601 return viewPoint + scrollOffset();
604 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
606 IntPoint viewPoint = contentsPoint - scrollOffset();
607 return convertToContainingWindow(viewPoint);
610 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
612 IntRect viewRect = convertFromContainingWindow(windowRect);
613 viewRect.move(scrollOffset());
617 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
619 IntRect viewRect = contentsRect;
620 viewRect.move(-scrollOffset());
621 return convertToContainingWindow(viewRect);
624 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
626 if (platformWidget())
627 return platformContentsToScreen(rect);
630 return hostWindow()->windowToScreen(contentsToWindow(rect));
633 IntPoint ScrollView::screenToContents(const IntPoint& point) const
635 if (platformWidget())
636 return platformScreenToContents(point);
639 return windowToContents(hostWindow()->screenToWindow(point));
642 bool ScrollView::containsScrollbarsAvoidingResizer() const
644 return !m_scrollbarsAvoidingResizer;
647 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
649 int oldCount = m_scrollbarsAvoidingResizer;
650 m_scrollbarsAvoidingResizer += overlapDelta;
652 parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
653 else if (!scrollbarsSuppressed()) {
654 // If we went from n to 0 or from 0 to n and we're the outermost view,
655 // we need to invalidate the windowResizerRect(), since it will now need to paint
657 if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
658 (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
659 invalidateRect(windowResizerRect());
663 void ScrollView::setParent(ScrollView* parentView)
665 if (parentView == parent())
668 if (m_scrollbarsAvoidingResizer && parent())
669 parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
671 Widget::setParent(parentView);
673 if (m_scrollbarsAvoidingResizer && parent())
674 parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
677 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
679 if (suppressed == m_scrollbarsSuppressed)
682 m_scrollbarsSuppressed = suppressed;
684 if (platformWidget())
685 platformSetScrollbarsSuppressed(repaintOnUnsuppress);
686 else if (repaintOnUnsuppress && !suppressed) {
687 if (m_horizontalScrollbar)
688 m_horizontalScrollbar->invalidate();
689 if (m_verticalScrollbar)
690 m_verticalScrollbar->invalidate();
692 // Invalidate the scroll corner too on unsuppress.
693 invalidateRect(scrollCornerRect());
697 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
699 if (platformWidget())
702 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
703 if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
704 return m_horizontalScrollbar.get();
705 if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
706 return m_verticalScrollbar.get();
710 void ScrollView::wheelEvent(PlatformWheelEvent& e)
712 // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
714 if (!canHaveScrollbars()) {
716 if (!canHaveScrollbars() || platformWidget()) {
721 // Accept the event if we have a scrollbar in that direction and can still
722 // scroll any further.
723 float deltaX = m_horizontalScrollbar ? e.deltaX() : 0;
724 float deltaY = m_verticalScrollbar ? e.deltaY() : 0;
725 IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
726 if ((deltaX < 0 && maxScrollDelta.width() > 0)
727 || (deltaX > 0 && scrollOffset().width() > 0)
728 || (deltaY < 0 && maxScrollDelta.height() > 0)
729 || (deltaY > 0 && scrollOffset().height() > 0)) {
731 if (e.granularity() == ScrollByPageWheelEvent) {
733 bool negative = deltaY < 0;
734 deltaY = max(max(static_cast<float>(visibleHeight()) * Scrollbar::minFractionToStepWhenPaging(), static_cast<float>(visibleHeight() - Scrollbar::maxOverlapBetweenPages())), 1.0f);
740 m_verticalScrollbar->scroll(ScrollUp, ScrollByPixel, deltaY);
742 m_horizontalScrollbar->scroll(ScrollLeft, ScrollByPixel, deltaX);
746 void ScrollView::setFrameRect(const IntRect& newRect)
748 IntRect oldRect = frameRect();
750 if (newRect == oldRect)
753 Widget::setFrameRect(newRect);
755 if (platformWidget())
758 if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
759 updateScrollbars(m_scrollOffset);
760 if (!m_useFixedLayout)
767 void ScrollView::frameRectsChanged()
769 if (platformWidget())
772 HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
773 for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
774 (*current)->frameRectsChanged();
777 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
779 IntRect paintRect = rect;
780 if (!paintsEntireContents())
781 paintRect.intersect(visibleContentRect());
782 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
783 if (rect != paintRect)
784 platformOffscreenContentRectangle(visibleContentRect(), rect);
786 if (paintRect.isEmpty())
788 if (platformWidget()) {
789 platformRepaintContentRectangle(paintRect, now);
794 hostWindow()->invalidateContentsAndWindow(contentsToWindow(paintRect), now /*immediate*/);
797 IntRect ScrollView::scrollCornerRect() const
801 if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
802 cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
803 height() - m_horizontalScrollbar->height(),
804 width() - m_horizontalScrollbar->width(),
805 m_horizontalScrollbar->height()));
808 if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
809 cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
810 m_verticalScrollbar->height(),
811 m_verticalScrollbar->width(),
812 height() - m_verticalScrollbar->height()));
818 void ScrollView::updateScrollCorner()
822 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
824 ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
827 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
829 if (m_horizontalScrollbar)
830 m_horizontalScrollbar->paint(context, rect);
831 if (m_verticalScrollbar)
832 m_verticalScrollbar->paint(context, rect);
834 paintScrollCorner(context, scrollCornerRect());
837 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
839 static Image* panScrollIcon = Image::loadPlatformResource("panIcon").releaseRef();
840 context->drawImage(panScrollIcon, ColorSpaceDeviceRGB, m_panScrollIconPoint);
843 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
845 if (platformWidget()) {
846 Widget::paint(context, rect);
850 if (context->paintingDisabled() && !context->updatingControlTints())
853 IntRect documentDirtyRect = rect;
854 documentDirtyRect.intersect(frameRect());
858 context->translate(x(), y());
859 documentDirtyRect.move(-x(), -y());
861 context->translate(-scrollX(), -scrollY());
862 documentDirtyRect.move(scrollX(), scrollY());
864 context->clip(visibleContentRect());
866 paintContents(context, documentDirtyRect);
870 // Now paint the scrollbars.
871 if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
873 IntRect scrollViewDirtyRect = rect;
874 scrollViewDirtyRect.intersect(frameRect());
875 context->translate(x(), y());
876 scrollViewDirtyRect.move(-x(), -y());
878 paintScrollbars(context, scrollViewDirtyRect);
883 // Paint the panScroll Icon
884 if (m_drawPanScrollIcon)
885 paintPanScrollIcon(context);
888 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
890 if (!scrollbarCornerPresent())
893 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
895 if (m_horizontalScrollbar) {
896 int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
897 int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
898 int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
900 return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
903 int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
904 int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
905 int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
907 return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
910 bool ScrollView::scrollbarCornerPresent() const
912 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
913 (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
916 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
918 // Scrollbars won't be transformed within us
919 IntRect newRect = localRect;
920 newRect.move(scrollbar->x(), scrollbar->y());
924 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
926 IntRect newRect = parentRect;
927 // Scrollbars won't be transformed within us
928 newRect.move(-scrollbar->x(), -scrollbar->y());
932 // FIXME: test these on windows
933 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
935 // Scrollbars won't be transformed within us
936 IntPoint newPoint = localPoint;
937 newPoint.move(scrollbar->x(), scrollbar->y());
941 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
943 IntPoint newPoint = parentPoint;
944 // Scrollbars won't be transformed within us
945 newPoint.move(-scrollbar->x(), -scrollbar->y());
949 void ScrollView::setParentVisible(bool visible)
951 if (isParentVisible() == visible)
954 Widget::setParentVisible(visible);
956 if (!isSelfVisible())
959 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
960 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
961 (*it)->setParentVisible(visible);
964 void ScrollView::show()
966 if (!isSelfVisible()) {
967 setSelfVisible(true);
968 if (isParentVisible()) {
969 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
970 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
971 (*it)->setParentVisible(true);
978 void ScrollView::hide()
980 if (isSelfVisible()) {
981 if (isParentVisible()) {
982 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
983 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
984 (*it)->setParentVisible(false);
986 setSelfVisible(false);
992 bool ScrollView::isOffscreen() const
994 if (platformWidget())
995 return platformIsOffscreen();
1000 // FIXME: Add a HostWindow::isOffscreen method here. Since only Mac implements this method
1001 // currently, we can add the method when the other platforms decide to implement this concept.
1006 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1010 m_drawPanScrollIcon = true;
1011 m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1012 hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
1015 void ScrollView::removePanScrollIcon()
1019 m_drawPanScrollIcon = false;
1020 hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
1023 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(EFL)
1025 void ScrollView::platformInit()
1029 void ScrollView::platformDestroy()
1035 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
1037 void ScrollView::platformAddChild(Widget*)
1041 void ScrollView::platformRemoveChild(Widget*)
1049 void ScrollView::platformSetScrollbarsSuppressed(bool)
1055 #if !PLATFORM(MAC) && !PLATFORM(WX)
1057 #if !PLATFORM(ANDROID)
1058 void ScrollView::platformSetScrollbarModes()
1062 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
1064 horizontal = ScrollbarAuto;
1065 vertical = ScrollbarAuto;
1069 void ScrollView::platformSetCanBlitOnScroll(bool)
1073 bool ScrollView::platformCanBlitOnScroll() const
1078 #if !PLATFORM(ANDROID)
1079 IntRect ScrollView::platformVisibleContentRect(bool) const
1085 #if !PLATFORM(ANDROID)
1086 IntSize ScrollView::platformContentsSize() const
1092 void ScrollView::platformSetContentsSize()
1096 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1101 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1106 #if !PLATFORM(ANDROID)
1107 void ScrollView::platformSetScrollPosition(const IntPoint&)
1112 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1117 #if !PLATFORM(ANDROID)
1118 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
1122 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
1123 void ScrollView::platformOffscreenContentRectangle(const IntRect& )
1128 bool ScrollView::platformIsOffscreen() const