1c7023acdSAxel Dörfler /* 2c7023acdSAxel Dörfler * Copyright 2001-2006, 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) 7c7023acdSAxel Dörfler * Stefano Ceccherini (burton666@libero.it) 8c7023acdSAxel Dörfler */ 9c7023acdSAxel Dörfler 10c7023acdSAxel Dörfler //! BMenuWindow is a custom BWindow for BMenus. 11c7023acdSAxel Dörfler 12446b8c19SStefano Ceccherini #include <Menu.h> 13446b8c19SStefano Ceccherini 142c11ec31SStefano Ceccherini #include <MenuPrivate.h> 152c11ec31SStefano Ceccherini #include <MenuWindow.h> 16c7023acdSAxel Dörfler #include <WindowPrivate.h> 17c7023acdSAxel Dörfler 18e411c658SStefano Ceccherini 19*2628e60cSAxel Dörfler namespace BPrivate { 20*2628e60cSAxel Dörfler 21*2628e60cSAxel Dörfler class BMenuScroller : public BView { 22*2628e60cSAxel Dörfler public: 23*2628e60cSAxel Dörfler BMenuScroller(BRect frame, BMenu *menu); 24*2628e60cSAxel Dörfler virtual ~BMenuScroller(); 25*2628e60cSAxel Dörfler 26*2628e60cSAxel Dörfler virtual void Pulse(); 27*2628e60cSAxel Dörfler virtual void Draw(BRect updateRect); 28*2628e60cSAxel Dörfler 29*2628e60cSAxel Dörfler private: 30*2628e60cSAxel Dörfler BMenu *fMenu; 31*2628e60cSAxel Dörfler BRect fUpperButton; 32*2628e60cSAxel Dörfler BRect fLowerButton; 33*2628e60cSAxel Dörfler 34*2628e60cSAxel Dörfler float fValue; 35*2628e60cSAxel Dörfler float fLimit; 36*2628e60cSAxel Dörfler 37*2628e60cSAxel Dörfler bool fUpperEnabled; 38*2628e60cSAxel Dörfler bool fLowerEnabled; 39*2628e60cSAxel Dörfler 40*2628e60cSAxel Dörfler uint32 fButton; 41*2628e60cSAxel Dörfler BPoint fPosition; 42*2628e60cSAxel Dörfler }; 43*2628e60cSAxel Dörfler 44*2628e60cSAxel Dörfler class BMenuFrame : public BView { 45*2628e60cSAxel Dörfler public: 46*2628e60cSAxel Dörfler BMenuFrame(BMenu *menu); 47*2628e60cSAxel Dörfler virtual ~BMenuFrame(); 48*2628e60cSAxel Dörfler 49*2628e60cSAxel Dörfler virtual void AttachedToWindow(); 50*2628e60cSAxel Dörfler virtual void DetachedFromWindow(); 51*2628e60cSAxel Dörfler virtual void Draw(BRect updateRect); 52*2628e60cSAxel Dörfler 53*2628e60cSAxel Dörfler private: 54*2628e60cSAxel Dörfler friend class BMenuWindow; 55*2628e60cSAxel Dörfler 56*2628e60cSAxel Dörfler BMenu *fMenu; 57*2628e60cSAxel Dörfler }; 58*2628e60cSAxel Dörfler 59*2628e60cSAxel Dörfler } // namespace BPrivate 60*2628e60cSAxel Dörfler 61*2628e60cSAxel Dörfler using namespace BPrivate; 62*2628e60cSAxel Dörfler 63*2628e60cSAxel Dörfler 6427cc2508SStefano Ceccherini const int kScrollerHeight = 10; 6527cc2508SStefano Ceccherini const int kScrollStep = 16; 665b752875SStefano Ceccherini 67*2628e60cSAxel Dörfler 6827cc2508SStefano Ceccherini BMenuScroller::BMenuScroller(BRect frame, BMenu *menu) 69*2628e60cSAxel Dörfler : BView(frame, "menu scroller", 0, 70*2628e60cSAxel Dörfler B_WILL_DRAW | B_FRAME_EVENTS | B_PULSE_NEEDED), 7127cc2508SStefano Ceccherini fMenu(menu), 7227cc2508SStefano Ceccherini fUpperButton(0, 0, frame.right, kScrollerHeight), 7327cc2508SStefano Ceccherini fLowerButton(0, frame.bottom - kScrollerHeight, frame.right, frame.bottom), 7427cc2508SStefano Ceccherini fValue(0), 7527cc2508SStefano Ceccherini fUpperEnabled(false), 7627cc2508SStefano Ceccherini fLowerEnabled(true) 7727cc2508SStefano Ceccherini { 7827cc2508SStefano Ceccherini if (!menu) 7927cc2508SStefano Ceccherini debugger("BMenuScroller(): Scroller not attached to a menu!"); 8027cc2508SStefano Ceccherini SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 8161ba5a32SStefano Ceccherini 8227cc2508SStefano Ceccherini fLimit = menu->Bounds().Height() - (frame.Height() - 2 * kScrollerHeight); 8327cc2508SStefano Ceccherini } 8427cc2508SStefano Ceccherini 8527cc2508SStefano Ceccherini 8627cc2508SStefano Ceccherini BMenuScroller::~BMenuScroller() 8727cc2508SStefano Ceccherini { 8827cc2508SStefano Ceccherini } 8927cc2508SStefano Ceccherini 9027cc2508SStefano Ceccherini 9127cc2508SStefano Ceccherini void 9227cc2508SStefano Ceccherini BMenuScroller::Pulse() 9327cc2508SStefano Ceccherini { 9427cc2508SStefano Ceccherini // To reduce the overhead even a little, fButton and fPosition were 9527cc2508SStefano Ceccherini // made private variables. 9627cc2508SStefano Ceccherini 9727cc2508SStefano Ceccherini // We track the mouse and move the scrollers depending on its position. 9827cc2508SStefano Ceccherini GetMouse(&fPosition, &fButton); 9927cc2508SStefano Ceccherini if (fLowerButton.Contains(fPosition) && fLowerEnabled) { 10027cc2508SStefano Ceccherini if (fValue == 0) { 10127cc2508SStefano Ceccherini fUpperEnabled = true; 10227cc2508SStefano Ceccherini 10327cc2508SStefano Ceccherini Invalidate(fUpperButton); 10427cc2508SStefano Ceccherini } 10527cc2508SStefano Ceccherini 10627cc2508SStefano Ceccherini if (fValue + kScrollStep >= fLimit) { 10727cc2508SStefano Ceccherini // If we reached the limit, we don't want to scroll a whole 10827cc2508SStefano Ceccherini // 'step' if not needed. 10927cc2508SStefano Ceccherini fMenu->ScrollBy(0, fLimit - fValue); 11027cc2508SStefano Ceccherini fValue = fLimit; 11127cc2508SStefano Ceccherini fLowerEnabled = false; 11227cc2508SStefano Ceccherini Invalidate(fLowerButton); 11327cc2508SStefano Ceccherini } else { 11427cc2508SStefano Ceccherini fMenu->ScrollBy(0, kScrollStep); 11527cc2508SStefano Ceccherini fValue += kScrollStep; 11627cc2508SStefano Ceccherini } 11727cc2508SStefano Ceccherini } else if (fUpperButton.Contains(fPosition) && fUpperEnabled) { 11827cc2508SStefano Ceccherini if (fValue == fLimit) { 11927cc2508SStefano Ceccherini fLowerEnabled = true; 12027cc2508SStefano Ceccherini Invalidate(fLowerButton); 12127cc2508SStefano Ceccherini } 12227cc2508SStefano Ceccherini 12327cc2508SStefano Ceccherini if (fValue - kScrollStep <= 0) { 12427cc2508SStefano Ceccherini fMenu->ScrollBy(0, -fValue); 12527cc2508SStefano Ceccherini fValue = 0; 12627cc2508SStefano Ceccherini fUpperEnabled = false; 12727cc2508SStefano Ceccherini Invalidate(fUpperButton); 12827cc2508SStefano Ceccherini } else { 12927cc2508SStefano Ceccherini fMenu->ScrollBy(0, -kScrollStep); 13027cc2508SStefano Ceccherini fValue -= kScrollStep; 13127cc2508SStefano Ceccherini } 13227cc2508SStefano Ceccherini 13327cc2508SStefano Ceccherini // In this case, we need to redraw the lower button because of a 13427cc2508SStefano Ceccherini // probable bug in ScrollBy handling. The scrolled view is drawing below 13527cc2508SStefano Ceccherini // its bounds, dirtying our button in this process. 13627cc2508SStefano Ceccherini // Redrawing it everytime makes scrolling a little slower. 13727cc2508SStefano Ceccherini // TODO: Try to find why and fix this. 13827cc2508SStefano Ceccherini Draw(fLowerButton); 13927cc2508SStefano Ceccherini } 14027cc2508SStefano Ceccherini } 14127cc2508SStefano Ceccherini 14227cc2508SStefano Ceccherini 14327cc2508SStefano Ceccherini void 14427cc2508SStefano Ceccherini BMenuScroller::Draw(BRect updateRect) 14527cc2508SStefano Ceccherini { 14627cc2508SStefano Ceccherini SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_1_TINT)); 14727cc2508SStefano Ceccherini float middle = Bounds().right / 2; 14827cc2508SStefano Ceccherini 14927cc2508SStefano Ceccherini // Draw the upper arrow. 15027cc2508SStefano Ceccherini if (updateRect.Intersects(fUpperButton)) { 15127cc2508SStefano Ceccherini if (fUpperEnabled) 15227cc2508SStefano Ceccherini SetHighColor(0, 0, 0); 15327cc2508SStefano Ceccherini else 15427cc2508SStefano Ceccherini SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 15527cc2508SStefano Ceccherini B_DARKEN_2_TINT)); 15627cc2508SStefano Ceccherini 15727cc2508SStefano Ceccherini FillRect(fUpperButton, B_SOLID_LOW); 15827cc2508SStefano Ceccherini 15927cc2508SStefano Ceccherini FillTriangle(BPoint(middle, (kScrollerHeight / 2) - 3), 16027cc2508SStefano Ceccherini BPoint(middle + 5, (kScrollerHeight / 2) + 2), 16127cc2508SStefano Ceccherini BPoint(middle - 5, (kScrollerHeight / 2) + 2)); 16227cc2508SStefano Ceccherini } 16327cc2508SStefano Ceccherini 16427cc2508SStefano Ceccherini // Draw the lower arrow. 16527cc2508SStefano Ceccherini if (updateRect.Intersects(fLowerButton)) { 16627cc2508SStefano Ceccherini if (fLowerEnabled) 16727cc2508SStefano Ceccherini SetHighColor(0, 0, 0); 16827cc2508SStefano Ceccherini else 16927cc2508SStefano Ceccherini SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 17027cc2508SStefano Ceccherini B_DARKEN_2_TINT)); 17127cc2508SStefano Ceccherini 17227cc2508SStefano Ceccherini FillRect(fLowerButton, B_SOLID_LOW); 17327cc2508SStefano Ceccherini 17427cc2508SStefano Ceccherini FillTriangle(BPoint(middle, fLowerButton.bottom - (kScrollerHeight / 2) + 3), 17527cc2508SStefano Ceccherini BPoint(middle + 5, fLowerButton.bottom - (kScrollerHeight / 2) - 2), 17627cc2508SStefano Ceccherini BPoint(middle - 5, fLowerButton.bottom - (kScrollerHeight / 2) - 2)); 17727cc2508SStefano Ceccherini } 17827cc2508SStefano Ceccherini } 17927cc2508SStefano Ceccherini 18027cc2508SStefano Ceccherini 18127cc2508SStefano Ceccherini // #pragma mark - 1825b752875SStefano Ceccherini 1835b752875SStefano Ceccherini 184*2628e60cSAxel Dörfler BMenuFrame::BMenuFrame(BMenu *menu) 185*2628e60cSAxel Dörfler : BView(BRect(0, 0, 1, 1), "menu frame", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), 186*2628e60cSAxel Dörfler fMenu(menu) 187*2628e60cSAxel Dörfler { 188*2628e60cSAxel Dörfler } 189*2628e60cSAxel Dörfler 190*2628e60cSAxel Dörfler 191*2628e60cSAxel Dörfler BMenuFrame::~BMenuFrame() 192*2628e60cSAxel Dörfler { 193*2628e60cSAxel Dörfler } 194*2628e60cSAxel Dörfler 195*2628e60cSAxel Dörfler 196*2628e60cSAxel Dörfler void 197*2628e60cSAxel Dörfler BMenuFrame::AttachedToWindow() 198*2628e60cSAxel Dörfler { 199*2628e60cSAxel Dörfler BView::AttachedToWindow(); 200*2628e60cSAxel Dörfler 201*2628e60cSAxel Dörfler if (fMenu != NULL) 202*2628e60cSAxel Dörfler AddChild(fMenu); 203*2628e60cSAxel Dörfler 204*2628e60cSAxel Dörfler ResizeTo(Window()->Bounds().Width(), Window()->Bounds().Height()); 205*2628e60cSAxel Dörfler if (fMenu != NULL) { 206*2628e60cSAxel Dörfler BFont font; 207*2628e60cSAxel Dörfler fMenu->GetFont(&font); 208*2628e60cSAxel Dörfler SetFont(&font); 209*2628e60cSAxel Dörfler } 210*2628e60cSAxel Dörfler } 211*2628e60cSAxel Dörfler 212*2628e60cSAxel Dörfler 213*2628e60cSAxel Dörfler void 214*2628e60cSAxel Dörfler BMenuFrame::DetachedFromWindow() 215*2628e60cSAxel Dörfler { 216*2628e60cSAxel Dörfler if (fMenu != NULL) 217*2628e60cSAxel Dörfler RemoveChild(fMenu); 218*2628e60cSAxel Dörfler } 219*2628e60cSAxel Dörfler 220*2628e60cSAxel Dörfler 221*2628e60cSAxel Dörfler void 222*2628e60cSAxel Dörfler BMenuFrame::Draw(BRect updateRect) 223*2628e60cSAxel Dörfler { 224*2628e60cSAxel Dörfler if (fMenu != NULL && fMenu->CountItems() == 0) { 225*2628e60cSAxel Dörfler // TODO: Review this as it's a bit hacky. 226*2628e60cSAxel Dörfler // Menu has a size of 0, 0, since there are no items in it. 227*2628e60cSAxel Dörfler // So the BMenuFrame class has to fake it and draw an empty item. 228*2628e60cSAxel Dörfler // Note that we can't add a real "empty" item because then we couldn't 229*2628e60cSAxel Dörfler // tell if the item was added by us or not. 230*2628e60cSAxel Dörfler // See also BMenu::UpdateWindowViewSize() 231*2628e60cSAxel Dörfler SetHighColor(ui_color(B_MENU_BACKGROUND_COLOR)); 232*2628e60cSAxel Dörfler SetLowColor(HighColor()); 233*2628e60cSAxel Dörfler FillRect(updateRect); 234*2628e60cSAxel Dörfler 235*2628e60cSAxel Dörfler font_height height; 236*2628e60cSAxel Dörfler GetFontHeight(&height); 237*2628e60cSAxel Dörfler SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DISABLED_LABEL_TINT)); 238*2628e60cSAxel Dörfler BPoint where((Bounds().Width() - fMenu->StringWidth(kEmptyMenuLabel)) / 2, ceilf(height.ascent + 1)); 239*2628e60cSAxel Dörfler DrawString(kEmptyMenuLabel, where); 240*2628e60cSAxel Dörfler } 241*2628e60cSAxel Dörfler 242*2628e60cSAxel Dörfler SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 243*2628e60cSAxel Dörfler BRect bounds(Bounds()); 244*2628e60cSAxel Dörfler 245*2628e60cSAxel Dörfler StrokeLine(BPoint(bounds.right, bounds.top), 246*2628e60cSAxel Dörfler BPoint(bounds.right, bounds.bottom - 1)); 247*2628e60cSAxel Dörfler StrokeLine(BPoint(bounds.left + 1, bounds.bottom), 248*2628e60cSAxel Dörfler BPoint(bounds.right, bounds.bottom)); 249*2628e60cSAxel Dörfler } 250*2628e60cSAxel Dörfler 251*2628e60cSAxel Dörfler 252*2628e60cSAxel Dörfler // #pragma mark - 253*2628e60cSAxel Dörfler 254*2628e60cSAxel Dörfler 2554185bd8bSStefano Ceccherini BMenuWindow::BMenuWindow(const char *name) 2561664b981SStefano Ceccherini // The window will be resized by BMenu, so just pass a dummy rect 257c7023acdSAxel Dörfler : BWindow(BRect(0, 0, 0, 0), name, B_BORDERED_WINDOW_LOOK, kMenuWindowFeel, 258c7023acdSAxel Dörfler B_NOT_ZOOMABLE | B_AVOID_FOCUS), 25927cc2508SStefano Ceccherini fScroller(NULL), 26027cc2508SStefano Ceccherini fMenuFrame(NULL) 261446b8c19SStefano Ceccherini { 26227cc2508SStefano Ceccherini SetPulseRate(200000); 263446b8c19SStefano Ceccherini } 264446b8c19SStefano Ceccherini 265446b8c19SStefano Ceccherini 266446b8c19SStefano Ceccherini BMenuWindow::~BMenuWindow() 267446b8c19SStefano Ceccherini { 268446b8c19SStefano Ceccherini } 2695aa032f1SStefano Ceccherini 2705aa032f1SStefano Ceccherini 27161ba5a32SStefano Ceccherini void 2724185bd8bSStefano Ceccherini BMenuWindow::AttachMenu(BMenu *menu) 2734185bd8bSStefano Ceccherini { 27427cc2508SStefano Ceccherini if (fMenuFrame) 27527cc2508SStefano Ceccherini debugger("BMenuWindow: a menu is already attached!"); 2764185bd8bSStefano Ceccherini if (menu != NULL) { 27727cc2508SStefano Ceccherini fMenuFrame = new BMenuFrame(menu); 27827cc2508SStefano Ceccherini AddChild(fMenuFrame); 27907dc2c69SAxel Dörfler menu->MakeFocus(true); 2804185bd8bSStefano Ceccherini } 2814185bd8bSStefano Ceccherini } 2824185bd8bSStefano Ceccherini 2834185bd8bSStefano Ceccherini 2844185bd8bSStefano Ceccherini void 2854185bd8bSStefano Ceccherini BMenuWindow::DetachMenu() 28661ba5a32SStefano Ceccherini { 28727cc2508SStefano Ceccherini if (fMenuFrame) { 28827cc2508SStefano Ceccherini if (fScroller) { 28927cc2508SStefano Ceccherini DetachScrollers(); 29027cc2508SStefano Ceccherini } else { 29127cc2508SStefano Ceccherini RemoveChild(fMenuFrame); 29227cc2508SStefano Ceccherini } 29327cc2508SStefano Ceccherini delete fMenuFrame; 29427cc2508SStefano Ceccherini fMenuFrame = NULL; 29527cc2508SStefano Ceccherini } 29627cc2508SStefano Ceccherini } 29727cc2508SStefano Ceccherini 29827cc2508SStefano Ceccherini 29927cc2508SStefano Ceccherini void 30027cc2508SStefano Ceccherini BMenuWindow::AttachScrollers() 30127cc2508SStefano Ceccherini { 30227cc2508SStefano Ceccherini // We want to attach a scroller only if there's a menu frame already 30327cc2508SStefano Ceccherini // existing. 30427cc2508SStefano Ceccherini if (fScroller || !fMenuFrame) 30527cc2508SStefano Ceccherini return; 30627cc2508SStefano Ceccherini 30727cc2508SStefano Ceccherini RemoveChild(fMenuFrame); 30827cc2508SStefano Ceccherini fScroller = new BMenuScroller(Bounds(), fMenuFrame->fMenu); 30927cc2508SStefano Ceccherini fScroller->AddChild(fMenuFrame); 31027cc2508SStefano Ceccherini AddChild(fScroller); 31127cc2508SStefano Ceccherini 31227cc2508SStefano Ceccherini fMenuFrame->fMenu->MakeFocus(true); 31327cc2508SStefano Ceccherini 31427cc2508SStefano Ceccherini fMenuFrame->ResizeBy(0, -2 * kScrollerHeight); 31527cc2508SStefano Ceccherini fMenuFrame->MoveBy(0, kScrollerHeight); 31627cc2508SStefano Ceccherini } 31727cc2508SStefano Ceccherini 31827cc2508SStefano Ceccherini 31927cc2508SStefano Ceccherini void 32027cc2508SStefano Ceccherini BMenuWindow::DetachScrollers() 32127cc2508SStefano Ceccherini { 32227cc2508SStefano Ceccherini if(!fScroller || !fMenuFrame) 32327cc2508SStefano Ceccherini return; 32427cc2508SStefano Ceccherini 32527cc2508SStefano Ceccherini // BeOS doesn't remember the position where the last scrolling ended, 32627cc2508SStefano Ceccherini // so we just scroll back to the beginning. 32727cc2508SStefano Ceccherini fMenuFrame->fMenu->ScrollTo(0, 0); 32827cc2508SStefano Ceccherini 32927cc2508SStefano Ceccherini fScroller->RemoveChild(fMenuFrame); 33027cc2508SStefano Ceccherini RemoveChild(fScroller); 33127cc2508SStefano Ceccherini 33227cc2508SStefano Ceccherini delete fScroller; 33327cc2508SStefano Ceccherini fScroller = NULL; 33461ba5a32SStefano Ceccherini } 335