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