BButton: Add optional pop-up marker

* Add behavior constant B_POP_UP_BEHAVIOR which adds a pop-up marker
  to the button (similar to that of BMenuField).
* Add methods [Set]PopUpMessage(). To set/get the the message that is
  sent to the button's target when the pop-up marker is clicked.
This commit is contained in:
Ingo Weinhold
2013-12-30 04:42:52 +01:00
parent 87f392c9f0
commit a0848a1916
4 changed files with 277 additions and 47 deletions
+7 -3
View File
@@ -13,6 +13,7 @@ public:
enum BBehavior {
B_BUTTON_BEHAVIOR,
B_TOGGLE_BEHAVIOR,
B_POP_UP_BEHAVIOR,
};
public:
@@ -51,6 +52,9 @@ public:
BBehavior Behavior() const;
void SetBehavior(BBehavior behavior);
BMessage* PopUpMessage() const;
void SetPopUpMessage(BMessage* message);
virtual void MessageReceived(BMessage* message);
virtual void WindowActivated(bool active);
virtual void MouseMoved(BPoint point, uint32 transit,
@@ -82,7 +86,6 @@ public:
virtual status_t SetIcon(const BBitmap* icon, uint32 flags = 0);
protected:
virtual void LayoutInvalidated(bool descendants = false);
@@ -95,6 +98,8 @@ private:
BSize _ValidatePreferredSize();
BRect _PopUpRect() const;
inline bool _Flag(uint32 flag) const;
inline bool _SetFlag(uint32 flag, bool set);
@@ -102,8 +107,7 @@ private:
BSize fPreferredSize;
uint32 fFlags;
BBehavior fBehavior;
uint32 _reserved[1];
BMessage* fPopUpMessage;
};
#endif // _BUTTON_H
+48
View File
@@ -44,6 +44,7 @@ public:
enum background_type {
B_BUTTON_BACKGROUND,
B_BUTTON_WITH_POP_UP_BACKGROUND,
B_MENU_BACKGROUND,
B_MENU_BAR_BACKGROUND,
B_MENU_FIELD_BACKGROUND,
@@ -360,6 +361,30 @@ public:
uint32 flags, float& _left, float& _top,
float& _right, float& _bottom);
virtual void DrawButtonWithPopUpBackground(BView* view,
BRect& rect, const BRect& updateRect,
const rgb_color& base,
uint32 flags = 0,
uint32 borders = B_ALL_BORDERS,
orientation orientation = B_HORIZONTAL);
virtual void DrawButtonWithPopUpBackground(BView* view,
BRect& rect, const BRect& updateRect,
float radius,
const rgb_color& base,
uint32 flags = 0,
uint32 borders = B_ALL_BORDERS,
orientation orientation = B_HORIZONTAL);
virtual void DrawButtonWithPopUpBackground(BView* view,
BRect& rect, const BRect& updateRect,
float leftTopRadius,
float rightTopRadius,
float leftBottomRadius,
float rightBottomRadius,
const rgb_color& base,
uint32 flags = 0,
uint32 borders = B_ALL_BORDERS,
orientation orientation = B_HORIZONTAL);
void SetBackgroundInfo(
const BMessage& backgroundInfo);
@@ -405,9 +430,32 @@ protected:
float leftBottomRadius,
float rightBottomRadius,
const rgb_color& base,
bool popupIndicator = false,
uint32 flags = 0,
uint32 borders = B_ALL_BORDERS,
orientation orientation = B_HORIZONTAL);
void _DrawFlatButtonBackground(BView* view,
BRect& rect, const BRect& updateRect,
const rgb_color& base,
bool popupIndicator = false,
uint32 flags = 0,
uint32 borders = B_ALL_BORDERS,
orientation orientation = B_HORIZONTAL);
void _DrawNonFlatButtonBackground(BView* view,
BRect& rect, const BRect& updateRect,
BRegion& clipping,
float leftTopRadius,
float rightTopRadius,
float leftBottomRadius,
float rightBottomRadius,
const rgb_color& base,
bool popupIndicator = false,
uint32 flags = 0,
uint32 borders = B_ALL_BORDERS,
orientation orientation = B_HORIZONTAL);
void _DrawPopUpMarker(BView* view, const BRect& rect,
const rgb_color& base, uint32 flags);
void _DrawMenuFieldBackgroundOutside(BView* view,
BRect& rect, const BRect& updateRect,
+64 -9
View File
@@ -44,7 +44,8 @@ BButton::BButton(BRect frame, const char* name, const char* label,
flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fPreferredSize(-1, -1),
fFlags(0),
fBehavior(B_BUTTON_BEHAVIOR)
fBehavior(B_BUTTON_BEHAVIOR),
fPopUpMessage(NULL)
{
// Resize to minimum height if needed
font_height fh;
@@ -61,7 +62,8 @@ BButton::BButton(const char* name, const char* label, BMessage* message,
flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fPreferredSize(-1, -1),
fFlags(0),
fBehavior(B_BUTTON_BEHAVIOR)
fBehavior(B_BUTTON_BEHAVIOR),
fPopUpMessage(NULL)
{
}
@@ -71,13 +73,15 @@ BButton::BButton(const char* label, BMessage* message)
B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE),
fPreferredSize(-1, -1),
fFlags(0),
fBehavior(B_BUTTON_BEHAVIOR)
fBehavior(B_BUTTON_BEHAVIOR),
fPopUpMessage(NULL)
{
}
BButton::~BButton()
{
SetPopUpMessage(NULL);
}
@@ -85,7 +89,8 @@ BButton::BButton(BMessage* archive)
: BControl(archive),
fPreferredSize(-1, -1),
fFlags(0),
fBehavior(B_BUTTON_BEHAVIOR)
fBehavior(B_BUTTON_BEHAVIOR),
fPopUpMessage(NULL)
{
bool isDefault = false;
if (archive->FindBool("_default", &isDefault) == B_OK && isDefault)
@@ -136,8 +141,14 @@ BButton::Draw(BRect updateRect)
be_control_look->DrawButtonFrame(this, rect, updateRect,
base, background, flags);
be_control_look->DrawButtonBackground(this, rect, updateRect,
base, flags);
if (fBehavior == B_POP_UP_BEHAVIOR) {
be_control_look->DrawButtonWithPopUpBackground(this, rect, updateRect,
base, flags);
} else {
be_control_look->DrawButtonBackground(this, rect, updateRect,
base, flags);
}
// always leave some room around the label
rect.InsetBy(kLabelMargin, kLabelMargin);
@@ -157,6 +168,11 @@ BButton::MouseDown(BPoint point)
if (!IsEnabled())
return;
if (fBehavior == B_POP_UP_BEHAVIOR && _PopUpRect().Contains(point)) {
InvokeNotify(fPopUpMessage, B_CONTROL_MODIFIED);
return;
}
bool toggleBehavior = fBehavior == B_TOGGLE_BEHAVIOR;
if (toggleBehavior) {
@@ -318,8 +334,26 @@ BButton::Behavior() const
void
BButton::SetBehavior(BBehavior behavior)
{
if (behavior != fBehavior)
if (behavior != fBehavior) {
fBehavior = behavior;
InvalidateLayout();
Invalidate();
}
}
BMessage*
BButton::PopUpMessage() const
{
return fPopUpMessage;
}
void
BButton::SetPopUpMessage(BMessage* message)
{
delete fPopUpMessage;
fPopUpMessage = message;
}
@@ -593,9 +627,12 @@ BSize
BButton::_ValidatePreferredSize()
{
if (fPreferredSize.width < 0) {
BControlLook::background_type backgroundType
= fBehavior == B_POP_UP_BEHAVIOR
? BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND
: BControlLook::B_BUTTON_BACKGROUND;
float left, top, right, bottom;
be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME,
BControlLook::B_BUTTON_BACKGROUND,
be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME, backgroundType,
IsDefault() ? BControlLook::B_DEFAULT_BUTTON : 0,
left, top, right, bottom);
@@ -647,6 +684,24 @@ BButton::_ValidatePreferredSize()
}
BRect
BButton::_PopUpRect() const
{
if (fBehavior != B_POP_UP_BEHAVIOR)
return BRect();
float left, top, right, bottom;
be_control_look->GetInsets(BControlLook::B_BUTTON_FRAME,
BControlLook::B_BUTTON_WITH_POP_UP_BACKGROUND,
IsDefault() ? BControlLook::B_DEFAULT_BUTTON : 0,
left, top, right, bottom);
BRect rect(Bounds());
rect.left = rect.right - right + 1;
return rect;
}
inline bool
BButton::_Flag(uint32 flag) const
{
+158 -35
View File
@@ -1,6 +1,6 @@
/*
* Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
* Copyright 2012, Haiku Inc. All rights reserved.
* Copyright 2012-2013, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -29,6 +29,9 @@ namespace BPrivate {
static const float kEdgeBevelLightTint = 0.59;
static const float kEdgeBevelShadowTint = 1.0735;
static const float kHoverTintFactor = 0.85;
static const float kButtonPopUpIndicatorWidth = 11;
BControlLook::BControlLook():
@@ -157,7 +160,7 @@ BControlLook::DrawButtonBackground(BView* view, BRect& rect,
uint32 borders, orientation orientation)
{
_DrawButtonBackground(view, rect, updateRect, 0.0f, 0.0f, 0.0f, 0.0f,
base, flags, borders, orientation);
base, false, flags, borders, orientation);
}
@@ -167,7 +170,7 @@ BControlLook::DrawButtonBackground(BView* view, BRect& rect,
uint32 borders, orientation orientation)
{
_DrawButtonBackground(view, rect, updateRect, radius, radius, radius,
radius, base, flags, borders, orientation);
radius, base, false, flags, borders, orientation);
}
@@ -178,7 +181,7 @@ BControlLook::DrawButtonBackground(BView* view, BRect& rect,
uint32 flags, uint32 borders, orientation orientation)
{
_DrawButtonBackground(view, rect, updateRect, leftTopRadius,
rightTopRadius, leftBottomRadius, rightBottomRadius, base, flags,
rightTopRadius, leftBottomRadius, rightBottomRadius, base, false, flags,
borders, orientation);
}
@@ -1147,7 +1150,6 @@ BControlLook::DrawSliderTriangle(BView* view, BRect& rect,
middleTint2 = (middleTint2 + B_NO_TINT) / 2;
bottomTint = (bottomTint + B_NO_TINT) / 2;
} else if ((flags & B_HOVER) != 0) {
static const float kHoverTintFactor = 0.85;
topTint *= kHoverTintFactor;
middleTint1 *= kHoverTintFactor;
middleTint2 *= kHoverTintFactor;
@@ -1981,6 +1983,12 @@ BControlLook::GetBackgroundInsets(background_type backgroundType,
case B_MENU_ITEM_BACKGROUND:
inset = 1;
break;
case B_BUTTON_WITH_POP_UP_BACKGROUND:
_left = 1;
_top = 1;
_right = 1 + kButtonPopUpIndicatorWidth;
_bottom = 1;
return;
case B_HORIZONTAL_SCROLL_BAR_BACKGROUND:
_left = 2;
_top = 0;
@@ -2018,6 +2026,38 @@ BControlLook::GetInsets(frame_type frameType, background_type backgroundType,
}
void
BControlLook::DrawButtonWithPopUpBackground(BView* view, BRect& rect,
const BRect& updateRect, const rgb_color& base, uint32 flags,
uint32 borders, orientation orientation)
{
_DrawButtonBackground(view, rect, updateRect, 0.0f, 0.0f, 0.0f, 0.0f,
base, true, flags, borders, orientation);
}
void
BControlLook::DrawButtonWithPopUpBackground(BView* view, BRect& rect,
const BRect& updateRect, float radius, const rgb_color& base, uint32 flags,
uint32 borders, orientation orientation)
{
_DrawButtonBackground(view, rect, updateRect, radius, radius, radius,
radius, base, true, flags, borders, orientation);
}
void
BControlLook::DrawButtonWithPopUpBackground(BView* view, BRect& rect,
const BRect& updateRect, float leftTopRadius, float rightTopRadius,
float leftBottomRadius, float rightBottomRadius, const rgb_color& base,
uint32 flags, uint32 borders, orientation orientation)
{
_DrawButtonBackground(view, rect, updateRect, leftTopRadius,
rightTopRadius, leftBottomRadius, rightBottomRadius, base, true, flags,
borders, orientation);
}
void
BControlLook::SetBackgroundInfo(const BMessage& backgroundInfo)
{
@@ -2319,7 +2359,7 @@ void
BControlLook::_DrawButtonBackground(BView* view, BRect& rect,
const BRect& updateRect, float leftTopRadius, float rightTopRadius,
float leftBottomRadius, float rightBottomRadius, const rgb_color& base,
uint32 flags, uint32 borders, orientation orientation)
bool popupIndicator, uint32 flags, uint32 borders, orientation orientation)
{
if (!rect.IsValid() || !rect.Intersects(updateRect))
return;
@@ -2337,13 +2377,51 @@ BControlLook::_DrawButtonBackground(BView* view, BRect& rect,
&& (flags & (B_ACTIVATED | B_PARTIALLY_ACTIVATED)) == 0
&& ((flags & (B_HOVER | B_FOCUSED)) == 0
|| (flags & B_DISABLED) != 0)) {
_DrawFrame(view, rect, base, base, base, base, borders);
view->SetHighColor(base);
view->FillRect(rect);
view->PopState();
return;
_DrawFlatButtonBackground(view, rect, updateRect, base, popupIndicator,
flags, borders, orientation);
} else {
_DrawNonFlatButtonBackground(view, rect, updateRect, clipping,
leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius,
base, popupIndicator, flags, borders, orientation);
}
// restore the clipping constraints of the view
view->PopState();
}
void
BControlLook::_DrawFlatButtonBackground(BView* view, BRect& rect,
const BRect& updateRect, const rgb_color& base, bool popupIndicator,
uint32 flags, uint32 borders, orientation orientation)
{
_DrawFrame(view, rect, base, base, base, base, borders);
// Not an actual frame, but the method insets our rect as needed.
view->SetHighColor(base);
view->FillRect(rect);
if (popupIndicator) {
BRect indicatorRect(rect);
rect.right -= kButtonPopUpIndicatorWidth;
indicatorRect.left = rect.right + 3;
// 2 pixels for the separator
view->SetHighColor(base);
view->FillRect(indicatorRect);
_DrawPopUpMarker(view, indicatorRect, base, flags);
}
}
void
BControlLook::_DrawNonFlatButtonBackground(BView* view, BRect& rect,
const BRect& updateRect, BRegion& clipping, float leftTopRadius,
float rightTopRadius, float leftBottomRadius, float rightBottomRadius,
const rgb_color& base, bool popupIndicator, uint32 flags, uint32 borders,
orientation orientation)
{
// inner bevel colors
rgb_color bevelLightColor = _BevelLightColor(base, flags);
rgb_color bevelShadowColor = _BevelShadowColor(base, flags);
@@ -2446,11 +2524,77 @@ BControlLook::_DrawButtonBackground(BView* view, BRect& rect,
buttonBgColor, buttonBgColor, borders);
}
if (popupIndicator) {
BRect indicatorRect(rect);
rect.right -= kButtonPopUpIndicatorWidth;
indicatorRect.left = rect.right + 3;
// 2 pixels for the separator
// Even when depressed we want the pop-up indicator background and
// separator to cover the area up to the top.
if ((flags & B_ACTIVATED) != 0)
indicatorRect.top--;
// draw the separator
rgb_color separatorBaseColor = base;
if ((flags & B_ACTIVATED) != 0)
separatorBaseColor = tint_color(base, B_DARKEN_1_TINT);
rgb_color separatorLightColor = _EdgeLightColor(separatorBaseColor,
(flags & B_DISABLED) != 0 ? 0.7 : 1.0, 1.0, flags);
rgb_color separatorShadowColor = _EdgeShadowColor(separatorBaseColor,
(flags & B_DISABLED) != 0 ? 0.7 : 1.0, 1.0, flags);
view->BeginLineArray(2);
view->AddLine(BPoint(indicatorRect.left - 2, indicatorRect.top),
BPoint(indicatorRect.left - 2, indicatorRect.bottom),
separatorShadowColor);
view->AddLine(BPoint(indicatorRect.left - 1, indicatorRect.top),
BPoint(indicatorRect.left - 1, indicatorRect.bottom),
separatorLightColor);
view->EndLineArray();
// draw background and pop-up marker
_DrawMenuFieldBackgroundInside(view, indicatorRect, updateRect,
0.0f, rightTopRadius, 0.0f, rightBottomRadius, base, flags, 0);
if ((flags & B_ACTIVATED) != 0)
indicatorRect.top++;
_DrawPopUpMarker(view, indicatorRect, base, flags);
}
// fill in the background
view->FillRect(rect, fillGradient);
}
// restore the clipping constraints of the view
view->PopState();
void
BControlLook::_DrawPopUpMarker(BView* view, const BRect& rect,
const rgb_color& base, uint32 flags)
{
BPoint center(roundf((rect.left + rect.right) / 2.0),
roundf((rect.top + rect.bottom) / 2.0));
BPoint triangle[3];
triangle[0] = center + BPoint(-2.5, -0.5);
triangle[1] = center + BPoint(2.5, -0.5);
triangle[2] = center + BPoint(0.0, 2.0);
uint32 viewFlags = view->Flags();
view->SetFlags(viewFlags | B_SUBPIXEL_PRECISE);
rgb_color markColor;
if ((flags & B_DISABLED) != 0)
markColor = tint_color(base, 1.35);
else
markColor = tint_color(base, 1.65);
view->SetHighColor(markColor);
view->FillTriangle(triangle[0], triangle[1], triangle[2]);
view->SetFlags(viewFlags);
}
@@ -2478,25 +2622,7 @@ BControlLook::_DrawMenuFieldBackgroundOutside(BView* view, BRect& rect,
0.0f, rightTopRadius, 0.0f, rightBottomRadius, base, flags,
B_TOP_BORDER | B_RIGHT_BORDER | B_BOTTOM_BORDER);
// popup marker
BPoint center(roundf((rightRect.left + rightRect.right) / 2.0),
roundf((rightRect.top + rightRect.bottom) / 2.0));
BPoint triangle[3];
triangle[0] = center + BPoint(-2.5, -0.5);
triangle[1] = center + BPoint(2.5, -0.5);
triangle[2] = center + BPoint(0.0, 2.0);
uint32 viewFlags = view->Flags();
view->SetFlags(viewFlags | B_SUBPIXEL_PRECISE);
rgb_color markColor;
if ((flags & B_DISABLED) != 0)
markColor = tint_color(base, 1.35);
else
markColor = tint_color(base, 1.65);
view->SetHighColor(markColor);
view->FillTriangle(triangle[0], triangle[1], triangle[2]);
_DrawPopUpMarker(view, rightRect, base, flags);
// draw a line on the left of the popup frame
rgb_color bevelShadowColor = _BevelShadowColor(base, flags);
@@ -2507,8 +2633,6 @@ BControlLook::_DrawMenuFieldBackgroundOutside(BView* view, BRect& rect,
floorf(rightRect.bottom + 1.0));
view->StrokeLine(leftTopCorner, leftBottomCorner);
view->SetFlags(viewFlags);
rect = leftRect;
} else {
_DrawMenuFieldBackgroundInside(view, rect, updateRect, leftTopRadius,
@@ -3327,7 +3451,6 @@ BControlLook::_MakeButtonGradient(BGradientLinear& gradient, BRect& rect,
middleTint2 = (middleTint2 + B_NO_TINT) / 2;
bottomTint = (bottomTint + B_NO_TINT) / 2;
} else if ((flags & B_HOVER) != 0) {
static const float kHoverTintFactor = 0.85;
topTint *= kHoverTintFactor;
middleTint1 *= kHoverTintFactor;
middleTint2 *= kHoverTintFactor;