Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (228023 => 228024)
--- trunk/Source/WebInspectorUI/ChangeLog 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/ChangeLog 2018-02-02 21:04:52 UTC (rev 228024)
@@ -1,3 +1,133 @@
+2018-02-02 Matt Baker <mattba...@apple.com>
+
+ Web Inspector: TabBar redesign: remove New Tab button and add experimental feature flag
+ https://bugs.webkit.org/show_bug.cgi?id=182342
+ <rdar://problem/37078662>
+
+ Reviewed by Devin Rousso.
+
+ This patch adds a new experimental setting group, "User Interface", with
+ a single setting, "Enable New TabBar". When enabled, the New Tab button is
+ no longer available in the top-level TabBar. The 'open tabs' context menu
+ no longer allows the last non-ephemeral open tab to be closed (unchecked).
+
+ * Localizations/en.lproj/localizedStrings.js:
+
+ * UserInterface/Base/Main.js:
+ (WI.contentLoaded):
+ (WI._tryToRestorePendingTabs):
+ Retain legacy behavior behind experimental feature setting.
+ (WI.isNewTabWithTypeAllowed):
+
+ * UserInterface/Base/Setting.js:
+ * UserInterface/Main.html:
+
+ * UserInterface/Views/CanvasTabContentView.js:
+ (WI.CanvasTabContentView):
+ * UserInterface/Views/ConsoleTabContentView.js:
+ (WI.ConsoleTabContentView):
+ * UserInterface/Views/DebuggerTabContentView.js:
+ (WI.DebuggerTabContentView):
+ * UserInterface/Views/ElementsTabContentView.js:
+ (WI.ElementsTabContentView):
+
+ * UserInterface/Views/GeneralTabBarItem.js:
+ (WI.GeneralTabBarItem):
+ (WI.GeneralTabBarItem.prototype.fromTabInfo):
+ (WI.GeneralTabBarItem.prototype.get isEphemeral):
+ (WI.GeneralTabBarItem.fromTabContentViewConstructor): Deleted.
+
+ * UserInterface/Views/LayersTabContentView.js:
+ (WI.LayersTabContentView):
+
+ * UserInterface/Views/LegacyTabBar.js: Copied from Source/WebInspectorUI/UserInterface/Views/TabBar.js.
+ (WI.LegacyTabBar):
+ (WI.LegacyTabBar.prototype.get newTabTabBarItem):
+ (WI.LegacyTabBar.prototype.updateNewTabTabBarItemState):
+ (WI.LegacyTabBar.prototype.addTabBarItem):
+ (WI.LegacyTabBar.prototype.insertTabBarItem.animateTabs):
+ (WI.LegacyTabBar.prototype.insertTabBarItem.removeStyles):
+ (WI.LegacyTabBar.prototype.insertTabBarItem):
+ (WI.LegacyTabBar.prototype.removeTabBarItem.animateTabs):
+ (WI.LegacyTabBar.prototype.removeTabBarItem.removeStyles):
+ (WI.LegacyTabBar.prototype.removeTabBarItem):
+ (WI.LegacyTabBar.prototype.selectPreviousTab):
+ (WI.LegacyTabBar.prototype.selectNextTab):
+ (WI.LegacyTabBar.prototype.get selectedTabBarItem):
+ (WI.LegacyTabBar.prototype.set selectedTabBarItem):
+ (WI.LegacyTabBar.prototype.get tabBarItems):
+ (WI.LegacyTabBar.prototype.get normalTabCount):
+ (WI.LegacyTabBar.prototype.layout.forceItemHidden):
+ (WI.LegacyTabBar.prototype.layout):
+ (WI.LegacyTabBar.prototype._tabBarItemsFromLeftToRight):
+ (WI.LegacyTabBar.prototype._findTabBarItem):
+ (WI.LegacyTabBar.prototype._hasMoreThanOneNormalTab):
+ (WI.LegacyTabBar.prototype._openDefaultTab):
+ (WI.LegacyTabBar.prototype._recordTabBarItemSizesAndPositions):
+ (WI.LegacyTabBar.prototype._applyTabBarItemSizesAndPositions):
+ (WI.LegacyTabBar.prototype._clearTabBarItemSizesAndPositions):
+ (WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose.):
+ (WI.LegacyTabBar.prototype._finishExpandingTabsAfterClose):
+ (WI.LegacyTabBar.prototype._handleMouseDown):
+ (WI.LegacyTabBar.prototype._handleClick):
+ (WI.LegacyTabBar.prototype._handleMouseMoved):
+ (WI.LegacyTabBar.prototype._handleMouseUp):
+ (WI.LegacyTabBar.prototype._handleMouseLeave):
+ (WI.LegacyTabBar.prototype._handleContextMenu):
+ (WI.LegacyTabBar.prototype._handleNewTabClick):
+ (WI.LegacyTabBar.prototype._handleTabPickerTabContextMenu):
+ (WI.LegacyTabBar.prototype._handleNewTabMouseEnter):
+
+ * UserInterface/Views/NetworkTabContentView.js:
+ (WI.NetworkTabContentView):
+ * UserInterface/Views/NewTabContentView.js:
+ (WI.NewTabContentView):
+ (WI.NewTabContentView.tabInfo):
+ (WI.NewTabContentView.isEphemeral): Deleted.
+ * UserInterface/Views/ResourcesTabContentView.js:
+ (WI.ResourcesTabContentView):
+ * UserInterface/Views/SearchTabContentView.js:
+ (WI.SearchTabContentView):
+ (WI.SearchTabContentView.tabInfo):
+ (WI.SearchTabContentView.isEphemeral): Deleted.
+
+ * UserInterface/Views/SettingsTabContentView.js:
+ (WI.SettingsTabContentView.tabInfo):
+ (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
+ (WI.SettingsTabContentView.isEphemeral): Deleted.
+
+ * UserInterface/Views/StorageTabContentView.js:
+ (WI.StorageTabContentView):
+
+ * UserInterface/Views/TabBar.css:
+ * UserInterface/Views/TabBar.js:
+ (WI.TabBar):
+ (WI.TabBar.prototype.insertTabBarItem):
+ (WI.TabBar.prototype.removeTabBarItem):
+ (WI.TabBar.prototype.set selectedTabBarItem):
+ (WI.TabBar.prototype.get normalNonEphemeralTabCount):
+ (WI.TabBar.prototype._handleMouseDown):
+ (WI.TabBar.prototype._handleClick):
+ (WI.TabBar.prototype._handleMouseMoved):
+ (WI.TabBar.prototype._handleMouseLeave):
+ (WI.TabBar.prototype._handleContextMenu):
+ (WI.TabBar.prototype._handleTabPickerTabContextMenu):
+ (WI.TabBar.prototype.get newTabTabBarItem): Deleted.
+ (WI.TabBar.prototype.updateNewTabTabBarItemState): Deleted.
+ (WI.TabBar.prototype._openDefaultTab): Deleted.
+ (WI.TabBar.prototype._handleNewTabClick): Deleted.
+ (WI.TabBar.prototype._handleNewTabMouseEnter): Deleted.
+ Remove support for the New Tab button and default tab. Without a default
+ tab, there is nothing to display when no tabs are open, so prevent the
+ last non-pinned tab from being removed.
+
+ * UserInterface/Views/TabBrowser.js:
+ (WI.TabBrowser._tabBarItemRemoved):
+ * UserInterface/Views/TabContentView.js:
+ (WI.TabContentView.isEphemeral): Deleted.
+ * UserInterface/Views/TimelineTabContentView.js:
+ (WI.TimelineTabContentView):
+
2018-01-30 Devin Rousso <web...@devinrousso.com>
Web Inspector: Replace Object.shallowMerge with ES2018 spread operator
Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -367,6 +367,7 @@
localizedStrings["Enable Breakpoints"] = "Enable Breakpoints";
localizedStrings["Enable Event Listener"] = "Enable Event Listener";
localizedStrings["Enable Layers Tab"] = "Enable Layers Tab";
+localizedStrings["Enable New Tab Bar"] = "Enable New Tab Bar";
localizedStrings["Enable Program"] = "Enable Program";
localizedStrings["Enable all breakpoints (%s)"] = "Enable all breakpoints (%s)";
localizedStrings["Enable breakpoints"] = "Enable breakpoints";
@@ -1008,6 +1009,7 @@
localizedStrings["Use the resource cache when loading resources"] = "Use the resource cache when loading resources";
localizedStrings["User Agent"] = "User Agent";
localizedStrings["User Agent Stylesheet"] = "User Agent Stylesheet";
+localizedStrings["User Interface:"] = "User Interface:";
localizedStrings["User Stylesheet"] = "User Stylesheet";
localizedStrings["Using Keyword Value"] = "Using Keyword Value";
localizedStrings["Using previous selector ā%sā"] = "Using previous selector ā%sā";
Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -270,8 +270,12 @@
// Create the user interface elements.
this.toolbar = new WI.Toolbar(document.getElementById("toolbar"));
- this.tabBar = new WI.TabBar(document.getElementById("tab-bar"));
- this.tabBar.addEventListener(WI.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
+ if (WI.settings.experimentalEnableNewTabBar.value)
+ this.tabBar = new WI.TabBar(document.getElementById("tab-bar"));
+ else {
+ this.tabBar = new WI.LegacyTabBar(document.getElementById("tab-bar"));
+ this.tabBar.addEventListener(WI.TabBar.Event.OpenDefaultTab, this._openDefaultTab, this);
+ }
this._contentElement = document.getElementById("content");
this._contentElement.setAttribute("role", "main");
@@ -601,7 +605,8 @@
this._pendingOpenTabs = stillPendingOpenTabs;
- this.tabBrowser.tabBar.updateNewTabTabBarItemState();
+ if (!WI.settings.experimentalEnableNewTabBar.value)
+ this.tabBrowser.tabBar.updateNewTabTabBarItemState();
};
WI.showNewTabTab = function(options)
@@ -632,7 +637,7 @@
if (tabClass === WI.NewTabContentView) {
let allTabs = Array.from(this.knownTabClasses());
- let addableTabs = allTabs.filter((tabClass) => !tabClass.isEphemeral());
+ let addableTabs = allTabs.filter((tabClass) => !tabClass.tabInfo().isEphemeral);
let canMakeNewTab = addableTabs.some((tabClass) => WI.isNewTabWithTypeAllowed(tabClass.Type));
return canMakeNewTab;
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -126,4 +126,5 @@
experimentalEnableLayersTab: new WI.Setting("experimental-enable-layers-tab", false),
experimentalLegacyStyleEditor: new WI.Setting("experimental-legacy-style-editor", false),
experimentalLegacyVisualSidebar: new WI.Setting("experimental-legacy-visual-sidebar", false),
+ experimentalEnableNewTabBar: new WI.Setting("experimental-enable-new-tab-bar", false),
};
Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Main.html 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html 2018-02-02 21:04:52 UTC (rev 228024)
@@ -479,6 +479,8 @@
<script src=""
<script src=""
+ <script src=""
+
<script src=""
<script src=""
<script src=""
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -29,7 +29,7 @@
{
console.assert(!representedObject || representedObject instanceof WI.Canvas);
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.CanvasTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.CanvasTabContentView.tabInfo());
const navigationSidebarPanelConstructor = WI.RecordingNavigationSidebarPanel;
const detailsSidebarPanelConstructors = [WI.RecordingStateDetailsSidebarPanel, WI.RecordingTraceDetailsSidebarPanel, WI.CanvasDetailsSidebarPanel];
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ConsoleTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.ConsoleTabContentView.tabInfo());
super(identifier || "console", "console", tabBarItem, null, null, true);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/DebuggerTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/DebuggerTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/DebuggerTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.DebuggerTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.DebuggerTabContentView.tabInfo());
let detailsSidebarPanelConstructors = [WI.ScopeChainDetailsSidebarPanel, WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
super(identifier || "debugger", "debugger", tabBarItem, WI.DebuggerSidebarPanel, detailsSidebarPanelConstructors);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ElementsTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.ElementsTabContentView.tabInfo());
let detailsSidebarPanelConstructors = [WI.RulesStyleDetailsSidebarPanel, WI.ComputedStyleDetailsSidebarPanel, WI.DOMNodeDetailsSidebarPanel];
if (window.LayerTreeAgent && !WI.settings.experimentalEnableLayersTab.value)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/GeneralTabBarItem.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/GeneralTabBarItem.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/GeneralTabBarItem.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -26,11 +26,13 @@
WI.GeneralTabBarItem = class GeneralTabBarItem extends WI.TabBarItem
{
- constructor(image, title, isEphemeral)
+ constructor(image, title, isEphemeral = false)
{
super(image, title);
- if (isEphemeral) {
+ this._isEphemeral = isEphemeral;
+
+ if (this._isEphemeral) {
this.element.classList.add("ephemeral");
let closeButtonElement = document.createElement("div");
@@ -42,15 +44,15 @@
}
}
- static fromTabContentViewConstructor(constructor)
+ static fromTabInfo({image, title, isEphemeral})
{
- let {image, title} = constructor.tabInfo();
- let isEphemeral = constructor.isEphemeral();
return new WI.GeneralTabBarItem(image, title, isEphemeral);
}
// Public
+ get isEphemeral() { return this._isEphemeral; }
+
get title()
{
return super.title;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LayersTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LayersTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LayersTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor()
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.LayersTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.LayersTabContentView.tabInfo());
const navigationSidebarPanelConstructor = null;
const detailsSidebarPanelConstructors = [WI.LayerDetailsSidebarPanel];
Copied: trunk/Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js (from rev 228023, trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.js) (0 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.LegacyTabBar = class LegacyTabBar extends WI.View
+{
+ constructor(element, tabBarItems)
+ {
+ super(element);
+
+ this.element.classList.add("tab-bar");
+ this.element.setAttribute("role", "tablist");
+ this.element.addEventListener("mousedown", this._handleMouseDown.bind(this));
+ this.element.addEventListener("click", this._handleClick.bind(this));
+ this.element.addEventListener("mouseleave", this._handleMouseLeave.bind(this));
+ this.element.addEventListener("contextmenu", this._handleContextMenu.bind(this));
+
+ this.element.createChild("div", "top-border");
+
+ this._tabBarItems = [];
+ this._hiddenTabBarItems = [];
+
+ if (tabBarItems) {
+ for (let tabBarItem in tabBarItems)
+ this.addTabBarItem(tabBarItem);
+ }
+
+ this.addTabBarItem(WI.settingsTabContentView.tabBarItem, {suppressAnimations: true});
+
+ this._newTabTabBarItem = new WI.PinnedTabBarItem("Images/NewTabPlus.svg", WI.UIString("Create a new tab"));
+ this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
+ this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
+ this.addTabBarItem(this._newTabTabBarItem, {suppressAnimations: true});
+
+ this._tabPickerTabBarItem = new WI.PinnedTabBarItem("Images/TabPicker.svg", WI.UIString("Show hidden tabs"));
+ this._tabPickerTabBarItem.element.classList.add("tab-picker");
+ this._tabPickerTabBarItem.element.addEventListener("contextmenu", this._handleTabPickerTabContextMenu.bind(this));
+ this.addTabBarItem(this._tabPickerTabBarItem, {suppressAnimations: true});
+ }
+
+ // Public
+
+ get newTabTabBarItem() { return this._newTabTabBarItem; }
+
+ updateNewTabTabBarItemState()
+ {
+ let newTabExists = !WI.isNewTabWithTypeAllowed(WI.NewTabContentView.Type);
+ this._newTabTabBarItem.disabled = newTabExists;
+ }
+
+ addTabBarItem(tabBarItem, options = {})
+ {
+ return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, options);
+ }
+
+ insertTabBarItem(tabBarItem, index, options = {})
+ {
+ console.assert(tabBarItem instanceof WI.TabBarItem);
+ if (!(tabBarItem instanceof WI.TabBarItem))
+ return null;
+
+ if (tabBarItem.parentTabBar === this)
+ return null;
+
+ if (this._tabAnimatedClosedSinceMouseEnter) {
+ // Delay adding the new tab until we can expand the tabs after a closed tab.
+ this._finishExpandingTabsAfterClose().then(() => {
+ this.insertTabBarItem(tabBarItem, index, options);
+ });
+ return null;
+ }
+
+ if (tabBarItem.parentTabBar)
+ tabBarItem.parentTabBar.removeTabBarItem(tabBarItem);
+
+ tabBarItem.parentTabBar = this;
+
+ index = Number.constrain(index, 0, this.normalTabCount);
+
+ if (this.element.classList.contains("animating")) {
+ requestAnimationFrame(removeStyles.bind(this));
+ options.suppressAnimations = true;
+ }
+
+ var beforeTabSizesAndPositions;
+ if (!options.suppressAnimations)
+ beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this._tabBarItems.splice(index, 0, tabBarItem);
+
+ var nextSibling = this._tabBarItems[index + 1];
+ let nextSiblingElement = nextSibling ? nextSibling.element : this._tabBarItems.lastValue.element;
+
+ if (this.element.isAncestor(nextSiblingElement))
+ this.element.insertBefore(tabBarItem.element, nextSiblingElement);
+ else
+ this.element.appendChild(tabBarItem.element);
+
+ this.element.classList.toggle("single-tab", !this._hasMoreThanOneNormalTab());
+
+ tabBarItem.element.style.left = null;
+ tabBarItem.element.style.width = null;
+
+ function animateTabs()
+ {
+ this.element.classList.add("animating");
+ this.element.classList.add("inserting-tab");
+
+ this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
+
+ this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ function removeStyles()
+ {
+ this.element.classList.remove("static-layout");
+ this.element.classList.remove("animating");
+ this.element.classList.remove("inserting-tab");
+
+ tabBarItem.element.classList.remove("being-inserted");
+
+ this._clearTabBarItemSizesAndPositions();
+
+ this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ if (!options.suppressAnimations) {
+ var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this.updateLayout();
+
+ let tabBarItems = this._tabBarItemsFromLeftToRight();
+ let previousTabBarItem = tabBarItems[tabBarItems.indexOf(tabBarItem) - 1] || null;
+ let previousTabBarItemSizeAndPosition = previousTabBarItem ? beforeTabSizesAndPositions.get(previousTabBarItem) : null;
+
+ if (previousTabBarItemSizeAndPosition)
+ beforeTabSizesAndPositions.set(tabBarItem, {left: previousTabBarItemSizeAndPosition.left + previousTabBarItemSizeAndPosition.width, width: 0});
+ else
+ beforeTabSizesAndPositions.set(tabBarItem, {left: 0, width: 0});
+
+ this.element.classList.add("static-layout");
+ tabBarItem.element.classList.add("being-inserted");
+
+ this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+
+ var removeStylesListener = removeStyles.bind(this);
+
+ requestAnimationFrame(animateTabs.bind(this));
+ } else
+ this.needsLayout();
+
+ if (!(tabBarItem instanceof WI.PinnedTabBarItem))
+ this.updateNewTabTabBarItemState();
+
+ this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemAdded, {tabBarItem});
+
+ return tabBarItem;
+ }
+
+ removeTabBarItem(tabBarItemOrIndex, options = {})
+ {
+ let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
+ if (!tabBarItem || tabBarItem instanceof WI.PinnedTabBarItem)
+ return null;
+
+ tabBarItem.parentTabBar = null;
+
+ if (this._selectedTabBarItem === tabBarItem) {
+ var index = this._tabBarItems.indexOf(tabBarItem);
+ var nextTabBarItem = this._tabBarItems[index + 1];
+ if (!nextTabBarItem || nextTabBarItem instanceof WI.PinnedTabBarItem)
+ nextTabBarItem = this._tabBarItems[index - 1];
+
+ this.selectedTabBarItem = nextTabBarItem;
+ }
+
+ if (this.element.classList.contains("animating")) {
+ requestAnimationFrame(removeStyles.bind(this));
+ options.suppressAnimations = true;
+ }
+
+ var beforeTabSizesAndPositions;
+ if (!options.suppressAnimations)
+ beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ // Subtract 1 from normalTabCount since arrays begin indexing at 0.
+ let wasLastNormalTab = this._tabBarItems.indexOf(tabBarItem) === this.normalTabCount - 1;
+
+ this._tabBarItems.remove(tabBarItem);
+ tabBarItem.element.remove();
+
+ var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
+ this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
+
+ const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
+ if (shouldOpenDefaultTab)
+ options.suppressAnimations = true;
+
+ if (!hasMoreThanOneNormalTab || wasLastNormalTab || !options.suppressExpansion) {
+ if (!options.suppressAnimations) {
+ this._tabAnimatedClosedSinceMouseEnter = true;
+ this._finishExpandingTabsAfterClose(beforeTabSizesAndPositions);
+ } else
+ this.needsLayout();
+
+ this.updateNewTabTabBarItemState();
+
+ this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
+
+ if (shouldOpenDefaultTab)
+ this._openDefaultTab();
+
+ return tabBarItem;
+ }
+
+ var lastNormalTabBarItem;
+
+ function animateTabs()
+ {
+ this.element.classList.add("animating");
+ this.element.classList.add("closing-tab");
+
+ // For RTL, we need to place extra space between pinned tab and first normal tab.
+ // From left to right there is pinned tabs, extra space, then normal tabs. Compute
+ // how much extra space we need to additionally add for normal tab items.
+ let extraSpaceBetweenNormalAndPinnedTabs = 0;
+ if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL) {
+ extraSpaceBetweenNormalAndPinnedTabs = this.element.getBoundingClientRect().width;
+ for (let currentTabBarItem of this._tabBarItemsFromLeftToRight())
+ extraSpaceBetweenNormalAndPinnedTabs -= currentTabBarItem.element.getBoundingClientRect().width;
+ }
+
+ let left = 0;
+ for (let currentTabBarItem of this._tabBarItemsFromLeftToRight()) {
+ let sizeAndPosition = beforeTabSizesAndPositions.get(currentTabBarItem);
+
+ if (!(currentTabBarItem instanceof WI.PinnedTabBarItem)) {
+ currentTabBarItem.element.style.left = extraSpaceBetweenNormalAndPinnedTabs + left + "px";
+ left += sizeAndPosition.width;
+ lastNormalTabBarItem = currentTabBarItem;
+ } else
+ left = sizeAndPosition.left + sizeAndPosition.width;
+ }
+
+ // The selected tab and last tab need to draw a right border as well, so make them 1px wider.
+ if (this._selectedTabBarItem)
+ this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) + 1) + "px";
+
+ if (lastNormalTabBarItem !== this._selectedTabBarItem)
+ lastNormalTabBarItem.element.style.width = (parseFloat(lastNormalTabBarItem.element.style.width) + 1) + "px";
+
+ this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ function removeStyles()
+ {
+ // The selected tab needs to stop drawing the right border, so make it 1px smaller. Only if it isn't the last.
+ if (this._selectedTabBarItem && this._selectedTabBarItem !== lastNormalTabBarItem)
+ this._selectedTabBarItem.element.style.width = (parseFloat(this._selectedTabBarItem.element.style.width) - 1) + "px";
+
+ this.element.classList.remove("animating");
+ this.element.classList.remove("closing-tab");
+
+ this.updateLayout();
+
+ this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ if (!options.suppressAnimations) {
+ this.element.classList.add("static-layout");
+
+ this._tabAnimatedClosedSinceMouseEnter = true;
+
+ this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+
+ var removeStylesListener = removeStyles.bind(this);
+
+ requestAnimationFrame(animateTabs.bind(this));
+ } else
+ this.needsLayout();
+
+ this.updateNewTabTabBarItemState();
+
+ this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
+
+ if (shouldOpenDefaultTab)
+ this._openDefaultTab();
+
+ return tabBarItem;
+ }
+
+ selectPreviousTab()
+ {
+ if (this._tabBarItems.length <= 1)
+ return;
+
+ var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+ var newIndex = startIndex;
+ do {
+ if (newIndex === 0)
+ newIndex = this._tabBarItems.length - 1;
+ else
+ newIndex--;
+
+ if (!(this._tabBarItems[newIndex] instanceof WI.PinnedTabBarItem))
+ break;
+ } while (newIndex !== startIndex);
+
+ if (newIndex === startIndex)
+ return;
+
+ this.selectedTabBarItem = this._tabBarItems[newIndex];
+ }
+
+ selectNextTab()
+ {
+ if (this._tabBarItems.length <= 1)
+ return;
+
+ var startIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+ var newIndex = startIndex;
+ do {
+ if (newIndex === this._tabBarItems.length - 1)
+ newIndex = 0;
+ else
+ newIndex++;
+
+ if (!(this._tabBarItems[newIndex] instanceof WI.PinnedTabBarItem))
+ break;
+ } while (newIndex !== startIndex);
+
+ if (newIndex === startIndex)
+ return;
+
+ this.selectedTabBarItem = this._tabBarItems[newIndex];
+ }
+
+ get selectedTabBarItem()
+ {
+ return this._selectedTabBarItem;
+ }
+
+ set selectedTabBarItem(tabBarItemOrIndex)
+ {
+ let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
+ if (tabBarItem === this._newTabTabBarItem) {
+ // Get the item before the New-Tab item since it is not selectable.
+ tabBarItem = this._tabBarItems[this.normalTabCount - 1];
+ }
+
+ if (this._selectedTabBarItem === tabBarItem)
+ return;
+
+ if (this._selectedTabBarItem)
+ this._selectedTabBarItem.selected = false;
+
+ this._selectedTabBarItem = tabBarItem || null;
+
+ if (this._selectedTabBarItem) {
+ this._selectedTabBarItem.selected = true;
+ if (this._selectedTabBarItem.element.classList.contains("hidden"))
+ this.needsLayout();
+ }
+
+ this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected);
+ }
+
+ get tabBarItems()
+ {
+ return this._tabBarItems;
+ }
+
+ get normalTabCount()
+ {
+ return this._tabBarItems.filter((item) => !(item instanceof WI.PinnedTabBarItem)).length;
+ }
+
+ // Protected
+
+ layout()
+ {
+ if (this.element.classList.contains("static-layout"))
+ return;
+
+ this.element.classList.add("calculate-width");
+ this.element.classList.remove("collapsed");
+
+ function forceItemHidden(item, hidden) {
+ item.element.classList.toggle("hidden", !!hidden);
+ }
+
+ for (let item of this._tabBarItems)
+ forceItemHidden(item, item === this._tabPickerTabBarItem);
+
+ function measureItemWidth(item) {
+ if (!item[WI.TabBar.CachedWidthSymbol])
+ item[WI.TabBar.CachedWidthSymbol] = item.element.realOffsetWidth;
+ return item[WI.TabBar.CachedWidthSymbol];
+ }
+
+ let recalculateItemWidths = () => {
+ return this._tabBarItems.reduce((total, item) => {
+ item[WI.TabBar.CachedWidthSymbol] = undefined;
+ return total + measureItemWidth(item);
+ }, 0);
+ };
+
+ this._hiddenTabBarItems = [];
+
+ let totalItemWidth = recalculateItemWidths();
+ let barWidth = this.element.realOffsetWidth;
+
+ if (totalItemWidth > barWidth) {
+ this.element.classList.add("collapsed");
+ totalItemWidth = recalculateItemWidths();
+ if (totalItemWidth > barWidth) {
+ forceItemHidden(this._tabPickerTabBarItem, false);
+ totalItemWidth += measureItemWidth(this._tabPickerTabBarItem);
+ }
+
+ let tabBarItems = this._tabBarItemsFromLeftToRight();
+ let index = tabBarItems.length;
+ while (totalItemWidth > barWidth && --index >= 0) {
+ let item = tabBarItems[index];
+ if (item === this.selectedTabBarItem || item instanceof WI.PinnedTabBarItem)
+ continue;
+
+ totalItemWidth -= measureItemWidth(item);
+ forceItemHidden(item, true);
+
+ this._hiddenTabBarItems.push(item);
+ }
+ }
+
+ this.element.classList.remove("calculate-width");
+ }
+
+ // Private
+
+ _tabBarItemsFromLeftToRight()
+ {
+ return WI.resolvedLayoutDirection() === WI.LayoutDirection.LTR ? this._tabBarItems : this._tabBarItems.slice().reverse();
+ }
+
+ _findTabBarItem(tabBarItemOrIndex)
+ {
+ if (typeof tabBarItemOrIndex === "number")
+ return this._tabBarItems[tabBarItemOrIndex] || null;
+
+ if (tabBarItemOrIndex instanceof WI.TabBarItem) {
+ if (this._tabBarItems.includes(tabBarItemOrIndex))
+ return tabBarItemOrIndex;
+ }
+
+ return null;
+ }
+
+ _hasMoreThanOneNormalTab()
+ {
+ let normalTabCount = 0;
+ for (let tabBarItem of this._tabBarItems) {
+ if (tabBarItem instanceof WI.PinnedTabBarItem)
+ continue;
+
+ ++normalTabCount;
+ if (normalTabCount >= 2)
+ return true;
+ }
+
+ return false;
+ }
+
+ _openDefaultTab()
+ {
+ this.dispatchEventToListeners(WI.TabBar.Event.OpenDefaultTab);
+ }
+
+ _recordTabBarItemSizesAndPositions()
+ {
+ var tabBarItemSizesAndPositions = new Map;
+
+ const barRect = this.element.getBoundingClientRect();
+
+ for (var tabBarItem of this._tabBarItems) {
+ var boundingRect = tabBarItem.element.getBoundingClientRect();
+ tabBarItemSizesAndPositions.set(tabBarItem, {left: boundingRect.left - barRect.left, width: boundingRect.width});
+ }
+
+ return tabBarItemSizesAndPositions;
+ }
+
+ _applyTabBarItemSizesAndPositions(tabBarItemSizesAndPositions, skipTabBarItem)
+ {
+ for (var [tabBarItem, sizeAndPosition] of tabBarItemSizesAndPositions) {
+ if (skipTabBarItem && tabBarItem === skipTabBarItem)
+ continue;
+ tabBarItem.element.style.left = sizeAndPosition.left + "px";
+ tabBarItem.element.style.width = sizeAndPosition.width + "px";
+ }
+ }
+
+ _clearTabBarItemSizesAndPositions(skipTabBarItem)
+ {
+ for (var tabBarItem of this._tabBarItems) {
+ if (skipTabBarItem && tabBarItem === skipTabBarItem)
+ continue;
+ tabBarItem.element.style.left = null;
+ tabBarItem.element.style.width = null;
+ }
+ }
+
+ _finishExpandingTabsAfterClose(beforeTabSizesAndPositions)
+ {
+ return new Promise(function(resolve, reject) {
+ console.assert(this._tabAnimatedClosedSinceMouseEnter);
+ this._tabAnimatedClosedSinceMouseEnter = false;
+
+ if (!beforeTabSizesAndPositions)
+ beforeTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this.element.classList.remove("static-layout");
+ this._clearTabBarItemSizesAndPositions();
+
+ var afterTabSizesAndPositions = this._recordTabBarItemSizesAndPositions();
+
+ this._applyTabBarItemSizesAndPositions(beforeTabSizesAndPositions);
+ this.element.classList.add("static-layout");
+
+ function animateTabs()
+ {
+ this.element.classList.add("static-layout");
+ this.element.classList.add("animating");
+ this.element.classList.add("expanding-tabs");
+
+ this._applyTabBarItemSizesAndPositions(afterTabSizesAndPositions);
+
+ this.element.addEventListener("webkitTransitionEnd", removeStylesListener);
+ }
+
+ function removeStyles()
+ {
+ this.element.classList.remove("static-layout");
+ this.element.classList.remove("animating");
+ this.element.classList.remove("expanding-tabs");
+
+ this._clearTabBarItemSizesAndPositions();
+
+ this.updateLayout();
+
+ this.element.removeEventListener("webkitTransitionEnd", removeStylesListener);
+
+ resolve();
+ }
+
+ var removeStylesListener = removeStyles.bind(this);
+
+ requestAnimationFrame(animateTabs.bind(this));
+ }.bind(this));
+ }
+
+ _handleMouseDown(event)
+ {
+ // Only consider left mouse clicks for tab movement.
+ if (event.button !== 0 || event.ctrlKey)
+ return;
+
+ let itemElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.StyleClassName);
+ if (!itemElement)
+ return;
+
+ let tabBarItem = itemElement[WI.TabBarItem.ElementReferenceSymbol];
+ if (!tabBarItem)
+ return;
+
+ if (tabBarItem.disabled)
+ return;
+
+ if (tabBarItem === this._newTabTabBarItem)
+ return;
+
+ if (tabBarItem === this._tabPickerTabBarItem) {
+ if (!this._hiddenTabBarItems.length)
+ return;
+
+ let contextMenu = WI.ContextMenu.createFromEvent(event);
+ for (let item of this._hiddenTabBarItems)
+ contextMenu.appendItem(item.title, () => this.selectedTabBarItem = item);
+
+ contextMenu.show();
+ return;
+ }
+
+ let closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
+ if (closeButtonElement)
+ return;
+
+ this.selectedTabBarItem = tabBarItem;
+
+ if (tabBarItem instanceof WI.PinnedTabBarItem || !this._hasMoreThanOneNormalTab())
+ return;
+
+ this._firstNormalTabItemIndex = 0;
+ for (let i = 0; i < this._tabBarItems.length; ++i) {
+ if (this._tabBarItems[i] instanceof WI.PinnedTabBarItem)
+ continue;
+
+ this._firstNormalTabItemIndex = i;
+ break;
+ }
+
+ this._mouseIsDown = true;
+
+ this._mouseMovedEventListener = this._handleMouseMoved.bind(this);
+ this._mouseUpEventListener = this._handleMouseUp.bind(this);
+
+ // Register these listeners on the document so we can track the mouse if it leaves the tab bar.
+ document.addEventListener("mousemove", this._mouseMovedEventListener, true);
+ document.addEventListener("mouseup", this._mouseUpEventListener, true);
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ _handleClick(event)
+ {
+ var itemElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.StyleClassName);
+ if (!itemElement)
+ return;
+
+ var tabBarItem = itemElement[WI.TabBarItem.ElementReferenceSymbol];
+ if (!tabBarItem)
+ return;
+
+ if (tabBarItem.disabled)
+ return;
+
+ const clickedMiddleButton = event.button === 1;
+
+ var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
+ if (closeButtonElement || clickedMiddleButton) {
+ // Disallow closing the default tab if it is the only tab.
+ if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
+ return;
+
+ if (!event.altKey) {
+ this.removeTabBarItem(tabBarItem, {suppressExpansion: true});
+ return;
+ }
+
+ for (let i = this._tabBarItems.length - 1; i >= 0; --i) {
+ let item = this._tabBarItems[i];
+ if (item === tabBarItem || item instanceof WI.PinnedTabBarItem)
+ continue;
+ this.removeTabBarItem(item);
+ }
+ }
+ }
+
+ _handleMouseMoved(event)
+ {
+ console.assert(event.button === 0);
+ console.assert(this._mouseIsDown);
+ if (!this._mouseIsDown)
+ return;
+
+ console.assert(this._selectedTabBarItem);
+ if (!this._selectedTabBarItem)
+ return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!this.element.classList.contains("static-layout")) {
+ this._applyTabBarItemSizesAndPositions(this._recordTabBarItemSizesAndPositions());
+ this.element.classList.add("static-layout");
+ this.element.classList.add("dragging-tab");
+ }
+
+ if (this._mouseOffset === undefined)
+ this._mouseOffset = event.pageX - this._selectedTabBarItem.element.totalOffsetLeft;
+
+ var tabBarMouseOffset = event.pageX - this.element.totalOffsetLeft;
+ var newLeft = tabBarMouseOffset - this._mouseOffset;
+
+ this._selectedTabBarItem.element.style.left = newLeft + "px";
+
+ var selectedTabMidX = newLeft + (this._selectedTabBarItem.element.realOffsetWidth / 2);
+
+ var currentIndex = this._tabBarItems.indexOf(this._selectedTabBarItem);
+ var newIndex = currentIndex;
+
+ for (let tabBarItem of this._tabBarItems) {
+ if (tabBarItem === this._selectedTabBarItem)
+ continue;
+
+ var tabBarItemRect = tabBarItem.element.getBoundingClientRect();
+
+ if (selectedTabMidX < tabBarItemRect.left || selectedTabMidX > tabBarItemRect.right)
+ continue;
+
+ newIndex = this._tabBarItems.indexOf(tabBarItem);
+ break;
+ }
+
+ // Subtract 1 from normalTabCount since arrays begin indexing at 0.
+ newIndex = Number.constrain(newIndex, this._firstNormalTabItemIndex, this.normalTabCount - 1);
+
+ if (currentIndex === newIndex)
+ return;
+
+ this._tabBarItems.splice(currentIndex, 1);
+ this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
+
+ let nextSibling = this._tabBarItems[newIndex + 1];
+ let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
+
+ this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
+
+ // FIXME: Animate the tabs that move to make room for the selected tab. This was causing me trouble when I tried.
+
+ let left = 0;
+ for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
+ if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
+ tabBarItem.element.style.left = left + "px";
+ left += parseFloat(tabBarItem.element.style.width);
+ }
+ }
+
+ _handleMouseUp(event)
+ {
+ console.assert(event.button === 0);
+ console.assert(this._mouseIsDown);
+ if (!this._mouseIsDown)
+ return;
+
+ this.element.classList.remove("dragging-tab");
+
+ if (!this._tabAnimatedClosedSinceMouseEnter) {
+ this.element.classList.remove("static-layout");
+ this._clearTabBarItemSizesAndPositions();
+ } else {
+ let left = 0;
+ for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
+ if (tabBarItem === this._selectedTabBarItem)
+ tabBarItem.element.style.left = left + "px";
+ left += parseFloat(tabBarItem.element.style.width);
+ }
+ }
+
+ this._mouseIsDown = false;
+ this._mouseOffset = undefined;
+
+ document.removeEventListener("mousemove", this._mouseMovedEventListener, true);
+ document.removeEventListener("mouseup", this._mouseUpEventListener, true);
+
+ this._mouseMovedEventListener = null;
+ this._mouseUpEventListener = null;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemsReordered);
+ }
+
+ _handleMouseLeave(event)
+ {
+ if (this._mouseIsDown || !this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
+ return;
+
+ // This event can still fire when the mouse is inside the element if DOM nodes are added, removed or generally change inside.
+ // Check if the mouse really did leave the element by checking the bounds.
+ // FIXME: Is this a WebKit bug or correct behavior?
+ const barRect = this.element.getBoundingClientRect();
+ const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
+ if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
+ return;
+
+ this._finishExpandingTabsAfterClose();
+ }
+
+ _handleContextMenu(event)
+ {
+ let contextMenu = WI.ContextMenu.createFromEvent(event);
+
+ for (let tabClass of WI.knownTabClasses()) {
+ if (tabClass.tabInfo().isEphemeral)
+ continue;
+
+ let openTabBarItem = null;
+ for (let tabBarItem of this._tabBarItems) {
+ let tabContentView = tabBarItem.representedObject;
+ if (!(tabContentView instanceof WI.TabContentView))
+ continue;
+
+ if (tabContentView.type === tabClass.Type) {
+ openTabBarItem = tabBarItem;
+ break;
+ }
+ }
+
+ contextMenu.appendCheckboxItem(tabClass.tabInfo().title, () => {
+ if (openTabBarItem)
+ this.removeTabBarItem(openTabBarItem);
+ else
+ WI.createNewTabWithType(tabClass.Type, {shouldShowNewTab: true});
+ }, !!openTabBarItem);
+ }
+ }
+
+ _handleNewTabClick(event)
+ {
+ WI.showNewTabTab();
+ }
+
+ _handleTabPickerTabContextMenu(event)
+ {
+ if (!this._hiddenTabBarItems.length)
+ return;
+
+ let contextMenu = WI.ContextMenu.createFromEvent(event);
+ for (let item of this._hiddenTabBarItems) {
+ contextMenu.appendItem(item.title, () => {
+ this.selectedTabBarItem = item;
+ });
+ }
+ }
+
+ _handleNewTabMouseEnter(event)
+ {
+ if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
+ return;
+
+ this._finishExpandingTabsAfterClose();
+ }
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.NetworkTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.NetworkTabContentView.tabInfo());
super(identifier || "network", "network", tabBarItem);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NewTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/NewTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NewTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.NewTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.NewTabContentView.tabInfo());
tabBarItem.isDefaultTab = true;
super(identifier || "new-tab", "new-tab", tabBarItem);
@@ -35,7 +35,7 @@
this._tabElementsByTabClass = new Map;
let allTabClasses = Array.from(WI.knownTabClasses());
- this._shownTabClasses = allTabClasses.filter((tabClass) => tabClass.isTabAllowed() && !tabClass.isEphemeral());
+ this._shownTabClasses = allTabClasses.filter((tabClass) => tabClass.isTabAllowed() && !tabClass.tabInfo().isEphemeral);
this._shownTabClasses.sort((a, b) => a.tabInfo().title.extendedLocaleCompare(b.tabInfo().title));
}
@@ -44,14 +44,10 @@
return {
image: "Images/NewTab.svg",
title: WI.UIString("New Tab"),
+ isEphemeral: true,
};
}
- static isEphemeral()
- {
- return true;
- }
-
static shouldSaveTab()
{
return false;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ResourcesTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.ResourcesTabContentView.tabInfo());
const detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
super(identifier || "resources", "resources", tabBarItem, WI.ResourceSidebarPanel, detailsSidebarPanelConstructors);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SearchTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SearchTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SearchTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.SearchTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.SearchTabContentView.tabInfo());
let detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel,
WI.DOMNodeDetailsSidebarPanel, WI.ComputedStyleDetailsSidebarPanel, WI.RulesStyleDetailsSidebarPanel];
@@ -44,14 +44,10 @@
return {
image: "Images/SearchResults.svg",
title: WI.UIString("Search"),
+ isEphemeral: true,
};
}
- static isEphemeral()
- {
- return true;
- }
-
// Public
get type()
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -44,14 +44,10 @@
return {
image: "Images/Gear.svg",
title: WI.UIString("Settings"),
+ isEphemeral: true,
};
}
- static isEphemeral()
- {
- return true;
- }
-
static shouldSaveTab()
{
return false;
@@ -254,6 +250,9 @@
experimentalSettingsView.addSeparator();
}
+ experimentalSettingsView.addSetting(WI.UIString("User Interface:"), WI.settings.experimentalEnableNewTabBar, WI.UIString("Enable New Tab Bar"));
+ experimentalSettingsView.addSeparator();
+
let reloadInspectorButton = document.createElement("button");
reloadInspectorButton.textContent = WI.UIString("Reload Web Inspector");
reloadInspectorButton.addEventListener("click", () => { window.location.reload(); });
@@ -271,6 +270,7 @@
listenForChange(WI.settings.experimentalLegacyStyleEditor);
listenForChange(WI.settings.experimentalLegacyVisualSidebar);
listenForChange(WI.settings.experimentalEnableLayersTab);
+ listenForChange(WI.settings.experimentalEnableNewTabBar);
this.addSettingsView(experimentalSettingsView);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/StorageTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/StorageTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/StorageTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.StorageTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.StorageTabContentView.tabInfo());
let detailsSidebarPanelConstructors = [WI.ApplicationCacheDetailsSidebarPanel, WI.IndexedDatabaseDetailsSidebarPanel];
super(identifier || "storage", "storage", tabBarItem, WI.StorageSidebarPanel, detailsSidebarPanelConstructors);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.css (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.css 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.css 2018-02-02 21:04:52 UTC (rev 228024)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabBar.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -48,11 +48,6 @@
this.addTabBarItem(WI.settingsTabContentView.tabBarItem, {suppressAnimations: true});
- this._newTabTabBarItem = new WI.PinnedTabBarItem("Images/NewTabPlus.svg", WI.UIString("Create a new tab"));
- this._newTabTabBarItem.element.addEventListener("mouseenter", this._handleNewTabMouseEnter.bind(this));
- this._newTabTabBarItem.element.addEventListener("click", this._handleNewTabClick.bind(this));
- this.addTabBarItem(this._newTabTabBarItem, {suppressAnimations: true});
-
this._tabPickerTabBarItem = new WI.PinnedTabBarItem("Images/TabPicker.svg", WI.UIString("Show hidden tabs"));
this._tabPickerTabBarItem.element.classList.add("tab-picker");
this._tabPickerTabBarItem.element.addEventListener("contextmenu", this._handleTabPickerTabContextMenu.bind(this));
@@ -61,14 +56,6 @@
// Public
- get newTabTabBarItem() { return this._newTabTabBarItem; }
-
- updateNewTabTabBarItemState()
- {
- let newTabExists = !WI.isNewTabWithTypeAllowed(WI.NewTabContentView.Type);
- this._newTabTabBarItem.disabled = newTabExists;
- }
-
addTabBarItem(tabBarItem, options = {})
{
return this.insertTabBarItem(tabBarItem, this._tabBarItems.length, options);
@@ -170,9 +157,6 @@
} else
this.needsLayout();
- if (!(tabBarItem instanceof WI.PinnedTabBarItem))
- this.updateNewTabTabBarItemState();
-
this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemAdded, {tabBarItem});
return tabBarItem;
@@ -184,6 +168,9 @@
if (!tabBarItem || tabBarItem instanceof WI.PinnedTabBarItem)
return null;
+ if (!tabBarItem.isEphemeral && this.normalNonEphemeralTabCount === 1)
+ return null;
+
tabBarItem.parentTabBar = null;
if (this._selectedTabBarItem === tabBarItem) {
@@ -213,10 +200,6 @@
var hasMoreThanOneNormalTab = this._hasMoreThanOneNormalTab();
this.element.classList.toggle("single-tab", !hasMoreThanOneNormalTab);
- const shouldOpenDefaultTab = !tabBarItem.isDefaultTab && !this.normalTabCount;
- if (shouldOpenDefaultTab)
- options.suppressAnimations = true;
-
if (!hasMoreThanOneNormalTab || wasLastNormalTab || !options.suppressExpansion) {
if (!options.suppressAnimations) {
this._tabAnimatedClosedSinceMouseEnter = true;
@@ -224,13 +207,7 @@
} else
this.needsLayout();
- this.updateNewTabTabBarItemState();
-
this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
-
- if (shouldOpenDefaultTab)
- this._openDefaultTab();
-
return tabBarItem;
}
@@ -300,13 +277,8 @@
} else
this.needsLayout();
- this.updateNewTabTabBarItemState();
-
this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemRemoved, {tabBarItem});
- if (shouldOpenDefaultTab)
- this._openDefaultTab();
-
return tabBarItem;
}
@@ -364,11 +336,6 @@
set selectedTabBarItem(tabBarItemOrIndex)
{
let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
- if (tabBarItem === this._newTabTabBarItem) {
- // Get the item before the New-Tab item since it is not selectable.
- tabBarItem = this._tabBarItems[this.normalTabCount - 1];
- }
-
if (this._selectedTabBarItem === tabBarItem)
return;
@@ -396,6 +363,11 @@
return this._tabBarItems.filter((item) => !(item instanceof WI.PinnedTabBarItem)).length;
}
+ get normalNonEphemeralTabCount()
+ {
+ return this._tabBarItems.filter((item) => !item.isEphemeral && !(item instanceof WI.PinnedTabBarItem)).length;
+ }
+
// Protected
layout()
@@ -491,11 +463,6 @@
return false;
}
- _openDefaultTab()
- {
- this.dispatchEventToListeners(WI.TabBar.Event.OpenDefaultTab);
- }
-
_recordTabBarItemSizesAndPositions()
{
var tabBarItemSizesAndPositions = new Map;
@@ -596,9 +563,6 @@
if (tabBarItem.disabled)
return;
- if (tabBarItem === this._newTabTabBarItem)
- return;
-
if (tabBarItem === this._tabPickerTabBarItem) {
if (!this._hiddenTabBarItems.length)
return;
@@ -659,8 +623,8 @@
var closeButtonElement = event.target.enclosingNodeOrSelfWithClass(WI.TabBarItem.CloseButtonStyleClassName);
if (closeButtonElement || clickedMiddleButton) {
- // Disallow closing the default tab if it is the only tab.
- if (tabBarItem.isDefaultTab && this.element.classList.contains("single-tab"))
+ // Disallow closing the only tab.
+ if (this.element.classList.contains("single-tab"))
return;
if (!event.altKey) {
@@ -733,7 +697,7 @@
this._tabBarItems.splice(newIndex, 0, this._selectedTabBarItem);
let nextSibling = this._tabBarItems[newIndex + 1];
- let nextSiblingElement = nextSibling ? nextSibling.element : this._newTabTabBarItem.element;
+ let nextSiblingElement = nextSibling ? nextSibling.element : null;
this.element.insertBefore(this._selectedTabBarItem.element, nextSiblingElement);
@@ -741,7 +705,7 @@
let left = 0;
for (let tabBarItem of this._tabBarItemsFromLeftToRight()) {
- if (tabBarItem !== this._selectedTabBarItem && tabBarItem !== this._newTabTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
+ if (tabBarItem !== this._selectedTabBarItem && parseFloat(tabBarItem.element.style.left) !== left)
tabBarItem.element.style.left = left + "px";
left += parseFloat(tabBarItem.element.style.width);
}
@@ -792,8 +756,7 @@
// Check if the mouse really did leave the element by checking the bounds.
// FIXME: Is this a WebKit bug or correct behavior?
const barRect = this.element.getBoundingClientRect();
- const newTabItemRect = this._newTabTabBarItem.element.getBoundingClientRect();
- if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < (newTabItemRect ? newTabItemRect.right : barRect.right))
+ if (event.pageY > barRect.top && event.pageY < barRect.bottom && event.pageX > barRect.left && event.pageX < barRect.right)
return;
this._finishExpandingTabsAfterClose();
@@ -804,7 +767,7 @@
let contextMenu = WI.ContextMenu.createFromEvent(event);
for (let tabClass of WI.knownTabClasses()) {
- if (tabClass.isEphemeral())
+ if (tabClass.tabInfo().isEphemeral)
continue;
let openTabBarItem = null;
@@ -819,20 +782,17 @@
}
}
+ let checked = !!openTabBarItem;
+ let disabled = checked && this.normalNonEphemeralTabCount === 1;
contextMenu.appendCheckboxItem(tabClass.tabInfo().title, () => {
if (openTabBarItem)
this.removeTabBarItem(openTabBarItem);
else
WI.createNewTabWithType(tabClass.Type, {shouldShowNewTab: true});
- }, !!openTabBarItem);
+ }, checked, disabled);
}
}
- _handleNewTabClick(event)
- {
- WI.showNewTabTab();
- }
-
_handleTabPickerTabContextMenu(event)
{
if (!this._hiddenTabBarItems.length)
@@ -845,14 +805,6 @@
});
}
}
-
- _handleNewTabMouseEnter(event)
- {
- if (!this._tabAnimatedClosedSinceMouseEnter || !this.element.classList.contains("static-layout") || this.element.classList.contains("animating"))
- return;
-
- this._finishExpandingTabsAfterClose();
- }
};
WI.TabBar.CachedWidthSymbol = Symbol("cached-width");
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -283,7 +283,7 @@
this._recentTabContentViews.remove(tabContentView);
- if (!tabContentView.constructor.isEphemeral())
+ if (!tabContentView.constructor.tabInfo().isEphemeral)
this._closedTabClasses.add(tabContentView.constructor);
this._contentViewContainer.closeContentView(tabContentView);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -65,12 +65,6 @@
return true;
}
- static isEphemeral()
- {
- // Returns true if the tab should not be shown in the new tab content view.
- return false;
- }
-
static shouldSaveTab()
{
// Returns false if the tab should not be restored when re-opening the Inspector.
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js (228023 => 228024)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js 2018-02-02 20:30:23 UTC (rev 228023)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js 2018-02-02 21:04:52 UTC (rev 228024)
@@ -27,7 +27,7 @@
{
constructor(identifier)
{
- let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.TimelineTabContentView);
+ let tabBarItem = WI.GeneralTabBarItem.fromTabInfo(WI.TimelineTabContentView.tabInfo());
let detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
super(identifier || "timeline", "timeline", tabBarItem, null, detailsSidebarPanelConstructors);