From 4c0368e30f6fe117f1f52215ccf673179691ef7d Mon Sep 17 00:00:00 2001 From: pfeldman Date: Wed, 20 Jul 2016 21:32:50 -0700 Subject: [PATCH] DevTools: keep widgets in widget hierarchy upon hide, split attach/detach cycle from show/hide. Review-Url: https://codereview.chromium.org/2157363006 Cr-Commit-Position: refs/heads/master@{#406767} --- front_end/extensions/ExtensionPanel.js | 4 +- front_end/main/Main.js | 2 +- front_end/ui/Dialog.js | 2 +- front_end/ui/SplitWidget.js | 16 +++-- front_end/ui/TabbedPane.js | 22 +++--- front_end/ui/Widget.js | 98 ++++++++++++++++++-------- 6 files changed, 96 insertions(+), 48 deletions(-) diff --git a/front_end/extensions/ExtensionPanel.js b/front_end/extensions/ExtensionPanel.js index 8a84df6ed7..c9aa41c769 100644 --- a/front_end/extensions/ExtensionPanel.js +++ b/front_end/extensions/ExtensionPanel.js @@ -249,7 +249,7 @@ WebInspector.ExtensionSidebarPane.prototype = { delete this._objectPropertiesView; } if (this._extensionView) - this._extensionView.detach(true); + this._extensionView.detach(); this._extensionView = new WebInspector.ExtensionView(this._server, this._id, url, "extension fill"); this._extensionView.show(this.element); @@ -286,7 +286,7 @@ WebInspector.ExtensionSidebarPane.prototype = { if (this._objectPropertiesView) return; if (this._extensionView) { - this._extensionView.detach(true); + this._extensionView.detach(); delete this._extensionView; } this._objectPropertiesView = new WebInspector.ExtensionNotifierView(this._server, this._id); diff --git a/front_end/main/Main.js b/front_end/main/Main.js index 8e31408e2b..0294bdeabf 100644 --- a/front_end/main/Main.js +++ b/front_end/main/Main.js @@ -1023,7 +1023,7 @@ WebInspector.TargetCrashedScreen.show = function(debuggerModel) dialog.setWrapsContent(true); dialog.addCloseButton(); dialog.setDimmed(true); - var hideBound = dialog.detach.bind(dialog, false); + var hideBound = dialog.detach.bind(dialog); debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, hideBound); new WebInspector.TargetCrashedScreen(onHide).show(dialog.element); diff --git a/front_end/ui/Dialog.js b/front_end/ui/Dialog.js index 74ac91b556..e603a905a5 100644 --- a/front_end/ui/Dialog.js +++ b/front_end/ui/Dialog.js @@ -108,7 +108,7 @@ WebInspector.Dialog.prototype = { { var closeButton = this.contentElement.createChild("div", "dialog-close-button", "dt-close-button"); closeButton.gray = true; - closeButton.addEventListener("click", this.detach.bind(this, false), false); + closeButton.addEventListener("click", this.detach.bind(this), false); }, /** diff --git a/front_end/ui/SplitWidget.js b/front_end/ui/SplitWidget.js index 6a52c0c667..fca916b0bc 100644 --- a/front_end/ui/SplitWidget.js +++ b/front_end/ui/SplitWidget.js @@ -158,8 +158,9 @@ WebInspector.SplitWidget.prototype = { if (widget) { widget.element.classList.add("insertion-point-main"); widget.element.classList.remove("insertion-point-sidebar"); + widget.attach(this.element, this._sidebarWidget ? this._sidebarWidget.element : null); if (this._showMode === WebInspector.SplitWidget.ShowMode.OnlyMain || this._showMode === WebInspector.SplitWidget.ShowMode.Both) - widget.show(this.element); + widget.showWidget(); } }, @@ -176,8 +177,9 @@ WebInspector.SplitWidget.prototype = { if (widget) { widget.element.classList.add("insertion-point-sidebar"); widget.element.classList.remove("insertion-point-main"); + widget.attach(this.element); if (this._showMode === WebInspector.SplitWidget.ShowMode.OnlySidebar || this._showMode === WebInspector.SplitWidget.ShowMode.Both) - widget.show(this.element); + widget.showWidget(); } }, @@ -316,13 +318,13 @@ WebInspector.SplitWidget.prototype = { if (sideToShow) { // Make sure main is first in the children list. if (sideToShow === this._mainWidget) - this._mainWidget.show(this.element, this._sidebarWidget ? this._sidebarWidget.element : null); + this._mainWidget.showWidget(); else - this._sidebarWidget.show(this.element); + this._sidebarWidget.showWidget(); } if (sideToHide) { this._detaching = true; - sideToHide.detach(); + sideToHide.hideWidget(); delete this._detaching; } @@ -380,9 +382,9 @@ WebInspector.SplitWidget.prototype = { // Make sure main is the first in the children list. if (this._sidebarWidget) - this._sidebarWidget.show(this.element); + this._sidebarWidget.showWidget(); if (this._mainWidget) - this._mainWidget.show(this.element, this._sidebarWidget ? this._sidebarWidget.element : null); + this._mainWidget.showWidget(); // Order widgets in DOM properly. this.setSecondIsSidebar(this._secondIsSidebar); diff --git a/front_end/ui/TabbedPane.js b/front_end/ui/TabbedPane.js index 5ab070652d..5ef09e2837 100644 --- a/front_end/ui/TabbedPane.js +++ b/front_end/ui/TabbedPane.js @@ -208,6 +208,7 @@ WebInspector.TabbedPane.prototype = { else this._tabs.push(tab); this._tabsHistory.push(tab); + view.attach(this.element); if (this._tabsHistory[0] === tab && this.isShowing()) this.selectTab(tab.id, userGesture); this._updateTabElements(); @@ -258,6 +259,7 @@ WebInspector.TabbedPane.prototype = { this._tabs.splice(this._tabs.indexOf(tab), 1); if (tab._shown) this._hideTabElement(tab); + tab.view.detach(); var eventData = { tabId: id, view: tab.view, isUserGesture: userGesture }; this.dispatchEventToListeners(WebInspector.TabbedPane.EventTypes.TabClosed, eventData); @@ -423,13 +425,17 @@ WebInspector.TabbedPane.prototype = { changeTabView: function(id, view) { var tab = this._tabsById[id]; - if (this._currentTab && this._currentTab.id === tab.id) { - if (tab.view !== view) - this._hideTab(tab); - tab.view = view; + if (tab.view === view) + return; + + var isSelected = this._currentTab && this._currentTab.id === id; + if (isSelected) + this._hideTab(tab); + tab.view.detach(); + tab.view = view; + tab.view.attach(this.element); + if (isSelected) this._showTab(tab); - } else - tab.view = view; }, onResize: function() @@ -759,7 +765,7 @@ WebInspector.TabbedPane.prototype = { _showTab: function(tab) { tab.tabElement.classList.add("selected"); - tab.view.show(this.element); + tab.view.showWidget(); this._updateTabSlider(); }, @@ -785,7 +791,7 @@ WebInspector.TabbedPane.prototype = { _hideTab: function(tab) { tab.tabElement.classList.remove("selected"); - tab.view.detach(); + tab.view.hideWidget(); }, /** diff --git a/front_end/ui/Widget.js b/front_end/ui/Widget.js index 5558f58914..5f7f7aa8d3 100644 --- a/front_end/ui/Widget.js +++ b/front_end/ui/Widget.js @@ -41,7 +41,7 @@ WebInspector.Widget = function(isWebComponent) } this._isWebComponent = isWebComponent; this.element.__widget = this; - this._visible = true; + this._visible = false; this._isRoot = false; this._isShowing = false; this._children = []; @@ -119,7 +119,7 @@ WebInspector.Widget.prototype = { { if (this._isRoot) return true; - return this._parentWidget && this._parentWidget.isShowing(); + return !!this._parentWidget && this._parentWidget.isShowing(); }, /** @@ -209,28 +209,49 @@ WebInspector.Widget.prototype = { * @param {?Element=} insertBefore */ show: function(parentElement, insertBefore) + { + this.attach(parentElement, insertBefore); + this.showWidget(); + }, + + /** + * @param {?Element} parentElement + * @param {?Element=} insertBefore + */ + attach: function(parentElement, insertBefore) { WebInspector.Widget.__assert(parentElement, "Attempt to attach widget with no parent element"); // Update widget hierarchy. - if (this.element.parentElement !== parentElement) { - if (this.element.parentElement) - this.detach(); - - var currentParent = parentElement; - while (currentParent && !currentParent.__widget) - currentParent = currentParent.parentElementOrShadowHost(); + var currentParent = parentElement; + while (currentParent && !currentParent.__widget) + currentParent = currentParent.parentElementOrShadowHost(); + var newParentWidget = currentParent ? currentParent.__widget : null; + + if (this._parentWidget && newParentWidget !== this._parentWidget) { + // Reparent. + this.detach(); + } - if (currentParent) { - this._parentWidget = currentParent.__widget; + if (newParentWidget) { + if (this._parentWidget !== newParentWidget) { + this._parentWidget = newParentWidget; this._parentWidget._children.push(this); - this._isRoot = false; - } else - WebInspector.Widget.__assert(this._isRoot, "Attempt to attach widget to orphan node"); - } else if (this._visible) { - return; + } + this._isRoot = false; + } else { + WebInspector.Widget.__assert(this._isRoot, "Attempt to attach widget to orphan node"); } + this._parentElement = parentElement; + this._insertBeforeElement = insertBefore; + }, + + showWidget: function() + { + WebInspector.Widget.__assert(this._parentElement, "Attempt to show detached widget"); + if (this._visible) + return; this._visible = true; if (this._parentIsShowing()) @@ -239,12 +260,12 @@ WebInspector.Widget.prototype = { this.element.classList.remove("hidden"); // Reparent - if (this.element.parentElement !== parentElement) { - WebInspector.Widget._incrementWidgetCounter(parentElement, this.element); - if (insertBefore) - WebInspector.Widget._originalInsertBefore.call(parentElement, this.element, insertBefore); + if (this.element.parentElement !== this._parentElement) { + WebInspector.Widget._incrementWidgetCounter(this._parentElement, this.element); + if (this._insertBeforeElement) + WebInspector.Widget._originalInsertBefore.call(this._parentElement, this.element, this._insertBeforeElement); else - WebInspector.Widget._originalAppendChild.call(parentElement, this.element); + WebInspector.Widget._originalAppendChild.call(this._parentElement, this.element); } if (this._parentIsShowing()) @@ -256,35 +277,51 @@ WebInspector.Widget.prototype = { this._processOnResize(); }, + hideWidget: function() + { + this._hideWidget(); + }, + /** * @param {boolean=} overrideHideOnDetach + * @return {boolean} */ - detach: function(overrideHideOnDetach) + _hideWidget: function(overrideHideOnDetach) { - var parentElement = this.element.parentElement; - if (!parentElement) - return; + WebInspector.Widget.__assert(this._parentElement, "Attempt to hide detached widget"); + if (!this._visible) + return false; + this._visible = false; + var parentElement = this._parentElement; if (this._parentIsShowing()) this._processWillHide(); if (!overrideHideOnDetach && this.shouldHideOnDetach()) { this.element.classList.add("hidden"); - this._visible = false; if (this._parentIsShowing()) this._processWasHidden(); if (this._parentWidget && this._hasNonZeroConstraints()) this._parentWidget.invalidateConstraints(); - return; + return true; } // Force legal removal WebInspector.Widget._decrementWidgetCounter(parentElement, this.element); WebInspector.Widget._originalRemoveChild.call(parentElement, this.element); - this._visible = false; if (this._parentIsShowing()) this._processWasHidden(); + return true; + }, + + detach: function() + { + if (!this._parentWidget) + return; + var wasShown = this._hideWidget(true); + if (!wasShown) + return; // Update widget hierarchy. if (this._parentWidget) { @@ -294,10 +331,13 @@ WebInspector.Widget.prototype = { this._parentWidget.childWasDetached(this); var parent = this._parentWidget; this._parentWidget = null; + this._parentElement = null; + this._insertBeforeElement = null; if (this._hasNonZeroConstraints()) parent.invalidateConstraints(); - } else + } else { WebInspector.Widget.__assert(this._isRoot, "Removing non-root widget from DOM"); + } }, detachChildWidgets: function()