Bug 892605 - part 0: add callMethod to WidgetHelpers, checkItem to SideMenuWidget; r=vporof
authorNick Fitzgerald <fitzgen@gmail.com>
Sat, 27 Jul 2013 10:48:10 -0700
changeset 140216 0c80222426fe8757b91f9db09ded8697a1017479
parent 140215 5f7ad1bfd200acd5b26c8c16a635e7645e8c95a9
child 140217 893b7d8e7b175de977c8aeb0cdb7eb161a02521d
push id1949
push usernfitzgerald@mozilla.com
push dateSat, 27 Jul 2013 17:51:10 +0000
treeherderfx-team@aa190a916dcc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvporof
bugs892605
milestone25.0a1
Bug 892605 - part 0: add callMethod to WidgetHelpers, checkItem to SideMenuWidget; r=vporof
browser/devtools/shared/widgets/SideMenuWidget.jsm
browser/devtools/shared/widgets/ViewHelpers.jsm
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -52,16 +52,17 @@ this.SideMenuWidget = function SideMenuW
   this._list.addEventListener("mousedown", e => this.emit("mousePress", e), false);
   this._parent.appendChild(this._list);
   this._boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
 
   // Menu items can optionally be grouped.
   this._groupsByName = new Map(); // Can't use a WeakMap because keys are strings.
   this._orderedGroupElementsArray = [];
   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.delegateWidgetEventMethods(this, aNode);
 };
@@ -161,16 +162,17 @@ SideMenuWidget.prototype = {
       aChild.parentNode.remove();
     } else {
       // Groups with no title don't have any special internal structure.
       aChild.remove();
     }
 
     this._orderedMenuElementsArray.splice(
       this._orderedMenuElementsArray.indexOf(aChild), 1);
+    this._itemsByElement.delete(aChild);
 
     if (this._selectedItem == aChild) {
       this._selectedItem = null;
     }
   },
 
   /**
    * Removes all of the child nodes from this container.
@@ -183,16 +185,17 @@ SideMenuWidget.prototype = {
       list.firstChild.remove();
     }
 
     this._selectedItem = null;
 
     this._groupsByName.clear();
     this._orderedGroupElementsArray.length = 0;
     this._orderedMenuElementsArray.length = 0;
+    this._itemsByElement.clear();
   },
 
   /**
    * Gets the currently selected child node in this container.
    * @return nsIDOMNode
    */
   get selectedItem() this._selectedItem,
 
@@ -324,16 +327,32 @@ SideMenuWidget.prototype = {
     this._parent.removeAttribute(aName);
 
     if (aName == "notice") {
       this._removeNotice();
     }
   },
 
   /**
+   * Set the checkbox state for the item associated with the given node.
+   *
+   * @param nsIDOMNode aNode
+   *        The dom node for an item we want to check.
+   * @param boolean aCheckState
+   *        True to check, false to uncheck.
+   */
+  checkItem: function(aNode, aCheckState) {
+    const widgetItem = this._itemsByElement.get(aNode);
+    if (!widgetItem) {
+      throw new Error("No item for " + aNode);
+    }
+    widgetItem.check(aCheckState);
+  },
+
+  /**
    * Sets the text displayed in this container as a notice.
    * @param string aValue
    */
   set notice(aValue) {
     if (this._noticeTextNode) {
       this._noticeTextNode.setAttribute("value", aValue);
     }
     this._noticeTextValue = aValue;
@@ -419,16 +438,17 @@ SideMenuWidget.prototype = {
   _showCheckboxes: false,
   _parent: null,
   _list: null,
   _boxObject: null,
   _selectedItem: null,
   _groupsByName: null,
   _orderedGroupElementsArray: null,
   _orderedMenuElementsArray: null,
+  _itemsByElement: null,
   _ensureVisibleTimeout: null,
   _noticeTextContainer: null,
   _noticeTextNode: null,
   _noticeTextValue: ""
 };
 
 /**
  * A SideMenuGroup constructor for the BreadcrumbsWidget.
@@ -473,16 +493,17 @@ function SideMenuGroup(aWidget, aName) {
     let target = this._target = this._list = this.document.createElement("vbox");
     target.className = "side-menu-widget-group side-menu-widget-group-list";
   }
 }
 
 SideMenuGroup.prototype = {
   get _orderedGroupElementsArray() this.ownerView._orderedGroupElementsArray,
   get _orderedMenuElementsArray() this.ownerView._orderedMenuElementsArray,
+  get _itemsByElement() { return this.ownerView._itemsByElement; },
 
   /**
    * Inserts this group in the parent container at the specified index.
    *
    * @param number aIndex
    *        The position in the container intended for this group.
    */
   insertSelfAt: function(aIndex) {
@@ -544,41 +565,28 @@ SideMenuGroup.prototype = {
  * @param object aAttachment [optional]
  *        The attachment object.
  */
 function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag, aCheckboxFlag, aAttachment={}) {
   this.document = aGroup.document;
   this.window = aGroup.window;
   this.ownerView = aGroup;
 
-  let makeCheckbox = () => {
-    let checkbox = this.document.createElement("checkbox");
-    checkbox.className = "side-menu-widget-item-checkbox";
-    checkbox.setAttribute("checked", aAttachment.checkboxState);
-    checkbox.setAttribute("tooltiptext", aAttachment.checkboxTooltip);
-    checkbox.addEventListener("command", function () {
-      ViewHelpers.dispatchEvent(checkbox, "check", {
-        checked: checkbox.checked,
-      });
-    }, false);
-    return checkbox;
-  };
-
   if (aArrowFlag || aCheckboxFlag) {
     let container = this._container = this.document.createElement("hbox");
     container.className = "side-menu-widget-item";
     container.setAttribute("tooltiptext", aTooltip);
     container.setAttribute("align", "start");
 
     let target = this._target = this.document.createElement("vbox");
     target.className = "side-menu-widget-item-contents";
 
     // Show a checkbox before the content.
     if (aCheckboxFlag) {
-      let checkbox = this._checkbox = makeCheckbox();
+      let checkbox = this._checkbox = this._makeCheckbox(aAttachment);
       container.appendChild(checkbox);
     }
 
     container.appendChild(target);
 
     // Show a horizontal arrow towards the content.
     if (aArrowFlag) {
       let arrow = this._arrow = this.document.createElement("hbox");
@@ -594,16 +602,46 @@ function SideMenuItem(aGroup, aContents,
 
   this._target.setAttribute("flex", "1");
   this.contents = aContents;
 }
 
 SideMenuItem.prototype = {
   get _orderedGroupElementsArray() this.ownerView._orderedGroupElementsArray,
   get _orderedMenuElementsArray() this.ownerView._orderedMenuElementsArray,
+  get _itemsByElement() { return this.ownerView._itemsByElement; },
+
+  /**
+   * Create the checkbox used when the checkbox flag is true. Emits a "check"
+   * event whenever the checkbox is checked or unchecked by the user.
+   *
+   * @param Object aAttachment
+   *        The attachment object. The following properties are used:
+   *          - checkboxState: true for checked, false for unchecked
+   8          - checkboxTooltip: The tooltip text of the checkbox
+   */
+  _makeCheckbox: function (aAttachment) {
+    let checkbox = this.document.createElement("checkbox");
+    checkbox.className = "side-menu-widget-item-checkbox";
+    checkbox.setAttribute("tooltiptext", aAttachment.checkboxTooltip);
+
+    if (aAttachment.checkboxState) {
+      checkbox.setAttribute("checked", true);
+    } else {
+      checkbox.removeAttribute("checked");
+    }
+
+    checkbox.addEventListener("command", function () {
+      ViewHelpers.dispatchEvent(checkbox, "check", {
+        checked: checkbox.checked,
+      });
+    }, false);
+
+    return checkbox;
+  },
 
   /**
    * Inserts this item in the parent group at the specified index.
    *
    * @param number aIndex
    *        The position in the container intended for this item.
    * @return nsIDOMNode
    *         The element associated with the displayed item.
@@ -614,21 +652,39 @@ SideMenuItem.prototype = {
 
     if (aIndex >= 0) {
       ownerList.insertBefore(this._container, ownerList.childNodes[aIndex]);
       menuArray.splice(aIndex, 0, this._target);
     } else {
       ownerList.appendChild(this._container);
       menuArray.push(this._target);
     }
+    this._itemsByElement.set(this._target, this);
 
     return this._target;
   },
 
   /**
+   * Check or uncheck the checkbox associated with this item.
+   *
+   * @param boolean aCheckState
+   *        True to check, false to uncheck.
+   */
+  check: function(aCheckState) {
+    if (!this._checkbox) {
+      throw new Error("Cannot check items that do not have checkboxes.");
+    }
+    if (aCheckState) {
+      this._checkbox.setAttribute("checked", true);
+    } else {
+      this._checkbox.removeAttribute("checked");
+    }
+  },
+
+  /**
    * Sets the contents displayed in this item's view.
    *
    * @param string | nsIDOMNode aContents
    *        The string or node displayed in the container.
    */
   set contents(aContents) {
     // If this item's view contents are a string, then create a label to hold
     // the text displayed in this breadcrumb.
@@ -650,10 +706,11 @@ SideMenuItem.prototype = {
     this._target.appendChild(aContents);
   },
 
   window: null,
   document: null,
   ownerView: null,
   _target: null,
   _container: null,
+  _checkbox: null,
   _arrow: null
 };
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -1607,16 +1607,29 @@ this.WidgetMethods = {
    *         -1 to sort aFirst to a lower index than aSecond
    *          0 to leave aFirst and aSecond unchanged with respect to each other
    *          1 to sort aSecond to a lower index than aFirst
    */
   _currentSortPredicate: function(aFirst, aSecond) {
     return +(aFirst._label.toLowerCase() > aSecond._label.toLowerCase());
   },
 
+  /**
+   * Call a method on this widget named `aMethodName`. Any further arguments are
+   * passed on to the method. Returns the result of the method call.
+   *
+   * @param String aMethodName
+   *        The name of the method you want to call.
+   * @param aArgs
+   *        Optional. Any arguments you want to pass through to the method.
+   */
+  callMethod: function(aMethodName, ...aArgs) {
+    return this._widget[aMethodName].apply(this._widget, aArgs);
+  },
+
   _widget: null,
   _preferredValue: null,
   _cachedCommandDispatcher: null
 };
 
 /**
  * A generator-iterator over all the items in this container.
  */