diff --git a/headers/os/interface/Menu.h b/headers/os/interface/Menu.h index cac85ef276..419fa406fd 100644 --- a/headers/os/interface/Menu.h +++ b/headers/os/interface/Menu.h @@ -188,7 +188,7 @@ virtual void _ReservedMenu6(); void InitData(BMessage *data = NULL); bool _show(bool selectFirstItem = false); void _hide(); - BMenuItem *_track(int *action, long start = -1); + BMenuItem *_track(int *action, bigtime_t trackTime, long start = -1); bool _AddItem(BMenuItem *item, int32 index); bool RemoveItems(int32 index, int32 count, diff --git a/src/kits/interface/Menu.cpp b/src/kits/interface/Menu.cpp index 41e83fc127..25c6683ca1 100644 --- a/src/kits/interface/Menu.cpp +++ b/src/kits/interface/Menu.cpp @@ -975,7 +975,7 @@ BMenu::Track(bool openAnyway, BRect *clickToOpenRect) } int action; - BMenuItem *menuItem = _track(&action, -1); + BMenuItem *menuItem = _track(&action, system_time()); SetStickyMode(false); fExtraRect = NULL; @@ -1136,7 +1136,7 @@ BMenu::_hide() BMenuItem * -BMenu::_track(int *action, long start) +BMenu::_track(int *action, bigtime_t trackTime, long start) { // TODO: cleanup ulong buttons; @@ -1186,7 +1186,7 @@ BMenu::_track(int *action, long start) UnlockLooper(); locked = false; int submenuAction = MENU_ACT_NONE; - BMenuItem *submenuItem = fSelected->Submenu()->_track(&submenuAction); + BMenuItem *submenuItem = fSelected->Submenu()->_track(&submenuAction, startTime); if (submenuAction == MENU_ACT_CLOSE) { item = submenuItem; localAction = submenuAction; @@ -1202,8 +1202,8 @@ BMenu::_track(int *action, long start) if (buttons != 0 && IsStickyMode()) { localAction = MENU_ACT_CLOSE; break; - } else if (buttons == 0) { - if (IsStickyPrefOn()) + } else if (buttons == 0 && !IsStickyMode()) { + if (IsStickyPrefOn() && system_time() < trackTime + 2000000) SetStickyMode(true); else { localAction = MENU_ACT_CLOSE; @@ -1220,6 +1220,9 @@ BMenu::_track(int *action, long start) UnlockLooper(); } + if (IsStickyMode()) + SetStickyMode(false); + // delete the menu window recycled for all the child menus DeleteMenuWindow(); @@ -1785,6 +1788,11 @@ void BMenu::SetStickyMode(bool on) { fStickyMode = on; + + // If we are switching to sticky mode, propagate the status + // back to the super menu + if (on && fSuper != NULL) + fSuper->SetStickyMode(on); } diff --git a/src/kits/interface/MenuBar.cpp b/src/kits/interface/MenuBar.cpp index 313be53b15..857648711d 100644 --- a/src/kits/interface/MenuBar.cpp +++ b/src/kits/interface/MenuBar.cpp @@ -376,16 +376,16 @@ BMenuBar::TrackTask(void *arg) BMenuItem * BMenuBar::Track(int32 *action, int32 startIndex, bool showMenu) -{ - // TODO: This function is very incomplete and just partially working: - // For example, it doesn't respect the "sticky mode" setting. +{ + // TODO: Cleanup, merge some "if" blocks if possible BMenuItem *resultItem = NULL; BWindow *window = Window(); int localAction = MENU_ACT_NONE; - + bigtime_t startTime = system_time(); while (true) { bigtime_t snoozeAmount = 30000; - if (!window->Lock())//WithTimeout(200000) < B_OK) + bool locked = window->Lock();//WithTimeout(200000) + if (!locked) break; BPoint where; @@ -393,12 +393,18 @@ BMenuBar::Track(int32 *action, int32 startIndex, bool showMenu) GetMouse(&where, &buttons); BMenuItem *menuItem = HitTestItems(where, B_ORIGIN); if (menuItem != NULL && menuItem != fSelected) { - // only select the item - SelectItem(menuItem, -1); - if (menuItem->Submenu() != NULL - && menuItem->Submenu()->Window() == NULL) { - // open the menu if it's not opened yet - SelectItem(menuItem); + // Select item if: + // - clicked in sticky mode + // - nonsticky mode, + // - no previous selection + if (fSelected == NULL || !IsStickyMode() || buttons != 0) { + // only select the item + SelectItem(menuItem, -1); + if (menuItem->Submenu() != NULL + && menuItem->Submenu()->Window() == NULL) { + // open the menu if it's not opened yet + SelectItem(menuItem); + } } } @@ -408,20 +414,22 @@ BMenuBar::Track(int32 *action, int32 startIndex, bool showMenu) BMenu *menu = fSelected->Submenu(); if (menu != NULL) { window->Unlock(); + locked = false; snoozeAmount = 0; - resultItem = menu->_track(&localAction); - if (!window->Lock())//WithTimeout(200000) < B_OK) - break; + resultItem = menu->_track(&localAction, startTime); } - } else if (menuItem == NULL && !fLastBounds->Contains(where)) + } else if (menuItem == NULL && !IsStickyMode()) SelectItem(NULL); - - window->Unlock(); - if (localAction == MENU_ACT_CLOSE || (buttons != 0 && IsStickyMode())) + if (locked) + window->Unlock(); + + if (localAction == MENU_ACT_CLOSE || (buttons != 0 && IsStickyMode() && menuItem == NULL)) break; - else if (buttons == 0) { - if (IsStickyPrefOn()) + else if (buttons == 0 && !IsStickyMode()) { + // Don't switch to sticky mode if user kept the mouse pressed for too long + // TODO: Delay could be smaller, but then it wouldn't be noticeable on QEMU on my machine + if (IsStickyPrefOn() && system_time() < startTime + 2000000) SetStickyMode(true); else break; @@ -444,6 +452,7 @@ BMenuBar::Track(int32 *action, int32 startIndex, bool showMenu) if (IsStickyMode()) SetStickyMode(false); + DeleteMenuWindow(); if (action != NULL)