2 * Copyright 2001-2013 Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
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
20 #include <ControlLook.h>
22 #include <LayoutUtils.h>
26 #include <binary_compatibility/Interface.h>
29 struct BBox::LayoutData
{
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
42 bool valid
; // validity the other fields
46 BBox::BBox(BRect frame
, const char* name
, uint32 resizingMode
, uint32 flags
,
49 BView(frame
, name
, resizingMode
, flags
| B_WILL_DRAW
| B_FRAME_EVENTS
),
52 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
53 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
59 BBox::BBox(const char* name
, uint32 flags
, border_style border
, BView
* child
)
61 BView(name
, flags
| B_WILL_DRAW
| B_FRAME_EVENTS
),
64 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
65 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
74 BBox::BBox(border_style border
, BView
* child
)
76 BView(NULL
, B_WILL_DRAW
| B_FRAME_EVENTS
| B_NAVIGABLE_JUMP
),
79 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
80 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
89 BBox::BBox(BMessage
* archive
)
92 fStyle(B_FANCY_BORDER
)
107 BBox::Instantiate(BMessage
* archive
)
109 if (validate_instantiation(archive
, "BBox"))
110 return new BBox(archive
);
117 BBox::Archive(BMessage
* archive
, bool deep
) const
119 status_t ret
= BView::Archive(archive
, deep
);
121 if (fLabel
&& ret
== B_OK
)
122 ret
= archive
->AddString("_label", fLabel
);
124 if (fLabelView
&& ret
== B_OK
)
125 ret
= archive
->AddBool("_lblview", true);
127 if (fStyle
!= B_FANCY_BORDER
&& ret
== B_OK
)
128 ret
= archive
->AddInt32("_style", fStyle
);
135 BBox::SetBorder(border_style border
)
137 if (border
== fStyle
)
144 if (Window() != NULL
&& LockLooper()) {
158 //! This function is not part of the R5 API and is not yet finalized yet
160 BBox::TopBorderOffset()
162 _ValidateLayoutData();
164 if (fLabel
!= NULL
|| fLabelView
!= NULL
)
165 return fLayoutData
->label_box
.Height() / 2;
171 //! This function is not part of the R5 API and is not yet finalized yet
175 _ValidateLayoutData();
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
;
188 BBox::SetLabel(const char* string
)
193 fLabel
= strdup(string
);
203 BBox::SetLabel(BView
* viewLabel
)
208 fLabelView
= viewLabel
;
209 fLabelView
->MoveTo(10.0f
, 0.0f
);
210 AddChild(fLabelView
, ChildAt(0));
230 BBox::LabelView() const
237 BBox::Draw(BRect updateRect
)
239 _ValidateLayoutData();
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
);
249 ConstrainClippingRegion(&update
);
250 } else if (fLabelView
!= NULL
)
251 labelBox
= fLabelView
->Bounds();
255 _DrawFancy(labelBox
);
259 _DrawPlain(labelBox
);
267 ConstrainClippingRegion(NULL
);
269 font_height fontHeight
;
270 GetFontHeight(&fontHeight
);
272 SetHighColor(0, 0, 0);
273 DrawString(fLabel
, BPoint(10.0f
, ceilf(fontHeight
.ascent
)));
281 BBox::AttachedToWindow()
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
);
294 // The box could have been resized in the mean time
300 BBox::DetachedFromWindow()
302 BView::DetachedFromWindow();
309 BView::AllAttached();
316 BView::AllDetached();
321 BBox::FrameResized(float width
, float height
)
323 BRect
bounds(Bounds());
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;
331 BRect
invalid(bounds
);
332 if (fBounds
.right
< bounds
.right
) {
334 invalid
.left
= fBounds
.right
- borderSize
;
335 invalid
.right
= fBounds
.right
;
338 } else if (fBounds
.right
> bounds
.right
) {
340 invalid
.left
= bounds
.right
- borderSize
;
346 if (fBounds
.bottom
< bounds
.bottom
) {
348 invalid
.top
= fBounds
.bottom
- borderSize
;
349 invalid
.bottom
= fBounds
.bottom
;
352 } else if (fBounds
.bottom
> bounds
.bottom
) {
354 invalid
.top
= bounds
.bottom
- borderSize
;
360 fBounds
.right
= bounds
.right
;
361 fBounds
.bottom
= bounds
.bottom
;
366 BBox::MessageReceived(BMessage
* message
)
368 BView::MessageReceived(message
);
373 BBox::MouseDown(BPoint point
)
375 BView::MouseDown(point
);
380 BBox::MouseUp(BPoint point
)
382 BView::MouseUp(point
);
387 BBox::WindowActivated(bool active
)
389 BView::WindowActivated(active
);
394 BBox::MouseMoved(BPoint point
, uint32 transit
, const BMessage
* message
)
396 BView::MouseMoved(point
, transit
, message
);
401 BBox::FrameMoved(BPoint newLocation
)
403 BView::FrameMoved(newLocation
);
408 BBox::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
409 int32 what
, const char* property
)
411 return BView::ResolveSpecifier(message
, index
, specifier
, what
, property
);
416 BBox::ResizeToPreferred()
419 GetPreferredSize(&width
, &height
);
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();
427 BView::ResizeTo(width
, height
);
432 BBox::GetPreferredSize(float* _width
, float* _height
)
434 _ValidateLayoutData();
437 *_width
= fLayoutData
->preferred
.width
;
439 *_height
= fLayoutData
->preferred
.height
;
444 BBox::MakeFocus(bool focused
)
446 BView::MakeFocus(focused
);
451 BBox::GetSupportedSuites(BMessage
* message
)
453 return BView::GetSupportedSuites(message
);
458 BBox::Perform(perform_code code
, void* _data
)
461 case PERFORM_CODE_MIN_SIZE
:
462 ((perform_data_min_size
*)_data
)->return_value
465 case PERFORM_CODE_MAX_SIZE
:
466 ((perform_data_max_size
*)_data
)->return_value
469 case PERFORM_CODE_PREFERRED_SIZE
:
470 ((perform_data_preferred_size
*)_data
)->return_value
471 = BBox::PreferredSize();
473 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
474 ((perform_data_layout_alignment
*)_data
)->return_value
475 = BBox::LayoutAlignment();
477 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
478 ((perform_data_has_height_for_width
*)_data
)->return_value
479 = BBox::HasHeightForWidth();
481 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
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
,
489 case PERFORM_CODE_SET_LAYOUT
:
491 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
492 BBox::SetLayout(data
->layout
);
495 case PERFORM_CODE_LAYOUT_INVALIDATED
:
497 perform_data_layout_invalidated
* data
498 = (perform_data_layout_invalidated
*)_data
;
499 BBox::LayoutInvalidated(data
->descendants
);
502 case PERFORM_CODE_DO_LAYOUT
:
509 return BView::Perform(code
, _data
);
516 _ValidateLayoutData();
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
);
528 _ValidateLayoutData();
530 BSize size
= (GetLayout() ? GetLayout()->MaxSize() : fLayoutData
->max
);
531 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size
);
536 BBox::PreferredSize()
538 _ValidateLayoutData();
540 BSize size
= (GetLayout() ? GetLayout()->PreferredSize()
541 : fLayoutData
->preferred
);
542 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size
);
547 BBox::LayoutAlignment()
549 _ValidateLayoutData();
551 BAlignment alignment
= (GetLayout() ? GetLayout()->Alignment()
552 : fLayoutData
->alignment
);
553 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), alignment
);
558 BBox::LayoutInvalidated(bool descendants
)
560 fLayoutData
->valid
= false;
567 // Bail out, if we shan't do layout.
568 if (!(Flags() & B_SUPPORTS_LAYOUT
))
571 BLayout
* layout
= GetLayout();
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
577 if (layout
!= NULL
) {
579 RemoveChild(fLabelView
);
583 if (fLabelView
!= NULL
) {
584 DisableLayoutInvalidation();
585 // don't trigger a relayout
586 AddChild(fLabelView
, ChildAt(0));
587 EnableLayoutInvalidation();
591 _ValidateLayoutData();
593 // Even if the user set a layout, restore label view to it's
596 // layout the label view
597 if (fLabelView
!= NULL
) {
598 fLabelView
->MoveTo(fLayoutData
->label_box
.LeftTop());
599 fLabelView
->ResizeTo(fLayoutData
->label_box
.Size());
602 // If we have layout return here and do not 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
;
614 BLayoutUtils::AlignInFrame(child
, frame
);
619 void BBox::_ReservedBox1() {}
620 void BBox::_ReservedBox2() {}
624 BBox::operator=(const BBox
&)
631 BBox::_InitObject(BMessage
* archive
)
637 fLayoutData
= new LayoutData
;
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
;
650 SetFont(&font
, flags
);
652 if (archive
!= NULL
) {
654 if (archive
->FindString("_label", &string
) == B_OK
)
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
;
666 if (archive
->FindBool("_lblview", &hasLabelView
) == B_OK
)
667 fLabelView
= ChildAt(0);
673 BBox::_DrawPlain(BRect labelBox
)
675 BRect rect
= Bounds();
676 rect
.top
+= TopBorderOffset();
680 if (be_control_look
!= NULL
) {
681 lightTint
= B_LIGHTEN_1_TINT
;
682 shadowTint
= B_DARKEN_1_TINT
;
684 lightTint
= B_LIGHTEN_MAX_TINT
;
685 shadowTint
= B_DARKEN_3_TINT
;
688 if (rect
.Height() == 0.0 || rect
.Width() == 0.0) {
690 rgb_color shadow
= tint_color(ViewColor(), B_DARKEN_2_TINT
);
692 SetHighColor(shadow
);
693 StrokeLine(rect
.LeftTop(),rect
.RightBottom());
696 rgb_color light
= tint_color(ViewColor(), lightTint
);
697 rgb_color shadow
= tint_color(ViewColor(), shadowTint
);
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
);
714 BBox::_DrawFancy(BRect labelBox
)
716 BRect rect
= Bounds();
717 rect
.top
+= TopBorderOffset();
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
);
731 be_control_look
->DrawGroupFrame(this, rect
, rect
, base
);
736 rgb_color light
= tint_color(ViewColor(), B_LIGHTEN_MAX_TINT
);
737 rgb_color shadow
= tint_color(ViewColor(), B_DARKEN_3_TINT
);
739 if (rect
.Height() == 1.0) {
740 // used as horizontal separator
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
);
747 } else if (rect
.Width() == 1.0) {
748 // used as vertical separator
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
);
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
);
767 rect
.InsetBy(1.0, 1.0);
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
);
790 } else if (fLabelView
) {
791 fLabelView
->RemoveSelf();
801 for (int32 i
= 0; BView
* view
= ChildAt(i
); i
++) {
802 if (view
!= fLabelView
)
811 BBox::_ValidateLayoutData()
813 if (fLayoutData
->valid
)
816 // compute the label box, width and height
818 float labelHeight
= 0; // height of the label (pixel count)
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;
838 fLayoutData
->insets
.Set(1, 1, 1, 1);
841 fLayoutData
->insets
.Set(3, 3, 3, 3);
845 fLayoutData
->insets
.Set(0, 0, 0, 0);
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
;
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
;
857 // compute the minimal width induced by the label
860 minWidth
= fLayoutData
->label_box
.right
+ fLayoutData
->insets
.right
;
862 minWidth
= addWidth
- 1;
864 BAlignment
alignment(B_ALIGN_HORIZONTAL_CENTER
, B_ALIGN_VERTICAL_CENTER
);
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();
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);
880 if (min
.width
< minWidth
)
881 min
.width
= minWidth
;
882 BLayoutUtils::FixSizeConstraints(min
, max
, preferred
);
884 fLayoutData
->min
= min
;
885 fLayoutData
->max
= max
;
886 fLayoutData
->preferred
= preferred
;
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
;
894 fLayoutData
->alignment
= alignment
;
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
;
902 fLayoutData
->valid
= true;
903 ResetLayoutInvalidation();
908 B_IF_GCC_2(InvalidateLayout__4BBoxb
, _ZN4BBox16InvalidateLayoutEb
)(
909 BBox
* box
, bool descendants
)
911 perform_data_layout_invalidated data
;
912 data
.descendants
= descendants
;
914 box
->Perform(PERFORM_CODE_LAYOUT_INVALIDATED
, &data
);