Bug 298371, make richlistbox multi-selectable p=surkov, r=mano+enndeakin

This commit is contained in:
Olli.Pettay%helsinki.fi 2007-01-16 17:09:58 +00:00
Родитель 4b3f356119
Коммит 107a292195
4 изменённых файлов: 1023 добавлений и 854 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -22,6 +22,7 @@
-
- Contributor(s):
- Doron Rosenberg <doronr@us.ibm.com> (Original Author)
- Simon Bünzli <zeniko@gmail.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
@ -42,7 +43,12 @@
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="richlistbox">
<binding id="richlistbox"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<resources>
<stylesheet src="chrome://global/skin/richlistbox.css"/>
</resources>
<content>
<xul:scrollbox allowevents="true" orient="vertical" anonid="main-box"
flex="1" style="overflow: auto;">
@ -50,11 +56,7 @@
</xul:scrollbox>
</content>
<resources>
<stylesheet src="chrome://global/skin/richlistbox.css"/>
</resources>
<implementation implements="nsIAccessibleProvider, nsIDOMXULSelectControlElement">
<implementation>
<field name="scrollBoxObject">null</field>
<constructor>
<![CDATA[
@ -74,187 +76,155 @@
// remove the template build listener
if (this.builder)
this.builder.removeListener(this._builderListener);
this._selectedItem = null;
]]>
</destructor>
<property name="accessibleType" readonly="true">
<getter>
<![CDATA[
return Components.interfaces.nsIAccessibleProvider.XULListbox;
]]>
</getter>
</property>
<property name="children">
<getter>
<![CDATA[
var childNodes = [];
for (var i = 0; i < this.childNodes.length; ++i) {
if (this.childNodes[i] instanceof Components.interfaces.nsIDOMXULSelectControlItemElement)
childNodes.push(this.childNodes[i]);
}
return childNodes;
]]>
</getter>
</property>
<field name="_builderListener">
<![CDATA[
({
mOuter: this,
item: null,
willRebuild : function(builder) {},
didRebuild : function(builder) {
this.mOuter._refreshSelection();
}
});
]]>
</field>
<method name="_refreshSelection">
<!-- Overriding baselistbox -->
<method name="_fireOnSelect">
<body>
<![CDATA[
// when this method is called, we know that either the selectedItem
// we have is null (ctor) or a reference to an element no longer in
// the DOM (template).
// fist look for a last-selected attribute
var lastSelected = this.getAttribute("last-selected");
if (lastSelected != "") {
var element = document.getElementById(lastSelected);
if (element) {
this.selectedItem = element;
if (!this._isItemVisible(this.selectedItem))
this.scrollBoxObject.scrollToElement(this.selectedItem);
return;
}
}
// cache the selected index
var selectedIndex = this._selectedIndex;
// refreshes selection. Called for example when a template rebuild
// happens.
if (this.selectedItem) {
if (this.selectedItem.hasAttribute("id")) {
var id = this.selectedItem.getAttribute("id");
var item = document.getElementById(id);
// if we find no item, clear selection so that the code at the bottom
// takes over
if (item) {
this.selectedItem = item;
} else {
this.clearSelection();
}
} else {
// if no id, we clear selection so that the below code will select
// based on the current index
this.clearSelection();
}
}
// if we have no previously selected item or the above if check fails to
// find the previous node (which causes it to clear selection)
if (!this.selectedItem) {
// if the selectedIndex is larger than the row count, select the last
// item.
if (selectedIndex >= this.getRowCount())
this.selectedIndex = this.getRowCount() - 1;
else
this.selectedIndex = selectedIndex;
// XXX: downloadmanager needs the following line, else we scroll to
// the middle on inital load.
this.ensureSelectedElementIsVisible();
}
]]>
</body>
</method>
<method name="fireActiveItemEvent">
<body>
<![CDATA[
if (this.selectedItem) {
var event = document.createEvent("Events");
event.initEvent("DOMMenuItemActive", true, true);
this.selectedItem.dispatchEvent(event);
}
return false;
]]>
</body>
</method>
<field name="_selectedIndex">0</field>
<property name="selectedIndex">
<getter>
<![CDATA[
return this.getIndexOf(this.selectedItem);
]]>
</getter>
<setter>
<![CDATA[
if (val == -1) {
// clear selection
this.clearSelection();
} else if (val >= 0) {
// only set if we get an item returned
var item = this.getItemAtIndex(val);
if (item)
this.selectedItem = item;
}
]]>
</setter>
</property>
<field name="_selectedItem">null</field>
<property name="selectedItem">
<getter>
return this._selectedItem;
</getter>
<setter>
<![CDATA[
if (this._selectedItem == val)
// make sure not to modify last-selected when suppressing select events
// (otherwise we'll lose the selection when a template gets rebuilt)
if (this._suppressOnSelect || this.suppressOnSelect)
return;
this._setItemSelection(val);
// remember the current item and all selected items with IDs
var state = this.currentItem ? this.currentItem.id : "";
if (this.selType == "multiple" && this.selectedCount) {
function getId(aItem) { return aItem.id; }
state += " " + this.selectedItems.filter(getId).map(getId).join(" ");
}
if (state)
this.setAttribute("last-selected", state);
else
this.removeAttribute("last-selected");
if (val)
this.fireActiveItemEvent();
// preserve the index just in case no IDs are available
if (this.currentIndex > -1)
this._currentIndex = this.currentIndex + 1;
this._fireOnSelect();
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
// always call this (allows a commandupdater without controller)
document.commandDispatcher.updateCommands("richlistbox-select");
]]>
</setter>
</property>
</body>
</method>
<!-- sets selection but doesn't cause any events -->
<method name="_setItemSelection">
<method name="appendItem">
<parameter name="aLabel"/>
<parameter name="aValue"/>
<body>
return this.insertItemAt(-1, aLabel, aValue);
</body>
</method>
<method name="insertItemAt">
<parameter name="aIndex"/>
<parameter name="aLabel"/>
<parameter name="aValue"/>
<body>
const XULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var item =
this.ownerDocument.createElementNS(XULNS, "richlistitem");
item.setAttribute("value", aValue);
var label = this.ownerDocument.createElementNS(XULNS, "label");
label.setAttribute("value", aLabel);
label.setAttribute("flex", "1");
label.setAttribute("crop", "end");
item.appendChild(label);
var before = this.getItemAtIndex(aIndex);
if (!before)
this.appendChild(item);
else
this.insertBefore(item, before);
return item;
</body>
</method>
<method name="getIndexOfItem">
<parameter name="aItem"/>
<body>
<![CDATA[
// unselect current item
if (this._selectedItem)
this._selectedItem.selected = false
// don't search the children, if we're looking for none of them
if (aItem == null)
return -1;
this._selectedItem = aItem;
this._selectedIndex = this.getIndexOf(aItem);
this.ensureSelectedElementIsVisible();
if (aItem) {
aItem.selected = true;
aItem.focus();
}
return this.children.indexOf(aItem);
]]>
</body>
</method>
<method name="clearSelection">
<method name="getItemAtIndex">
<parameter name="aIndex"/>
<body>
return this.children[aIndex] || null;
</body>
</method>
<method name="ensureIndexIsVisible">
<parameter name="aIndex"/>
<body>
<![CDATA[
this.selectedItem = null;
// work around missing implementation in scrollBoxObject
return this.ensureElementIsVisible(this.getItemAtIndex(aIndex));
]]>
</body>
</method>
<method name="ensureElementIsVisible">
<parameter name="aElement"/>
<body>
<![CDATA[
if (aElement)
this.scrollBoxObject.ensureElementIsVisible(aElement);
]]>
</body>
</method>
<method name="scrollToIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
var item = this.getItemAtIndex(aIndex);
if (item)
this.scrollBoxObject.scrollToElement(item);
]]>
</body>
</method>
<method name="getNumberOfVisibleRows">
<!-- returns the number of currently visible rows -->
<!-- don't rely on this function, if the items' height can vary! -->
<body>
<![CDATA[
var children = this.children;
for (var top = 0; top < children.length && !this._isItemVisible(children[top]); top++);
for (var ix = top; ix < children.length && this._isItemVisible(children[ix]); ix++);
return ix - top;
]]>
</body>
</method>
<method name="getIndexOfFirstVisibleRow">
<body>
<![CDATA[
var children = this.children;
for (var ix = 0; ix < children.length; ix++)
if (this._isItemVisible(children[ix]))
return ix;
return -1;
]]>
</body>
</method>
@ -267,34 +237,154 @@
</body>
</method>
<method name="goUp">
<method name="scrollOnePage">
<parameter name="aDirection"/> <!-- Must be -1 or 1 -->
<body>
<![CDATA[
// if nothing selected, we go from the bottom
for (var i = this.selectedItem ? this.selectedItem.previousSibling : this.lastChild; i; i = i.previousSibling) {
// could have a template element, which would be a sibling
if (i instanceof Components.interfaces.nsIDOMXULSelectControlItemElement) {
this.selectedItem = i;
return true;
}
var children = this.children;
if (children.length == 0)
return 0;
// If nothing is selected, we just select the first element
// at the extreme we're moving away from
if (!this.currentItem)
return aDirection == -1 ? children.length : 0;
// If the current item is visible, scroll by one page so that
// the new current item is at approximately the same position as
// the existing current item.
if (this._isItemVisible(this.currentItem))
this.scrollBoxObject.scrollBy(0, this.scrollBoxObject.height * aDirection);
// Figure out, how many items fully fit into the view port
// (including the currently selected one), and determine
// the index of the first one lying (partially) outside
var height = this.scrollBoxObject.height;
var border = this.currentItem.boxObject.y;
if (aDirection == -1)
border += this.currentItem.boxObject.height;
var index = this.currentIndex;
while (0 <= index && index < children.length) {
var border2 = children[index].boxObject.y;
if (aDirection == -1)
border2 += children[index].boxObject.height;
if ((border2 - border) * aDirection > height)
break;
index += aDirection;
}
return false;
index -= aDirection;
return index != this.currentIndex ? index - this.currentIndex : aDirection;
]]>
</body>
</method>
<method name="goDown">
<!-- richlistbox specific -->
<property name="children" readonly="true">
<getter>
<![CDATA[
var childNodes = [];
for (var child = this.firstChild; child; child = child.nextSibling) {
if (child instanceof Components.interfaces.nsIDOMXULSelectControlItemElement)
childNodes.push(child);
}
return childNodes;
]]>
</getter>
</property>
<field name="_builderListener" readonly="true">
<![CDATA[
({
mOuter: this,
item: null,
willRebuild: function(builder) { },
didRebuild: function(builder) {
this.mOuter._refreshSelection();
}
});
]]>
</field>
<method name="_refreshSelection">
<body>
<![CDATA[
// if nothing selected, we go from the top
for (var i = this.selectedItem ? this.selectedItem.nextSibling : this.firstChild; i; i = i.nextSibling) {
// could have a template element, which would be a sibling
if (i instanceof Components.interfaces.nsIDOMXULSelectControlItemElement) {
this.selectedItem = i;
return true;
// when this method is called, we know that either the currentItem
// and selectedItems we have are null (ctor) or a reference to an
// element no longer in the DOM (template).
// first look for the last-selected attribute
var state = this.getAttribute("last-selected");
if (state) {
var ids = state.split(" ");
var suppressSelect = this._suppressOnSelect;
this._suppressOnSelect = true;
this.clearSelection();
for (var i = 1; i < ids.length; i++) {
var selectedItem = document.getElementById(ids[i]);
if (selectedItem)
this.addItemToSelection(selectedItem);
}
var currentItem = document.getElementById(ids[0]);
if (!currentItem && this._currentIndex)
currentItem = this.getItemAtIndex(Math.min(
this._currentIndex - 1, this.getRowCount()));
if (currentItem) {
this.currentItem = currentItem;
if (this.selType != "multiple" && this.selectedCount == 0)
this.selectedItem = currentItem;
if (this.scrollBoxObject.height)
this.ensureElementIsVisible(currentItem);
else // XXX hack around a bug in ensureElementIsVisible
this.ensureElementIsVisible(currentItem.previousSibling);
}
this._suppressOnSelect = suppressSelect;
// XXX actually it's just a refresh, but at least
// the Extensions manager expects this:
this._fireOnSelect();
return;
}
// try to restore the selected items according to their IDs
// (applies after a template rebuild, if last-selected was not set)
if (this.selectedItems) {
for (i = this.selectedCount - 1; i >= 0; i--) {
if (this.selectedItems[i] && this.selectedItems[i].id)
this.selectedItems[i] = document.getElementById(this.selectedItems[i].id);
else
this.selectedItems[i] = null;
if (!this.selectedItems[i])
this.selectedItems.splice(i, 1);
}
}
return false;
if (this.currentItem && this.currentItem.id)
this.currentItem = document.getElementById(this.currentItem.id);
else
this.currentItem = null;
// if we have no previously current item or if the above check fails to
// find the previous nodes (which causes it to clear selection)
if (!this.currentItem && this.selectedCount == 0) {
this.currentIndex = this._currentIndex ? this._currentIndex - 1 : 0;
// cf. listbox constructor:
// select items according to their attributes
var els = this.getElementsByAttribute("selected", "true");
for (i = 0; i < els.length; i++)
this.selectedItems.push(els[i]);
}
if (this.selType != "multiple" && this.selectedCount == 0)
this.selectedItem = this.currentItem;
// XXX hack for the Downloads manager (better to focus the list than
// the individual items - these usually aren't tabbable anyway, and
// we need the keyboard focus for navigation), see bug 363271:
this.focus();
]]>
</body>
</method>
@ -317,148 +407,88 @@
</body>
</method>
<method name="scrollOnePage">
<parameter name="aDirection"/> <!-- Must be -1 or 1 -->
<body>
<![CDATA[
var children = this.children;
if (children.length == 0)
return false;
var index = children.indexOf(this.selectedItem);
// If nothing is selected, we just select the first element
// at the extreme we're moving away from
if (index == -1) {
index = aDirection == -1 ? children.length - 1 : 0;
this.selectedItem = children[index];
return true;
}
// If the selected item is visible, we scroll by one page so that
// the newly selected item is at approximately the same position as
// the currently selected one
var currentItem = children[index];
if (this._isItemVisible(currentItem))
this.scrollBoxObject.scrollBy(0, this.scrollBoxObject.height * aDirection);
// Figure out, how many items fully fit into the view port
// (including the currently selected one), and determine
// the index of the first one lying (partially) outside
var height = this.scrollBoxObject.height;
var border = currentItem.boxObject.y;
if (aDirection == -1)
border += currentItem.boxObject.height;
while (index >= 0 && index < children.length) {
var border2 = children[index].boxObject.y;
if (aDirection == -1)
border2 += children[index].boxObject.height;
if ((border2 - border) * aDirection > height)
break;
index += aDirection;
}
index -= aDirection;
if (this.selectedItem != children[index]) {
this.selectedItem = children[index];
return true;
}
// Move by at least one item if the view port is too small
if (aDirection == -1)
return this.goUp();
return this.goDown();
]]>
</body>
</method>
<method name="getItemAtIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
return this.children[aIndex];
]]>
</body>
</method>
<field name="_currentIndex">null</field>
<!-- For backwards-compatibility and for convenience.
Use getIndexOfItem instead. -->
<method name="getIndexOf">
<parameter name="aElement"/>
<body>
<![CDATA[
// don't search the children, if we're looking for none of them
if (aElement == null)
return -1;
return this.children.indexOf(aElement);
]]>
</body>
</method>
<method name="ensureElementIsVisible">
<parameter name="aElement"/>
<body>
<![CDATA[
if (aElement)
this.scrollBoxObject.ensureElementIsVisible(aElement);
return this.getIndexOfItem(aElement);
]]>
</body>
</method>
<!-- For backwards-compatibility and for convenience.
Use ensureElementIsVisible instead -->
<method name="ensureSelectedElementIsVisible">
<body>
<![CDATA[
if (this.selectedItem) {
this.ensureElementIsVisible(this.selectedItem);
}
return this.ensureElementIsVisible(this.selectedItem);
]]>
</body>
</method>
<property name="suppressOnSelect">
<getter>
<![CDATA[
return this.getAttribute("suppressonselect") == "true";
]]>
</getter>
</property>
<method name="_fireOnSelect">
<!-- For backwards-compatibility and for convenience.
Use moveByOffset instead. -->
<method name="goUp">
<body>
<![CDATA[
if (this.selectedItem)
this.setAttribute("last-selected", this.selectedItem.getAttribute("id"));
else
this.removeAttribute("last-selected");
if (!this.suppressOnSelect) {
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
// if we have controllers, notify the command dispatcher
if (this.controllers.getControllerCount() > 0)
document.commandDispatcher.updateCommands("richlistbox-select");
}
var index = this.currentIndex;
this.moveByOffset(-1, true, false);
return index != this.currentIndex;
]]>
</body>
</method>
<method name="goDown">
<body>
<![CDATA[
var index = this.currentIndex;
this.moveByOffset(1, true, false);
return index != this.currentIndex;
]]>
</body>
</method>
<!-- deprecated (is implied by currentItem and selectItem) -->
<method name="fireActiveItemEvent"><body/></method>
</implementation>
<handlers>
<handler event="keypress" keycode="VK_UP" action="goUp(); event.preventDefault();"/>
<handler event="keypress" keycode="VK_DOWN" action="goDown(); event.preventDefault();"/>
<handler event="keypress" keycode="VK_PAGE_UP" action="scrollOnePage(-1); event.preventDefault();"/>
<handler event="keypress" keycode="VK_PAGE_DOWN" action="scrollOnePage(1); event.preventDefault();"/>
<handler event="keypress" keycode="VK_HOME" action="clearSelection(); goDown(); event.preventDefault();"/>
<handler event="keypress" keycode="VK_END" action="clearSelection(); goUp(); event.preventDefault();"/>
<!-- handle keyboard navigation also when a child element has got the focus -->
<handler event="keypress" keycode="VK_UP"
modifiers="control shift any"
action="this.moveByOffset(-1, !event.ctrlKey, event.shiftKey);"
preventdefault="true"/>
<handler event="keypress" keycode="VK_DOWN"
modifiers="control shift any"
action="this.moveByOffset(1, !event.ctrlKey, event.shiftKey);"
preventdefault="true"/>
<handler event="keypress" keycode="VK_HOME"
modifiers="control shift any"
action="this.moveByOffset(-this.currentIndex, !event.ctrlKey, event.shiftKey);"
preventdefault="true"/>
<handler event="keypress" keycode="VK_END"
modifiers="control shift any"
action="this.moveByOffset(this.getRowCount(), !event.ctrlKey, event.shiftKey);"
preventdefault="true"/>
<handler event="keypress" keycode="VK_PAGE_UP"
modifiers="control shift any"
action="this.moveByOffset(this.scrollOnePage(-1), !event.ctrlKey, event.shiftKey);"
preventdefault="true"/>
<handler event="keypress" keycode="VK_PAGE_DOWN"
modifiers="control shift any"
action="this.moveByOffset(this.scrollOnePage(1), !event.ctrlKey, event.shiftKey);"
preventdefault="true"/>
<handler event="click">
<![CDATA[
// clicking into nothing should unselect
if (event.originalTarget.getAttribute("anonid") == "main-box")
if (event.originalTarget.getAttribute("anonid") == "main-box") {
this.clearSelection();
this.currentItem = null;
}
]]>
</handler>
<handler event="contextmenu">
@ -468,119 +498,70 @@
if (event.button != 2) {
var popup = document.getElementById(this.getAttribute("context"));
if (popup)
popup.showPopup(this.selectedItem, -1, -1, "context", "bottomleft", "topleft");
popup.showPopup(this.currentItem, -1, -1, "context", "bottomleft", "topleft");
}
]]>
</handler>
<handler event="focus">
<![CDATA[
if (event.target == this)
this.fireActiveItemEvent();
]]>
</handler>
</handlers>
</binding>
<binding id="richlistitem"
extends="chrome://global/content/bindings/general.xml#basecontrol">
extends="chrome://global/content/bindings/listbox.xml#listitem">
<content>
<children />
<children/>
</content>
<resources>
<stylesheet src="chrome://global/skin/richlistbox.css"/>
</resources>
<implementation implements="nsIAccessibleProvider, nsIDOMXULSelectControlItemElement">
<implementation>
<destructor>
<![CDATA[
// When we are destructed and we are selected, unselect ourselves so
// that richlistbox's selection doesn't point to something not in the DOM.
// We don't want to reset last-selected, so we don't call clearSelection().
var control = this.control;
if (!control)
return;
// When we are destructed and we are current or selected, unselect ourselves
// so that richlistbox's selection doesn't point to something not in the DOM.
// We don't want to reset last-selected, so we set _suppressOnSelect.
if (this.selected) {
this.control._setItemSelection(null);
var suppressSelect = control._suppressOnSelect;
control._suppressOnSelect = true;
control.removeItemFromSelection(this);
control._suppressOnSelect = suppressSelect;
}
if (this.current)
control.currentItem = null;
]]>
</destructor>
<!-- ///////////////// nsIAccessibleProvider ///////////////// -->
<property name="accessibleType" readonly="true">
<getter>
<![CDATA[
return Components.interfaces.nsIAccessibleProvider.XULListitem;
]]>
</getter>
</property>
<!-- ///////////////// nsIDOMXULSelectControlItemElement ///////////////// -->
<property name="value" onget="return this.getAttribute('value');"
onset="this.setAttribute('value', val); return val;"/>
<property name="label">
<property name="label" readonly="true">
<!-- Setter purposely not implemented; the getter returns a
concatentation of label text to expose via accessibility APIs-->
concatentation of label text to expose via accessibility APIs -->
<getter>
<![CDATA[
const XULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var labelText = "";
var startEl = document.getAnonymousNodes(this)[0];
if (startEl) {
var labels =
startEl.getElementsByTagNameNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',
'label');
var numLabels = labels.length;
for (count = 0; count < numLabels; count ++) {
var label = labels[count];
if (!label.collapsed && !label.hidden &&
label.className != 'text-link') {
labelText += label.value + ' ';
}
}
}
var startEl = document.getAnonymousNodes(this);
if (!startEl.length)
startEl = [this];
var labels = startEl[0].getElementsByTagNameNS(XULNS, "label");
for (var count = 0; count < labels.length; count++)
labelText += labels[count].value + " ";
return labelText;
]]>
</getter>
</property>
<property name="selected"
onget="return this.getAttribute('selected') == 'true';"
onset="return this.setAttribute('selected',val);"/>
<property name="control">
<getter>
<![CDATA[
var parent = this.parentNode;
while (parent) {
if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
return parent;
parent = parent.parentNode;
}
return null;
]]>
</getter>
</property>
</implementation>
<handlers>
<handler event="click">
<![CDATA[
var listbox = this.control;
if ((event.target == this) && event.ctrlKey && (listbox.selectedItem == this)) {
listbox.clearSelection();
} else {
listbox.selectedItem = this;
}
]]>
</handler>
<handler event="contextmenu" phase="capturing">
<![CDATA[
// This needed to be called before the contextmenu gets shown to handle
// someone rightclicking on an unselected item
if (event.target == this) {
var listbox = this.control;
if (listbox) {
listbox.selectedItem = this;
}
}
// handle someone right-clicking on an item other than the current one
if (event.target == this && this.control)
this.control.currentItem = this;
]]>
</handler>
</handlers>

Просмотреть файл

@ -20,6 +20,7 @@
*
* Contributor(s):
* Doron Rosenberg <doronr@us.ibm.com> (original author)
* Simon Bünzli <zeniko@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -38,6 +39,7 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
richlistbox {
margin: 2px 4px;
background-color: -moz-Field;
color: -moz-FieldText;
}
@ -47,7 +49,21 @@ richlistbox[disabled="true"] {
}
richlistitem[selected="true"] {
background-color: -moz-Dialog;
color: -moz-DialogText;
}
richlistbox:focus > richlistitem[selected="true"] {
background-color: Highlight;
color: HighlightText;
}
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"] {
outline: 1px dotted Highlight;
-moz-outline-offset: -1px;
}
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"][selected="true"] {
outline: 1px dotted #F3D982; /* TODO: find a suitable system color */
}

Просмотреть файл

@ -20,6 +20,7 @@
*
* Contributor(s):
* Doron Rosenberg <doronr@us.ibm.com> (original author)
* Simon Bünzli <zeniko@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -38,6 +39,7 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
richlistbox {
margin: 2px 4px;
background-color: -moz-Field;
color: -moz-FieldText;
}
@ -47,7 +49,21 @@ richlistbox[disabled="true"] {
}
richlistitem[selected="true"] {
background-color: -moz-Dialog;
color: -moz-DialogText;
}
richlistbox:focus > richlistitem[selected="true"] {
background-color: Highlight;
color: HighlightText;
}
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"] {
outline: 1px dotted Highlight;
-moz-outline-offset: -1px;
}
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"][selected="true"] {
outline: 1px dotted #F3D982; /* TODO: find a suitable system color */
}