Bug 1124904 - cleanup keyboard navigation in the new search panel - simplify the 'selected' attribute handling, r=Mossop.

This commit is contained in:
Florian Quèze 2015-02-05 00:08:19 +01:00
parent d33c74fc36
commit d59bfba6e6
3 changed files with 55 additions and 75 deletions

View File

@ -1137,6 +1137,7 @@
"search-panel-one-offs")
while (list.firstChild)
list.firstChild.remove();
textbox._selectedButton = null;
let hiddenList;
try {

View File

@ -509,7 +509,7 @@
let type = "unknown";
if (aEvent instanceof KeyboardEvent) {
type = "key";
if (this._textbox.getSelectedOneOff()) {
if (this._textbox.selectedButton) {
source = "oneoff";
}
} else if (aEvent instanceof MouseEvent) {
@ -989,7 +989,7 @@
var evt = aEvent || this.mEnterEvent;
let engine;
let oneOff = this.getSelectedOneOff();
let oneOff = this.selectedButton;
if (oneOff)
engine = oneOff.engine;
if (this.mEnterEvent && this._selectionDetails &&
@ -1003,22 +1003,22 @@
]]></body>
</method>
<method name="getSelectedOneOff">
<body><![CDATA[
let list = document.getAnonymousElementByAttribute(this.popup, "anonid",
"search-panel-one-offs");
if (!list)
return null;
<field name="_selectedButton"/>
<property name="selectedButton" onget="return this._selectedButton;">
<setter><![CDATA[
if (this._selectedButton)
this._selectedButton.removeAttribute("selected");
for (let button = list.firstChild; button; button = button.nextSibling) {
if (button.hasAttribute("selected"))
return button;
// Avoid selecting dummy buttons.
if (val && !val.classList.contains("dummy")) {
val.setAttribute("selected", "true");
this._selectedButton = val;
return;
}
return null;
]]></body>
</method>
this._selectedButton = null;
]]></setter>
</property>
<method name="handleKeyboardNavigation">
<parameter name="aEvent"/>
@ -1054,7 +1054,7 @@
#endif
return;
let selectedButton = this.getSelectedOneOff();
let selectedButton = this.selectedButton;
// Alt + up/down is very similar to (shift +) tab but differs in that
// it loops through the list, whereas tab will move the focus out.
@ -1064,29 +1064,23 @@
let forward = aEvent.keyCode == KeyEvent.DOM_VK_DOWN;
if (selectedButton) {
// cycle though the list of one-off buttons.
selectedButton.removeAttribute("selected");
if (forward)
selectedButton = selectedButton.nextSibling;
this.selectedButton = selectedButton.nextSibling;
else
selectedButton = selectedButton.previousSibling;
// Avoid selecting dummy buttons.
if (selectedButton && selectedButton.classList.contains("dummy"))
selectedButton = null;
this.selectedButton = selectedButton.previousSibling;
}
else {
// If no selection, select the first or last one-off button.
if (forward) {
selectedButton = list.firstChild;
this.selectedButton = list.firstChild;
}
else {
selectedButton = list.lastChild;
while (selectedButton.classList.contains("dummy"))
selectedButton = selectedButton.previousSibling;
this.selectedButton = selectedButton;
}
}
if (selectedButton)
selectedButton.setAttribute("selected", "true");
aEvent.preventDefault();
aEvent.stopPropagation();
@ -1096,11 +1090,7 @@
// If the last suggestion is selected, DOWN selects the first one-off.
if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN &&
popup.selectedIndex + 1 == popup.view.rowCount) {
if (selectedButton)
selectedButton.removeAttribute("selected");
selectedButton = list.firstChild;
if (selectedButton)
selectedButton.setAttribute("selected", "true");
this.selectedButton = list.firstChild;
}
// If no suggestion is selected and a one-off is selected,
@ -1108,17 +1098,12 @@
if (popup.selectedIndex == -1 && selectedButton &&
(aEvent.keyCode == KeyEvent.DOM_VK_DOWN ||
aEvent.keyCode == KeyEvent.DOM_VK_UP)) {
selectedButton.removeAttribute("selected");
if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN)
selectedButton = selectedButton.nextSibling;
this.selectedButton = selectedButton.nextSibling;
else
selectedButton = selectedButton.previousSibling;
if (selectedButton && selectedButton.classList.contains("dummy"))
selectedButton = null;
if (selectedButton) {
selectedButton.setAttribute("selected", "true");
this.selectedButton = selectedButton.previousSibling;
if (this.selectedButton) {
aEvent.preventDefault();
aEvent.stopPropagation();
}
@ -1139,7 +1124,7 @@
selectedButton = list.lastChild;
while (selectedButton.classList.contains("dummy"))
selectedButton = selectedButton.previousSibling;
selectedButton.setAttribute("selected", "true");
this.selectedButton = selectedButton;
aEvent.preventDefault();
aEvent.stopPropagation();
@ -1148,18 +1133,13 @@
if (aEvent.keyCode == KeyEvent.DOM_VK_TAB) {
if (selectedButton) {
// TAB cycles though the list of one-off buttons.
selectedButton.removeAttribute("selected");
if (aEvent.shiftKey)
selectedButton = selectedButton.previousSibling;
this.selectedButton = selectedButton.previousSibling;
else
selectedButton = selectedButton.nextSibling;
// Avoid selecting dummy buttons.
if (selectedButton && selectedButton.classList.contains("dummy"))
selectedButton = null;
this.selectedButton = selectedButton.nextSibling;
// If we are out of the list, revert the text field to what the user typed.
if (!selectedButton) {
if (!this.selectedButton) {
// Set the selectedIndex to something that will make
// handleKeyNavigation (called by autocomplete.xml's onKeyPress
// method) reset the text field value to what the user typed.
@ -1173,9 +1153,8 @@
return;
// and select the first one-off button for <tab>.
selectedButton = list.firstChild;
this.selectedButton = list.firstChild;
}
selectedButton.setAttribute("selected", "true");
aEvent.preventDefault();
aEvent.stopPropagation();

View File

@ -77,7 +77,7 @@ add_task(function* test_arrows() {
let oneOffs = getOneOffs();
ok(oneOffs.length >= 4, "we have at least 4 one-off buttons displayed")
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
// The down arrow should first go through the suggestions.
for (let i = 0; i < kValues.length; ++i) {
@ -97,27 +97,27 @@ add_task(function* test_arrows() {
// now cycle through the one-off items, the first one is already selected.
for (let i = 0; i < oneOffs.length; ++i) {
is(textbox.getSelectedOneOff(), oneOffs[i],
is(textbox.selectedButton, oneOffs[i],
"the one-off button #" + (i + 1) + " should be selected");
EventUtils.synthesizeKey("VK_DOWN", {});
}
// We should now be back to the initial situation.
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
info("now test the up arrow key");
// cycle through the one-off items, the first one is already selected.
for (let i = oneOffs.length; i; --i) {
EventUtils.synthesizeKey("VK_UP", {});
is(textbox.getSelectedOneOff(), oneOffs[i - 1],
is(textbox.selectedButton, oneOffs[i - 1],
"the one-off button #" + i + " should be selected");
}
// Another press on up should clear the one-off selection and select the
// last suggestion.
EventUtils.synthesizeKey("VK_UP", {});
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
for (let i = kValues.length - 1; i >= 0; --i) {
is(searchPopup.selectedIndex, i,
@ -137,13 +137,13 @@ add_task(function* test_tab() {
"the search bar should be focused"); // from the previous test.
let oneOffs = getOneOffs();
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
// Pressing tab should select the first one-off without selecting suggestions.
// now cycle through the one-off items, the first one is already selected.
for (let i = 0; i < oneOffs.length; ++i) {
EventUtils.synthesizeKey("VK_TAB", {});
is(textbox.getSelectedOneOff(), oneOffs[i],
is(textbox.selectedButton, oneOffs[i],
"the one-off button #" + (i + 1) + " should be selected");
}
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
@ -168,14 +168,14 @@ add_task(function* test_shift_tab() {
yield promise;
let oneOffs = getOneOffs();
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
// Press up once to select the last one-off button.
EventUtils.synthesizeKey("VK_UP", {});
// Pressing shift+tab should cycle through the one-off items.
for (let i = oneOffs.length - 1; i >= 0; --i) {
is(textbox.getSelectedOneOff(), oneOffs[i],
is(textbox.selectedButton, oneOffs[i],
"the one-off button #" + (i + 1) + " should be selected");
if (i)
EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
@ -211,7 +211,7 @@ add_task(function* test_alt_down() {
yield promise;
// ... and does nothing else.
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
is(textbox.value, kUserValue, "the textfield value should be unmodified");
@ -220,18 +220,18 @@ add_task(function* test_alt_down() {
let oneOffs = getOneOffs();
for (let i = 0; i < oneOffs.length; ++i) {
EventUtils.synthesizeKey("VK_DOWN", {altKey: true});
is(textbox.getSelectedOneOff(), oneOffs[i],
is(textbox.selectedButton, oneOffs[i],
"the one-off button #" + (i + 1) + " should be selected");
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
}
// One more alt+down keypress and nothing should be selected.
EventUtils.synthesizeKey("VK_DOWN", {altKey: true});
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
// another one and the first one-off should be selected.
EventUtils.synthesizeKey("VK_DOWN", {altKey: true});
is(textbox.getSelectedOneOff(), oneOffs[0],
is(textbox.selectedButton, oneOffs[0],
"the first one-off button should be selected");
});
@ -247,7 +247,7 @@ add_task(function* test_alt_up() {
yield promise;
// ... and does nothing else.
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
is(textbox.value, kUserValue, "the textfield value should be unmodified");
@ -256,28 +256,28 @@ add_task(function* test_alt_up() {
let oneOffs = getOneOffs();
for (let i = oneOffs.length - 1; i >= 0; --i) {
EventUtils.synthesizeKey("VK_UP", {altKey: true});
is(textbox.getSelectedOneOff(), oneOffs[i],
is(textbox.selectedButton, oneOffs[i],
"the one-off button #" + (i + 1) + " should be selected");
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
}
// One more alt+down keypress and nothing should be selected.
EventUtils.synthesizeKey("VK_UP", {altKey: true});
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
// another one and the last one-off should be selected.
EventUtils.synthesizeKey("VK_UP", {altKey: true});
is(textbox.getSelectedOneOff(), oneOffs[oneOffs.length - 1],
is(textbox.selectedButton, oneOffs[oneOffs.length - 1],
"the last one-off button should be selected");
// Cleanup for the next test.
EventUtils.synthesizeKey("VK_DOWN", {});
ok(!textbox.getSelectedOneOff(), "no one-off should be selected anymore");
ok(!textbox.selectedButton, "no one-off should be selected anymore");
});
add_task(function* test_tab_and_arrows() {
// Check the initial state is as expected.
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
is(textbox.value, kUserValue, "the textfield value should be unmodified");
@ -285,27 +285,27 @@ add_task(function* test_tab_and_arrows() {
EventUtils.synthesizeKey("VK_DOWN", {});
is(searchPopup.selectedIndex, 0, "first suggestion should be selected");
is(textbox.value, kValues[0], "the textfield value should have changed");
ok(!textbox.getSelectedOneOff(), "no one-off button should be selected");
ok(!textbox.selectedButton, "no one-off button should be selected");
// After pressing tab, the first one-off should be selected,
// and the first suggestion still selected.
let oneOffs = getOneOffs();
EventUtils.synthesizeKey("VK_TAB", {});
is(textbox.getSelectedOneOff(), oneOffs[0],
is(textbox.selectedButton, oneOffs[0],
"the first one-off button should be selected");
is(searchPopup.selectedIndex, 0, "first suggestion should still be selected");
// After pressing down, the second suggestion should be selected,
// and the first one-off still selected.
EventUtils.synthesizeKey("VK_DOWN", {});
is(textbox.getSelectedOneOff(), oneOffs[0],
is(textbox.selectedButton, oneOffs[0],
"the first one-off button should still be selected");
is(searchPopup.selectedIndex, 1, "second suggestion should be selected");
// After pressing up, the first suggestion should be selected again,
// and the first one-off still selected.
EventUtils.synthesizeKey("VK_UP", {});
is(textbox.getSelectedOneOff(), oneOffs[0],
is(textbox.selectedButton, oneOffs[0],
"the first one-off button should still be selected");
is(searchPopup.selectedIndex, 0, "second suggestion should be selected again");
@ -316,12 +316,12 @@ add_task(function* test_tab_and_arrows() {
is(searchPopup.selectedIndex, -1, "no suggestion should be selected");
is(textbox.value, kUserValue,
"the textfield value should be back to user typed value");
is(textbox.getSelectedOneOff(), oneOffs[0],
is(textbox.selectedButton, oneOffs[0],
"the first one-off button should still be selected");
// Now pressing down should select the second one-off.
EventUtils.synthesizeKey("VK_DOWN", {});
is(textbox.getSelectedOneOff(), oneOffs[1],
is(textbox.selectedButton, oneOffs[1],
"the second one-off button should be selected");
is(searchPopup.selectedIndex, -1, "there should still be no selected suggestion");