Changeset 220180 in webkit


Ignore:
Timestamp:
Aug 2, 2017 9:21:32 PM (7 years ago)
Author:
Devin Rousso
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.

Location:
trunk/Source/WebInspectorUI
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r220119 r220180  
     12017-08-02  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: add TreeElement virtualization for the Recording tab
     4        https://bugs.webkit.org/show_bug.cgi?id=174968
     5
     6        Reviewed by Joseph Pecoraro.
     7
     8        * UserInterface/Views/RecordingNavigationSidebarPanel.js:
     9        (WI.RecordingNavigationSidebarPanel):
     10
     11        * UserInterface/Views/TreeOutline.js:
     12        (WI.TreeOutline):
     13        (WI.TreeOutline.prototype.get virtualized):
     14        (WI.TreeOutline.prototype.registerScrollVirtualizer):
     15        (WI.TreeOutline.prototype.updateVirtualizedElements.walk):
     16        (WI.TreeOutline.prototype.updateVirtualizedElements):
     17        Add spacer elements before and after the TreeOutline element that will size to ensure that
     18        the TreeOutline node still takes up the same amount of space after some of the TreeElements
     19        are removed. Whenever the scroll of the container view changes, recalculate the visible area
     20        and add/remove TreeElements based on whether they would be in that. This is only possible if
     21        every TreeElement has the same vertical height, which is given when setting up the scroll
     22        listener on the container view.
     23
     24        * UserInterface/Views/TreeElement.js:
     25        (WI.TreeElement.prototype.set hidden):
     26        (WI.TreeElement.prototype._attach):
     27        (WI.TreeElement.prototype.collapse):
     28        (WI.TreeElement.prototype.expand):
     29        (WI.TreeElement.prototype.reveal):
     30        If the TreeOutline is being virtualized, don't add each TreeElement's node to the DOM. They
     31        will be added at the end of the frame (via setTimeout) if they are within the visible + padding
     32        area of the TreeOutline.
     33
    1342017-08-01  Devin Rousso  <drousso@apple.com>
    235
  • trunk/Source/WebInspectorUI/UserInterface/Views/RecordingNavigationSidebarPanel.js

    r220119 r220180  
    3131
    3232        this.contentTreeOutline.customIndent = true;
     33        this.contentTreeOutline.registerScrollVirtualizer(this.contentView.element, 20);
    3334
    3435        this.filterBar.placeholder = WI.UIString("Filter Actions");
  • trunk/Source/WebInspectorUI/UserInterface/Views/TreeElement.js

    r220119 r220180  
    166166            this._childrenListNode.hidden = this._hidden;
    167167
    168         if (this.treeOutline)
     168        if (this.treeOutline) {
     169            this.treeOutline.soon.updateVirtualizedElements(this);
     170
    169171            this.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementVisibilityDidChange, {element: this});
     172        }
    170173    }
    171174
     
    245248        if (this.nextSibling && this.nextSibling._listItemNode && this.nextSibling._listItemNode.parentNode === this.parent._childrenListNode)
    246249            nextSibling = this.nextSibling._listItemNode;
    247         this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
    248         if (this._childrenListNode)
    249             this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
     250
     251        if (!this.treeOutline || !this.treeOutline.virtualized) {
     252            this.parent._childrenListNode.insertBefore(this._listItemNode, nextSibling);
     253            if (this._childrenListNode)
     254                this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
     255        }
     256
     257        if (this.treeOutline)
     258            this.treeOutline.soon.updateVirtualizedElements();
     259
    250260        if (this.selected)
    251261            this.select();
     
    335345            this.oncollapse(this);
    336346
    337         if (this.treeOutline)
     347        if (this.treeOutline) {
     348            this.treeOutline.soon.updateVirtualizedElements(this);
     349
    338350            this.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementDisclosureDidChanged, {element: this});
     351        }
    339352    }
    340353
     
    399412            this.onexpand(this);
    400413
    401         if (this.treeOutline)
     414        if (this.treeOutline) {
     415            this.treeOutline.soon.updateVirtualizedElements(this);
     416
    402417            this.treeOutline.dispatchEventToListeners(WI.TreeOutline.Event.ElementDisclosureDidChanged, {element: this});
     418        }
    403419    }
    404420
     
    446462            currentAncestor = currentAncestor.parent;
    447463        }
     464
     465        // This must be called before onreveal, as some subclasses will scrollIntoViewIfNeeded and
     466        // we should update the visible elements before attempting to scroll.
     467        if (this.treeOutline)
     468            this.treeOutline.updateVirtualizedElements(this);
    448469
    449470        if (this.onreveal)
  • trunk/Source/WebInspectorUI/UserInterface/Views/TreeOutline.js

    r220119 r220180  
    5454        this._disclosureButtons = true;
    5555        this._customIndent = false;
     56
     57        this._virtualizedScrollContainer = null;
     58        this._virtualizedTreeItemHeight = NaN;
     59        this._virtualizedTopSpacer = null;
     60        this._virtualizedBottomSpacer = null;
    5661
    5762        this._childrenListNode.tabIndex = 0;
     
    634639    }
    635640
     641    get virtualized()
     642    {
     643        return this._virtualizedScrollContainer && !isNaN(this._virtualizedTreeItemHeight);
     644    }
     645
     646    registerScrollVirtualizer(scrollContainer, treeItemHeight)
     647    {
     648        console.assert(!isNaN(treeItemHeight));
     649
     650        this._virtualizedScrollContainer = scrollContainer;
     651        this._virtualizedTreeItemHeight = treeItemHeight;
     652        this._virtualizedTopSpacer = document.createElement("div");
     653        this._virtualizedBottomSpacer = document.createElement("div");
     654
     655        this._virtualizedScrollContainer.addEventListener("scroll", (event) => {
     656            this.updateVirtualizedElements();
     657        });
     658    }
     659
     660    updateVirtualizedElements(focusedTreeElement)
     661    {
     662        if (!this.virtualized)
     663            return;
     664
     665        function walk(parent, callback) {
     666            let count = 0;
     667            let shouldReturn = false;
     668            for (let i = 0; i < parent.children.length; ++i) {
     669                if (!parent.children[i].revealed(false))
     670                    continue;
     671
     672                shouldReturn = callback({
     673                    parent,
     674                    treeElement: parent.children[i],
     675                    count,
     676                });
     677                if (shouldReturn)
     678                    break;
     679
     680                ++count;
     681                if (parent.children[i].expanded) {
     682                    let result = walk(parent.children[i], callback);
     683                    count += result.count;
     684                    if (result.shouldReturn)
     685                        break;
     686                }
     687            }
     688            return {count, shouldReturn};
     689        }
     690
     691        let numberVisible = Math.ceil(this._virtualizedScrollContainer.offsetHeight / this._virtualizedTreeItemHeight);
     692        let extraRows = Math.max(numberVisible * 5, 50);
     693        let firstItem = Math.floor(this._virtualizedScrollContainer.scrollTop / this._virtualizedTreeItemHeight) - extraRows;
     694        let lastItem = firstItem + numberVisible + (extraRows * 2);
     695
     696        if (focusedTreeElement && focusedTreeElement.revealed(false)) {
     697            let index = walk(this, ({treeElement}) => treeElement === focusedTreeElement).count;
     698            if (index < firstItem) {
     699                firstItem = index - extraRows;
     700                lastItem = index + numberVisible + extraRows;
     701            } else if (index > lastItem) {
     702                firstItem = index - numberVisible - extraRows;
     703                lastItem = index + extraRows;
     704            }
     705        }
     706
     707        let totalItems = walk(this, ({parent, treeElement, count}) => {
     708            if (count < firstItem || count > lastItem)
     709                treeElement.element.remove();
     710            else {
     711                parent._childrenListNode.appendChild(treeElement.element);
     712                if (treeElement._childrenListNode)
     713                    parent._childrenListNode.appendChild(treeElement._childrenListNode);
     714            }
     715            return false;
     716        }).count;
     717
     718        this._virtualizedTopSpacer.style.height = (Math.max(firstItem, 0) * this._virtualizedTreeItemHeight) + "px";
     719        this.element.parentNode.insertBefore(this._virtualizedTopSpacer, this.element);
     720
     721        this._virtualizedBottomSpacer.style.height = (Math.max(totalItems - lastItem, 0) * this._virtualizedTreeItemHeight) + "px";
     722        this.element.parentNode.insertBefore(this._virtualizedBottomSpacer, this.element.nextElementSibling);
     723
     724        if (focusedTreeElement)
     725            this._virtualizedScrollContainer.scrollTop = (firstItem + extraRows) * this._virtualizedTreeItemHeight;
     726    }
     727
    636728    // Protected
    637729
Note: See TracChangeset for help on using the changeset viewer.