123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const EventEmitter = require("devtools/shared/event-emitter");
- const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
- /**
- * A list menu widget that attempts to be very fast.
- *
- * Note: this widget should be used in tandem with the WidgetMethods in
- * view-helpers.js.
- *
- * @param nsIDOMNode aNode
- * The element associated with the widget.
- */
- const FastListWidget = module.exports = function FastListWidget(node) {
- this.document = node.ownerDocument;
- this.window = this.document.defaultView;
- this._parent = node;
- this._fragment = this.document.createDocumentFragment();
- // This is a prototype element that each item added to the list clones.
- this._templateElement = this.document.createElement("hbox");
- // Create an internal scrollbox container.
- this._list = this.document.createElement("scrollbox");
- this._list.className = "fast-list-widget-container theme-body";
- this._list.setAttribute("flex", "1");
- this._list.setAttribute("orient", "vertical");
- this._list.setAttribute("tabindex", "0");
- this._list.addEventListener("keypress", e => this.emit("keyPress", e), false);
- this._list.addEventListener("mousedown", e => this.emit("mousePress", e),
- false);
- this._parent.appendChild(this._list);
- this._orderedMenuElementsArray = [];
- this._itemsByElement = new Map();
- // This widget emits events that can be handled in a MenuContainer.
- EventEmitter.decorate(this);
- // Delegate some of the associated node's methods to satisfy the interface
- // required by MenuContainer instances.
- ViewHelpers.delegateWidgetAttributeMethods(this, node);
- ViewHelpers.delegateWidgetEventMethods(this, node);
- };
- FastListWidget.prototype = {
- /**
- * Inserts an item in this container at the specified index, optionally
- * grouping by name.
- *
- * @param number aIndex
- * The position in the container intended for this item.
- * @param nsIDOMNode aContents
- * The node to be displayed in the container.
- * @param Object aAttachment [optional]
- * Extra data for the user.
- * @return nsIDOMNode
- * The element associated with the displayed item.
- */
- insertItemAt: function (index, contents, attachment = {}) {
- let element = this._templateElement.cloneNode();
- element.appendChild(contents);
- if (index >= 0) {
- throw new Error("FastListWidget only supports appending items.");
- }
- this._fragment.appendChild(element);
- this._orderedMenuElementsArray.push(element);
- this._itemsByElement.set(element, this);
- return element;
- },
- /**
- * This is a non-standard widget implementation method. When appending items,
- * they are queued in a document fragment. This method appends the document
- * fragment to the dom.
- */
- flush: function () {
- this._list.appendChild(this._fragment);
- },
- /**
- * Removes all of the child nodes from this container.
- */
- removeAllItems: function () {
- let list = this._list;
- while (list.hasChildNodes()) {
- list.firstChild.remove();
- }
- this._selectedItem = null;
- this._orderedMenuElementsArray.length = 0;
- this._itemsByElement.clear();
- },
- /**
- * Remove the given item.
- */
- removeChild: function (child) {
- throw new Error("Not yet implemented");
- },
- /**
- * Gets the currently selected child node in this container.
- * @return nsIDOMNode
- */
- get selectedItem() {
- return this._selectedItem;
- },
- /**
- * Sets the currently selected child node in this container.
- * @param nsIDOMNode child
- */
- set selectedItem(child) {
- let menuArray = this._orderedMenuElementsArray;
- if (!child) {
- this._selectedItem = null;
- }
- for (let node of menuArray) {
- if (node == child) {
- node.classList.add("selected");
- this._selectedItem = node;
- } else {
- node.classList.remove("selected");
- }
- }
- this.ensureElementIsVisible(this.selectedItem);
- },
- /**
- * Returns the child node in this container situated at the specified index.
- *
- * @param number index
- * The position in the container intended for this item.
- * @return nsIDOMNode
- * The element associated with the displayed item.
- */
- getItemAtIndex: function (index) {
- return this._orderedMenuElementsArray[index];
- },
- /**
- * Adds a new attribute or changes an existing attribute on this container.
- *
- * @param string name
- * The name of the attribute.
- * @param string value
- * The desired attribute value.
- */
- setAttribute: function (name, value) {
- this._parent.setAttribute(name, value);
- if (name == "emptyText") {
- this._textWhenEmpty = value;
- }
- },
- /**
- * Removes an attribute on this container.
- *
- * @param string name
- * The name of the attribute.
- */
- removeAttribute: function (name) {
- this._parent.removeAttribute(name);
- if (name == "emptyText") {
- this._removeEmptyText();
- }
- },
- /**
- * Ensures the specified element is visible.
- *
- * @param nsIDOMNode element
- * The element to make visible.
- */
- ensureElementIsVisible: function (element) {
- if (!element) {
- return;
- }
- // Ensure the element is visible but not scrolled horizontally.
- let boxObject = this._list.boxObject;
- boxObject.ensureElementIsVisible(element);
- boxObject.scrollBy(-this._list.clientWidth, 0);
- },
- /**
- * Sets the text displayed in this container when empty.
- * @param string aValue
- */
- set _textWhenEmpty(value) {
- if (this._emptyTextNode) {
- this._emptyTextNode.setAttribute("value", value);
- }
- this._emptyTextValue = value;
- this._showEmptyText();
- },
- /**
- * Creates and appends a label signaling that this container is empty.
- */
- _showEmptyText: function () {
- if (this._emptyTextNode || !this._emptyTextValue) {
- return;
- }
- let label = this.document.createElement("label");
- label.className = "plain fast-list-widget-empty-text";
- label.setAttribute("value", this._emptyTextValue);
- this._parent.insertBefore(label, this._list);
- this._emptyTextNode = label;
- },
- /**
- * Removes the label signaling that this container is empty.
- */
- _removeEmptyText: function () {
- if (!this._emptyTextNode) {
- return;
- }
- this._parent.removeChild(this._emptyTextNode);
- this._emptyTextNode = null;
- },
- window: null,
- document: null,
- _parent: null,
- _list: null,
- _selectedItem: null,
- _orderedMenuElementsArray: null,
- _itemsByElement: null,
- _emptyTextNode: null,
- _emptyTextValue: ""
- };
|