1 /*
2 * Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009 Stephan Aßmus, superstippi@gmx.de.
4 * Copyright 2014-2015 Haiku, Inc. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 * Stephan Aßmus, superstippi@gmx.de
9 * Axel Dörfler, axeld@pinc-software.de
10 * John Scipione, jscpione@gmail.com
11 */
12
13
14 #include <ScrollView.h>
15
16 #include <ControlLook.h>
17 #include <LayoutUtils.h>
18 #include <Message.h>
19 #include <Region.h>
20 #include <Window.h>
21
22 #include <binary_compatibility/Interface.h>
23
24
25 static const float kFancyBorderSize = 2;
26 static const float kPlainBorderSize = 1;
27
28
BScrollView(const char * name,BView * target,uint32 resizingMode,uint32 flags,bool horizontal,bool vertical,border_style border)29 BScrollView::BScrollView(const char* name, BView* target, uint32 resizingMode,
30 uint32 flags, bool horizontal, bool vertical, border_style border)
31 :
32 BView(BRect(), name, resizingMode, _ModifyFlags(flags, target, border)),
33 fTarget(target),
34 fBorder(border)
35 {
36 _Init(horizontal, vertical);
37 }
38
39
BScrollView(const char * name,BView * target,uint32 flags,bool horizontal,bool vertical,border_style border)40 BScrollView::BScrollView(const char* name, BView* target, uint32 flags,
41 bool horizontal, bool vertical, border_style border)
42 :
43 BView(name, _ModifyFlags(flags, target, border)),
44 fTarget(target),
45 fBorder(border)
46 {
47 _Init(horizontal, vertical);
48 }
49
50
BScrollView(BMessage * archive)51 BScrollView::BScrollView(BMessage* archive)
52 :
53 BView(archive),
54 fHighlighted(false)
55 {
56 int32 border;
57 fBorder = archive->FindInt32("_style", &border) == B_OK ?
58 (border_style)border : B_FANCY_BORDER;
59
60 // in a shallow archive, we may not have a target anymore. We must
61 // be prepared for this case
62
63 // don't confuse our scroll bars with our (eventual) target
64 int32 firstBar = 0;
65 if (!archive->FindBool("_no_target_")) {
66 fTarget = ChildAt(0);
67 firstBar++;
68 } else
69 fTarget = NULL;
70
71 // search for our scroll bars
72 // This will not work for managed archives (when the layout kit is used).
73 // In that case the children are attached later, and we perform the search
74 // again in the AllUnarchived method.
75
76 fHorizontalScrollBar = NULL;
77 fVerticalScrollBar = NULL;
78
79 BView* view;
80 while ((view = ChildAt(firstBar++)) != NULL) {
81 BScrollBar *bar = dynamic_cast<BScrollBar *>(view);
82 if (bar == NULL)
83 continue;
84
85 if (bar->Orientation() == B_HORIZONTAL)
86 fHorizontalScrollBar = bar;
87 else if (bar->Orientation() == B_VERTICAL)
88 fVerticalScrollBar = bar;
89 }
90
91 fPreviousWidth = uint16(Bounds().Width());
92 fPreviousHeight = uint16(Bounds().Height());
93
94 }
95
96
~BScrollView()97 BScrollView::~BScrollView()
98 {
99 }
100
101
102 // #pragma mark - Archiving
103
104
105 BArchivable*
Instantiate(BMessage * archive)106 BScrollView::Instantiate(BMessage* archive)
107 {
108 if (validate_instantiation(archive, "BScrollView"))
109 return new BScrollView(archive);
110
111 return NULL;
112 }
113
114
115 status_t
Archive(BMessage * archive,bool deep) const116 BScrollView::Archive(BMessage* archive, bool deep) const
117 {
118 status_t status = BView::Archive(archive, deep);
119 if (status != B_OK)
120 return status;
121
122 // If this is a deep archive, the BView class will take care
123 // of our children.
124
125 if (status == B_OK && fBorder != B_FANCY_BORDER)
126 status = archive->AddInt32("_style", fBorder);
127 if (status == B_OK && fTarget == NULL)
128 status = archive->AddBool("_no_target_", true);
129
130 // The highlighted state is not archived, but since it is
131 // usually (or should be) used to indicate focus, this
132 // is probably the right thing to do.
133
134 return status;
135 }
136
137
138 status_t
AllUnarchived(const BMessage * archive)139 BScrollView::AllUnarchived(const BMessage* archive)
140 {
141 status_t result = BView::AllUnarchived(archive);
142 if (result != B_OK)
143 return result;
144
145 // search for our scroll bars and target
146 int32 firstBar = 0;
147 BView* view;
148 while ((view = ChildAt(firstBar++)) != NULL) {
149 BScrollBar *bar = dynamic_cast<BScrollBar *>(view);
150 // We assume that the first non-scrollbar child view is the target.
151 // So the target view can't be a BScrollBar, but who would do that?
152 if (bar == NULL) {
153 // in a shallow archive, we may not have a target anymore. We must
154 // be prepared for this case
155 if (fTarget == NULL && !archive->FindBool("_no_target_"))
156 fTarget = view;
157 continue;
158 }
159
160 if (bar->Orientation() == B_HORIZONTAL)
161 fHorizontalScrollBar = bar;
162 else if (bar->Orientation() == B_VERTICAL)
163 fVerticalScrollBar = bar;
164 }
165
166 // Now connect the bars to the target, and make the target aware of them
167 if (fHorizontalScrollBar)
168 fHorizontalScrollBar->SetTarget(fTarget);
169 if (fVerticalScrollBar)
170 fVerticalScrollBar->SetTarget(fTarget);
171
172 if (fTarget)
173 fTarget->TargetedByScrollView(this);
174
175 fPreviousWidth = uint16(Bounds().Width());
176 fPreviousHeight = uint16(Bounds().Height());
177
178 return B_OK;
179 }
180
181
182 // #pragma mark - Hook methods
183
184
185 void
AttachedToWindow()186 BScrollView::AttachedToWindow()
187 {
188 BView::AttachedToWindow();
189
190 if ((fHorizontalScrollBar == NULL && fVerticalScrollBar == NULL)
191 || (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL)
192 || Window()->Look() != B_DOCUMENT_WINDOW_LOOK) {
193 return;
194 }
195
196 // If we have only one bar, we need to check if we are in the
197 // bottom right edge of a window with the B_DOCUMENT_LOOK to
198 // adjust the size of the bar to acknowledge the resize knob.
199
200 BRect bounds = ConvertToScreen(Bounds());
201 BRect windowBounds = Window()->Frame();
202
203 if (bounds.right - _BorderSize() != windowBounds.right
204 || bounds.bottom - _BorderSize() != windowBounds.bottom) {
205 return;
206 }
207
208 if (fHorizontalScrollBar != NULL)
209 fHorizontalScrollBar->ResizeBy(-B_V_SCROLL_BAR_WIDTH, 0);
210 else if (fVerticalScrollBar != NULL)
211 fVerticalScrollBar->ResizeBy(0, -B_H_SCROLL_BAR_HEIGHT);
212 }
213
214
215 void
DetachedFromWindow()216 BScrollView::DetachedFromWindow()
217 {
218 BView::DetachedFromWindow();
219 }
220
221
222 void
AllAttached()223 BScrollView::AllAttached()
224 {
225 BView::AllAttached();
226 }
227
228
229 void
AllDetached()230 BScrollView::AllDetached()
231 {
232 BView::AllDetached();
233 }
234
235
236 void
Draw(BRect updateRect)237 BScrollView::Draw(BRect updateRect)
238 {
239 uint32 flags = 0;
240 if (fHighlighted && Window()->IsActive())
241 flags |= BControlLook::B_FOCUSED;
242
243 BRect rect(Bounds());
244 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
245
246 BRect verticalScrollBarFrame(0, 0, -1, -1);
247 if (fVerticalScrollBar)
248 verticalScrollBarFrame = fVerticalScrollBar->Frame();
249
250 BRect horizontalScrollBarFrame(0, 0, -1, -1);
251 if (fHorizontalScrollBar)
252 horizontalScrollBarFrame = fHorizontalScrollBar->Frame();
253
254 be_control_look->DrawScrollViewFrame(this, rect, updateRect,
255 verticalScrollBarFrame, horizontalScrollBarFrame, base, fBorder,
256 flags, fBorders);
257 }
258
259
260 void
FrameMoved(BPoint newPosition)261 BScrollView::FrameMoved(BPoint newPosition)
262 {
263 BView::FrameMoved(newPosition);
264 }
265
266
267 void
FrameResized(float newWidth,float newHeight)268 BScrollView::FrameResized(float newWidth, float newHeight)
269 {
270 BView::FrameResized(newWidth, newHeight);
271
272 const BRect bounds = Bounds();
273
274 if (fTarget != NULL && (fTarget->Flags() & B_SUPPORTS_LAYOUT) != 0
275 && (fTarget->Flags() & B_SCROLL_VIEW_AWARE) == 0) {
276 BSize size = fTarget->PreferredSize();
277 if (fHorizontalScrollBar != NULL) {
278 float delta = size.Width() - bounds.Width(),
279 proportion = bounds.Width() / size.Width();
280 if (delta < 0)
281 delta = 0;
282
283 fHorizontalScrollBar->SetRange(0, delta);
284 fHorizontalScrollBar->SetSteps(be_plain_font->Size() * 1.33,
285 bounds.Width());
286 fHorizontalScrollBar->SetProportion(proportion);
287 }
288 if (fVerticalScrollBar != NULL) {
289 float delta = size.Height() - bounds.Height(),
290 proportion = bounds.Height() / size.Height();
291 if (delta < 0)
292 delta = 0;
293
294 fVerticalScrollBar->SetRange(0, delta);
295 fVerticalScrollBar->SetSteps(be_plain_font->Size() * 1.33,
296 bounds.Height());
297 fVerticalScrollBar->SetProportion(proportion);
298 }
299 }
300
301 if (fBorder == B_NO_BORDER)
302 return;
303
304 float border = _BorderSize() - 1;
305
306 if (fHorizontalScrollBar != NULL && fVerticalScrollBar != NULL) {
307 BRect scrollCorner(bounds);
308 scrollCorner.left = min_c(
309 fPreviousWidth - fVerticalScrollBar->Frame().Height(),
310 fHorizontalScrollBar->Frame().right + 1);
311 scrollCorner.top = min_c(
312 fPreviousHeight - fHorizontalScrollBar->Frame().Width(),
313 fVerticalScrollBar->Frame().bottom + 1);
314 Invalidate(scrollCorner);
315 }
316
317 // changes in newWidth
318
319 if (bounds.Width() > fPreviousWidth) {
320 // invalidate the region between the old and the new right border
321 BRect rect = bounds;
322 rect.left += fPreviousWidth - border;
323 rect.right--;
324 Invalidate(rect);
325 } else if (bounds.Width() < fPreviousWidth) {
326 // invalidate the region of the new right border
327 BRect rect = bounds;
328 rect.left = rect.right - border;
329 Invalidate(rect);
330 }
331
332 // changes in newHeight
333
334 if (bounds.Height() > fPreviousHeight) {
335 // invalidate the region between the old and the new bottom border
336 BRect rect = bounds;
337 rect.top += fPreviousHeight - border;
338 rect.bottom--;
339 Invalidate(rect);
340 } else if (bounds.Height() < fPreviousHeight) {
341 // invalidate the region of the new bottom border
342 BRect rect = bounds;
343 rect.top = rect.bottom - border;
344 Invalidate(rect);
345 }
346
347 fPreviousWidth = uint16(bounds.Width());
348 fPreviousHeight = uint16(bounds.Height());
349 }
350
351
352 void
MessageReceived(BMessage * message)353 BScrollView::MessageReceived(BMessage* message)
354 {
355 BView::MessageReceived(message);
356 }
357
358
359 void
MouseDown(BPoint where)360 BScrollView::MouseDown(BPoint where)
361 {
362 BView::MouseDown(where);
363 }
364
365
366 void
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)367 BScrollView::MouseMoved(BPoint where, uint32 code,
368 const BMessage* dragMessage)
369 {
370 BView::MouseMoved(where, code, dragMessage);
371 }
372
373
374 void
MouseUp(BPoint where)375 BScrollView::MouseUp(BPoint where)
376 {
377 BView::MouseUp(where);
378 }
379
380
381 void
WindowActivated(bool active)382 BScrollView::WindowActivated(bool active)
383 {
384 if (fHighlighted)
385 Invalidate();
386
387 BView::WindowActivated(active);
388 }
389
390
391 // #pragma mark - Size methods
392
393
394 void
GetPreferredSize(float * _width,float * _height)395 BScrollView::GetPreferredSize(float* _width, float* _height)
396 {
397 BSize size = PreferredSize();
398
399 if (_width)
400 *_width = size.width;
401
402 if (_height)
403 *_height = size.height;
404 }
405
406
407 void
ResizeToPreferred()408 BScrollView::ResizeToPreferred()
409 {
410 if (Window() == NULL)
411 return;
412 BView::ResizeToPreferred();
413 }
414
415
416 void
MakeFocus(bool focus)417 BScrollView::MakeFocus(bool focus)
418 {
419 BView::MakeFocus(focus);
420 }
421
422
423 BSize
MinSize()424 BScrollView::MinSize()
425 {
426 BSize size = _ComputeSize(fTarget != NULL ? fTarget->MinSize()
427 : BSize(16, 16));
428
429 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
430 }
431
432
433 BSize
MaxSize()434 BScrollView::MaxSize()
435 {
436 BSize size = _ComputeSize(fTarget != NULL ? fTarget->MaxSize()
437 : BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
438
439 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
440 }
441
442
443 BSize
PreferredSize()444 BScrollView::PreferredSize()
445 {
446 BSize size = _ComputeSize(fTarget != NULL ? fTarget->PreferredSize()
447 : BSize(32, 32));
448
449 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
450 }
451
452
453 // #pragma mark - BScrollView methods
454
455
456 BScrollBar*
ScrollBar(orientation direction) const457 BScrollView::ScrollBar(orientation direction) const
458 {
459 if (direction == B_HORIZONTAL)
460 return fHorizontalScrollBar;
461
462 return fVerticalScrollBar;
463 }
464
465
466 void
SetBorder(border_style border)467 BScrollView::SetBorder(border_style border)
468 {
469 if (fBorder == border)
470 return;
471
472 if ((Flags() & B_SUPPORTS_LAYOUT) != 0) {
473 fBorder = border;
474 SetFlags(_ModifyFlags(Flags(), fTarget, border));
475
476 DoLayout();
477 Invalidate();
478 return;
479 }
480
481 float offset = _BorderSize() - _BorderSize(border);
482 float resize = 2 * offset;
483
484 float horizontalGap = 0, verticalGap = 0;
485 float change = 0;
486 if (border == B_NO_BORDER || fBorder == B_NO_BORDER) {
487 if (fHorizontalScrollBar != NULL)
488 verticalGap = border != B_NO_BORDER ? 1 : -1;
489 if (fVerticalScrollBar != NULL)
490 horizontalGap = border != B_NO_BORDER ? 1 : -1;
491
492 change = border != B_NO_BORDER ? -1 : 1;
493 if (fHorizontalScrollBar == NULL || fVerticalScrollBar == NULL)
494 change *= 2;
495 }
496
497 fBorder = border;
498
499 int32 savedResizingMode = 0;
500 if (fTarget != NULL) {
501 savedResizingMode = fTarget->ResizingMode();
502 fTarget->SetResizingMode(B_FOLLOW_NONE);
503 }
504
505 MoveBy(offset, offset);
506 ResizeBy(-resize - horizontalGap, -resize - verticalGap);
507
508 if (fTarget != NULL) {
509 fTarget->MoveBy(-offset, -offset);
510 fTarget->SetResizingMode(savedResizingMode);
511 }
512
513 if (fHorizontalScrollBar != NULL) {
514 fHorizontalScrollBar->MoveBy(-offset - verticalGap, offset + verticalGap);
515 fHorizontalScrollBar->ResizeBy(resize + horizontalGap - change, 0);
516 }
517 if (fVerticalScrollBar != NULL) {
518 fVerticalScrollBar->MoveBy(offset + horizontalGap, -offset - horizontalGap);
519 fVerticalScrollBar->ResizeBy(0, resize + verticalGap - change);
520 }
521
522 SetFlags(_ModifyFlags(Flags(), fTarget, border));
523 }
524
525
526 border_style
Border() const527 BScrollView::Border() const
528 {
529 return fBorder;
530 }
531
532
533 void
SetBorders(uint32 borders)534 BScrollView::SetBorders(uint32 borders)
535 {
536 if (fBorders == borders || (Flags() & B_SUPPORTS_LAYOUT) == 0)
537 return;
538
539 fBorders = borders;
540 DoLayout();
541 Invalidate();
542 }
543
544
545 uint32
Borders() const546 BScrollView::Borders() const
547 {
548 return fBorders;
549 }
550
551
552 status_t
SetBorderHighlighted(bool highlight)553 BScrollView::SetBorderHighlighted(bool highlight)
554 {
555 if (fHighlighted == highlight)
556 return B_OK;
557
558 if (fBorder != B_FANCY_BORDER)
559 // highlighting only works for B_FANCY_BORDER
560 return B_ERROR;
561
562 fHighlighted = highlight;
563
564 if (fHorizontalScrollBar != NULL)
565 fHorizontalScrollBar->SetBorderHighlighted(highlight);
566 if (fVerticalScrollBar != NULL)
567 fVerticalScrollBar->SetBorderHighlighted(highlight);
568
569 BRect bounds = Bounds();
570 bounds.InsetBy(1, 1);
571
572 Invalidate(BRect(bounds.left, bounds.top, bounds.right, bounds.top));
573 Invalidate(BRect(bounds.left, bounds.top + 1, bounds.left,
574 bounds.bottom - 1));
575 Invalidate(BRect(bounds.right, bounds.top + 1, bounds.right,
576 bounds.bottom - 1));
577 Invalidate(BRect(bounds.left, bounds.bottom, bounds.right, bounds.bottom));
578
579 return B_OK;
580 }
581
582
583 bool
IsBorderHighlighted() const584 BScrollView::IsBorderHighlighted() const
585 {
586 return fHighlighted;
587 }
588
589
590 void
SetTarget(BView * target)591 BScrollView::SetTarget(BView* target)
592 {
593 if (fTarget == target)
594 return;
595
596 if (fTarget != NULL) {
597 fTarget->TargetedByScrollView(NULL);
598 RemoveChild(fTarget);
599
600 // we are not supposed to delete the view
601 }
602
603 fTarget = target;
604 if (fHorizontalScrollBar != NULL)
605 fHorizontalScrollBar->SetTarget(target);
606 if (fVerticalScrollBar != NULL)
607 fVerticalScrollBar->SetTarget(target);
608
609 if (target != NULL) {
610 float borderSize = _BorderSize();
611 target->MoveTo((fBorders & BControlLook::B_LEFT_BORDER) != 0
612 ? borderSize : 0, (fBorders & BControlLook::B_TOP_BORDER) != 0
613 ? borderSize : 0);
614 BRect innerFrame = _InnerFrame();
615 target->ResizeTo(innerFrame.Width() - 1, innerFrame.Height() - 1);
616 target->TargetedByScrollView(this);
617
618 AddChild(target, ChildAt(0));
619 // This way, we are making sure that the target will
620 // be added top most in the list (which is important
621 // for unarchiving)
622 }
623
624 SetFlags(_ModifyFlags(Flags(), fTarget, fBorder));
625 }
626
627
628 BView*
Target() const629 BScrollView::Target() const
630 {
631 return fTarget;
632 }
633
634
635 // #pragma mark - Scripting methods
636
637
638
639 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 what,const char * property)640 BScrollView::ResolveSpecifier(BMessage* message, int32 index,
641 BMessage* specifier, int32 what, const char* property)
642 {
643 return BView::ResolveSpecifier(message, index, specifier, what, property);
644 }
645
646
647 status_t
GetSupportedSuites(BMessage * message)648 BScrollView::GetSupportedSuites(BMessage* message)
649 {
650 return BView::GetSupportedSuites(message);
651 }
652
653
654 // #pragma mark - Perform
655
656
657 status_t
Perform(perform_code code,void * _data)658 BScrollView::Perform(perform_code code, void* _data)
659 {
660 switch (code) {
661 case PERFORM_CODE_MIN_SIZE:
662 ((perform_data_min_size*)_data)->return_value
663 = BScrollView::MinSize();
664 return B_OK;
665
666 case PERFORM_CODE_MAX_SIZE:
667 ((perform_data_max_size*)_data)->return_value
668 = BScrollView::MaxSize();
669 return B_OK;
670
671 case PERFORM_CODE_PREFERRED_SIZE:
672 ((perform_data_preferred_size*)_data)->return_value
673 = BScrollView::PreferredSize();
674 return B_OK;
675
676 case PERFORM_CODE_LAYOUT_ALIGNMENT:
677 ((perform_data_layout_alignment*)_data)->return_value
678 = BScrollView::LayoutAlignment();
679 return B_OK;
680
681 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
682 ((perform_data_has_height_for_width*)_data)->return_value
683 = BScrollView::HasHeightForWidth();
684 return B_OK;
685
686 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
687 {
688 perform_data_get_height_for_width* data
689 = (perform_data_get_height_for_width*)_data;
690 BScrollView::GetHeightForWidth(data->width, &data->min, &data->max,
691 &data->preferred);
692 return B_OK;
693 }
694
695 case PERFORM_CODE_SET_LAYOUT:
696 {
697 perform_data_set_layout* data = (perform_data_set_layout*)_data;
698 BScrollView::SetLayout(data->layout);
699 return B_OK;
700 }
701
702 case PERFORM_CODE_LAYOUT_INVALIDATED:
703 {
704 perform_data_layout_invalidated* data
705 = (perform_data_layout_invalidated*)_data;
706 BScrollView::LayoutInvalidated(data->descendants);
707 return B_OK;
708 }
709
710 case PERFORM_CODE_DO_LAYOUT:
711 {
712 BScrollView::DoLayout();
713 return B_OK;
714 }
715 }
716
717 return BView::Perform(code, _data);
718 }
719
720
721 // #pragma mark - Protected methods
722
723
724 void
LayoutInvalidated(bool descendants)725 BScrollView::LayoutInvalidated(bool descendants)
726 {
727 }
728
729
730 void
DoLayout()731 BScrollView::DoLayout()
732 {
733 if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
734 return;
735
736 // If the user set a layout, we let the base class version call its hook.
737 if (GetLayout() != NULL) {
738 BView::DoLayout();
739 return;
740 }
741
742 BRect innerFrame = _InnerFrame();
743
744 if (fTarget != NULL) {
745 fTarget->MoveTo(innerFrame.left, innerFrame.top);
746 fTarget->ResizeTo(innerFrame.Width(), innerFrame.Height());
747
748 //BLayoutUtils::AlignInFrame(fTarget, fTarget->Bounds());
749 }
750
751 _AlignScrollBars(fHorizontalScrollBar != NULL, fVerticalScrollBar != NULL,
752 innerFrame);
753 }
754
755
756 // #pragma mark - Private methods
757
758
759 void
_Init(bool horizontal,bool vertical)760 BScrollView::_Init(bool horizontal, bool vertical)
761 {
762 fHorizontalScrollBar = NULL;
763 fVerticalScrollBar = NULL;
764 fHighlighted = false;
765 fBorders = BControlLook::B_ALL_BORDERS;
766
767 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
768
769 if (horizontal) {
770 fHorizontalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_HSB_",
771 fTarget, 0, 1000, B_HORIZONTAL);
772 AddChild(fHorizontalScrollBar);
773 }
774
775 if (vertical) {
776 fVerticalScrollBar = new BScrollBar(BRect(0, 0, 14, 14), "_VSB_",
777 fTarget, 0, 1000, B_VERTICAL);
778 AddChild(fVerticalScrollBar);
779 }
780
781 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) {
782 BRect frame = _ComputeFrame(fTarget, fHorizontalScrollBar,
783 fVerticalScrollBar, fBorder, BControlLook::B_ALL_BORDERS);
784 MoveTo(frame.LeftTop());
785 ResizeTo(frame.Size());
786 }
787
788 BRect targetFrame;
789 if (fTarget) {
790 // layout target and add it
791 fTarget->TargetedByScrollView(this);
792 fTarget->MoveTo(B_ORIGIN);
793
794 if (fBorder != B_NO_BORDER)
795 fTarget->MoveBy(_BorderSize(), _BorderSize());
796
797 AddChild(fTarget);
798 targetFrame = fTarget->Frame();
799 } else {
800 // no target specified
801 targetFrame = Bounds();
802 if (horizontal)
803 targetFrame.bottom -= fHorizontalScrollBar->PreferredSize().Height() + 1;
804 if (vertical)
805 targetFrame.right -= fVerticalScrollBar->PreferredSize().Width() + 1;
806 if (fBorder == B_FANCY_BORDER) {
807 targetFrame.bottom--;
808 targetFrame.right--;
809 }
810 }
811
812 _AlignScrollBars(horizontal, vertical, targetFrame);
813
814 fPreviousWidth = uint16(Bounds().Width());
815 fPreviousHeight = uint16(Bounds().Height());
816 }
817
818
819 float
_BorderSize() const820 BScrollView::_BorderSize() const
821 {
822 return _BorderSize(fBorder);
823 }
824
825
826 BRect
_InnerFrame() const827 BScrollView::_InnerFrame() const
828 {
829 BRect frame = Bounds();
830 _InsetBorders(frame, fBorder, fBorders);
831
832 float borderSize = _BorderSize();
833
834 if (fHorizontalScrollBar != NULL) {
835 frame.bottom -= fHorizontalScrollBar->PreferredSize().Height();
836 if (borderSize == 0)
837 frame.bottom--;
838 }
839 if (fVerticalScrollBar != NULL) {
840 frame.right -= fVerticalScrollBar->PreferredSize().Width();
841 if (borderSize == 0)
842 frame.right--;
843 }
844
845 return frame;
846 }
847
848
849 BSize
_ComputeSize(BSize targetSize) const850 BScrollView::_ComputeSize(BSize targetSize) const
851 {
852 BRect frame = _ComputeFrame(
853 BRect(0, 0, targetSize.width, targetSize.height));
854
855 return BSize(frame.Width(), frame.Height());
856 }
857
858
859 BRect
_ComputeFrame(BRect targetRect) const860 BScrollView::_ComputeFrame(BRect targetRect) const
861 {
862 return _ComputeFrame(targetRect, fHorizontalScrollBar,
863 fVerticalScrollBar, fBorder, fBorders);
864 }
865
866
867 void
_AlignScrollBars(bool horizontal,bool vertical,BRect targetFrame)868 BScrollView::_AlignScrollBars(bool horizontal, bool vertical, BRect targetFrame)
869 {
870 if (horizontal) {
871 BRect rect = targetFrame;
872 rect.top = rect.bottom + 1;
873 rect.bottom = rect.top + fHorizontalScrollBar->PreferredSize().Height();
874 if (fBorder != B_NO_BORDER || vertical) {
875 // extend scrollbar so that it overlaps one pixel with vertical
876 // scrollbar
877 rect.right++;
878 }
879
880 if (fBorder != B_NO_BORDER) {
881 // the scrollbar draws part of the surrounding frame on the left
882 rect.left--;
883 }
884
885 fHorizontalScrollBar->MoveTo(rect.left, rect.top);
886 fHorizontalScrollBar->ResizeTo(rect.Width(), rect.Height());
887 }
888
889 if (vertical) {
890 BRect rect = targetFrame;
891 rect.left = rect.right + 1;
892 rect.right = rect.left + fVerticalScrollBar->PreferredSize().Width();
893 if (fBorder != B_NO_BORDER || horizontal) {
894 // extend scrollbar so that it overlaps one pixel with vertical
895 // scrollbar
896 rect.bottom++;
897 }
898
899 if (fBorder != B_NO_BORDER) {
900 // the scrollbar draws part of the surrounding frame on the left
901 rect.top--;
902 }
903
904 fVerticalScrollBar->MoveTo(rect.left, rect.top);
905 fVerticalScrollBar->ResizeTo(rect.Width(), rect.Height());
906 }
907 }
908
909
910 /*! This static method is used to calculate the frame that the
911 ScrollView will cover depending on the frame of its target
912 and which border style is used.
913 It is used in the constructor and at other places.
914 */
915 /*static*/ BRect
_ComputeFrame(BRect frame,BScrollBar * horizontal,BScrollBar * vertical,border_style border,uint32 borders)916 BScrollView::_ComputeFrame(BRect frame, BScrollBar* horizontal,
917 BScrollBar* vertical, border_style border, uint32 borders)
918 {
919 if (vertical != NULL)
920 frame.right += vertical->PreferredSize().Width();
921 if (horizontal != NULL)
922 frame.bottom += horizontal->PreferredSize().Height();
923
924 // Take the other minimum dimensions into account, too, but only if
925 // the frame already has a greater-than-zero value for them. Otherwise,
926 // non-layouted applications could wind up with broken layouts.
927 if (vertical != NULL) {
928 const float minHeight = vertical->MinSize().Height();
929 if (frame.Height() > 0 && frame.Height() < minHeight)
930 frame.bottom += minHeight - frame.Height();
931 }
932 if (horizontal != NULL) {
933 const float minWidth = horizontal->MinSize().Width();
934 if (frame.Width() > 0 && frame.Width() < minWidth)
935 frame.right += minWidth - frame.Width();
936 }
937
938 _InsetBorders(frame, border, borders, true);
939
940 if (_BorderSize(border) == 0) {
941 if (vertical != NULL)
942 frame.right++;
943 if (horizontal != NULL)
944 frame.bottom++;
945 }
946
947 return frame;
948 }
949
950
951 /*static*/ BRect
_ComputeFrame(BView * target,BScrollBar * horizontal,BScrollBar * vertical,border_style border,uint32 borders)952 BScrollView::_ComputeFrame(BView *target, BScrollBar* horizontal,
953 BScrollBar* vertical, border_style border, uint32 borders)
954 {
955 return _ComputeFrame(target != NULL ? target->Frame()
956 : BRect(0, 0, 16, 16), horizontal, vertical, border, borders);
957 }
958
959
960 /*! This method returns the size of the specified border.
961 */
962 /*static*/ float
_BorderSize(border_style border)963 BScrollView::_BorderSize(border_style border)
964 {
965 if (border == B_FANCY_BORDER)
966 return kFancyBorderSize;
967 if (border == B_PLAIN_BORDER)
968 return kPlainBorderSize;
969
970 return 0;
971 }
972
973
974 /*! This method changes the "flags" argument as passed on to
975 the BView constructor.
976 */
977 /*static*/ uint32
_ModifyFlags(uint32 flags,BView * target,border_style border)978 BScrollView::_ModifyFlags(uint32 flags, BView* target, border_style border)
979 {
980 if (target != NULL && (target->Flags() & B_SUPPORTS_LAYOUT) != 0
981 && (target->Flags() & B_SCROLL_VIEW_AWARE) == 0)
982 flags |= B_FRAME_EVENTS;
983
984 // We either need B_FULL_UPDATE_ON_RESIZE or B_FRAME_EVENTS if we have
985 // to draw a border.
986 if (border != B_NO_BORDER) {
987 flags |= B_WILL_DRAW | ((flags & B_FULL_UPDATE_ON_RESIZE) != 0 ?
988 0 : B_FRAME_EVENTS);
989 }
990
991 return flags;
992 }
993
994
995 /*static*/ void
_InsetBorders(BRect & frame,border_style border,uint32 borders,bool expand)996 BScrollView::_InsetBorders(BRect& frame, border_style border, uint32 borders, bool expand)
997 {
998 float borderSize = _BorderSize(border);
999 if (expand)
1000 borderSize = -borderSize;
1001 if ((borders & BControlLook::B_LEFT_BORDER) != 0)
1002 frame.left += borderSize;
1003 if ((borders & BControlLook::B_TOP_BORDER) != 0)
1004 frame.top += borderSize;
1005 if ((borders & BControlLook::B_RIGHT_BORDER) != 0)
1006 frame.right -= borderSize;
1007 if ((borders & BControlLook::B_BOTTOM_BORDER) != 0)
1008 frame.bottom -= borderSize;
1009 }
1010
1011
1012 // #pragma mark - FBC and forbidden
1013
1014
1015 BScrollView&
operator =(const BScrollView &)1016 BScrollView::operator=(const BScrollView &)
1017 {
1018 return *this;
1019 }
1020
1021
_ReservedScrollView1()1022 void BScrollView::_ReservedScrollView1() {}
_ReservedScrollView2()1023 void BScrollView::_ReservedScrollView2() {}
_ReservedScrollView3()1024 void BScrollView::_ReservedScrollView3() {}
_ReservedScrollView4()1025 void BScrollView::_ReservedScrollView4() {}
1026
1027
1028 extern "C" void
B_IF_GCC_2(InvalidateLayout__11BScrollViewb,_ZN11BScrollView16InvalidateLayoutEb)1029 B_IF_GCC_2(InvalidateLayout__11BScrollViewb,
1030 _ZN11BScrollView16InvalidateLayoutEb)(BScrollView* view, bool descendants)
1031 {
1032 perform_data_layout_invalidated data;
1033 data.descendants = descendants;
1034
1035 view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
1036 }
1037