From a0848a191693aac77d3301a13dc59a27114acaa7 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 30 Dec 2013 04:42:52 +0100 Subject: [PATCH] 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. --- headers/os/interface/Button.h | 10 +- headers/os/interface/ControlLook.h | 48 +++++++ src/kits/interface/Button.cpp | 73 +++++++++-- src/kits/interface/ControlLook.cpp | 193 +++++++++++++++++++++++------ 4 files changed, 277 insertions(+), 47 deletions(-) diff --git a/headers/os/interface/Button.h b/headers/os/interface/Button.h index 6d2779eb22..7ee86fd970 100644 --- a/headers/os/interface/Button.h +++ b/headers/os/interface/Button.h @@ -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 diff --git a/headers/os/interface/ControlLook.h b/headers/os/interface/ControlLook.h index 43fc15bfa6..ec4476248e 100644 --- a/headers/os/interface/ControlLook.h +++ b/headers/os/interface/ControlLook.h @@ -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, diff --git a/src/kits/interface/Button.cpp b/src/kits/interface/Button.cpp index 3fe12f2c91..0ec945354a 100644 --- a/src/kits/interface/Button.cpp +++ b/src/kits/interface/Button.cpp @@ -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 { diff --git a/src/kits/interface/ControlLook.cpp b/src/kits/interface/ControlLook.cpp index 1a8865dcc5..fc5b68c872 100644 --- a/src/kits/interface/ControlLook.cpp +++ b/src/kits/interface/ControlLook.cpp @@ -1,6 +1,6 @@ /* * Copyright 2009, Stephan Aßmus - * 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;