Bug 250091, support page up and page down in menulists, r=neil

This commit is contained in:
Neil Deakin 2015-05-20 13:11:06 -04:00
parent 9399983176
commit 0bf9e896e5
5 changed files with 224 additions and 0 deletions

View File

@ -1635,6 +1635,87 @@ void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsMenuFrame* aMenuItem)
} }
} }
void nsMenuPopupFrame::ChangeByPage(bool aIsUp)
{
nsIFrame* parentMenu = GetParent();
if (parentMenu) {
// Only scroll by page within menulists.
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(parentMenu->GetContent());
if (!menulist) {
return;
}
}
nsMenuFrame* newMenu = nullptr;
nsIFrame* currentMenu = mCurrentMenu;
if (!currentMenu) {
// If there is no current menu item, get the first item. When moving up,
// just use this as the newMenu and leave currentMenu null so that no
// check for a later element is performed. When moving down, set currentMenu
// so that we look for one page down from the first item.
newMenu = nsXULPopupManager::GetNextMenuItem(this, nullptr, true);
if (!aIsUp) {
currentMenu = newMenu;
}
}
if (currentMenu) {
nscoord scrollHeight = mRect.height;
nsIScrollableFrame *scrollframe = GetScrollFrame(this);
if (scrollframe) {
scrollHeight = scrollframe->GetScrollPortRect().height;
}
// Get the position of the current item and add or subtract one popup's
// height to or from it.
nscoord targetPosition = aIsUp ? currentMenu->GetRect().YMost() - scrollHeight :
currentMenu->GetRect().y + scrollHeight;
// Indicates that the last visible child was a valid menuitem.
bool lastWasValid = false;
// Look for the next child which is just past the target position. This child
// will need to be selected.
while (currentMenu) {
// Only consider menu frames.
nsMenuFrame* menuFrame = do_QueryFrame(currentMenu);
if (menuFrame &&
nsXULPopupManager::IsValidMenuItem(PresContext(), menuFrame->GetContent(), true)) {
// If the right position was found, break out. Otherwise, look for another item.
if ((!aIsUp && currentMenu->GetRect().YMost() > targetPosition) ||
(aIsUp && currentMenu->GetRect().y < targetPosition)) {
// If the last visible child was not a valid menuitem or was disabled,
// use this as the menu to select, skipping over any non-valid items at
// the edge of the page.
if (!lastWasValid) {
newMenu = menuFrame;
}
break;
}
// Assign this item to newMenu. This item will be selected in case we
// don't find any more.
lastWasValid = true;
newMenu = menuFrame;
}
else {
lastWasValid = false;
}
currentMenu = aIsUp ? currentMenu->GetPrevSibling() :
currentMenu->GetNextSibling();
}
}
// Select the new menuitem.
if (newMenu) {
ChangeMenuItem(newMenu, false);
}
}
NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem) NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
{ {
if (mCurrentMenu == aMenuItem) if (mCurrentMenu == aMenuItem)

View File

@ -326,6 +326,8 @@ public:
void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame); void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame);
void ChangeByPage(bool aIsUp);
// Move the popup to the screen coordinate (aLeft, aTop) in CSS pixels. // Move the popup to the screen coordinate (aLeft, aTop) in CSS pixels.
// If aUpdateAttrs is true, and the popup already has left or top attributes, // If aUpdateAttrs is true, and the popup already has left or top attributes,
// then those attributes are updated to the new location. // then those attributes are updated to the new location.

View File

@ -2172,6 +2172,13 @@ nsXULPopupManager::HandleKeyboardEventWithKeyCode(
HandleKeyboardNavigation(keyCode); HandleKeyboardNavigation(keyCode);
break; break;
case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
if (aTopVisibleMenuItem) {
aTopVisibleMenuItem->Frame()->ChangeByPage(keyCode == nsIDOMKeyEvent::DOM_VK_PAGE_UP);
}
break;
case nsIDOMKeyEvent::DOM_VK_ESCAPE: case nsIDOMKeyEvent::DOM_VK_ESCAPE:
// Pressing Escape hides one level of menus only. If no menu is open, // Pressing Escape hides one level of menus only. If no menu is open,
// check if a menubar is active and inform it that a menu closed. Even // check if a menubar is active and inform it that a menu closed. Even

View File

@ -116,6 +116,7 @@ skip-if = buildapp == 'mulet'
skip-if = buildapp == 'mulet' skip-if = buildapp == 'mulet'
[test_menulist_keynav.xul] [test_menulist_keynav.xul]
[test_menulist_null_value.xul] [test_menulist_null_value.xul]
[test_menulist_paging.xul]
[test_mousecapture.xul] [test_mousecapture.xul]
skip-if = buildapp == 'mulet' skip-if = buildapp == 'mulet'
[test_mousescroll.xul] [test_mousescroll.xul]

View File

@ -0,0 +1,133 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<window title="Menulist Tests"
onload="setTimeout(startTest, 0);"
onpopupshown="menulistShown()" onpopuphidden="runTest()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<menulist id="menulist1">
<menupopup id="menulist-popup1">
<menuitem label="One"/>
<menuitem label="Two"/>
<menuitem label="Three"/>
<menuitem label="Four"/>
<menuitem label="Five"/>
<menuitem label="Six"/>
<menuitem label="Seven"/>
<menuitem label="Eight"/>
<menuitem label="Nine"/>
<menuitem label="Ten"/>
</menupopup>
</menulist>
<menulist id="menulist2">
<menupopup id="menulist-popup2">
<menuitem label="One" disabled="true"/>
<menuitem label="Two" selected="true"/>
<menuitem label="Three"/>
<menuitem label="Four"/>
<menuitem label="Five"/>
<menuitem label="Six"/>
<menuitem label="Seven"/>
<menuitem label="Eight"/>
<menuitem label="Nine"/>
<menuitem label="Ten" disabled="true"/>
</menupopup>
</menulist>
<menulist id="menulist3">
<menupopup id="menulist-popup3">
<label value="One"/>
<menuitem label="Two" selected="true"/>
<menuitem label="Three"/>
<menuitem label="Four"/>
<menuitem label="Five" disabled="true"/>
<menuitem label="Six" disabled="true"/>
<menuitem label="Seven"/>
<menuitem label="Eight"/>
<menuitem label="Nine"/>
<label value="Ten"/>
</menupopup>
</menulist>
<script class="testbody" type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
let test;
// Windows allows disabled items to be selected.
let isWindows = navigator.platform.indexOf("Win") >= 0;
let tests = [
{ list: "menulist1", initial: 0, downs: [3, 6, 9, 9],
ups: [6, 3, 0, 0] },
{ list: "menulist2", initial: 1, downs: [4, 7, isWindows ? 9 : 8, isWindows ? 9 : 8],
ups: [isWindows ? 6 : 5, isWindows ? 3 : 2, isWindows ? 0 : 1] },
{ list: "menulist3", initial: 1, downs: [isWindows ? 4 : 6, isWindows ? 7 : 8, 8],
ups: [isWindows ? 5 : 3, isWindows ? 2 : 1, 1] }
];
function startTest()
{
let popup = document.getElementById("menulist-popup1");
let menupopupHeight = popup.getBoundingClientRect().height;
let menuitemHeight = popup.firstChild.getBoundingClientRect().height;
// First, set the height of each popup to the height of four menuitems plus
// any padding and border on the menupopup.
let height = menuitemHeight * 4 + (menupopupHeight - menuitemHeight * 10);
popup.height = height;
document.getElementById("menulist-popup2").height = height;
document.getElementById("menulist-popup3").height = height;
runTest();
}
function runTest()
{
if (!tests.length) {
SimpleTest.finish();
return;
}
test = tests.shift();
document.getElementById(test.list).open = true;
}
function menulistShown()
{
let menulist = document.getElementById(test.list);
is(menulist.menuBoxObject.activeChild.label, menulist.getItemAtIndex(test.initial).label, test.list + " initial selection");
for (let i = 0; i < test.downs.length; i++) {
sendKey("PAGE_DOWN");
is(menulist.menuBoxObject.activeChild.label, menulist.getItemAtIndex(test.downs[i]).label, test.list + " page down " + i);
}
for (let i = 0; i < test.ups.length; i++) {
sendKey("PAGE_UP");
is(menulist.menuBoxObject.activeChild.label, menulist.getItemAtIndex(test.ups[i]).label, test.list + " page up " + i);
}
menulist.open = false;
}
]]>
</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>