1c7023acdSAxel Dörfler /* 2059ca4bdSStefano Ceccherini * Copyright 2001-2007, Haiku, Inc. 3c7023acdSAxel Dörfler * Distributed under the terms of the MIT License. 4c7023acdSAxel Dörfler * 5c7023acdSAxel Dörfler * Authors: 6c7023acdSAxel Dörfler * Marc Flerackers (mflerackers@androme.be) 7059ca4bdSStefano Ceccherini * Stefano Ceccherini (stefano.ceccherini@gmail.com) 8c7023acdSAxel Dörfler */ 9c7023acdSAxel Dörfler 10c7023acdSAxel Dörfler //! BMenuWindow is a custom BWindow for BMenus. 11c7023acdSAxel Dörfler 12059ca4bdSStefano Ceccherini #include <Debug.h> 13446b8c19SStefano Ceccherini #include <Menu.h> 14446b8c19SStefano Ceccherini 152c11ec31SStefano Ceccherini #include <MenuPrivate.h> 162c11ec31SStefano Ceccherini #include <MenuWindow.h> 17c7023acdSAxel Dörfler #include <WindowPrivate.h> 18c7023acdSAxel Dörfler 19e411c658SStefano Ceccherini 202628e60cSAxel Dörfler namespace BPrivate { 212628e60cSAxel Dörfler 222628e60cSAxel Dörfler class BMenuScroller : public BView { 232628e60cSAxel Dörfler public: 243616859aSStefano Ceccherini BMenuScroller(BRect frame); 252628e60cSAxel Dörfler 263616859aSStefano Ceccherini bool IsEnabled() const; 273616859aSStefano Ceccherini void SetEnabled(const bool &enabled); 282628e60cSAxel Dörfler private: 293616859aSStefano Ceccherini bool fEnabled; 302628e60cSAxel Dörfler }; 312628e60cSAxel Dörfler 323616859aSStefano Ceccherini 332628e60cSAxel Dörfler class BMenuFrame : public BView { 342628e60cSAxel Dörfler public: 352628e60cSAxel Dörfler BMenuFrame(BMenu *menu); 362628e60cSAxel Dörfler 372628e60cSAxel Dörfler virtual void AttachedToWindow(); 382628e60cSAxel Dörfler virtual void DetachedFromWindow(); 392628e60cSAxel Dörfler virtual void Draw(BRect updateRect); 402628e60cSAxel Dörfler 412628e60cSAxel Dörfler private: 422628e60cSAxel Dörfler friend class BMenuWindow; 432628e60cSAxel Dörfler 442628e60cSAxel Dörfler BMenu *fMenu; 452628e60cSAxel Dörfler }; 462628e60cSAxel Dörfler 473616859aSStefano Ceccherini 483616859aSStefano Ceccherini class UpperScroller : public BMenuScroller { 493616859aSStefano Ceccherini public: 503616859aSStefano Ceccherini UpperScroller(BRect frame); 513616859aSStefano Ceccherini virtual void Draw(BRect updateRect); 523616859aSStefano Ceccherini }; 533616859aSStefano Ceccherini 54059ca4bdSStefano Ceccherini 553616859aSStefano Ceccherini class LowerScroller : public BMenuScroller { 563616859aSStefano Ceccherini public: 573616859aSStefano Ceccherini LowerScroller(BRect frame); 583616859aSStefano Ceccherini virtual void Draw(BRect updateRect); 593616859aSStefano Ceccherini }; 603616859aSStefano Ceccherini 61059ca4bdSStefano Ceccherini 622628e60cSAxel Dörfler } // namespace BPrivate 632628e60cSAxel Dörfler 643616859aSStefano Ceccherini 652628e60cSAxel Dörfler using namespace BPrivate; 662628e60cSAxel Dörfler 672628e60cSAxel Dörfler 6827cc2508SStefano Ceccherini const int kScrollerHeight = 10; 69c7338938SStefano Ceccherini const int kScrollStep = 19; 705b752875SStefano Ceccherini 712628e60cSAxel Dörfler 723616859aSStefano Ceccherini BMenuScroller::BMenuScroller(BRect frame) 73c7338938SStefano Ceccherini : BView(frame, "menu scroller", 0, B_WILL_DRAW | B_FRAME_EVENTS), 743616859aSStefano Ceccherini fEnabled(false) 7527cc2508SStefano Ceccherini { 7627cc2508SStefano Ceccherini SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 7727cc2508SStefano Ceccherini } 7827cc2508SStefano Ceccherini 7927cc2508SStefano Ceccherini 80c7338938SStefano Ceccherini bool 813616859aSStefano Ceccherini BMenuScroller::IsEnabled() const 8227cc2508SStefano Ceccherini { 833616859aSStefano Ceccherini return fEnabled; 8427cc2508SStefano Ceccherini } 8527cc2508SStefano Ceccherini 8627cc2508SStefano Ceccherini 8727cc2508SStefano Ceccherini void 883616859aSStefano Ceccherini BMenuScroller::SetEnabled(const bool &enabled) 893616859aSStefano Ceccherini { 903616859aSStefano Ceccherini fEnabled = enabled; 913616859aSStefano Ceccherini } 923616859aSStefano Ceccherini 933616859aSStefano Ceccherini 943616859aSStefano Ceccherini // #pragma mark - 953616859aSStefano Ceccherini 963616859aSStefano Ceccherini 973616859aSStefano Ceccherini UpperScroller::UpperScroller(BRect frame) 983616859aSStefano Ceccherini : 993616859aSStefano Ceccherini BMenuScroller(frame) 1003616859aSStefano Ceccherini { 1013616859aSStefano Ceccherini } 1023616859aSStefano Ceccherini 1033616859aSStefano Ceccherini 1043616859aSStefano Ceccherini void 1053616859aSStefano Ceccherini UpperScroller::Draw(BRect updateRect) 10627cc2508SStefano Ceccherini { 10727cc2508SStefano Ceccherini SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 10827cc2508SStefano Ceccherini float middle = Bounds().right / 2; 10927cc2508SStefano Ceccherini 11027cc2508SStefano Ceccherini // Draw the upper arrow. 1113616859aSStefano Ceccherini if (IsEnabled()) 11227cc2508SStefano Ceccherini SetHighColor(0, 0, 0); 11327cc2508SStefano Ceccherini else 11427cc2508SStefano Ceccherini SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 11527cc2508SStefano Ceccherini B_DARKEN_2_TINT)); 11627cc2508SStefano Ceccherini 1173616859aSStefano Ceccherini FillRect(Bounds(), B_SOLID_LOW); 11827cc2508SStefano Ceccherini 11927cc2508SStefano Ceccherini FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3), 12027cc2508SStefano Ceccherini BPoint(middle + 5, (kScrollerHeight / 2) + 2), 12127cc2508SStefano Ceccherini BPoint(middle - 5, (kScrollerHeight / 2) + 2)); 12227cc2508SStefano Ceccherini } 12327cc2508SStefano Ceccherini 1243616859aSStefano Ceccherini 1253616859aSStefano Ceccherini // #pragma mark - 1263616859aSStefano Ceccherini 1273616859aSStefano Ceccherini 1283616859aSStefano Ceccherini LowerScroller::LowerScroller(BRect frame) 1293616859aSStefano Ceccherini : 1303616859aSStefano Ceccherini BMenuScroller(frame) 1313616859aSStefano Ceccherini { 1323616859aSStefano Ceccherini } 1333616859aSStefano Ceccherini 1343616859aSStefano Ceccherini 1353616859aSStefano Ceccherini void 1363616859aSStefano Ceccherini LowerScroller::Draw(BRect updateRect) 1373616859aSStefano Ceccherini { 1383616859aSStefano Ceccherini SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 1393616859aSStefano Ceccherini 1403616859aSStefano Ceccherini BRect frame = Bounds(); 14127cc2508SStefano Ceccherini // Draw the lower arrow. 1423616859aSStefano Ceccherini if (IsEnabled()) 14327cc2508SStefano Ceccherini SetHighColor(0, 0, 0); 14427cc2508SStefano Ceccherini else 14527cc2508SStefano Ceccherini SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 14627cc2508SStefano Ceccherini B_DARKEN_2_TINT)); 14727cc2508SStefano Ceccherini 148059ca4bdSStefano Ceccherini FillRect(frame, B_SOLID_LOW); 14927cc2508SStefano Ceccherini 1503616859aSStefano Ceccherini float middle = Bounds().right / 2; 1513616859aSStefano Ceccherini 1523616859aSStefano Ceccherini FillTriangle(BPoint(middle, frame.bottom - (kScrollerHeight / 2) + 3), 1533616859aSStefano Ceccherini BPoint(middle + 5, frame.bottom - (kScrollerHeight / 2) - 2), 1543616859aSStefano Ceccherini BPoint(middle - 5, frame.bottom - (kScrollerHeight / 2) - 2)); 15527cc2508SStefano Ceccherini } 15627cc2508SStefano Ceccherini 15727cc2508SStefano Ceccherini 15827cc2508SStefano Ceccherini // #pragma mark - 1595b752875SStefano Ceccherini 1605b752875SStefano Ceccherini 1612628e60cSAxel Dörfler BMenuFrame::BMenuFrame(BMenu *menu) 1622628e60cSAxel Dörfler : BView(BRect(0, 0, 1, 1), "menu frame", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), 1632628e60cSAxel Dörfler fMenu(menu) 1642628e60cSAxel Dörfler { 1652628e60cSAxel Dörfler } 1662628e60cSAxel Dörfler 1672628e60cSAxel Dörfler 1682628e60cSAxel Dörfler void 1692628e60cSAxel Dörfler BMenuFrame::AttachedToWindow() 1702628e60cSAxel Dörfler { 1712628e60cSAxel Dörfler BView::AttachedToWindow(); 1722628e60cSAxel Dörfler 1732628e60cSAxel Dörfler if (fMenu != NULL) 1742628e60cSAxel Dörfler AddChild(fMenu); 1752628e60cSAxel Dörfler 1762628e60cSAxel Dörfler ResizeTo(Window()->Bounds().Width(), Window()->Bounds().Height()); 1772628e60cSAxel Dörfler if (fMenu != NULL) { 1782628e60cSAxel Dörfler BFont font; 1792628e60cSAxel Dörfler fMenu->GetFont(&font); 1802628e60cSAxel Dörfler SetFont(&font); 1812628e60cSAxel Dörfler } 1822628e60cSAxel Dörfler } 1832628e60cSAxel Dörfler 1842628e60cSAxel Dörfler 1852628e60cSAxel Dörfler void 1862628e60cSAxel Dörfler BMenuFrame::DetachedFromWindow() 1872628e60cSAxel Dörfler { 1882628e60cSAxel Dörfler if (fMenu != NULL) 1892628e60cSAxel Dörfler RemoveChild(fMenu); 1902628e60cSAxel Dörfler } 1912628e60cSAxel Dörfler 1922628e60cSAxel Dörfler 1932628e60cSAxel Dörfler void 1942628e60cSAxel Dörfler BMenuFrame::Draw(BRect updateRect) 1952628e60cSAxel Dörfler { 1962628e60cSAxel Dörfler if (fMenu != NULL && fMenu->CountItems() == 0) { 1972628e60cSAxel Dörfler // TODO: Review this as it's a bit hacky. 1982628e60cSAxel Dörfler // Menu has a size of 0, 0, since there are no items in it. 1992628e60cSAxel Dörfler // So the BMenuFrame class has to fake it and draw an empty item. 2002628e60cSAxel Dörfler // Note that we can't add a real "empty" item because then we couldn't 2012628e60cSAxel Dörfler // tell if the item was added by us or not. 2022628e60cSAxel Dörfler // See also BMenu::UpdateWindowViewSize() 2032628e60cSAxel Dörfler SetHighColor(ui_color(B_MENU_BACKGROUND_COLOR)); 2042628e60cSAxel Dörfler SetLowColor(HighColor()); 2052628e60cSAxel Dörfler FillRect(updateRect); 2062628e60cSAxel Dörfler 2072628e60cSAxel Dörfler font_height height; 2082628e60cSAxel Dörfler GetFontHeight(&height); 2092628e60cSAxel Dörfler SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DISABLED_LABEL_TINT)); 2102628e60cSAxel Dörfler BPoint where((Bounds().Width() - fMenu->StringWidth(kEmptyMenuLabel)) / 2, ceilf(height.ascent + 1)); 2112628e60cSAxel Dörfler DrawString(kEmptyMenuLabel, where); 2122628e60cSAxel Dörfler } 2132628e60cSAxel Dörfler 2142628e60cSAxel Dörfler SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 2152628e60cSAxel Dörfler BRect bounds(Bounds()); 2162628e60cSAxel Dörfler 2172628e60cSAxel Dörfler StrokeLine(BPoint(bounds.right, bounds.top), 2182628e60cSAxel Dörfler BPoint(bounds.right, bounds.bottom - 1)); 2192628e60cSAxel Dörfler StrokeLine(BPoint(bounds.left + 1, bounds.bottom), 2202628e60cSAxel Dörfler BPoint(bounds.right, bounds.bottom)); 2212628e60cSAxel Dörfler } 2222628e60cSAxel Dörfler 2232628e60cSAxel Dörfler 2243616859aSStefano Ceccherini 2252628e60cSAxel Dörfler // #pragma mark - 2262628e60cSAxel Dörfler 2272628e60cSAxel Dörfler 2284185bd8bSStefano Ceccherini BMenuWindow::BMenuWindow(const char *name) 2291664b981SStefano Ceccherini // The window will be resized by BMenu, so just pass a dummy rect 230c7023acdSAxel Dörfler : BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel, 231c7023acdSAxel Dörfler B_NOT_ZOOMABLE | B_AVOID_FOCUS), 2323616859aSStefano Ceccherini fMenu(NULL), 2333616859aSStefano Ceccherini fMenuFrame(NULL), 2343616859aSStefano Ceccherini fUpperScroller(NULL), 2353616859aSStefano Ceccherini fLowerScroller(NULL) 236446b8c19SStefano Ceccherini { 237446b8c19SStefano Ceccherini } 238446b8c19SStefano Ceccherini 239446b8c19SStefano Ceccherini 240446b8c19SStefano Ceccherini BMenuWindow::~BMenuWindow() 241446b8c19SStefano Ceccherini { 242059ca4bdSStefano Ceccherini DetachMenu(); 243446b8c19SStefano Ceccherini } 2445aa032f1SStefano Ceccherini 2455aa032f1SStefano Ceccherini 24661ba5a32SStefano Ceccherini void 247*9e64a7edSStefano Ceccherini BMenuWindow::DispatchMessage(BMessage *message, BHandler *handler) 248*9e64a7edSStefano Ceccherini { 249*9e64a7edSStefano Ceccherini BWindow::DispatchMessage(message, handler); 250*9e64a7edSStefano Ceccherini } 251*9e64a7edSStefano Ceccherini 252*9e64a7edSStefano Ceccherini 253*9e64a7edSStefano Ceccherini void 2544185bd8bSStefano Ceccherini BMenuWindow::AttachMenu(BMenu *menu) 2554185bd8bSStefano Ceccherini { 25627cc2508SStefano Ceccherini if (fMenuFrame) 25727cc2508SStefano Ceccherini debugger("BMenuWindow: a menu is already attached!"); 2584185bd8bSStefano Ceccherini if (menu != NULL) { 25927cc2508SStefano Ceccherini fMenuFrame = new BMenuFrame(menu); 26027cc2508SStefano Ceccherini AddChild(fMenuFrame); 26107dc2c69SAxel Dörfler menu->MakeFocus(true); 2623616859aSStefano Ceccherini fMenu = menu; 2634185bd8bSStefano Ceccherini } 2644185bd8bSStefano Ceccherini } 2654185bd8bSStefano Ceccherini 2664185bd8bSStefano Ceccherini 2674185bd8bSStefano Ceccherini void 2684185bd8bSStefano Ceccherini BMenuWindow::DetachMenu() 26961ba5a32SStefano Ceccherini { 27027cc2508SStefano Ceccherini DetachScrollers(); 2713616859aSStefano Ceccherini if (fMenuFrame) { 27227cc2508SStefano Ceccherini RemoveChild(fMenuFrame); 27327cc2508SStefano Ceccherini delete fMenuFrame; 27427cc2508SStefano Ceccherini fMenuFrame = NULL; 2753616859aSStefano Ceccherini fMenu = NULL; 27627cc2508SStefano Ceccherini } 27727cc2508SStefano Ceccherini } 27827cc2508SStefano Ceccherini 27927cc2508SStefano Ceccherini 28027cc2508SStefano Ceccherini void 28127cc2508SStefano Ceccherini BMenuWindow::AttachScrollers() 28227cc2508SStefano Ceccherini { 28327cc2508SStefano Ceccherini // We want to attach a scroller only if there's a menu frame already 28427cc2508SStefano Ceccherini // existing. 2853616859aSStefano Ceccherini if (!fMenu || !fMenuFrame) 28627cc2508SStefano Ceccherini return; 28727cc2508SStefano Ceccherini 288059ca4bdSStefano Ceccherini if (fUpperScroller || fLowerScroller) 289059ca4bdSStefano Ceccherini debugger("Scrollers are already attached!"); 290059ca4bdSStefano Ceccherini 2913616859aSStefano Ceccherini fMenu->MakeFocus(true); 29227cc2508SStefano Ceccherini 2933616859aSStefano Ceccherini BRect frame = Bounds(); 2943616859aSStefano Ceccherini fUpperScroller = new UpperScroller(BRect(0, 0, frame.right, kScrollerHeight)); 2953616859aSStefano Ceccherini AddChild(fUpperScroller); 2963616859aSStefano Ceccherini fLowerScroller = new LowerScroller(BRect(0, frame.bottom - kScrollerHeight, frame.right, frame.bottom)); 2973616859aSStefano Ceccherini AddChild(fLowerScroller); 2983616859aSStefano Ceccherini 2993616859aSStefano Ceccherini fUpperScroller->SetEnabled(false); 3003616859aSStefano Ceccherini fLowerScroller->SetEnabled(true); 30127cc2508SStefano Ceccherini 302cf5948aeSStefano Ceccherini fMenuFrame->ResizeBy(0, -2 * kScrollerHeight - 1); 30327cc2508SStefano Ceccherini fMenuFrame->MoveBy(0, kScrollerHeight); 3043616859aSStefano Ceccherini 3053616859aSStefano Ceccherini fValue = 0; 3063616859aSStefano Ceccherini fLimit = fMenu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight); 30727cc2508SStefano Ceccherini } 30827cc2508SStefano Ceccherini 30927cc2508SStefano Ceccherini 31027cc2508SStefano Ceccherini void 31127cc2508SStefano Ceccherini BMenuWindow::DetachScrollers() 31227cc2508SStefano Ceccherini { 31327cc2508SStefano Ceccherini // BeOS doesn't remember the position where the last scrolling ended, 31427cc2508SStefano Ceccherini // so we just scroll back to the beginning. 315059ca4bdSStefano Ceccherini if (fMenu) 3163616859aSStefano Ceccherini fMenu->ScrollTo(0, 0); 31727cc2508SStefano Ceccherini 3183616859aSStefano Ceccherini if (fLowerScroller) { 3193616859aSStefano Ceccherini RemoveChild(fLowerScroller); 3203616859aSStefano Ceccherini delete fLowerScroller; 3213616859aSStefano Ceccherini fLowerScroller = NULL; 3223616859aSStefano Ceccherini } 32327cc2508SStefano Ceccherini 3243616859aSStefano Ceccherini if (fUpperScroller) { 3253616859aSStefano Ceccherini RemoveChild(fUpperScroller); 3263616859aSStefano Ceccherini delete fUpperScroller; 3273616859aSStefano Ceccherini fUpperScroller = NULL; 3283616859aSStefano Ceccherini } 32961ba5a32SStefano Ceccherini } 330c7338938SStefano Ceccherini 331c7338938SStefano Ceccherini 332c7338938SStefano Ceccherini bool 333c7338938SStefano Ceccherini BMenuWindow::CheckForScrolling(BPoint cursor) 334c7338938SStefano Ceccherini { 335059ca4bdSStefano Ceccherini if (!fMenuFrame || !fUpperScroller || !fLowerScroller) 336c7338938SStefano Ceccherini return false; 337c7338938SStefano Ceccherini 3383616859aSStefano Ceccherini return _Scroll(cursor); 3393616859aSStefano Ceccherini } 3403616859aSStefano Ceccherini 3413616859aSStefano Ceccherini 3423616859aSStefano Ceccherini bool 3433616859aSStefano Ceccherini BMenuWindow::_Scroll(BPoint cursor) 3443616859aSStefano Ceccherini { 345059ca4bdSStefano Ceccherini ASSERT((fLowerScroller != NULL)); 346059ca4bdSStefano Ceccherini ASSERT((fUpperScroller != NULL)); 347059ca4bdSStefano Ceccherini 3483616859aSStefano Ceccherini ConvertFromScreen(&cursor); 3493616859aSStefano Ceccherini 350059ca4bdSStefano Ceccherini BRect lowerFrame = fLowerScroller->Frame(); 351059ca4bdSStefano Ceccherini BRect upperFrame = fUpperScroller->Frame(); 3523616859aSStefano Ceccherini 353059ca4bdSStefano Ceccherini if (fLowerScroller->IsEnabled() && lowerFrame.Contains(cursor)) { 3543616859aSStefano Ceccherini if (fValue == 0) { 3553616859aSStefano Ceccherini fUpperScroller->SetEnabled(true); 3563616859aSStefano Ceccherini fUpperScroller->Invalidate(); 3573616859aSStefano Ceccherini } 3583616859aSStefano Ceccherini 3593616859aSStefano Ceccherini if (fValue + kScrollStep >= fLimit) { 3603616859aSStefano Ceccherini // If we reached the limit, we don't want to scroll a whole 3613616859aSStefano Ceccherini // 'step' if not needed. 3623616859aSStefano Ceccherini fMenu->ScrollBy(0, fLimit - fValue); 3633616859aSStefano Ceccherini fValue = fLimit; 3643616859aSStefano Ceccherini fLowerScroller->SetEnabled(false); 3653616859aSStefano Ceccherini fLowerScroller->Invalidate(); 3663616859aSStefano Ceccherini 3673616859aSStefano Ceccherini } else { 3683616859aSStefano Ceccherini fMenu->ScrollBy(0, kScrollStep); 3693616859aSStefano Ceccherini fValue += kScrollStep; 3703616859aSStefano Ceccherini } 371059ca4bdSStefano Ceccherini } else if (fUpperScroller->IsEnabled() && upperFrame.Contains(cursor)) { 3723616859aSStefano Ceccherini if (fValue == fLimit) { 3733616859aSStefano Ceccherini fLowerScroller->SetEnabled(true); 3743616859aSStefano Ceccherini fLowerScroller->Invalidate(); 3753616859aSStefano Ceccherini } 3763616859aSStefano Ceccherini 3773616859aSStefano Ceccherini if (fValue - kScrollStep <= 0) { 3783616859aSStefano Ceccherini fMenu->ScrollBy(0, -fValue); 3793616859aSStefano Ceccherini fValue = 0; 3803616859aSStefano Ceccherini fUpperScroller->SetEnabled(false); 3813616859aSStefano Ceccherini fUpperScroller->Invalidate(); 3823616859aSStefano Ceccherini 3833616859aSStefano Ceccherini } else { 3843616859aSStefano Ceccherini fMenu->ScrollBy(0, -kScrollStep); 3853616859aSStefano Ceccherini fValue -= kScrollStep; 3863616859aSStefano Ceccherini } 387059ca4bdSStefano Ceccherini } else 3883616859aSStefano Ceccherini return false; 3893616859aSStefano Ceccherini 3903616859aSStefano Ceccherini snooze(10000); 3913616859aSStefano Ceccherini 3923616859aSStefano Ceccherini return true; 393c7338938SStefano Ceccherini } 394c7338938SStefano Ceccherini 395