btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / interface / Box.cpp
blob70d53d7ac0dc3f3769069ec8439bfb0123463147
1 /*
2 * Copyright 2001-2017 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
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
14 #include <Box.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include <ControlLook.h>
21 #include <Layout.h>
22 #include <LayoutUtils.h>
23 #include <Message.h>
24 #include <Region.h>
26 #include <binary_compatibility/Interface.h>
29 struct BBox::LayoutData {
30 LayoutData()
31 : valid(false)
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
46 BBox::BBox(BRect frame, const char* name, uint32 resizingMode, uint32 flags,
47 border_style border)
49 BView(frame, name, resizingMode, flags | B_WILL_DRAW | B_FRAME_EVENTS),
50 fStyle(border)
52 _InitObject();
56 BBox::BBox(const char* name, uint32 flags, border_style border, BView* child)
58 BView(name, flags | B_WILL_DRAW | B_FRAME_EVENTS),
59 fStyle(border)
61 _InitObject();
63 if (child)
64 AddChild(child);
68 BBox::BBox(border_style border, BView* child)
70 BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP),
71 fStyle(border)
73 _InitObject();
75 if (child)
76 AddChild(child);
80 BBox::BBox(BMessage* archive)
82 BView(archive),
83 fStyle(B_FANCY_BORDER)
85 _InitObject(archive);
89 BBox::~BBox()
91 _ClearLabel();
93 delete fLayoutData;
97 BArchivable*
98 BBox::Instantiate(BMessage* archive)
100 if (validate_instantiation(archive, "BBox"))
101 return new BBox(archive);
103 return NULL;
107 status_t
108 BBox::Archive(BMessage* archive, bool deep) const
110 status_t ret = BView::Archive(archive, deep);
112 if (fLabel && ret == B_OK)
113 ret = archive->AddString("_label", fLabel);
115 if (fLabelView && ret == B_OK)
116 ret = archive->AddBool("_lblview", true);
118 if (fStyle != B_FANCY_BORDER && ret == B_OK)
119 ret = archive->AddInt32("_style", fStyle);
121 return ret;
125 void
126 BBox::SetBorder(border_style border)
128 if (border == fStyle)
129 return;
131 fStyle = border;
133 InvalidateLayout();
135 if (Window() != NULL && LockLooper()) {
136 Invalidate();
137 UnlockLooper();
142 border_style
143 BBox::Border() const
145 return fStyle;
149 //! This function is not part of the R5 API and is not yet finalized yet
150 float
151 BBox::TopBorderOffset()
153 _ValidateLayoutData();
155 if (fLabel != NULL || fLabelView != NULL)
156 return fLayoutData->label_box.Height() / 2;
158 return 0;
162 //! This function is not part of the R5 API and is not yet finalized yet
163 BRect
164 BBox::InnerFrame()
166 _ValidateLayoutData();
168 BRect frame(Bounds());
169 frame.left += fLayoutData->insets.left;
170 frame.top += fLayoutData->insets.top;
171 frame.right -= fLayoutData->insets.right;
172 frame.bottom -= fLayoutData->insets.bottom;
174 return frame;
178 void
179 BBox::SetLabel(const char* string)
181 _ClearLabel();
183 if (string)
184 fLabel = strdup(string);
186 InvalidateLayout();
188 if (Window())
189 Invalidate();
193 status_t
194 BBox::SetLabel(BView* viewLabel)
196 _ClearLabel();
198 if (viewLabel) {
199 fLabelView = viewLabel;
200 fLabelView->MoveTo(10.0f, 0.0f);
201 AddChild(fLabelView, ChildAt(0));
204 InvalidateLayout();
206 if (Window())
207 Invalidate();
209 return B_OK;
213 const char*
214 BBox::Label() const
216 return fLabel;
220 BView*
221 BBox::LabelView() const
223 return fLabelView;
227 void
228 BBox::Draw(BRect updateRect)
230 _ValidateLayoutData();
232 PushState();
234 BRect labelBox = BRect(0, 0, 0, 0);
235 if (fLabel != NULL) {
236 labelBox = fLayoutData->label_box;
237 BRegion update(updateRect);
238 update.Exclude(labelBox);
240 ConstrainClippingRegion(&update);
241 } else if (fLabelView != NULL)
242 labelBox = fLabelView->Bounds();
244 switch (fStyle) {
245 case B_FANCY_BORDER:
246 _DrawFancy(labelBox);
247 break;
249 case B_PLAIN_BORDER:
250 _DrawPlain(labelBox);
251 break;
253 default:
254 break;
257 if (fLabel) {
258 ConstrainClippingRegion(NULL);
260 font_height fontHeight;
261 GetFontHeight(&fontHeight);
263 SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
264 DrawString(fLabel, BPoint(10.0f, ceilf(fontHeight.ascent)));
267 PopState();
271 void
272 BBox::AttachedToWindow()
274 AdoptParentColors();
276 // Force low color to match view color for proper label drawing.
277 float viewTint = B_NO_TINT;
278 float lowTint = B_NO_TINT;
280 if (LowUIColor(&lowTint) != ViewUIColor(&viewTint) || viewTint != lowTint)
281 SetLowUIColor(ViewUIColor(), viewTint);
282 else if (LowColor() != ViewColor())
283 SetLowColor(ViewColor());
285 if (ViewColor() == B_TRANSPARENT_COLOR)
286 AdoptSystemColors();
288 // The box could have been resized in the mean time
289 fBounds = Bounds().OffsetToCopy(0, 0);
293 void
294 BBox::DetachedFromWindow()
296 BView::DetachedFromWindow();
300 void
301 BBox::AllAttached()
303 BView::AllAttached();
307 void
308 BBox::AllDetached()
310 BView::AllDetached();
314 void
315 BBox::FrameResized(float width, float height)
317 BRect bounds(Bounds());
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;
325 // Horizontal
326 BRect invalid(bounds);
327 if (fBounds.Width() < bounds.Width()) {
328 // enlarging
329 invalid.left = bounds.left + fBounds.right - borderSize;
330 invalid.right = bounds.left + fBounds.right;
332 Invalidate(invalid);
333 } else if (fBounds.Width() > bounds.Width()) {
334 // shrinking
335 invalid.left = bounds.left + bounds.right - borderSize;
337 Invalidate(invalid);
340 // Vertical
341 invalid = bounds;
342 if (fBounds.Height() < bounds.Height()) {
343 // enlarging
344 invalid.top = bounds.top + fBounds.bottom - borderSize;
345 invalid.bottom = bounds.top + fBounds.bottom;
347 Invalidate(invalid);
348 } else if (fBounds.Height() > bounds.Height()) {
349 // shrinking
350 invalid.top = bounds.top + bounds.bottom - borderSize;
352 Invalidate(invalid);
356 fBounds.right = width;
357 fBounds.bottom = height;
361 void
362 BBox::MessageReceived(BMessage* message)
364 BView::MessageReceived(message);
368 void
369 BBox::MouseDown(BPoint point)
371 BView::MouseDown(point);
375 void
376 BBox::MouseUp(BPoint point)
378 BView::MouseUp(point);
382 void
383 BBox::WindowActivated(bool active)
385 BView::WindowActivated(active);
389 void
390 BBox::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
392 BView::MouseMoved(point, transit, message);
396 void
397 BBox::FrameMoved(BPoint newLocation)
399 BView::FrameMoved(newLocation);
403 BHandler*
404 BBox::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
405 int32 what, const char* property)
407 return BView::ResolveSpecifier(message, index, specifier, what, property);
411 void
412 BBox::ResizeToPreferred()
414 float width, height;
415 GetPreferredSize(&width, &height);
417 // make sure the box don't get smaller than it already is
418 if (width < Bounds().Width())
419 width = Bounds().Width();
420 if (height < Bounds().Height())
421 height = Bounds().Height();
423 BView::ResizeTo(width, height);
427 void
428 BBox::GetPreferredSize(float* _width, float* _height)
430 _ValidateLayoutData();
432 if (_width)
433 *_width = fLayoutData->preferred.width;
434 if (_height)
435 *_height = fLayoutData->preferred.height;
439 void
440 BBox::MakeFocus(bool focused)
442 BView::MakeFocus(focused);
446 status_t
447 BBox::GetSupportedSuites(BMessage* message)
449 return BView::GetSupportedSuites(message);
453 status_t
454 BBox::Perform(perform_code code, void* _data)
456 switch (code) {
457 case PERFORM_CODE_MIN_SIZE:
458 ((perform_data_min_size*)_data)->return_value
459 = BBox::MinSize();
460 return B_OK;
461 case PERFORM_CODE_MAX_SIZE:
462 ((perform_data_max_size*)_data)->return_value
463 = BBox::MaxSize();
464 return B_OK;
465 case PERFORM_CODE_PREFERRED_SIZE:
466 ((perform_data_preferred_size*)_data)->return_value
467 = BBox::PreferredSize();
468 return B_OK;
469 case PERFORM_CODE_LAYOUT_ALIGNMENT:
470 ((perform_data_layout_alignment*)_data)->return_value
471 = BBox::LayoutAlignment();
472 return B_OK;
473 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
474 ((perform_data_has_height_for_width*)_data)->return_value
475 = BBox::HasHeightForWidth();
476 return B_OK;
477 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
479 perform_data_get_height_for_width* data
480 = (perform_data_get_height_for_width*)_data;
481 BBox::GetHeightForWidth(data->width, &data->min, &data->max,
482 &data->preferred);
483 return B_OK;
485 case PERFORM_CODE_SET_LAYOUT:
487 perform_data_set_layout* data = (perform_data_set_layout*)_data;
488 BBox::SetLayout(data->layout);
489 return B_OK;
491 case PERFORM_CODE_LAYOUT_INVALIDATED:
493 perform_data_layout_invalidated* data
494 = (perform_data_layout_invalidated*)_data;
495 BBox::LayoutInvalidated(data->descendants);
496 return B_OK;
498 case PERFORM_CODE_DO_LAYOUT:
500 BBox::DoLayout();
501 return B_OK;
505 return BView::Perform(code, _data);
509 BSize
510 BBox::MinSize()
512 _ValidateLayoutData();
514 BSize size = (GetLayout() ? GetLayout()->MinSize() : fLayoutData->min);
515 if (size.width < fLayoutData->min.width)
516 size.width = fLayoutData->min.width;
517 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
521 BSize
522 BBox::MaxSize()
524 _ValidateLayoutData();
526 BSize size = (GetLayout() ? GetLayout()->MaxSize() : fLayoutData->max);
527 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
531 BSize
532 BBox::PreferredSize()
534 _ValidateLayoutData();
536 BSize size = (GetLayout() ? GetLayout()->PreferredSize()
537 : fLayoutData->preferred);
538 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
542 BAlignment
543 BBox::LayoutAlignment()
545 _ValidateLayoutData();
547 BAlignment alignment = (GetLayout() ? GetLayout()->Alignment()
548 : fLayoutData->alignment);
549 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), alignment);
553 void
554 BBox::LayoutInvalidated(bool descendants)
556 fLayoutData->valid = false;
560 void
561 BBox::DoLayout()
563 // Bail out, if we shan't do layout.
564 if (!(Flags() & B_SUPPORTS_LAYOUT))
565 return;
567 BLayout* layout = GetLayout();
569 // If the user set a layout, let the base class version call its
570 // hook. In case when we have BView as a label, remove it from child list
571 // so it won't be layouted with the rest of views and add it again
572 // after that.
573 if (layout != NULL) {
574 if (fLabelView)
575 RemoveChild(fLabelView);
577 BView::DoLayout();
579 if (fLabelView != NULL) {
580 DisableLayoutInvalidation();
581 // don't trigger a relayout
582 AddChild(fLabelView, ChildAt(0));
583 EnableLayoutInvalidation();
587 _ValidateLayoutData();
589 // Even if the user set a layout, restore label view to it's
590 // desired position.
592 // layout the label view
593 if (fLabelView != NULL) {
594 fLabelView->MoveTo(fLayoutData->label_box.LeftTop());
595 fLabelView->ResizeTo(fLayoutData->label_box.Size());
598 // If we have layout return here and do not layout the child
599 if (layout != NULL)
600 return;
602 // layout the child
603 BView* child = _Child();
604 if (child != NULL) {
605 BRect frame(Bounds());
606 frame.left += fLayoutData->insets.left;
607 frame.top += fLayoutData->insets.top;
608 frame.right -= fLayoutData->insets.right;
609 frame.bottom -= fLayoutData->insets.bottom;
611 if ((child->Flags() & B_SUPPORTS_LAYOUT) != 0)
612 BLayoutUtils::AlignInFrame(child, frame);
613 else
614 child->MoveTo(frame.LeftTop());
619 void BBox::_ReservedBox1() {}
620 void BBox::_ReservedBox2() {}
623 BBox &
624 BBox::operator=(const BBox &)
626 return *this;
630 void
631 BBox::_InitObject(BMessage* archive)
633 fBounds = Bounds().OffsetToCopy(0, 0);
635 fLabel = NULL;
636 fLabelView = NULL;
637 fLayoutData = new LayoutData;
639 int32 flags = 0;
641 BFont font(be_bold_font);
643 if (!archive || !archive->HasString("_fname"))
644 flags = B_FONT_FAMILY_AND_STYLE;
646 if (!archive || !archive->HasFloat("_fflt"))
647 flags |= B_FONT_SIZE;
649 if (flags != 0)
650 SetFont(&font, flags);
652 if (archive != NULL) {
653 const char* string;
654 if (archive->FindString("_label", &string) == B_OK)
655 SetLabel(string);
657 bool fancy;
658 int32 style;
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;
665 bool hasLabelView;
666 if (archive->FindBool("_lblview", &hasLabelView) == B_OK)
667 fLabelView = ChildAt(0);
670 AdoptSystemColors();
674 void
675 BBox::_DrawPlain(BRect labelBox)
677 BRect rect = Bounds();
678 rect.top += TopBorderOffset();
680 float lightTint;
681 float shadowTint;
682 lightTint = B_LIGHTEN_1_TINT;
683 shadowTint = B_DARKEN_1_TINT;
685 if (rect.Height() == 0.0 || rect.Width() == 0.0) {
686 // used as separator
687 rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
689 SetHighColor(shadow);
690 StrokeLine(rect.LeftTop(),rect.RightBottom());
691 } else {
692 // used as box
693 rgb_color light = tint_color(ViewColor(), lightTint);
694 rgb_color shadow = tint_color(ViewColor(), shadowTint);
696 BeginLineArray(4);
697 AddLine(BPoint(rect.left, rect.bottom),
698 BPoint(rect.left, rect.top), light);
699 AddLine(BPoint(rect.left + 1.0f, rect.top),
700 BPoint(rect.right, rect.top), light);
701 AddLine(BPoint(rect.left + 1.0f, rect.bottom),
702 BPoint(rect.right, rect.bottom), shadow);
703 AddLine(BPoint(rect.right, rect.bottom - 1.0f),
704 BPoint(rect.right, rect.top + 1.0f), shadow);
705 EndLineArray();
710 void
711 BBox::_DrawFancy(BRect labelBox)
713 BRect rect = Bounds();
714 rect.top += TopBorderOffset();
716 rgb_color base = ViewColor();
717 if (rect.Height() == 1.0) {
718 // used as horizontal separator
719 be_control_look->DrawGroupFrame(this, rect, rect, base,
720 BControlLook::B_TOP_BORDER);
721 } else if (rect.Width() == 1.0) {
722 // used as vertical separator
723 be_control_look->DrawGroupFrame(this, rect, rect, base,
724 BControlLook::B_LEFT_BORDER);
725 } else {
726 // used as box
727 be_control_look->DrawGroupFrame(this, rect, rect, base);
732 void
733 BBox::_ClearLabel()
735 if (fLabel) {
736 free(fLabel);
737 fLabel = NULL;
738 } else if (fLabelView) {
739 fLabelView->RemoveSelf();
740 delete fLabelView;
741 fLabelView = NULL;
746 BView*
747 BBox::_Child() const
749 for (int32 i = 0; BView* view = ChildAt(i); i++) {
750 if (view != fLabelView)
751 return view;
754 return NULL;
758 void
759 BBox::_ValidateLayoutData()
761 if (fLayoutData->valid)
762 return;
764 // compute the label box, width and height
765 bool label = true;
766 float labelHeight = 0; // height of the label (pixel count)
767 if (fLabel) {
768 // leave 6 pixels of the frame, and have a gap of 4 pixels between
769 // the frame and the text on either side
770 font_height fontHeight;
771 GetFontHeight(&fontHeight);
772 fLayoutData->label_box.Set(6.0f, 0, 14.0f + StringWidth(fLabel),
773 ceilf(fontHeight.ascent));
774 labelHeight = ceilf(fontHeight.ascent + fontHeight.descent) + 1;
775 } else if (fLabelView) {
776 // the label view is placed at (0, 10) at its preferred size
777 BSize size = fLabelView->PreferredSize();
778 fLayoutData->label_box.Set(10, 0, 10 + size.width, size.height);
779 labelHeight = size.height + 1;
780 } else
781 label = false;
783 // border
784 switch (fStyle) {
785 case B_PLAIN_BORDER:
786 fLayoutData->insets.Set(1, 1, 1, 1);
787 break;
788 case B_FANCY_BORDER:
789 fLayoutData->insets.Set(3, 3, 3, 3);
790 break;
791 case B_NO_BORDER:
792 default:
793 fLayoutData->insets.Set(0, 0, 0, 0);
794 break;
797 // if there's a label, the top inset will be dictated by the label
798 if (label && labelHeight > fLayoutData->insets.top)
799 fLayoutData->insets.top = labelHeight;
801 // total number of pixel the border adds
802 float addWidth = fLayoutData->insets.left + fLayoutData->insets.right;
803 float addHeight = fLayoutData->insets.top + fLayoutData->insets.bottom;
805 // compute the minimal width induced by the label
806 float minWidth;
807 if (label)
808 minWidth = fLayoutData->label_box.right + fLayoutData->insets.right;
809 else
810 minWidth = addWidth - 1;
812 BAlignment alignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER);
814 // finally consider the child constraints, if we shall support layout
815 BView* child = _Child();
816 if (child && (child->Flags() & B_SUPPORTS_LAYOUT)) {
817 BSize min = child->MinSize();
818 BSize max = child->MaxSize();
819 BSize preferred = child->PreferredSize();
821 min.width += addWidth;
822 min.height += addHeight;
823 preferred.width += addWidth;
824 preferred.height += addHeight;
825 max.width = BLayoutUtils::AddDistances(max.width, addWidth - 1);
826 max.height = BLayoutUtils::AddDistances(max.height, addHeight - 1);
828 if (min.width < minWidth)
829 min.width = minWidth;
830 BLayoutUtils::FixSizeConstraints(min, max, preferred);
832 fLayoutData->min = min;
833 fLayoutData->max = max;
834 fLayoutData->preferred = preferred;
836 BAlignment childAlignment = child->LayoutAlignment();
837 if (childAlignment.horizontal == B_ALIGN_USE_FULL_WIDTH)
838 alignment.horizontal = B_ALIGN_USE_FULL_WIDTH;
839 if (childAlignment.vertical == B_ALIGN_USE_FULL_HEIGHT)
840 alignment.vertical = B_ALIGN_USE_FULL_HEIGHT;
842 fLayoutData->alignment = alignment;
843 } else {
844 fLayoutData->min.Set(minWidth, addHeight - 1);
845 fLayoutData->max.Set(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
846 fLayoutData->preferred = fLayoutData->min;
847 fLayoutData->alignment = alignment;
850 fLayoutData->valid = true;
851 ResetLayoutInvalidation();
855 extern "C" void
856 B_IF_GCC_2(InvalidateLayout__4BBoxb, _ZN4BBox16InvalidateLayoutEb)(
857 BBox* box, bool descendants)
859 perform_data_layout_invalidated data;
860 data.descendants = descendants;
862 box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);