2 * Copyright 2001-2014 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
= LowColor();
137 rgb_color base
= background
;
138 uint32 flags
= be_control_look
->Flags(this);
139 if (_Flag(FLAG_DEFAULT
))
140 flags
|= BControlLook::B_DEFAULT_BUTTON
;
141 if (_Flag(FLAG_FLAT
) && !IsTracking())
142 flags
|= BControlLook::B_FLAT
;
143 if (_Flag(FLAG_INSIDE
))
144 flags
|= BControlLook::B_HOVER
;
146 be_control_look
->DrawButtonFrame(this, rect
, updateRect
,
147 base
, background
, flags
);
149 if (fBehavior
== B_POP_UP_BEHAVIOR
) {
150 be_control_look
->DrawButtonWithPopUpBackground(this, rect
, updateRect
,
153 be_control_look
->DrawButtonBackground(this, rect
, updateRect
,
157 // always leave some room around the label
158 rect
.InsetBy(kLabelMargin
, kLabelMargin
);
160 const BBitmap
* icon
= IconBitmap(
161 (Value() == B_CONTROL_OFF
162 ? B_INACTIVE_ICON_BITMAP
: B_ACTIVE_ICON_BITMAP
)
163 | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP
));
164 be_control_look
->DrawLabel(this, Label(), icon
, rect
, updateRect
,
165 base
, flags
, BAlignment(B_ALIGN_CENTER
, B_ALIGN_MIDDLE
));
170 BButton::MouseDown(BPoint where
)
175 if (fBehavior
== B_POP_UP_BEHAVIOR
&& _PopUpRect().Contains(where
)) {
176 InvokeNotify(fPopUpMessage
, B_CONTROL_MODIFIED
);
180 bool toggleBehavior
= fBehavior
== B_TOGGLE_BEHAVIOR
;
182 if (toggleBehavior
) {
183 bool wasPressed
= Value() == B_CONTROL_ON
;
184 _SetFlag(FLAG_WAS_PRESSED
, wasPressed
);
185 SetValue(wasPressed
? B_CONTROL_OFF
: B_CONTROL_ON
);
188 SetValue(B_CONTROL_ON
);
190 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS
) {
192 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
);
194 BRect bounds
= Bounds();
199 Window()->UpdateIfNeeded();
202 GetMouse(&where
, &buttons
, true);
203 inside
= bounds
.Contains(where
);
205 if (toggleBehavior
) {
206 bool pressed
= inside
^ _Flag(FLAG_WAS_PRESSED
);
207 SetValue(pressed
? B_CONTROL_ON
: B_CONTROL_OFF
);
209 if ((Value() == B_CONTROL_ON
) != inside
)
210 SetValue(inside
? B_CONTROL_ON
: B_CONTROL_OFF
);
212 } while (buttons
!= 0);
215 if (toggleBehavior
) {
217 _Flag(FLAG_WAS_PRESSED
) ? B_CONTROL_OFF
: B_CONTROL_ON
);
221 } else if (_Flag(FLAG_FLAT
))
228 BButton::AttachedToWindow()
230 BControl::AttachedToWindow();
231 // low color will now be the parents view color
234 Window()->SetDefaultButton(this);
236 SetViewColor(B_TRANSPARENT_COLOR
);
241 BButton::KeyDown(const char* bytes
, int32 numBytes
)
243 if (*bytes
== B_ENTER
|| *bytes
== B_SPACE
) {
247 SetValue(B_CONTROL_ON
);
249 // make sure the user saw that
250 Window()->UpdateIfNeeded();
255 BControl::KeyDown(bytes
, numBytes
);
260 BButton::MakeDefault(bool flag
)
262 BButton
* oldDefault
= NULL
;
263 BWindow
* window
= Window();
266 oldDefault
= window
->DefaultButton();
269 if (_Flag(FLAG_DEFAULT
) && oldDefault
== this)
272 if (_SetFlag(FLAG_DEFAULT
, true)) {
273 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0)
276 ResizeBy(6.0f
, 6.0f
);
277 MoveBy(-3.0f
, -3.0f
);
281 if (window
&& oldDefault
!= this)
282 window
->SetDefaultButton(this);
284 if (!_SetFlag(FLAG_DEFAULT
, false))
287 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0)
290 ResizeBy(-6.0f
, -6.0f
);
294 if (window
&& oldDefault
== this)
295 window
->SetDefaultButton(NULL
);
301 BButton::SetLabel(const char* label
)
303 BControl::SetLabel(label
);
308 BButton::IsDefault() const
310 return _Flag(FLAG_DEFAULT
);
315 BButton::IsFlat() const
317 return _Flag(FLAG_FLAT
);
322 BButton::SetFlat(bool flat
)
324 if (_SetFlag(FLAG_FLAT
, flat
))
330 BButton::Behavior() const
337 BButton::SetBehavior(BBehavior behavior
)
339 if (behavior
!= fBehavior
) {
340 fBehavior
= behavior
;
348 BButton::PopUpMessage() const
350 return fPopUpMessage
;
355 BButton::SetPopUpMessage(BMessage
* message
)
357 delete fPopUpMessage
;
358 fPopUpMessage
= message
;
363 BButton::MessageReceived(BMessage
* message
)
365 BControl::MessageReceived(message
);
370 BButton::WindowActivated(bool active
)
372 BControl::WindowActivated(active
);
377 BButton::MouseMoved(BPoint where
, uint32 code
, const BMessage
* dragMessage
)
379 bool inside
= (code
!= B_EXITED_VIEW
) && Bounds().Contains(where
);
380 if (_SetFlag(FLAG_INSIDE
, inside
))
386 if (fBehavior
== B_TOGGLE_BEHAVIOR
) {
387 bool pressed
= inside
^ _Flag(FLAG_WAS_PRESSED
);
388 SetValue(pressed
? B_CONTROL_ON
: B_CONTROL_OFF
);
390 if ((Value() == B_CONTROL_ON
) != inside
)
391 SetValue(inside
? B_CONTROL_ON
: B_CONTROL_OFF
);
397 BButton::MouseUp(BPoint where
)
402 if (Bounds().Contains(where
)) {
403 if (fBehavior
== B_TOGGLE_BEHAVIOR
)
404 SetValue(_Flag(FLAG_WAS_PRESSED
) ? B_CONTROL_OFF
: B_CONTROL_ON
);
407 } else if (_Flag(FLAG_FLAT
))
415 BButton::DetachedFromWindow()
417 BControl::DetachedFromWindow();
422 BButton::SetValue(int32 value
)
424 if (value
!= Value())
425 BControl::SetValue(value
);
430 BButton::GetPreferredSize(float* _width
, float* _height
)
432 _ValidatePreferredSize();
435 *_width
= fPreferredSize
.width
;
438 *_height
= fPreferredSize
.height
;
443 BButton::ResizeToPreferred()
445 BControl::ResizeToPreferred();
450 BButton::Invoke(BMessage
* message
)
455 status_t err
= BControl::Invoke(message
);
457 if (fBehavior
!= B_TOGGLE_BEHAVIOR
)
458 SetValue(B_CONTROL_OFF
);
465 BButton::FrameMoved(BPoint newPosition
)
467 BControl::FrameMoved(newPosition
);
472 BButton::FrameResized(float newWidth
, float newHeight
)
474 BControl::FrameResized(newWidth
, newHeight
);
479 BButton::MakeFocus(bool focus
)
481 BControl::MakeFocus(focus
);
486 BButton::AllAttached()
488 BControl::AllAttached();
493 BButton::AllDetached()
495 BControl::AllDetached();
500 BButton::ResolveSpecifier(BMessage
* message
, int32 index
,
501 BMessage
* specifier
, int32 what
, const char* property
)
503 return BControl::ResolveSpecifier(message
, index
, specifier
, what
,
509 BButton::GetSupportedSuites(BMessage
* message
)
511 return BControl::GetSupportedSuites(message
);
516 BButton::Perform(perform_code code
, void* _data
)
519 case PERFORM_CODE_MIN_SIZE
:
520 ((perform_data_min_size
*)_data
)->return_value
521 = BButton::MinSize();
524 case PERFORM_CODE_MAX_SIZE
:
525 ((perform_data_max_size
*)_data
)->return_value
526 = BButton::MaxSize();
529 case PERFORM_CODE_PREFERRED_SIZE
:
530 ((perform_data_preferred_size
*)_data
)->return_value
531 = BButton::PreferredSize();
534 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
535 ((perform_data_layout_alignment
*)_data
)->return_value
536 = BButton::LayoutAlignment();
539 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
540 ((perform_data_has_height_for_width
*)_data
)->return_value
541 = BButton::HasHeightForWidth();
544 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
546 perform_data_get_height_for_width
* data
547 = (perform_data_get_height_for_width
*)_data
;
548 BButton::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
553 case PERFORM_CODE_SET_LAYOUT
:
555 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
556 BButton::SetLayout(data
->layout
);
560 case PERFORM_CODE_LAYOUT_INVALIDATED
:
562 perform_data_layout_invalidated
* data
563 = (perform_data_layout_invalidated
*)_data
;
564 BButton::LayoutInvalidated(data
->descendants
);
568 case PERFORM_CODE_DO_LAYOUT
:
574 case PERFORM_CODE_SET_ICON
:
576 perform_data_set_icon
* data
= (perform_data_set_icon
*)_data
;
577 return BButton::SetIcon(data
->icon
, data
->flags
);
581 return BControl::Perform(code
, _data
);
588 return BLayoutUtils::ComposeSize(ExplicitMinSize(),
589 _ValidatePreferredSize());
596 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
597 _ValidatePreferredSize());
602 BButton::PreferredSize()
604 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
605 _ValidatePreferredSize());
610 BButton::SetIcon(const BBitmap
* icon
, uint32 flags
)
612 return BControl::SetIcon(icon
,
613 flags
| B_CREATE_ACTIVE_ICON_BITMAP
| B_CREATE_DISABLED_ICON_BITMAPS
);
618 BButton::LayoutInvalidated(bool descendants
)
620 // invalidate cached preferred size
621 fPreferredSize
.Set(-1, -1);
625 void BButton::_ReservedButton1() {}
626 void BButton::_ReservedButton2() {}
627 void BButton::_ReservedButton3() {}
631 BButton::operator=(const BButton
&)
638 BButton::_ValidatePreferredSize()
640 if (fPreferredSize
.width
< 0) {
641 BControlLook::background_type backgroundType
642 = fBehavior
== B_POP_UP_BEHAVIOR
643 ? BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
644 : BControlLook::B_BUTTON_BACKGROUND
;
645 float left
, top
, right
, bottom
;
646 be_control_look
->GetInsets(BControlLook::B_BUTTON_FRAME
, backgroundType
,
647 IsDefault() ? BControlLook::B_DEFAULT_BUTTON
: 0,
648 left
, top
, right
, bottom
);
651 float width
= left
+ right
+ 2 * kLabelMargin
- 1;
653 const char* label
= Label();
655 width
= std::max(width
, 20.0f
);
656 width
+= (float)ceil(StringWidth(label
));
659 const BBitmap
* icon
= IconBitmap(B_INACTIVE_ICON_BITMAP
);
661 width
+= icon
->Bounds().Width() + 1;
663 if (label
!= NULL
&& icon
!= NULL
)
664 width
+= be_control_look
->DefaultLabelSpacing();
667 float minHorizontalMargins
= top
+ bottom
+ 2 * kLabelMargin
;
671 font_height fontHeight
;
672 GetFontHeight(&fontHeight
);
673 float textHeight
= fontHeight
.ascent
+ fontHeight
.descent
;
674 height
= ceilf(textHeight
* 1.8);
675 float margins
= height
- ceilf(textHeight
);
676 if (margins
< minHorizontalMargins
)
677 height
+= minHorizontalMargins
- margins
;
681 height
= std::max(height
,
682 icon
->Bounds().Height() + minHorizontalMargins
);
685 // force some minimum width/height values
686 width
= std::max(width
, label
!= NULL
? 75.0f
: 5.0f
);
687 height
= std::max(height
, 5.0f
);
689 fPreferredSize
.Set(width
, height
);
691 ResetLayoutInvalidation();
694 return fPreferredSize
;
699 BButton::_PopUpRect() const
701 if (fBehavior
!= B_POP_UP_BEHAVIOR
)
704 float left
, top
, right
, bottom
;
705 be_control_look
->GetInsets(BControlLook::B_BUTTON_FRAME
,
706 BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
,
707 IsDefault() ? BControlLook::B_DEFAULT_BUTTON
: 0,
708 left
, top
, right
, bottom
);
710 BRect
rect(Bounds());
711 rect
.left
= rect
.right
- right
+ 1;
717 BButton::_Flag(uint32 flag
) const
719 return (fFlags
& flag
) != 0;
724 BButton::_SetFlag(uint32 flag
, bool set
)
726 if (((fFlags
& flag
) != 0) == set
)
739 B_IF_GCC_2(InvalidateLayout__7BButtonb
, _ZN7BButton16InvalidateLayoutEb
)(
740 BView
* view
, bool descendants
)
742 perform_data_layout_invalidated data
;
743 data
.descendants
= descendants
;
745 view
->Perform(PERFORM_CODE_LAYOUT_INVALIDATED
, &data
);