Bug 955087 - Improve keyboard accessibility of the contact list, r=florian.
This commit is contained in:
Родитель
f1dac03acb
Коммит
9293563b86
|
@ -82,8 +82,13 @@ tooltip[type="buddy"] {
|
|||
-moz-binding: url("chrome://instantbird/content/buddytooltip.xml#tooltip");
|
||||
}
|
||||
|
||||
#userIcon {
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
#statusTypeIcon {
|
||||
cursor: pointer;
|
||||
-moz-user-focus: ignore;
|
||||
}
|
||||
|
||||
#displayName,
|
||||
|
@ -139,6 +144,10 @@ conv {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#buddyListMsg[listedConvCount="0"] > #convlistbox {
|
||||
-moz-user-focus: ignore;
|
||||
}
|
||||
|
||||
/* Make the notification bar work with narrow windows. */
|
||||
.notification-inner > hbox {
|
||||
display: inline-block;
|
||||
|
|
|
@ -475,7 +475,8 @@ var buddyList = {
|
|||
let elt = document.getElementById("statusMessage");
|
||||
if (!elt.hasAttribute("editing")) {
|
||||
elt.setAttribute("editing", "true");
|
||||
elt.addEventListener("keypress", this.statusMessageKeyPress);
|
||||
elt.removeAttribute("role");
|
||||
elt.removeAttribute("aria-haspopup");
|
||||
elt.addEventListener("blur", this.statusMessageBlur);
|
||||
if (elt.hasAttribute("usingDefault")) {
|
||||
if ("_statusTypeBeforeEditing" in this &&
|
||||
|
@ -509,6 +510,23 @@ var buddyList = {
|
|||
},
|
||||
|
||||
statusMessageKeyPress: function bl_statusMessageKeyPress(aEvent) {
|
||||
let editing = document.getElementById("statusMessage").hasAttribute("editing");
|
||||
if (!editing) {
|
||||
switch (aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_DOWN:
|
||||
buddyList.openStatusTypePopup();
|
||||
aEvent.preventDefault();
|
||||
return;
|
||||
|
||||
case aEvent.DOM_VK_TAB:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (aEvent.charCode == aEvent.DOM_VK_SPACE)
|
||||
buddyList.statusMessageClick();
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
|
@ -519,6 +537,14 @@ var buddyList = {
|
|||
buddyList.finishEditStatusMessage(false);
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_TAB:
|
||||
if (aEvent.shiftKey)
|
||||
break;
|
||||
// Ensure some item is selected when navigating by keyboard.
|
||||
if (!this.selectFirstItem("convlistbox"))
|
||||
this.selectFirstItem("buddylistbox");
|
||||
break;
|
||||
|
||||
default:
|
||||
buddyList.statusMessageRefreshTimer();
|
||||
}
|
||||
|
@ -556,7 +582,8 @@ var buddyList = {
|
|||
elt.setAttribute("value", elt.getAttribute("usingDefault"));
|
||||
TextboxSpellChecker.unregisterTextbox(elt);
|
||||
elt.removeAttribute("editing");
|
||||
elt.removeEventListener("keypress", this.statusMessageKeyPress, false);
|
||||
elt.setAttribute("role", "button");
|
||||
elt.setAttribute("aria-haspopup", "true");
|
||||
elt.removeEventListener("blur", this.statusMessageBlur, false);
|
||||
if (!elt.getAttribute("focused"))
|
||||
return;
|
||||
|
@ -565,6 +592,39 @@ var buddyList = {
|
|||
elt.focus();
|
||||
},
|
||||
|
||||
openStatusTypePopup: function() {
|
||||
let button = document.getElementById("statusTypeIcon");
|
||||
document.getElementById("setStatusTypeMenupopup").openPopup(button, "after_start");
|
||||
},
|
||||
|
||||
onStatusTypePopupShown: function() {
|
||||
// Without this, the #userIcon gains focus when the popup is opened
|
||||
// from the #statusMessage whenever the #statusMessage has been edited
|
||||
// at least once (thus changing the binding).
|
||||
document.getElementById("statusMessage").focus();
|
||||
},
|
||||
|
||||
userIconKeyPress: function bl_userIconKeyPress(aEvent) {
|
||||
switch (aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
this.userIconClick();
|
||||
break;
|
||||
|
||||
case aEvent.DOM_VK_TAB:
|
||||
if (!aEvent.shiftKey)
|
||||
break;
|
||||
// Ensure a contact is selected when navigating by keyboard.
|
||||
this.selectFirstItem("buddylistbox");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (aEvent.charCode == aEvent.DOM_VK_SPACE)
|
||||
this.userIconClick();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
userIconClick: function bl_userIconClick() {
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
let fp = Components.classes["@mozilla.org/filepicker;1"]
|
||||
|
@ -581,9 +641,9 @@ var buddyList = {
|
|||
let elt = document.getElementById("displayName");
|
||||
if (!elt.hasAttribute("editing")) {
|
||||
elt.setAttribute("editing", "true");
|
||||
elt.removeAttribute("role");
|
||||
if (elt.hasAttribute("usingDefault"))
|
||||
elt.removeAttribute("value");
|
||||
elt.addEventListener("keypress", this.displayNameKeyPress);
|
||||
elt.addEventListener("blur", this.displayNameBlur);
|
||||
// force binding attachment by forcing layout
|
||||
elt.getBoundingClientRect();
|
||||
|
@ -607,6 +667,12 @@ var buddyList = {
|
|||
},
|
||||
|
||||
displayNameKeyPress: function bl_displayNameKeyPress(aEvent) {
|
||||
let editing = document.getElementById("displayName").hasAttribute("editing");
|
||||
if (!editing) {
|
||||
if (aEvent.charCode == aEvent.DOM_VK_SPACE)
|
||||
buddyList.displayNameClick();
|
||||
return;
|
||||
}
|
||||
switch (aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
|
@ -632,7 +698,7 @@ var buddyList = {
|
|||
elt.setAttribute("value", elt.getAttribute("usingDefault"));
|
||||
|
||||
elt.removeAttribute("editing");
|
||||
elt.removeEventListener("keypress", this.displayNameKeyPress, false);
|
||||
elt.setAttribute("role", "button");
|
||||
elt.removeEventListener("blur", this.displayNameBlur, false);
|
||||
if (!elt.getAttribute("focused"))
|
||||
return;
|
||||
|
@ -776,11 +842,28 @@ var buddyList = {
|
|||
Services.prefs.removeObserver(showOfflineBuddiesPref, buddyList);
|
||||
},
|
||||
|
||||
selectFirstItem: function (aListboxID) {
|
||||
let listbox = document.getElementById(aListboxID);
|
||||
if (!listbox.itemCount)
|
||||
return false;
|
||||
if (listbox.selectedIndex == -1)
|
||||
listbox.selectedIndex = 0;
|
||||
return true;
|
||||
},
|
||||
|
||||
// Handle key pressing
|
||||
keyPress: function bl_keyPress(aEvent) {
|
||||
let target = aEvent.target;
|
||||
while (target && target.localName != "richlistbox")
|
||||
target = target.parentNode;
|
||||
if (aEvent.keyCode == aEvent.DOM_VK_TAB) {
|
||||
// Ensure some item is selected when navigating by keyboard.
|
||||
if (target.id == "convlistbox" && !aEvent.shiftKey)
|
||||
this.selectFirstItem("buddylistbox");
|
||||
if (target.id == "buddylistbox" && aEvent.shiftKey)
|
||||
this.selectFirstItem("convlistbox");
|
||||
return;
|
||||
}
|
||||
var item = target.selectedItem;
|
||||
if (!item || !item.parentNode) // empty list or item no longer in the list
|
||||
return;
|
||||
|
|
|
@ -119,9 +119,13 @@
|
|||
<toolbox id="mainToolbox">
|
||||
<toolbar id="statusArea">
|
||||
<stack id="statusImageStack">
|
||||
<image id="userIcon" onclick="buddyList.userIconClick();"/>
|
||||
<image id="userIcon" role="button"
|
||||
aria-label="&userIcon.label;" tooltiptext="&userIcon.label;"
|
||||
onclick="buddyList.userIconClick();"
|
||||
onkeypress="buddyList.userIconKeyPress(event);"/>
|
||||
<button type="menu" id="statusTypeIcon" status="available">
|
||||
<menupopup id="setStatusTypeMenupopup"
|
||||
onpopupshown="buddyList.onStatusTypePopupShown();"
|
||||
oncommand="buddyList.editStatus(event);">
|
||||
<menuitem id="statusTypeAvailable" label="&available;"
|
||||
status="available" class="menuitem-iconic"/>
|
||||
|
@ -135,10 +139,14 @@
|
|||
</stack>
|
||||
<stack id="displayNameAndstatusMessageStack" flex="1">
|
||||
<vbox flex="1" pack="center">
|
||||
<label id="displayName" onclick="buddyList.displayNameClick();"/>
|
||||
<label id="displayName" role="button"
|
||||
onclick="buddyList.displayNameClick();"
|
||||
onkeypress="buddyList.displayNameKeyPress(event);"/>
|
||||
</vbox>
|
||||
<label id="statusMessage" crop="end" value=""
|
||||
onclick="buddyList.statusMessageClick();"/>
|
||||
aria-haspopup="true" role="button"
|
||||
onclick="buddyList.statusMessageClick();"
|
||||
onkeypress="buddyList.statusMessageKeyPress(event);"/>
|
||||
</stack>
|
||||
</toolbar>
|
||||
</toolbox>
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
<!ENTITY copyEmailCmd.accesskey "E">
|
||||
<!ENTITY engineManagerCmd.label "Manage Search Engines…">
|
||||
|
||||
<!ENTITY userIcon.label "Change your icon">
|
||||
<!ENTITY contactsHeader.label "Contacts">
|
||||
<!ENTITY convsHeader.label "Conversations on hold">
|
||||
|
||||
|
|
|
@ -341,6 +341,7 @@ group[closed] .twisty {
|
|||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#userIcon:focus,
|
||||
#userIcon:hover {
|
||||
border-color: rgba(0,0,0,0.35);
|
||||
background-color: rgba(0,0,0,0.35);
|
||||
|
@ -431,3 +432,26 @@ group[closed] .twisty {
|
|||
margin-bottom: 18px;
|
||||
}
|
||||
%endif
|
||||
|
||||
#statusMessage:-moz-focusring:not([editing]) {
|
||||
%ifdef XP_MACOSX
|
||||
margin: 30px -2px -1px -22px;
|
||||
border: 2px solid rgba(0,0,0,0.15);
|
||||
border-radius: 5px;
|
||||
%else
|
||||
margin: 31px -1px -1px -21px;
|
||||
border: dotted 1px -moz-dialogtext;
|
||||
%endif
|
||||
padding-left: 20px; /* 16px for the statusTypeIcon and 4px from the margins on the two stacks */
|
||||
}
|
||||
|
||||
#displayName:-moz-focusring:not([editing]) {
|
||||
%ifdef XP_MACOSX
|
||||
margin: -1px -1px 16px -2px;
|
||||
border: 2px solid rgba(0,0,0,0.15);
|
||||
border-radius: 5px;
|
||||
%else
|
||||
margin: -1px -1px 17px;
|
||||
border: dotted 1px -moz-dialogtext;
|
||||
%endif
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче