2 * Copyright 2001-2008, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Marc Flerackers (mflerackers@androme.be)
7 * Axel Dörfler, axeld@pinc-software.de
8 * Stephan Aßmus <superstippi@gmx.de>
11 /*! BStatusBar displays a "percentage-of-completion" gauge. */
12 #include <StatusBar.h>
18 #include <ControlLook.h>
20 #include <LayoutUtils.h>
24 #include <binary_compatibility/Interface.h>
27 static const rgb_color kDefaultBarColor
= {50, 150, 255, 255};
30 BStatusBar::BStatusBar(BRect frame
, const char *name
, const char *label
,
31 const char *trailingLabel
)
33 BView(frame
, name
, B_FOLLOW_LEFT
| B_FOLLOW_TOP
, B_WILL_DRAW
),
35 fTrailingLabel(trailingLabel
)
41 BStatusBar::BStatusBar(const char *name
, const char *label
,
42 const char *trailingLabel
)
44 BView(BRect(0, 0, -1, -1), name
, B_FOLLOW_LEFT
| B_FOLLOW_TOP
,
45 B_WILL_DRAW
| B_SUPPORTS_LAYOUT
),
47 fTrailingLabel(trailingLabel
)
53 BStatusBar::BStatusBar(BMessage
*archive
)
59 archive
->FindString("_label", &fLabel
);
60 archive
->FindString("_tlabel", &fTrailingLabel
);
62 archive
->FindString("_text", &fText
);
63 archive
->FindString("_ttext", &fTrailingText
);
66 if (archive
->FindFloat("_high", &floatValue
) == B_OK
) {
67 fBarHeight
= floatValue
;
68 fCustomBarHeight
= true;
72 if (archive
->FindInt32("_bcolor", (int32
*)&color
) == B_OK
)
73 fBarColor
= *(rgb_color
*)&color
;
75 if (archive
->FindFloat("_val", &floatValue
) == B_OK
)
76 fCurrent
= floatValue
;
77 if (archive
->FindFloat("_max", &floatValue
) == B_OK
)
82 BStatusBar::~BStatusBar()
88 BStatusBar::Instantiate(BMessage
*archive
)
90 if (validate_instantiation(archive
, "BStatusBar"))
91 return new BStatusBar(archive
);
98 BStatusBar::Archive(BMessage
*archive
, bool deep
) const
100 status_t err
= BView::Archive(archive
, deep
);
104 if (fCustomBarHeight
)
105 err
= archive
->AddFloat("_high", fBarHeight
);
107 if (err
== B_OK
&& fBarColor
!= kDefaultBarColor
)
108 err
= archive
->AddInt32("_bcolor", (const uint32
&)fBarColor
);
110 if (err
== B_OK
&& fCurrent
!= 0)
111 err
= archive
->AddFloat("_val", fCurrent
);
112 if (err
== B_OK
&& fMax
!= 100 )
113 err
= archive
->AddFloat("_max", fMax
);
115 if (err
== B_OK
&& fText
.Length())
116 err
= archive
->AddString("_text", fText
);
117 if (err
== B_OK
&& fTrailingText
.Length())
118 err
= archive
->AddString("_ttext", fTrailingText
);
120 if (err
== B_OK
&& fLabel
.Length())
121 err
= archive
->AddString("_label", fLabel
);
122 if (err
== B_OK
&& fTrailingLabel
.Length())
123 err
= archive
->AddString ("_tlabel", fTrailingLabel
);
133 BStatusBar::AttachedToWindow()
135 // resize so that the height fits
137 GetPreferredSize(&width
, &height
);
138 ResizeTo(Bounds().Width(), height
);
140 SetViewColor(B_TRANSPARENT_COLOR
);
141 rgb_color lowColor
= B_TRANSPARENT_COLOR
;
143 BView
* parent
= Parent();
145 lowColor
= parent
->ViewColor();
147 if (lowColor
== B_TRANSPARENT_COLOR
)
148 lowColor
= ui_color(B_PANEL_BACKGROUND_COLOR
);
150 SetLowColor(lowColor
);
152 fTextDivider
= Bounds().Width();
157 BStatusBar::DetachedFromWindow()
159 BView::DetachedFromWindow();
164 BStatusBar::AllAttached()
166 BView::AllAttached();
171 BStatusBar::AllDetached()
173 BView::AllDetached();
181 BStatusBar::WindowActivated(bool state
)
183 BView::WindowActivated(state
);
188 BStatusBar::MakeFocus(bool state
)
190 BView::MakeFocus(state
);
198 BStatusBar::GetPreferredSize(float* _width
, float* _height
)
201 // AttachedToWindow() might not have been called yet
202 *_width
= ceilf(StringWidth(fLabel
.String()))
203 + ceilf(StringWidth(fTrailingLabel
.String()))
204 + ceilf(StringWidth(fText
.String()))
205 + ceilf(StringWidth(fTrailingText
.String()))
210 float labelHeight
= 0;
212 font_height fontHeight
;
213 GetFontHeight(&fontHeight
);
214 labelHeight
= ceilf(fontHeight
.ascent
+ fontHeight
.descent
) + 6;
217 *_height
= labelHeight
+ BarHeight();
223 BStatusBar::MinSize()
226 GetPreferredSize(&width
, &height
);
228 return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(width
, height
));
233 BStatusBar::MaxSize()
236 GetPreferredSize(&width
, &height
);
238 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
239 BSize(B_SIZE_UNLIMITED
, height
));
244 BStatusBar::PreferredSize()
247 GetPreferredSize(&width
, &height
);
249 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
250 BSize(width
, height
));
255 BStatusBar::ResizeToPreferred()
257 BView::ResizeToPreferred();
262 BStatusBar::FrameMoved(BPoint newPosition
)
264 BView::FrameMoved(newPosition
);
269 BStatusBar::FrameResized(float newWidth
, float newHeight
)
271 BView::FrameResized(newWidth
, newHeight
);
280 BStatusBar::Draw(BRect updateRect
)
282 rgb_color backgroundColor
= LowColor();
284 font_height fontHeight
;
285 GetFontHeight(&fontHeight
);
286 BRect barFrame
= _BarFrame(&fontHeight
);
287 BRect outerFrame
= barFrame
.InsetByCopy(-2, -2);
289 BRegion
background(updateRect
);
290 background
.Exclude(outerFrame
);
291 FillRegion(&background
, B_SOLID_LOW
);
295 BRect rect
= outerFrame
;
297 rect
.bottom
= outerFrame
.top
- 1;
299 if (updateRect
.Intersects(rect
)) {
302 leftText
<< fLabel
<< fText
;
305 rightText
<< fTrailingText
<< fTrailingLabel
;
307 float baseLine
= ceilf(fontHeight
.ascent
) + 1;
308 fTextDivider
= rect
.right
;
313 if (rightText
.Length()) {
314 font
.TruncateString(&rightText
, B_TRUNCATE_BEGINNING
, rect
.Width());
315 fTextDivider
-= StringWidth(rightText
.String());
318 if (leftText
.Length()) {
319 float width
= max_c(0.0, fTextDivider
- rect
.left
);
320 font
.TruncateString(&leftText
, B_TRUNCATE_END
, width
);
323 SetHighColor(ui_color(B_CONTROL_TEXT_COLOR
));
325 if (leftText
.Length())
326 DrawString(leftText
.String(), BPoint(rect
.left
, baseLine
));
328 if (rightText
.Length())
329 DrawString(rightText
.String(), BPoint(fTextDivider
, baseLine
));
335 if (!updateRect
.Intersects(outerFrame
))
340 if (be_control_look
!= NULL
) {
341 be_control_look
->DrawStatusBar(this, rect
, updateRect
,
342 backgroundColor
, fBarColor
, _BarPosition(barFrame
));
347 SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR
), B_DARKEN_1_TINT
));
348 StrokeLine(rect
.LeftBottom(), rect
.LeftTop());
349 StrokeLine(rect
.RightTop());
351 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR
), B_LIGHTEN_2_TINT
));
352 StrokeLine(BPoint(rect
.left
+ 1, rect
.bottom
), rect
.RightBottom());
353 StrokeLine(BPoint(rect
.right
, rect
.top
+ 1));
358 SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR
), B_DARKEN_4_TINT
));
359 StrokeLine(rect
.LeftBottom(), rect
.LeftTop());
360 StrokeLine(rect
.RightTop());
362 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
363 StrokeLine(BPoint(rect
.left
+ 1, rect
.bottom
), rect
.RightBottom());
364 StrokeLine(BPoint(rect
.right
, rect
.top
+ 1));
367 rect
.right
= _BarPosition(barFrame
);
371 if (rect
.right
>= rect
.left
) {
373 SetHighColor(tint_color(fBarColor
, B_LIGHTEN_2_TINT
));
374 StrokeLine(rect
.LeftBottom(), rect
.LeftTop());
375 StrokeLine(rect
.RightTop());
377 SetHighColor(tint_color(fBarColor
, B_DARKEN_2_TINT
));
378 StrokeLine(BPoint(rect
.left
+ 1, rect
.bottom
), rect
.RightBottom());
379 StrokeLine(BPoint(rect
.right
, rect
.top
+ 1));
382 SetHighColor(fBarColor
);
383 FillRect(rect
.InsetByCopy(1, 1));
386 if (rect
.right
< barFrame
.right
) {
388 rect
.left
= rect
.right
+ 1;
389 rect
.right
= barFrame
.right
;
390 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR
), B_LIGHTEN_MAX_TINT
));
397 BStatusBar::MessageReceived(BMessage
*message
)
399 switch(message
->what
) {
400 case B_UPDATE_STATUS_BAR
:
403 const char *text
= NULL
, *trailing_text
= NULL
;
405 message
->FindFloat("delta", &delta
);
406 message
->FindString("text", &text
);
407 message
->FindString("trailing_text", &trailing_text
);
409 Update(delta
, text
, trailing_text
);
414 case B_RESET_STATUS_BAR
:
416 const char *label
= NULL
, *trailing_label
= NULL
;
418 message
->FindString("label", &label
);
419 message
->FindString("trailing_label", &trailing_label
);
421 Reset(label
, trailing_label
);
427 BView::MessageReceived(message
);
434 BStatusBar::MouseDown(BPoint point
)
436 BView::MouseDown(point
);
441 BStatusBar::MouseUp(BPoint point
)
443 BView::MouseUp(point
);
448 BStatusBar::MouseMoved(BPoint point
, uint32 transit
, const BMessage
*message
)
450 BView::MouseMoved(point
, transit
, message
);
458 BStatusBar::SetBarColor(rgb_color color
)
467 BStatusBar::SetBarHeight(float barHeight
)
469 float oldHeight
= BarHeight();
471 fCustomBarHeight
= true;
472 fBarHeight
= barHeight
;
474 if (barHeight
== oldHeight
)
477 // resize so that the height fits
478 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0)
482 GetPreferredSize(&width
, &height
);
483 ResizeTo(Bounds().Width(), height
);
489 BStatusBar::SetText(const char* string
)
491 _SetTextData(fText
, string
, fLabel
, false);
496 BStatusBar::SetTrailingText(const char* string
)
498 _SetTextData(fTrailingText
, string
, fTrailingLabel
, true);
503 BStatusBar::SetMaxValue(float max
)
505 // R5 and/or Zeta's SetMaxValue does not trigger an invalidate here.
506 // this is probably not ideal behavior, but it does break apps in some cases
507 // as observed with SpaceMonitor.
508 // TODO: revisit this when we break binary compatibility
514 BStatusBar::Update(float delta
, const char* text
, const char* trailingText
)
516 // If any of these are NULL, the existing text remains (BeBook)
518 text
= fText
.String();
519 if (trailingText
== NULL
)
520 trailingText
= fTrailingText
.String();
521 BStatusBar::SetTo(fCurrent
+ delta
, text
, trailingText
);
526 BStatusBar::Reset(const char *label
, const char *trailingLabel
)
528 // Reset replaces the label and trailing label with copies of the
529 // strings passed as arguments. If either argument is NULL, the
530 // label or trailing label will be deleted and erased.
531 fLabel
= label
? label
: "";
532 fTrailingLabel
= trailingLabel
? trailingLabel
: "";
534 // Reset deletes and erases any text or trailing text
546 BStatusBar::SetTo(float value
, const char* text
, const char* trailingText
)
549 SetTrailingText(trailingText
);
555 if (value
== fCurrent
)
558 BRect barFrame
= _BarFrame();
559 float oldPosition
= _BarPosition(barFrame
);
563 float newPosition
= _BarPosition(barFrame
);
564 if (oldPosition
== newPosition
)
567 // update only the part of the status bar with actual changes
568 BRect update
= barFrame
;
569 if (oldPosition
< newPosition
) {
570 update
.left
= floorf(max_c(oldPosition
- 1, update
.left
));
571 update
.right
= ceilf(newPosition
);
573 update
.left
= floorf(max_c(newPosition
- 1, update
.left
));
574 update
.right
= ceilf(oldPosition
);
577 // TODO: Ask the BControlLook in the first place about dirty rect.
578 if (be_control_look
!= NULL
)
579 update
.InsetBy(-1, -1);
586 BStatusBar::CurrentValue() const
593 BStatusBar::MaxValue() const
600 BStatusBar::BarColor() const
607 BStatusBar::BarHeight() const
609 if (!fCustomBarHeight
&& fBarHeight
== -1) {
610 // the default bar height is as height as the label
611 font_height fontHeight
;
612 GetFontHeight(&fontHeight
);
613 const_cast<BStatusBar
*>(this)->fBarHeight
= fontHeight
.ascent
614 + fontHeight
.descent
+ 5;
617 return ceilf(fBarHeight
);
622 BStatusBar::Text() const
624 return fText
.String();
629 BStatusBar::TrailingText() const
631 return fTrailingText
.String();
636 BStatusBar::Label() const
638 return fLabel
.String();
643 BStatusBar::TrailingLabel() const
645 return fTrailingLabel
.String();
653 BStatusBar::ResolveSpecifier(BMessage
* message
, int32 index
,
654 BMessage
* specifier
, int32 what
, const char *property
)
656 return BView::ResolveSpecifier(message
, index
, specifier
, what
, property
);
661 BStatusBar::GetSupportedSuites(BMessage
* data
)
663 return BView::GetSupportedSuites(data
);
668 BStatusBar::Perform(perform_code code
, void* _data
)
671 case PERFORM_CODE_MIN_SIZE
:
672 ((perform_data_min_size
*)_data
)->return_value
673 = BStatusBar::MinSize();
675 case PERFORM_CODE_MAX_SIZE
:
676 ((perform_data_max_size
*)_data
)->return_value
677 = BStatusBar::MaxSize();
679 case PERFORM_CODE_PREFERRED_SIZE
:
680 ((perform_data_preferred_size
*)_data
)->return_value
681 = BStatusBar::PreferredSize();
683 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
684 ((perform_data_layout_alignment
*)_data
)->return_value
685 = BStatusBar::LayoutAlignment();
687 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
688 ((perform_data_has_height_for_width
*)_data
)->return_value
689 = BStatusBar::HasHeightForWidth();
691 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
693 perform_data_get_height_for_width
* data
694 = (perform_data_get_height_for_width
*)_data
;
695 BStatusBar::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
699 case PERFORM_CODE_SET_LAYOUT
:
701 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
702 BStatusBar::SetLayout(data
->layout
);
705 case PERFORM_CODE_LAYOUT_INVALIDATED
:
707 perform_data_layout_invalidated
* data
708 = (perform_data_layout_invalidated
*)_data
;
709 BStatusBar::LayoutInvalidated(data
->descendants
);
712 case PERFORM_CODE_DO_LAYOUT
:
714 BStatusBar::DoLayout();
719 return BView::Perform(code
, _data
);
727 _ReservedStatusBar1__10BStatusBar(BStatusBar
* self
, float value
,
728 const char* text
, const char* trailingText
)
730 self
->BStatusBar::SetTo(value
, text
, trailingText
);
734 void BStatusBar::_ReservedStatusBar2() {}
735 void BStatusBar::_ReservedStatusBar3() {}
736 void BStatusBar::_ReservedStatusBar4() {}
740 BStatusBar::operator=(const BStatusBar
& other
)
750 BStatusBar::_InitObject()
756 fTextDivider
= Bounds().Width();
758 fBarColor
= kDefaultBarColor
;
759 fCustomBarHeight
= false;
761 SetFlags(Flags() | B_FRAME_EVENTS
);
766 BStatusBar::_SetTextData(BString
& text
, const char* source
,
767 const BString
& combineWith
, bool rightAligned
)
772 // If there were no changes, we don't have to do anything
776 bool oldHasText
= _HasText();
781 newString
<< text
<< combineWith
;
783 newString
<< combineWith
<< text
;
785 if (oldHasText
!= _HasText())
788 font_height fontHeight
;
789 GetFontHeight(&fontHeight
);
791 // Invalidate(BRect(position, 0, position + invalidateWidth,
792 // ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)));
793 // TODO: redrawing the entire area takes care of the edge case
794 // where the left side string changes because of truncation and
795 // part of it needs to be redrawn as well.
796 Invalidate(BRect(0, 0, Bounds().right
,
797 ceilf(fontHeight
.ascent
) + ceilf(fontHeight
.descent
)));
802 Returns the inner bar frame without the surrounding bevel.
805 BStatusBar::_BarFrame(const font_height
* fontHeight
) const
809 if (fontHeight
== NULL
) {
811 GetFontHeight(&height
);
812 top
= ceilf(height
.ascent
+ height
.descent
) + 6;
814 top
= ceilf(fontHeight
->ascent
+ fontHeight
->descent
) + 6;
817 return BRect(2, top
, Bounds().right
- 2, top
+ BarHeight() - 4);
822 BStatusBar::_BarPosition(const BRect
& barFrame
) const
825 return barFrame
.left
- 1;
827 return roundf(barFrame
.left
- 1
828 + (fCurrent
* (barFrame
.Width() + 3) / fMax
));
833 BStatusBar::_HasText() const
835 // Force BeOS behavior where the size of the BStatusBar always included
836 // room for labels, even when there weren't any.
837 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
839 return fLabel
.Length() > 0 || fTrailingLabel
.Length() > 0
840 || fTrailingText
.Length() > 0 || fText
.Length() > 0;