1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
Viewport(const String & name)29 Viewport::Viewport (const String& name)  : Component (name)
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 
35     scrollBarThickness = getLookAndFeel().getDefaultScrollbarWidth();
36 
37     setInterceptsMouseClicks (false, true);
38     setWantsKeyboardFocus (true);
39     setScrollOnDragEnabled (Desktop::getInstance().getMainMouseSource().isTouch());
40 
41     recreateScrollbars();
42 }
43 
~Viewport()44 Viewport::~Viewport()
45 {
46     setScrollOnDragEnabled (false);
47     deleteOrRemoveContentComp();
48 }
49 
50 //==============================================================================
visibleAreaChanged(const Rectangle<int> &)51 void Viewport::visibleAreaChanged (const Rectangle<int>&) {}
viewedComponentChanged(Component *)52 void Viewport::viewedComponentChanged (Component*) {}
53 
54 //==============================================================================
deleteOrRemoveContentComp()55 void Viewport::deleteOrRemoveContentComp()
56 {
57     if (contentComp != nullptr)
58     {
59         contentComp->removeComponentListener (this);
60 
61         if (deleteContent)
62         {
63             // This sets the content comp to a null pointer before deleting the old one, in case
64             // anything tries to use the old one while it's in mid-deletion..
65             std::unique_ptr<Component> oldCompDeleter (contentComp.get());
66             contentComp = nullptr;
67         }
68         else
69         {
70             contentHolder.removeChildComponent (contentComp);
71             contentComp = nullptr;
72         }
73     }
74 }
75 
setViewedComponent(Component * const newViewedComponent,const bool deleteComponentWhenNoLongerNeeded)76 void Viewport::setViewedComponent (Component* const newViewedComponent, const bool deleteComponentWhenNoLongerNeeded)
77 {
78     if (contentComp.get() != newViewedComponent)
79     {
80         deleteOrRemoveContentComp();
81         contentComp = newViewedComponent;
82         deleteContent = deleteComponentWhenNoLongerNeeded;
83 
84         if (contentComp != nullptr)
85         {
86             contentHolder.addAndMakeVisible (contentComp);
87             setViewPosition (Point<int>());
88             contentComp->addComponentListener (this);
89         }
90 
91         viewedComponentChanged (contentComp);
92         updateVisibleArea();
93     }
94 }
95 
recreateScrollbars()96 void Viewport::recreateScrollbars()
97 {
98     verticalScrollBar.reset();
99     horizontalScrollBar.reset();
100 
101     verticalScrollBar  .reset (createScrollBarComponent (true));
102     horizontalScrollBar.reset (createScrollBarComponent (false));
103 
104     addChildComponent (verticalScrollBar.get());
105     addChildComponent (horizontalScrollBar.get());
106 
107     getVerticalScrollBar().addListener (this);
108     getHorizontalScrollBar().addListener (this);
109 
110     resized();
111 }
112 
getMaximumVisibleWidth() const113 int Viewport::getMaximumVisibleWidth() const            { return contentHolder.getWidth(); }
getMaximumVisibleHeight() const114 int Viewport::getMaximumVisibleHeight() const           { return contentHolder.getHeight(); }
115 
canScrollVertically() const116 bool Viewport::canScrollVertically() const noexcept     { return contentComp->getY() < 0 || contentComp->getBottom() > getHeight(); }
canScrollHorizontally() const117 bool Viewport::canScrollHorizontally() const noexcept   { return contentComp->getX() < 0 || contentComp->getRight()  > getWidth(); }
118 
viewportPosToCompPos(Point<int> pos) const119 Point<int> Viewport::viewportPosToCompPos (Point<int> pos) const
120 {
121     jassert (contentComp != nullptr);
122 
123     auto contentBounds = contentHolder.getLocalArea (contentComp.get(), contentComp->getLocalBounds());
124 
125     Point<int> p (jmax (jmin (0, contentHolder.getWidth()  - contentBounds.getWidth()),  jmin (0, -(pos.x))),
126                   jmax (jmin (0, contentHolder.getHeight() - contentBounds.getHeight()), jmin (0, -(pos.y))));
127 
128     return p.transformedBy (contentComp->getTransform().inverted());
129 }
130 
setViewPosition(const int xPixelsOffset,const int yPixelsOffset)131 void Viewport::setViewPosition (const int xPixelsOffset, const int yPixelsOffset)
132 {
133     setViewPosition ({ xPixelsOffset, yPixelsOffset });
134 }
135 
setViewPosition(Point<int> newPosition)136 void Viewport::setViewPosition (Point<int> newPosition)
137 {
138     if (contentComp != nullptr)
139         contentComp->setTopLeftPosition (viewportPosToCompPos (newPosition));
140 }
141 
setViewPositionProportionately(const double x,const double y)142 void Viewport::setViewPositionProportionately (const double x, const double y)
143 {
144     if (contentComp != nullptr)
145         setViewPosition (jmax (0, roundToInt (x * (contentComp->getWidth()  - getWidth()))),
146                          jmax (0, roundToInt (y * (contentComp->getHeight() - getHeight()))));
147 }
148 
autoScroll(const int mouseX,const int mouseY,const int activeBorderThickness,const int maximumSpeed)149 bool Viewport::autoScroll (const int mouseX, const int mouseY, const int activeBorderThickness, const int maximumSpeed)
150 {
151     if (contentComp != nullptr)
152     {
153         int dx = 0, dy = 0;
154 
155         if (getHorizontalScrollBar().isVisible() || canScrollHorizontally())
156         {
157             if (mouseX < activeBorderThickness)
158                 dx = activeBorderThickness - mouseX;
159             else if (mouseX >= contentHolder.getWidth() - activeBorderThickness)
160                 dx = (contentHolder.getWidth() - activeBorderThickness) - mouseX;
161 
162             if (dx < 0)
163                 dx = jmax (dx, -maximumSpeed, contentHolder.getWidth() - contentComp->getRight());
164             else
165                 dx = jmin (dx, maximumSpeed, -contentComp->getX());
166         }
167 
168         if (getVerticalScrollBar().isVisible() || canScrollVertically())
169         {
170             if (mouseY < activeBorderThickness)
171                 dy = activeBorderThickness - mouseY;
172             else if (mouseY >= contentHolder.getHeight() - activeBorderThickness)
173                 dy = (contentHolder.getHeight() - activeBorderThickness) - mouseY;
174 
175             if (dy < 0)
176                 dy = jmax (dy, -maximumSpeed, contentHolder.getHeight() - contentComp->getBottom());
177             else
178                 dy = jmin (dy, maximumSpeed, -contentComp->getY());
179         }
180 
181         if (dx != 0 || dy != 0)
182         {
183             contentComp->setTopLeftPosition (contentComp->getX() + dx,
184                                              contentComp->getY() + dy);
185 
186             return true;
187         }
188     }
189 
190     return false;
191 }
192 
componentMovedOrResized(Component &,bool,bool)193 void Viewport::componentMovedOrResized (Component&, bool, bool)
194 {
195     updateVisibleArea();
196 }
197 
198 //==============================================================================
199 typedef AnimatedPosition<AnimatedPositionBehaviours::ContinuousWithMomentum> ViewportDragPosition;
200 
201 struct Viewport::DragToScrollListener   : private MouseListener,
202                                           private ViewportDragPosition::Listener
203 {
DragToScrollListenerjuce::Viewport::DragToScrollListener204     DragToScrollListener (Viewport& v)  : viewport (v)
205     {
206         viewport.contentHolder.addMouseListener (this, true);
207         offsetX.addListener (this);
208         offsetY.addListener (this);
209         offsetX.behaviour.setMinimumVelocity (60);
210         offsetY.behaviour.setMinimumVelocity (60);
211     }
212 
~DragToScrollListenerjuce::Viewport::DragToScrollListener213     ~DragToScrollListener() override
214     {
215         viewport.contentHolder.removeMouseListener (this);
216         Desktop::getInstance().removeGlobalMouseListener (this);
217     }
218 
positionChangedjuce::Viewport::DragToScrollListener219     void positionChanged (ViewportDragPosition&, double) override
220     {
221         viewport.setViewPosition (originalViewPos - Point<int> ((int) offsetX.getPosition(),
222                                                                 (int) offsetY.getPosition()));
223     }
224 
mouseDownjuce::Viewport::DragToScrollListener225     void mouseDown (const MouseEvent&) override
226     {
227         if (! isGlobalMouseListener)
228         {
229             offsetX.setPosition (offsetX.getPosition());
230             offsetY.setPosition (offsetY.getPosition());
231 
232             // switch to a global mouse listener so we still receive mouseUp events
233             // if the original event component is deleted
234             viewport.contentHolder.removeMouseListener (this);
235             Desktop::getInstance().addGlobalMouseListener (this);
236 
237             isGlobalMouseListener = true;
238         }
239     }
240 
mouseDragjuce::Viewport::DragToScrollListener241     void mouseDrag (const MouseEvent& e) override
242     {
243         if (Desktop::getInstance().getNumDraggingMouseSources() == 1 && ! doesMouseEventComponentBlockViewportDrag (e.eventComponent))
244         {
245             auto totalOffset = e.getOffsetFromDragStart().toFloat();
246 
247             if (! isDragging && totalOffset.getDistanceFromOrigin() > 8.0f)
248             {
249                 isDragging = true;
250 
251                 originalViewPos = viewport.getViewPosition();
252                 offsetX.setPosition (0.0);
253                 offsetX.beginDrag();
254                 offsetY.setPosition (0.0);
255                 offsetY.beginDrag();
256             }
257 
258             if (isDragging)
259             {
260                 offsetX.drag (totalOffset.x);
261                 offsetY.drag (totalOffset.y);
262             }
263         }
264     }
265 
mouseUpjuce::Viewport::DragToScrollListener266     void mouseUp (const MouseEvent&) override
267     {
268         if (isGlobalMouseListener && Desktop::getInstance().getNumDraggingMouseSources() == 0)
269             endDragAndClearGlobalMouseListener();
270     }
271 
endDragAndClearGlobalMouseListenerjuce::Viewport::DragToScrollListener272     void endDragAndClearGlobalMouseListener()
273     {
274         offsetX.endDrag();
275         offsetY.endDrag();
276         isDragging = false;
277 
278         viewport.contentHolder.addMouseListener (this, true);
279         Desktop::getInstance().removeGlobalMouseListener (this);
280 
281         isGlobalMouseListener = false;
282     }
283 
doesMouseEventComponentBlockViewportDragjuce::Viewport::DragToScrollListener284     bool doesMouseEventComponentBlockViewportDrag (const Component* eventComp)
285     {
286         for (auto c = eventComp; c != nullptr && c != &viewport; c = c->getParentComponent())
287             if (c->getViewportIgnoreDragFlag())
288                 return true;
289 
290         return false;
291     }
292 
293     Viewport& viewport;
294     ViewportDragPosition offsetX, offsetY;
295     Point<int> originalViewPos;
296     bool isDragging = false;
297     bool isGlobalMouseListener = false;
298 
299     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragToScrollListener)
300 };
301 
setScrollOnDragEnabled(bool shouldScrollOnDrag)302 void Viewport::setScrollOnDragEnabled (bool shouldScrollOnDrag)
303 {
304     if (isScrollOnDragEnabled() != shouldScrollOnDrag)
305     {
306         if (shouldScrollOnDrag)
307             dragToScrollListener.reset (new DragToScrollListener (*this));
308         else
309             dragToScrollListener.reset();
310     }
311 }
312 
isScrollOnDragEnabled() const313 bool Viewport::isScrollOnDragEnabled() const noexcept
314 {
315     return dragToScrollListener != nullptr;
316 }
317 
isCurrentlyScrollingOnDrag() const318 bool Viewport::isCurrentlyScrollingOnDrag() const noexcept
319 {
320     return dragToScrollListener != nullptr && dragToScrollListener->isDragging;
321 }
322 
323 //==============================================================================
lookAndFeelChanged()324 void Viewport::lookAndFeelChanged()
325 {
326     if (! customScrollBarThickness)
327     {
328         scrollBarThickness = getLookAndFeel().getDefaultScrollbarWidth();
329         resized();
330     }
331 }
332 
resized()333 void Viewport::resized()
334 {
335     updateVisibleArea();
336 }
337 
338 //==============================================================================
updateVisibleArea()339 void Viewport::updateVisibleArea()
340 {
341     auto scrollbarWidth = getScrollBarThickness();
342     const bool canShowAnyBars = getWidth() > scrollbarWidth && getHeight() > scrollbarWidth;
343     const bool canShowHBar = showHScrollbar && canShowAnyBars;
344     const bool canShowVBar = showVScrollbar && canShowAnyBars;
345 
346     bool hBarVisible = false, vBarVisible = false;
347     Rectangle<int> contentArea;
348 
349     for (int i = 3; --i >= 0;)
350     {
351         hBarVisible = canShowHBar && ! getHorizontalScrollBar().autoHides();
352         vBarVisible = canShowVBar && ! getVerticalScrollBar().autoHides();
353         contentArea = getLocalBounds();
354 
355         if (contentComp != nullptr && ! contentArea.contains (contentComp->getBounds()))
356         {
357             hBarVisible = canShowHBar && (hBarVisible || contentComp->getX() < 0 || contentComp->getRight() > contentArea.getWidth());
358             vBarVisible = canShowVBar && (vBarVisible || contentComp->getY() < 0 || contentComp->getBottom() > contentArea.getHeight());
359 
360             if (vBarVisible)
361                 contentArea.setWidth (getWidth() - scrollbarWidth);
362 
363             if (hBarVisible)
364                 contentArea.setHeight (getHeight() - scrollbarWidth);
365 
366             if (! contentArea.contains (contentComp->getBounds()))
367             {
368                 hBarVisible = canShowHBar && (hBarVisible || contentComp->getRight() > contentArea.getWidth());
369                 vBarVisible = canShowVBar && (vBarVisible || contentComp->getBottom() > contentArea.getHeight());
370             }
371         }
372 
373         if (vBarVisible)  contentArea.setWidth  (getWidth()  - scrollbarWidth);
374         if (hBarVisible)  contentArea.setHeight (getHeight() - scrollbarWidth);
375 
376         if (! vScrollbarRight  && vBarVisible)
377             contentArea.setX (scrollbarWidth);
378 
379         if (! hScrollbarBottom && hBarVisible)
380             contentArea.setY (scrollbarWidth);
381 
382         if (contentComp == nullptr)
383         {
384             contentHolder.setBounds (contentArea);
385             break;
386         }
387 
388         auto oldContentBounds = contentComp->getBounds();
389         contentHolder.setBounds (contentArea);
390 
391         // If the content has changed its size, that might affect our scrollbars, so go round again and re-calculate..
392         if (oldContentBounds == contentComp->getBounds())
393             break;
394     }
395 
396     Rectangle<int> contentBounds;
397 
398     if (auto cc = contentComp.get())
399         contentBounds = contentHolder.getLocalArea (cc, cc->getLocalBounds());
400 
401     auto visibleOrigin = -contentBounds.getPosition();
402 
403     auto& hbar = getHorizontalScrollBar();
404     auto& vbar = getVerticalScrollBar();
405 
406     hbar.setBounds (contentArea.getX(), hScrollbarBottom ? contentArea.getHeight() : 0, contentArea.getWidth(), scrollbarWidth);
407     hbar.setRangeLimits (0.0, contentBounds.getWidth());
408     hbar.setCurrentRange (visibleOrigin.x, contentArea.getWidth());
409     hbar.setSingleStepSize (singleStepX);
410 
411     if (canShowHBar && ! hBarVisible)
412         visibleOrigin.setX (0);
413 
414     vbar.setBounds (vScrollbarRight ? contentArea.getWidth() : 0, contentArea.getY(), scrollbarWidth, contentArea.getHeight());
415     vbar.setRangeLimits (0.0, contentBounds.getHeight());
416     vbar.setCurrentRange (visibleOrigin.y, contentArea.getHeight());
417     vbar.setSingleStepSize (singleStepY);
418 
419     if (canShowVBar && ! vBarVisible)
420         visibleOrigin.setY (0);
421 
422     // Force the visibility *after* setting the ranges to avoid flicker caused by edge conditions in the numbers.
423     hbar.setVisible (hBarVisible);
424     vbar.setVisible (vBarVisible);
425 
426     if (contentComp != nullptr)
427     {
428         auto newContentCompPos = viewportPosToCompPos (visibleOrigin);
429 
430         if (contentComp->getBounds().getPosition() != newContentCompPos)
431         {
432             contentComp->setTopLeftPosition (newContentCompPos);  // (this will re-entrantly call updateVisibleArea again)
433             return;
434         }
435     }
436 
437     const Rectangle<int> visibleArea (visibleOrigin.x, visibleOrigin.y,
438                                       jmin (contentBounds.getWidth()  - visibleOrigin.x, contentArea.getWidth()),
439                                       jmin (contentBounds.getHeight() - visibleOrigin.y, contentArea.getHeight()));
440 
441     if (lastVisibleArea != visibleArea)
442     {
443         lastVisibleArea = visibleArea;
444         visibleAreaChanged (visibleArea);
445     }
446 
447     hbar.handleUpdateNowIfNeeded();
448     vbar.handleUpdateNowIfNeeded();
449 }
450 
451 //==============================================================================
setSingleStepSizes(const int stepX,const int stepY)452 void Viewport::setSingleStepSizes (const int stepX, const int stepY)
453 {
454     if (singleStepX != stepX || singleStepY != stepY)
455     {
456         singleStepX = stepX;
457         singleStepY = stepY;
458         updateVisibleArea();
459     }
460 }
461 
setScrollBarsShown(const bool showVerticalScrollbarIfNeeded,const bool showHorizontalScrollbarIfNeeded,const bool allowVerticalScrollingWithoutScrollbar,const bool allowHorizontalScrollingWithoutScrollbar)462 void Viewport::setScrollBarsShown (const bool showVerticalScrollbarIfNeeded,
463                                    const bool showHorizontalScrollbarIfNeeded,
464                                    const bool allowVerticalScrollingWithoutScrollbar,
465                                    const bool allowHorizontalScrollingWithoutScrollbar)
466 {
467     allowScrollingWithoutScrollbarV = allowVerticalScrollingWithoutScrollbar;
468     allowScrollingWithoutScrollbarH = allowHorizontalScrollingWithoutScrollbar;
469 
470     if (showVScrollbar != showVerticalScrollbarIfNeeded
471          || showHScrollbar != showHorizontalScrollbarIfNeeded)
472     {
473         showVScrollbar = showVerticalScrollbarIfNeeded;
474         showHScrollbar = showHorizontalScrollbarIfNeeded;
475         updateVisibleArea();
476     }
477 }
478 
setScrollBarThickness(const int thickness)479 void Viewport::setScrollBarThickness (const int thickness)
480 {
481     int newThickness;
482 
483     // To stay compatible with the previous code: use the
484     // default thickness if thickness parameter is zero
485     // or negative
486     if (thickness <= 0)
487     {
488         customScrollBarThickness = false;
489         newThickness = getLookAndFeel().getDefaultScrollbarWidth();
490     }
491     else
492     {
493         customScrollBarThickness = true;
494         newThickness = thickness;
495     }
496 
497     if (scrollBarThickness != newThickness)
498     {
499         scrollBarThickness = newThickness;
500         updateVisibleArea();
501     }
502 }
503 
getScrollBarThickness() const504 int Viewport::getScrollBarThickness() const
505 {
506     return scrollBarThickness;
507 }
508 
scrollBarMoved(ScrollBar * scrollBarThatHasMoved,double newRangeStart)509 void Viewport::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart)
510 {
511     auto newRangeStartInt = roundToInt (newRangeStart);
512 
513     if (scrollBarThatHasMoved == horizontalScrollBar.get())
514     {
515         setViewPosition (newRangeStartInt, getViewPositionY());
516     }
517     else if (scrollBarThatHasMoved == verticalScrollBar.get())
518     {
519         setViewPosition (getViewPositionX(), newRangeStartInt);
520     }
521 }
522 
mouseWheelMove(const MouseEvent & e,const MouseWheelDetails & wheel)523 void Viewport::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
524 {
525     if (! useMouseWheelMoveIfNeeded (e, wheel))
526         Component::mouseWheelMove (e, wheel);
527 }
528 
rescaleMouseWheelDistance(float distance,int singleStepSize)529 static int rescaleMouseWheelDistance (float distance, int singleStepSize) noexcept
530 {
531     if (distance == 0.0f)
532         return 0;
533 
534     distance *= 14.0f * (float) singleStepSize;
535 
536     return roundToInt (distance < 0 ? jmin (distance, -1.0f)
537                                     : jmax (distance,  1.0f));
538 }
539 
useMouseWheelMoveIfNeeded(const MouseEvent & e,const MouseWheelDetails & wheel)540 bool Viewport::useMouseWheelMoveIfNeeded (const MouseEvent& e, const MouseWheelDetails& wheel)
541 {
542     if (! (e.mods.isAltDown() || e.mods.isCtrlDown() || e.mods.isCommandDown()))
543     {
544         const bool canScrollVert = (allowScrollingWithoutScrollbarV || getVerticalScrollBar().isVisible());
545         const bool canScrollHorz = (allowScrollingWithoutScrollbarH || getHorizontalScrollBar().isVisible());
546 
547         if (canScrollHorz || canScrollVert)
548         {
549             auto deltaX = rescaleMouseWheelDistance (wheel.deltaX, singleStepX);
550             auto deltaY = rescaleMouseWheelDistance (wheel.deltaY, singleStepY);
551 
552             auto pos = getViewPosition();
553 
554             if (deltaX != 0 && deltaY != 0 && canScrollHorz && canScrollVert)
555             {
556                 pos.x -= deltaX;
557                 pos.y -= deltaY;
558             }
559             else if (canScrollHorz && (deltaX != 0 || e.mods.isShiftDown() || ! canScrollVert))
560             {
561                 pos.x -= deltaX != 0 ? deltaX : deltaY;
562             }
563             else if (canScrollVert && deltaY != 0)
564             {
565                 pos.y -= deltaY;
566             }
567 
568             if (pos != getViewPosition())
569             {
570                 setViewPosition (pos);
571                 return true;
572             }
573         }
574     }
575 
576     return false;
577 }
578 
isUpDownKeyPress(const KeyPress & key)579 static bool isUpDownKeyPress (const KeyPress& key)
580 {
581     return key == KeyPress::upKey
582         || key == KeyPress::downKey
583         || key == KeyPress::pageUpKey
584         || key == KeyPress::pageDownKey
585         || key == KeyPress::homeKey
586         || key == KeyPress::endKey;
587 }
588 
isLeftRightKeyPress(const KeyPress & key)589 static bool isLeftRightKeyPress (const KeyPress& key)
590 {
591     return key == KeyPress::leftKey
592         || key == KeyPress::rightKey;
593 }
594 
keyPressed(const KeyPress & key)595 bool Viewport::keyPressed (const KeyPress& key)
596 {
597     const bool isUpDownKey = isUpDownKeyPress (key);
598 
599     if (getVerticalScrollBar().isVisible() && isUpDownKey)
600         return getVerticalScrollBar().keyPressed (key);
601 
602     const bool isLeftRightKey = isLeftRightKeyPress (key);
603 
604     if (getHorizontalScrollBar().isVisible() && (isUpDownKey || isLeftRightKey))
605         return getHorizontalScrollBar().keyPressed (key);
606 
607     return false;
608 }
609 
respondsToKey(const KeyPress & key)610 bool Viewport::respondsToKey (const KeyPress& key)
611 {
612     return isUpDownKeyPress (key) || isLeftRightKeyPress (key);
613 }
614 
createScrollBarComponent(bool isVertical)615 ScrollBar* Viewport::createScrollBarComponent (bool isVertical)
616 {
617     return new ScrollBar (isVertical);
618 }
619 
setScrollBarPosition(bool verticalScrollbarOnRight,bool horizontalScrollbarAtBottom)620 void Viewport::setScrollBarPosition (bool verticalScrollbarOnRight,
621                                      bool horizontalScrollbarAtBottom)
622 {
623     vScrollbarRight  = verticalScrollbarOnRight;
624     hScrollbarBottom = horizontalScrollbarAtBottom;
625 
626     resized();
627 }
628 
629 } // namespace juce
630