Bug 1472748 - Convert the listbox in "editBookmarkPanel.inc.xul" to "richlistbox". r=mak draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Thu, 05 Jul 2018 13:06:54 +0100
changeset 814468 e442cb2719b45cdf9a03d89797ec563d0c2e1c88
parent 814437 4544dd7771e5f2e08c54b67ce109d6c1bbd1d96e
push id115218
push userpaolo.mozmail@amadzone.org
push dateThu, 05 Jul 2018 12:07:24 +0000
reviewersmak
bugs1472748
milestone63.0a1
Bug 1472748 - Convert the listbox in "editBookmarkPanel.inc.xul" to "richlistbox". r=mak This uses the same event handling as the "listbox" and "listitem-checkbox" bindings that are scheduled for removal, and copies the required styles locally. MozReview-Commit-ID: 4gYhwlprPN7
browser/base/content/browser.css
browser/components/places/content/editBookmark.js
browser/components/places/content/editBookmarkPanel.inc.xul
browser/themes/osx/places/editBookmark.css
browser/themes/shared/places/editBookmarkPanel.inc.css
browser/themes/windows/places/editBookmark.css
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -844,21 +844,16 @@ toolbarspring {
     image-rendering: -moz-crisp-edges;
   }
   /* Synced Tabs sidebar */
   html|*.tabs-container html|*.item-tabs-list html|*.item-icon-container {
     image-rendering: -moz-crisp-edges;
   }
 }
 
-#editBMPanel_tagsSelector {
-  /* override default listbox width from xul.css */
-  width: auto;
-}
-
 menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
   display: none;
 }
 menuitem.spell-suggestion {
   font-weight: bold;
 }
 
 /* Hide extension toolbars that neglected to set the proper class */
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -754,26 +754,34 @@ var gEditItemOverlay = {
                                          : null;
 
     while (tagsSelector.hasChildNodes()) {
       tagsSelector.removeChild(tagsSelector.lastChild);
     }
 
     let tagsInField = this._getTagsArrayFromTagsInputField();
     let allTags = PlacesUtils.tagging.allTags;
-    for (let tag of allTags) {
-      let elt = document.createElement("listitem");
-      elt.setAttribute("type", "checkbox");
-      elt.setAttribute("label", tag);
+    let fragment = document.createDocumentFragment();
+    let newSelectedIndex = -1;
+    for (var i = 0; i < allTags.length; i++) {
+      let tag = allTags[i];
+      let elt = document.createElement("richlistitem");
+      elt.appendChild(document.createElement("image"));
+      let label = document.createElement("label");
+      label.setAttribute("value", tag);
+      elt.appendChild(label);
       if (tagsInField.includes(tag))
         elt.setAttribute("checked", "true");
-      tagsSelector.appendChild(elt);
+      fragment.appendChild(elt);
       if (selectedTag === tag)
-        selectedIndex = tagsSelector.getIndexOfItem(elt);
+        newSelectedIndex = i;
     }
+    tagsSelector.appendChild(fragment);
+    if (selectedIndex >= 0)
+      tagsSelector.selectedIndex = newSelectedIndex;
 
     // Restore position.
     // The listbox allows to scroll only if the required offset doesn't
     // overflow its capacity, thus need to adjust the index for removals.
     firstIndex =
       Math.min(firstIndex,
                tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows());
     tagsSelector.scrollToIndex(firstIndex);
@@ -791,22 +799,27 @@ var gEditItemOverlay = {
     if (tagsSelectorRow.collapsed) {
       expander.className = "expander-up";
       expander.setAttribute("tooltiptext",
                             expander.getAttribute("tooltiptextup"));
       tagsSelectorRow.collapsed = false;
       this._rebuildTagsSelectorList();
 
       // This is a no-op if we've added the listener.
-      tagsSelector.addEventListener("CheckboxStateChange", this);
+      tagsSelector.addEventListener("mousedown", this);
+      tagsSelector.addEventListener("keypress", this);
     } else {
       expander.className = "expander-down";
       expander.setAttribute("tooltiptext",
                             expander.getAttribute("tooltiptextdown"));
       tagsSelectorRow.collapsed = true;
+
+      // This is a no-op if we've removed the listener.
+      tagsSelector.removeEventListener("mousedown", this);
+      tagsSelector.removeEventListener("keypress", this);
     }
   },
 
   /**
    * Splits "tagsField" element value, returning an array of valid tag strings.
    *
    * @return Array of tag strings found in the field value.
    */
@@ -840,42 +853,62 @@ var gEditItemOverlay = {
     this._folderTree.selectItems([ip.guid]);
     PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
     this._folderTree.selectItems([guid]);
     this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
                                   this._folderTree.columns.getFirstColumn());
   },
 
   // EventListener
-  handleEvent(aEvent) {
-    switch (aEvent.type) {
-    case "CheckboxStateChange":
-      // Update the tags field when items are checked/unchecked in the listbox
-      let tags = this._getTagsArrayFromTagsInputField();
-      let tagCheckbox = aEvent.target;
-
-      let curTagIndex = tags.indexOf(tagCheckbox.label);
-      let tagsSelector = this._element("tagsSelector");
-      tagsSelector.selectedItem = tagCheckbox;
-
-      if (tagCheckbox.checked) {
-        if (curTagIndex == -1)
-          tags.push(tagCheckbox.label);
-      } else if (curTagIndex != -1) {
-        tags.splice(curTagIndex, 1);
+  handleEvent(event) {
+    switch (event.type) {
+    case "mousedown":
+      if (event.button == 0) {
+        // Make sure the event is triggered on an item and not the empty space.
+        let item = event.target.closest("richlistbox,richlistitem");
+        if (item.localName == "richlistitem") {
+          this.toggleItemCheckbox(item);
+        }
       }
-      this._element("tagsField").value = tags.join(", ");
-      this._updateTags();
+      break;
+    case "keypress":
+      if (event.key == " ") {
+        let item = event.target.currentItem;
+        if (item) {
+          this.toggleItemCheckbox(item);
+        }
+      }
       break;
     case "unload":
       this.uninitPanel(false);
       break;
     }
   },
 
+  toggleItemCheckbox(item) {
+    // Update the tags field when items are checked/unchecked in the listbox
+    let tags = this._getTagsArrayFromTagsInputField();
+
+    let curTagIndex = tags.indexOf(item.label);
+    let tagsSelector = this._element("tagsSelector");
+    tagsSelector.selectedItem = item;
+
+    if (!item.hasAttribute("checked")) {
+      item.setAttribute("checked", "true");
+      if (curTagIndex == -1)
+        tags.push(item.label);
+    } else {
+      item.removeAttribute("checked");
+      if (curTagIndex != -1)
+        tags.splice(curTagIndex, 1);
+    }
+    this._element("tagsField").value = tags.join(", ");
+    this._updateTags();
+  },
+
   _initTagsField() {
     let tags;
     if (this._paneInfo.isURI)
       tags = PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
     else if (this._paneInfo.bulkTagging)
       tags = this._getCommonTags();
     else
       throw new Error("_promiseTagsStr called unexpectedly");
--- a/browser/components/places/content/editBookmarkPanel.inc.xul
+++ b/browser/components/places/content/editBookmarkPanel.inc.xul
@@ -102,19 +102,20 @@
                 tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                 tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
                 tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
                 oncommand="gEditItemOverlay.toggleTagsSelector();"/>
       </hbox>
     </vbox>
 
     <vbox id="editBMPanel_tagsSelectorRow"
-         collapsed="true">
-      <listbox id="editBMPanel_tagsSelector"
-               height="150"/>
+          collapsed="true">
+      <richlistbox id="editBMPanel_tagsSelector"
+                   styled="true"
+                   height="150"/>
     </vbox>
 
     <vbox id="editBMPanel_keywordRow"
           collapsed="true">
       <label value="&editBookmarkOverlay.keyword.label;"
              accesskey="&editBookmarkOverlay.keyword.accesskey;"
              control="editBMPanel_keywordField"/>
       <textbox id="editBMPanel_keywordField"
--- a/browser/themes/osx/places/editBookmark.css
+++ b/browser/themes/osx/places/editBookmark.css
@@ -55,16 +55,25 @@
  * leaving only the comment column visible. This is
  * so that only the tag being edited is shown in the
  * popup.
  */
 #editBMPanel_tagsField #treecolAutoCompleteValue {
   visibility: collapse;
 }
 
+#editBMPanel_tagsSelector > richlistitem > image {
+  border: 1px solid -moz-DialogText;
+  background: -moz-Field no-repeat 50% 50%;
+}
+
+#editBMPanel_tagsSelector > richlistitem[checked="true"] > image {
+  background-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
 
 /* ----- BOOKMARK PANEL DROPDOWN MENU ITEMS ----- */
 
 #editBMPanel_folderMenuList[selectedIndex="0"],
 #editBMPanel_toolbarFolderItem {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg") !important;
 }
 
--- a/browser/themes/shared/places/editBookmarkPanel.inc.css
+++ b/browser/themes/shared/places/editBookmarkPanel.inc.css
@@ -37,16 +37,24 @@ html|img#editBookmarkPanelFavicon[src] {
 #editBookmarkPanelBottomContent {
   padding: var(--arrowpanel-padding);
 }
 
 #editBookmarkPanelRows {
   padding-bottom: 0;
 }
 
+#editBMPanel_tagsSelector > richlistitem > image {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0px 2px;
+  min-width: 13px;
+  min-height: 13px;
+}
+
 #editBookmarkPanelBottomContent {
   padding-top: 0;
 }
 
 /* Implements editBookmarkPanel resizing on folderTree un-collapse. */
 #editBMPanel_folderTree {
   min-width: 27em;
 }
--- a/browser/themes/windows/places/editBookmark.css
+++ b/browser/themes/windows/places/editBookmark.css
@@ -58,16 +58,21 @@
  * leaving only the comment column visible. This is
  * so that only the tag being edited is shown in the
  * popup.
  */
 #editBMPanel_tagsField #treecolAutoCompleteValue {
   visibility: collapse;
 }
 
+#editBMPanel_tagsSelector > richlistitem > image {
+  border: 1px solid -moz-DialogText;
+  background: -moz-Field no-repeat 50% 50%;
+}
+
 
 /* ::::: bookmark panel dropdown icons ::::: */
 
 #editBMPanel_folderMenuList[selectedIndex="0"],
 #editBMPanel_toolbarFolderItem {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg") !important;
 }