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.

440 lines
16KB

  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. Viewport::Viewport (const String& name)
  19. : Component (name),
  20. scrollBarThickness (0),
  21. singleStepX (16),
  22. singleStepY (16),
  23. showHScrollbar (true),
  24. showVScrollbar (true),
  25. deleteContent (true),
  26. verticalScrollBar (true),
  27. horizontalScrollBar (false)
  28. {
  29. // content holder is used to clip the contents so they don't overlap the scrollbars
  30. addAndMakeVisible (&contentHolder);
  31. contentHolder.setInterceptsMouseClicks (false, true);
  32. addChildComponent (&verticalScrollBar);
  33. addChildComponent (&horizontalScrollBar);
  34. verticalScrollBar.addListener (this);
  35. horizontalScrollBar.addListener (this);
  36. setInterceptsMouseClicks (false, true);
  37. setWantsKeyboardFocus (true);
  38. }
  39. Viewport::~Viewport()
  40. {
  41. deleteContentComp();
  42. }
  43. //==============================================================================
  44. void Viewport::visibleAreaChanged (const Rectangle<int>&) {}
  45. void Viewport::viewedComponentChanged (Component*) {}
  46. //==============================================================================
  47. void Viewport::deleteContentComp()
  48. {
  49. if (contentComp != nullptr)
  50. contentComp->removeComponentListener (this);
  51. if (deleteContent)
  52. {
  53. // This sets the content comp to a null pointer before deleting the old one, in case
  54. // anything tries to use the old one while it's in mid-deletion..
  55. ScopedPointer<Component> oldCompDeleter (contentComp);
  56. }
  57. else
  58. {
  59. contentComp = nullptr;
  60. }
  61. }
  62. void Viewport::setViewedComponent (Component* const newViewedComponent, const bool deleteComponentWhenNoLongerNeeded)
  63. {
  64. if (contentComp.get() != newViewedComponent)
  65. {
  66. deleteContentComp();
  67. contentComp = newViewedComponent;
  68. deleteContent = deleteComponentWhenNoLongerNeeded;
  69. if (contentComp != nullptr)
  70. {
  71. contentHolder.addAndMakeVisible (contentComp);
  72. setViewPosition (Point<int>());
  73. contentComp->addComponentListener (this);
  74. }
  75. viewedComponentChanged (contentComp);
  76. updateVisibleArea();
  77. }
  78. }
  79. int Viewport::getMaximumVisibleWidth() const { return contentHolder.getWidth(); }
  80. int Viewport::getMaximumVisibleHeight() const { return contentHolder.getHeight(); }
  81. Point<int> Viewport::viewportPosToCompPos (const Point<int>& pos) const
  82. {
  83. jassert (contentComp != nullptr);
  84. return Point<int> (jmax (jmin (0, contentHolder.getWidth() - contentComp->getWidth()), jmin (0, -(pos.x))),
  85. jmax (jmin (0, contentHolder.getHeight() - contentComp->getHeight()), jmin (0, -(pos.y))));
  86. }
  87. void Viewport::setViewPosition (const int xPixelsOffset, const int yPixelsOffset)
  88. {
  89. setViewPosition (Point<int> (xPixelsOffset, yPixelsOffset));
  90. }
  91. void Viewport::setViewPosition (const Point<int>& newPosition)
  92. {
  93. if (contentComp != nullptr)
  94. contentComp->setTopLeftPosition (viewportPosToCompPos (newPosition));
  95. }
  96. void Viewport::setViewPositionProportionately (const double x, const double y)
  97. {
  98. if (contentComp != nullptr)
  99. setViewPosition (jmax (0, roundToInt (x * (contentComp->getWidth() - getWidth()))),
  100. jmax (0, roundToInt (y * (contentComp->getHeight() - getHeight()))));
  101. }
  102. bool Viewport::autoScroll (const int mouseX, const int mouseY, const int activeBorderThickness, const int maximumSpeed)
  103. {
  104. if (contentComp != nullptr)
  105. {
  106. int dx = 0, dy = 0;
  107. if (horizontalScrollBar.isVisible() || contentComp->getX() < 0 || contentComp->getRight() > getWidth())
  108. {
  109. if (mouseX < activeBorderThickness)
  110. dx = activeBorderThickness - mouseX;
  111. else if (mouseX >= contentHolder.getWidth() - activeBorderThickness)
  112. dx = (contentHolder.getWidth() - activeBorderThickness) - mouseX;
  113. if (dx < 0)
  114. dx = jmax (dx, -maximumSpeed, contentHolder.getWidth() - contentComp->getRight());
  115. else
  116. dx = jmin (dx, maximumSpeed, -contentComp->getX());
  117. }
  118. if (verticalScrollBar.isVisible() || contentComp->getY() < 0 || contentComp->getBottom() > getHeight())
  119. {
  120. if (mouseY < activeBorderThickness)
  121. dy = activeBorderThickness - mouseY;
  122. else if (mouseY >= contentHolder.getHeight() - activeBorderThickness)
  123. dy = (contentHolder.getHeight() - activeBorderThickness) - mouseY;
  124. if (dy < 0)
  125. dy = jmax (dy, -maximumSpeed, contentHolder.getHeight() - contentComp->getBottom());
  126. else
  127. dy = jmin (dy, maximumSpeed, -contentComp->getY());
  128. }
  129. if (dx != 0 || dy != 0)
  130. {
  131. contentComp->setTopLeftPosition (contentComp->getX() + dx,
  132. contentComp->getY() + dy);
  133. return true;
  134. }
  135. }
  136. return false;
  137. }
  138. void Viewport::componentMovedOrResized (Component&, bool, bool)
  139. {
  140. updateVisibleArea();
  141. }
  142. void Viewport::resized()
  143. {
  144. updateVisibleArea();
  145. }
  146. //==============================================================================
  147. void Viewport::updateVisibleArea()
  148. {
  149. const int scrollbarWidth = getScrollBarThickness();
  150. const bool canShowAnyBars = getWidth() > scrollbarWidth && getHeight() > scrollbarWidth;
  151. const bool canShowHBar = showHScrollbar && canShowAnyBars;
  152. const bool canShowVBar = showVScrollbar && canShowAnyBars;
  153. bool hBarVisible = false, vBarVisible = false;
  154. Rectangle<int> contentArea;
  155. for (int i = 3; --i >= 0;)
  156. {
  157. hBarVisible = canShowHBar && ! horizontalScrollBar.autoHides();
  158. vBarVisible = canShowVBar && ! verticalScrollBar.autoHides();
  159. contentArea = getLocalBounds();
  160. if (contentComp != nullptr && ! contentArea.contains (contentComp->getBounds()))
  161. {
  162. hBarVisible = canShowHBar && (hBarVisible || contentComp->getX() < 0 || contentComp->getRight() > contentArea.getWidth());
  163. vBarVisible = canShowVBar && (vBarVisible || contentComp->getY() < 0 || contentComp->getBottom() > contentArea.getHeight());
  164. if (vBarVisible)
  165. contentArea.setWidth (getWidth() - scrollbarWidth);
  166. if (hBarVisible)
  167. contentArea.setHeight (getHeight() - scrollbarWidth);
  168. if (! contentArea.contains (contentComp->getBounds()))
  169. {
  170. hBarVisible = canShowHBar && (hBarVisible || contentComp->getRight() > contentArea.getWidth());
  171. vBarVisible = canShowVBar && (vBarVisible || contentComp->getBottom() > contentArea.getHeight());
  172. }
  173. }
  174. if (vBarVisible) contentArea.setWidth (getWidth() - scrollbarWidth);
  175. if (hBarVisible) contentArea.setHeight (getHeight() - scrollbarWidth);
  176. if (contentComp == nullptr)
  177. {
  178. contentHolder.setBounds (contentArea);
  179. break;
  180. }
  181. const Rectangle<int> oldContentBounds (contentComp->getBounds());
  182. contentHolder.setBounds (contentArea);
  183. // If the content has changed its size, that might affect our scrollbars, so go round again and re-caclulate..
  184. if (oldContentBounds == contentComp->getBounds())
  185. break;
  186. }
  187. Rectangle<int> contentBounds;
  188. if (contentComp != nullptr)
  189. contentBounds = contentHolder.getLocalArea (contentComp, contentComp->getLocalBounds());
  190. Point<int> visibleOrigin (-contentBounds.getPosition());
  191. if (hBarVisible)
  192. {
  193. horizontalScrollBar.setBounds (0, contentArea.getHeight(), contentArea.getWidth(), scrollbarWidth);
  194. horizontalScrollBar.setRangeLimits (0.0, contentBounds.getWidth());
  195. horizontalScrollBar.setCurrentRange (visibleOrigin.x, contentArea.getWidth());
  196. horizontalScrollBar.setSingleStepSize (singleStepX);
  197. horizontalScrollBar.cancelPendingUpdate();
  198. }
  199. else if (canShowHBar)
  200. {
  201. visibleOrigin.setX (0);
  202. }
  203. if (vBarVisible)
  204. {
  205. verticalScrollBar.setBounds (contentArea.getWidth(), 0, scrollbarWidth, contentArea.getHeight());
  206. verticalScrollBar.setRangeLimits (0.0, contentBounds.getHeight());
  207. verticalScrollBar.setCurrentRange (visibleOrigin.y, contentArea.getHeight());
  208. verticalScrollBar.setSingleStepSize (singleStepY);
  209. verticalScrollBar.cancelPendingUpdate();
  210. }
  211. else if (canShowVBar)
  212. {
  213. visibleOrigin.setY (0);
  214. }
  215. // Force the visibility *after* setting the ranges to avoid flicker caused by edge conditions in the numbers.
  216. horizontalScrollBar.setVisible (hBarVisible);
  217. verticalScrollBar.setVisible (vBarVisible);
  218. if (contentComp != nullptr)
  219. {
  220. const Point<int> newContentCompPos (viewportPosToCompPos (visibleOrigin));
  221. if (contentComp->getBounds().getPosition() != newContentCompPos)
  222. {
  223. contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
  224. return;
  225. }
  226. }
  227. const Rectangle<int> visibleArea (visibleOrigin.x, visibleOrigin.y,
  228. jmin (contentBounds.getWidth() - visibleOrigin.x, contentArea.getWidth()),
  229. jmin (contentBounds.getHeight() - visibleOrigin.y, contentArea.getHeight()));
  230. if (lastVisibleArea != visibleArea)
  231. {
  232. lastVisibleArea = visibleArea;
  233. visibleAreaChanged (visibleArea);
  234. }
  235. horizontalScrollBar.handleUpdateNowIfNeeded();
  236. verticalScrollBar.handleUpdateNowIfNeeded();
  237. }
  238. //==============================================================================
  239. void Viewport::setSingleStepSizes (const int stepX, const int stepY)
  240. {
  241. if (singleStepX != stepX || singleStepY != stepY)
  242. {
  243. singleStepX = stepX;
  244. singleStepY = stepY;
  245. updateVisibleArea();
  246. }
  247. }
  248. void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded,
  249. const bool showHorizontalScrollbarIfNeeded)
  250. {
  251. if (showVScrollbar != showVerticalScrollbarIfNeeded
  252. || showHScrollbar != showHorizontalScrollbarIfNeeded)
  253. {
  254. showVScrollbar = showVerticalScrollbarIfNeeded;
  255. showHScrollbar = showHorizontalScrollbarIfNeeded;
  256. updateVisibleArea();
  257. }
  258. }
  259. void Viewport::setScrollBarThickness (const int thickness)
  260. {
  261. if (scrollBarThickness != thickness)
  262. {
  263. scrollBarThickness = thickness;
  264. updateVisibleArea();
  265. }
  266. }
  267. int Viewport::getScrollBarThickness() const
  268. {
  269. return scrollBarThickness > 0 ? scrollBarThickness
  270. : getLookAndFeel().getDefaultScrollbarWidth();
  271. }
  272. void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart)
  273. {
  274. const int newRangeStartInt = roundToInt (newRangeStart);
  275. if (scrollBarThatHasMoved == &horizontalScrollBar)
  276. {
  277. setViewPosition (newRangeStartInt, getViewPositionY());
  278. }
  279. else if (scrollBarThatHasMoved == &verticalScrollBar)
  280. {
  281. setViewPosition (getViewPositionX(), newRangeStartInt);
  282. }
  283. }
  284. void Viewport::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
  285. {
  286. if (! useMouseWheelMoveIfNeeded (e, wheel))
  287. Component::mouseWheelMove (e, wheel);
  288. }
  289. bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelDetails& wheel)
  290. {
  291. if (! (e.mods.isAltDown() || e.mods.isCtrlDown()))
  292. {
  293. const bool hasVertBar = verticalScrollBar.isVisible();
  294. const bool hasHorzBar = horizontalScrollBar.isVisible();
  295. if (hasHorzBar || hasVertBar)
  296. {
  297. float wheelIncrementX = wheel.deltaX;
  298. float wheelIncrementY = wheel.deltaY;
  299. if (wheelIncrementX != 0)
  300. {
  301. wheelIncrementX *= 14.0f * singleStepX;
  302. wheelIncrementX = (wheelIncrementX < 0) ? jmin (wheelIncrementX, -1.0f)
  303. : jmax (wheelIncrementX, 1.0f);
  304. }
  305. if (wheelIncrementY != 0)
  306. {
  307. wheelIncrementY *= 14.0f * singleStepY;
  308. wheelIncrementY = (wheelIncrementY < 0) ? jmin (wheelIncrementY, -1.0f)
  309. : jmax (wheelIncrementY, 1.0f);
  310. }
  311. Point<int> pos (getViewPosition());
  312. if (wheelIncrementX != 0 && wheelIncrementY != 0 && hasHorzBar && hasVertBar)
  313. {
  314. pos.setX (pos.x - roundToInt (wheelIncrementX));
  315. pos.setY (pos.y - roundToInt (wheelIncrementY));
  316. }
  317. else if (hasHorzBar && (wheelIncrementX != 0 || e.mods.isShiftDown() || ! hasVertBar))
  318. {
  319. if (wheelIncrementX == 0 && ! hasVertBar)
  320. wheelIncrementX = wheelIncrementY;
  321. pos.setX (pos.x - roundToInt (wheelIncrementX));
  322. }
  323. else if (hasVertBar && wheelIncrementY != 0)
  324. {
  325. pos.setY (pos.y - roundToInt (wheelIncrementY));
  326. }
  327. if (pos != getViewPosition())
  328. {
  329. setViewPosition (pos);
  330. return true;
  331. }
  332. }
  333. }
  334. return false;
  335. }
  336. bool Viewport::keyPressed (const KeyPress& key)
  337. {
  338. const bool isUpDownKey = key.isKeyCode (KeyPress::upKey)
  339. || key.isKeyCode (KeyPress::downKey)
  340. || key.isKeyCode (KeyPress::pageUpKey)
  341. || key.isKeyCode (KeyPress::pageDownKey)
  342. || key.isKeyCode (KeyPress::homeKey)
  343. || key.isKeyCode (KeyPress::endKey);
  344. if (verticalScrollBar.isVisible() && isUpDownKey)
  345. return verticalScrollBar.keyPressed (key);
  346. const bool isLeftRightKey = key.isKeyCode (KeyPress::leftKey)
  347. || key.isKeyCode (KeyPress::rightKey);
  348. if (horizontalScrollBar.isVisible() && (isUpDownKey || isLeftRightKey))
  349. return horizontalScrollBar.keyPressed (key);
  350. return false;
  351. }
  352. //==============================================================================
  353. const Identifier Viewport::Ids::showScrollBarV ("showScrollBarV");
  354. const Identifier Viewport::Ids::showScrollBarH ("showScrollBarH");
  355. const Identifier Viewport::Ids::scrollBarWidth ("scrollBarWidth");
  356. void Viewport::refreshFromValueTree (const ValueTree& state, ComponentBuilder&)
  357. {
  358. ComponentBuilder::refreshBasicComponentProperties (*this, state);
  359. setScrollBarsShown (state [Ids::showScrollBarV], state [Ids::showScrollBarH]);
  360. setScrollBarThickness (state [Ids::scrollBarWidth]);
  361. }