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.

406 lines
14KB

  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 (0, 0);
  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. void Viewport::setViewPosition (const int xPixelsOffset, const int yPixelsOffset)
  83. {
  84. if (contentComp != nullptr)
  85. contentComp->setTopLeftPosition (jmax (jmin (0, contentHolder.getWidth() - contentComp->getWidth()), jmin (0, -xPixelsOffset)),
  86. jmax (jmin (0, contentHolder.getHeight() - contentComp->getHeight()), jmin (0, -yPixelsOffset)));
  87. }
  88. void Viewport::setViewPosition (const Point<int>& newPosition)
  89. {
  90. setViewPosition (newPosition.getX(), newPosition.getY());
  91. }
  92. void Viewport::setViewPositionProportionately (const double x, const double y)
  93. {
  94. if (contentComp != nullptr)
  95. setViewPosition (jmax (0, roundToInt (x * (contentComp->getWidth() - getWidth()))),
  96. jmax (0, roundToInt (y * (contentComp->getHeight() - getHeight()))));
  97. }
  98. bool Viewport::autoScroll (const int mouseX, const int mouseY, const int activeBorderThickness, const int maximumSpeed)
  99. {
  100. if (contentComp != nullptr)
  101. {
  102. int dx = 0, dy = 0;
  103. if (horizontalScrollBar.isVisible() || contentComp->getX() < 0 || contentComp->getRight() > getWidth())
  104. {
  105. if (mouseX < activeBorderThickness)
  106. dx = activeBorderThickness - mouseX;
  107. else if (mouseX >= contentHolder.getWidth() - activeBorderThickness)
  108. dx = (contentHolder.getWidth() - activeBorderThickness) - mouseX;
  109. if (dx < 0)
  110. dx = jmax (dx, -maximumSpeed, contentHolder.getWidth() - contentComp->getRight());
  111. else
  112. dx = jmin (dx, maximumSpeed, -contentComp->getX());
  113. }
  114. if (verticalScrollBar.isVisible() || contentComp->getY() < 0 || contentComp->getBottom() > getHeight())
  115. {
  116. if (mouseY < activeBorderThickness)
  117. dy = activeBorderThickness - mouseY;
  118. else if (mouseY >= contentHolder.getHeight() - activeBorderThickness)
  119. dy = (contentHolder.getHeight() - activeBorderThickness) - mouseY;
  120. if (dy < 0)
  121. dy = jmax (dy, -maximumSpeed, contentHolder.getHeight() - contentComp->getBottom());
  122. else
  123. dy = jmin (dy, maximumSpeed, -contentComp->getY());
  124. }
  125. if (dx != 0 || dy != 0)
  126. {
  127. contentComp->setTopLeftPosition (contentComp->getX() + dx,
  128. contentComp->getY() + dy);
  129. return true;
  130. }
  131. }
  132. return false;
  133. }
  134. void Viewport::componentMovedOrResized (Component&, bool, bool)
  135. {
  136. updateVisibleArea();
  137. }
  138. void Viewport::resized()
  139. {
  140. updateVisibleArea();
  141. }
  142. //==============================================================================
  143. void Viewport::updateVisibleArea()
  144. {
  145. const int scrollbarWidth = getScrollBarThickness();
  146. const bool canShowAnyBars = getWidth() > scrollbarWidth && getHeight() > scrollbarWidth;
  147. const bool canShowHBar = showHScrollbar && canShowAnyBars;
  148. const bool canShowVBar = showVScrollbar && canShowAnyBars;
  149. bool hBarVisible = canShowHBar && ! horizontalScrollBar.autoHides();
  150. bool vBarVisible = canShowVBar && ! verticalScrollBar.autoHides();
  151. Rectangle<int> contentArea (getLocalBounds());
  152. if (contentComp != nullptr && ! contentArea.contains (contentComp->getBounds()))
  153. {
  154. hBarVisible = canShowHBar && (hBarVisible || contentComp->getX() < 0 || contentComp->getRight() > contentArea.getWidth());
  155. vBarVisible = canShowVBar && (vBarVisible || contentComp->getY() < 0 || contentComp->getBottom() > contentArea.getHeight());
  156. if (vBarVisible)
  157. contentArea.setWidth (getWidth() - scrollbarWidth);
  158. if (hBarVisible)
  159. contentArea.setHeight (getHeight() - scrollbarWidth);
  160. if (! contentArea.contains (contentComp->getBounds()))
  161. {
  162. hBarVisible = canShowHBar && (hBarVisible || contentComp->getRight() > contentArea.getWidth());
  163. vBarVisible = canShowVBar && (vBarVisible || contentComp->getBottom() > contentArea.getHeight());
  164. }
  165. }
  166. if (vBarVisible)
  167. contentArea.setWidth (getWidth() - scrollbarWidth);
  168. if (hBarVisible)
  169. contentArea.setHeight (getHeight() - scrollbarWidth);
  170. contentHolder.setBounds (contentArea);
  171. Rectangle<int> contentBounds;
  172. if (contentComp != nullptr)
  173. contentBounds = contentHolder.getLocalArea (contentComp, contentComp->getLocalBounds());
  174. Point<int> visibleOrigin (-contentBounds.getPosition());
  175. if (hBarVisible)
  176. {
  177. horizontalScrollBar.setBounds (0, contentArea.getHeight(), contentArea.getWidth(), scrollbarWidth);
  178. horizontalScrollBar.setRangeLimits (0.0, contentBounds.getWidth());
  179. horizontalScrollBar.setCurrentRange (visibleOrigin.getX(), contentArea.getWidth());
  180. horizontalScrollBar.setSingleStepSize (singleStepX);
  181. horizontalScrollBar.cancelPendingUpdate();
  182. }
  183. else if (canShowHBar)
  184. {
  185. visibleOrigin.setX (0);
  186. }
  187. if (vBarVisible)
  188. {
  189. verticalScrollBar.setBounds (contentArea.getWidth(), 0, scrollbarWidth, contentArea.getHeight());
  190. verticalScrollBar.setRangeLimits (0.0, contentBounds.getHeight());
  191. verticalScrollBar.setCurrentRange (visibleOrigin.getY(), contentArea.getHeight());
  192. verticalScrollBar.setSingleStepSize (singleStepY);
  193. verticalScrollBar.cancelPendingUpdate();
  194. }
  195. else if (canShowVBar)
  196. {
  197. visibleOrigin.setY (0);
  198. }
  199. // Force the visibility *after* setting the ranges to avoid flicker caused by edge conditions in the numbers.
  200. horizontalScrollBar.setVisible (hBarVisible);
  201. verticalScrollBar.setVisible (vBarVisible);
  202. setViewPosition (visibleOrigin);
  203. const Rectangle<int> visibleArea (visibleOrigin.getX(), visibleOrigin.getY(),
  204. jmin (contentBounds.getWidth() - visibleOrigin.getX(), contentArea.getWidth()),
  205. jmin (contentBounds.getHeight() - visibleOrigin.getY(), contentArea.getHeight()));
  206. if (lastVisibleArea != visibleArea)
  207. {
  208. lastVisibleArea = visibleArea;
  209. visibleAreaChanged (visibleArea);
  210. }
  211. horizontalScrollBar.handleUpdateNowIfNeeded();
  212. verticalScrollBar.handleUpdateNowIfNeeded();
  213. }
  214. //==============================================================================
  215. void Viewport::setSingleStepSizes (const int stepX, const int stepY)
  216. {
  217. if (singleStepX != stepX || singleStepY != stepY)
  218. {
  219. singleStepX = stepX;
  220. singleStepY = stepY;
  221. updateVisibleArea();
  222. }
  223. }
  224. void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded,
  225. const bool showHorizontalScrollbarIfNeeded)
  226. {
  227. if (showVScrollbar != showVerticalScrollbarIfNeeded
  228. || showHScrollbar != showHorizontalScrollbarIfNeeded)
  229. {
  230. showVScrollbar = showVerticalScrollbarIfNeeded;
  231. showHScrollbar = showHorizontalScrollbarIfNeeded;
  232. updateVisibleArea();
  233. }
  234. }
  235. void Viewport::setScrollBarThickness (const int thickness)
  236. {
  237. if (scrollBarThickness != thickness)
  238. {
  239. scrollBarThickness = thickness;
  240. updateVisibleArea();
  241. }
  242. }
  243. int Viewport::getScrollBarThickness() const
  244. {
  245. return scrollBarThickness > 0 ? scrollBarThickness
  246. : getLookAndFeel().getDefaultScrollbarWidth();
  247. }
  248. void Viewport::setScrollBarButtonVisibility (const bool buttonsVisible)
  249. {
  250. verticalScrollBar.setButtonVisibility (buttonsVisible);
  251. horizontalScrollBar.setButtonVisibility (buttonsVisible);
  252. }
  253. void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart)
  254. {
  255. const int newRangeStartInt = roundToInt (newRangeStart);
  256. if (scrollBarThatHasMoved == &horizontalScrollBar)
  257. {
  258. setViewPosition (newRangeStartInt, getViewPositionY());
  259. }
  260. else if (scrollBarThatHasMoved == &verticalScrollBar)
  261. {
  262. setViewPosition (getViewPositionX(), newRangeStartInt);
  263. }
  264. }
  265. void Viewport::mouseWheelMove (const MouseEvent& e, const float wheelIncrementX, const float wheelIncrementY)
  266. {
  267. if (! useMouseWheelMoveIfNeeded (e, wheelIncrementX, wheelIncrementY))
  268. Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY);
  269. }
  270. bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY)
  271. {
  272. if (! (e.mods.isAltDown() || e.mods.isCtrlDown()))
  273. {
  274. const bool hasVertBar = verticalScrollBar.isVisible();
  275. const bool hasHorzBar = horizontalScrollBar.isVisible();
  276. if (hasHorzBar || hasVertBar)
  277. {
  278. if (wheelIncrementX != 0)
  279. {
  280. wheelIncrementX *= 14.0f * singleStepX;
  281. wheelIncrementX = (wheelIncrementX < 0) ? jmin (wheelIncrementX, -1.0f)
  282. : jmax (wheelIncrementX, 1.0f);
  283. }
  284. if (wheelIncrementY != 0)
  285. {
  286. wheelIncrementY *= 14.0f * singleStepY;
  287. wheelIncrementY = (wheelIncrementY < 0) ? jmin (wheelIncrementY, -1.0f)
  288. : jmax (wheelIncrementY, 1.0f);
  289. }
  290. Point<int> pos (getViewPosition());
  291. if (wheelIncrementX != 0 && wheelIncrementY != 0 && hasHorzBar && hasVertBar)
  292. {
  293. pos.setX (pos.getX() - roundToInt (wheelIncrementX));
  294. pos.setY (pos.getY() - roundToInt (wheelIncrementY));
  295. }
  296. else if (hasHorzBar && (wheelIncrementX != 0 || e.mods.isShiftDown() || ! hasVertBar))
  297. {
  298. if (wheelIncrementX == 0 && ! hasVertBar)
  299. wheelIncrementX = wheelIncrementY;
  300. pos.setX (pos.getX() - roundToInt (wheelIncrementX));
  301. }
  302. else if (hasVertBar && wheelIncrementY != 0)
  303. {
  304. pos.setY (pos.getY() - roundToInt (wheelIncrementY));
  305. }
  306. if (pos != getViewPosition())
  307. {
  308. setViewPosition (pos);
  309. return true;
  310. }
  311. }
  312. }
  313. return false;
  314. }
  315. bool Viewport::keyPressed (const KeyPress& key)
  316. {
  317. const bool isUpDownKey = key.isKeyCode (KeyPress::upKey)
  318. || key.isKeyCode (KeyPress::downKey)
  319. || key.isKeyCode (KeyPress::pageUpKey)
  320. || key.isKeyCode (KeyPress::pageDownKey)
  321. || key.isKeyCode (KeyPress::homeKey)
  322. || key.isKeyCode (KeyPress::endKey);
  323. if (verticalScrollBar.isVisible() && isUpDownKey)
  324. return verticalScrollBar.keyPressed (key);
  325. const bool isLeftRightKey = key.isKeyCode (KeyPress::leftKey)
  326. || key.isKeyCode (KeyPress::rightKey);
  327. if (horizontalScrollBar.isVisible() && (isUpDownKey || isLeftRightKey))
  328. return horizontalScrollBar.keyPressed (key);
  329. return false;
  330. }
  331. END_JUCE_NAMESPACE