Web Inspector: Elements tab should allow selecting/deleting multiple DOM nodes
authormattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Nov 2018 06:49:34 +0000 (06:49 +0000)
committermattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Nov 2018 06:49:34 +0000 (06:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192059
<rdar://problem/46294827>

Reviewed by Devin Rousso.

Enable multiple DOM node selection in the DOMTreeContentView.

* UserInterface/Controllers/SelectionController.js:
(WI.SelectionController):
(WI.SelectionController.prototype.get allowsEmptySelection):
(WI.SelectionController.prototype.set allowsEmptySelection):
Allow clients to control whether the last selected item can be deselected.
(WI.SelectionController.prototype.deselectItem):
(WI.SelectionController.prototype.didInsertItem):
Rewritten to prevent infinite loop.
(WI.SelectionController.prototype.didRemoveItem):
(WI.SelectionController.prototype._updateSelectedItems):
(WI.SelectionController.prototype._adjustIndexesAfter): Deleted.

* UserInterface/Views/DOMTreeContentView.js:
(WI.DOMTreeContentView):

* UserInterface/Views/DOMTreeElement.js:
(WI.DOMTreeElement.prototype.updateSelectionArea):
* UserInterface/Views/DOMTreeOutline.js:
(WI.DOMTreeOutline.prototype.updateSelection):
Updating the selection area DOM element should not assume that only one
TreeElement is selected at a time.

* UserInterface/Views/TreeOutline.js:
(WI.TreeOutline.prototype.get allowsEmptySelection):
(WI.TreeOutline.prototype.set allowsEmptySelection):
(WI.TreeOutline.prototype.set selectedTreeElement):
(WI.TreeOutline.prototype.get selectedTreeElements):
(WI.TreeOutline.prototype._treeKeyDown):

* UserInterface/Views/TreeOutlineGroup.js:
(WI.TreeOutlineGroup):
(WI.TreeOutlineGroup.prototype._removeConflictingTreeSelections):
Eliminate use of `TreeElement.prototype.deselect`.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@238602 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Controllers/SelectionController.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeContentView.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js
Source/WebInspectorUI/UserInterface/Views/TreeOutline.js
Source/WebInspectorUI/UserInterface/Views/TreeOutlineGroup.js

index 2e6ecdc..f6eed99 100644 (file)
@@ -1,5 +1,49 @@
 2018-11-27  Matt Baker  <mattbaker@apple.com>
 
+        Web Inspector: Elements tab should allow selecting/deleting multiple DOM nodes
+        https://bugs.webkit.org/show_bug.cgi?id=192059
+        <rdar://problem/46294827>
+
+        Reviewed by Devin Rousso.
+
+        Enable multiple DOM node selection in the DOMTreeContentView.
+
+        * UserInterface/Controllers/SelectionController.js:
+        (WI.SelectionController):
+        (WI.SelectionController.prototype.get allowsEmptySelection):
+        (WI.SelectionController.prototype.set allowsEmptySelection):
+        Allow clients to control whether the last selected item can be deselected.
+        (WI.SelectionController.prototype.deselectItem):
+        (WI.SelectionController.prototype.didInsertItem):
+        Rewritten to prevent infinite loop.
+        (WI.SelectionController.prototype.didRemoveItem):
+        (WI.SelectionController.prototype._updateSelectedItems):
+        (WI.SelectionController.prototype._adjustIndexesAfter): Deleted.
+
+        * UserInterface/Views/DOMTreeContentView.js:
+        (WI.DOMTreeContentView):
+
+        * UserInterface/Views/DOMTreeElement.js:
+        (WI.DOMTreeElement.prototype.updateSelectionArea):
+        * UserInterface/Views/DOMTreeOutline.js:
+        (WI.DOMTreeOutline.prototype.updateSelection):
+        Updating the selection area DOM element should not assume that only one
+        TreeElement is selected at a time.
+
+        * UserInterface/Views/TreeOutline.js:
+        (WI.TreeOutline.prototype.get allowsEmptySelection):
+        (WI.TreeOutline.prototype.set allowsEmptySelection):
+        (WI.TreeOutline.prototype.set selectedTreeElement):
+        (WI.TreeOutline.prototype.get selectedTreeElements):
+        (WI.TreeOutline.prototype._treeKeyDown):
+
+        * UserInterface/Views/TreeOutlineGroup.js:
+        (WI.TreeOutlineGroup):
+        (WI.TreeOutlineGroup.prototype._removeConflictingTreeSelections):
+        Eliminate use of `TreeElement.prototype.deselect`.
+
+2018-11-27  Matt Baker  <mattbaker@apple.com>
+
         Web Inspector: TreeOutline should re-use multiple-selection logic from Table
         https://bugs.webkit.org/show_bug.cgi?id=191483
         <rdar://problem/45953305>
index ddfbf53..9456905 100644 (file)
@@ -32,6 +32,7 @@ WI.SelectionController = class SelectionController extends WI.Object
         console.assert(delegate);
         this._delegate = delegate;
 
+        this._allowsEmptySelection = true;
         this._allowsMultipleSelection = false;
         this._lastSelectedIndex = NaN;
         this._shiftAnchorIndex = NaN;
@@ -48,6 +49,9 @@ WI.SelectionController = class SelectionController extends WI.Object
     get lastSelectedItem() { return this._lastSelectedIndex; }
     get selectedItems() { return this._selectedIndexes; }
 
+    get allowsEmptySelection() { return this._allowsEmptySelection; }
+    set allowsEmptySelection(flag) { this._allowsEmptySelection = flag; }
+
     get allowsMultipleSelection()
     {
         return this._allowsMultipleSelection;
@@ -105,6 +109,9 @@ WI.SelectionController = class SelectionController extends WI.Object
         if (!this.hasSelectedItem(index))
             return;
 
+        if (!this._allowsEmptySelection && this._selectedIndexes.size === 1)
+            return;
+
         let newSelectedItems = this._selectedIndexes.copy();
         newSelectedItems.delete(index);
 
@@ -194,7 +201,13 @@ WI.SelectionController = class SelectionController extends WI.Object
 
     didInsertItem(index)
     {
-        this._adjustIndexesAfter(index - 1, 1);
+        let current = this._selectedIndexes.lastIndex;
+        while (current >= index) {
+            this._selectedIndexes.delete(index);
+            this._selectedIndexes.add(index + 1);
+
+            current = this._selectedIndexes.indexLessThan(current);
+        }
     }
 
     didRemoveItem(index)
@@ -202,7 +215,10 @@ WI.SelectionController = class SelectionController extends WI.Object
         if (this.hasSelectedItem(index))
             this.deselectItem(index);
 
-        this._adjustIndexesAfter(index, -1);
+        while (index = this._selectedIndexes.indexGreaterThan(index)) {
+            this._selectedIndexes.delete(index);
+            this._selectedIndexes.add(index - 1);
+        }
     }
 
     handleKeyDown(event)
@@ -374,12 +390,4 @@ WI.SelectionController = class SelectionController extends WI.Object
         let selectedItems = indexes.difference(oldSelectedIndexes);
         this._delegate.selectionControllerSelectionDidChange(this, deselectedItems, selectedItems);
     }
-
-    _adjustIndexesAfter(index, delta)
-    {
-        while (index = this._selectedIndexes.indexGreaterThan(index)) {
-            this._selectedIndexes.delete(index);
-            this._selectedIndexes.add(index + delta);
-        }
-    }
 };
index c7a6fe0..ab8678f 100644 (file)
@@ -64,6 +64,8 @@ WI.DOMTreeContentView = class DOMTreeContentView extends WI.ContentView
         this.element.addEventListener("click", this._mouseWasClicked.bind(this), false);
 
         this._domTreeOutline = new WI.DOMTreeOutline(true, true, true);
+        this._domTreeOutline.allowsEmptySelection = false;
+        this._domTreeOutline.allowsMultipleSelection = true;
         this._domTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._domTreeElementAdded, this);
         this._domTreeOutline.addEventListener(WI.DOMTreeOutline.Event.SelectedNodeChanged, this._selectedNodeDidChange, this);
         this._domTreeOutline.wireToDomAgent();
index 6c97a0a..edab8a7 100644 (file)
@@ -367,7 +367,7 @@ WI.DOMTreeElement = class DOMTreeElement extends WI.TreeElement
             return;
 
         // If there's no reason to have a selection area, remove the DOM element.
-        let indicatesTreeOutlineState = this.treeOutline && (this.treeOutline.dragOverTreeElement === this || this.treeOutline.selectedTreeElement === this || this._animatingHighlight);
+        let indicatesTreeOutlineState = this.treeOutline && (this.treeOutline.dragOverTreeElement === this || this.selected || this._animatingHighlight);
         if (!this.hovered && !this.pseudoClassesEnabled && !indicatesTreeOutlineState) {
             if (this._selectionElement) {
                 this._selectionElement.remove();
index d5eaa16..541e2f9 100644 (file)
@@ -191,8 +191,9 @@ WI.DOMTreeOutline = class DOMTreeOutline extends WI.TreeOutline
         // and those used to show forced pseudo class indicators, but this should be okay.
         // The hovered element will update when user moves the mouse, and indicators don't need the
         // selection area height to be accurate since they use ::before to place the indicator.
-        if (this.selectedTreeElement)
-            this.selectedTreeElement.updateSelectionArea();
+        let selectedTreeElements = this.selectedTreeElements;
+        for (let treeElement of selectedTreeElements)
+            treeElement.updateSelectionArea();
     }
 
     _selectedNodeChanged()
index da2d2bd..5e1b990 100644 (file)
@@ -81,6 +81,16 @@ WI.TreeOutline = class TreeOutline extends WI.Object
 
     // Public
 
+    get allowsEmptySelection()
+    {
+        return this._selectionController.allowsEmptySelection;
+    }
+
+    set allowsEmptySelection(flag)
+    {
+        this._selectionController.allowsEmptySelection = flag;
+    }
+
     get allowsMultipleSelection()
     {
         return this._selectionController.allowsMultipleSelection;
@@ -99,8 +109,27 @@ WI.TreeOutline = class TreeOutline extends WI.Object
 
     set selectedTreeElement(treeElement)
     {
-        let index = this._indexOfTreeElement(treeElement);
-        this._selectionController.selectItem(index);
+        if (treeElement) {
+            let index = this._indexOfTreeElement(treeElement);
+            this._selectionController.selectItem(index);
+        } else
+            this._selectionController.deselectAll();
+    }
+
+    get selectedTreeElements()
+    {
+        if (this.allowsMultipleSelection) {
+            let treeElements = [];
+            for (let index of this._selectionController.selectedItems)
+                treeElements.push(this._treeElementAtIndex(index));
+            return treeElements;
+        }
+
+        let selectedTreeElement = this.selectedTreeElement;
+        if (selectedTreeElement)
+            return [selectedTreeElement];
+
+        return [];
     }
 
     get hidden()
@@ -607,8 +636,10 @@ WI.TreeOutline = class TreeOutline extends WI.Object
                 }
             }
         } else if (event.keyCode === 8 /* Backspace */ || event.keyCode === 46 /* Delete */) {
-            if (this.selectedTreeElement.ondelete)
-                handled = this.selectedTreeElement.ondelete();
+            for (let treeElement of this.selectedTreeElements) {
+                if (treeElement.ondelete && treeElement.ondelete())
+                    handled = true;
+            }
             if (!handled && this.treeOutline.ondelete)
                 handled = this.treeOutline.ondelete(this.selectedTreeElement);
         } else if (isEnterKey(event)) {
index 12c1615..b9195c1 100644 (file)
@@ -87,8 +87,7 @@ WI.TreeOutlineGroup = class TreeOutlineGroup extends WI.Collection
             if (selectedTreeOutline === treeOutline)
                 continue;
 
-            if (treeOutline.selectedTreeElement)
-                treeOutline.selectedTreeElement.deselect();
+            treeOutline.selectedTreeElement = null;
         }
     }
 };