ScrollableArea.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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. #include "config.h"
  32. #include "ScrollableArea.h"
  33. #include "GraphicsContext.h"
  34. #include "GraphicsLayer.h"
  35. #include "FloatPoint.h"
  36. #include "PlatformWheelEvent.h"
  37. #include "ScrollAnimator.h"
  38. #include "ScrollbarTheme.h"
  39. #include <wtf/PassOwnPtr.h>
  40. namespace WebCore {
  41. struct SameSizeAsScrollableArea {
  42. virtual ~SameSizeAsScrollableArea();
  43. void* pointer;
  44. unsigned bitfields : 16;
  45. IntPoint origin;
  46. };
  47. COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
  48. ScrollableArea::ScrollableArea()
  49. : m_constrainsScrollingToContentEdge(true)
  50. , m_inLiveResize(false)
  51. , m_verticalScrollElasticity(ScrollElasticityNone)
  52. , m_horizontalScrollElasticity(ScrollElasticityNone)
  53. , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
  54. , m_scrollOriginChanged(false)
  55. {
  56. }
  57. ScrollableArea::~ScrollableArea()
  58. {
  59. }
  60. ScrollAnimator* ScrollableArea::scrollAnimator() const
  61. {
  62. if (!m_scrollAnimator)
  63. m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
  64. return m_scrollAnimator.get();
  65. }
  66. void ScrollableArea::setScrollOrigin(const IntPoint& origin)
  67. {
  68. if (m_scrollOrigin != origin) {
  69. m_scrollOrigin = origin;
  70. m_scrollOriginChanged = true;
  71. }
  72. }
  73. bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
  74. {
  75. ScrollbarOrientation orientation;
  76. Scrollbar* scrollbar;
  77. if (direction == ScrollUp || direction == ScrollDown) {
  78. orientation = VerticalScrollbar;
  79. scrollbar = verticalScrollbar();
  80. } else {
  81. orientation = HorizontalScrollbar;
  82. scrollbar = horizontalScrollbar();
  83. }
  84. if (!scrollbar)
  85. return false;
  86. float step = 0;
  87. switch (granularity) {
  88. case ScrollByLine:
  89. step = scrollbar->lineStep();
  90. break;
  91. case ScrollByPage:
  92. step = scrollbar->pageStep();
  93. break;
  94. case ScrollByDocument:
  95. step = scrollbar->totalSize();
  96. break;
  97. case ScrollByPixel:
  98. case ScrollByPrecisePixel:
  99. step = scrollbar->pixelStep();
  100. break;
  101. }
  102. if (direction == ScrollUp || direction == ScrollLeft)
  103. multiplier = -multiplier;
  104. return scrollAnimator()->scroll(orientation, granularity, step, multiplier);
  105. }
  106. void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
  107. {
  108. scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
  109. }
  110. void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
  111. {
  112. if (orientation == HorizontalScrollbar)
  113. scrollToOffsetWithoutAnimation(FloatPoint(offset, scrollAnimator()->currentPosition().y()));
  114. else
  115. scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), offset));
  116. }
  117. void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
  118. {
  119. scrollPositionChanged(position);
  120. scrollAnimator()->setCurrentPosition(position);
  121. }
  122. void ScrollableArea::scrollPositionChanged(const IntPoint& position)
  123. {
  124. IntPoint oldPosition = scrollPosition();
  125. // Tell the derived class to scroll its contents.
  126. setScrollOffset(position);
  127. Scrollbar* verticalScrollbar = this->verticalScrollbar();
  128. // Tell the scrollbars to update their thumb postions.
  129. if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
  130. horizontalScrollbar->offsetDidChange();
  131. if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
  132. if (!verticalScrollbar)
  133. horizontalScrollbar->invalidate();
  134. else {
  135. // If there is both a horizontalScrollbar and a verticalScrollbar,
  136. // then we must also invalidate the corner between them.
  137. IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
  138. boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
  139. horizontalScrollbar->invalidateRect(boundsAndCorner);
  140. }
  141. }
  142. }
  143. if (verticalScrollbar) {
  144. verticalScrollbar->offsetDidChange();
  145. if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
  146. verticalScrollbar->invalidate();
  147. }
  148. if (scrollPosition() != oldPosition)
  149. scrollAnimator()->notifyContentAreaScrolled(scrollPosition() - oldPosition);
  150. }
  151. bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
  152. {
  153. return scrollAnimator()->handleWheelEvent(wheelEvent);
  154. }
  155. // NOTE: Only called from Internals for testing.
  156. void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
  157. {
  158. setScrollOffsetFromAnimation(offset);
  159. }
  160. void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
  161. {
  162. if (requestScrollPositionUpdate(offset))
  163. return;
  164. scrollPositionChanged(offset);
  165. }
  166. void ScrollableArea::willStartLiveResize()
  167. {
  168. if (m_inLiveResize)
  169. return;
  170. m_inLiveResize = true;
  171. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  172. scrollAnimator->willStartLiveResize();
  173. }
  174. void ScrollableArea::willEndLiveResize()
  175. {
  176. if (!m_inLiveResize)
  177. return;
  178. m_inLiveResize = false;
  179. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  180. scrollAnimator->willEndLiveResize();
  181. }
  182. void ScrollableArea::contentAreaWillPaint() const
  183. {
  184. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  185. scrollAnimator->contentAreaWillPaint();
  186. }
  187. void ScrollableArea::mouseEnteredContentArea() const
  188. {
  189. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  190. scrollAnimator->mouseEnteredContentArea();
  191. }
  192. void ScrollableArea::mouseExitedContentArea() const
  193. {
  194. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  195. scrollAnimator->mouseEnteredContentArea();
  196. }
  197. void ScrollableArea::mouseMovedInContentArea() const
  198. {
  199. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  200. scrollAnimator->mouseMovedInContentArea();
  201. }
  202. void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
  203. {
  204. scrollAnimator()->mouseEnteredScrollbar(scrollbar);
  205. }
  206. void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
  207. {
  208. scrollAnimator()->mouseExitedScrollbar(scrollbar);
  209. }
  210. void ScrollableArea::contentAreaDidShow() const
  211. {
  212. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  213. scrollAnimator->contentAreaDidShow();
  214. }
  215. void ScrollableArea::contentAreaDidHide() const
  216. {
  217. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  218. scrollAnimator->contentAreaDidHide();
  219. }
  220. void ScrollableArea::finishCurrentScrollAnimations() const
  221. {
  222. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  223. scrollAnimator->finishCurrentScrollAnimations();
  224. }
  225. void ScrollableArea::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
  226. {
  227. if (orientation == VerticalScrollbar)
  228. scrollAnimator()->didAddVerticalScrollbar(scrollbar);
  229. else
  230. scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
  231. // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
  232. setScrollbarOverlayStyle(scrollbarOverlayStyle());
  233. }
  234. void ScrollableArea::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
  235. {
  236. if (orientation == VerticalScrollbar)
  237. scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
  238. else
  239. scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
  240. }
  241. void ScrollableArea::contentsResized()
  242. {
  243. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  244. scrollAnimator->contentsResized();
  245. }
  246. bool ScrollableArea::hasOverlayScrollbars() const
  247. {
  248. return (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
  249. || (horizontalScrollbar() && horizontalScrollbar()->isOverlayScrollbar());
  250. }
  251. void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
  252. {
  253. m_scrollbarOverlayStyle = overlayStyle;
  254. if (horizontalScrollbar()) {
  255. ScrollbarTheme::theme()->updateScrollbarOverlayStyle(horizontalScrollbar());
  256. horizontalScrollbar()->invalidate();
  257. }
  258. if (verticalScrollbar()) {
  259. ScrollbarTheme::theme()->updateScrollbarOverlayStyle(verticalScrollbar());
  260. verticalScrollbar()->invalidate();
  261. }
  262. }
  263. void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
  264. {
  265. #if USE(ACCELERATED_COMPOSITING)
  266. if (scrollbar == horizontalScrollbar()) {
  267. if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
  268. graphicsLayer->setNeedsDisplay();
  269. graphicsLayer->setContentsNeedsDisplay();
  270. return;
  271. }
  272. } else if (scrollbar == verticalScrollbar()) {
  273. if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
  274. graphicsLayer->setNeedsDisplay();
  275. graphicsLayer->setContentsNeedsDisplay();
  276. return;
  277. }
  278. }
  279. #endif
  280. invalidateScrollbarRect(scrollbar, rect);
  281. }
  282. void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
  283. {
  284. #if USE(ACCELERATED_COMPOSITING)
  285. if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
  286. graphicsLayer->setNeedsDisplay();
  287. return;
  288. }
  289. #endif
  290. invalidateScrollCornerRect(rect);
  291. }
  292. bool ScrollableArea::hasLayerForHorizontalScrollbar() const
  293. {
  294. #if USE(ACCELERATED_COMPOSITING)
  295. return layerForHorizontalScrollbar();
  296. #else
  297. return false;
  298. #endif
  299. }
  300. bool ScrollableArea::hasLayerForVerticalScrollbar() const
  301. {
  302. #if USE(ACCELERATED_COMPOSITING)
  303. return layerForVerticalScrollbar();
  304. #else
  305. return false;
  306. #endif
  307. }
  308. bool ScrollableArea::hasLayerForScrollCorner() const
  309. {
  310. #if USE(ACCELERATED_COMPOSITING)
  311. return layerForScrollCorner();
  312. #else
  313. return false;
  314. #endif
  315. }
  316. void ScrollableArea::serviceScrollAnimations()
  317. {
  318. if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
  319. scrollAnimator->serviceScrollAnimations();
  320. }
  321. IntPoint ScrollableArea::scrollPosition() const
  322. {
  323. int x = horizontalScrollbar() ? horizontalScrollbar()->value() : 0;
  324. int y = verticalScrollbar() ? verticalScrollbar()->value() : 0;
  325. return IntPoint(x, y);
  326. }
  327. IntPoint ScrollableArea::minimumScrollPosition() const
  328. {
  329. return IntPoint();
  330. }
  331. IntPoint ScrollableArea::maximumScrollPosition() const
  332. {
  333. return IntPoint(totalContentsSize().width() - visibleWidth(), totalContentsSize().height() - visibleHeight());
  334. }
  335. IntSize ScrollableArea::totalContentsSize() const
  336. {
  337. IntSize totalContentsSize = contentsSize();
  338. totalContentsSize.setHeight(totalContentsSize.height() + headerHeight() + footerHeight());
  339. return totalContentsSize;
  340. }
  341. IntRect ScrollableArea::visibleContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
  342. {
  343. int verticalScrollbarWidth = 0;
  344. int horizontalScrollbarHeight = 0;
  345. if (scrollbarInclusion == IncludeScrollbars) {
  346. if (Scrollbar* verticalBar = verticalScrollbar())
  347. verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
  348. if (Scrollbar* horizontalBar = horizontalScrollbar())
  349. horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
  350. }
  351. return IntRect(scrollPosition().x(),
  352. scrollPosition().y(),
  353. std::max(0, visibleWidth() + verticalScrollbarWidth),
  354. std::max(0, visibleHeight() + horizontalScrollbarHeight));
  355. }
  356. IntPoint ScrollableArea::constrainScrollPositionForOverhang(const IntRect& visibleContentRect, const IntSize& totalContentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, int headerHeight, int footerHeight)
  357. {
  358. // The viewport rect that we're scrolling shouldn't be larger than our document.
  359. IntSize idealScrollRectSize(std::min(visibleContentRect.width(), totalContentsSize.width()), std::min(visibleContentRect.height(), totalContentsSize.height()));
  360. IntRect scrollRect(scrollPosition + scrollOrigin - IntSize(0, headerHeight), idealScrollRectSize);
  361. IntRect documentRect(IntPoint(), IntSize(totalContentsSize.width(), totalContentsSize.height() - headerHeight - footerHeight));
  362. // Use intersection to constrain our ideal scroll rect by the document rect.
  363. scrollRect.intersect(documentRect);
  364. if (scrollRect.size() != idealScrollRectSize) {
  365. // If the rect was clipped, restore its size, effectively pushing it "down" from the top left.
  366. scrollRect.setSize(idealScrollRectSize);
  367. // If we still clip, push our rect "up" from the bottom right.
  368. scrollRect.intersect(documentRect);
  369. if (scrollRect.width() < idealScrollRectSize.width())
  370. scrollRect.move(-(idealScrollRectSize.width() - scrollRect.width()), 0);
  371. if (scrollRect.height() < idealScrollRectSize.height())
  372. scrollRect.move(0, -(idealScrollRectSize.height() - scrollRect.height()));
  373. }
  374. return scrollRect.location() - toIntSize(scrollOrigin);
  375. }
  376. IntPoint ScrollableArea::constrainScrollPositionForOverhang(const IntPoint& scrollPosition)
  377. {
  378. return constrainScrollPositionForOverhang(visibleContentRect(), totalContentsSize(), scrollPosition, scrollOrigin(), headerHeight(), footerHeight());
  379. }
  380. } // namespace WebCore