1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/shared/widgets/SimpleListWidget.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,253 @@ 1.4 +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 +"use strict"; 1.10 + 1.11 +const Ci = Components.interfaces; 1.12 +const Cu = Components.utils; 1.13 + 1.14 +Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); 1.15 + 1.16 +this.EXPORTED_SYMBOLS = ["SimpleListWidget"]; 1.17 + 1.18 +/** 1.19 + * A very simple vertical list view. 1.20 + * 1.21 + * Note: this widget should be used in tandem with the WidgetMethods in 1.22 + * ViewHelpers.jsm. 1.23 + * 1.24 + * @param nsIDOMNode aNode 1.25 + * The element associated with the widget. 1.26 + */ 1.27 +function SimpleListWidget(aNode) { 1.28 + this.document = aNode.ownerDocument; 1.29 + this.window = this.document.defaultView; 1.30 + this._parent = aNode; 1.31 + 1.32 + // Create an internal list container. 1.33 + this._list = this.document.createElement("scrollbox"); 1.34 + this._list.className = "simple-list-widget-container theme-body"; 1.35 + this._list.setAttribute("flex", "1"); 1.36 + this._list.setAttribute("orient", "vertical"); 1.37 + this._parent.appendChild(this._list); 1.38 + 1.39 + // Delegate some of the associated node's methods to satisfy the interface 1.40 + // required by WidgetMethods instances. 1.41 + ViewHelpers.delegateWidgetAttributeMethods(this, aNode); 1.42 + ViewHelpers.delegateWidgetEventMethods(this, aNode); 1.43 +} 1.44 + 1.45 +SimpleListWidget.prototype = { 1.46 + /** 1.47 + * Inserts an item in this container at the specified index. 1.48 + * 1.49 + * @param number aIndex 1.50 + * The position in the container intended for this item. 1.51 + * @param nsIDOMNode aContents 1.52 + * The node displayed in the container. 1.53 + * @return nsIDOMNode 1.54 + * The element associated with the displayed item. 1.55 + */ 1.56 + insertItemAt: function(aIndex, aContents) { 1.57 + aContents.classList.add("simple-list-widget-item"); 1.58 + 1.59 + let list = this._list; 1.60 + return list.insertBefore(aContents, list.childNodes[aIndex]); 1.61 + }, 1.62 + 1.63 + /** 1.64 + * Returns the child node in this container situated at the specified index. 1.65 + * 1.66 + * @param number aIndex 1.67 + * The position in the container intended for this item. 1.68 + * @return nsIDOMNode 1.69 + * The element associated with the displayed item. 1.70 + */ 1.71 + getItemAtIndex: function(aIndex) { 1.72 + return this._list.childNodes[aIndex]; 1.73 + }, 1.74 + 1.75 + /** 1.76 + * Immediately removes the specified child node from this container. 1.77 + * 1.78 + * @param nsIDOMNode aChild 1.79 + * The element associated with the displayed item. 1.80 + */ 1.81 + removeChild: function(aChild) { 1.82 + this._list.removeChild(aChild); 1.83 + 1.84 + if (this._selectedItem == aChild) { 1.85 + this._selectedItem = null; 1.86 + } 1.87 + }, 1.88 + 1.89 + /** 1.90 + * Removes all of the child nodes from this container. 1.91 + */ 1.92 + removeAllItems: function() { 1.93 + let list = this._list; 1.94 + let parent = this._parent; 1.95 + 1.96 + while (list.hasChildNodes()) { 1.97 + list.firstChild.remove(); 1.98 + } 1.99 + 1.100 + parent.scrollTop = 0; 1.101 + parent.scrollLeft = 0; 1.102 + this._selectedItem = null; 1.103 + }, 1.104 + 1.105 + /** 1.106 + * Gets the currently selected child node in this container. 1.107 + * @return nsIDOMNode 1.108 + */ 1.109 + get selectedItem() { 1.110 + return this._selectedItem; 1.111 + }, 1.112 + 1.113 + /** 1.114 + * Sets the currently selected child node in this container. 1.115 + * @param nsIDOMNode aChild 1.116 + */ 1.117 + set selectedItem(aChild) { 1.118 + let childNodes = this._list.childNodes; 1.119 + 1.120 + if (!aChild) { 1.121 + this._selectedItem = null; 1.122 + } 1.123 + for (let node of childNodes) { 1.124 + if (node == aChild) { 1.125 + node.classList.add("selected"); 1.126 + this._selectedItem = node; 1.127 + } else { 1.128 + node.classList.remove("selected"); 1.129 + } 1.130 + } 1.131 + }, 1.132 + 1.133 + /** 1.134 + * Adds a new attribute or changes an existing attribute on this container. 1.135 + * 1.136 + * @param string aName 1.137 + * The name of the attribute. 1.138 + * @param string aValue 1.139 + * The desired attribute value. 1.140 + */ 1.141 + setAttribute: function(aName, aValue) { 1.142 + this._parent.setAttribute(aName, aValue); 1.143 + 1.144 + if (aName == "emptyText") { 1.145 + this._textWhenEmpty = aValue; 1.146 + } else if (aName == "headerText") { 1.147 + this._textAsHeader = aValue; 1.148 + } 1.149 + }, 1.150 + 1.151 + /** 1.152 + * Removes an attribute on this container. 1.153 + * 1.154 + * @param string aName 1.155 + * The name of the attribute. 1.156 + */ 1.157 + removeAttribute: function(aName) { 1.158 + this._parent.removeAttribute(aName); 1.159 + 1.160 + if (aName == "emptyText") { 1.161 + this._removeEmptyText(); 1.162 + } 1.163 + }, 1.164 + 1.165 + /** 1.166 + * Ensures the specified element is visible. 1.167 + * 1.168 + * @param nsIDOMNode aElement 1.169 + * The element to make visible. 1.170 + */ 1.171 + ensureElementIsVisible: function(aElement) { 1.172 + if (!aElement) { 1.173 + return; 1.174 + } 1.175 + 1.176 + // Ensure the element is visible but not scrolled horizontally. 1.177 + let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject); 1.178 + boxObject.ensureElementIsVisible(aElement); 1.179 + boxObject.scrollBy(-this._list.clientWidth, 0); 1.180 + }, 1.181 + 1.182 + /** 1.183 + * Sets the text displayed permanently in this container as a header. 1.184 + * @param string aValue 1.185 + */ 1.186 + set _textAsHeader(aValue) { 1.187 + if (this._headerTextNode) { 1.188 + this._headerTextNode.setAttribute("value", aValue); 1.189 + } 1.190 + this._headerTextValue = aValue; 1.191 + this._showHeaderText(); 1.192 + }, 1.193 + 1.194 + /** 1.195 + * Sets the text displayed in this container when empty. 1.196 + * @param string aValue 1.197 + */ 1.198 + set _textWhenEmpty(aValue) { 1.199 + if (this._emptyTextNode) { 1.200 + this._emptyTextNode.setAttribute("value", aValue); 1.201 + } 1.202 + this._emptyTextValue = aValue; 1.203 + this._showEmptyText(); 1.204 + }, 1.205 + 1.206 + /** 1.207 + * Creates and appends a label displayed as this container's header. 1.208 + */ 1.209 + _showHeaderText: function() { 1.210 + if (this._headerTextNode || !this._headerTextValue) { 1.211 + return; 1.212 + } 1.213 + let label = this.document.createElement("label"); 1.214 + label.className = "plain simple-list-widget-perma-text"; 1.215 + label.setAttribute("value", this._headerTextValue); 1.216 + 1.217 + this._parent.insertBefore(label, this._list); 1.218 + this._headerTextNode = label; 1.219 + }, 1.220 + 1.221 + /** 1.222 + * Creates and appends a label signaling that this container is empty. 1.223 + */ 1.224 + _showEmptyText: function() { 1.225 + if (this._emptyTextNode || !this._emptyTextValue) { 1.226 + return; 1.227 + } 1.228 + let label = this.document.createElement("label"); 1.229 + label.className = "plain simple-list-widget-empty-text"; 1.230 + label.setAttribute("value", this._emptyTextValue); 1.231 + 1.232 + this._parent.appendChild(label); 1.233 + this._emptyTextNode = label; 1.234 + }, 1.235 + 1.236 + /** 1.237 + * Removes the label signaling that this container is empty. 1.238 + */ 1.239 + _removeEmptyText: function() { 1.240 + if (!this._emptyTextNode) { 1.241 + return; 1.242 + } 1.243 + this._parent.removeChild(this._emptyTextNode); 1.244 + this._emptyTextNode = null; 1.245 + }, 1.246 + 1.247 + window: null, 1.248 + document: null, 1.249 + _parent: null, 1.250 + _list: null, 1.251 + _selectedItem: null, 1.252 + _headerTextNode: null, 1.253 + _headerTextValue: "", 1.254 + _emptyTextNode: null, 1.255 + _emptyTextValue: "" 1.256 +};