From 27cc25083edc760e3ce0f2163f5222fa118e7852 Mon Sep 17 00:00:00 2001 From: Stefano Ceccherini Date: Mon, 20 Nov 2006 20:19:55 +0000 Subject: [PATCH] =?UTF-8?q?First=20try=20at=20menu=20scrolling,=20courthes?= =?UTF-8?q?y=20of=20=C5=81ukasz=20Zemczak.=20Works=20more=20or=20less,=20a?= =?UTF-8?q?lthough=20a=20bit=20buggy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19343 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/interface/MenuWindow.h | 58 ++++++-- src/kits/interface/Menu.cpp | 22 ++- src/kits/interface/MenuWindow.cpp | 190 ++++++++++++++++++++++--- 3 files changed, 237 insertions(+), 33 deletions(-) diff --git a/headers/private/interface/MenuWindow.h b/headers/private/interface/MenuWindow.h index 8b2792999f..c8efdb449b 100644 --- a/headers/private/interface/MenuWindow.h +++ b/headers/private/interface/MenuWindow.h @@ -30,21 +30,59 @@ #include class BMenu; -class BMenuScroller; +class BMenuScroller : public BView { + public: + BMenuScroller(BRect frame, BMenu *menu); + virtual ~BMenuScroller(); + + virtual void Pulse(); + virtual void Draw(BRect updateRect); + + private: + BMenu *fMenu; + BRect fUpperButton; + BRect fLowerButton; + + float fValue; + float fLimit; + + bool fUpperEnabled; + bool fLowerEnabled; + + uint32 fButton; + BPoint fPosition; +}; + + +class BMenuFrame : public BView { + public: + BMenuFrame(BMenu *menu); + + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void Draw(BRect updateRect); + + private: + friend class BMenuWindow; + + BMenu *fMenu; +}; + class BMenuWindow : public BWindow { -public: - BMenuWindow(const char *name); - virtual ~BMenuWindow(); + public: + BMenuWindow(const char *name); + virtual ~BMenuWindow(); - void AttachMenu(BMenu *menu); - void DetachMenu(); + void AttachMenu(BMenu *menu); + void DetachMenu(); - void UpdateScrollers(); + void AttachScrollers(); + void DetachScrollers(); -private: - BMenuScroller *fUpperScroller; - BMenuScroller *fLowerScroller; + private: + BMenuScroller *fScroller; + BMenuFrame *fMenuFrame; }; #endif diff --git a/src/kits/interface/Menu.cpp b/src/kits/interface/Menu.cpp index 2633d4eb85..e18fbef7ba 100644 --- a/src/kits/interface/Menu.cpp +++ b/src/kits/interface/Menu.cpp @@ -1184,6 +1184,7 @@ BMenu::_show(bool selectFirstItem) if (window->Lock()) { fAttachAborted = false; window->AttachMenu(this); + fCachedMenuWindow = window; // Menu didn't have the time to add its items: aborting... if (fAttachAborted) { @@ -1203,6 +1204,7 @@ BMenu::_show(bool selectFirstItem) MoveTo(1, 1); UpdateWindowViewSize(); + fCachedMenuWindow = NULL; window->Show(); if (selectFirstItem) @@ -2061,13 +2063,27 @@ BMenu::UpdateWindowViewSize(bool upWind) BRect frame = CalcFrame(ScreenLocation(), &scroll); ResizeTo(frame.Width(), frame.Height()); - if (fItems.CountItems() > 0) - window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2); - else { + if (fItems.CountItems() > 0) { + if (!scroll) { + window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2); + } else { + BScreen screen(window); + + // If we need scrolling, resize the window to fit the screen and + // attach scrollers to our cached MenuWindow. + window->ResizeTo(Bounds().Width() + 2, screen.Frame().bottom - 10); + + if (fCachedMenuWindow) + fCachedMenuWindow->AttachScrollers(); + + frame.top = 0; + } + } else { CacheFontInfo(); window->ResizeTo(StringWidth(kEmptyMenuLabel) + fPad.left + fPad.right, fFontHeight + fPad.top + fPad.bottom); } + window->MoveTo(frame.LeftTop()); } diff --git a/src/kits/interface/MenuWindow.cpp b/src/kits/interface/MenuWindow.cpp index a3cce07b77..36f788677c 100644 --- a/src/kits/interface/MenuWindow.cpp +++ b/src/kits/interface/MenuWindow.cpp @@ -5,12 +5,11 @@ * Authors: * Marc Flerackers (mflerackers@androme.be) * Stefano Ceccherini (burton666@libero.it) + * */ //! BMenuWindow is a custom BWindow for BMenus. -// TODO: Add scrollers - #include #include @@ -18,27 +17,133 @@ #include -// This draws the frame around the BMenu -class BMenuFrame : public BView { - public: - BMenuFrame(BMenu *menu); +const int kScrollerHeight = 10; +const int kScrollStep = 16; - virtual void AttachedToWindow(); - virtual void DetachedFromWindow(); - virtual void Draw(BRect updateRect); +BMenuScroller::BMenuScroller(BRect frame, BMenu *menu) + : BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS + | B_PULSE_NEEDED), + fMenu(menu), + fUpperButton(0, 0, frame.right, kScrollerHeight), + fLowerButton(0, frame.bottom - kScrollerHeight, frame.right, frame.bottom), + fValue(0), + fUpperEnabled(false), + fLowerEnabled(true) +{ + if (!menu) + debugger("BMenuScroller(): Scroller not attached to a menu!"); + SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); - private: - BMenu *fMenu; -}; + fLimit = menu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight); +} + + +BMenuScroller::~BMenuScroller() +{ +} + + +void +BMenuScroller::Pulse() +{ + // To reduce the overhead even a little, fButton and fPosition were + // made private variables. + + // We track the mouse and move the scrollers depending on its position. + GetMouse(&fPosition, &fButton); + if (fLowerButton.Contains(fPosition) && fLowerEnabled) { + if (fValue == 0) { + fUpperEnabled = true; + + Invalidate(fUpperButton); + } + + if (fValue + kScrollStep >= fLimit) { + // If we reached the limit, we don't want to scroll a whole + // 'step' if not needed. + fMenu->ScrollBy(0, fLimit - fValue); + fValue = fLimit; + fLowerEnabled = false; + Invalidate(fLowerButton); + } else { + fMenu->ScrollBy(0, kScrollStep); + fValue += kScrollStep; + } + } else if (fUpperButton.Contains(fPosition) && fUpperEnabled) { + if (fValue == fLimit) { + fLowerEnabled = true; + Invalidate(fLowerButton); + } + + if (fValue - kScrollStep <= 0) { + fMenu->ScrollBy(0, -fValue); + fValue = 0; + fUpperEnabled = false; + Invalidate(fUpperButton); + } else { + fMenu->ScrollBy(0, -kScrollStep); + fValue -= kScrollStep; + } + + // In this case, we need to redraw the lower button because of a + // probable bug in ScrollBy handling. The scrolled view is drawing below + // its bounds, dirtying our button in this process. + // Redrawing it everytime makes scrolling a little slower. + // TODO: Try to find why and fix this. + Draw(fLowerButton); + } +} + + +void +BMenuScroller::Draw(BRect updateRect) +{ + SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); + float middle = Bounds().right / 2; + + // Draw the upper arrow. + if (updateRect.Intersects(fUpperButton)) { + if (fUpperEnabled) + SetHighColor(0, 0, 0); + else + SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), + B_DARKEN_2_TINT)); + + FillRect(fUpperButton, B_SOLID_LOW); + + FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3), + BPoint(middle + 5, (kScrollerHeight / 2) + 2), + BPoint(middle - 5, (kScrollerHeight / 2) + 2)); + } + + // Draw the lower arrow. + if (updateRect.Intersects(fLowerButton)) { + if (fLowerEnabled) + SetHighColor(0, 0, 0); + else + SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), + B_DARKEN_2_TINT)); + + FillRect(fLowerButton, B_SOLID_LOW); + + FillTriangle(BPoint(middle, fLowerButton.bottom - (kScrollerHeight / 2) + 3), + BPoint(middle + 5, fLowerButton.bottom - (kScrollerHeight / 2) - 2), + BPoint(middle - 5, fLowerButton.bottom - (kScrollerHeight / 2) - 2)); + } +} + + +// #pragma mark - 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), - fUpperScroller(NULL), - fLowerScroller(NULL) + fScroller(NULL), + fMenuFrame(NULL) { + SetPulseRate(200000); } @@ -50,11 +155,11 @@ BMenuWindow::~BMenuWindow() void BMenuWindow::AttachMenu(BMenu *menu) { - if (CountChildren() > 0) - debugger("BMenuWindow (%s): a menu is already attached!"); + if (fMenuFrame) + debugger("BMenuWindow: a menu is already attached!"); if (menu != NULL) { - BMenuFrame *menuFrame = new BMenuFrame(menu); - AddChild(menuFrame); + fMenuFrame = new BMenuFrame(menu); + AddChild(fMenuFrame); menu->MakeFocus(true); } } @@ -63,8 +168,53 @@ BMenuWindow::AttachMenu(BMenu *menu) void BMenuWindow::DetachMenu() { - if (CountChildren() > 0) - RemoveChild(ChildAt(0)); + if (fMenuFrame) { + if (fScroller) { + DetachScrollers(); + } else { + RemoveChild(fMenuFrame); + } + delete fMenuFrame; + fMenuFrame = NULL; + } +} + + +void +BMenuWindow::AttachScrollers() +{ + // We want to attach a scroller only if there's a menu frame already + // existing. + if (fScroller || !fMenuFrame) + return; + + RemoveChild(fMenuFrame); + fScroller = new BMenuScroller(Bounds(), fMenuFrame->fMenu); + fScroller->AddChild(fMenuFrame); + AddChild(fScroller); + + fMenuFrame->fMenu->MakeFocus(true); + + fMenuFrame->ResizeBy(0, -2 * kScrollerHeight); + fMenuFrame->MoveBy(0, kScrollerHeight); +} + + +void +BMenuWindow::DetachScrollers() +{ + if(!fScroller || !fMenuFrame) + return; + + // BeOS doesn't remember the position where the last scrolling ended, + // so we just scroll back to the beginning. + fMenuFrame->fMenu->ScrollTo(0, 0); + + fScroller->RemoveChild(fMenuFrame); + RemoveChild(fScroller); + + delete fScroller; + fScroller = NULL; }