mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 387499, 387985, key navigation and shortcuts not working when menulist is closed, r=neil,sr=bz
This commit is contained in:
parent
7f1f88be47
commit
3fe172d56a
@ -514,9 +514,12 @@ public:
|
||||
|
||||
/**
|
||||
* Handles navigation for menu accelkeys. Returns true if the key has
|
||||
* been handled.
|
||||
* been handled. If aFrame is specified, then the key is handled by that
|
||||
* popup, otherwise if aFrame is null, the key is handled by the active
|
||||
* popup or menubar.
|
||||
*/
|
||||
PRBool HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent);
|
||||
PRBool HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent,
|
||||
nsMenuPopupFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Handles cursor navigation within a menu. Returns true if the key has
|
||||
@ -524,6 +527,17 @@ public:
|
||||
*/
|
||||
PRBool HandleKeyboardNavigation(PRUint32 aKeyCode);
|
||||
|
||||
/**
|
||||
* Handle keyboard navigation within a menu popup specified by aFrame.
|
||||
* Returns true if the key was handled and that other default handling
|
||||
* should not occur.
|
||||
*/
|
||||
PRBool HandleKeyboardNavigationInPopup(nsMenuPopupFrame* aFrame,
|
||||
nsNavigationDirection aDir)
|
||||
{
|
||||
return HandleKeyboardNavigationInPopup(nsnull, aFrame, aDir);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
|
||||
|
||||
NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent);
|
||||
@ -594,11 +608,29 @@ protected:
|
||||
PRBool aIsMenu,
|
||||
PRBool aDeselectMenu);
|
||||
|
||||
// handle keyboard navigation within a menu popup. Returns true if the
|
||||
// key was handled and that other default handling should not occur.
|
||||
PRBool HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
|
||||
/**
|
||||
* Handle keyboard navigation within a menu popup specified by aItem.
|
||||
*/
|
||||
PRBool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
|
||||
nsNavigationDirection aDir)
|
||||
{
|
||||
return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Handle keyboard navigation within a menu popup aFrame. If aItem is
|
||||
* supplied, then it is expected to have a frame equal to aFrame.
|
||||
* If aItem is non-null, then the navigation may be redirected to
|
||||
* an open submenu if one exists. Returns true if the key was
|
||||
* handled and that other default handling should not occur.
|
||||
*/
|
||||
PRBool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
|
||||
nsMenuPopupFrame* aFrame,
|
||||
nsNavigationDirection aDir);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Set mouse capturing for the current popup. This traps mouse clicks that
|
||||
* occur outside the popup so that it can be closed up. aOldPopup should be
|
||||
|
@ -165,10 +165,15 @@ NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool*
|
||||
case NS_VK_DOWN:
|
||||
case NS_VK_HOME:
|
||||
case NS_VK_END:
|
||||
*aHandledFlag = pm->HandleKeyboardNavigation(keyCode);
|
||||
{
|
||||
nsNavigationDirection theDirection;
|
||||
NS_DIRECTION_FROM_KEY_CODE(popupFrame, theDirection, keyCode);
|
||||
*aHandledFlag =
|
||||
pm->HandleKeyboardNavigationInPopup(popupFrame, theDirection);
|
||||
return NS_OK;
|
||||
}
|
||||
default:
|
||||
*aHandledFlag = pm->HandleShortcutNavigation(aKeyEvent);
|
||||
*aHandledFlag = pm->HandleShortcutNavigation(aKeyEvent, popupFrame);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -1129,15 +1129,17 @@ nsXULPopupManager::CancelMenuTimer(nsIMenuParent* aMenuParent)
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsXULPopupManager::HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent)
|
||||
nsXULPopupManager::HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent,
|
||||
nsMenuPopupFrame* aFrame)
|
||||
{
|
||||
if (mCurrentMenu) {
|
||||
nsMenuPopupFrame* currentPopup = mCurrentMenu->Frame();
|
||||
if (!aFrame && mCurrentMenu)
|
||||
aFrame = mCurrentMenu->Frame();
|
||||
|
||||
if (aFrame) {
|
||||
PRBool action;
|
||||
nsMenuFrame* result = currentPopup->FindMenuWithShortcut(aKeyEvent, action);
|
||||
nsMenuFrame* result = aFrame->FindMenuWithShortcut(aKeyEvent, action);
|
||||
if (result) {
|
||||
currentPopup->ChangeMenuItem(result, PR_FALSE);
|
||||
aFrame->ChangeMenuItem(result, PR_FALSE);
|
||||
if (action) {
|
||||
nsMenuFrame* menuToOpen = result->Enter();
|
||||
if (menuToOpen) {
|
||||
@ -1236,21 +1238,25 @@ nsXULPopupManager::HandleKeyboardNavigation(PRUint32 aKeyCode)
|
||||
|
||||
PRBool
|
||||
nsXULPopupManager::HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
|
||||
nsMenuPopupFrame* aFrame,
|
||||
nsNavigationDirection aDir)
|
||||
{
|
||||
nsMenuPopupFrame* popupFrame = item->Frame();
|
||||
nsMenuFrame* currentMenu = popupFrame->GetCurrentMenuItem();
|
||||
NS_ASSERTION(aFrame, "aFrame is null");
|
||||
NS_ASSERTION(!item || item->Frame() == aFrame,
|
||||
"aFrame is expected to be equal to item->Frame()");
|
||||
|
||||
popupFrame->ClearIncrementalString();
|
||||
nsMenuFrame* currentMenu = aFrame->GetCurrentMenuItem();
|
||||
|
||||
aFrame->ClearIncrementalString();
|
||||
|
||||
// This method only gets called if we're open.
|
||||
if (!currentMenu && NS_DIRECTION_IS_INLINE(aDir)) {
|
||||
// We've been opened, but we haven't had anything selected.
|
||||
// We can handle End, but our parent handles Start.
|
||||
if (aDir == eNavigationDirection_End) {
|
||||
nsMenuFrame* nextItem = GetNextMenuItem(popupFrame, nsnull, PR_TRUE);
|
||||
nsMenuFrame* nextItem = GetNextMenuItem(aFrame, nsnull, PR_TRUE);
|
||||
if (nextItem) {
|
||||
popupFrame->ChangeMenuItem(nextItem, PR_FALSE);
|
||||
aFrame->ChangeMenuItem(nextItem, PR_FALSE);
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
@ -1264,7 +1270,7 @@ nsXULPopupManager::HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
|
||||
isContainer = currentMenu->IsMenu();
|
||||
if (isOpen) {
|
||||
// for an open popup, have the child process the event
|
||||
nsMenuChainItem* child = item->GetChild();
|
||||
nsMenuChainItem* child = item ? item->GetChild() : nsnull;
|
||||
if (child && HandleKeyboardNavigationInPopup(child, aDir))
|
||||
return PR_TRUE;
|
||||
}
|
||||
@ -1283,16 +1289,16 @@ nsXULPopupManager::HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
|
||||
nsMenuFrame* nextItem;
|
||||
|
||||
if (aDir == eNavigationDirection_Before)
|
||||
nextItem = GetPreviousMenuItem(popupFrame, currentMenu, PR_TRUE);
|
||||
nextItem = GetPreviousMenuItem(aFrame, currentMenu, PR_TRUE);
|
||||
else if (aDir == eNavigationDirection_After)
|
||||
nextItem = GetNextMenuItem(popupFrame, currentMenu, PR_TRUE);
|
||||
nextItem = GetNextMenuItem(aFrame, currentMenu, PR_TRUE);
|
||||
else if (aDir == eNavigationDirection_First)
|
||||
nextItem = GetNextMenuItem(popupFrame, nsnull, PR_TRUE);
|
||||
nextItem = GetNextMenuItem(aFrame, nsnull, PR_TRUE);
|
||||
else
|
||||
nextItem = GetPreviousMenuItem(popupFrame, nsnull, PR_TRUE);
|
||||
nextItem = GetPreviousMenuItem(aFrame, nsnull, PR_TRUE);
|
||||
|
||||
if (nextItem) {
|
||||
popupFrame->ChangeMenuItem(nextItem, PR_FALSE);
|
||||
aFrame->ChangeMenuItem(nextItem, PR_FALSE);
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
@ -1538,7 +1544,7 @@ nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent)
|
||||
}
|
||||
#endif // !XP_MAC && !XP_MACOSX
|
||||
else {
|
||||
HandleShortcutNavigation(keyEvent);
|
||||
HandleShortcutNavigation(keyEvent, nsnull);
|
||||
}
|
||||
|
||||
aKeyEvent->StopPropagation();
|
||||
|
@ -48,6 +48,7 @@ _TEST_FILES = test_bug360220.xul \
|
||||
test_bug359754.xul \
|
||||
test_bug365773.xul \
|
||||
test_colorpicker_popup.xul \
|
||||
test_menulist_keynav.xul \
|
||||
test_popup_coords.xul \
|
||||
test_popup_recreate.xul \
|
||||
test_progressmeter.xul \
|
||||
|
86
toolkit/content/tests/widgets/test_menulist_keynav.xul
Normal file
86
toolkit/content/tests/widgets/test_menulist_keynav.xul
Normal file
@ -0,0 +1,86 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Menulist Key Navigation Tests"
|
||||
onload="setTimeout(runTests, 0);"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<title>Menulist Key Navigation Tests</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<menulist id="list">
|
||||
<menupopup>
|
||||
<menuitem id="i1" label="One"/>
|
||||
<menuitem id="i2" label="Two"/>
|
||||
<menuitem id="i2b" disabled="true" label="Two and a Half"/>
|
||||
<menuitem id="i3" label="Three"/>
|
||||
<menuitem id="i4" label="Four"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests()
|
||||
{
|
||||
var list = $("list");
|
||||
list.focus();
|
||||
// on Mac, up and cursor keys open the menu, but on other platforms, the
|
||||
// cursor keys navigate between items without opening the menu
|
||||
if (navigator.platform.indexOf("Mac") == -1) {
|
||||
keyCheck(list, "VK_DOWN", 2, "cursor down");
|
||||
keyCheck(list, "VK_DOWN", 3, "cursor down skip disabled");
|
||||
keyCheck(list, "VK_UP", 2, "cursor up skip disabled");
|
||||
keyCheck(list, "VK_UP", 1, "cursor up");
|
||||
keyCheck(list, "VK_UP", 4, "cursor up wrap");
|
||||
keyCheck(list, "VK_DOWN", 1, "cursor down wrap");
|
||||
}
|
||||
|
||||
synthesizeKey("G", { });
|
||||
is(list.selectedItem, $("i1"), "letter pressed not found selectedItem");
|
||||
|
||||
keyCheck(list, "T", 2, "letter pressed");
|
||||
keyCheck(list, "T", 2, "letter pressed");
|
||||
setTimeout(pressedAgain, 1200);
|
||||
}
|
||||
|
||||
function pressedAgain()
|
||||
{
|
||||
var list = $("list");
|
||||
keyCheck(list, "T", 3, "letter pressed again");
|
||||
keyCheck(list, "W", 2, "second letter pressed");
|
||||
setTimeout(differentPressed, 1200);
|
||||
}
|
||||
|
||||
function differentPressed()
|
||||
{
|
||||
keyCheck($("list"), "O", 1, "different letter pressed");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function keyCheck(list, key, index, testname)
|
||||
{
|
||||
var item = $("i" + index);
|
||||
synthesizeKeyExpectEvent(key, { }, item, "command", testname);
|
||||
is(list.selectedItem, item, testname + " selectedItem");
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</window>
|
Loading…
Reference in New Issue
Block a user