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