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