📄 scrollview.cpp
字号:
/* * Copyright (C) 2006, 2007, 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 COMPUTER, 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 COMPUTER, 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 "ScrollView.h"#include "GraphicsContext.h"#include "HostWindow.h"#include "PlatformMouseEvent.h"#include "PlatformWheelEvent.h"#include "Scrollbar.h"#include "ScrollbarTheme.h"#include <wtf/StdLibExtras.h>using std::max;namespace WebCore {ScrollView::ScrollView() : m_horizontalScrollbarMode(ScrollbarAuto) , m_verticalScrollbarMode(ScrollbarAuto) , m_prohibitsScrolling(false) , m_canBlitOnScroll(true) , m_scrollbarsAvoidingResizer(0) , m_scrollbarsSuppressed(false) , m_inUpdateScrollbars(false) , m_drawPanScrollIcon(false) , m_useFixedLayout(false){ platformInit();}ScrollView::~ScrollView(){ platformDestroy();}void ScrollView::addChild(Widget* child) { ASSERT(child != this && !child->parent()); child->setParent(this); m_children.add(child); if (child->platformWidget()) platformAddChild(child);}void ScrollView::removeChild(Widget* child){ ASSERT(child->parent() == this); child->setParent(0); m_children.remove(child); if (child->platformWidget()) platformRemoveChild(child);}void ScrollView::setHasHorizontalScrollbar(bool hasBar){ if (hasBar && !m_horizontalScrollbar && !platformHasHorizontalAdjustment()) { m_horizontalScrollbar = createScrollbar(HorizontalScrollbar); addChild(m_horizontalScrollbar.get()); } else if (!hasBar && m_horizontalScrollbar) { removeChild(m_horizontalScrollbar.get()); m_horizontalScrollbar = 0; }}void ScrollView::setHasVerticalScrollbar(bool hasBar){ if (hasBar && !m_verticalScrollbar && !platformHasVerticalAdjustment()) { m_verticalScrollbar = createScrollbar(VerticalScrollbar); addChild(m_verticalScrollbar.get()); } else if (!hasBar && m_verticalScrollbar) { removeChild(m_verticalScrollbar.get()); m_verticalScrollbar = 0; }}PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation){ return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);}void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode){ if (horizontalMode == horizontalScrollbarMode() && verticalMode == verticalScrollbarMode()) return; m_horizontalScrollbarMode = horizontalMode; m_verticalScrollbarMode = verticalMode; if (platformWidget()) platformSetScrollbarModes(); else updateScrollbars(scrollOffset());}void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const{ if (platformWidget()) { platformScrollbarModes(horizontalMode, verticalMode); return; } horizontalMode = m_horizontalScrollbarMode; verticalMode = m_verticalScrollbarMode;}void ScrollView::setCanHaveScrollbars(bool canScroll){ ScrollbarMode newHorizontalMode; ScrollbarMode newVerticalMode; scrollbarModes(newHorizontalMode, newVerticalMode); if (canScroll && newVerticalMode == ScrollbarAlwaysOff) newVerticalMode = ScrollbarAuto; else if (!canScroll) newVerticalMode = ScrollbarAlwaysOff; if (canScroll && newHorizontalMode == ScrollbarAlwaysOff) newHorizontalMode = ScrollbarAuto; else if (!canScroll) newHorizontalMode = ScrollbarAlwaysOff; setScrollbarModes(newHorizontalMode, newVerticalMode);}void ScrollView::setCanBlitOnScroll(bool b){ if (platformWidget()) { platformSetCanBlitOnScroll(b); return; } m_canBlitOnScroll = b;}bool ScrollView::canBlitOnScroll() const{ if (platformWidget()) return platformCanBlitOnScroll(); return m_canBlitOnScroll;}IntRect ScrollView::visibleContentRect(bool includeScrollbars) const{ if (platformWidget()) return platformVisibleContentRect(includeScrollbars); return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()), IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)), max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));}int ScrollView::layoutWidth() const{ return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();}int ScrollView::layoutHeight() const{ return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();}IntSize ScrollView::fixedLayoutSize() const{ return m_fixedLayoutSize;}void ScrollView::setFixedLayoutSize(const IntSize& newSize){ if (fixedLayoutSize() == newSize) return; m_fixedLayoutSize = newSize; updateScrollbars(scrollOffset());}bool ScrollView::useFixedLayout() const{ return m_useFixedLayout;}void ScrollView::setUseFixedLayout(bool enable){ if (useFixedLayout() == enable) return; m_useFixedLayout = enable; updateScrollbars(scrollOffset());}IntSize ScrollView::contentsSize() const{ if (platformWidget()) return platformContentsSize(); return m_contentsSize;}void ScrollView::setContentsSize(const IntSize& newSize){ if (contentsSize() == newSize) return; m_contentsSize = newSize; if (platformWidget()) platformSetContentsSize(); else updateScrollbars(scrollOffset());}IntPoint ScrollView::maximumScrollPosition() const{ IntSize maximumOffset = contentsSize() - visibleContentRect().size(); maximumOffset.clampNegativeToZero(); return IntPoint(maximumOffset.width(), maximumOffset.height());}void ScrollView::valueChanged(Scrollbar* scrollbar){ // Figure out if we really moved. IntSize newOffset = m_scrollOffset; if (scrollbar) { if (scrollbar == m_horizontalScrollbar) newOffset.setWidth(scrollbar->value()); else if (scrollbar == m_verticalScrollbar) newOffset.setHeight(scrollbar->value()); } IntSize scrollDelta = newOffset - m_scrollOffset; if (scrollDelta == IntSize()) return; m_scrollOffset = newOffset; if (scrollbarsSuppressed()) return; scrollContents(scrollDelta);}void ScrollView::scrollRectIntoViewRecursively(const IntRect& r){ // FIXME: This method is not transform-aware. It should just be moved to FrameView so that an accurate // position for the child view can be determined. IntRect rect = r; ScrollView* view = this; while (view) { if (view->prohibitsScrolling()) // Allow the views to scroll into view recursively until we hit one that prohibits scrolling. return; view->setScrollPosition(rect.location()); rect.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height()); if (view->parent()) rect.intersect(view->frameRect()); view = view->parent(); } // We may be embedded inside some containing platform scroll view that we don't manage. This is the case // in Mail.app on OS X, for example, where the WebKit view for message bodies is inside a Cocoa NSScrollView // that contains both it and message headers. Let the HostWindow know about this scroll so that it can pass the message // on up the view chain. // This rect is not clamped, since Mail actually relies on receiving an unclamped rect with negative coordinates in order to // expose the headers. hostWindow()->scrollRectIntoView(rect, this);}void ScrollView::setScrollPosition(const IntPoint& scrollPoint){ if (prohibitsScrolling()) return; if (platformWidget()) { platformSetScrollPosition(scrollPoint); return; } IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition()); newScrollPosition.clampNegativeToZero(); if (newScrollPosition == scrollPosition()) return; updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));}bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity){ if (platformWidget()) return platformScroll(direction, granularity); if (direction == ScrollUp || direction == ScrollDown) { if (m_verticalScrollbar) return m_verticalScrollbar->scroll(direction, granularity); } else { if (m_horizontalScrollbar) return m_horizontalScrollbar->scroll(direction, granularity); } return false;}void ScrollView::updateScrollbars(const IntSize& desiredOffset){ // Don't allow re-entrancy into this function. if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget()) return; m_inUpdateScrollbars = true; bool hasVerticalScrollbar = m_verticalScrollbar; bool hasHorizontalScrollbar = m_horizontalScrollbar; bool oldHasVertical = hasVerticalScrollbar; bool oldHasHorizontal = hasHorizontalScrollbar; ScrollbarMode hScroll = m_horizontalScrollbarMode; ScrollbarMode vScroll = m_verticalScrollbarMode; const int scrollbarThickness = ScrollbarTheme::nativeTheme()->scrollbarThickness(); for (int pass = 0; pass < 2; pass++) { bool scrollsVertically; bool scrollsHorizontally; if (!m_scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScroll == ScrollbarAuto)) { // Do a layout if pending before checking if scrollbars are needed. if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar != oldHasHorizontal) visibleContentsResized(); scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() > height()); if (scrollsVertically) scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() + scrollbarThickness > width()); else { scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll == ScrollbarAuto && contentsWidth() > width()); if (scrollsHorizontally) scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == ScrollbarAuto && contentsHeight() + scrollbarThickness > height()); } } else { scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScrollbar : (hScroll == ScrollbarAlwaysOn); scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollbar : (vScroll == ScrollbarAlwaysOn); } if (hasVerticalScrollbar != scrollsVertically) { setHasVerticalScrollbar(scrollsVertically); hasVerticalScrollbar = scrollsVertically; } if (hasHorizontalScrollbar != scrollsHorizontally) { setHasHorizontalScrollbar(scrollsHorizontally); hasHorizontalScrollbar = scrollsHorizontally; } } // Set up the range (and page step/line step). IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight()); IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition); scroll.clampNegativeToZero(); if (!platformHandleHorizontalAdjustment(scroll) && m_horizontalScrollbar) { int clientWidth = visibleWidth(); m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth); int pageStep = (clientWidth - cAmountToKeepWhenPaging); if (pageStep < 0) pageStep = clientWidth; IntRect oldRect(m_horizontalScrollbar->frameRect()); IntRect hBarRect = IntRect(0, height() - m_horizontalScrollbar->height(), width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0), m_horizontalScrollbar->height()); m_horizontalScrollbar->setFrameRect(hBarRect); if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect()) m_horizontalScrollbar->invalidate(); if (m_scrollbarsSuppressed) m_horizontalScrollbar->setSuppressInvalidation(true); m_horizontalScrollbar->setSteps(cScrollbarPixelsPerLineStep, pageStep); m_horizontalScrollbar->setProportion(clientWidth, contentsWidth()); m_horizontalScrollbar->setValue(scroll.width()); if (m_scrollbarsSuppressed) m_horizontalScrollbar->setSuppressInvalidation(false); } if (!platformHandleVerticalAdjustment(scroll) && m_verticalScrollbar) { int clientHeight = visibleHeight(); m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight); int pageStep = (clientHeight - cAmountToKeepWhenPaging); if (pageStep < 0) pageStep = clientHeight; IntRect oldRect(m_verticalScrollbar->frameRect()); IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(), 0, m_verticalScrollbar->width(), height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0)); m_verticalScrollbar->setFrameRect(vBarRect); if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect()) m_verticalScrollbar->invalidate(); if (m_scrollbarsSuppressed) m_verticalScrollbar->setSuppressInvalidation(true); m_verticalScrollbar->setSteps(cScrollbarPixelsPerLineStep, pageStep); m_verticalScrollbar->setProportion(clientHeight, contentsHeight()); m_verticalScrollbar->setValue(scroll.height()); if (m_scrollbarsSuppressed) m_verticalScrollbar->setSuppressInvalidation(false); } if (oldHasVertical != (m_verticalScrollbar != 0) || oldHasHorizontal != (m_horizontalScrollbar != 0)) frameRectsChanged(); // See if our offset has changed in a situation where we might not have scrollbars. // This can happen when editing a body with overflow:hidden and scrolling to reveal selection. // It can also happen when maximizing a window that has scrollbars (but the new maximized result // does not). IntSize scrollDelta = scroll - m_scrollOffset; if (scrollDelta != IntSize()) { m_scrollOffset = scroll; scrollContents(scrollDelta); } m_inUpdateScrollbars = false;}const int panIconSizeLength = 20;void ScrollView::scrollContents(const IntSize& scrollDelta){ if (!hostWindow()) return; // Since scrolling is double buffered, we will be blitting the scroll view's intersection // with the clip rect every time to keep it smooth. IntRect clipRect = windowClipRect(); IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight())); IntRect updateRect = clipRect; updateRect.intersect(scrollViewRect); // Invalidate the window (not the backing store). hostWindow()->repaint(updateRect, false); if (m_drawPanScrollIcon) { int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2)); IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength)); panScrollIconDirtyRect.intersect(clipRect); hostWindow()->repaint(panScrollIconDirtyRect, true); } if (canBlitOnScroll() && !rootPreventsBlitting()) { // The main frame can just blit the WebView window // FIXME: Find a way to blit subframes without blitting overlapping content hostWindow()->scroll(-scrollDelta, scrollViewRect, clipRect); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -