|
1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 "use strict"; |
|
7 |
|
8 const Ci = Components.interfaces; |
|
9 const Cu = Components.utils; |
|
10 |
|
11 Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); |
|
12 |
|
13 this.EXPORTED_SYMBOLS = ["SimpleListWidget"]; |
|
14 |
|
15 /** |
|
16 * A very simple vertical list view. |
|
17 * |
|
18 * Note: this widget should be used in tandem with the WidgetMethods in |
|
19 * ViewHelpers.jsm. |
|
20 * |
|
21 * @param nsIDOMNode aNode |
|
22 * The element associated with the widget. |
|
23 */ |
|
24 function SimpleListWidget(aNode) { |
|
25 this.document = aNode.ownerDocument; |
|
26 this.window = this.document.defaultView; |
|
27 this._parent = aNode; |
|
28 |
|
29 // Create an internal list container. |
|
30 this._list = this.document.createElement("scrollbox"); |
|
31 this._list.className = "simple-list-widget-container theme-body"; |
|
32 this._list.setAttribute("flex", "1"); |
|
33 this._list.setAttribute("orient", "vertical"); |
|
34 this._parent.appendChild(this._list); |
|
35 |
|
36 // Delegate some of the associated node's methods to satisfy the interface |
|
37 // required by WidgetMethods instances. |
|
38 ViewHelpers.delegateWidgetAttributeMethods(this, aNode); |
|
39 ViewHelpers.delegateWidgetEventMethods(this, aNode); |
|
40 } |
|
41 |
|
42 SimpleListWidget.prototype = { |
|
43 /** |
|
44 * Inserts an item in this container at the specified index. |
|
45 * |
|
46 * @param number aIndex |
|
47 * The position in the container intended for this item. |
|
48 * @param nsIDOMNode aContents |
|
49 * The node displayed in the container. |
|
50 * @return nsIDOMNode |
|
51 * The element associated with the displayed item. |
|
52 */ |
|
53 insertItemAt: function(aIndex, aContents) { |
|
54 aContents.classList.add("simple-list-widget-item"); |
|
55 |
|
56 let list = this._list; |
|
57 return list.insertBefore(aContents, list.childNodes[aIndex]); |
|
58 }, |
|
59 |
|
60 /** |
|
61 * Returns the child node in this container situated at the specified index. |
|
62 * |
|
63 * @param number aIndex |
|
64 * The position in the container intended for this item. |
|
65 * @return nsIDOMNode |
|
66 * The element associated with the displayed item. |
|
67 */ |
|
68 getItemAtIndex: function(aIndex) { |
|
69 return this._list.childNodes[aIndex]; |
|
70 }, |
|
71 |
|
72 /** |
|
73 * Immediately removes the specified child node from this container. |
|
74 * |
|
75 * @param nsIDOMNode aChild |
|
76 * The element associated with the displayed item. |
|
77 */ |
|
78 removeChild: function(aChild) { |
|
79 this._list.removeChild(aChild); |
|
80 |
|
81 if (this._selectedItem == aChild) { |
|
82 this._selectedItem = null; |
|
83 } |
|
84 }, |
|
85 |
|
86 /** |
|
87 * Removes all of the child nodes from this container. |
|
88 */ |
|
89 removeAllItems: function() { |
|
90 let list = this._list; |
|
91 let parent = this._parent; |
|
92 |
|
93 while (list.hasChildNodes()) { |
|
94 list.firstChild.remove(); |
|
95 } |
|
96 |
|
97 parent.scrollTop = 0; |
|
98 parent.scrollLeft = 0; |
|
99 this._selectedItem = null; |
|
100 }, |
|
101 |
|
102 /** |
|
103 * Gets the currently selected child node in this container. |
|
104 * @return nsIDOMNode |
|
105 */ |
|
106 get selectedItem() { |
|
107 return this._selectedItem; |
|
108 }, |
|
109 |
|
110 /** |
|
111 * Sets the currently selected child node in this container. |
|
112 * @param nsIDOMNode aChild |
|
113 */ |
|
114 set selectedItem(aChild) { |
|
115 let childNodes = this._list.childNodes; |
|
116 |
|
117 if (!aChild) { |
|
118 this._selectedItem = null; |
|
119 } |
|
120 for (let node of childNodes) { |
|
121 if (node == aChild) { |
|
122 node.classList.add("selected"); |
|
123 this._selectedItem = node; |
|
124 } else { |
|
125 node.classList.remove("selected"); |
|
126 } |
|
127 } |
|
128 }, |
|
129 |
|
130 /** |
|
131 * Adds a new attribute or changes an existing attribute on this container. |
|
132 * |
|
133 * @param string aName |
|
134 * The name of the attribute. |
|
135 * @param string aValue |
|
136 * The desired attribute value. |
|
137 */ |
|
138 setAttribute: function(aName, aValue) { |
|
139 this._parent.setAttribute(aName, aValue); |
|
140 |
|
141 if (aName == "emptyText") { |
|
142 this._textWhenEmpty = aValue; |
|
143 } else if (aName == "headerText") { |
|
144 this._textAsHeader = aValue; |
|
145 } |
|
146 }, |
|
147 |
|
148 /** |
|
149 * Removes an attribute on this container. |
|
150 * |
|
151 * @param string aName |
|
152 * The name of the attribute. |
|
153 */ |
|
154 removeAttribute: function(aName) { |
|
155 this._parent.removeAttribute(aName); |
|
156 |
|
157 if (aName == "emptyText") { |
|
158 this._removeEmptyText(); |
|
159 } |
|
160 }, |
|
161 |
|
162 /** |
|
163 * Ensures the specified element is visible. |
|
164 * |
|
165 * @param nsIDOMNode aElement |
|
166 * The element to make visible. |
|
167 */ |
|
168 ensureElementIsVisible: function(aElement) { |
|
169 if (!aElement) { |
|
170 return; |
|
171 } |
|
172 |
|
173 // Ensure the element is visible but not scrolled horizontally. |
|
174 let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject); |
|
175 boxObject.ensureElementIsVisible(aElement); |
|
176 boxObject.scrollBy(-this._list.clientWidth, 0); |
|
177 }, |
|
178 |
|
179 /** |
|
180 * Sets the text displayed permanently in this container as a header. |
|
181 * @param string aValue |
|
182 */ |
|
183 set _textAsHeader(aValue) { |
|
184 if (this._headerTextNode) { |
|
185 this._headerTextNode.setAttribute("value", aValue); |
|
186 } |
|
187 this._headerTextValue = aValue; |
|
188 this._showHeaderText(); |
|
189 }, |
|
190 |
|
191 /** |
|
192 * Sets the text displayed in this container when empty. |
|
193 * @param string aValue |
|
194 */ |
|
195 set _textWhenEmpty(aValue) { |
|
196 if (this._emptyTextNode) { |
|
197 this._emptyTextNode.setAttribute("value", aValue); |
|
198 } |
|
199 this._emptyTextValue = aValue; |
|
200 this._showEmptyText(); |
|
201 }, |
|
202 |
|
203 /** |
|
204 * Creates and appends a label displayed as this container's header. |
|
205 */ |
|
206 _showHeaderText: function() { |
|
207 if (this._headerTextNode || !this._headerTextValue) { |
|
208 return; |
|
209 } |
|
210 let label = this.document.createElement("label"); |
|
211 label.className = "plain simple-list-widget-perma-text"; |
|
212 label.setAttribute("value", this._headerTextValue); |
|
213 |
|
214 this._parent.insertBefore(label, this._list); |
|
215 this._headerTextNode = label; |
|
216 }, |
|
217 |
|
218 /** |
|
219 * Creates and appends a label signaling that this container is empty. |
|
220 */ |
|
221 _showEmptyText: function() { |
|
222 if (this._emptyTextNode || !this._emptyTextValue) { |
|
223 return; |
|
224 } |
|
225 let label = this.document.createElement("label"); |
|
226 label.className = "plain simple-list-widget-empty-text"; |
|
227 label.setAttribute("value", this._emptyTextValue); |
|
228 |
|
229 this._parent.appendChild(label); |
|
230 this._emptyTextNode = label; |
|
231 }, |
|
232 |
|
233 /** |
|
234 * Removes the label signaling that this container is empty. |
|
235 */ |
|
236 _removeEmptyText: function() { |
|
237 if (!this._emptyTextNode) { |
|
238 return; |
|
239 } |
|
240 this._parent.removeChild(this._emptyTextNode); |
|
241 this._emptyTextNode = null; |
|
242 }, |
|
243 |
|
244 window: null, |
|
245 document: null, |
|
246 _parent: null, |
|
247 _list: null, |
|
248 _selectedItem: null, |
|
249 _headerTextNode: null, |
|
250 _headerTextValue: "", |
|
251 _emptyTextNode: null, |
|
252 _emptyTextValue: "" |
|
253 }; |