2 * Copyright 2001-2015 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Marc Flerackers (mflerackers@androme.be)
8 * Stefano Ceccherini (burton666@libero.it)
10 * Stephan Aßmus <superstippi@gmx.de>
11 * Ingo Weinhold, ingo_weinhold@gmx.de
21 #include <ControlLook.h>
23 #include <LayoutUtils.h>
27 #include <binary_compatibility/Interface.h>
34 FLAG_WAS_PRESSED
= 0x08,
38 static const float kLabelMargin
= 3;
41 BButton::BButton(BRect frame
, const char* name
, const char* label
,
42 BMessage
* message
, uint32 resizingMode
, uint32 flags
)
44 BControl(frame
, name
, label
, message
, resizingMode
,
45 flags
| B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
),
46 fPreferredSize(-1, -1),
48 fBehavior(B_BUTTON_BEHAVIOR
),
51 // Resize to minimum height if needed
54 float minHeight
= 12.0f
+ (float)ceil(fh
.ascent
+ fh
.descent
);
55 if (Bounds().Height() < minHeight
)
56 ResizeTo(Bounds().Width(), minHeight
);
60 BButton::BButton(const char* name
, const char* label
, BMessage
* message
,
63 BControl(name
, label
, message
,
64 flags
| B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
),
65 fPreferredSize(-1, -1),
67 fBehavior(B_BUTTON_BEHAVIOR
),
73 BButton::BButton(const char* label
, BMessage
* message
)
75 BControl(NULL
, label
, message
,
76 B_WILL_DRAW
| B_NAVIGABLE
| B_FULL_UPDATE_ON_RESIZE
),
77 fPreferredSize(-1, -1),
79 fBehavior(B_BUTTON_BEHAVIOR
),
87 SetPopUpMessage(NULL
);
91 BButton::BButton(BMessage
* data
)
94 fPreferredSize(-1, -1),
96 fBehavior(B_BUTTON_BEHAVIOR
),
99 bool isDefault
= false;
100 if (data
->FindBool("_default", &isDefault
) == B_OK
&& isDefault
)
101 _SetFlag(FLAG_DEFAULT
, true);
102 // NOTE: Default button state will be synchronized with the window
103 // in AttachedToWindow().
108 BButton::Instantiate(BMessage
* data
)
110 if (validate_instantiation(data
, "BButton"))
111 return new(std::nothrow
) BButton(data
);
118 BButton::Archive(BMessage
* data
, bool deep
) const
120 status_t err
= BControl::Archive(data
, deep
);
126 err
= data
->AddBool("_default", true);
133 BButton::Draw(BRect updateRect
)
135 BRect
rect(Bounds());
136 rgb_color background
= ViewColor();
137 rgb_color base
= LowColor();
138 rgb_color textColor
= ui_color(B_CONTROL_TEXT_COLOR
);
140 uint32 flags
= be_control_look
->Flags(this);
141 if (_Flag(FLAG_DEFAULT
))
142 flags
|= BControlLook::B_DEFAULT_BUTTON
;
143 if (_Flag(FLAG_FLAT
) && !IsTracking())
144 flags
|= BControlLook::B_FLAT
;
145 if (_Flag(FLAG_INSIDE
))
146 flags
|= BControlLook::B_HOVER
;
148 be_control_look
->DrawButtonFrame(this, rect
, updateRect
,
149 base
, background
, flags
);
151 if (fBehavior
== B_POP_UP_BEHAVIOR
) {
152 be_control_look
->DrawButtonWithPopUpBackground(this, rect
, updateRect
,
155 be_control_look
->DrawButtonBackground(this, rect
, updateRect
,
159 // always leave some room around the label
160 rect
.InsetBy(kLabelMargin
, kLabelMargin
);
162 const BBitmap
* icon
= IconBitmap(
163 (Value() == B_CONTROL_OFF
164 ? B_INACTIVE_ICON_BITMAP
: B_ACTIVE_ICON_BITMAP
)
165 | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP
));
167 be_control_look
->DrawLabel(this, Label(), icon
, rect
, updateRect
, base
,
168 flags
, BAlignment(B_ALIGN_CENTER
, B_ALIGN_MIDDLE
), &textColor
);
173 BButton::MouseDown(BPoint where
)
178 if (fBehavior
== B_POP_UP_BEHAVIOR
&& _PopUpRect().Contains(where
)) {
179 InvokeNotify(fPopUpMessage
, B_CONTROL_MODIFIED
);
183 bool toggleBehavior
= fBehavior
== B_TOGGLE_BEHAVIOR
;
185 if (toggleBehavior
) {
186 bool wasPressed
= Value() == B_CONTROL_ON
;
187 _SetFlag(FLAG_WAS_PRESSED
, wasPressed
);
188 SetValue(wasPressed
? B_CONTROL_OFF
: B_CONTROL_ON
);
191 SetValue(B_CONTROL_ON
);
193 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS
) {
195 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
);
197 BRect bounds
= Bounds();
202 Window()->UpdateIfNeeded();
205 GetMouse(&where
, &buttons
, true);
206 inside
= bounds
.Contains(where
);
208 if (toggleBehavior
) {
209 bool pressed
= inside
^ _Flag(FLAG_WAS_PRESSED
);
210 SetValue(pressed
? B_CONTROL_ON
: B_CONTROL_OFF
);
212 if ((Value() == B_CONTROL_ON
) != inside
)
213 SetValue(inside
? B_CONTROL_ON
: B_CONTROL_OFF
);
215 } while (buttons
!= 0);
218 if (toggleBehavior
) {
220 _Flag(FLAG_WAS_PRESSED
) ? B_CONTROL_OFF
: B_CONTROL_ON
);
224 } else if (_Flag(FLAG_FLAT
))
230 BButton::AttachedToWindow()
232 BControl::AttachedToWindow();
234 // Tint default control background color to match default panel background.
235 SetLowUIColor(B_CONTROL_BACKGROUND_COLOR
, 1.115);
236 SetHighUIColor(B_CONTROL_TEXT_COLOR
);
239 Window()->SetDefaultButton(this);
244 BButton::KeyDown(const char* bytes
, int32 numBytes
)
246 if (*bytes
== B_ENTER
|| *bytes
== B_SPACE
) {
250 SetValue(B_CONTROL_ON
);
252 // make sure the user saw that
253 Window()->UpdateIfNeeded();
258 BControl::KeyDown(bytes
, numBytes
);
263 BButton::MakeDefault(bool flag
)
265 BButton
* oldDefault
= NULL
;
266 BWindow
* window
= Window();
269 oldDefault
= window
->DefaultButton();
272 if (_Flag(FLAG_DEFAULT
) && oldDefault
== this)
275 if (_SetFlag(FLAG_DEFAULT
, true)) {
276 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0)
279 ResizeBy(6.0f
, 6.0f
);
280 MoveBy(-3.0f
, -3.0f
);
284 if (window
&& oldDefault
!= this)
285 window
->SetDefaultButton(this);
287 if (!_SetFlag(FLAG_DEFAULT
, false))
290 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0)
293 ResizeBy(-6.0f
, -6.0f
);
297 if (window
&& oldDefault
== this)
298 window
->SetDefaultButton(NULL
);
304 BButton::SetLabel(const char* label
)
306 BControl::SetLabel(label
);
311 BButton::IsDefault() const
313 return _Flag(FLAG_DEFAULT
);
318 BButton::IsFlat() const
320 return _Flag(FLAG_FLAT
);
325 BButton::SetFlat(bool flat
)
327 if (_SetFlag(FLAG_FLAT
, flat
))
333 BButton::Behavior() const
340 BButton::SetBehavior(BBehavior behavior
)
342 if (behavior
!= fBehavior
) {
343 fBehavior
= behavior
;
351 BButton::PopUpMessage() const
353 return fPopUpMessage
;
358 BButton::SetPopUpMessage(BMessage
* message
)
360 delete fPopUpMessage
;
361 fPopUpMessage
= message
;
366 BButton::MessageReceived(BMessage
* message
)
368 BControl::MessageReceived(message
);
373 BButton::WindowActivated(bool active
)
375 BControl::WindowActivated(active
);
380 BButton::MouseMoved(BPoint where
, uint32 code
, const BMessage
* dragMessage
)
382 bool inside
= (code
!= B_EXITED_VIEW
) && Bounds().Contains(where
);
383 if (_SetFlag(FLAG_INSIDE
, inside
))
389 if (fBehavior
== B_TOGGLE_BEHAVIOR
) {
390 bool pressed
= inside
^ _Flag(FLAG_WAS_PRESSED
);
391 SetValue(pressed
? B_CONTROL_ON
: B_CONTROL_OFF
);
393 if ((Value() == B_CONTROL_ON
) != inside
)
394 SetValue(inside
? B_CONTROL_ON
: B_CONTROL_OFF
);
400 BButton::MouseUp(BPoint where
)
405 if (Bounds().Contains(where
)) {
406 if (fBehavior
== B_TOGGLE_BEHAVIOR
)
407 SetValue(_Flag(FLAG_WAS_PRESSED
) ? B_CONTROL_OFF
: B_CONTROL_ON
);
410 } else if (_Flag(FLAG_FLAT
))
418 BButton::DetachedFromWindow()
420 BControl::DetachedFromWindow();
425 BButton::SetValue(int32 value
)
427 if (value
!= Value())
428 BControl::SetValue(value
);
433 BButton::GetPreferredSize(float* _width
, float* _height
)
435 _ValidatePreferredSize();
438 *_width
= fPreferredSize
.width
;
441 *_height
= fPreferredSize
.height
;
446 BButton::ResizeToPreferred()
448 BControl::ResizeToPreferred();
453 BButton::Invoke(BMessage
* message
)
458 status_t err
= BControl::Invoke(message
);
460 if (fBehavior
!= B_TOGGLE_BEHAVIOR
)
461 SetValue(B_CONTROL_OFF
);
468 BButton::FrameMoved(BPoint newPosition
)
470 BControl::FrameMoved(newPosition
);
475 BButton::FrameResized(float newWidth
, float newHeight
)
477 BControl::FrameResized(newWidth
, newHeight
);
482 BButton::MakeFocus(bool focus
)
484 BControl::MakeFocus(focus
);
489 BButton::AllAttached()
491 BControl::AllAttached();
496 BButton::AllDetached()
498 BControl::AllDetached();
503 BButton::ResolveSpecifier(BMessage
* message
, int32 index
,
504 BMessage
* specifier
, int32 what
, const char* property
)
506 return BControl::ResolveSpecifier(message
, index
, specifier
, what
,
512 BButton::GetSupportedSuites(BMessage
* message
)
514 return BControl::GetSupportedSuites(message
);
519 BButton::Perform(perform_code code
, void* _data
)
522 case PERFORM_CODE_MIN_SIZE
:
523 ((perform_data_min_size
*)_data
)->return_value
524 = BButton::MinSize();
527 case PERFORM_CODE_MAX_SIZE
:
528 ((perform_data_max_size
*)_data
)->return_value
529 = BButton::MaxSize();
532 case PERFORM_CODE_PREFERRED_SIZE
:
533 ((perform_data_preferred_size
*)_data
)->return_value
534 = BButton::PreferredSize();
537 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
538 ((perform_data_layout_alignment
*)_data
)->return_value
539 = BButton::LayoutAlignment();
542 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
543 ((perform_data_has_height_for_width
*)_data
)->return_value
544 = BButton::HasHeightForWidth();
547 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
549 perform_data_get_height_for_width
* data
550 = (perform_data_get_height_for_width
*)_data
;
551 BButton::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
556 case PERFORM_CODE_SET_LAYOUT
:
558 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
559 BButton::SetLayout(data
->layout
);
563 case PERFORM_CODE_LAYOUT_INVALIDATED
:
565 perform_data_layout_invalidated
* data
566 = (perform_data_layout_invalidated
*)_data
;
567 BButton::LayoutInvalidated(data
->descendants
);
571 case PERFORM_CODE_DO_LAYOUT
:
577 case PERFORM_CODE_SET_ICON
:
579 perform_data_set_icon
* data
= (perform_data_set_icon
*)_data
;
580 return BButton::SetIcon(data
->icon
, data
->flags
);
584 return BControl::Perform(code
, _data
);
591 return BLayoutUtils::ComposeSize(ExplicitMinSize(),
592 _ValidatePreferredSize());
599 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
600 _ValidatePreferredSize());
605 BButton::PreferredSize()
607 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
608 _ValidatePreferredSize());
613 BButton::SetIcon(const BBitmap
* icon
, uint32 flags
)
615 return BControl::SetIcon(icon
,
616 flags
| B_CREATE_ACTIVE_ICON_BITMAP
| B_CREATE_DISABLED_ICON_BITMAPS
);
621 BButton::LayoutInvalidated(bool descendants
)
623 // invalidate cached preferred size
624 fPreferredSize
.Set(-1, -1);
628 void BButton::_ReservedButton1() {}
629 void BButton::_ReservedButton2() {}
630 void BButton::_ReservedButton3() {}
634 BButton::operator=(const BButton
&)
641 BButton::_ValidatePreferredSize()
643 if (fPreferredSize
.width
< 0) {
644 BControlLook::background_type backgroundType
645 = fBehavior
== B_POP_UP_BEHAVIOR
646 ? BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
647 : BControlLook::B_BUTTON_BACKGROUND
;
648 float left
, top
, right
, bottom
;
649 be_control_look
->GetInsets(BControlLook::B_BUTTON_FRAME
, backgroundType
,
650 IsDefault() ? BControlLook::B_DEFAULT_BUTTON
: 0,
651 left
, top
, right
, bottom
);
654 float width
= left
+ right
+ 2 * kLabelMargin
- 1;
656 const char* label
= Label();
658 width
= std::max(width
, 20.0f
);
659 width
+= (float)ceil(StringWidth(label
));
662 const BBitmap
* icon
= IconBitmap(B_INACTIVE_ICON_BITMAP
);
664 width
+= icon
->Bounds().Width() + 1;
666 if (label
!= NULL
&& icon
!= NULL
)
667 width
+= be_control_look
->DefaultLabelSpacing();
670 float minHorizontalMargins
= top
+ bottom
+ 2 * kLabelMargin
;
674 font_height fontHeight
;
675 GetFontHeight(&fontHeight
);
676 float textHeight
= fontHeight
.ascent
+ fontHeight
.descent
;
677 height
= ceilf(textHeight
* 1.8);
678 float margins
= height
- ceilf(textHeight
);
679 if (margins
< minHorizontalMargins
)
680 height
+= minHorizontalMargins
- margins
;
684 height
= std::max(height
,
685 icon
->Bounds().Height() + minHorizontalMargins
);
688 // force some minimum width/height values
689 width
= std::max(width
, label
!= NULL
? 75.0f
: 5.0f
);
690 height
= std::max(height
, 5.0f
);
692 fPreferredSize
.Set(width
, height
);
694 ResetLayoutInvalidation();
697 return fPreferredSize
;
702 BButton::_PopUpRect() const
704 if (fBehavior
!= B_POP_UP_BEHAVIOR
)
707 float left
, top
, right
, bottom
;
708 be_control_look
->GetInsets(BControlLook::B_BUTTON_FRAME
,
709 BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
,
710 IsDefault() ? BControlLook::B_DEFAULT_BUTTON
: 0,
711 left
, top
, right
, bottom
);
713 BRect
rect(Bounds());
714 rect
.left
= rect
.right
- right
+ 1;
720 BButton::_Flag(uint32 flag
) const
722 return (fFlags
& flag
) != 0;
727 BButton::_SetFlag(uint32 flag
, bool set
)
729 if (((fFlags
& flag
) != 0) == set
)
742 B_IF_GCC_2(InvalidateLayout__7BButtonb
, _ZN7BButton16InvalidateLayoutEb
)(
743 BView
* view
, bool descendants
)
745 perform_data_layout_invalidated data
;
746 data
.descendants
= descendants
;
748 view
->Perform(PERFORM_CODE_LAYOUT_INVALIDATED
, &data
);