xref: /haiku/src/kits/interface/Box.cpp (revision ca8ed5ea660fb6275799a3b7f138b201c41a667b)
1 /*
2  * Copyright 2001-2013 Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Stephan Aßmus, superstippi@gmx.de
7  *		DarkWyrm, bpmagic@columbus.rr.com
8  *		Axel Dörfler, axeld@pinc-software.de
9  *		Marc Flerackers, mflerackers@androme.be
10  *		John Scipione, jscipione@gmail.com
11  */
12 
13 
14 #include <Box.h>
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include <ControlLook.h>
21 #include <Layout.h>
22 #include <LayoutUtils.h>
23 #include <Message.h>
24 #include <Region.h>
25 
26 #include <binary_compatibility/Interface.h>
27 
28 
29 struct BBox::LayoutData {
30 	LayoutData()
31 		: valid(false)
32 	{
33 	}
34 
35 	BRect	label_box;		// label box (label string or label view); in case
36 							// of a label string not including descent
37 	BRect	insets;			// insets induced by border and label
38 	BSize	min;
39 	BSize	max;
40 	BSize	preferred;
41 	BAlignment alignment;
42 	bool	valid;			// validity the other fields
43 };
44 
45 
46 BBox::BBox(BRect frame, const char* name, uint32 resizingMode, uint32 flags,
47 		border_style border)
48 	:
49 	BView(frame, name, resizingMode, flags  | B_WILL_DRAW | B_FRAME_EVENTS),
50 	  fStyle(border)
51 {
52 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
53 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
54 
55 	_InitObject();
56 }
57 
58 
59 BBox::BBox(const char* name, uint32 flags, border_style border, BView* child)
60 	:
61 	BView(name, flags | B_WILL_DRAW | B_FRAME_EVENTS),
62 	fStyle(border)
63 {
64 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
65 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
66 
67 	_InitObject();
68 
69 	if (child)
70 		AddChild(child);
71 }
72 
73 
74 BBox::BBox(border_style border, BView* child)
75 	:
76 	BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP),
77 	fStyle(border)
78 {
79 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
80 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
81 
82 	_InitObject();
83 
84 	if (child)
85 		AddChild(child);
86 }
87 
88 
89 BBox::BBox(BMessage* archive)
90 	:
91 	BView(archive),
92 	fStyle(B_FANCY_BORDER)
93 {
94 	_InitObject(archive);
95 }
96 
97 
98 BBox::~BBox()
99 {
100 	_ClearLabel();
101 
102 	delete fLayoutData;
103 }
104 
105 
106 BArchivable*
107 BBox::Instantiate(BMessage* archive)
108 {
109 	if (validate_instantiation(archive, "BBox"))
110 		return new BBox(archive);
111 
112 	return NULL;
113 }
114 
115 
116 status_t
117 BBox::Archive(BMessage* archive, bool deep) const
118 {
119 	status_t ret = BView::Archive(archive, deep);
120 
121 	if (fLabel && ret == B_OK)
122 		ret = archive->AddString("_label", fLabel);
123 
124 	if (fLabelView && ret == B_OK)
125 		ret = archive->AddBool("_lblview", true);
126 
127 	if (fStyle != B_FANCY_BORDER && ret == B_OK)
128 		ret = archive->AddInt32("_style", fStyle);
129 
130 	return ret;
131 }
132 
133 
134 void
135 BBox::SetBorder(border_style border)
136 {
137 	if (border == fStyle)
138 		return;
139 
140 	fStyle = border;
141 
142 	InvalidateLayout();
143 
144 	if (Window() != NULL && LockLooper()) {
145 		Invalidate();
146 		UnlockLooper();
147 	}
148 }
149 
150 
151 border_style
152 BBox::Border() const
153 {
154 	return fStyle;
155 }
156 
157 
158 //! This function is not part of the R5 API and is not yet finalized yet
159 float
160 BBox::TopBorderOffset()
161 {
162 	_ValidateLayoutData();
163 
164 	if (fLabel != NULL || fLabelView != NULL)
165 		return fLayoutData->label_box.Height() / 2;
166 
167 	return 0;
168 }
169 
170 
171 //! This function is not part of the R5 API and is not yet finalized yet
172 BRect
173 BBox::InnerFrame()
174 {
175 	_ValidateLayoutData();
176 
177 	BRect frame(Bounds());
178 	frame.left += fLayoutData->insets.left;
179 	frame.top += fLayoutData->insets.top;
180 	frame.right -= fLayoutData->insets.right;
181 	frame.bottom -= fLayoutData->insets.bottom;
182 
183 	return frame;
184 }
185 
186 
187 void
188 BBox::SetLabel(const char* string)
189 {
190 	_ClearLabel();
191 
192 	if (string)
193 		fLabel = strdup(string);
194 
195 	InvalidateLayout();
196 
197 	if (Window())
198 		Invalidate();
199 }
200 
201 
202 status_t
203 BBox::SetLabel(BView* viewLabel)
204 {
205 	_ClearLabel();
206 
207 	if (viewLabel) {
208 		fLabelView = viewLabel;
209 		fLabelView->MoveTo(10.0f, 0.0f);
210 		AddChild(fLabelView, ChildAt(0));
211 	}
212 
213 	InvalidateLayout();
214 
215 	if (Window())
216 		Invalidate();
217 
218 	return B_OK;
219 }
220 
221 
222 const char*
223 BBox::Label() const
224 {
225 	return fLabel;
226 }
227 
228 
229 BView*
230 BBox::LabelView() const
231 {
232 	return fLabelView;
233 }
234 
235 
236 void
237 BBox::Draw(BRect updateRect)
238 {
239 	_ValidateLayoutData();
240 
241 	PushState();
242 
243 	BRect labelBox = BRect(0, 0, 0, 0);
244 	if (fLabel != NULL) {
245 		labelBox = fLayoutData->label_box;
246 		BRegion update(updateRect);
247 		update.Exclude(labelBox);
248 
249 		ConstrainClippingRegion(&update);
250 	} else if (fLabelView != NULL)
251 		labelBox = fLabelView->Bounds();
252 
253 	switch (fStyle) {
254 		case B_FANCY_BORDER:
255 			_DrawFancy(labelBox);
256 			break;
257 
258 		case B_PLAIN_BORDER:
259 			_DrawPlain(labelBox);
260 			break;
261 
262 		default:
263 			break;
264 	}
265 
266 	if (fLabel) {
267 		ConstrainClippingRegion(NULL);
268 
269 		font_height fontHeight;
270 		GetFontHeight(&fontHeight);
271 
272 		SetHighColor(0, 0, 0);
273 		DrawString(fLabel, BPoint(10.0f, ceilf(fontHeight.ascent)));
274 	}
275 
276 	PopState();
277 }
278 
279 
280 void
281 BBox::AttachedToWindow()
282 {
283 	BView* parent = Parent();
284 	if (parent != NULL) {
285 		// inherit the color from parent
286 		rgb_color color = parent->ViewColor();
287 		if (color == B_TRANSPARENT_COLOR)
288 			color = ui_color(B_PANEL_BACKGROUND_COLOR);
289 
290 		SetViewColor(color);
291 		SetLowColor(color);
292 	}
293 
294 	// The box could have been resized in the mean time
295 	fBounds = Bounds();
296 }
297 
298 
299 void
300 BBox::DetachedFromWindow()
301 {
302 	BView::DetachedFromWindow();
303 }
304 
305 
306 void
307 BBox::AllAttached()
308 {
309 	BView::AllAttached();
310 }
311 
312 
313 void
314 BBox::AllDetached()
315 {
316 	BView::AllDetached();
317 }
318 
319 
320 void
321 BBox::FrameResized(float width, float height)
322 {
323 	BRect bounds(Bounds());
324 
325 	// invalidate the regions that the app_server did not
326 	// (for removing the previous or drawing the new border)
327 	if (fStyle != B_NO_BORDER) {
328 		// TODO: this must be made part of the be_control_look stuff!
329 		int32 borderSize = fStyle == B_PLAIN_BORDER ? 0 : 2;
330 
331 		BRect invalid(bounds);
332 		if (fBounds.right < bounds.right) {
333 			// enlarging
334 			invalid.left = fBounds.right - borderSize;
335 			invalid.right = fBounds.right;
336 
337 			Invalidate(invalid);
338 		} else if (fBounds.right > bounds.right) {
339 			// shrinking
340 			invalid.left = bounds.right - borderSize;
341 
342 			Invalidate(invalid);
343 		}
344 
345 		invalid = bounds;
346 		if (fBounds.bottom < bounds.bottom) {
347 			// enlarging
348 			invalid.top = fBounds.bottom - borderSize;
349 			invalid.bottom = fBounds.bottom;
350 
351 			Invalidate(invalid);
352 		} else if (fBounds.bottom > bounds.bottom) {
353 			// shrinking
354 			invalid.top = bounds.bottom - borderSize;
355 
356 			Invalidate(invalid);
357 		}
358 	}
359 
360 	fBounds.right = bounds.right;
361 	fBounds.bottom = bounds.bottom;
362 }
363 
364 
365 void
366 BBox::MessageReceived(BMessage* message)
367 {
368 	BView::MessageReceived(message);
369 }
370 
371 
372 void
373 BBox::MouseDown(BPoint point)
374 {
375 	BView::MouseDown(point);
376 }
377 
378 
379 void
380 BBox::MouseUp(BPoint point)
381 {
382 	BView::MouseUp(point);
383 }
384 
385 
386 void
387 BBox::WindowActivated(bool active)
388 {
389 	BView::WindowActivated(active);
390 }
391 
392 
393 void
394 BBox::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
395 {
396 	BView::MouseMoved(point, transit, message);
397 }
398 
399 
400 void
401 BBox::FrameMoved(BPoint newLocation)
402 {
403 	BView::FrameMoved(newLocation);
404 }
405 
406 
407 BHandler*
408 BBox::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
409 	int32 what, const char* property)
410 {
411 	return BView::ResolveSpecifier(message, index, specifier, what, property);
412 }
413 
414 
415 void
416 BBox::ResizeToPreferred()
417 {
418 	float width, height;
419 	GetPreferredSize(&width, &height);
420 
421 	// make sure the box don't get smaller than it already is
422 	if (width < Bounds().Width())
423 		width = Bounds().Width();
424 	if (height < Bounds().Height())
425 		height = Bounds().Height();
426 
427 	BView::ResizeTo(width, height);
428 }
429 
430 
431 void
432 BBox::GetPreferredSize(float* _width, float* _height)
433 {
434 	_ValidateLayoutData();
435 
436 	if (_width)
437 		*_width = fLayoutData->preferred.width;
438 	if (_height)
439 		*_height = fLayoutData->preferred.height;
440 }
441 
442 
443 void
444 BBox::MakeFocus(bool focused)
445 {
446 	BView::MakeFocus(focused);
447 }
448 
449 
450 status_t
451 BBox::GetSupportedSuites(BMessage* message)
452 {
453 	return BView::GetSupportedSuites(message);
454 }
455 
456 
457 status_t
458 BBox::Perform(perform_code code, void* _data)
459 {
460 	switch (code) {
461 		case PERFORM_CODE_MIN_SIZE:
462 			((perform_data_min_size*)_data)->return_value
463 				= BBox::MinSize();
464 			return B_OK;
465 		case PERFORM_CODE_MAX_SIZE:
466 			((perform_data_max_size*)_data)->return_value
467 				= BBox::MaxSize();
468 			return B_OK;
469 		case PERFORM_CODE_PREFERRED_SIZE:
470 			((perform_data_preferred_size*)_data)->return_value
471 				= BBox::PreferredSize();
472 			return B_OK;
473 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
474 			((perform_data_layout_alignment*)_data)->return_value
475 				= BBox::LayoutAlignment();
476 			return B_OK;
477 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
478 			((perform_data_has_height_for_width*)_data)->return_value
479 				= BBox::HasHeightForWidth();
480 			return B_OK;
481 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
482 		{
483 			perform_data_get_height_for_width* data
484 				= (perform_data_get_height_for_width*)_data;
485 			BBox::GetHeightForWidth(data->width, &data->min, &data->max,
486 				&data->preferred);
487 			return B_OK;
488 		}
489 		case PERFORM_CODE_SET_LAYOUT:
490 		{
491 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
492 			BBox::SetLayout(data->layout);
493 			return B_OK;
494 		}
495 		case PERFORM_CODE_LAYOUT_INVALIDATED:
496 		{
497 			perform_data_layout_invalidated* data
498 				= (perform_data_layout_invalidated*)_data;
499 			BBox::LayoutInvalidated(data->descendants);
500 			return B_OK;
501 		}
502 		case PERFORM_CODE_DO_LAYOUT:
503 		{
504 			BBox::DoLayout();
505 			return B_OK;
506 		}
507 	}
508 
509 	return BView::Perform(code, _data);
510 }
511 
512 
513 BSize
514 BBox::MinSize()
515 {
516 	_ValidateLayoutData();
517 
518 	BSize size = (GetLayout() ? GetLayout()->MinSize() : fLayoutData->min);
519 	if (size.width < fLayoutData->min.width)
520 		size.width = fLayoutData->min.width;
521 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
522 }
523 
524 
525 BSize
526 BBox::MaxSize()
527 {
528 	_ValidateLayoutData();
529 
530 	BSize size = (GetLayout() ? GetLayout()->MaxSize() : fLayoutData->max);
531 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
532 }
533 
534 
535 BSize
536 BBox::PreferredSize()
537 {
538 	_ValidateLayoutData();
539 
540 	BSize size = (GetLayout() ? GetLayout()->PreferredSize()
541 		: fLayoutData->preferred);
542 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
543 }
544 
545 
546 BAlignment
547 BBox::LayoutAlignment()
548 {
549 	_ValidateLayoutData();
550 
551 	BAlignment alignment = (GetLayout() ? GetLayout()->Alignment()
552 			: fLayoutData->alignment);
553 	return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), alignment);
554 }
555 
556 
557 void
558 BBox::LayoutInvalidated(bool descendants)
559 {
560 	fLayoutData->valid = false;
561 }
562 
563 
564 void
565 BBox::DoLayout()
566 {
567 	// Bail out, if we shan't do layout.
568 	if (!(Flags() & B_SUPPORTS_LAYOUT))
569 		return;
570 
571 	BLayout* layout = GetLayout();
572 
573 	// If the user set a layout, let the base class version call its
574 	// hook. In case when we have BView as a label, remove it from child list
575 	// so it won't be layouted with the rest of views and add it again
576 	// after that.
577 	if (layout != NULL) {
578 		if (fLabelView)
579 			RemoveChild(fLabelView);
580 
581 		BView::DoLayout();
582 
583 		if (fLabelView != NULL) {
584 			DisableLayoutInvalidation();
585 				// don't trigger a relayout
586 			AddChild(fLabelView, ChildAt(0));
587 			EnableLayoutInvalidation();
588 		}
589 	}
590 
591 	_ValidateLayoutData();
592 
593 	// Even if the user set a layout, restore label view to it's
594 	// desired position.
595 
596 	// layout the label view
597 	if (fLabelView != NULL) {
598 		fLabelView->MoveTo(fLayoutData->label_box.LeftTop());
599 		fLabelView->ResizeTo(fLayoutData->label_box.Size());
600 	}
601 
602 	// If we have layout return here and do not layout the child
603 	if (layout != NULL)
604 		return;
605 
606 	// layout the child
607 	if (BView* child = _Child()) {
608 		BRect frame(Bounds());
609 		frame.left += fLayoutData->insets.left;
610 		frame.top += fLayoutData->insets.top;
611 		frame.right -= fLayoutData->insets.right;
612 		frame.bottom -= fLayoutData->insets.bottom;
613 
614 		BLayoutUtils::AlignInFrame(child, frame);
615 	}
616 }
617 
618 
619 void BBox::_ReservedBox1() {}
620 void BBox::_ReservedBox2() {}
621 
622 
623 BBox &
624 BBox::operator=(const BBox &)
625 {
626 	return *this;
627 }
628 
629 
630 void
631 BBox::_InitObject(BMessage* archive)
632 {
633 	fBounds = Bounds();
634 
635 	fLabel = NULL;
636 	fLabelView = NULL;
637 	fLayoutData = new LayoutData;
638 
639 	int32 flags = 0;
640 
641 	BFont font(be_bold_font);
642 
643 	if (!archive || !archive->HasString("_fname"))
644 		flags = B_FONT_FAMILY_AND_STYLE;
645 
646 	if (!archive || !archive->HasFloat("_fflt"))
647 		flags |= B_FONT_SIZE;
648 
649 	if (flags != 0)
650 		SetFont(&font, flags);
651 
652 	if (archive != NULL) {
653 		const char* string;
654 		if (archive->FindString("_label", &string) == B_OK)
655 			SetLabel(string);
656 
657 		bool fancy;
658 		int32 style;
659 
660 		if (archive->FindBool("_style", &fancy) == B_OK)
661 			fStyle = fancy ? B_FANCY_BORDER : B_PLAIN_BORDER;
662 		else if (archive->FindInt32("_style", &style) == B_OK)
663 			fStyle = (border_style)style;
664 
665 		bool hasLabelView;
666 		if (archive->FindBool("_lblview", &hasLabelView) == B_OK)
667 			fLabelView = ChildAt(0);
668 	}
669 }
670 
671 
672 void
673 BBox::_DrawPlain(BRect labelBox)
674 {
675 	BRect rect = Bounds();
676 	rect.top += TopBorderOffset();
677 
678 	float lightTint;
679 	float shadowTint;
680 	if (be_control_look != NULL) {
681 		lightTint = B_LIGHTEN_1_TINT;
682 		shadowTint = B_DARKEN_1_TINT;
683 	} else {
684 		lightTint = B_LIGHTEN_MAX_TINT;
685 		shadowTint = B_DARKEN_3_TINT;
686 	}
687 
688 	if (rect.Height() == 0.0 || rect.Width() == 0.0) {
689 		// used as separator
690 		rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
691 
692 		SetHighColor(shadow);
693 		StrokeLine(rect.LeftTop(),rect.RightBottom());
694 	} else {
695 		// used as box
696 		rgb_color light = tint_color(ViewColor(), lightTint);
697 		rgb_color shadow = tint_color(ViewColor(), shadowTint);
698 
699 		BeginLineArray(4);
700 			AddLine(BPoint(rect.left, rect.bottom),
701 					BPoint(rect.left, rect.top), light);
702 			AddLine(BPoint(rect.left + 1.0f, rect.top),
703 					BPoint(rect.right, rect.top), light);
704 			AddLine(BPoint(rect.left + 1.0f, rect.bottom),
705 					BPoint(rect.right, rect.bottom), shadow);
706 			AddLine(BPoint(rect.right, rect.bottom - 1.0f),
707 					BPoint(rect.right, rect.top + 1.0f), shadow);
708 		EndLineArray();
709 	}
710 }
711 
712 
713 void
714 BBox::_DrawFancy(BRect labelBox)
715 {
716 	BRect rect = Bounds();
717 	rect.top += TopBorderOffset();
718 
719 	if (be_control_look != NULL) {
720 		rgb_color base = ViewColor();
721 		if (rect.Height() == 1.0) {
722 			// used as horizontal separator
723 			be_control_look->DrawGroupFrame(this, rect, rect, base,
724 				BControlLook::B_TOP_BORDER);
725 		} else if (rect.Width() == 1.0) {
726 			// used as vertical separator
727 			be_control_look->DrawGroupFrame(this, rect, rect, base,
728 				BControlLook::B_LEFT_BORDER);
729 		} else {
730 			// used as box
731 			be_control_look->DrawGroupFrame(this, rect, rect, base);
732 		}
733 		return;
734 	}
735 
736 	rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
737 	rgb_color shadow = tint_color(ViewColor(), B_DARKEN_3_TINT);
738 
739 	if (rect.Height() == 1.0) {
740 		// used as horizontal separator
741 		BeginLineArray(2);
742 			AddLine(BPoint(rect.left, rect.top),
743 					BPoint(rect.right, rect.top), shadow);
744 			AddLine(BPoint(rect.left, rect.bottom),
745 					BPoint(rect.right, rect.bottom), light);
746 		EndLineArray();
747 	} else if (rect.Width() == 1.0) {
748 		// used as vertical separator
749 		BeginLineArray(2);
750 			AddLine(BPoint(rect.left, rect.top),
751 					BPoint(rect.left, rect.bottom), shadow);
752 			AddLine(BPoint(rect.right, rect.top),
753 					BPoint(rect.right, rect.bottom), light);
754 		EndLineArray();
755 	} else {
756 		// used as box
757 		BeginLineArray(8);
758 			AddLine(BPoint(rect.left, rect.bottom - 1.0),
759 					BPoint(rect.left, rect.top), shadow);
760 			AddLine(BPoint(rect.left + 1.0, rect.top),
761 					BPoint(rect.right - 1.0, rect.top), shadow);
762 			AddLine(BPoint(rect.left, rect.bottom),
763 					BPoint(rect.right, rect.bottom), light);
764 			AddLine(BPoint(rect.right, rect.bottom - 1.0),
765 					BPoint(rect.right, rect.top), light);
766 
767 			rect.InsetBy(1.0, 1.0);
768 
769 			AddLine(BPoint(rect.left, rect.bottom - 1.0),
770 					BPoint(rect.left, rect.top), light);
771 			AddLine(BPoint(rect.left + 1.0, rect.top),
772 					BPoint(rect.right - 1.0, rect.top), light);
773 			AddLine(BPoint(rect.left, rect.bottom),
774 					BPoint(rect.right, rect.bottom), shadow);
775 			AddLine(BPoint(rect.right, rect.bottom - 1.0),
776 					BPoint(rect.right, rect.top), shadow);
777 		EndLineArray();
778 	}
779 }
780 
781 
782 void
783 BBox::_ClearLabel()
784 {
785 	fBounds.top = 0;
786 
787 	if (fLabel) {
788 		free(fLabel);
789 		fLabel = NULL;
790 	} else if (fLabelView) {
791 		fLabelView->RemoveSelf();
792 		delete fLabelView;
793 		fLabelView = NULL;
794 	}
795 }
796 
797 
798 BView*
799 BBox::_Child() const
800 {
801 	for (int32 i = 0; BView* view = ChildAt(i); i++) {
802 		if (view != fLabelView)
803 			return view;
804 	}
805 
806 	return NULL;
807 }
808 
809 
810 void
811 BBox::_ValidateLayoutData()
812 {
813 	if (fLayoutData->valid)
814 		return;
815 
816 	// compute the label box, width and height
817 	bool label = true;
818 	float labelHeight = 0;	// height of the label (pixel count)
819 	if (fLabel) {
820 		// leave 6 pixels of the frame, and have a gap of 4 pixels between
821 		// the frame and the text on either side
822 		font_height fontHeight;
823 		GetFontHeight(&fontHeight);
824 		fLayoutData->label_box.Set(6.0f, 0, 14.0f + StringWidth(fLabel),
825 			ceilf(fontHeight.ascent));
826 		labelHeight = ceilf(fontHeight.ascent + fontHeight.descent) + 1;
827 	} else if (fLabelView) {
828 		// the label view is placed at (0, 10) at its preferred size
829 		BSize size = fLabelView->PreferredSize();
830 		fLayoutData->label_box.Set(10, 0, 10 + size.width, size.height);
831 		labelHeight = size.height + 1;
832 	} else
833 		label = false;
834 
835 	// border
836 	switch (fStyle) {
837 		case B_PLAIN_BORDER:
838 			fLayoutData->insets.Set(1, 1, 1, 1);
839 			break;
840 		case B_FANCY_BORDER:
841 			fLayoutData->insets.Set(3, 3, 3, 3);
842 			break;
843 		case B_NO_BORDER:
844 		default:
845 			fLayoutData->insets.Set(0, 0, 0, 0);
846 			break;
847 	}
848 
849 	// if there's a label, the top inset will be dictated by the label
850 	if (label && labelHeight > fLayoutData->insets.top)
851 		fLayoutData->insets.top = labelHeight;
852 
853 	// total number of pixel the border adds
854 	float addWidth = fLayoutData->insets.left + fLayoutData->insets.right;
855 	float addHeight = fLayoutData->insets.top + fLayoutData->insets.bottom;
856 
857 	// compute the minimal width induced by the label
858 	float minWidth;
859 	if (label)
860 		minWidth = fLayoutData->label_box.right + fLayoutData->insets.right;
861 	else
862 		minWidth = addWidth - 1;
863 
864 	BAlignment alignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER);
865 
866 	// finally consider the child constraints, if we shall support layout
867 	BView* child = _Child();
868 	if (child && (Flags() & B_SUPPORTS_LAYOUT)) {
869 		BSize min = child->MinSize();
870 		BSize max = child->MaxSize();
871 		BSize preferred = child->PreferredSize();
872 
873 		min.width += addWidth;
874 		min.height += addHeight;
875 		preferred.width += addWidth;
876 		preferred.height += addHeight;
877 		max.width = BLayoutUtils::AddDistances(max.width, addWidth - 1);
878 		max.height = BLayoutUtils::AddDistances(max.height, addHeight - 1);
879 
880 		if (min.width < minWidth)
881 			min.width = minWidth;
882 		BLayoutUtils::FixSizeConstraints(min, max, preferred);
883 
884 		fLayoutData->min = min;
885 		fLayoutData->max = max;
886 		fLayoutData->preferred = preferred;
887 
888 		BAlignment childAlignment = child->LayoutAlignment();
889 		if (childAlignment.horizontal == B_ALIGN_USE_FULL_WIDTH)
890 			alignment.horizontal = B_ALIGN_USE_FULL_WIDTH;
891 		if (childAlignment.vertical == B_ALIGN_USE_FULL_HEIGHT)
892 			alignment.vertical = B_ALIGN_USE_FULL_HEIGHT;
893 
894 		fLayoutData->alignment = alignment;
895 	} else {
896 		fLayoutData->min.Set(minWidth, addHeight - 1);
897 		fLayoutData->max.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
898 		fLayoutData->preferred = fLayoutData->min;
899 		fLayoutData->alignment = alignment;
900 	}
901 
902 	fLayoutData->valid = true;
903 	ResetLayoutInvalidation();
904 }
905 
906 
907 extern "C" void
908 B_IF_GCC_2(InvalidateLayout__4BBoxb, _ZN4BBox16InvalidateLayoutEb)(
909 	BBox* box, bool descendants)
910 {
911 	perform_data_layout_invalidated data;
912 	data.descendants = descendants;
913 
914 	box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
915 }
916 
917