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.

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