Web Inspector: TabBar redesign: improvements to tab layout and resize behavior
authormattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 27 Jan 2018 00:49:10 +0000 (00:49 +0000)
committermattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 27 Jan 2018 00:49:10 +0000 (00:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181468
<rdar://problem/36395439>

Reviewed by Devin Rousso.

* Localizations/en.lproj/localizedStrings.js:

* UserInterface/Images/TabPicker.svg: Added.
New ">>" icon for the tab picker button.

* 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.fromTabContentViewConstructor):
(WI.GeneralTabBarItem.prototype.get title):
Add missing override for getter/setter pair.
(WI.GeneralTabBarItem.prototype.set title):
(WI.GeneralTabBarItem.prototype._handleContextMenuEvent):
Show the close button on ephemeral tabs only (Search, New Tab).
Replace unused `representedObject` parameter with `isEphemeral`, which
determines whether to show a close button for the tab.

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

* UserInterface/Views/NavigationBar.js:
Remove unused symbol.

* UserInterface/Views/NetworkTabContentView.js:
(WI.NetworkTabContentView):
* UserInterface/Views/NewTabContentView.js:
(WI.NewTabContentView):

* UserInterface/Views/PinnedTabBarItem.js:
(WI.PinnedTabBarItem):
Remove unused parameter.

* UserInterface/Views/ResourcesTabContentView.js:
(WI.ResourcesTabContentView):
* UserInterface/Views/SearchTabContentView.js:
(WI.SearchTabContentView):
* UserInterface/Views/StorageTabContentView.js:
(WI.StorageTabContentView):

* UserInterface/Views/TabBar.css:
(.tab-bar > .item):
(.tab-bar.calculate-width > .item):
(.tab-bar > .item.pinned.tab-picker):
(.tab-bar > .item > .close):
(.tab-bar > .item > .title):
(.tab-bar:not(.collapsed) > .item > .title):
(.tab-bar.collapsed > .item:not(.pinned) > .icon):
(.tab-bar > .item:hover > .close):
(.tab-bar.collapsed > .item:hover > .close):
(.tab-bar:not(.collapsed) > .item.ephemeral:hover > .icon):
(.tab-bar.collapsed > .item.ephemeral:hover > .title):
(body[dir=ltr] .tab-bar > .item > .close): Deleted.
(body[dir=rtl] .tab-bar > .item > .close): Deleted.
(.tab-bar > .item > .flex-space): Deleted.
(.tab-bar > .item:not(.pinned) > .flex-space:last-child): Deleted.
(body[dir=ltr] .tab-bar > .item:not(.pinned) > .flex-space:last-child): Deleted.
(body[dir=rtl] .tab-bar > .item:not(.pinned) > .flex-space:last-child): Deleted.
(body[dir=ltr] .tab-bar > .item > .title): Deleted.
(body[dir=rtl] .tab-bar > .item > .title): Deleted.
(.tab-bar.collapsed > .item): Deleted.
(.tab-bar.collapsed > .item > .flex-space): Deleted.
(.tab-bar.collapsed > .item > .close): Deleted.
(body[dir=ltr] .tab-bar.collapsed > .item > .close): Deleted.
(body[dir=rtl] .tab-bar.collapsed > .item > .close): Deleted.
(.tab-bar.hide-titles > .item > .title): Deleted.
(.tab-bar.collapsed:not(.hide-titles) > .item:not(.pinned):hover > .icon,): Deleted.
(.tab-bar.collapsed:not(.hide-titles) > .item:hover > .close,): Deleted.
Clean up tab styles and prevent tabs from shrinking during flex layout.
Added new `calculate-width` class, to disable flex layout when measuring
the minimum width of the TabBar required to fit all tab items.

* UserInterface/Views/TabBar.js:
(WI.TabBar):
(WI.TabBar.prototype.set selectedTabBarItem):
(WI.TabBar.prototype.layout.forceItemHidden):
(WI.TabBar.prototype.layout):
Perform two layout passes, similar to NavigationBar. The first pass disables
flex layout and measures tab items at full size. If the bar isn't wide enough
to show all the tabs, hide their icons and measure again. If there still isn't
room, hide tabs starting from the end of the bar and display the tab picker.

(WI.TabBar.prototype._handleMouseDown):
(WI.TabBar.prototype._handleTabPickerTabContextMenu):

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

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

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

20 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Images/TabPicker.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/CanvasTabContentView.js
Source/WebInspectorUI/UserInterface/Views/ConsoleTabContentView.js
Source/WebInspectorUI/UserInterface/Views/DebuggerTabContentView.js
Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js
Source/WebInspectorUI/UserInterface/Views/GeneralTabBarItem.js
Source/WebInspectorUI/UserInterface/Views/LayersTabContentView.js
Source/WebInspectorUI/UserInterface/Views/NavigationBar.js
Source/WebInspectorUI/UserInterface/Views/NetworkTabContentView.js
Source/WebInspectorUI/UserInterface/Views/NewTabContentView.js
Source/WebInspectorUI/UserInterface/Views/PinnedTabBarItem.js
Source/WebInspectorUI/UserInterface/Views/ResourcesTabContentView.js
Source/WebInspectorUI/UserInterface/Views/SearchTabContentView.js
Source/WebInspectorUI/UserInterface/Views/StorageTabContentView.js
Source/WebInspectorUI/UserInterface/Views/TabBar.css
Source/WebInspectorUI/UserInterface/Views/TabBar.js
Source/WebInspectorUI/UserInterface/Views/TabBarItem.js
Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js

index 89d1211..97ed392 100644 (file)
@@ -1,3 +1,109 @@
+2018-01-26  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: TabBar redesign: improvements to tab layout and resize behavior
+        https://bugs.webkit.org/show_bug.cgi?id=181468
+        <rdar://problem/36395439>
+
+        Reviewed by Devin Rousso.
+
+        * Localizations/en.lproj/localizedStrings.js:
+
+        * UserInterface/Images/TabPicker.svg: Added.
+        New ">>" icon for the tab picker button.
+
+        * 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.fromTabContentViewConstructor):
+        (WI.GeneralTabBarItem.prototype.get title):
+        Add missing override for getter/setter pair.
+        (WI.GeneralTabBarItem.prototype.set title):
+        (WI.GeneralTabBarItem.prototype._handleContextMenuEvent):
+        Show the close button on ephemeral tabs only (Search, New Tab).
+        Replace unused `representedObject` parameter with `isEphemeral`, which
+        determines whether to show a close button for the tab.
+
+        * UserInterface/Views/LayersTabContentView.js:
+        (WI.LayersTabContentView):
+
+        * UserInterface/Views/NavigationBar.js:
+        Remove unused symbol.
+
+        * UserInterface/Views/NetworkTabContentView.js:
+        (WI.NetworkTabContentView):
+        * UserInterface/Views/NewTabContentView.js:
+        (WI.NewTabContentView):
+
+        * UserInterface/Views/PinnedTabBarItem.js:
+        (WI.PinnedTabBarItem):
+        Remove unused parameter.
+
+        * UserInterface/Views/ResourcesTabContentView.js:
+        (WI.ResourcesTabContentView):
+        * UserInterface/Views/SearchTabContentView.js:
+        (WI.SearchTabContentView):
+        * UserInterface/Views/StorageTabContentView.js:
+        (WI.StorageTabContentView):
+
+        * UserInterface/Views/TabBar.css:
+        (.tab-bar > .item):
+        (.tab-bar.calculate-width > .item):
+        (.tab-bar > .item.pinned.tab-picker):
+        (.tab-bar > .item > .close):
+        (.tab-bar > .item > .title):
+        (.tab-bar:not(.collapsed) > .item > .title):
+        (.tab-bar.collapsed > .item:not(.pinned) > .icon):
+        (.tab-bar > .item:hover > .close):
+        (.tab-bar.collapsed > .item:hover > .close):
+        (.tab-bar:not(.collapsed) > .item.ephemeral:hover > .icon):
+        (.tab-bar.collapsed > .item.ephemeral:hover > .title):
+        (body[dir=ltr] .tab-bar > .item > .close): Deleted.
+        (body[dir=rtl] .tab-bar > .item > .close): Deleted.
+        (.tab-bar > .item > .flex-space): Deleted.
+        (.tab-bar > .item:not(.pinned) > .flex-space:last-child): Deleted.
+        (body[dir=ltr] .tab-bar > .item:not(.pinned) > .flex-space:last-child): Deleted.
+        (body[dir=rtl] .tab-bar > .item:not(.pinned) > .flex-space:last-child): Deleted.
+        (body[dir=ltr] .tab-bar > .item > .title): Deleted.
+        (body[dir=rtl] .tab-bar > .item > .title): Deleted.
+        (.tab-bar.collapsed > .item): Deleted.
+        (.tab-bar.collapsed > .item > .flex-space): Deleted.
+        (.tab-bar.collapsed > .item > .close): Deleted.
+        (body[dir=ltr] .tab-bar.collapsed > .item > .close): Deleted.
+        (body[dir=rtl] .tab-bar.collapsed > .item > .close): Deleted.
+        (.tab-bar.hide-titles > .item > .title): Deleted.
+        (.tab-bar.collapsed:not(.hide-titles) > .item:not(.pinned):hover > .icon,): Deleted.
+        (.tab-bar.collapsed:not(.hide-titles) > .item:hover > .close,): Deleted.
+        Clean up tab styles and prevent tabs from shrinking during flex layout.
+        Added new `calculate-width` class, to disable flex layout when measuring
+        the minimum width of the TabBar required to fit all tab items.
+
+        * UserInterface/Views/TabBar.js:
+        (WI.TabBar):
+        (WI.TabBar.prototype.set selectedTabBarItem):
+        (WI.TabBar.prototype.layout.forceItemHidden):
+        (WI.TabBar.prototype.layout):
+        Perform two layout passes, similar to NavigationBar. The first pass disables
+        flex layout and measures tab items at full size. If the bar isn't wide enough
+        to show all the tabs, hide their icons and measure again. If there still isn't
+        room, hide tabs starting from the end of the bar and display the tab picker.
+
+        (WI.TabBar.prototype._handleMouseDown):
+        (WI.TabBar.prototype._handleTabPickerTabContextMenu):
+
+        * UserInterface/Views/TabBarItem.js:
+        (WI.TabBarItem):
+
+        * UserInterface/Views/TimelineTabContentView.js:
+        (WI.TimelineTabContentView):
+
 2018-01-25  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Network Table: Sort indicator is not displayed when sorted column is hidden and re-shown
index 0fb3335..6ef9001 100644 (file)
@@ -198,14 +198,13 @@ localizedStrings["Clear object store"] = "Clear object store";
 localizedStrings["Clear samples"] = "Clear samples";
 localizedStrings["Clear watch expressions"] = "Clear watch expressions";
 localizedStrings["Click Listener"] = "Click Listener";
-localizedStrings["Click to close this tab; Option-click to close all tabs except this one"] = "Click to close this tab; Option-click to close all tabs except this one";
+localizedStrings["Click to close this tab"] = "Click to close this tab";
 localizedStrings["Click to link property values"] = "Click to link property values";
 localizedStrings["Click to select a color. Shift-click to switch color formats."] = "Click to select a color. Shift-click to switch color formats.";
 localizedStrings["Clickable"] = "Clickable";
 localizedStrings["Clip"] = "Clip";
 localizedStrings["Close"] = "Close";
 localizedStrings["Close %s timeline view"] = "Close %s timeline view";
-localizedStrings["Close Other Tabs"] = "Close Other Tabs";
 localizedStrings["Close Tab"] = "Close Tab";
 localizedStrings["Close detail view"] = "Close detail view";
 localizedStrings["Closed"] = "Closed";
@@ -732,7 +731,7 @@ localizedStrings["Radius Y"] = "Radius Y";
 localizedStrings["Range Issue"] = "Range Issue";
 localizedStrings["Readonly"] = "Readonly";
 localizedStrings["Reasons for compositing"] = "Reasons for compositing";
-localizedStrings["Recently Closed Tabs"] = "Recently Closed Tabs";
+localizedStrings["Reasons for compositing:"] = "Reasons for compositing:";
 localizedStrings["Recording"] = "Recording";
 localizedStrings["Recording %d"] = "Recording %d";
 localizedStrings["Recording Timeline Data"] = "Recording Timeline Data";
@@ -862,6 +861,7 @@ localizedStrings["Show all actions"] = "Show all actions";
 localizedStrings["Show all resources"] = "Show all resources";
 localizedStrings["Show compositing borders"] = "Show compositing borders";
 localizedStrings["Show errors logged to the Console"] = "Show errors logged to the Console";
+localizedStrings["Show hidden tabs"] = "Show hidden tabs";
 localizedStrings["Show messages logged to the Console"] = "Show messages logged to the Console";
 localizedStrings["Show network information"] = "Show network information";
 localizedStrings["Show page load timing"] = "Show page load timing";
diff --git a/Source/WebInspectorUI/UserInterface/Images/TabPicker.svg b/Source/WebInspectorUI/UserInterface/Images/TabPicker.svg
new file mode 100644 (file)
index 0000000..d61ed1d
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2018 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 13 16">
+    <path fill="none" stroke="currentColor" d="M 2 4 L 6 8 L 2 12"/>
+    <path fill="none" stroke="currentColor" d="M 7 4 L 11 8 L 7 12"/>
+</svg>
index 8d0c4e5..c4dea07 100644 (file)
@@ -29,8 +29,7 @@ WI.CanvasTabContentView = class CanvasTabContentView extends WI.ContentBrowserTa
     {
         console.assert(!representedObject || representedObject instanceof WI.Canvas);
 
-        let {image, title} = WI.CanvasTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.CanvasTabContentView);
 
         const navigationSidebarPanelConstructor = WI.RecordingNavigationSidebarPanel;
         const detailsSidebarPanelConstructors = [WI.RecordingStateDetailsSidebarPanel, WI.RecordingTraceDetailsSidebarPanel, WI.CanvasDetailsSidebarPanel];
index c8594d8..84d3e38 100644 (file)
@@ -27,8 +27,7 @@ WI.ConsoleTabContentView = class ConsoleTabContentView extends WI.ContentBrowser
 {
     constructor(identifier)
     {
-        let {image, title} = WI.ConsoleTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ConsoleTabContentView);
 
         super(identifier || "console", "console", tabBarItem, null, null, true);
     }
index 3cb76a6..ede7d2e 100644 (file)
@@ -27,8 +27,7 @@ WI.DebuggerTabContentView = class DebuggerTabContentView extends WI.ContentBrows
 {
     constructor(identifier)
     {
-        let {image, title} = WI.DebuggerTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.DebuggerTabContentView);
         let detailsSidebarPanelConstructors = [WI.ScopeChainDetailsSidebarPanel, WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
 
         super(identifier || "debugger", "debugger", tabBarItem, WI.DebuggerSidebarPanel, detailsSidebarPanelConstructors);
index f403194..e04de37 100644 (file)
@@ -27,8 +27,7 @@ WI.ElementsTabContentView = class ElementsTabContentView extends WI.ContentBrows
 {
     constructor(identifier)
     {
-        let {image, title} = WI.ElementsTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ElementsTabContentView);
         let detailsSidebarPanelConstructors = [WI.RulesStyleDetailsSidebarPanel, WI.ComputedStyleDetailsSidebarPanel, WI.DOMNodeDetailsSidebarPanel];
 
         if (window.LayerTreeAgent && !WI.settings.experimentalEnableLayersTab.value)
index 16d6043..fd05dbd 100644 (file)
 
 WI.GeneralTabBarItem = class GeneralTabBarItem extends WI.TabBarItem
 {
-    constructor(image, title, representedObject)
+    constructor(image, title, isEphemeral)
     {
-        super(image, title, representedObject);
+        super(image, title);
 
-        let closeButtonElement = document.createElement("div");
-        closeButtonElement.classList.add(WI.TabBarItem.CloseButtonStyleClassName);
-        closeButtonElement.title = WI.UIString("Click to close this tab; Option-click to close all tabs except this one");
-        this.element.insertBefore(closeButtonElement, this.element.firstChild);
+        if (isEphemeral) {
+            this.element.classList.add("ephemeral");
+
+            let closeButtonElement = document.createElement("div");
+            closeButtonElement.classList.add(WI.TabBarItem.CloseButtonStyleClassName);
+            closeButtonElement.title = WI.UIString("Click to close this tab");
+
+            this.element.insertBefore(closeButtonElement, this.element.firstChild);
+            this.element.addEventListener("contextmenu", this._handleContextMenuEvent.bind(this));
+        }
+    }
+
+    static fromTabContentViewConstructor(constructor)
+    {
+        let {image, title} = constructor.tabInfo();
+        let isEphemeral = constructor.isEphemeral();
+        return new WI.GeneralTabBarItem(image, title, isEphemeral);
     }
 
     // Public
 
+    get title()
+    {
+        return super.title;
+    }
+
     set title(title)
     {
         if (title) {
@@ -49,7 +67,7 @@ WI.GeneralTabBarItem = class GeneralTabBarItem extends WI.TabBarItem
             titleContentElement.textContent = title;
             this._titleElement.appendChild(titleContentElement);
 
-            this.element.insertBefore(this._titleElement, this.element.lastChild);
+            this.element.appendChild(this._titleElement);
         } else {
             if (this._titleElement)
                 this._titleElement.remove();
@@ -59,4 +77,18 @@ WI.GeneralTabBarItem = class GeneralTabBarItem extends WI.TabBarItem
 
         super.title = title;
     }
+
+    // Private
+
+    _handleContextMenuEvent(event)
+    {
+        if (!this._parentTabBar)
+            return;
+
+        let contextMenu = WI.ContextMenu.createFromEvent(event);
+        contextMenu.appendItem(WI.UIString("Close Tab"), () => {
+            this._parentTabBar.removeTabBarItem(this);
+        }, this.isDefaultTab);
+        contextMenu.appendSeparator();
+    }
 };
index 436f951..6d26d4c 100644 (file)
@@ -27,8 +27,7 @@ WI.LayersTabContentView = class LayersTabContentView extends WI.ContentBrowserTa
 {
     constructor()
     {
-        let {image, title} = WI.LayersTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.LayersTabContentView);
 
         const navigationSidebarPanelConstructor = null;
         const detailsSidebarPanelConstructors = [WI.LayerDetailsSidebarPanel];
index 34da24d..d7226d6 100644 (file)
@@ -452,7 +452,6 @@ WI.NavigationBar = class NavigationBar extends WI.View
     }
 };
 
-WI.NavigationBar.CachedWidthSymbol = Symbol("cached-width");
 WI.NavigationBar.ForceHiddenSymbol = Symbol("force-hidden");
 
 WI.NavigationBar.CollapsedStyleClassName = "collapsed";
index 09e4dc1..75f5399 100644 (file)
@@ -27,8 +27,7 @@ WI.NetworkTabContentView = class NetworkTabContentView extends WI.TabContentView
 {
     constructor(identifier)
     {
-        let {image, title} = WI.NetworkTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.NetworkTabContentView);
 
         super(identifier || "network", "network", tabBarItem);
 
index 5f12736..effbe56 100644 (file)
@@ -27,8 +27,7 @@ WI.NewTabContentView = class NewTabContentView extends WI.TabContentView
 {
     constructor(identifier)
     {
-        let {image, title} = WI.NewTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.NewTabContentView);
         tabBarItem.isDefaultTab = true;
 
         super(identifier || "new-tab", "new-tab", tabBarItem);
index f74c420..8d397ea 100644 (file)
@@ -25,9 +25,9 @@
 
 WI.PinnedTabBarItem = class PinnedTabBarItem extends WI.TabBarItem
 {
-    constructor(image, title, representedObject)
+    constructor(image, title)
     {
-        super(image, title, representedObject);
+        super(image, title);
 
         this.element.classList.add("pinned");
 
index 3314c64..b0822da 100644 (file)
@@ -27,8 +27,7 @@ WI.ResourcesTabContentView = class ResourcesTabContentView extends WI.ContentBro
 {
     constructor(identifier)
     {
-        let {image, title} = WI.ResourcesTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.ResourcesTabContentView);
         const detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
         super(identifier || "resources", "resources", tabBarItem, WI.ResourceSidebarPanel, detailsSidebarPanelConstructors);
     }
index b276652..d0eca6d 100644 (file)
@@ -27,8 +27,7 @@ WI.SearchTabContentView = class SearchTabContentView extends WI.ContentBrowserTa
 {
     constructor(identifier)
     {
-        let {image, title} = WI.SearchTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.SearchTabContentView);
         let detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel,
             WI.DOMNodeDetailsSidebarPanel, WI.ComputedStyleDetailsSidebarPanel, WI.RulesStyleDetailsSidebarPanel];
 
index ccc13ce..0f1369c 100644 (file)
@@ -27,8 +27,7 @@ WI.StorageTabContentView = class StorageTabContentView extends WI.ContentBrowser
 {
     constructor(identifier)
     {
-        let {image, title} = WI.StorageTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.StorageTabContentView);
         let detailsSidebarPanelConstructors = [WI.ApplicationCacheDetailsSidebarPanel, WI.IndexedDatabaseDetailsSidebarPanel];
 
         super(identifier || "storage", "storage", tabBarItem, WI.StorageSidebarPanel, detailsSidebarPanelConstructors);
index 512bd07..aff0d51 100644 (file)
@@ -78,12 +78,13 @@ body.window-inactive .tab-bar > .top-border {
 
 .tab-bar > .item {
     display: flex;
-    flex: 1;
+    flex-grow: 1;
 
     position: relative;
 
     z-index: 1;
 
+    justify-content: center;
     align-items: center;
 
     padding: 0 6px;
@@ -106,6 +107,10 @@ body.window-inactive .tab-bar > .top-border {
     /* transition-delay: 50ms; */
 }
 
+.tab-bar.calculate-width > .item {
+    flex: initial;
+}
+
 body[dir=ltr] .tab-bar > :nth-child(n + 2 of .item),
 body[dir=ltr] .tab-bar.dragging-tab > .item.selected {
     border-left: var(--tab-item-medium-border-style);
@@ -122,6 +127,11 @@ body[dir=rtl] .tab-bar.dragging-tab > .item.selected {
     justify-content: center;
 }
 
+.tab-bar > .item.pinned.tab-picker {
+    width: 18px;
+    min-width: initial;
+}
+
 .tab-bar > .item:not(.disabled).selected {
     border-top-color: hsl(0, 0%, 74%);
     background-image: linear-gradient(to bottom, hsl(0, 0%, 87%), hsl(0, 0%, 82%));
@@ -182,16 +192,6 @@ body.window-inactive .tab-bar > .item.selected {
     transition-property: background-color, opacity;
     transition-duration: 250ms, 500ms;
     transition-delay: 0, 50ms;
-
-    --tab-item-close-button-margin-end: 4px;
-}
-
-body[dir=ltr] .tab-bar > .item > .close {
-    margin-right: var(--tab-item-close-button-margin-end);
-}
-
-body[dir=rtl] .tab-bar > .item > .close {
-    margin-left: var(--tab-item-close-button-margin-end);
 }
 
 body:not(.window-inactive) .tab-bar > .item:hover > .close {
@@ -214,23 +214,6 @@ body:not(.window-inactive) .tab-bar.single-tab > .item.default-tab:hover > .clos
     opacity: 0.8 !important;
 }
 
-.tab-bar > .item > .flex-space {
-    display: flex;
-    flex: 1;
-}
-
-.tab-bar > .item:not(.pinned) > .flex-space:last-child {
-    --tab-item-unpinned-trailing-flex-space-margin-end: 16px;
-}
-
-body[dir=ltr] .tab-bar > .item:not(.pinned) > .flex-space:last-child {
-    margin-right: var(--tab-item-unpinned-trailing-flex-space-margin-end);
-}
-
-body[dir=rtl] .tab-bar > .item:not(.pinned) > .flex-space:last-child {
-    margin-left: var(--tab-item-unpinned-trailing-flex-space-margin-end);
-}
-
 .tab-bar > .item > .icon {
     width: 16px;
     height: 16px;
@@ -269,16 +252,10 @@ body[dir=rtl] .tab-bar > .item:not(.pinned) > .flex-space:last-child {
 
     min-width: 0;
     max-width: 400px;
-
-    --tab-item-title-margin-start: 6px;
 }
 
-body[dir=ltr] .tab-bar > .item > .title {
-    margin-left: var(--tab-item-title-margin-start);
-}
-
-body[dir=rtl] .tab-bar > .item > .title {
-    margin-right: var(--tab-item-title-margin-start);
+.tab-bar:not(.collapsed) > .item > .title {
+    -webkit-margin-start: 6px;
 }
 
 .tab-bar > .item > .title > .content {
@@ -297,39 +274,29 @@ body[dir=rtl] .tab-bar > .item > .title {
     color: hsla(0, 0%, 0%, 0.7);
 }
 
-.tab-bar.collapsed > .item {
-    justify-content: center;
-}
-
-.tab-bar.collapsed > .item > .flex-space {
+.tab-bar.collapsed > .item:not(.pinned) > .icon {
     display: none;
 }
 
-.tab-bar.collapsed > .item > .close {
+.tab-bar > .item > .close {
     display: none;
-    --tab-item-close-button-margin-end: 0;
 }
 
-body[dir=ltr] .tab-bar.collapsed > .item > .close {
-    margin-right: var(--tab-item-close-button-margin-end);
+.tab-bar > .item:hover > .close {
+    display: inline-block;
 }
 
-body[dir=rtl] .tab-bar.collapsed > .item > .close {
-    margin-left: var(--tab-item-close-button-margin-end);
+.tab-bar.collapsed > .item:hover > .close {
+    position: absolute;
 }
 
-.tab-bar.hide-titles > .item > .title {
-    display: none;
-}
 
-.tab-bar.collapsed:not(.hide-titles) > .item:not(.pinned):hover > .icon,
-.tab-bar.hide-titles > .item.selected:hover > .icon {
+.tab-bar:not(.collapsed) > .item.ephemeral:hover > .icon {
     display: none;
 }
 
-.tab-bar.collapsed:not(.hide-titles) > .item:hover > .close,
-.tab-bar.hide-titles > .item.selected:hover > .close {
-    display: inline-block;
+.tab-bar.collapsed > .item.ephemeral:hover > .title {
+    visibility: hidden;
 }
 
 .tab-bar.static-layout {
index 42cdf86..6f8dc3b 100644 (file)
@@ -39,6 +39,7 @@ WI.TabBar = class TabBar extends WI.View
         this.element.createChild("div", "top-border");
 
         this._tabBarItems = [];
+        this._hiddenTabBarItems = [];
 
         if (tabBarItems) {
             for (let tabBarItem in tabBarItems)
@@ -51,6 +52,11 @@ WI.TabBar = class TabBar extends WI.View
         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
@@ -371,8 +377,11 @@ WI.TabBar = class TabBar extends WI.View
 
         this._selectedTabBarItem = tabBarItem || null;
 
-        if (this._selectedTabBarItem)
+        if (this._selectedTabBarItem) {
             this._selectedTabBarItem.selected = true;
+            if (this._selectedTabBarItem.element.classList.contains("hidden"))
+                this.needsLayout();
+        }
 
         this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected);
     }
@@ -394,30 +403,57 @@ WI.TabBar = class TabBar extends WI.View
         if (this.element.classList.contains("static-layout"))
             return;
 
-        this.element.classList.remove("hide-titles");
+        this.element.classList.add("calculate-width");
         this.element.classList.remove("collapsed");
 
-        let firstNormalTabItem = null;
-        for (let tabItem of this._tabBarItems) {
-            if (tabItem instanceof WI.PinnedTabBarItem)
-                continue;
+        function forceItemHidden(item, hidden) {
+            item.element.classList.toggle("hidden", !!hidden);
+        }
 
-            firstNormalTabItem = tabItem;
-            break;
+        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];
         }
 
-        if (!firstNormalTabItem)
-            return;
+        let recalculateItemWidths = () => {
+            return this._tabBarItems.reduce((total, item) => {
+                item[WI.TabBar.CachedWidthSymbol] = undefined;
+                return total + measureItemWidth(item);
+            }, 0);
+        };
 
-        if (firstNormalTabItem.element.offsetWidth >= 120)
-            return;
+        this._hiddenTabBarItems = [];
 
-        this.element.classList.add("collapsed");
+        let totalItemWidth = recalculateItemWidths();
+        let barWidth = this.element.realOffsetWidth;
 
-        if (firstNormalTabItem.element.offsetWidth >= 75)
-            return;
+        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.add("hide-titles");
+        this.element.classList.remove("calculate-width");
     }
 
     // Private
@@ -563,6 +599,18 @@ WI.TabBar = class TabBar extends WI.View
         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;
@@ -785,6 +833,19 @@ WI.TabBar = class TabBar extends WI.View
         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"))
@@ -794,6 +855,8 @@ WI.TabBar = class TabBar extends WI.View
     }
 };
 
+WI.TabBar.CachedWidthSymbol = Symbol("cached-width");
+
 WI.TabBar.Event = {
     TabBarItemSelected: "tab-bar-tab-bar-item-selected",
     TabBarItemAdded: "tab-bar-tab-bar-item-added",
index b095f84..728aaa8 100644 (file)
@@ -38,14 +38,10 @@ WI.TabBarItem = class TabBarItem extends WI.Object
         this._element.tabIndex = 0;
         this._element[WI.TabBarItem.ElementReferenceSymbol] = this;
 
-        this._element.createChild("div", "flex-space");
-
         this._iconElement = document.createElement("img");
         this._iconElement.classList.add("icon");
         this._element.appendChild(this._iconElement);
 
-        this._element.createChild("div", "flex-space");
-
         this.title = title;
         this.image = image;
         this.representedObject = representedObject;
index b7fe99b..7207ec3 100644 (file)
@@ -27,8 +27,7 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows
 {
     constructor(identifier)
     {
-        let {image, title} = WI.TimelineTabContentView.tabInfo();
-        let tabBarItem = new WI.GeneralTabBarItem(image, title);
+        let tabBarItem = WI.GeneralTabBarItem.fromTabContentViewConstructor(WI.TimelineTabContentView);
         let detailsSidebarPanelConstructors = [WI.ResourceDetailsSidebarPanel, WI.ProbeDetailsSidebarPanel];
 
         super(identifier || "timeline", "timeline", tabBarItem, null, detailsSidebarPanelConstructors);