From 4e23bc03830b81ec305154bcbddfa0792ece17f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 3 Jul 2009 12:09:16 +0000 Subject: [PATCH] * BMenu now scrolls when you press page up/down, if possible. * BMenuWindow no longer uses a fixed scroll step - instead, the menu sets it to the height of its first item. * Cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31389 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/interface/MenuWindow.h | 61 +++++++------ src/kits/interface/Menu.cpp | 42 +++++++-- src/kits/interface/MenuWindow.cpp | 113 +++++++++++++++---------- 3 files changed, 136 insertions(+), 80 deletions(-) diff --git a/headers/private/interface/MenuWindow.h b/headers/private/interface/MenuWindow.h index 1f8053ab60..e4f32ee7ab 100644 --- a/headers/private/interface/MenuWindow.h +++ b/headers/private/interface/MenuWindow.h @@ -1,13 +1,13 @@ /* - * Copyright 2001-2006, Haiku, Inc. + * Copyright 2001-2009, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers (mflerackers@androme.be) * Stefano Ceccherini (burton666@libero.it) */ -#ifndef __MENUWINDOW_H -#define __MENUWINDOW_H +#ifndef MENU_WINDOW_H +#define MENU_WINDOW_H #include @@ -22,34 +22,39 @@ class BMenuScroller; class BMenuWindow : public BWindow { - public: - BMenuWindow(const char *name); - virtual ~BMenuWindow(); +public: + BMenuWindow(const char* name); + virtual ~BMenuWindow(); - virtual void DispatchMessage(BMessage *message, BHandler *handler); - - void AttachMenu(BMenu *menu); - void DetachMenu(); - - void AttachScrollers(); - void DetachScrollers(); + virtual void DispatchMessage(BMessage* message, + BHandler* handler); - bool CheckForScrolling(const BPoint &cursor); - bool TryScrollBy(const float &step); - - private: - BMenu *fMenu; - BMenuFrame *fMenuFrame; - BMenuScroller *fUpperScroller; - BMenuScroller *fLowerScroller; - - float fValue; - float fLimit; - - bool _Scroll(const BPoint &cursor); - void _ScrollBy(const float &step); + void AttachMenu(BMenu* menu); + void DetachMenu(); + + void AttachScrollers(); + void DetachScrollers(); + + void SetSmallStep(float step); + void GetSteps(float* _smallStep, float* _largeStep); + bool HasScrollers() const; + bool CheckForScrolling(const BPoint& cursor); + bool TryScrollBy(const float& step); + +private: + bool _Scroll(const BPoint& cursor); + void _ScrollBy(const float& step); + + BMenu* fMenu; + BMenuFrame* fMenuFrame; + BMenuScroller* fUpperScroller; + BMenuScroller* fLowerScroller; + + float fScrollStep; + float fValue; + float fLimit; }; } // namespace BPrivate -#endif // __MENUWINDOW_H +#endif // MENU_WINDOW_H diff --git a/src/kits/interface/Menu.cpp b/src/kits/interface/Menu.cpp index 7c4b07c825..b84c649304 100644 --- a/src/kits/interface/Menu.cpp +++ b/src/kits/interface/Menu.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008, Haiku, Inc. + * Copyright 2001-2009, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: @@ -8,6 +8,8 @@ * Rene Gollent (anevilyak@gmail.com) */ +#include + #include #include #include @@ -18,13 +20,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -819,9 +821,7 @@ BMenu::MessageReceived(BMessage *msg) switch (msg->what) { case B_MOUSE_WHEEL_CHANGED: { - //float deltaX = 0 float deltaY = 0; - //msg->FindFloat("be:wheel_delta_x", &deltaX); msg->FindFloat("be:wheel_delta_y", &deltaY); if (deltaY == 0) return; @@ -830,7 +830,9 @@ BMenu::MessageReceived(BMessage *msg) if (window == NULL) return; - window->TryScrollBy(deltaY); + float smallStep; + window->GetSteps(&smallStep, NULL); + window->TryScrollBy(deltaY * smallStep); break; } default: @@ -890,6 +892,21 @@ BMenu::KeyDown(const char *bytes, int32 numBytes) } break; + case B_PAGE_UP: + case B_PAGE_DOWN: + { + BMenuWindow *window = dynamic_cast(Window()); + if (window == NULL || !window->HasScrollers()) + break; + + int32 deltaY = bytes[0] == B_PAGE_UP ? -1 : 1; + + float largeStep; + window->GetSteps(NULL, &largeStep); + window->TryScrollBy(deltaY * largeStep); + break; + } + case B_ENTER: case B_SPACE: if (fSelected) { @@ -1426,6 +1443,13 @@ BMenu::_Show(bool selectFirstItem) fAttachAborted = false; window->AttachMenu(this); + if (ItemAt(0) != NULL) { + float width, height; + ItemAt(0)->GetContentSize(&width, &height); + + window->SetSmallStep(ceilf(height)); + } + // Menu didn't have the time to add its items: aborting... if (fAttachAborted) { window->DetachMenu(); @@ -2213,7 +2237,7 @@ BMenu::_CalcFrame(BPoint where, bool *scrollOn) if (frame.right > screenFrame.right) frame.OffsetBy(screenFrame.right - frame.right, 0); } - + if (!scroll) { // basically, if this returns false, it means // that the menu frame won't fit completely inside the screen @@ -2221,10 +2245,10 @@ BMenu::_CalcFrame(BPoint where, bool *scrollOn) // not left/right scroll = screenFrame.Height() < frame.Height(); } - + if (scrollOn != NULL) *scrollOn = scroll; - + return frame; } @@ -2286,7 +2310,7 @@ BMenu::_InvokeItem(BMenuItem *item, bool now) rootMenu = parent; parent = rootMenu->Supermenu(); } while (parent != NULL); - + if (rootMenu->LockLooper()) { item->Invoke(); rootMenu->UnlockLooper(); diff --git a/src/kits/interface/MenuWindow.cpp b/src/kits/interface/MenuWindow.cpp index b5e6306f35..19b6b38c4a 100644 --- a/src/kits/interface/MenuWindow.cpp +++ b/src/kits/interface/MenuWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007, Haiku, Inc. + * Copyright 2001-2009, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: @@ -14,6 +14,8 @@ #include #include #include +#include + #include #include @@ -23,7 +25,7 @@ namespace BPrivate { class BMenuScroller : public BView { public: BMenuScroller(BRect frame); - + bool IsEnabled() const; void SetEnabled(const bool &enabled); private: @@ -34,11 +36,11 @@ class BMenuScroller : public BView { class BMenuFrame : public BView { public: BMenuFrame(BMenu *menu); - + virtual void AttachedToWindow(); virtual void DetachedFromWindow(); virtual void Draw(BRect updateRect); - + private: friend class BMenuWindow; @@ -67,14 +69,13 @@ using namespace BPrivate; const int kScrollerHeight = 10; -const int kScrollStep = 19; BMenuScroller::BMenuScroller(BRect frame) : BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS), fEnabled(false) { - SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); + SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); } @@ -112,11 +113,11 @@ UpperScroller::Draw(BRect updateRect) if (IsEnabled()) SetHighColor(0, 0, 0); else - SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), + SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); FillRect(Bounds(), B_SOLID_LOW); - + FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3), BPoint(middle + 5, (kScrollerHeight / 2) + 2), BPoint(middle - 5, (kScrollerHeight / 2) + 2)); @@ -137,7 +138,7 @@ void LowerScroller::Draw(BRect updateRect) { SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); - + BRect frame = Bounds(); // Draw the lower arrow. if (IsEnabled()) @@ -164,16 +165,16 @@ BMenuFrame::BMenuFrame(BMenu *menu) fMenu(menu) { } - + void BMenuFrame::AttachedToWindow() { BView::AttachedToWindow(); - + if (fMenu != NULL) AddChild(fMenu); - + ResizeTo(Window()->Bounds().Width(), Window()->Bounds().Height()); if (fMenu != NULL) { BFont font; @@ -181,7 +182,7 @@ BMenuFrame::AttachedToWindow() SetFont(&font); } } - + void BMenuFrame::DetachedFromWindow() @@ -240,10 +241,11 @@ BMenuWindow::BMenuWindow(const char *name) // The window will be resized by BMenu, so just pass a dummy rect : BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel, B_NOT_ZOOMABLE | B_AVOID_FOCUS), - fMenu(NULL), + fMenu(NULL), fMenuFrame(NULL), fUpperScroller(NULL), - fLowerScroller(NULL) + fLowerScroller(NULL), + fScrollStep(19) { SetSizeLimits(2, 10000, 2, 10000); } @@ -279,7 +281,7 @@ BMenuWindow::AttachMenu(BMenu *menu) void BMenuWindow::DetachMenu() { - DetachScrollers(); + DetachScrollers(); if (fMenuFrame) { RemoveChild(fMenuFrame); delete fMenuFrame; @@ -296,30 +298,30 @@ BMenuWindow::AttachScrollers() // menu frame already existing. if (!fMenu || !fMenuFrame) return; - + fMenu->MakeFocus(true); BRect frame = Bounds(); - + if (fUpperScroller == NULL) { fUpperScroller = new UpperScroller( BRect(0, 0, frame.right, kScrollerHeight - 1)); AddChild(fUpperScroller); } - + if (fLowerScroller == NULL) { fLowerScroller = new LowerScroller( BRect(0, frame.bottom - kScrollerHeight + 1, frame.right, frame.bottom)); AddChild(fLowerScroller); } - + fUpperScroller->SetEnabled(false); fLowerScroller->SetEnabled(true); fMenuFrame->ResizeBy(0, -2 * kScrollerHeight); fMenuFrame->MoveBy(0, kScrollerHeight); - + fValue = 0; fLimit = fMenu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight); } @@ -330,7 +332,7 @@ BMenuWindow::DetachScrollers() { // BeOS doesn't remember the position where the last scrolling ended, // so we just scroll back to the beginning. - if (fMenu) + if (fMenu) fMenu->ScrollTo(0, 0); if (fLowerScroller) { @@ -343,7 +345,36 @@ BMenuWindow::DetachScrollers() RemoveChild(fUpperScroller); delete fUpperScroller; fUpperScroller = NULL; - } + } +} + + +void +BMenuWindow::SetSmallStep(float step) +{ + fScrollStep = step; +} + + +void +BMenuWindow::GetSteps(float* _smallStep, float* _largeStep) +{ + if (_smallStep != NULL) + *_smallStep = fScrollStep; + if (_largeStep != NULL) { + if (fMenuFrame != NULL) + *_largeStep = fMenuFrame->Bounds().Height() - fScrollStep; + else + *_largeStep = fScrollStep * 2; + } +} + + +bool +BMenuWindow::HasScrollers() const +{ + return fMenuFrame != NULL && fUpperScroller != NULL + && fLowerScroller != NULL; } @@ -352,39 +383,38 @@ BMenuWindow::CheckForScrolling(const BPoint &cursor) { if (!fMenuFrame || !fUpperScroller || !fLowerScroller) return false; - + return _Scroll(cursor); } bool -BMenuWindow::TryScrollBy(const float &step) +BMenuWindow::TryScrollBy(const float& step) { if (!fMenuFrame || !fUpperScroller || !fLowerScroller) return false; - + _ScrollBy(step); - return true; } bool -BMenuWindow::_Scroll(const BPoint &where) +BMenuWindow::_Scroll(const BPoint& where) { ASSERT((fLowerScroller != NULL)); ASSERT((fUpperScroller != NULL)); - + const BPoint cursor = ConvertFromScreen(where); BRect lowerFrame = fLowerScroller->Frame(); BRect upperFrame = fUpperScroller->Frame(); - if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor)) { + if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor)) _ScrollBy(1); - } else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor)) { + else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor)) _ScrollBy(-1); - } else + else return false; snooze(5000); @@ -394,7 +424,7 @@ BMenuWindow::_Scroll(const BPoint &where) void -BMenuWindow::_ScrollBy(const float &step) +BMenuWindow::_ScrollBy(const float& step) { if (step > 0) { if (fValue == 0) { @@ -402,17 +432,15 @@ BMenuWindow::_ScrollBy(const float &step) fUpperScroller->Invalidate(); } - if (fValue + kScrollStep >= fLimit) { - // If we reached the limit, we don't want to scroll a whole - // 'step' if not needed. + if (fValue + step >= fLimit) { + // If we reached the limit, only scroll to the end fMenu->ScrollBy(0, fLimit - fValue); fValue = fLimit; fLowerScroller->SetEnabled(false); fLowerScroller->Invalidate(); - } else { - fMenu->ScrollBy(0, kScrollStep); - fValue += kScrollStep; + fMenu->ScrollBy(0, step); + fValue += step; } } else if (step < 0) { if (fValue == fLimit) { @@ -420,15 +448,14 @@ BMenuWindow::_ScrollBy(const float &step) fLowerScroller->Invalidate(); } - if (fValue - kScrollStep <= 0) { + if (fValue + step <= 0) { fMenu->ScrollBy(0, -fValue); fValue = 0; fUpperScroller->SetEnabled(false); fUpperScroller->Invalidate(); - } else { - fMenu->ScrollBy(0, -kScrollStep); - fValue -= kScrollStep; + fMenu->ScrollBy(0, step); + fValue += step; } } }