Bug 387499, 387985, key navigation and shortcuts not working when menulist is closed, r=neil,sr=bz

This commit is contained in:
enndeakin@sympatico.ca 2007-07-23 10:08:10 -07:00
parent 7f1f88be47
commit 3fe172d56a
5 changed files with 154 additions and 24 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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 \

View 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>