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.

419 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. const Point<int> newContentCompPos (viewportPosToCompPos (visibleOrigin));
  208. if (contentComp != nullptr && contentComp->getBounds().getPosition() != newContentCompPos)
  209. {
  210. contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
  211. }
  212. else
  213. {
  214. const Rectangle<int> visibleArea (visibleOrigin.getX(), visibleOrigin.getY(),
  215. jmin (contentBounds.getWidth() - visibleOrigin.getX(), contentArea.getWidth()),
  216. jmin (contentBounds.getHeight() - visibleOrigin.getY(), contentArea.getHeight()));
  217. if (lastVisibleArea != visibleArea)
  218. {
  219. lastVisibleArea = visibleArea;
  220. visibleAreaChanged (visibleArea);
  221. }
  222. horizontalScrollBar.handleUpdateNowIfNeeded();
  223. verticalScrollBar.handleUpdateNowIfNeeded();
  224. }
  225. }
  226. //==============================================================================
  227. void Viewport::setSingleStepSizes (const int stepX, const int stepY)
  228. {
  229. if (singleStepX != stepX || singleStepY != stepY)
  230. {
  231. singleStepX = stepX;
  232. singleStepY = stepY;
  233. updateVisibleArea();
  234. }
  235. }
  236. void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded,
  237. const bool showHorizontalScrollbarIfNeeded)
  238. {
  239. if (showVScrollbar != showVerticalScrollbarIfNeeded
  240. || showHScrollbar != showHorizontalScrollbarIfNeeded)
  241. {
  242. showVScrollbar = showVerticalScrollbarIfNeeded;
  243. showHScrollbar = showHorizontalScrollbarIfNeeded;
  244. updateVisibleArea();
  245. }
  246. }
  247. void Viewport::setScrollBarThickness (const int thickness)
  248. {
  249. if (scrollBarThickness != thickness)
  250. {
  251. scrollBarThickness = thickness;
  252. updateVisibleArea();
  253. }
  254. }
  255. int Viewport::getScrollBarThickness() const
  256. {
  257. return scrollBarThickness > 0 ? scrollBarThickness
  258. : getLookAndFeel().getDefaultScrollbarWidth();
  259. }
  260. void Viewport::setScrollBarButtonVisibility (const bool buttonsVisible)
  261. {
  262. verticalScrollBar.setButtonVisibility (buttonsVisible);
  263. horizontalScrollBar.setButtonVisibility (buttonsVisible);
  264. }
  265. void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart)
  266. {
  267. const int newRangeStartInt = roundToInt (newRangeStart);
  268. if (scrollBarThatHasMoved == &horizontalScrollBar)
  269. {
  270. setViewPosition (newRangeStartInt, getViewPositionY());
  271. }
  272. else if (scrollBarThatHasMoved == &verticalScrollBar)
  273. {
  274. setViewPosition (getViewPositionX(), newRangeStartInt);
  275. }
  276. }
  277. void Viewport::mouseWheelMove (const MouseEvent& e, const float wheelIncrementX, const float wheelIncrementY)
  278. {
  279. if (! useMouseWheelMoveIfNeeded (e, wheelIncrementX, wheelIncrementY))
  280. Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY);
  281. }
  282. bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY)
  283. {
  284. if (! (e.mods.isAltDown() || e.mods.isCtrlDown()))
  285. {
  286. const bool hasVertBar = verticalScrollBar.isVisible();
  287. const bool hasHorzBar = horizontalScrollBar.isVisible();
  288. if (hasHorzBar || hasVertBar)
  289. {
  290. if (wheelIncrementX != 0)
  291. {
  292. wheelIncrementX *= 14.0f * singleStepX;
  293. wheelIncrementX = (wheelIncrementX < 0) ? jmin (wheelIncrementX, -1.0f)
  294. : jmax (wheelIncrementX, 1.0f);
  295. }
  296. if (wheelIncrementY != 0)
  297. {
  298. wheelIncrementY *= 14.0f * singleStepY;
  299. wheelIncrementY = (wheelIncrementY < 0) ? jmin (wheelIncrementY, -1.0f)
  300. : jmax (wheelIncrementY, 1.0f);
  301. }
  302. Point<int> pos (getViewPosition());
  303. if (wheelIncrementX != 0 && wheelIncrementY != 0 && hasHorzBar && hasVertBar)
  304. {
  305. pos.setX (pos.getX() - roundToInt (wheelIncrementX));
  306. pos.setY (pos.getY() - roundToInt (wheelIncrementY));
  307. }
  308. else if (hasHorzBar && (wheelIncrementX != 0 || e.mods.isShiftDown() || ! hasVertBar))
  309. {
  310. if (wheelIncrementX == 0 && ! hasVertBar)
  311. wheelIncrementX = wheelIncrementY;
  312. pos.setX (pos.getX() - roundToInt (wheelIncrementX));
  313. }
  314. else if (hasVertBar && wheelIncrementY != 0)
  315. {
  316. pos.setY (pos.getY() - roundToInt (wheelIncrementY));
  317. }
  318. if (pos != getViewPosition())
  319. {
  320. setViewPosition (pos);
  321. return true;
  322. }
  323. }
  324. }
  325. return false;
  326. }
  327. bool Viewport::keyPressed (const KeyPress& key)
  328. {
  329. const bool isUpDownKey = key.isKeyCode (KeyPress::upKey)
  330. || key.isKeyCode (KeyPress::downKey)
  331. || key.isKeyCode (KeyPress::pageUpKey)
  332. || key.isKeyCode (KeyPress::pageDownKey)
  333. || key.isKeyCode (KeyPress::homeKey)
  334. || key.isKeyCode (KeyPress::endKey);
  335. if (verticalScrollBar.isVisible() && isUpDownKey)
  336. return verticalScrollBar.keyPressed (key);
  337. const bool isLeftRightKey = key.isKeyCode (KeyPress::leftKey)
  338. || key.isKeyCode (KeyPress::rightKey);
  339. if (horizontalScrollBar.isVisible() && (isUpDownKey || isLeftRightKey))
  340. return horizontalScrollBar.keyPressed (key);
  341. return false;
  342. }
  343. END_JUCE_NAMESPACE