diff --git a/src/kits/interface/Menu.cpp b/src/kits/interface/Menu.cpp index 95aa5a7e72..df1322cdba 100644 --- a/src/kits/interface/Menu.cpp +++ b/src/kits/interface/Menu.cpp @@ -499,6 +499,13 @@ BMenu::MessageReceived(BMessage* message) break; } + case B_MODIFIERS_CHANGED: + if (fSuper != NULL && fSuper->fState != MENU_STATE_CLOSED) { + // inform parent to update its modifier keys and relayout + BMessenger(fSuper).SendMessage(Window()->CurrentMessage()); + } + break; + default: BView::MessageReceived(message); break; diff --git a/src/kits/interface/MenuWindow.cpp b/src/kits/interface/MenuWindow.cpp index f1b373c087..6904e3f799 100644 --- a/src/kits/interface/MenuWindow.cpp +++ b/src/kits/interface/MenuWindow.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,11 @@ public: virtual void AttachedToWindow(); virtual void DetachedFromWindow(); virtual void Draw(BRect updateRect); + virtual void LayoutChanged(); + + void MoveSubmenusOver(BMenu* menu, + BRect menuFrame, + BRect screenFrame); private: friend class BMenuWindow; @@ -228,6 +234,91 @@ BMenuFrame::Draw(BRect updateRect) } +void +BMenuFrame::LayoutChanged() +{ + if (fMenu == NULL || Window() == NULL) + return BView::LayoutChanged(); + + // shift child menus over recursively + MoveSubmenusOver(fMenu, fMenu->ConvertToScreen(fMenu->Frame()), + (BScreen(fMenu->Window())).Frame()); + + BView::LayoutChanged(); +} + + +void +BMenuFrame::MoveSubmenusOver(BMenu* menu, BRect menuFrame, BRect screenFrame) +{ + if (menu == NULL) + return; + + BMenu* submenu; + BMenuWindow* submenuWindow; + BPoint submenuLoc; + BRect submenuFrame; + + int32 itemCount = menu->CountItems(); + for (int32 index = 0; index < itemCount; index++) { + submenu = menu->SubmenuAt(index); + if (submenu == NULL || submenu->Window() == NULL) + continue; // not an open submenu, next + + submenuWindow = dynamic_cast(submenu->Window()); + if (submenuWindow == NULL) + break; // submenu window was not a BMenuWindow, strange if true + + // found an open submenu, get submenu frame + if (submenu->LockLooper()) { + // need to lock looper because we're in a different thread + submenuFrame = submenu->Frame(); + submenu->ConvertToScreen(&submenuFrame); + submenu->UnlockLooper(); + } else { + // give up + break; + } + + // get submenu loc and convert it to screen coords using menu + if (menu->LockLooper()) { + // check if submenu should be displayed right or left of menu + BRect superFrame = submenu->Superitem()->Frame(); + if (submenuFrame.right < menuFrame.right) + submenuLoc = superFrame.LeftTop() - BPoint(submenuFrame.Width() + 1, -1); + else + submenuLoc = superFrame.RightTop() + BPoint(1, 1); + + menu->ConvertToScreen(&submenuLoc); + submenuFrame.OffsetTo(submenuLoc); + menu->UnlockLooper(); + } else { + // give up + break; + } + + // move submenu frame into screen bounds vertically + if (submenuFrame.Height() < screenFrame.Height()) { + if (submenuFrame.bottom >= screenFrame.bottom) + submenuLoc.y -= (submenuFrame.bottom - screenFrame.bottom); + else if (submenuFrame.top <= screenFrame.top) + submenuLoc.y += (screenFrame.top - submenuFrame.top); + } else { + // put menu at top of screen, turn on the scroll arrows + submenuLoc.y = 0; + } + + // move submenu window into place + submenuWindow->MoveTo(submenuLoc); + + // recurse through submenu's submenus + MoveSubmenusOver(submenu, submenuFrame, screenFrame); + + // we're done with this menu + break; + } +} + // #pragma mark -