The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. Viewport::Viewport (const String& name)
  21. : Component (name),
  22. scrollBarThickness (0),
  23. singleStepX (16),
  24. singleStepY (16),
  25. showHScrollbar (true),
  26. showVScrollbar (true),
  27. deleteContent (true),
  28. verticalScrollBar (true),
  29. horizontalScrollBar (false)
  30. {
  31. // content holder is used to clip the contents so they don't overlap the scrollbars
  32. addAndMakeVisible (&contentHolder);
  33. contentHolder.setInterceptsMouseClicks (false, true);
  34. addChildComponent (&verticalScrollBar);
  35. addChildComponent (&horizontalScrollBar);
  36. verticalScrollBar.addListener (this);
  37. horizontalScrollBar.addListener (this);
  38. setInterceptsMouseClicks (false, true);
  39. setWantsKeyboardFocus (true);
  40. }
  41. Viewport::~Viewport()
  42. {
  43. deleteContentComp();
  44. }
  45. //==============================================================================
  46. void Viewport::visibleAreaChanged (const Rectangle<int>&)
  47. {
  48. }
  49. //==============================================================================
  50. void Viewport::deleteContentComp()
  51. {
  52. if (deleteContent)
  53. {
  54. // This sets the content comp to a null pointer before deleting the old one, in case
  55. // anything tries to use the old one while it's in mid-deletion..
  56. ScopedPointer<Component> oldCompDeleter (contentComp);
  57. contentComp = nullptr;
  58. }
  59. else
  60. {
  61. contentComp = nullptr;
  62. }
  63. }
  64. void Viewport::setViewedComponent (Component* const newViewedComponent, const bool deleteComponentWhenNoLongerNeeded)
  65. {
  66. if (contentComp.get() != newViewedComponent)
  67. {
  68. deleteContentComp();
  69. contentComp = newViewedComponent;
  70. deleteContent = deleteComponentWhenNoLongerNeeded;
  71. if (contentComp != nullptr)
  72. {
  73. contentHolder.addAndMakeVisible (contentComp);
  74. setViewPosition (Point<int>());
  75. contentComp->addComponentListener (this);
  76. }
  77. updateVisibleArea();
  78. }
  79. }
  80. int Viewport::getMaximumVisibleWidth() const { return contentHolder.getWidth(); }
  81. int Viewport::getMaximumVisibleHeight() const { return contentHolder.getHeight(); }
  82. Point<int> Viewport::viewportPosToCompPos (const Point<int>& pos) const
  83. {
  84. jassert (contentComp != nullptr);
  85. return Point<int> (jmax (jmin (0, contentHolder.getWidth() - contentComp->getWidth()), jmin (0, -pos.getX())),
  86. jmax (jmin (0, contentHolder.getHeight() - contentComp->getHeight()), jmin (0, -pos.getY())));
  87. }
  88. void Viewport::setViewPosition (const int xPixelsOffset, const int yPixelsOffset)
  89. {
  90. setViewPosition (Point<int> (xPixelsOffset, yPixelsOffset));
  91. }
  92. void Viewport::setViewPosition (const Point<int>& newPosition)
  93. {
  94. if (contentComp != nullptr)
  95. contentComp->setTopLeftPosition (viewportPosToCompPos (newPosition));
  96. }
  97. void Viewport::setViewPositionProportionately (const double x, const double y)
  98. {
  99. if (contentComp != nullptr)
  100. setViewPosition (jmax (0, roundToInt (x * (contentComp->getWidth() - getWidth()))),
  101. jmax (0, roundToInt (y * (contentComp->getHeight() - getHeight()))));
  102. }
  103. bool Viewport::autoScroll (const int mouseX, const int mouseY, const int activeBorderThickness, const int maximumSpeed)
  104. {
  105. if (contentComp != nullptr)
  106. {
  107. int dx = 0, dy = 0;
  108. if (horizontalScrollBar.isVisible() || contentComp->getX() < 0 || contentComp->getRight() > getWidth())
  109. {
  110. if (mouseX < activeBorderThickness)
  111. dx = activeBorderThickness - mouseX;
  112. else if (mouseX >= contentHolder.getWidth() - activeBorderThickness)
  113. dx = (contentHolder.getWidth() - activeBorderThickness) - mouseX;
  114. if (dx < 0)
  115. dx = jmax (dx, -maximumSpeed, contentHolder.getWidth() - contentComp->getRight());
  116. else
  117. dx = jmin (dx, maximumSpeed, -contentComp->getX());
  118. }
  119. if (verticalScrollBar.isVisible() || contentComp->getY() < 0 || contentComp->getBottom() > getHeight())
  120. {
  121. if (mouseY < activeBorderThickness)
  122. dy = activeBorderThickness - mouseY;
  123. else if (mouseY >= contentHolder.getHeight() - activeBorderThickness)
  124. dy = (contentHolder.getHeight() - activeBorderThickness) - mouseY;
  125. if (dy < 0)
  126. dy = jmax (dy, -maximumSpeed, contentHolder.getHeight() - contentComp->getBottom());
  127. else
  128. dy = jmin (dy, maximumSpeed, -contentComp->getY());
  129. }
  130. if (dx != 0 || dy != 0)
  131. {
  132. contentComp->setTopLeftPosition (contentComp->getX() + dx,
  133. contentComp->getY() + dy);
  134. return true;
  135. }
  136. }
  137. return false;
  138. }
  139. void Viewport::componentMovedOrResized (Component&, bool, bool)
  140. {
  141. updateVisibleArea();
  142. }
  143. void Viewport::resized()
  144. {
  145. updateVisibleArea();
  146. }
  147. //==============================================================================
  148. void Viewport::updateVisibleArea()
  149. {
  150. const int scrollbarWidth = getScrollBarThickness();
  151. const bool canShowAnyBars = getWidth() > scrollbarWidth && getHeight() > scrollbarWidth;
  152. const bool canShowHBar = showHScrollbar && canShowAnyBars;
  153. const bool canShowVBar = showVScrollbar && canShowAnyBars;
  154. bool hBarVisible = canShowHBar && ! horizontalScrollBar.autoHides();
  155. bool vBarVisible = canShowVBar && ! verticalScrollBar.autoHides();
  156. Rectangle<int> contentArea (getLocalBounds());
  157. if (contentComp != nullptr && ! contentArea.contains (contentComp->getBounds()))
  158. {
  159. hBarVisible = canShowHBar && (hBarVisible || contentComp->getX() < 0 || contentComp->getRight() > contentArea.getWidth());
  160. vBarVisible = canShowVBar && (vBarVisible || contentComp->getY() < 0 || contentComp->getBottom() > contentArea.getHeight());
  161. if (vBarVisible)
  162. contentArea.setWidth (getWidth() - scrollbarWidth);
  163. if (hBarVisible)
  164. contentArea.setHeight (getHeight() - scrollbarWidth);
  165. if (! contentArea.contains (contentComp->getBounds()))
  166. {
  167. hBarVisible = canShowHBar && (hBarVisible || contentComp->getRight() > contentArea.getWidth());
  168. vBarVisible = canShowVBar && (vBarVisible || contentComp->getBottom() > contentArea.getHeight());
  169. }
  170. }
  171. if (vBarVisible)
  172. contentArea.setWidth (getWidth() - scrollbarWidth);
  173. if (hBarVisible)
  174. contentArea.setHeight (getHeight() - scrollbarWidth);
  175. contentHolder.setBounds (contentArea);
  176. Rectangle<int> contentBounds;
  177. if (contentComp != nullptr)
  178. contentBounds = contentHolder.getLocalArea (contentComp, contentComp->getLocalBounds());
  179. Point<int> visibleOrigin (-contentBounds.getPosition());
  180. if (hBarVisible)
  181. {
  182. horizontalScrollBar.setBounds (0, contentArea.getHeight(), contentArea.getWidth(), scrollbarWidth);
  183. horizontalScrollBar.setRangeLimits (0.0, contentBounds.getWidth());
  184. horizontalScrollBar.setCurrentRange (visibleOrigin.getX(), contentArea.getWidth());
  185. horizontalScrollBar.setSingleStepSize (singleStepX);
  186. horizontalScrollBar.cancelPendingUpdate();
  187. }
  188. else if (canShowHBar)
  189. {
  190. visibleOrigin.setX (0);
  191. }
  192. if (vBarVisible)
  193. {
  194. verticalScrollBar.setBounds (contentArea.getWidth(), 0, scrollbarWidth, contentArea.getHeight());
  195. verticalScrollBar.setRangeLimits (0.0, contentBounds.getHeight());
  196. verticalScrollBar.setCurrentRange (visibleOrigin.getY(), contentArea.getHeight());
  197. verticalScrollBar.setSingleStepSize (singleStepY);
  198. verticalScrollBar.cancelPendingUpdate();
  199. }
  200. else if (canShowVBar)
  201. {
  202. visibleOrigin.setY (0);
  203. }
  204. // Force the visibility *after* setting the ranges to avoid flicker caused by edge conditions in the numbers.
  205. horizontalScrollBar.setVisible (hBarVisible);
  206. verticalScrollBar.setVisible (vBarVisible);
  207. if (contentComp != nullptr)
  208. {
  209. const Point<int> newContentCompPos (viewportPosToCompPos (visibleOrigin));
  210. if (contentComp->getBounds().getPosition() != newContentCompPos)
  211. {
  212. contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
  213. return;
  214. }
  215. }
  216. const Rectangle<int> visibleArea (visibleOrigin.getX(), visibleOrigin.getY(),
  217. jmin (contentBounds.getWidth() - visibleOrigin.getX(), contentArea.getWidth()),
  218. jmin (contentBounds.getHeight() - visibleOrigin.getY(), contentArea.getHeight()));
  219. if (lastVisibleArea != visibleArea)
  220. {
  221. lastVisibleArea = visibleArea;
  222. visibleAreaChanged (visibleArea);
  223. }
  224. horizontalScrollBar.handleUpdateNowIfNeeded();
  225. verticalScrollBar.handleUpdateNowIfNeeded();
  226. }
  227. //==============================================================================
  228. void Viewport::setSingleStepSizes (const int stepX, const int stepY)
  229. {
  230. if (singleStepX != stepX || singleStepY != stepY)
  231. {
  232. singleStepX = stepX;
  233. singleStepY = stepY;
  234. updateVisibleArea();
  235. }
  236. }
  237. void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded,
  238. const bool showHorizontalScrollbarIfNeeded)
  239. {
  240. if (showVScrollbar != showVerticalScrollbarIfNeeded
  241. || showHScrollbar != showHorizontalScrollbarIfNeeded)
  242. {
  243. showVScrollbar = showVerticalScrollbarIfNeeded;
  244. showHScrollbar = showHorizontalScrollbarIfNeeded;
  245. updateVisibleArea();
  246. }
  247. }
  248. void Viewport::setScrollBarThickness (const int thickness)
  249. {
  250. if (scrollBarThickness != thickness)
  251. {
  252. scrollBarThickness = thickness;
  253. updateVisibleArea();
  254. }
  255. }
  256. int Viewport::getScrollBarThickness() const
  257. {
  258. return scrollBarThickness > 0 ? scrollBarThickness
  259. : getLookAndFeel().getDefaultScrollbarWidth();
  260. }
  261. void Viewport::setScrollBarButtonVisibility (const bool buttonsVisible)
  262. {
  263. verticalScrollBar.setButtonVisibility (buttonsVisible);
  264. horizontalScrollBar.setButtonVisibility (buttonsVisible);
  265. }
  266. void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart)
  267. {
  268. const int newRangeStartInt = roundToInt (newRangeStart);
  269. if (scrollBarThatHasMoved == &horizontalScrollBar)
  270. {
  271. setViewPosition (newRangeStartInt, getViewPositionY());
  272. }
  273. else if (scrollBarThatHasMoved == &verticalScrollBar)
  274. {
  275. setViewPosition (getViewPositionX(), newRangeStartInt);
  276. }
  277. }
  278. void Viewport::mouseWheelMove (const MouseEvent& e, const float wheelIncrementX, const float wheelIncrementY)
  279. {
  280. if (! useMouseWheelMoveIfNeeded (e, wheelIncrementX, wheelIncrementY))
  281. Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY);
  282. }
  283. bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY)
  284. {
  285. if (! (e.mods.isAltDown() || e.mods.isCtrlDown()))
  286. {
  287. const bool hasVertBar = verticalScrollBar.isVisible();
  288. const bool hasHorzBar = horizontalScrollBar.isVisible();
  289. if (hasHorzBar || hasVertBar)
  290. {
  291. if (wheelIncrementX != 0)
  292. {
  293. wheelIncrementX *= 14.0f * singleStepX;
  294. wheelIncrementX = (wheelIncrementX < 0) ? jmin (wheelIncrementX, -1.0f)
  295. : jmax (wheelIncrementX, 1.0f);
  296. }
  297. if (wheelIncrementY != 0)
  298. {
  299. wheelIncrementY *= 14.0f * singleStepY;
  300. wheelIncrementY = (wheelIncrementY < 0) ? jmin (wheelIncrementY, -1.0f)
  301. : jmax (wheelIncrementY, 1.0f);
  302. }
  303. Point<int> pos (getViewPosition());
  304. if (wheelIncrementX != 0 && wheelIncrementY != 0 && hasHorzBar && hasVertBar)
  305. {
  306. pos.setX (pos.getX() - roundToInt (wheelIncrementX));
  307. pos.setY (pos.getY() - roundToInt (wheelIncrementY));
  308. }
  309. else if (hasHorzBar && (wheelIncrementX != 0 || e.mods.isShiftDown() || ! hasVertBar))
  310. {
  311. if (wheelIncrementX == 0 && ! hasVertBar)
  312. wheelIncrementX = wheelIncrementY;
  313. pos.setX (pos.getX() - roundToInt (wheelIncrementX));
  314. }
  315. else if (hasVertBar && wheelIncrementY != 0)
  316. {
  317. pos.setY (pos.getY() - roundToInt (wheelIncrementY));
  318. }
  319. if (pos != getViewPosition())
  320. {
  321. setViewPosition (pos);
  322. return true;
  323. }
  324. }
  325. }
  326. return false;
  327. }
  328. bool Viewport::keyPressed (const KeyPress& key)
  329. {
  330. const bool isUpDownKey = key.isKeyCode (KeyPress::upKey)
  331. || key.isKeyCode (KeyPress::downKey)
  332. || key.isKeyCode (KeyPress::pageUpKey)
  333. || key.isKeyCode (KeyPress::pageDownKey)
  334. || key.isKeyCode (KeyPress::homeKey)
  335. || key.isKeyCode (KeyPress::endKey);
  336. if (verticalScrollBar.isVisible() && isUpDownKey)
  337. return verticalScrollBar.keyPressed (key);
  338. const bool isLeftRightKey = key.isKeyCode (KeyPress::leftKey)
  339. || key.isKeyCode (KeyPress::rightKey);
  340. if (horizontalScrollBar.isVisible() && (isUpDownKey || isLeftRightKey))
  341. return horizontalScrollBar.keyPressed (key);
  342. return false;
  343. }
  344. END_JUCE_NAMESPACE