1 /*
2 * Copyright 2006-2009, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Ingo Weinhold <bonefish@cs.tu-berlin.de>
7 * Stephan Aßmus <superstippi@gmx.de>
8 */
9
10 #include "ScrollView.h"
11
12 #include <algorithm>
13 #include <stdio.h>
14 #include <string.h>
15
16 #include <Bitmap.h>
17 #ifdef __HAIKU__
18 # include <LayoutUtils.h>
19 #endif
20 #include <Message.h>
21 #include <ScrollBar.h>
22 #include <Window.h>
23
24 #include "Scrollable.h"
25 #include "ScrollCornerBitmaps.h"
26
27 using namespace std;
28
29 // #pragma mark - InternalScrollBar
30
31 class InternalScrollBar : public BScrollBar {
32 public:
33 InternalScrollBar(ScrollView* scrollView,
34 BRect frame,
35 orientation posture);
36 virtual ~InternalScrollBar();
37
38 virtual void ValueChanged(float value);
39
40 virtual void MouseDown(BPoint where);
41 virtual void MouseUp(BPoint where);
42
43 private:
44 ScrollView* fScrollView;
45 };
46
47 // constructor
InternalScrollBar(ScrollView * scrollView,BRect frame,orientation posture)48 InternalScrollBar::InternalScrollBar(ScrollView* scrollView, BRect frame,
49 orientation posture)
50 : BScrollBar(frame, NULL, NULL, 0, 0, posture),
51 fScrollView(scrollView)
52 {
53 }
54
55 // destructor
~InternalScrollBar()56 InternalScrollBar::~InternalScrollBar()
57 {
58 }
59
60 // ValueChanged
61 void
ValueChanged(float value)62 InternalScrollBar::ValueChanged(float value)
63 {
64 // Notify our parent scroll view. Note: the value already has changed,
65 // so that we can't check, if it really has changed.
66 if (fScrollView)
67 fScrollView->_ScrollValueChanged(this, value);
68 }
69
70 // MouseDown
71 void
MouseDown(BPoint where)72 InternalScrollBar::MouseDown(BPoint where)
73 {
74 if (fScrollView)
75 fScrollView->_SetScrolling(true);
76 BScrollBar::MouseDown(where);
77 }
78
79 // MouseUp
80 void
MouseUp(BPoint where)81 InternalScrollBar::MouseUp(BPoint where)
82 {
83 BScrollBar::MouseUp(where);
84 if (fScrollView)
85 fScrollView->_SetScrolling(false);
86 }
87
88
89
90 // #pragma mark -ScrollCorner
91
92 class ScrollCorner : public BView {
93 public:
94 ScrollCorner(ScrollView* scrollView);
95 virtual ~ScrollCorner();
96
97 virtual void MouseDown(BPoint point);
98 virtual void MouseUp(BPoint point);
99 virtual void MouseMoved(BPoint point, uint32 transit,
100 const BMessage* message);
101
102 virtual void Draw(BRect updateRect);
103 virtual void WindowActivated(bool active);
104
105 void SetActive(bool active);
IsActive() const106 inline bool IsActive() const
107 { return fState & STATE_ACTIVE; }
108
109 private:
110 ScrollView* fScrollView;
111 uint32 fState;
112 BPoint fStartPoint;
113 BPoint fStartScrollOffset;
114 BBitmap* fBitmaps[3];
115
IsEnabled() const116 inline bool IsEnabled() const
117 { return ((fState & STATE_ENABLED) ==
118 STATE_ENABLED); }
119
120 void SetDragging(bool dragging);
IsDragging() const121 inline bool IsDragging() const
122 { return (fState & STATE_DRAGGING); }
123
124 enum {
125 STATE_DRAGGING = 0x01,
126 STATE_WINDOW_ACTIVE = 0x02,
127 STATE_ACTIVE = 0x04,
128 STATE_ENABLED = STATE_WINDOW_ACTIVE | STATE_ACTIVE,
129 };
130 };
131
132 // constructor
ScrollCorner(ScrollView * scrollView)133 ScrollCorner::ScrollCorner(ScrollView* scrollView)
134 : BView(BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH - 1.0f, B_H_SCROLL_BAR_HEIGHT - 1.0f), NULL,
135 0, B_WILL_DRAW),
136 fScrollView(scrollView),
137 fState(0),
138 fStartPoint(0, 0),
139 fStartScrollOffset(0, 0)
140 {
141 SetViewColor(B_TRANSPARENT_32_BIT);
142
143 fBitmaps[0] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth - 1,
144 sBitmapHeight - 1), sColorSpace);
145 char* bits = (char*)fBitmaps[0]->Bits();
146 int32 bpr = fBitmaps[0]->BytesPerRow();
147 for (int i = 0; i < sBitmapHeight; i++, bits += bpr) {
148 memcpy(bits, &sScrollCornerNormalBits[i * sBitmapHeight * 4],
149 sBitmapWidth * 4);
150 }
151
152 fBitmaps[1] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth - 1,
153 sBitmapHeight - 1), sColorSpace);
154 bits = (char*)fBitmaps[1]->Bits();
155 bpr = fBitmaps[1]->BytesPerRow();
156 for (int i = 0; i < sBitmapHeight; i++, bits += bpr) {
157 memcpy(bits, &sScrollCornerPushedBits[i * sBitmapHeight * 4],
158 sBitmapWidth * 4);
159 }
160
161 fBitmaps[2] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth - 1,
162 sBitmapHeight - 1), sColorSpace);
163 bits = (char*)fBitmaps[2]->Bits();
164 bpr = fBitmaps[2]->BytesPerRow();
165 for (int i = 0; i < sBitmapHeight; i++, bits += bpr) {
166 memcpy(bits, &sScrollCornerDisabledBits[i * sBitmapHeight * 4],
167 sBitmapWidth * 4);
168 }
169 }
170
171 // destructor
~ScrollCorner()172 ScrollCorner::~ScrollCorner()
173 {
174 for (int i = 0; i < 3; i++)
175 delete fBitmaps[i];
176 }
177
178 // MouseDown
179 void
MouseDown(BPoint point)180 ScrollCorner::MouseDown(BPoint point)
181 {
182 BView::MouseDown(point);
183 uint32 buttons = 0;
184 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
185 if (buttons & B_PRIMARY_MOUSE_BUTTON) {
186 SetMouseEventMask(B_POINTER_EVENTS);
187 if (fScrollView && IsEnabled() && Bounds().Contains(point)) {
188 SetDragging(true);
189 fStartPoint = point;
190 fStartScrollOffset = fScrollView->ScrollOffset();
191 }
192 }
193 }
194
195 // MouseUp
196 void
MouseUp(BPoint point)197 ScrollCorner::MouseUp(BPoint point)
198 {
199 BView::MouseUp(point);
200 uint32 buttons = 0;
201 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
202 if (!(buttons & B_PRIMARY_MOUSE_BUTTON))
203 SetDragging(false);
204 }
205
206 // MouseMoved
207 void
MouseMoved(BPoint point,uint32 transit,const BMessage * message)208 ScrollCorner::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
209 {
210 BView::MouseMoved(point, transit, message);
211 if (IsDragging()) {
212 uint32 buttons = 0;
213 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
214 // This is a work-around for a BeOS bug: We sometimes don't get a
215 // MouseUp(), but fortunately it seems, that within the last
216 // MouseMoved() the button is not longer pressed.
217 if (buttons & B_PRIMARY_MOUSE_BUTTON) {
218 BPoint diff = point - fStartPoint;
219 if (fScrollView) {
220 fScrollView->_ScrollCornerValueChanged(fStartScrollOffset
221 - diff);
222 // + diff);
223 }
224 } else
225 SetDragging(false);
226 }
227 }
228
229 // Draw
230 void
Draw(BRect updateRect)231 ScrollCorner::Draw(BRect updateRect)
232 {
233 if (IsEnabled()) {
234 if (IsDragging())
235 DrawBitmap(fBitmaps[1], BPoint(0.0f, 0.0f));
236 else
237 DrawBitmap(fBitmaps[0], BPoint(0.0f, 0.0f));
238 }
239 else
240 DrawBitmap(fBitmaps[2], BPoint(0.0f, 0.0f));
241 }
242
243 // WindowActivated
244 void
WindowActivated(bool active)245 ScrollCorner::WindowActivated(bool active)
246 {
247 if (active != (fState & STATE_WINDOW_ACTIVE)) {
248 bool enabled = IsEnabled();
249 if (active)
250 fState |= STATE_WINDOW_ACTIVE;
251 else
252 fState &= ~STATE_WINDOW_ACTIVE;
253 if (enabled != IsEnabled())
254 Invalidate();
255 }
256 }
257
258 // SetActive
259 void
SetActive(bool active)260 ScrollCorner::SetActive(bool active)
261 {
262 if (active != IsActive()) {
263 bool enabled = IsEnabled();
264 if (active)
265 fState |= STATE_ACTIVE;
266 else
267 fState &= ~STATE_ACTIVE;
268 if (enabled != IsEnabled())
269 Invalidate();
270 }
271 }
272
273 // SetDragging
274 void
SetDragging(bool dragging)275 ScrollCorner::SetDragging(bool dragging)
276 {
277 if (dragging != IsDragging()) {
278 if (dragging)
279 fState |= STATE_DRAGGING;
280 else
281 fState &= ~STATE_DRAGGING;
282 Invalidate();
283 }
284 }
285
286
287 // #pragma mark - ScrollView
288
289
290 // constructor
ScrollView(BView * child,uint32 scrollingFlags,BRect frame,const char * name,uint32 resizingMode,uint32 viewFlags,uint32 borderStyle,uint32 borderFlags)291 ScrollView::ScrollView(BView* child, uint32 scrollingFlags, BRect frame,
292 const char* name, uint32 resizingMode, uint32 viewFlags,
293 uint32 borderStyle, uint32 borderFlags)
294 : BView(frame, name, resizingMode,
295 viewFlags | B_FRAME_EVENTS | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
296 Scroller()
297 {
298 _Init(child, scrollingFlags, borderStyle, borderFlags);
299 }
300
301 #ifdef __HAIKU__
302
303 // constructor
ScrollView(BView * child,uint32 scrollingFlags,const char * name,uint32 viewFlags,uint32 borderStyle,uint32 borderFlags)304 ScrollView::ScrollView(BView* child, uint32 scrollingFlags, const char* name,
305 uint32 viewFlags, uint32 borderStyle, uint32 borderFlags)
306 : BView(name, viewFlags | B_FRAME_EVENTS | B_WILL_DRAW
307 | B_FULL_UPDATE_ON_RESIZE),
308 Scroller()
309 {
310 _Init(child, scrollingFlags, borderStyle, borderFlags);
311 }
312
313 #endif // __HAIKU__
314
315 // destructor
~ScrollView()316 ScrollView::~ScrollView()
317 {
318 }
319
320 // AllAttached
321 void
AllAttached()322 ScrollView::AllAttached()
323 {
324 // do a first layout
325 _Layout(_UpdateScrollBarVisibility());
326 }
327
328 // Draw
Draw(BRect updateRect)329 void ScrollView::Draw(BRect updateRect)
330 {
331 if (fBorderStyle == B_NO_BORDER)
332 return;
333
334 rgb_color keyboardFocus = keyboard_navigation_color();
335 rgb_color light = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
336 B_LIGHTEN_MAX_TINT);
337 rgb_color shadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
338 B_DARKEN_1_TINT);
339 rgb_color darkShadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
340 B_DARKEN_2_TINT);
341
342 BRect r = Bounds();
343
344 if (fChildFocused && fWindowActive) {
345 SetHighColor(keyboardFocus);
346 StrokeRect(r);
347 } else {
348 if (fBorderStyle == B_PLAIN_BORDER) {
349 SetHighColor(darkShadow);
350 StrokeRect(r);
351 } else {
352 BeginLineArray(4);
353 AddLine(BPoint(r.left, r.bottom),
354 BPoint(r.left, r.top), shadow);
355 AddLine(BPoint(r.left + 1.0, r.top),
356 BPoint(r.right, r.top), shadow);
357 AddLine(BPoint(r.right, r.top + 1.0),
358 BPoint(r.right, r.bottom), light);
359 AddLine(BPoint(r.right - 1.0, r.bottom),
360 BPoint(r.left + 1.0, r.bottom), light);
361 EndLineArray();
362 }
363 }
364 if (fBorderStyle == B_PLAIN_BORDER)
365 return;
366
367 // The right and bottom lines will be hidden if the scroll views are
368 // visible. But that doesn't harm.
369 r.InsetBy(1, 1);
370 SetHighColor(darkShadow);
371 StrokeRect(r);
372 }
373
374 // FrameResized
375 void
FrameResized(float width,float height)376 ScrollView::FrameResized(float width, float height)
377 {
378 _Layout(0);
379 }
380
381 // WindowActivated
WindowActivated(bool activated)382 void ScrollView::WindowActivated(bool activated)
383 {
384 fWindowActive = activated;
385 if (fChildFocused)
386 Invalidate();
387 }
388
389 #ifdef __HAIKU__
390
391 // MinSize
392 BSize
MinSize()393 ScrollView::MinSize()
394 {
395 BSize size = (fChild ? fChild->MinSize() : BSize(-1, -1));
396 return _Size(size);
397 }
398
399 // PreferredSize
400 BSize
PreferredSize()401 ScrollView::PreferredSize()
402 {
403 BSize size = (fChild ? fChild->PreferredSize() : BSize(-1, -1));
404 return _Size(size);
405 }
406
407 #endif // __HAIKU__
408
409 // #pragma mark -
410
411 // ScrollingFlags
412 uint32
ScrollingFlags() const413 ScrollView::ScrollingFlags() const
414 {
415 return fScrollingFlags;
416 }
417
418 // SetVisibleRectIsChildBounds
419 void
SetVisibleRectIsChildBounds(bool flag)420 ScrollView::SetVisibleRectIsChildBounds(bool flag)
421 {
422 if (flag != VisibleRectIsChildBounds()) {
423 if (flag)
424 fScrollingFlags |= SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS;
425 else
426 fScrollingFlags &= ~SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS;
427 if (fChild && _UpdateScrollBarVisibility())
428 _Layout(0);
429 }
430 }
431
432 // VisibleRectIsChildBounds
433 bool
VisibleRectIsChildBounds() const434 ScrollView::VisibleRectIsChildBounds() const
435 {
436 return (fScrollingFlags & SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS);
437 }
438
439 // Child
440 BView*
Child() const441 ScrollView::Child() const
442 {
443 return fChild;
444 }
445
446 // ChildFocusChanged
447 //
448 // To be called by the scroll child, when its has got or lost the focus.
449 // We need this to know, when to draw the blue focus frame.
450 void
ChildFocusChanged(bool focused)451 ScrollView::ChildFocusChanged(bool focused)
452 {
453 if (fChildFocused != focused) {
454 fChildFocused = focused;
455 Invalidate();
456 }
457 }
458
459 // HScrollBar
460 BScrollBar*
HScrollBar() const461 ScrollView::HScrollBar() const
462 {
463 return fHScrollBar;
464 }
465
466 // VScrollBar
467 BScrollBar*
VScrollBar() const468 ScrollView::VScrollBar() const
469 {
470 return fVScrollBar;
471 }
472
473 // HVScrollCorner
474 BView*
HVScrollCorner() const475 ScrollView::HVScrollCorner() const
476 {
477 return fScrollCorner;
478 }
479
480 // #pragma mark -
481
482 // SetHSmallStep
483 void
SetHSmallStep(float hStep)484 ScrollView::SetHSmallStep(float hStep)
485 {
486 SetSmallSteps(hStep, fVSmallStep);
487 }
488
489 // SetVSmallStep
490 void
SetVSmallStep(float vStep)491 ScrollView::SetVSmallStep(float vStep)
492 {
493 SetSmallSteps(fHSmallStep, vStep);
494 }
495
496 // SetSmallSteps
497 void
SetSmallSteps(float hStep,float vStep)498 ScrollView::SetSmallSteps(float hStep, float vStep)
499 {
500 if (fHSmallStep != hStep || fVSmallStep != vStep) {
501 fHSmallStep = hStep;
502 fVSmallStep = vStep;
503 _UpdateScrollBars();
504 }
505 }
506
507 // GetSmallSteps
508 void
GetSmallSteps(float * hStep,float * vStep) const509 ScrollView::GetSmallSteps(float* hStep, float* vStep) const
510 {
511 *hStep = fHSmallStep;
512 *vStep = fVSmallStep;
513 }
514
515 // HSmallStep
516 float
HSmallStep() const517 ScrollView::HSmallStep() const
518 {
519 return fHSmallStep;
520 }
521
522 // VSmallStep
523 float
VSmallStep() const524 ScrollView::VSmallStep() const
525 {
526 return fVSmallStep;
527 }
528
529 // IsScrolling
530 bool
IsScrolling() const531 ScrollView::IsScrolling() const
532 {
533 return fScrolling;
534 }
535
536 void
SetScrollingEnabled(bool enabled)537 ScrollView::SetScrollingEnabled(bool enabled)
538 {
539 Scroller::SetScrollingEnabled(enabled);
540 if (IsScrollingEnabled())
541 SetScrollOffset(ScrollOffset());
542 }
543
544 // #pragma mark -
545
546 // DataRectChanged
547 void
DataRectChanged(BRect,BRect)548 ScrollView::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/)
549 {
550 if (ScrollTarget()) {
551 if (_UpdateScrollBarVisibility())
552 _Layout(0);
553 else
554 _UpdateScrollBars();
555 }
556 }
557
558 // ScrollOffsetChanged
559 void
ScrollOffsetChanged(BPoint,BPoint newOffset)560 ScrollView::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint newOffset)
561 {
562 if (fHScrollBar && fHScrollBar->Value() != newOffset.x)
563 fHScrollBar->SetValue(newOffset.x);
564 if (fVScrollBar && fVScrollBar->Value() != newOffset.y)
565 fVScrollBar->SetValue(newOffset.y);
566 }
567
568 // VisibleSizeChanged
569 void
VisibleSizeChanged(float,float,float,float)570 ScrollView::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/,
571 float /*newWidth*/, float /*newHeight*/)
572 {
573 if (ScrollTarget()) {
574 if (_UpdateScrollBarVisibility())
575 _Layout(0);
576 else
577 _UpdateScrollBars();
578 }
579 }
580
581 // ScrollTargetChanged
582 void
ScrollTargetChanged(Scrollable *,Scrollable * newTarget)583 ScrollView::ScrollTargetChanged(Scrollable* /*oldTarget*/,
584 Scrollable* newTarget)
585 {
586 /* // remove the old child
587 if (fChild)
588 RemoveChild(fChild);
589 // add the new child
590 BView* view = dynamic_cast<BView*>(newTarget);
591 fChild = view;
592 if (view)
593 AddChild(view);
594 else if (newTarget) // set the scroll target to NULL, if it isn't a BView
595 SetScrollTarget(NULL);
596 */
597 }
598
599 // _Init
600 void
_Init(BView * child,uint32 scrollingFlags,uint32 borderStyle,uint32 borderFlags)601 ScrollView::_Init(BView* child, uint32 scrollingFlags, uint32 borderStyle,
602 uint32 borderFlags)
603 {
604 fChild = NULL;
605 fScrollingFlags = scrollingFlags;
606
607 fHScrollBar = NULL;
608 fVScrollBar = NULL;
609 fScrollCorner = NULL;
610
611 fHVisible = true;
612 fVVisible = true;
613 fCornerVisible = true;
614
615 fWindowActive = false;
616 fChildFocused = false;
617
618 fScrolling = false;
619
620 fHSmallStep = 1;
621 fVSmallStep = 1;
622
623 fBorderStyle = borderStyle;
624 fBorderFlags = borderFlags;
625
626 // Set transparent view color -- our area is completely covered by
627 // our children.
628 SetViewColor(B_TRANSPARENT_32_BIT);
629 // create scroll bars
630 if (fScrollingFlags & (SCROLL_HORIZONTAL | SCROLL_HORIZONTAL_MAGIC)) {
631 fHScrollBar = new InternalScrollBar(this,
632 BRect(0.0, 0.0, 100.0, B_H_SCROLL_BAR_HEIGHT), B_HORIZONTAL);
633 AddChild(fHScrollBar);
634 }
635 if (fScrollingFlags & (SCROLL_VERTICAL | SCROLL_VERTICAL_MAGIC)) {
636 fVScrollBar = new InternalScrollBar(this,
637 BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH, 100.0), B_VERTICAL);
638 AddChild(fVScrollBar);
639 }
640 // Create a scroll corner, if we can scroll into both direction.
641 if (fHScrollBar && fVScrollBar) {
642 fScrollCorner = new ScrollCorner(this);
643 AddChild(fScrollCorner);
644 }
645 // add child
646 if (child) {
647 fChild = child;
648 AddChild(child);
649 if (Scrollable* scrollable = dynamic_cast<Scrollable*>(child))
650 SetScrollTarget(scrollable);
651 }
652 }
653
654
655 // _ScrollValueChanged
656 void
_ScrollValueChanged(InternalScrollBar * scrollBar,float value)657 ScrollView::_ScrollValueChanged(InternalScrollBar* scrollBar, float value)
658 {
659 if (!IsScrollingEnabled())
660 return;
661
662 switch (scrollBar->Orientation()) {
663 case B_HORIZONTAL:
664 if (fHScrollBar)
665 SetScrollOffset(BPoint(value, ScrollOffset().y));
666 break;
667 case B_VERTICAL:
668 if (fVScrollBar)
669 SetScrollOffset(BPoint(ScrollOffset().x, value));
670 break;
671 default:
672 break;
673 }
674 }
675
676 // _ScrollCornerValueChanged
677 void
_ScrollCornerValueChanged(BPoint offset)678 ScrollView::_ScrollCornerValueChanged(BPoint offset)
679 {
680 // The logic in Scrollable::SetScrollOffset() handles offsets, that
681 // are out of range.
682 SetScrollOffset(offset);
683 }
684
685 // #pragma mark -
686
687 // _Layout
688 //
689 // Relayouts all children (fChild, scroll bars).
690 // flags indicates which scrollbars' visibility has changed.
691 // May be overridden to do a custom layout -- the SCROLL_*_MAGIC must
692 // be disabled in this case, or strange things happen.
693 void
_Layout(uint32 flags)694 ScrollView::_Layout(uint32 flags)
695 {
696 bool hbar = (fHScrollBar && fHVisible);
697 bool vbar = (fVScrollBar && fVVisible);
698 bool corner = (fScrollCorner && fCornerVisible);
699 BRect childRect(_ChildRect());
700 float innerWidth = childRect.Width();
701 float innerHeight = childRect.Height();
702 BPoint scrollLT(_InnerRect().LeftTop());
703 scrollLT.x--;
704 scrollLT.y--;
705
706 BPoint scrollRB(childRect.RightBottom() + BPoint(1.0f, 1.0f));
707
708 // layout scroll bars and scroll corner
709 if (corner) {
710 // In this case the scrollbars overlap one pixel.
711 fHScrollBar->MoveTo(scrollLT.x, scrollRB.y);
712 fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT);
713 fVScrollBar->MoveTo(scrollRB.x, scrollLT.y);
714 fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0);
715 fScrollCorner->MoveTo(childRect.right + 2.0, childRect.bottom + 2.0);
716 } else if (hbar) {
717 fHScrollBar->MoveTo(scrollLT.x, scrollRB.y);
718 fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT);
719 } else if (vbar) {
720 fVScrollBar->MoveTo(scrollRB.x, scrollLT.y);
721 fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0);
722 }
723 // layout child
724 if (fChild) {
725 fChild->MoveTo(childRect.LeftTop());
726 fChild->ResizeTo(innerWidth, innerHeight);
727 if (VisibleRectIsChildBounds())
728 SetVisibleSize(innerWidth, innerHeight);
729 // Due to a BeOS bug sometimes the area under a recently hidden
730 // scroll bar isn't updated correctly.
731 // We force this manually: The position of hidden scroll bar isn't
732 // updated any longer, so we can't just invalidate it.
733 if (fChild->Window()) {
734 if (flags & SCROLL_HORIZONTAL && !fHVisible)
735 fChild->Invalidate(fHScrollBar->Frame());
736 if (flags & SCROLL_VERTICAL && !fVVisible)
737 fChild->Invalidate(fVScrollBar->Frame());
738 }
739 }
740 }
741
742 // _UpdateScrollBars
743 //
744 // Probably somewhat misnamed. This function updates the scroll bars'
745 // proportion, range attributes and step widths according to the scroll
746 // target's DataRect() and VisibleBounds(). May also be called, if there's
747 // no scroll target -- then the scroll bars are disabled.
748 void
_UpdateScrollBars()749 ScrollView::_UpdateScrollBars()
750 {
751 BRect dataRect = DataRect();
752 BRect visibleBounds = VisibleBounds();
753 if (!fScrollTarget) {
754 dataRect.Set(0.0, 0.0, 0.0, 0.0);
755 visibleBounds.Set(0.0, 0.0, 0.0, 0.0);
756 }
757 float hProportion = min_c(1.0f, (visibleBounds.Width() + 1.0f)
758 / (dataRect.Width() + 1.0f));
759 float hMaxValue = max_c(dataRect.left,
760 dataRect.right - visibleBounds.Width());
761 float vProportion = min_c(1.0f, (visibleBounds.Height() + 1.0f)
762 / (dataRect.Height() + 1.0f));
763 float vMaxValue = max_c(dataRect.top,
764 dataRect.bottom - visibleBounds.Height());
765 // update horizontal scroll bar
766 if (fHScrollBar) {
767 fHScrollBar->SetProportion(hProportion);
768 fHScrollBar->SetRange(dataRect.left, hMaxValue);
769 // This obviously ineffective line works around a BScrollBar bug:
770 // As documented the scrollbar's value is adjusted, if the range
771 // has been changed and it therefore falls out of the range. But if,
772 // after resetting the range to what it has been before, the user
773 // moves the scrollbar to the original value via one click
774 // it is failed to invoke BScrollBar::ValueChanged().
775 fHScrollBar->SetValue(fHScrollBar->Value());
776 fHScrollBar->SetSteps(fHSmallStep, visibleBounds.Width());
777 }
778 // update vertical scroll bar
779 if (fVScrollBar) {
780 fVScrollBar->SetProportion(vProportion);
781 fVScrollBar->SetRange(dataRect.top, vMaxValue);
782 // This obviously ineffective line works around a BScrollBar bug.
783 fVScrollBar->SetValue(fVScrollBar->Value());
784 fVScrollBar->SetSteps(fVSmallStep, visibleBounds.Height());
785 }
786 // update scroll corner
787 if (fScrollCorner) {
788 fScrollCorner->SetActive(hProportion < 1.0f || vProportion < 1.0f);
789 }
790 }
791
792 // set_visible_state
793 //
794 // Convenience function: Sets a view's visibility state to /visible/.
795 // Returns true, if the state was actually changed, false otherwise.
796 // This function never calls Hide() on a hidden or Show() on a visible
797 // view. /view/ must be valid.
798 static inline
799 bool
set_visible_state(BView * view,bool visible,bool * currentlyVisible)800 set_visible_state(BView* view, bool visible, bool* currentlyVisible)
801 {
802 bool changed = false;
803 if (*currentlyVisible != visible) {
804 if (visible)
805 view->Show();
806 else
807 view->Hide();
808 *currentlyVisible = visible;
809 changed = true;
810 }
811 return changed;
812 }
813
814 // _UpdateScrollBarVisibility
815 //
816 // Checks which of scroll bars need to be visible according to
817 // SCROLL_*_MAGIG and shows/hides them, if necessary.
818 // Returns a bitwise combination of SCROLL_HORIZONTAL and SCROLL_VERTICAL
819 // according to which scroll bar's visibility state has changed, 0 if none.
820 // A return value != 0 usually means that the layout isn't valid any longer.
821 uint32
_UpdateScrollBarVisibility()822 ScrollView::_UpdateScrollBarVisibility()
823 {
824 uint32 changed = 0;
825 BRect childRect(_MaxVisibleRect());
826 float width = childRect.Width();
827 float height = childRect.Height();
828 BRect dataRect = DataRect(); // Invalid if !ScrollTarget(),
829 float dataWidth = dataRect.Width(); // but that doesn't harm.
830 float dataHeight = dataRect.Height(); //
831 bool hbar = (fScrollingFlags & SCROLL_HORIZONTAL_MAGIC);
832 bool vbar = (fScrollingFlags & SCROLL_VERTICAL_MAGIC);
833 if (!ScrollTarget()) {
834 if (hbar) {
835 if (set_visible_state(fHScrollBar, false, &fHVisible))
836 changed |= SCROLL_HORIZONTAL;
837 }
838 if (vbar) {
839 if (set_visible_state(fVScrollBar, false, &fVVisible))
840 changed |= SCROLL_VERTICAL;
841 }
842 } else if (hbar && width >= dataWidth && vbar && height >= dataHeight) {
843 // none
844 if (set_visible_state(fHScrollBar, false, &fHVisible))
845 changed |= SCROLL_HORIZONTAL;
846 if (set_visible_state(fVScrollBar, false, &fVVisible))
847 changed |= SCROLL_VERTICAL;
848 } else {
849 // The case, that both scroll bars are magic and invisible is catched,
850 // so that while checking one bar we can suppose, that the other one
851 // is visible (if it does exist at all).
852 BRect innerRect(_GuessVisibleRect(fHScrollBar, fVScrollBar));
853 float innerWidth = innerRect.Width();
854 float innerHeight = innerRect.Height();
855 // the horizontal one?
856 if (hbar) {
857 if (innerWidth >= dataWidth) {
858 if (set_visible_state(fHScrollBar, false, &fHVisible))
859 changed |= SCROLL_HORIZONTAL;
860 } else {
861 if (set_visible_state(fHScrollBar, true, &fHVisible))
862 changed |= SCROLL_HORIZONTAL;
863 }
864 }
865 // the vertical one?
866 if (vbar) {
867 if (innerHeight >= dataHeight) {
868 if (set_visible_state(fVScrollBar, false, &fVVisible))
869 changed |= SCROLL_VERTICAL;
870 } else {
871 if (set_visible_state(fVScrollBar, true, &fVVisible))
872 changed |= SCROLL_VERTICAL;
873 }
874 }
875 }
876 // If anything has changed, update the scroll corner as well.
877 if (changed && fScrollCorner)
878 set_visible_state(fScrollCorner, fHVisible && fVVisible, &fCornerVisible);
879 return changed;
880 }
881
882 // _InnerRect
883 //
884 // Returns the rectangle that actually can be used for the child and the
885 // scroll bars, i.e. the view's Bounds() subtracted the space for the
886 // decorative frame.
887 BRect
_InnerRect() const888 ScrollView::_InnerRect() const
889 {
890 BRect r = Bounds();
891 float borderWidth = 0;
892 switch (fBorderStyle) {
893 case B_NO_BORDER:
894 break;
895 case B_PLAIN_BORDER:
896 borderWidth = 1;
897 break;
898 case B_FANCY_BORDER:
899 default:
900 borderWidth = 2;
901 break;
902 }
903 if (fBorderFlags & BORDER_LEFT)
904 r.left += borderWidth;
905 if (fBorderFlags & BORDER_TOP)
906 r.top += borderWidth;
907 if (fBorderFlags & BORDER_RIGHT)
908 r.right -= borderWidth;
909 if (fBorderFlags & BORDER_BOTTOM)
910 r.bottom -= borderWidth;
911 return r;
912 }
913
914 // _ChildRect
915 //
916 // Returns the rectangle, that should be the current child frame.
917 // `should' because 1. we might not have a child at all or 2. a
918 // relayout is pending.
919 BRect
_ChildRect() const920 ScrollView::_ChildRect() const
921 {
922 return _ChildRect(fHScrollBar && fHVisible, fVScrollBar && fVVisible);
923 }
924
925 // _ChildRect
926 //
927 // The same as _ChildRect() with the exception that not the current
928 // scroll bar visibility, but a fictitious one given by /hbar/ and /vbar/
929 // is considered.
930 BRect
_ChildRect(bool hbar,bool vbar) const931 ScrollView::_ChildRect(bool hbar, bool vbar) const
932 {
933 BRect rect(_InnerRect());
934 if (vbar)
935 rect.right -= B_V_SCROLL_BAR_WIDTH;
936 if (hbar)
937 rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
938
939 return rect;
940 }
941
942 // _GuessVisibleRect
943 //
944 // Returns an approximation of the visible rect for the
945 // fictitious scroll bar visibility given by /hbar/ and /vbar/.
946 // In the case !VisibleRectIsChildBounds() it is simply the current
947 // visible rect.
948 BRect
_GuessVisibleRect(bool hbar,bool vbar) const949 ScrollView::_GuessVisibleRect(bool hbar, bool vbar) const
950 {
951 if (VisibleRectIsChildBounds())
952 return _ChildRect(hbar, vbar).OffsetToCopy(ScrollOffset());
953 return VisibleRect();
954 }
955
956 // _MaxVisibleRect
957 //
958 // Returns the maximal possible visible rect in the current situation, that
959 // is depending on if the visible rect is the child's bounds either the
960 // rectangle the child covers when both scroll bars are hidden (offset to
961 // the scroll offset) or the current visible rect.
962 BRect
_MaxVisibleRect() const963 ScrollView::_MaxVisibleRect() const
964 {
965 return _GuessVisibleRect(true, true);
966 }
967
968 #ifdef __HAIKU__
969
970 BSize
_Size(BSize size)971 ScrollView::_Size(BSize size)
972 {
973 if (fVVisible)
974 size.width += B_V_SCROLL_BAR_WIDTH;
975 if (fHVisible)
976 size.height += B_H_SCROLL_BAR_HEIGHT;
977
978 switch (fBorderStyle) {
979 case B_NO_BORDER:
980 // one line of pixels from scrollbar possibly hidden
981 if (fBorderFlags & BORDER_RIGHT)
982 size.width += fVVisible ? -1 : 0;
983 if (fBorderFlags & BORDER_BOTTOM)
984 size.height += fHVisible ? -1 : 0;
985 break;
986
987 case B_PLAIN_BORDER:
988 if (fBorderFlags & BORDER_LEFT)
989 size.width += 1;
990 if (fBorderFlags & BORDER_TOP)
991 size.height += 1;
992 // one line of pixels in frame possibly from scrollbar
993 if (fBorderFlags & BORDER_RIGHT)
994 size.width += fVVisible ? 0 : 1;
995 if (fBorderFlags & BORDER_BOTTOM)
996 size.height += fHVisible ? 0 : 1;
997 break;
998
999 case B_FANCY_BORDER:
1000 default:
1001 if (fBorderFlags & BORDER_LEFT)
1002 size.width += 2;
1003 if (fBorderFlags & BORDER_TOP)
1004 size.height += 2;
1005 // one line of pixels in frame possibly from scrollbar
1006 if (fBorderFlags & BORDER_RIGHT)
1007 size.width += fVVisible ? 1 : 2;
1008 if (fBorderFlags & BORDER_BOTTOM)
1009 size.height += fHVisible ? 1 : 2;
1010 break;
1011 }
1012
1013 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
1014 }
1015
1016 #endif // __HAIKU__
1017
1018 // _SetScrolling
1019 void
_SetScrolling(bool scrolling)1020 ScrollView::_SetScrolling(bool scrolling)
1021 {
1022 fScrolling = scrolling;
1023 }
1024