diff --git a/headers/os/interface/ScrollMenu.h b/headers/os/interface/ScrollMenu.h new file mode 100644 index 0000000000..4bb14f2981 --- /dev/null +++ b/headers/os/interface/ScrollMenu.h @@ -0,0 +1,55 @@ +/* + * Copyright 2011, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marc Flerackers (mflerackers@androme.be) + * Stefano Ceccherini (stefano.ceccherini@gmail.com) + * John Scipione (jscipione@gmail.com) + */ +#ifndef SCROLL_MENU_H +#define SCROLL_MENU_H + + +#include + +class BLayout; +class BMenu; +class BMenuScroller; + + +class BScrollMenu : public BView { +public: + BScrollMenu(BMenu* menu); + virtual ~BScrollMenu(); + + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void Draw(BRect updateRect); + virtual void FrameResized(float newWidth, float newHeight); + + void AttachScrollers(); + void DetachScrollers(); + bool HasScrollers() const; + + void SetSmallStep(float step); + void GetSteps(float* _smallStep, float* _largeStep) const; + bool CheckForScrolling(const BPoint& cursor); + bool TryScrollBy(const float& step); + +protected: + bool _Scroll(const BPoint& cursor); + void _ScrollBy(const float& step); + +private: + BMenu* fMenu; + BMenuScroller* fUpperScroller; + BMenuScroller* fLowerScroller; + + float fScrollStep; + float fValue; + float fLimit; +}; + + +#endif // SCROLL_MENU_H diff --git a/src/apps/deskbar/BarView.cpp b/src/apps/deskbar/BarView.cpp index 0bc16d2f6f..f874bd1536 100644 --- a/src/apps/deskbar/BarView.cpp +++ b/src/apps/deskbar/BarView.cpp @@ -48,6 +48,7 @@ All rights reserved. #include #include #include +#include #include #include "icons.h" @@ -130,6 +131,7 @@ BarViewMessageFilter::Filter(BMessage* message, BHandler** target) TBarView::TBarView(BRect frame, bool vertical, bool left, bool top, uint32 state, float) : BView(frame, "BarView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), + fBarScrollMenu(NULL), fBarMenuBar(NULL), fExpando(NULL), fTrayLocation(1), @@ -347,8 +349,7 @@ TBarView::MouseDown(BPoint where) void TBarView::PlaceDeskbarMenu() { - // top or bottom, full - if (!fVertical && fBarMenuBar) { + if (!fVertical && fBarMenuBar != NULL) { fBarMenuBar->RemoveSelf(); delete fBarMenuBar; fBarMenuBar = NULL; @@ -367,12 +368,13 @@ TBarView::PlaceDeskbarMenu() // if there isn't a bemenu at this point, // DB should be in top/bottom mode, else error - if (!fBarMenuBar) + if (fBarMenuBar == NULL) return; float width = sMinimumWindowWidth; BPoint loc(B_ORIGIN); BRect menuFrame(fBarMenuBar->Frame()); + if (fState == kFullState) { fBarMenuBar->RemoveTeamMenu(); // TODO: Magic constants need explanation @@ -441,22 +443,22 @@ TBarView::PlaceTray(bool vertSwap, bool leftSwap) void TBarView::PlaceApplicationBar() { - if (fExpando != NULL) { - SaveExpandedItems(); + if (fBarScrollMenu != NULL) { + fBarScrollMenu->RemoveSelf(); + delete fBarScrollMenu; + // Also deletes fExpando + fBarScrollMenu = NULL; + fExpando = NULL; + } else if (fExpando != NULL) { fExpando->RemoveSelf(); delete fExpando; fExpando = NULL; } - BRect screenFrame = (BScreen(Window())).Frame(); - if (fState == kMiniState) { - SizeWindow(screenFrame); - PositionWindow(screenFrame); - Window()->UpdateIfNeeded(); - Invalidate(); + if (fState == kMiniState) return; - } + BRect screenFrame = (BScreen(Window())).Frame(); BRect expandoFrame(0, 0, 0, 0); if (fVertical) { // top left/right @@ -485,7 +487,13 @@ TBarView::PlaceApplicationBar() fExpando = new TExpandoMenuBar(this, expandoFrame, "ExpandoMenuBar", fVertical, !hideLabels && fState != kFullState); - AddChild(fExpando); + + if (fVertical) { + fBarScrollMenu = new BScrollMenu(fExpando); + AddChild(fBarScrollMenu); +//printf("fExpando bottom: %f, fBarScrollMenu bottom: %f\n", fExpando->Frame().bottom, fBarScrollMenu->Frame().bottom); + } else + AddChild(fExpando); if (fVertical) ExpandItems(); @@ -522,7 +530,7 @@ TBarView::GetPreferredWindowSize(BRect screenFrame, float* width, float* height) } else if (fState == kExpandoState) { if (fVertical) { // top left or right - windowHeight = fExpando->Frame().bottom; + windowHeight = fBarScrollMenu->Frame().bottom; } else { // top or bottom, full fExpando->CheckItemSizes(0); diff --git a/src/apps/deskbar/BarView.h b/src/apps/deskbar/BarView.h index f375c872d4..e8e87ba95b 100644 --- a/src/apps/deskbar/BarView.h +++ b/src/apps/deskbar/BarView.h @@ -65,7 +65,7 @@ const float kStatusHeight = 22.0f; const float kHiddenDimension = 1.0f; const float kMaxPreventHidingDist = 80.0f; - +class BScrollMenu; class BShelf; class TBarMenuBar; class TExpandoMenuBar; @@ -168,6 +168,7 @@ class TBarView : public BView { void ExpandItems(); void _ChangeState(BMessage* message); + BScrollMenu* fBarScrollMenu; TBarMenuBar* fBarMenuBar; TExpandoMenuBar* fExpando; diff --git a/src/apps/deskbar/ExpandoMenuBar.cpp b/src/apps/deskbar/ExpandoMenuBar.cpp index 15d724fd58..f9cfbb86dc 100644 --- a/src/apps/deskbar/ExpandoMenuBar.cpp +++ b/src/apps/deskbar/ExpandoMenuBar.cpp @@ -45,6 +45,7 @@ All rights reserved. #include #include #include +#include #include "icons.h" @@ -797,10 +798,22 @@ TExpandoMenuBar::DrawBackground(BRect) void TExpandoMenuBar::CheckForSizeOverrun() { - BRect screenFrame = (BScreen(Window())).Frame(); + if (!fVertical) { + fIsScrolling = false; + return; + } - fIsScrolling = fVertical ? Window()->Frame().bottom > screenFrame.bottom - : false; + BScrollMenu* scrollMenu = dynamic_cast(Parent()); + if (scrollMenu == NULL) + return; + + BRect screenFrame = (BScreen(Window())).Frame(); + fIsScrolling = Window()->Frame().bottom > screenFrame.bottom; + + if (fIsScrolling) + scrollMenu->AttachScrollers(); + else + scrollMenu->DetachScrollers(); } diff --git a/src/apps/deskbar/TeamMenuItem.cpp b/src/apps/deskbar/TeamMenuItem.cpp index 5b7900f244..35ab7ff65c 100644 --- a/src/apps/deskbar/TeamMenuItem.cpp +++ b/src/apps/deskbar/TeamMenuItem.cpp @@ -479,8 +479,7 @@ TTeamMenuItem::DrawContentLabel() if (Submenu() && fVertical) cachedWidth += 18; - const char* label = Label(); - char* truncLabel = NULL; + BString label(Label()); float max = 0; if (fVertical && static_cast(be_app)->Settings()->superExpando) @@ -492,30 +491,23 @@ TTeamMenuItem::DrawContentLabel() BPoint penloc = menu->PenLocation(); BRect frame = Frame(); float offset = penloc.x - frame.left; - if (cachedWidth + offset > max) { - truncLabel = (char*)malloc(strlen(label) + 4); - if (!truncLabel) - return; - TruncateLabel(max-offset, truncLabel); - label = truncLabel; - } + if (cachedWidth + offset > max) + menu->TruncateString(&label, B_TRUNCATE_MIDDLE, max - offset); } if (!label) - label = Label(); + label = BString(Label()); - TBarView* barview = (static_cast(be_app))->BarView(); - bool canHandle = !barview->Dragging() - || barview->AppCanHandleTypes(Signature()); + TBarView* barView = (static_cast(be_app))->BarView(); + bool canHandle = !barView->Dragging() + || barView->AppCanHandleTypes(Signature()); if (_IsSelected() && IsEnabled() && canHandle) menu->SetLowColor(tint_color(menu->LowColor(), B_HIGHLIGHT_BACKGROUND_TINT)); else menu->SetLowColor(menu->LowColor()); - menu->DrawString(label); - - free(truncLabel); + menu->DrawString(label.String()); } diff --git a/src/kits/interface/Jamfile b/src/kits/interface/Jamfile index 81c59e0b32..4109585a2f 100644 --- a/src/kits/interface/Jamfile +++ b/src/kits/interface/Jamfile @@ -101,6 +101,7 @@ MergeObject interface_kit.o : RegionSupport.cpp Screen.cpp ScrollBar.cpp + ScrollMenu.cpp ScrollView.cpp SeparatorItem.cpp SeparatorView.cpp diff --git a/src/kits/interface/Menu.cpp b/src/kits/interface/Menu.cpp index 6905c15605..cde95056fe 100644 --- a/src/kits/interface/Menu.cpp +++ b/src/kits/interface/Menu.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -2811,13 +2812,18 @@ BMenu::_ChooseTrigger(const char* title, int32& index, uint32& trigger, void BMenu::_UpdateWindowViewSize(const bool &move) { + if (dynamic_cast(this) != NULL) { + BScrollMenu* scrollMenu = dynamic_cast(Parent()); + if (scrollMenu != NULL) + scrollMenu->ResizeTo(Bounds().Width(), Bounds().Height()); + + return; + } + BMenuWindow* window = static_cast(Window()); if (window == NULL) return; - if (dynamic_cast(this) != NULL) - return; - if (!fResizeToFit) return; diff --git a/src/kits/interface/MenuBar.cpp b/src/kits/interface/MenuBar.cpp index b29f9d5ef5..ee2e978b8f 100644 --- a/src/kits/interface/MenuBar.cpp +++ b/src/kits/interface/MenuBar.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -328,7 +329,36 @@ BMenuBar::Draw(BRect updateRect) void BMenuBar::MessageReceived(BMessage* msg) { - BMenu::MessageReceived(msg); + switch (msg->what) { + case B_MOUSE_WHEEL_CHANGED: + { + float deltaY = 0; + msg->FindFloat("be:wheel_delta_y", &deltaY); + if (deltaY == 0) + return; + + BScrollMenu* scrollMenu = dynamic_cast(Parent()); + if (scrollMenu == NULL) + return; + + float largeStep; + float smallStep; + scrollMenu->GetSteps(&smallStep, &largeStep); + + // pressing the option/command/control key scrolls faster + if (modifiers() & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY)) + deltaY *= largeStep; + else + deltaY *= smallStep; + + scrollMenu->TryScrollBy(deltaY); + break; + } + + default: + BMenu::MessageReceived(msg); + break; + } } diff --git a/src/kits/interface/ScrollMenu.cpp b/src/kits/interface/ScrollMenu.cpp new file mode 100644 index 0000000000..2923f01dc7 --- /dev/null +++ b/src/kits/interface/ScrollMenu.cpp @@ -0,0 +1,436 @@ +/* + * Copyright 2011, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marc Flerackers (mflerackers@androme.be) + * Stefano Ceccherini (stefano.ceccherini@gmail.com) + * John Scipione (jscipione@gmail.com) + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +const char* kEmptyMenuLabel = ""; +const int kDefaultScrollStep = 19; +const int kScrollerHeight = 12; + + +class BMenuScroller : public BView { +public: + BMenuScroller(BRect frame); + virtual ~BMenuScroller(); + + bool IsEnabled() const { return fEnabled; }; + void SetEnabled(bool enabled); + +private: + bool fEnabled; +}; + + +class BMenuUpScroller : public BMenuScroller { +public: + BMenuUpScroller(BRect frame); + virtual ~BMenuUpScroller(); + + virtual void Draw(BRect updateRect); +}; + + +class BMenuDownScroller : public BMenuScroller { +public: + BMenuDownScroller(BRect frame); + virtual ~BMenuDownScroller(); + + virtual void Draw(BRect updateRect); +}; + + +// #pragma mark - + + +BMenuScroller::BMenuScroller(BRect frame) + : + BView(frame, "menu scroll arrow", 0, B_WILL_DRAW | B_FRAME_EVENTS), + fEnabled(false) +{ + SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); +} + + +BMenuScroller::~BMenuScroller() +{ +} + + +void +BMenuScroller::SetEnabled(bool enabled) +{ + fEnabled = enabled; + Invalidate(); +} + + +// #pragma mark - + + +BMenuUpScroller::BMenuUpScroller(BRect frame) + : + BMenuScroller(frame) +{ +} + + +BMenuUpScroller::~BMenuUpScroller() +{ +} + + +void +BMenuUpScroller::Draw(BRect updateRect) +{ + SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); + + // Draw the upper arrow. + if (IsEnabled()) + SetHighColor(0, 0, 0); + else { + SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), + B_DARKEN_2_TINT)); + } + + FillRect(Bounds(), B_SOLID_LOW); + + float middle = Bounds().right / 2; + FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3), + BPoint(middle + 5, (kScrollerHeight / 2) + 2), + BPoint(middle - 5, (kScrollerHeight / 2) + 2)); +} + + +// #pragma mark - + + +BMenuDownScroller::BMenuDownScroller(BRect frame) + : + BMenuScroller(frame) +{ +} + + +BMenuDownScroller::~BMenuDownScroller() +{ +} + + +void +BMenuDownScroller::Draw(BRect updateRect) +{ + SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); + + // Draw the lower arrow. + if (IsEnabled()) + SetHighColor(0, 0, 0); + else { + SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), + B_DARKEN_2_TINT)); + } + + BRect frame = Bounds(); + FillRect(frame, B_SOLID_LOW); + + float middle = Bounds().right / 2; + FillTriangle(BPoint(middle, frame.bottom - (kScrollerHeight / 2) + 3), + BPoint(middle + 5, frame.bottom - (kScrollerHeight / 2) - 2), + BPoint(middle - 5, frame.bottom - (kScrollerHeight / 2) - 2)); +} + + +// #pragma mark - + + +BScrollMenu::BScrollMenu(BMenu *menu) + : + BView("menu scroll view", B_WILL_DRAW | B_FRAME_EVENTS), + fMenu(menu), + fUpperScroller(NULL), + fLowerScroller(NULL), + fScrollStep(kDefaultScrollStep) +{ +} + + +BScrollMenu::~BScrollMenu() +{ + if (fMenu != NULL) { + fMenu->RemoveSelf(); + delete fMenu; + fMenu = NULL; + } + + if (fUpperScroller != NULL) { + fUpperScroller->RemoveSelf(); + delete fUpperScroller; + fUpperScroller = NULL; + } + + if (fLowerScroller != NULL) { + fLowerScroller->RemoveSelf(); + delete fLowerScroller; + fLowerScroller = NULL; + } +} + + +void +BScrollMenu::AttachedToWindow() +{ + BView::AttachedToWindow(); + + if (fMenu == NULL) + return; + + AddChild(fMenu); + + // Move the scroll menu into the right position + MoveTo(fMenu->Frame().LeftTop()); + + BFont font; + fMenu->GetFont(&font); + SetFont(&font); +} + + +void +BScrollMenu::DetachedFromWindow() +{ + BView::DetachedFromWindow(); + + if (fMenu != NULL) + fMenu->RemoveSelf(); + + if (fUpperScroller != NULL) + fUpperScroller->RemoveSelf(); + + if (fLowerScroller != NULL) + fLowerScroller->RemoveSelf(); +} + + +void +BScrollMenu::Draw(BRect updateRect) +{ + if (be_control_look != NULL) + return; + + SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); + BRect bounds(Bounds()); + + StrokeLine(BPoint(bounds.right, bounds.top), + BPoint(bounds.right, bounds.bottom - 1)); + StrokeLine(BPoint(bounds.left + 1, bounds.bottom), + BPoint(bounds.right, bounds.bottom)); +} + + +void +BScrollMenu::FrameResized(float newWidth, float newHeight) +{ + BView::FrameResized(newWidth, newHeight); + + if (fMenu != NULL) { + if (HasScrollers()) + fMenu->MoveTo(0, kScrollerHeight); + else + fMenu->MoveTo(0, 0); + } +} + + +void +BScrollMenu::AttachScrollers() +{ + if (fMenu == NULL) + return; + + BRect frame = Bounds(); + BRect screenFrame = (BScreen(Window())).Frame(); + + if (HasScrollers()) { + fLimit = Frame().bottom + 2 * kScrollerHeight - screenFrame.bottom; + return; + } + + fMenu->MakeFocus(true); + + if (fUpperScroller == NULL) { + fUpperScroller = new BMenuUpScroller( + BRect(0, 0, frame.right, kScrollerHeight - 1)); + AddChild(fUpperScroller, fMenu); + } + + if (fLowerScroller == NULL) { + fLowerScroller = new BMenuDownScroller( + BRect(0, frame.bottom - kScrollerHeight + 1, frame.right, + frame.bottom)); + AddChild(fLowerScroller); + } + + fUpperScroller->SetEnabled(false); + fLowerScroller->SetEnabled(true); + + fLimit = Frame().bottom + 2 * kScrollerHeight - screenFrame.bottom; + fValue = 0; +} + + +void +BScrollMenu::DetachScrollers() +{ + if (!HasScrollers()) + return; + + if (fLowerScroller) { + fLowerScroller->RemoveSelf(); + delete fLowerScroller; + fLowerScroller = NULL; + } + + if (fUpperScroller) { + fUpperScroller->RemoveSelf(); + delete fUpperScroller; + fUpperScroller = NULL; + } + + if (fMenu) { + // We don't remember the position where the last scrolling + // ended, so scroll back to the beginning. + fMenu->ScrollTo(0, 0); + // Since the scrollers were removed move back up. + //fMenu->ResizeBy(0, 2 * kScrollerHeight); + //fMenu->MoveBy(0, -kScrollerHeight); + fValue = 0; + } +} + + +bool +BScrollMenu::HasScrollers() const +{ + return fMenu != NULL && fUpperScroller != NULL && fLowerScroller != NULL; +} + + +void +BScrollMenu::SetSmallStep(float step) +{ + fScrollStep = step; +} + + +void +BScrollMenu::GetSteps(float* _smallStep, float* _largeStep) const +{ + if (_smallStep != NULL) + *_smallStep = fScrollStep; + if (_largeStep != NULL) { + if (fMenu != NULL) + *_largeStep = fMenu->Frame().Height() - fScrollStep; + else + *_largeStep = fScrollStep * 2; + } +} + + +bool +BScrollMenu::CheckForScrolling(const BPoint &cursor) +{ + if (!HasScrollers()) + return false; + + return _Scroll(cursor); +} + + +bool +BScrollMenu::TryScrollBy(const float& step) +{ + if (!HasScrollers()) + return false; + + _ScrollBy(step); + return true; +} + + +bool +BScrollMenu::_Scroll(const BPoint& where) +{ + ASSERT((fLowerScroller != NULL)); + ASSERT((fUpperScroller != NULL)); + + const BPoint cursor = ConvertFromScreen(where); + const BRect &lowerFrame = fLowerScroller->Frame(); + const BRect &upperFrame = fUpperScroller->Frame(); + + int32 delta = 0; + if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor)) + delta = 1; + else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor)) + delta = -1; + + if (delta == 0) + return false; + + float smallStep; + GetSteps(&smallStep, NULL); + _ScrollBy(smallStep * delta); + + snooze(5000); + + return true; +} + + +void +BScrollMenu::_ScrollBy(const float& step) +{ + if (step > 0) { + if (fValue == 0) + fUpperScroller->SetEnabled(true); + + if (fValue + step >= fLimit) { + // If we reached the limit, only scroll to the end + fMenu->ScrollBy(0, fLimit - fValue); + fValue = fLimit; + fLowerScroller->SetEnabled(false); + } else { + fMenu->ScrollBy(0, step); + fValue += step; + } + } else if (step < 0) { + if (fValue == fLimit) + fLowerScroller->SetEnabled(true); + + if (fValue + step <= 0) { + fMenu->ScrollBy(0, -fValue); + fValue = 0; + fUpperScroller->SetEnabled(false); + } else { + fMenu->ScrollBy(0, step); + fValue += step; + } + } +}