Title: [220180] trunk/Source/WebInspectorUI
Revision
220180
Author
[email protected]
Date
2017-08-02 21:21:32 -0700 (Wed, 02 Aug 2017)

Log Message

Web Inspector: add TreeElement virtualization for the Recording tab
https://bugs.webkit.org/show_bug.cgi?id=174968

Reviewed by Joseph Pecoraro.

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

* UserInterface/Views/TreeOutline.js:
(WI.TreeOutline):
(WI.TreeOutline.prototype.get virtualized):
(WI.TreeOutline.prototype.registerScrollVirtualizer):
(WI.TreeOutline.prototype.updateVirtualizedElements.walk):
(WI.TreeOutline.prototype.updateVirtualizedElements):
Add spacer elements before and after the TreeOutline element that will size to ensure that
the TreeOutline node still takes up the same amount of space after some of the TreeElements
are removed. Whenever the scroll of the container view changes, recalculate the visible area
and add/remove TreeElements based on whether they would be in that. This is only possible if
every TreeElement has the same vertical height, which is given when setting up the scroll
listener on the container view.

* UserInterface/Views/TreeElement.js:
(WI.TreeElement.prototype.set hidden):
(WI.TreeElement.prototype._attach):
(WI.TreeElement.prototype.collapse):
(WI.TreeElement.prototype.expand):
(WI.TreeElement.prototype.reveal):
If the TreeOutline is being virtualized, don't add each TreeElement's node to the DOM. They
will be added at the end of the frame (via setTimeout) if they are within the visible + padding
area of the TreeOutline.

Modified Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (220179 => 220180)


--- trunk/Source/WebInspectorUI/ChangeLog	2017-08-03 04:19:10 UTC (rev 220179)
+++ trunk/Source/WebInspectorUI/ChangeLog	2017-08-03 04:21:32 UTC (rev 220180)
@@ -1,3 +1,36 @@
+2017-08-02  Devin Rousso  <[email protected]>
+
+        Web Inspector: add TreeElement virtualization for the Recording tab
+        https://bugs.webkit.org/show_bug.cgi?id=174968
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Views/RecordingNavigationSidebarPanel.js:
+        (WI.RecordingNavigationSidebarPanel):
+
+        * UserInterface/Views/TreeOutline.js:
+        (WI.TreeOutline):
+        (WI.TreeOutline.prototype.get virtualized):
+        (WI.TreeOutline.prototype.registerScrollVirtualizer):
+        (WI.TreeOutline.prototype.updateVirtualizedElements.walk):
+        (WI.TreeOutline.prototype.updateVirtualizedElements):
+        Add spacer elements before and after the TreeOutline element that will size to ensure that
+        the TreeOutline node still takes up the same amount of space after some of the TreeElements
+        are removed. Whenever the scroll of the container view changes, recalculate the visible area
+        and add/remove TreeElements based on whether they would be in that. This is only possible if
+        every TreeElement has the same vertical height, which is given when setting up the scroll
+        listener on the container view.
+
+        * UserInterface/Views/TreeElement.js:
+        (WI.TreeElement.prototype.set hidden):
+        (WI.TreeElement.prototype._attach):
+        (WI.TreeElement.prototype.collapse):
+        (WI.TreeElement.prototype.expand):
+        (WI.TreeElement.prototype.reveal):
+        If the TreeOutline is being virtualized, don't add each TreeElement's node to the DOM. They
+        will be added at the end of the frame (via setTimeout) if they are within the visible + padding
+        area of the TreeOutline.
+
 2017-08-01  Devin Rousso  <[email protected]>
 
         Web Inspector: simplify WebInspector with WI

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js (220179 => 220180)


--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js	2017-08-03 04:19:10 UTC (rev 220179)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js	2017-08-03 04:21:32 UTC (rev 220180)
@@ -30,6 +30,7 @@
         super("recording", WI.UIString("Recording"));
 
         this.contentTreeOutline.customIndent = true;
+        this.contentTreeOutline.registerScrollVirtualizer(this.contentView.element, 20);
 
         this.filterBar.placeholder = WI.UIString("Filter Actions");
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TreeElement.js (220179 => 220180)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TreeElement.js	2017-08-03 04:19:10 UTC (rev 220179)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TreeElement.js	2017-08-03 04:21:32 UTC (rev 220180)
@@ -165,8 +165,11 @@
         if (this._childrenListNode)
             this._childrenListNode.hidden = this._hidden;
 
-        if (this.treeOutline)
+        if (this.treeOutline) {
+            this.treeOutline.soon.updateVirtualizedElements(this);
+
             this.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementVisibilityDidChange, {element: this});
+        }
     }
 
     get shouldRefreshChildren()
@@ -244,9 +247,16 @@
         var nextSibling = null;
         if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
             nextSibling = this.nextSibling._listItemNode;
-        this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
-        if (this._childrenListNode)
-            this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+
+        if (!this.treeOutline || !this.treeOutline.virtualized) {
+            this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
+            if (this._childrenListNode)
+                this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
+        }
+
+        if (this.treeOutline)
+            this.treeOutline.soon.updateVirtualizedElements();
+
         if (this.selected)
             this.select();
         if (this.expanded)
@@ -334,8 +344,11 @@
         if (this.oncollapse)
             this.oncollapse(this);
 
-        if (this.treeOutline)
+        if (this.treeOutline) {
+            this.treeOutline.soon.updateVirtualizedElements(this);
+
             this.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementDisclosureDidChanged, {element: this});
+        }
     }
 
     collapseRecursively()
@@ -398,8 +411,11 @@
         if (this.onexpand)
             this.onexpand(this);
 
-        if (this.treeOutline)
+        if (this.treeOutline) {
+            this.treeOutline.soon.updateVirtualizedElements(this);
+
             this.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementDisclosureDidChanged, {element: this});
+        }
     }
 
     expandRecursively(maxDepth)
@@ -446,6 +462,11 @@
             currentAncestor = currentAncestor.parent;
         }
 
+        // This must be called before onreveal, as some subclasses will scrollIntoViewIfNeeded and
+        // we should update the visible elements before attempting to scroll.
+        if (this.treeOutline)
+            this.treeOutline.updateVirtualizedElements(this);
+
         if (this.onreveal)
             this.onreveal(this);
     }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js (220179 => 220180)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js	2017-08-03 04:19:10 UTC (rev 220179)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js	2017-08-03 04:21:32 UTC (rev 220180)
@@ -54,6 +54,11 @@
         this._disclosureButtons = true;
         this._customIndent = false;
 
+        this._virtualizedScrollContainer = null;
+        this._virtualizedTreeItemHeight = NaN;
+        this._virtualizedTopSpacer = null;
+        this._virtualizedBottomSpacer = null;
+
         this._childrenListNode.tabIndex = 0;
         this._childrenListNode.addEventListener("keydown", this._treeKeyDown.bind(this), true);
 
@@ -633,6 +638,93 @@
         return false;
     }
 
+    get virtualized()
+    {
+        return this._virtualizedScrollContainer && !isNaN(this._virtualizedTreeItemHeight);
+    }
+
+    registerScrollVirtualizer(scrollContainer, treeItemHeight)
+    {
+        console.assert(!isNaN(treeItemHeight));
+
+        this._virtualizedScrollContainer = scrollContainer;
+        this._virtualizedTreeItemHeight = treeItemHeight;
+        this._virtualizedTopSpacer = document.createElement("div");
+        this._virtualizedBottomSpacer = document.createElement("div");
+
+        this._virtualizedScrollContainer.addEventListener("scroll", (event) => {
+            this.updateVirtualizedElements();
+        });
+    }
+
+    updateVirtualizedElements(focusedTreeElement)
+    {
+        if (!this.virtualized)
+            return;
+
+        function walk(parent, callback) {
+            let count = 0;
+            let shouldReturn = false;
+            for (let i = 0; i < parent.children.length; ++i) {
+                if (!parent.children[i].revealed(false))
+                    continue;
+
+                shouldReturn = callback({
+                    parent,
+                    treeElement: parent.children[i],
+                    count,
+                });
+                if (shouldReturn)
+                    break;
+
+                ++count;
+                if (parent.children[i].expanded) {
+                    let result = walk(parent.children[i], callback);
+                    count += result.count;
+                    if (result.shouldReturn)
+                        break;
+                }
+            }
+            return {count, shouldReturn};
+        }
+
+        let numberVisible = Math.ceil(this._virtualizedScrollContainer.offsetHeight / this._virtualizedTreeItemHeight);
+        let extraRows = Math.max(numberVisible * 5, 50);
+        let firstItem = Math.floor(this._virtualizedScrollContainer.scrollTop / this._virtualizedTreeItemHeight) - extraRows;
+        let lastItem = firstItem + numberVisible + (extraRows * 2);
+
+        if (focusedTreeElement && focusedTreeElement.revealed(false)) {
+            let index = walk(this, ({treeElement}) => treeElement === focusedTreeElement).count;
+            if (index < firstItem) {
+                firstItem = index - extraRows;
+                lastItem = index + numberVisible + extraRows;
+            } else if (index > lastItem) {
+                firstItem = index - numberVisible - extraRows;
+                lastItem = index + extraRows;
+            }
+        }
+
+        let totalItems = walk(this, ({parent, treeElement, count}) => {
+            if (count < firstItem || count > lastItem)
+                treeElement.element.remove();
+            else {
+                parent._childrenListNode.appendChild(treeElement.element);
+                if (treeElement._childrenListNode)
+                    parent._childrenListNode.appendChild(treeElement._childrenListNode);
+            }
+            return false;
+        }).count;
+
+        this._virtualizedTopSpacer.style.height = (Math.max(firstItem, 0) * this._virtualizedTreeItemHeight) + "px";
+        this.element.parentNode.insertBefore(this._virtualizedTopSpacer, this.element);
+
+        this._virtualizedBottomSpacer.style.height = (Math.max(totalItems - lastItem, 0) * this._virtualizedTreeItemHeight) + "px";
+        this.element.parentNode.insertBefore(this._virtualizedBottomSpacer, this.element.nextElementSibling);
+
+        if (focusedTreeElement)
+            this._virtualizedScrollContainer.scrollTop = (firstItem + extraRows) * this._virtualizedTreeItemHeight;
+    }
+
     // Protected
 
     treeElementFromEvent(event)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to