Applied patch by Pete Goodeve from ticket #7182. It improves
keyboard navigation/tracking of BMenus and BMenuBars, although many issues remain. Should not yet go into alpha, since there is one issue which I am not sure if it's not a regression. The issue is that invoking a menu item with Enter for the first time seems to have no effect, while invoking it subsequently works as expected. I don't know, yet, if that's a regression of this patch. In any case, it's better than before, thanks, Pete! git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42004 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
7b03963e8a
commit
e750d35cbf
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2006-2008, Haiku, Inc.
|
* Copyright 2006-2011, Haiku, Inc.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@ -14,6 +14,8 @@
|
|||||||
enum menu_states {
|
enum menu_states {
|
||||||
MENU_STATE_TRACKING = 0,
|
MENU_STATE_TRACKING = 0,
|
||||||
MENU_STATE_TRACKING_SUBMENU = 1,
|
MENU_STATE_TRACKING_SUBMENU = 1,
|
||||||
|
MENU_STATE_KEY_TO_SUBMENU = 2,
|
||||||
|
MENU_STATE_KEY_LEAVE_SUBMENU = 3,
|
||||||
MENU_STATE_CLOSED = 5
|
MENU_STATE_CLOSED = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2001-2009, Haiku Inc. All rights reserved.
|
* Copyright 2001-2011, Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT license.
|
* Distributed under the terms of the MIT license.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@ -477,6 +477,13 @@ BMenu::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case B_DOWN_ARROW:
|
case B_DOWN_ARROW:
|
||||||
|
{
|
||||||
|
BMenuBar* bar = dynamic_cast<BMenuBar*>(Supermenu());
|
||||||
|
if (bar != NULL && fState == MENU_STATE_CLOSED) {
|
||||||
|
// tell MenuBar's _Track:
|
||||||
|
bar->fState = MENU_STATE_KEY_TO_SUBMENU;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (fLayout == B_ITEMS_IN_COLUMN)
|
if (fLayout == B_ITEMS_IN_COLUMN)
|
||||||
_SelectNextItem(fSelected, true);
|
_SelectNextItem(fSelected, true);
|
||||||
break;
|
break;
|
||||||
@ -494,8 +501,10 @@ BMenu::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
// another top level menu.
|
// another top level menu.
|
||||||
BMessenger msgr(Supermenu());
|
BMessenger msgr(Supermenu());
|
||||||
msgr.SendMessage(Window()->CurrentMessage());
|
msgr.SendMessage(Window()->CurrentMessage());
|
||||||
} else
|
} else {
|
||||||
_QuitTracking();
|
// tell _Track
|
||||||
|
fState = MENU_STATE_KEY_LEAVE_SUBMENU;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -505,10 +514,11 @@ BMenu::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
_SelectNextItem(fSelected, true);
|
_SelectNextItem(fSelected, true);
|
||||||
else {
|
else {
|
||||||
if (fSelected && fSelected->Submenu()) {
|
if (fSelected && fSelected->Submenu()) {
|
||||||
fSelected->Submenu()->_SetStickyMode(true);
|
fSelected->Submenu()->_SetStickyMode(true);
|
||||||
// fix me: this shouldn't be needed but dynamic menus
|
// fix me: this shouldn't be needed but dynamic menus
|
||||||
// aren't getting it set correctly when keyboard
|
// aren't getting it set correctly when keyboard
|
||||||
// navigating, which aborts the attach
|
// navigating, which aborts the attach
|
||||||
|
fState = MENU_STATE_KEY_TO_SUBMENU;
|
||||||
_SelectItem(fSelected, true, true, true);
|
_SelectItem(fSelected, true, true, true);
|
||||||
} else if (dynamic_cast<BMenuBar*>(Supermenu())) {
|
} else if (dynamic_cast<BMenuBar*>(Supermenu())) {
|
||||||
// if we have no submenu and we're an
|
// if we have no submenu and we're an
|
||||||
@ -539,13 +549,20 @@ BMenu::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
case B_ENTER:
|
case B_ENTER:
|
||||||
case B_SPACE:
|
case B_SPACE:
|
||||||
if (fSelected) {
|
if (fSelected) {
|
||||||
_InvokeItem(fSelected);
|
// preserve for exit handling
|
||||||
|
fChosenItem = fSelected;
|
||||||
_QuitTracking(false);
|
_QuitTracking(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case B_ESCAPE:
|
case B_ESCAPE:
|
||||||
_QuitTracking();
|
_SelectItem(NULL);
|
||||||
|
if (fState == MENU_STATE_CLOSED && dynamic_cast<BMenuBar*>(Supermenu())) {
|
||||||
|
// Keyboard may show menu without tracking it
|
||||||
|
BMessenger msgr(Supermenu());
|
||||||
|
msgr.SendMessage(Window()->CurrentMessage());
|
||||||
|
} else
|
||||||
|
_QuitTracking(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1580,8 +1597,8 @@ BMenu::_Track(int* action, long start)
|
|||||||
bigtime_t navigationAreaTime = 0;
|
bigtime_t navigationAreaTime = 0;
|
||||||
|
|
||||||
fState = MENU_STATE_TRACKING;
|
fState = MENU_STATE_TRACKING;
|
||||||
if (fSuper != NULL)
|
// we will use this for keyboard selection:
|
||||||
fSuper->fState = MENU_STATE_TRACKING_SUBMENU;
|
fChosenItem = NULL;
|
||||||
|
|
||||||
BPoint location;
|
BPoint location;
|
||||||
uint32 buttons = 0;
|
uint32 buttons = 0;
|
||||||
@ -1608,11 +1625,15 @@ BMenu::_Track(int* action, long start)
|
|||||||
// The order of the checks is important
|
// The order of the checks is important
|
||||||
// to be able to handle overlapping menus:
|
// to be able to handle overlapping menus:
|
||||||
// first we check if mouse is inside a submenu,
|
// first we check if mouse is inside a submenu,
|
||||||
// then if the menu is inside this menu,
|
// then if the mouse is inside this menu,
|
||||||
// then if it's over a super menu.
|
// then if it's over a super menu.
|
||||||
bool overSub = _OverSubmenu(fSelected, screenLocation);
|
bool overSub = _OverSubmenu(fSelected, screenLocation);
|
||||||
item = _HitTestItems(location, B_ORIGIN);
|
item = _HitTestItems(location, B_ORIGIN);
|
||||||
if (overSub) {
|
if (overSub || fState == MENU_STATE_KEY_TO_SUBMENU) {
|
||||||
|
if (fState == MENU_STATE_TRACKING) {
|
||||||
|
// not if from R.Arrow
|
||||||
|
fState = MENU_STATE_TRACKING_SUBMENU;
|
||||||
|
}
|
||||||
navAreaRectAbove = BRect();
|
navAreaRectAbove = BRect();
|
||||||
navAreaRectBelow = BRect();
|
navAreaRectBelow = BRect();
|
||||||
|
|
||||||
@ -1633,7 +1654,19 @@ BMenu::_Track(int* action, long start)
|
|||||||
if (submenuAction == MENU_STATE_CLOSED) {
|
if (submenuAction == MENU_STATE_CLOSED) {
|
||||||
item = submenuItem;
|
item = submenuItem;
|
||||||
fState = MENU_STATE_CLOSED;
|
fState = MENU_STATE_CLOSED;
|
||||||
}
|
} else if (submenuAction == MENU_STATE_KEY_LEAVE_SUBMENU) {
|
||||||
|
if (LockLooper()) {
|
||||||
|
BMenuItem *temp = fSelected;
|
||||||
|
// close the submenu:
|
||||||
|
_SelectItem(NULL);
|
||||||
|
// but reselect the item itself for user:
|
||||||
|
_SelectItem(temp, false);
|
||||||
|
UnlockLooper();
|
||||||
|
}
|
||||||
|
// cancel key-nav state
|
||||||
|
fState = MENU_STATE_TRACKING;
|
||||||
|
} else
|
||||||
|
fState = MENU_STATE_TRACKING;
|
||||||
if (!LockLooper())
|
if (!LockLooper())
|
||||||
break;
|
break;
|
||||||
} else if (item != NULL) {
|
} else if (item != NULL) {
|
||||||
@ -1641,11 +1674,14 @@ BMenu::_Track(int* action, long start)
|
|||||||
navAreaRectBelow, selectedTime, navigationAreaTime);
|
navAreaRectBelow, selectedTime, navigationAreaTime);
|
||||||
if (!releasedOnce)
|
if (!releasedOnce)
|
||||||
releasedOnce = true;
|
releasedOnce = true;
|
||||||
} else if (_OverSuper(screenLocation)) {
|
} else if (_OverSuper(screenLocation) && fSuper->fState != MENU_STATE_KEY_TO_SUBMENU) {
|
||||||
fState = MENU_STATE_TRACKING;
|
fState = MENU_STATE_TRACKING;
|
||||||
UnlockLooper();
|
UnlockLooper();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else if (fState == MENU_STATE_KEY_LEAVE_SUBMENU) {
|
||||||
|
UnlockLooper();
|
||||||
|
break;
|
||||||
|
} else if (fSuper == NULL || fSuper->fState != MENU_STATE_KEY_TO_SUBMENU) {
|
||||||
// Mouse pointer outside menu:
|
// Mouse pointer outside menu:
|
||||||
// If there's no other submenu opened,
|
// If there's no other submenu opened,
|
||||||
// deselect the current selected item
|
// deselect the current selected item
|
||||||
@ -1676,7 +1712,7 @@ BMenu::_Track(int* action, long start)
|
|||||||
uint32 newButtons = buttons;
|
uint32 newButtons = buttons;
|
||||||
|
|
||||||
// If user doesn't move the mouse, loop here,
|
// If user doesn't move the mouse, loop here,
|
||||||
// so we don't interfer with keyboard menu navigation
|
// so we don't interfere with keyboard menu navigation
|
||||||
do {
|
do {
|
||||||
snooze(snoozeAmount);
|
snooze(snoozeAmount);
|
||||||
if (!LockLooper())
|
if (!LockLooper())
|
||||||
@ -1684,7 +1720,8 @@ BMenu::_Track(int* action, long start)
|
|||||||
GetMouse(&newLocation, &newButtons, true);
|
GetMouse(&newLocation, &newButtons, true);
|
||||||
UnlockLooper();
|
UnlockLooper();
|
||||||
} while (newLocation == location && newButtons == buttons
|
} while (newLocation == location && newButtons == buttons
|
||||||
&& !(item && item->Submenu() != NULL));
|
&& !(item && item->Submenu() != NULL)
|
||||||
|
&& fState == MENU_STATE_TRACKING);
|
||||||
|
|
||||||
if (newLocation != location || newButtons != buttons) {
|
if (newLocation != location || newButtons != buttons) {
|
||||||
if (!releasedOnce && newButtons == 0 && buttons != 0)
|
if (!releasedOnce && newButtons == 0 && buttons != 0)
|
||||||
@ -1700,12 +1737,19 @@ BMenu::_Track(int* action, long start)
|
|||||||
|
|
||||||
if (action != NULL)
|
if (action != NULL)
|
||||||
*action = fState;
|
*action = fState;
|
||||||
|
|
||||||
|
// keyboard Enter will set this
|
||||||
|
if (fChosenItem != NULL)
|
||||||
|
item = fChosenItem;
|
||||||
|
else if (fSelected == NULL)
|
||||||
|
// needed to cover (rare) mouse/ESC combination
|
||||||
|
item = NULL;
|
||||||
|
|
||||||
if (fSelected != NULL && LockLooper()) {
|
if (fSelected != NULL && LockLooper()) {
|
||||||
_SelectItem(NULL);
|
_SelectItem(NULL);
|
||||||
UnlockLooper();
|
UnlockLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the menu window recycled for all the child menus
|
// delete the menu window recycled for all the child menus
|
||||||
_DeleteMenuWindow();
|
_DeleteMenuWindow();
|
||||||
|
|
||||||
@ -2829,7 +2873,6 @@ BMenu::_QuitTracking(bool onlyThis)
|
|||||||
if (BMenuBar* menuBar = dynamic_cast<BMenuBar*>(this))
|
if (BMenuBar* menuBar = dynamic_cast<BMenuBar*>(this))
|
||||||
menuBar->_RestoreFocus();
|
menuBar->_RestoreFocus();
|
||||||
|
|
||||||
fChosenItem = NULL;
|
|
||||||
fState = MENU_STATE_CLOSED;
|
fState = MENU_STATE_CLOSED;
|
||||||
|
|
||||||
// Close the whole menu hierarchy
|
// Close the whole menu hierarchy
|
||||||
|
@ -566,7 +566,7 @@ BMenuBar::_Track(int32* action, int32 startIndex, bool showMenu)
|
|||||||
if (window->Lock()) {
|
if (window->Lock()) {
|
||||||
if (startIndex != -1) {
|
if (startIndex != -1) {
|
||||||
be_app->ObscureCursor();
|
be_app->ObscureCursor();
|
||||||
_SelectItem(ItemAt(startIndex), true, true);
|
_SelectItem(ItemAt(startIndex), true, false);
|
||||||
}
|
}
|
||||||
GetMouse(&where, &buttons);
|
GetMouse(&where, &buttons);
|
||||||
window->Unlock();
|
window->Unlock();
|
||||||
@ -582,7 +582,8 @@ BMenuBar::_Track(int32* action, int32 startIndex, bool showMenu)
|
|||||||
menuItem = ItemAt(0);
|
menuItem = ItemAt(0);
|
||||||
else
|
else
|
||||||
menuItem = _HitTestItems(where, B_ORIGIN);
|
menuItem = _HitTestItems(where, B_ORIGIN);
|
||||||
if (_OverSubmenu(fSelected, ConvertToScreen(where))) {
|
if (_OverSubmenu(fSelected, ConvertToScreen(where))
|
||||||
|
|| fState == MENU_STATE_KEY_TO_SUBMENU) {
|
||||||
// call _Track() from the selected sub-menu when the mouse cursor
|
// call _Track() from the selected sub-menu when the mouse cursor
|
||||||
// is over its window
|
// is over its window
|
||||||
BMenu* menu = fSelected->Submenu();
|
BMenu* menu = fSelected->Submenu();
|
||||||
@ -645,7 +646,7 @@ BMenuBar::_Track(int32* action, int32 startIndex, bool showMenu)
|
|||||||
|
|
||||||
if (fState != MENU_STATE_CLOSED) {
|
if (fState != MENU_STATE_CLOSED) {
|
||||||
// If user doesn't move the mouse, loop here,
|
// If user doesn't move the mouse, loop here,
|
||||||
// so we don't interfer with keyboard menu navigation
|
// so we don't interfere with keyboard menu navigation
|
||||||
BPoint newLocation = where;
|
BPoint newLocation = where;
|
||||||
uint32 newButtons = buttons;
|
uint32 newButtons = buttons;
|
||||||
do {
|
do {
|
||||||
@ -654,7 +655,8 @@ BMenuBar::_Track(int32* action, int32 startIndex, bool showMenu)
|
|||||||
break;
|
break;
|
||||||
GetMouse(&newLocation, &newButtons, true);
|
GetMouse(&newLocation, &newButtons, true);
|
||||||
UnlockLooper();
|
UnlockLooper();
|
||||||
} while (newLocation == where && newButtons == buttons);
|
} while (newLocation == where && newButtons == buttons
|
||||||
|
&& fState == MENU_STATE_TRACKING);
|
||||||
|
|
||||||
where = newLocation;
|
where = newLocation;
|
||||||
buttons = newButtons;
|
buttons = newButtons;
|
||||||
|
Loading…
Reference in New Issue
Block a user