1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4 
5 #include "cscrollview.h"
6 #include "cvstguitimer.h"
7 #include "cdrawcontext.h"
8 #include "cframe.h"
9 #include "dragging.h"
10 #include "controls/cscrollbar.h"
11 #include <cmath>
12 
13 /// @cond ignore
14 namespace VSTGUI {
15 
16 // CScrollContainer is private
17 //-----------------------------------------------------------------------------
18 class CScrollContainer : public CViewContainer
19 //-----------------------------------------------------------------------------
20 {
21 public:
22 	CScrollContainer (const CRect &size, const CRect &containerSize);
23 	CScrollContainer (const CScrollContainer& v);
24 	~CScrollContainer () override = default;
25 
26 	void setScrollOffset (CPoint offset, bool withRedraw = false);
getScrollOffset(CPoint & off) const27 	void getScrollOffset (CPoint& off) const { off = offset; }
getScrollOffset() const28 	const CPoint& getScrollOffset () const { return offset; }
29 
getContainerSize() const30 	CRect getContainerSize () const { return containerSize; }
31 	void setContainerSize (const CRect& cs);
32 
33 	bool isDirty () const override;
34 
setAutoDragScroll(bool state)35 	void setAutoDragScroll (bool state) { autoDragScroll = state; }
36 
37 	bool attached (CView* parent) override;
38 	CMessageResult notify (CBaseObject* sender, IdStringPtr message) override;
39 
40 	SharedPointer<IDropTarget> getDropTarget () override;
41 	void onDragMove (CPoint where);
42 
43 	CLASS_METHODS(CScrollContainer, CViewContainer)
44 //-----------------------------------------------------------------------------
45 protected:
46 	struct DropTarget : public IDropTarget, public NonAtomicReferenceCounted
47 	{
DropTargetVSTGUI::CScrollContainer::DropTarget48 		DropTarget (CScrollContainer* scrollContainer, SharedPointer<IDropTarget>&& parent)
49 		: scrollContainer (scrollContainer), parent (std::move (parent))
50 		{
51 		}
52 
onDragEnterVSTGUI::CScrollContainer::DropTarget53 		DragOperation onDragEnter (DragEventData data) override
54 		{
55 			return parent->onDragEnter (data);
56 		}
onDragMoveVSTGUI::CScrollContainer::DropTarget57 		DragOperation onDragMove (DragEventData data) override
58 		{
59 			scrollContainer->onDragMove (data.pos);
60 			return parent->onDragMove (data);
61 		}
onDragLeaveVSTGUI::CScrollContainer::DropTarget62 		void onDragLeave (DragEventData data) override
63 		{
64 			return parent->onDragLeave (data);
65 		}
onDropVSTGUI::CScrollContainer::DropTarget66 		bool onDrop (DragEventData data) override
67 		{
68 			return parent->onDrop (data);
69 		}
70 
71 		CScrollContainer* scrollContainer;
72 		SharedPointer<IDropTarget> parent;
73 	};
74 
75 	bool getScrollValue (const CPoint& where, float& x, float& y);
76 
77 	CRect containerSize;
78 	CPoint offset;
79 	bool autoDragScroll;
80 	bool inScrolling;
81 };
82 
83 //-----------------------------------------------------------------------------
84 //-----------------------------------------------------------------------------
85 //-----------------------------------------------------------------------------
CScrollContainer(const CRect & size,const CRect & containerSize)86 CScrollContainer::CScrollContainer (const CRect &size, const CRect &containerSize)
87 : CViewContainer (size)
88 , containerSize (containerSize)
89 , offset (CPoint (0, 0))
90 , autoDragScroll (false)
91 , inScrolling (false)
92 {
93 	setTransparency (true);
94 }
95 
96 //-----------------------------------------------------------------------------
CScrollContainer(const CScrollContainer & v)97 CScrollContainer::CScrollContainer (const CScrollContainer& v)
98 : CViewContainer (v)
99 , containerSize (v.containerSize)
100 , offset (v.offset)
101 , autoDragScroll (v.autoDragScroll)
102 , inScrolling (false)
103 {
104 }
105 
106 //-----------------------------------------------------------------------------
setContainerSize(const CRect & cs)107 void CScrollContainer::setContainerSize (const CRect& cs)
108 {
109 	containerSize = cs;
110 	setScrollOffset (offset, false);
111 }
112 
113 //-----------------------------------------------------------------------------
setScrollOffset(CPoint newOffset,bool redraw)114 void CScrollContainer::setScrollOffset (CPoint newOffset, bool redraw)
115 {
116 	newOffset.x = floor (newOffset.x + 0.5);
117 	newOffset.y = floor (newOffset.y + 0.5);
118 	if (containerSize.getWidth () >= getViewSize ().getWidth ())
119 	{
120 		if (newOffset.x < containerSize.left - (containerSize.getWidth () - getViewSize ().getWidth ()))
121 			newOffset.x = containerSize.left - (containerSize.getWidth () - getViewSize ().getWidth ());
122 	}
123 	else
124 	{
125 		if (newOffset.x < containerSize.left - containerSize.getWidth ())
126 			newOffset.x = containerSize.left - containerSize.getWidth ();
127 	}
128 	if (newOffset.x > containerSize.right)
129 		newOffset.x = containerSize.right;
130 	if (newOffset.y < containerSize.top)
131 		newOffset.y = containerSize.top;
132 	if (newOffset.y > containerSize.bottom)
133 		newOffset.y = containerSize.bottom;
134 	CPoint diff ((int32_t)(newOffset.x - offset.x), (int32_t)(offset.y - newOffset.y));
135 	if (diff.x == 0 && diff.y == 0)
136 		return;
137 	offset = newOffset;
138 	inScrolling = true;
139 	for (const auto& pV : getChildren ())
140 	{
141 		CRect r = pV->getViewSize ();
142 		CRect mr = pV->getMouseableArea ();
143 		r.offset (diff.x , diff.y);
144 		pV->setViewSize (r, false);
145 		mr.offset (diff.x , diff.y);
146 		pV->setMouseableArea (mr);
147 	}
148 	inScrolling = false;
149 	if (!isAttached ())
150 		return;
151 
152 	if (getTransparency ())
153 	{
154 		invalid ();
155 	}
156 	else
157 	{
158 		CRect scrollRect (0, 0, getViewSize ().getWidth (), getViewSize ().getHeight ());
159 		CPoint p;
160 		localToFrame (p);
161 		scrollRect.offset (p.x, p.y);
162 		CRect visibleRect = getVisibleSize (CRect (0, 0, getViewSize ().getWidth (), getViewSize ().getHeight ()));
163 		visibleRect.offset (p.x, p.y);
164 		scrollRect.bound (visibleRect);
165 
166 		CPoint distance (diff.x, diff.y);
167 		if (distance.x > 0)
168 			scrollRect.right -= distance.x;
169 		else if (distance.x < 0)
170 			scrollRect.left -= distance.x;
171 		if (distance.y > 0)
172 			scrollRect.bottom -= distance.y;
173 		else if (distance.y < 0)
174 			scrollRect.top -= distance.y;
175 		getFrame ()->scrollRect (scrollRect, distance);
176 	}
177 }
178 
179 //-----------------------------------------------------------------------------
isDirty() const180 bool CScrollContainer::isDirty () const
181 {
182 	if (CView::isDirty ())
183 		return true;
184 
185 	for (const auto& pV : getChildren ())
186 	{
187 		if (pV->isDirty () && pV->isVisible ())
188 		{
189 			CRect r = pV->getVisibleViewSize ();
190 			if (r.getWidth () > 0 && r.getHeight () > 0)
191 				return true;
192 			else
193 				pV->setDirty (false);
194 		}
195 	}
196 	return false;
197 }
198 
199 //-----------------------------------------------------------------------------
getScrollValue(const CPoint & where,float & x,float & y)200 bool CScrollContainer::getScrollValue (const CPoint& where, float& x, float& y)
201 {
202 	const CCoord kWidth = 10;
203 	x = 0.f;
204 	y = 0.f;
205 	if (where.x <= getViewSize ().left + kWidth)
206 	{
207 		x = (float)(where.x - (getViewSize ().left + kWidth));
208 	}
209 	else if (where.x >= getViewSize ().right - kWidth)
210 	{
211 		x = (float)(where.x - (getViewSize ().right - kWidth));
212 	}
213 	if (where.y <= getViewSize ().top + kWidth)
214 	{
215 		y = (float)(where.y - (getViewSize ().top + kWidth));
216 	}
217 	else if (where.y >= getViewSize ().bottom - kWidth)
218 	{
219 		y = (float)(where.y - (getViewSize ().bottom - kWidth));
220 	}
221 	return (x != 0.f || y != 0.f);
222 }
223 
224 //-----------------------------------------------------------------------------
getDropTarget()225 SharedPointer<IDropTarget> CScrollContainer::getDropTarget ()
226 {
227 	auto dropTarget = CViewContainer::getDropTarget ();
228 	if (autoDragScroll)
229 	{
230 		return makeOwned<DropTarget> (this, std::move (dropTarget));
231 	}
232 	return dropTarget;
233 }
234 
235 //-----------------------------------------------------------------------------
onDragMove(CPoint where)236 void CScrollContainer::onDragMove (CPoint where)
237 {
238 	float x, y;
239 	if (getScrollValue (where, x, y))
240 	{
241 		if (auto* scrollView = static_cast<CScrollView*> (getParentView ()))
242 		{
243 			CRect r (getViewSize ());
244 			r.originize ();
245 			r.offset (x, y);
246 			scrollView->makeRectVisible (r);
247 		}
248 	}
249 }
250 
251 //-----------------------------------------------------------------------------
attached(CView * parent)252 bool CScrollContainer::attached (CView* parent)
253 {
254 	bool result = CViewContainer::attached (parent);
255 	if (getNbViews () == 1)
256 	{
257 		if (CView* view = getView (0))
258 		{
259 			const CRect& r (view->getViewSize ());
260 			CRect newContainerSize (containerSize);
261 			newContainerSize.setWidth (r.getWidth ());
262 			newContainerSize.setHeight (r.getHeight ());
263 			if (newContainerSize != containerSize)
264 			{
265 				auto* scrollView = static_cast<CScrollView*> (getParentView ());
266 				if (scrollView)
267 					scrollView->setContainerSize (newContainerSize);
268 			}
269 		}
270 	}
271 	return result;
272 }
273 
274 //-----------------------------------------------------------------------------
notify(CBaseObject * sender,IdStringPtr message)275 CMessageResult CScrollContainer::notify (CBaseObject* sender, IdStringPtr message)
276 {
277 	if (message == kMsgViewSizeChanged && !inScrolling)
278 	{
279 		uint32_t numSubViews = getNbViews ();
280 		auto* view = static_cast<CView*> (sender);
281 		if (numSubViews == 1 && view && isChild (view))
282 		{
283 			const CRect& r (view->getViewSize ());
284 			CRect newContainerSize (containerSize);
285 			newContainerSize.setWidth (r.getWidth ());
286 			newContainerSize.setHeight (r.getHeight ());
287 			if (newContainerSize != containerSize)
288 			{
289 				auto* scrollView = static_cast<CScrollView*> (getParentView ());
290 				if (scrollView)
291 					scrollView->setContainerSize (newContainerSize);
292 			}
293 		}
294 	}
295 	return getParentView () ? getParentView ()->notify (sender, message) : kMessageUnknown;
296 }
297 
298 /// @endcond
299 
300 //-----------------------------------------------------------------------------
301 //-----------------------------------------------------------------------------
302 //-----------------------------------------------------------------------------
CScrollView(const CRect & size,const CRect & containerSize,int32_t style,CCoord scrollbarWidth,CBitmap * pBackground)303 CScrollView::CScrollView (const CRect &size, const CRect &containerSize, int32_t style, CCoord scrollbarWidth, CBitmap* pBackground)
304 : CViewContainer (size)
305 , sc (nullptr)
306 , vsb (nullptr)
307 , hsb (nullptr)
308 , containerSize (containerSize)
309 , scrollbarWidth (scrollbarWidth)
310 , style (style)
311 , activeScrollbarStyle (0)
312 {
313 	if (pBackground)
314 		setBackground(pBackground);
315 	recalculateSubViews ();
316 }
317 
318 //-----------------------------------------------------------------------------
CScrollView(const CScrollView & v)319 CScrollView::CScrollView (const CScrollView& v)
320 : CViewContainer (v)
321 , containerSize (v.containerSize)
322 , scrollbarWidth (v.scrollbarWidth)
323 , style (v.style)
324 , activeScrollbarStyle (v.activeScrollbarStyle)
325 {
326 	CViewContainer::removeAll ();
327 	if (activeScrollbarStyle & kHorizontalScrollbar && v.hsb)
328 	{
329 		hsb = static_cast<CScrollbar*> (v.hsb->newCopy ());
330 		hsb->setListener (this);
331 		CViewContainer::addView (hsb, nullptr);
332 	}
333 	if (activeScrollbarStyle & kVerticalScrollbar && v.vsb)
334 	{
335 		vsb = static_cast<CScrollbar*> (v.vsb->newCopy ());
336 		vsb->setListener (this);
337 		CViewContainer::addView (vsb, nullptr);
338 	}
339 	sc = static_cast<CScrollContainer*> (v.sc->newCopy ());
340 	CViewContainer::addView (sc, nullptr);
341 }
342 
343 //-----------------------------------------------------------------------------
recalculateSubViews()344 void CScrollView::recalculateSubViews ()
345 {
346 	if (recalculateSubViewsRecursionGard)
347 		return;
348 	recalculateSubViewsRecursionGard = true;
349 	CRect scsize (containerSize.left, containerSize.top, getViewSize ().getWidth (), getViewSize ().getHeight ());
350 	if (!(style & kDontDrawFrame))
351 	{
352 		scsize.left++; scsize.top++;
353 		scsize.right-=1; scsize.bottom--;
354 	}
355 	if (style & kAutoHideScrollbars)
356 	{
357 		activeScrollbarStyle = 0;
358 		CRect r (scsize);
359 		if (style & kHorizontalScrollbar)
360 		{
361 			if (style & kVerticalScrollbar && r.getHeight () < containerSize.getHeight ())
362 			{
363 				activeScrollbarStyle |= kVerticalScrollbar;
364 				if (!(style & kOverlayScrollbars))
365 					r.right -= scrollbarWidth;
366 			}
367 			activeScrollbarStyle |= containerSize.getWidth () <= r.getWidth () ? 0 : kHorizontalScrollbar;
368 			if (!(style & kOverlayScrollbars))
369 				r.bottom -= scrollbarWidth;
370 			if (activeScrollbarStyle == kHorizontalScrollbar && style & kVerticalScrollbar && r.getHeight () < containerSize.getHeight ())
371 			{
372 				activeScrollbarStyle |= kVerticalScrollbar;
373 			}
374 		}
375 		else if (style & kVerticalScrollbar)
376 		{
377 			activeScrollbarStyle |= containerSize.getHeight () <= r.getHeight () ? 0 : kVerticalScrollbar;
378 		}
379 	}
380 	else
381 	{
382 		activeScrollbarStyle = (style & kHorizontalScrollbar) | (style & kVerticalScrollbar);
383 	}
384 
385 	if (activeScrollbarStyle & kHorizontalScrollbar)
386 	{
387 		CRect sbr (getViewSize ());
388 		sbr.originize ();
389 		sbr.top = sbr.bottom - scrollbarWidth;
390 		if (activeScrollbarStyle & kVerticalScrollbar)
391 		{
392 			if (hsb && (vsb && vsb->isVisible () == false))
393 				hsb->invalid ();
394 			sbr.right -= (scrollbarWidth - 1);
395 		}
396 		if (hsb)
397 		{
398 			hsb->setViewSize (sbr, true);
399 			hsb->setMouseableArea (sbr);
400 			hsb->setVisible (true);
401 		}
402 		else
403 		{
404 			hsb = new CScrollbar (sbr, this, kHSBTag, CScrollbar::kHorizontal, containerSize);
405 			hsb->setAutosizeFlags (kAutosizeLeft | kAutosizeRight | kAutosizeBottom);
406 			CViewContainer::addView (hsb, nullptr);
407 			hsb->registerViewListener (this);
408 		}
409 		if (!(style & kOverlayScrollbars))
410 			scsize.bottom = sbr.top;
411 		hsb->setOverlayStyle ((style & kOverlayScrollbars) ? true : false);
412 	}
413 	else if (hsb)
414 	{
415 		hsb->setVisible (false);
416 	}
417 	if (activeScrollbarStyle & kVerticalScrollbar)
418 	{
419 		CRect sbr (getViewSize ());
420 		sbr.originize ();
421 		sbr.left = sbr.right - scrollbarWidth;
422 		if (activeScrollbarStyle & kHorizontalScrollbar)
423 		{
424 			if (vsb && (hsb  && hsb->isVisible () == false))
425 				vsb->invalid ();
426 			sbr.bottom -= (scrollbarWidth - 1);
427 		}
428 		if (vsb)
429 		{
430 			vsb->setViewSize (sbr, true);
431 			vsb->setMouseableArea (sbr);
432 			vsb->setVisible (true);
433 		}
434 		else
435 		{
436 			vsb = new CScrollbar (sbr, this, kVSBTag, CScrollbar::kVertical, containerSize);
437 			vsb->setAutosizeFlags (kAutosizeTop | kAutosizeRight | kAutosizeBottom);
438 			CViewContainer::addView (vsb, nullptr);
439 			vsb->registerViewListener (this);
440 		}
441 		if (!(style & kOverlayScrollbars))
442 			scsize.right = sbr.left;
443 		vsb->setOverlayStyle ((style & kOverlayScrollbars) ? true : false);
444 	}
445 	else if (vsb)
446 	{
447 		vsb->setVisible (false);
448 	}
449 
450 	if (!sc)
451 	{
452 		sc = new CScrollContainer (scsize, containerSize);
453 		sc->setAutosizeFlags (kAutosizeAll);
454 		CViewContainer::addView (sc, CViewContainer::getView (0));
455 	}
456 	else
457 	{
458 		sc->setViewSize (scsize, true);
459 		sc->setMouseableArea (scsize);
460 	}
461 	sc->setAutoDragScroll ((style & kAutoDragScrolling) ? true : false);
462 	recalculateSubViewsRecursionGard = false;
463 }
464 
465 //-----------------------------------------------------------------------------
setViewSize(const CRect & rect,bool invalid)466 void CScrollView::setViewSize (const CRect &rect, bool invalid)
467 {
468 	bool autoHideScrollbars = (style & kAutoHideScrollbars) != 0;
469 	style &= ~ kAutoHideScrollbars;
470 	CViewContainer::setViewSize (rect, invalid);
471 	if (autoHideScrollbars)
472 		style |= kAutoHideScrollbars;
473 	setContainerSize (containerSize, true);
474 }
475 
476 //-----------------------------------------------------------------------------
setAutosizeFlags(int32_t flags)477 void CScrollView::setAutosizeFlags (int32_t flags)
478 {
479 	CViewContainer::setAutosizeFlags (flags);
480 	if (sc)
481 		sc->setAutosizeFlags (flags);
482 }
483 
484 //-----------------------------------------------------------------------------
setStyle(int32_t newStyle)485 void CScrollView::setStyle (int32_t newStyle)
486 {
487 	if (style != newStyle)
488 	{
489 		if ((style & kDontDrawFrame) != (newStyle & kDontDrawFrame))
490 			setBackgroundColorDrawStyle ((style & kDontDrawFrame) ? kDrawFilled : kDrawFilledAndStroked);
491 		style = newStyle;
492 		recalculateSubViews ();
493 	}
494 }
495 
496 //-----------------------------------------------------------------------------
setScrollbarWidth(CCoord width)497 void CScrollView::setScrollbarWidth (CCoord width)
498 {
499 	if (scrollbarWidth != width)
500 	{
501 		scrollbarWidth = width;
502 		recalculateSubViews ();
503 	}
504 }
505 
506 //-----------------------------------------------------------------------------
setContainerSize(const CRect & cs,bool keepVisibleArea)507 void CScrollView::setContainerSize (const CRect& cs, bool keepVisibleArea)
508 {
509 	CRect oldSize (containerSize);
510 	containerSize = cs;
511 	if (sc)
512 	{
513 		sc->setContainerSize (cs);
514 	}
515 	if (style & kAutoHideScrollbars)
516 		recalculateSubViews ();
517 	if (vsb)
518 	{
519 		CRect oldScrollSize = vsb->getScrollSize (oldScrollSize);
520 		float oldValue = vsb->getValue ();
521 		vsb->setScrollSize (cs);
522 		if (cs.getHeight () <= getViewSize ().getHeight ())
523 			vsb->setValue (0);
524 		else if (sc && keepVisibleArea && oldScrollSize.getHeight () != cs.getHeight ())
525 		{
526 			CRect vSize = sc->getViewSize ();
527 			float newValue = (float)(oldValue * ((float)(oldScrollSize.getHeight () - vSize.getHeight ()) / ((float)cs.getHeight () - vSize.getHeight ())));
528 			if (newValue > 1.f)
529 				newValue = 1.f;
530 			else if (newValue < 0.f)
531 				newValue = 0.f;
532 			vsb->setValue (newValue);
533 		}
534 		if (oldSize != containerSize)
535 			vsb->onVisualChange ();
536 		valueChanged (vsb);
537 	}
538 	if (hsb)
539 	{
540 		CRect oldScrollSize = hsb->getScrollSize (oldScrollSize);
541 		float oldValue = hsb->getValue ();
542 		hsb->setScrollSize (cs);
543 		if (cs.getWidth () <= getViewSize ().getWidth ())
544 			hsb->setValue (0);
545 		else if (sc && keepVisibleArea && oldScrollSize.getWidth () != cs.getWidth ())
546 		{
547 			CRect vSize = sc->getViewSize ();
548 			float newValue = (float)(oldValue * ((float)(oldScrollSize.getWidth () - vSize.getWidth ()) / ((float)cs.getWidth () - vSize.getWidth ())));
549 			if (newValue > 1.f)
550 				newValue = 1.f;
551 			else if (newValue < 0.f)
552 				newValue = 0.f;
553 			hsb->setValue (newValue);
554 		}
555 		if (oldSize != containerSize)
556 			hsb->onVisualChange ();
557 		valueChanged (hsb);
558 	}
559 }
560 
561 //-----------------------------------------------------------------------------
makeRectVisible(const CRect & rect)562 void CScrollView::makeRectVisible (const CRect& rect)
563 {
564 	CRect r (rect);
565 	const CPoint& scrollOffset = sc->getScrollOffset ();
566 	CPoint newOffset (scrollOffset);
567 	CRect vs = sc->getViewSize ();
568 	if (!(style & kDontDrawFrame))
569 	{
570 		vs.left--; //vs.top--;
571 		vs.right++; //vs.bottom++;
572 	}
573 	CRect cs (containerSize);
574 	cs.offset (-cs.left, -cs.top);
575 	cs.setWidth (vs.getWidth ());
576 	cs.setHeight (vs.getHeight ());
577 	if (r.top >= cs.top && r.bottom <= cs.bottom && r.left >= cs.left && r.right <= cs.right)
578 		return;
579 	if (r.top < cs.top)
580 	{
581 		newOffset.y -= (cs.top - r.top);
582 	}
583 	else if (r.bottom > cs.bottom)
584 	{
585 		newOffset.y += (r.bottom - cs.bottom);
586 	}
587 	if (r.left < cs.left)
588 	{
589 		newOffset.x -= (cs.left + r.left);
590 	}
591 	else if (r.right > cs.right && r.left != cs.left)
592 	{
593 		newOffset.x += (cs.right - r.right);
594 	}
595 	if (vsb && newOffset.y != scrollOffset.y)
596 	{
597 		if (containerSize.getHeight () == vs.getHeight ())
598 			vsb->setValue (0.f);
599 		else
600 			vsb->setValue ((float)(newOffset.y - vs.top) / (float)(containerSize.getHeight () - vs.getHeight ()));
601 		vsb->bounceValue ();
602 		vsb->onVisualChange ();
603 		vsb->invalid ();
604 		valueChanged (vsb);
605 	}
606 	if (hsb && newOffset.x != scrollOffset.x)
607 	{
608 		if (containerSize.getWidth () == vs.getWidth ())
609 			hsb->setValue (0.f);
610 		else
611 			hsb->setValue (-(float)(newOffset.x - vs.left) / (float)(containerSize.getWidth () - vs.getWidth ()));
612 		hsb->bounceValue ();
613 		hsb->onVisualChange ();
614 		hsb->invalid ();
615 		valueChanged (hsb);
616 	}
617 }
618 
619 //-----------------------------------------------------------------------------
resetScrollOffset()620 void CScrollView::resetScrollOffset ()
621 {
622 	if (vsb)
623 	{
624 		vsb->setValue (0);
625 		vsb->bounceValue ();
626 		vsb->onVisualChange ();
627 		vsb->invalid ();
628 		valueChanged (vsb);
629 	}
630 	if (hsb)
631 	{
632 		hsb->setValue (0);
633 		hsb->bounceValue ();
634 		hsb->onVisualChange ();
635 		hsb->invalid ();
636 		valueChanged (hsb);
637 	}
638 }
639 
640 //-----------------------------------------------------------------------------
getScrollOffset() const641 const CPoint& CScrollView::getScrollOffset () const
642 {
643 	return sc->getScrollOffset ();
644 }
645 
646 //-----------------------------------------------------------------------------
addView(CView * pView,CView * pBefore)647 bool CScrollView::addView (CView* pView, CView* pBefore)
648 {
649 	return sc->addView (pView, pBefore);
650 }
651 
652 //-----------------------------------------------------------------------------
removeView(CView * pView,bool withForget)653 bool CScrollView::removeView (CView *pView, bool withForget)
654 {
655 	return sc->removeView (pView, withForget);
656 }
657 
658 //-----------------------------------------------------------------------------
removeAll(bool withForget)659 bool CScrollView::removeAll (bool withForget)
660 {
661 	return sc->removeAll (withForget);
662 }
663 
664 //-----------------------------------------------------------------------------
getNbViews() const665 uint32_t CScrollView::getNbViews () const
666 {
667 	return sc->getNbViews ();
668 }
669 
670 //-----------------------------------------------------------------------------
getView(uint32_t index) const671 CView* CScrollView::getView (uint32_t index) const
672 {
673 	return sc->getView (index);
674 }
675 
676 //-----------------------------------------------------------------------------
changeViewZOrder(CView * view,uint32_t newIndex)677 bool CScrollView::changeViewZOrder (CView* view, uint32_t newIndex)
678 {
679 	return sc->changeViewZOrder (view, newIndex);
680 }
681 
682 //-----------------------------------------------------------------------------
setTransparency(bool val)683 void CScrollView::setTransparency (bool val)
684 {
685 	CViewContainer::setTransparency (val);
686 }
687 
688 //-----------------------------------------------------------------------------
setBackgroundColor(const CColor & color)689 void CScrollView::setBackgroundColor (const CColor& color)
690 {
691 	CViewContainer::setBackgroundColor (color);
692 }
693 
694 //-----------------------------------------------------------------------------
valueChanged(CControl * pControl)695 void CScrollView::valueChanged (CControl *pControl)
696 {
697 	if (sc)
698 	{
699 		float value = pControl->getValue ();
700 		int32_t tag = pControl->getTag ();
701 		CPoint offset;
702 		CRect vsize = sc->getViewSize ();
703 		CRect csize = sc->getContainerSize ();
704 		sc->getScrollOffset (offset);
705 
706 		switch (tag)
707 		{
708 			case kHSBTag:
709 			{
710 				if (csize.getWidth () > vsize.getWidth ())
711 				{
712 					offset.x = (int32_t) (csize.left - (csize.getWidth () - vsize.getWidth ()) * value);
713 					sc->setScrollOffset (offset, false);
714 				}
715 				else if (offset.x < 0)
716 				{
717 					offset.x = 0;
718 					sc->setScrollOffset (offset, false);
719 				}
720 				break;
721 			}
722 			case kVSBTag:
723 			{
724 				if (csize.getHeight () > vsize.getHeight ())
725 				{
726 					offset.y = (int32_t) (csize.top + (csize.getHeight () - vsize.getHeight ()) * value);
727 					sc->setScrollOffset (offset, false);
728 				}
729 				else if (offset.y > 0)
730 				{
731 					offset.y = 0;
732 					sc->setScrollOffset (offset, false);
733 				}
734 				break;
735 			}
736 		}
737 	}
738 }
739 
740 //-----------------------------------------------------------------------------
drawBackgroundRect(CDrawContext * pContext,const CRect & _updateRect)741 void CScrollView::drawBackgroundRect (CDrawContext *pContext, const CRect& _updateRect)
742 {
743 	CRect r (getViewSize ());
744 	r.originize ();
745 	CViewContainer::drawBackgroundRect (pContext, r);
746 }
747 
748 //-----------------------------------------------------------------------------
onWheel(const CPoint & where,const CMouseWheelAxis & axis,const float & distance,const CButtonState & buttons)749 bool CScrollView::onWheel (const CPoint &where, const CMouseWheelAxis &axis, const float &distance, const CButtonState &buttons)
750 {
751 	bool result = CViewContainer::onWheel (where, axis, distance, buttons);
752 	if (!result)
753 	{
754 		if (vsb && axis == kMouseWheelAxisY)
755 			result = vsb->onWheel (where, axis, distance, buttons);
756 		else if (hsb && axis == kMouseWheelAxisX)
757 			result = hsb->onWheel (where, axis, distance, buttons);
758 	}
759 	return result;
760 }
761 
762 //-----------------------------------------------------------------------------
notify(CBaseObject * sender,IdStringPtr message)763 CMessageResult CScrollView::notify (CBaseObject* sender, IdStringPtr message)
764 {
765 	if (message == kMsgNewFocusView && getStyle () & kFollowFocusView)
766 	{
767 		auto* focusView = static_cast<CView*> (sender);
768 		if (sc->isChild (focusView, true))
769 		{
770 			CRect r = focusView->getViewSize ();
771 			CPoint p;
772 			focusView->localToFrame (p);
773 			frameToLocal (p);
774 			r.offset (p.x, p.y);
775 			makeRectVisible (r);
776 		}
777 	}
778 	return CViewContainer::notify (sender, message);
779 }
780 
781 //-----------------------------------------------------------------------------
viewSizeChanged(CView * view,const CRect & oldSize)782 void CScrollView::viewSizeChanged (CView* view, const CRect& oldSize)
783 {
784 	if (view == hsb)
785 	{
786 		hsb->setScrollSize (containerSize);
787 		hsb->onVisualChange ();
788 	}
789 	else if (view == vsb)
790 	{
791 		vsb->setScrollSize (containerSize);
792 		vsb->onVisualChange ();
793 	}
794 }
795 
796 //-----------------------------------------------------------------------------
viewWillDelete(CView * view)797 void CScrollView::viewWillDelete (CView* view)
798 {
799 	if (view == hsb || view == vsb)
800 		view->unregisterViewListener (this);
801 }
802 
803 } // VSTGUI
804 
805