Title: [268691] trunk/Source/WebInspectorUI
Revision
268691
Author
commit-qu...@webkit.org
Date
2020-10-19 15:00:41 -0700 (Mon, 19 Oct 2020)

Log Message

Web Inspector: Support three-column pane arrangement in Elements Tab
https://bugs.webkit.org/show_bug.cgi?id=217396

Patch by Patrick Angle <pan...@apple.com> on 2020-10-19
Reviewed by Devin Rousso.

The new multiple-sidebar layout for the Elements tab (and any other tab for which it is enabled in the future)
is achieved with a new WI.MultiSidebar, which is itself a WI.Sidebar and contains multiple new WI.SingleSidebar.
SingleSidebar replaces usage of the former Sidebar in favor of Sidebar being a common base shared between the
SingleSidebar and MultiSidebar.

A MultiSidebar supports showing multiple sidebars, and makes that decision based on the user having enabled
showing multiple sidebars and the `WI.SidebarPanel.prototype.allowExclusivePresentation` property on specific
SidebarPanels. The MultiSidebar manages these sidebars and their presentation, with some added properties to
manage the visibility of multiple sidebars. A MultiSidebar can display any number of sidebar panels
simultaneously. A MultiSidebar will always have a `primarySidebar` (although it may be collapsed), which is
where most panels will be presented. Additional panels that are marked as allowing exclusive presentation are
laid out in natural language order when the `MultiSidebar`'s side is `Leading` and reverse order when it is
`Trailing`. This allows the sidebar to remain conceptually anchored, so the `primarySidebar` in the set does not
change locations when adding or removing other sidebars from the set.

The presentation of more than the `primarySidebar` is dependant on the following conditions:
1. The tab (e.g. Elements Tab) must allow the three-panel layout.
2. At least one panel in the MultiSidebar must allow exclusive presentation (e.g. Styles when enabled in
Experimental Settings).
3. There must be at least one other panel to keep in the primary sidebar.
4. There must be enough available space horizontally to satisfy the minimum width of both the `primarySidebar`
and any sidebar panels that will be shown by themselves.

This feature is behind an Experimental Feature setting, and is disabled by default.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Main.js: Use new SingleSidebar and MultiSidebars, include each detail sidebar in maximum
width calculations, and listen for both width and collapse events.
* UserInterface/Base/Setting.js: Added setting for allowing Styles panel to be exclusive.
* UserInterface/Main.html:
* UserInterface/Views/ComputedStyleDetailsPanel.css: Hide shared elements between Styles and Computed when both
are visible.
(.multi-sidebar.showing-multiple > .sidebar > .panel.details.style-computed > .content > .pseudo-classes,):
* UserInterface/Views/ElementsTabContentView.js:
(WI.ElementsTabContentView.prototype.get allowMultipleDetailSidebars):
* UserInterface/Views/GeneralStyleDetailsSidebarPanel.css: Show pseudo-class rules as if they were a navigation
bar only when shown exclusively. Otherwise, still allow them to wrap like normal.
(.panel.exclusive-presentation.details.css-style > .content > .pseudo-classes):
(.panel.exclusive-presentation.details.css-style > .content > .pseudo-classes > .group):
(.panel.exclusive-presentation.details.css-style > .content > .rules):
(.sidebar > .panel.details.css-style.style-rules > .content ~ :matches(.options-container, .class-list-container)): Removed.
* UserInterface/Views/GeneralStyleDetailsSidebarPanel.js: If showing exclusively, prevent wrapping pseudo-class
rules by reporting their combined minimum width.
(WI.GeneralStyleDetailsSidebarPanel.prototype.get minimumWidth):
* UserInterface/Views/Main.css:
(#tab-browser): Match existing assumption for minimum tab-browser width.
(#details-sidebar): Match assumption for minimum sidebar width.
* UserInterface/Views/MultiSidebar.css: Added.
(.multi-sidebar):
(.multi-sidebar.trailing):
(.multi-sidebar.collapsed):
* UserInterface/Views/MultiSidebar.js: A Sidebar subclass that manages multiple SingleSidebars, and dynamically
presents them as space is available.
(WI.MultiSidebar):
(WI.MultiSidebar.prototype.get sidebars):
(WI.MultiSidebar.prototype.get primarySidebar):
(WI.MultiSidebar.prototype.get allowMultipleSidebars):
(WI.MultiSidebar.prototype.set allowMultipleSidebars):
(WI.MultiSidebar.prototype.get multipleSidebarsVisible):
(WI.MultiSidebar.prototype.set multipleSidebarsVisible):
(WI.MultiSidebar.prototype.addSidebar):
(WI.MultiSidebar.prototype.removeSidebar):
(WI.MultiSidebar.prototype.get selectedSidebarPanel):
(WI.MultiSidebar.prototype.set selectedSidebarPanel):
(WI.MultiSidebar.prototype.get collapsable):
(WI.MultiSidebar.prototype.set collapsable):
(WI.MultiSidebar.prototype.get minimumWidth):
(WI.MultiSidebar.prototype.get width):
(WI.MultiSidebar.prototype.didInsertSidebarPanel):
(WI.MultiSidebar.prototype.didRemoveSidebarPanel):
(WI.MultiSidebar.prototype.didSetCollapsed):
(WI.MultiSidebar.prototype.get _canShowMultipleSidebars):
(WI.MultiSidebar.prototype._updateMinimumWidthForMultipleSidebars):
(WI.MultiSidebar.prototype.get _hasWidthForMultipleSidebars):
(WI.MultiSidebar.prototype.get _hasSidebarPanelSupportingExclusive):
(WI.MultiSidebar.prototype._updateMultipleSidebarLayout):
(WI.MultiSidebar.prototype._canSidebarPanelBeExclusive):
(WI.MultiSidebar.prototype._makeSidebarPanelExclusive):
(WI.MultiSidebar.prototype._makeSidebarPanelNotExclusive):
(WI.MultiSidebar.prototype._nonExclusiveIndexOfSidebarPanel):
(WI.MultiSidebar.prototype._findSidebarForSidebarPanel):
(WI.MultiSidebar.prototype._addSidebarEventListeners):
(WI.MultiSidebar.prototype._removeSidebarEventListeners):
(WI.MultiSidebar.prototype._handleSidebarPanelSelected):
(WI.MultiSidebar.prototype._handleSidebarCollapsedStateDidChange):
(WI.MultiSidebar.prototype._handleSidebarWidthDidChange):
(WI.MultiSidebar.prototype._handleWindowResize):
* UserInterface/Views/RulesStyleDetailsSidebarPanel.js: If enabled in settings, allow Styles panel to be its own
sidebar.
(WI.RulesStyleDetailsSidebarPanel.prototype.get allowExclusivePresentation):
(WI.RulesStyleDetailsSidebarPanel):
* UserInterface/Views/SettingsTabContentView.js: Add setting for showing the Styles panel as its own sidebar.
(WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
* UserInterface/Views/Sidebar.css: Moved navigation and resizing to SingleSidebar.css.
(.sidebar):
(.sidebar.collapsed):
(.sidebar > .navigation-bar): Deleted.
(.sidebar.has-navigation-bar > .panel): Deleted.
(body[dir=ltr] .sidebar.right > .resizer,): Deleted.
(body[dir=ltr] .sidebar.left > .resizer,): Deleted.
(body[dir=ltr] .sidebar.left,): Deleted.
(body[dir=ltr] .sidebar.right,): Deleted.
* UserInterface/Views/Sidebar.js: Common base class for both SingleSidebar and MultiSidebar. Moved existing
navigation and resizing behavior to SingleSidebar.
(WI.Sidebar):
(WI.Sidebar.get sidebarPanels):
(WI.Sidebar.prototype.get side):
(WI.Sidebar.prototype.addSidebarPanel):
(WI.Sidebar.prototype.insertSidebarPanel):
(WI.Sidebar.prototype.removeSidebarPanel):
(WI.Sidebar.prototype.get selectedSidebarPanel):
(WI.Sidebar.prototype.set selectedSidebarPanel):
(WI.Sidebar.prototype.get collapsed):
(WI.Sidebar.prototype.set collapsed):
(WI.Sidebar.prototype.get collapsable):
(WI.Sidebar.prototype.set collapsable):
(WI.Sidebar.prototype.get minimumWidth):
(WI.Sidebar.prototype.get maximumWidth):
(WI.Sidebar.prototype.shouldInsertSidebarPanel):
(WI.Sidebar.prototype.didInsertSidebarPanel):
(WI.Sidebar.prototype.didRemoveSidebarPanel):
(WI.Sidebar.prototype.willSetSelectedSidebarPanel):
(WI.Sidebar.prototype.didSetSelectedSidebarPanel):
(WI.Sidebar.prototype.didSetCollapsed):
(WI.Sidebar.prototype._findSidebarPanel):
(WI.Sidebar.prototype.get width): Deleted.
(WI.Sidebar.prototype.set width): Deleted.
(WI.Sidebar.prototype.get sidebarPanels): Deleted.
(WI.Sidebar.prototype.findSidebarPanel): Deleted.
(WI.Sidebar.prototype.resizerDragStarted): Deleted.
(WI.Sidebar.prototype.resizerDragging): Deleted.
(WI.Sidebar.prototype.resizerDragEnded): Deleted.
(WI.Sidebar.prototype._recalculateWidth): Deleted.
(WI.Sidebar.prototype._navigationItemSelected): Deleted.
* UserInterface/Views/SidebarPanel.js: Support tracking exclusive display as well as enabling exclusive display.
(WI.SidebarPanel):
(WI.SidebarPanel.prototype.get exclusive):
(WI.SidebarPanel.prototype.set exclusive):
(WI.SidebarPanel.prototype.get allowExclusivePresentation):
* UserInterface/Views/SingleSidebar.css: Moved navigation and resizing from Sidebar.css.
(.single-sidebar > .navigation-bar):
(.single-sidebar.has-navigation-bar > .panel):
(body[dir=ltr] .single-sidebar.trailing > .resizer,):
(body[dir=ltr] .single-sidebar.leading > .resizer,):
(.single-sidebar.leading):
(.single-sidebar.trailing):
* UserInterface/Views/SingleSidebar.js: Added. Contains navigation and resizing previously in Sidebar.js.
(WI.SingleSidebar):
(WI.SingleSidebar.prototype.get allowResizingToCollapse):
(WI.SingleSidebar.prototype.set allowResizingToCollapse):
(WI.SingleSidebar.prototype.get minimumWidth):
(WI.SingleSidebar.prototype.get width):
(WI.SingleSidebar.prototype.set width):
(WI.SingleSidebar.prototype.shouldInsertSidebarPanel):
(WI.SingleSidebar.prototype.didInsertSidebarPanel):
(WI.SingleSidebar.prototype.didRemoveSidebarPanel):
(WI.SingleSidebar.prototype.willSetSelectedSidebarPanel):
(WI.SingleSidebar.prototype.didSetSelectedSidebarPanel):
(WI.SingleSidebar.prototype.didSetCollapsed):
(WI.SingleSidebar.prototype.resizerDragStarted):
(WI.SingleSidebar.prototype.resizerDragging):
(WI.SingleSidebar.prototype.resizerDragEnded):
(WI.SingleSidebar.prototype._recalculateWidth):
(WI.SingleSidebar.prototype._handleNavigationItemSelected):
* UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.css: The last rule row should not show a bottom border,
otherwise a double-border is visible at the bottom of the sidebar.
(.sidebar > .panel.details.css-style > .content > .rules > :nth-last-child(1 of .spreadsheet-css-declaration)):
* UserInterface/Views/TabBrowser.js: Updated naming of handlers and added support for multiple detail sidebars.
(WI.TabBrowser):
(WI.TabBrowser._handleSidebarPanelSelected):
(WI.TabBrowser.prototype._handleSidebarWidthDidChange):
(WI.TabBrowser.prototype._handleMultiSidebarSidebarAdded):
(WI.TabBrowser.prototype._showDetailsSidebarPanelsForTabContentView):
(WI.TabBrowser._sidebarPanelSelected): Deleted.
(WI.TabBrowser.prototype._sidebarCollapsedStateDidChange): Deleted.
(WI.TabBrowser.prototype._sidebarWidthDidChange): Deleted.
* UserInterface/Views/TabContentView.js:
(WI.TabContentView.prototype.get allowMultipleDetailSidebars):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (268690 => 268691)


--- trunk/Source/WebInspectorUI/ChangeLog	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/ChangeLog	2020-10-19 22:00:41 UTC (rev 268691)
@@ -1,3 +1,189 @@
+2020-10-19  Patrick Angle  <pan...@apple.com>
+
+        Web Inspector: Support three-column pane arrangement in Elements Tab
+        https://bugs.webkit.org/show_bug.cgi?id=217396
+
+        Reviewed by Devin Rousso.
+
+        The new multiple-sidebar layout for the Elements tab (and any other tab for which it is enabled in the future)
+        is achieved with a new WI.MultiSidebar, which is itself a WI.Sidebar and contains multiple new WI.SingleSidebar.
+        SingleSidebar replaces usage of the former Sidebar in favor of Sidebar being a common base shared between the
+        SingleSidebar and MultiSidebar.
+
+        A MultiSidebar supports showing multiple sidebars, and makes that decision based on the user having enabled
+        showing multiple sidebars and the `WI.SidebarPanel.prototype.allowExclusivePresentation` property on specific
+        SidebarPanels. The MultiSidebar manages these sidebars and their presentation, with some added properties to
+        manage the visibility of multiple sidebars. A MultiSidebar can display any number of sidebar panels
+        simultaneously. A MultiSidebar will always have a `primarySidebar` (although it may be collapsed), which is
+        where most panels will be presented. Additional panels that are marked as allowing exclusive presentation are
+        laid out in natural language order when the `MultiSidebar`'s side is `Leading` and reverse order when it is
+        `Trailing`. This allows the sidebar to remain conceptually anchored, so the `primarySidebar` in the set does not
+        change locations when adding or removing other sidebars from the set.
+
+        The presentation of more than the `primarySidebar` is dependant on the following conditions:
+        1. The tab (e.g. Elements Tab) must allow the three-panel layout.
+        2. At least one panel in the MultiSidebar must allow exclusive presentation (e.g. Styles when enabled in
+        Experimental Settings).
+        3. There must be at least one other panel to keep in the primary sidebar.
+        4. There must be enough available space horizontally to satisfy the minimum width of both the `primarySidebar`
+        and any sidebar panels that will be shown by themselves.
+
+        This feature is behind an Experimental Feature setting, and is disabled by default.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Base/Main.js: Use new SingleSidebar and MultiSidebars, include each detail sidebar in maximum
+        width calculations, and listen for both width and collapse events.
+        * UserInterface/Base/Setting.js: Added setting for allowing Styles panel to be exclusive.
+        * UserInterface/Main.html:
+        * UserInterface/Views/ComputedStyleDetailsPanel.css: Hide shared elements between Styles and Computed when both
+        are visible.
+        (.multi-sidebar.showing-multiple > .sidebar > .panel.details.style-computed > .content > .pseudo-classes,):
+        * UserInterface/Views/ElementsTabContentView.js:
+        (WI.ElementsTabContentView.prototype.get allowMultipleDetailSidebars):
+        * UserInterface/Views/GeneralStyleDetailsSidebarPanel.css: Show pseudo-class rules as if they were a navigation
+        bar only when shown exclusively. Otherwise, still allow them to wrap like normal.
+        (.panel.exclusive-presentation.details.css-style > .content > .pseudo-classes):
+        (.panel.exclusive-presentation.details.css-style > .content > .pseudo-classes > .group):
+        (.panel.exclusive-presentation.details.css-style > .content > .rules):
+        (.sidebar > .panel.details.css-style.style-rules > .content ~ :matches(.options-container, .class-list-container)): Removed.
+        * UserInterface/Views/GeneralStyleDetailsSidebarPanel.js: If showing exclusively, prevent wrapping pseudo-class
+        rules by reporting their combined minimum width.
+        (WI.GeneralStyleDetailsSidebarPanel.prototype.get minimumWidth):
+        * UserInterface/Views/Main.css:
+        (#tab-browser): Match existing assumption for minimum tab-browser width.
+        (#details-sidebar): Match assumption for minimum sidebar width.
+        * UserInterface/Views/MultiSidebar.css: Added.
+        (.multi-sidebar):
+        (.multi-sidebar.trailing):
+        (.multi-sidebar.collapsed):
+        * UserInterface/Views/MultiSidebar.js: A Sidebar subclass that manages multiple SingleSidebars, and dynamically
+        presents them as space is available.
+        (WI.MultiSidebar):
+        (WI.MultiSidebar.prototype.get sidebars):
+        (WI.MultiSidebar.prototype.get primarySidebar):
+        (WI.MultiSidebar.prototype.get allowMultipleSidebars):
+        (WI.MultiSidebar.prototype.set allowMultipleSidebars):
+        (WI.MultiSidebar.prototype.get multipleSidebarsVisible):
+        (WI.MultiSidebar.prototype.set multipleSidebarsVisible):
+        (WI.MultiSidebar.prototype.addSidebar):
+        (WI.MultiSidebar.prototype.removeSidebar):
+        (WI.MultiSidebar.prototype.get selectedSidebarPanel):
+        (WI.MultiSidebar.prototype.set selectedSidebarPanel):
+        (WI.MultiSidebar.prototype.get collapsable):
+        (WI.MultiSidebar.prototype.set collapsable):
+        (WI.MultiSidebar.prototype.get minimumWidth):
+        (WI.MultiSidebar.prototype.get width):
+        (WI.MultiSidebar.prototype.didInsertSidebarPanel):
+        (WI.MultiSidebar.prototype.didRemoveSidebarPanel):
+        (WI.MultiSidebar.prototype.didSetCollapsed):
+        (WI.MultiSidebar.prototype.get _canShowMultipleSidebars):
+        (WI.MultiSidebar.prototype._updateMinimumWidthForMultipleSidebars):
+        (WI.MultiSidebar.prototype.get _hasWidthForMultipleSidebars):
+        (WI.MultiSidebar.prototype.get _hasSidebarPanelSupportingExclusive):
+        (WI.MultiSidebar.prototype._updateMultipleSidebarLayout):
+        (WI.MultiSidebar.prototype._canSidebarPanelBeExclusive):
+        (WI.MultiSidebar.prototype._makeSidebarPanelExclusive):
+        (WI.MultiSidebar.prototype._makeSidebarPanelNotExclusive):
+        (WI.MultiSidebar.prototype._nonExclusiveIndexOfSidebarPanel):
+        (WI.MultiSidebar.prototype._findSidebarForSidebarPanel):
+        (WI.MultiSidebar.prototype._addSidebarEventListeners):
+        (WI.MultiSidebar.prototype._removeSidebarEventListeners):
+        (WI.MultiSidebar.prototype._handleSidebarPanelSelected):
+        (WI.MultiSidebar.prototype._handleSidebarCollapsedStateDidChange):
+        (WI.MultiSidebar.prototype._handleSidebarWidthDidChange):
+        (WI.MultiSidebar.prototype._handleWindowResize):
+        * UserInterface/Views/RulesStyleDetailsSidebarPanel.js: If enabled in settings, allow Styles panel to be its own
+        sidebar.
+        (WI.RulesStyleDetailsSidebarPanel.prototype.get allowExclusivePresentation):
+        (WI.RulesStyleDetailsSidebarPanel):
+        * UserInterface/Views/SettingsTabContentView.js: Add setting for showing the Styles panel as its own sidebar.
+        (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
+        * UserInterface/Views/Sidebar.css: Moved navigation and resizing to SingleSidebar.css.
+        (.sidebar):
+        (.sidebar.collapsed):
+        (.sidebar > .navigation-bar): Deleted.
+        (.sidebar.has-navigation-bar > .panel): Deleted.
+        (body[dir=ltr] .sidebar.right > .resizer,): Deleted.
+        (body[dir=ltr] .sidebar.left > .resizer,): Deleted.
+        (body[dir=ltr] .sidebar.left,): Deleted.
+        (body[dir=ltr] .sidebar.right,): Deleted.
+        * UserInterface/Views/Sidebar.js: Common base class for both SingleSidebar and MultiSidebar. Moved existing
+        navigation and resizing behavior to SingleSidebar.
+        (WI.Sidebar):
+        (WI.Sidebar.get sidebarPanels):
+        (WI.Sidebar.prototype.get side):
+        (WI.Sidebar.prototype.addSidebarPanel):
+        (WI.Sidebar.prototype.insertSidebarPanel):
+        (WI.Sidebar.prototype.removeSidebarPanel):
+        (WI.Sidebar.prototype.get selectedSidebarPanel):
+        (WI.Sidebar.prototype.set selectedSidebarPanel):
+        (WI.Sidebar.prototype.get collapsed):
+        (WI.Sidebar.prototype.set collapsed):
+        (WI.Sidebar.prototype.get collapsable):
+        (WI.Sidebar.prototype.set collapsable):
+        (WI.Sidebar.prototype.get minimumWidth):
+        (WI.Sidebar.prototype.get maximumWidth):
+        (WI.Sidebar.prototype.shouldInsertSidebarPanel):
+        (WI.Sidebar.prototype.didInsertSidebarPanel):
+        (WI.Sidebar.prototype.didRemoveSidebarPanel):
+        (WI.Sidebar.prototype.willSetSelectedSidebarPanel):
+        (WI.Sidebar.prototype.didSetSelectedSidebarPanel):
+        (WI.Sidebar.prototype.didSetCollapsed):
+        (WI.Sidebar.prototype._findSidebarPanel):
+        (WI.Sidebar.prototype.get width): Deleted.
+        (WI.Sidebar.prototype.set width): Deleted.
+        (WI.Sidebar.prototype.get sidebarPanels): Deleted.
+        (WI.Sidebar.prototype.findSidebarPanel): Deleted.
+        (WI.Sidebar.prototype.resizerDragStarted): Deleted.
+        (WI.Sidebar.prototype.resizerDragging): Deleted.
+        (WI.Sidebar.prototype.resizerDragEnded): Deleted.
+        (WI.Sidebar.prototype._recalculateWidth): Deleted.
+        (WI.Sidebar.prototype._navigationItemSelected): Deleted.
+        * UserInterface/Views/SidebarPanel.js: Support tracking exclusive display as well as enabling exclusive display.
+        (WI.SidebarPanel):
+        (WI.SidebarPanel.prototype.get exclusive):
+        (WI.SidebarPanel.prototype.set exclusive):
+        (WI.SidebarPanel.prototype.get allowExclusivePresentation):
+        * UserInterface/Views/SingleSidebar.css: Moved navigation and resizing from Sidebar.css.
+        (.single-sidebar > .navigation-bar):
+        (.single-sidebar.has-navigation-bar > .panel):
+        (body[dir=ltr] .single-sidebar.trailing > .resizer,):
+        (body[dir=ltr] .single-sidebar.leading > .resizer,):
+        (.single-sidebar.leading):
+        (.single-sidebar.trailing):
+        * UserInterface/Views/SingleSidebar.js: Added. Contains navigation and resizing previously in Sidebar.js.
+        (WI.SingleSidebar):
+        (WI.SingleSidebar.prototype.get allowResizingToCollapse):
+        (WI.SingleSidebar.prototype.set allowResizingToCollapse):
+        (WI.SingleSidebar.prototype.get minimumWidth):
+        (WI.SingleSidebar.prototype.get width):
+        (WI.SingleSidebar.prototype.set width):
+        (WI.SingleSidebar.prototype.shouldInsertSidebarPanel):
+        (WI.SingleSidebar.prototype.didInsertSidebarPanel):
+        (WI.SingleSidebar.prototype.didRemoveSidebarPanel):
+        (WI.SingleSidebar.prototype.willSetSelectedSidebarPanel):
+        (WI.SingleSidebar.prototype.didSetSelectedSidebarPanel):
+        (WI.SingleSidebar.prototype.didSetCollapsed):
+        (WI.SingleSidebar.prototype.resizerDragStarted):
+        (WI.SingleSidebar.prototype.resizerDragging):
+        (WI.SingleSidebar.prototype.resizerDragEnded):
+        (WI.SingleSidebar.prototype._recalculateWidth):
+        (WI.SingleSidebar.prototype._handleNavigationItemSelected):
+        * UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.css: The last rule row should not show a bottom border,
+        otherwise a double-border is visible at the bottom of the sidebar.
+        (.sidebar > .panel.details.css-style > .content > .rules > :nth-last-child(1 of .spreadsheet-css-declaration)):
+        * UserInterface/Views/TabBrowser.js: Updated naming of handlers and added support for multiple detail sidebars.
+        (WI.TabBrowser):
+        (WI.TabBrowser._handleSidebarPanelSelected):
+        (WI.TabBrowser.prototype._handleSidebarWidthDidChange):
+        (WI.TabBrowser.prototype._handleMultiSidebarSidebarAdded):
+        (WI.TabBrowser.prototype._showDetailsSidebarPanelsForTabContentView):
+        (WI.TabBrowser._sidebarPanelSelected): Deleted.
+        (WI.TabBrowser.prototype._sidebarCollapsedStateDidChange): Deleted.
+        (WI.TabBrowser.prototype._sidebarWidthDidChange): Deleted.
+        * UserInterface/Views/TabContentView.js:
+        (WI.TabContentView.prototype.get allowMultipleDetailSidebars):
+
 2020-10-16  Devin Rousso  <drou...@apple.com>
 
         Web Inspector: REGRESSION(r266669): DOMBreakpoint.js:106:23: CONSOLE ASSERT ERROR domNode should not change once set

Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -413,6 +413,8 @@
 localizedStrings["Detach into separate window"] = "Detach into separate window";
 localizedStrings["Detached"] = "Detached";
 localizedStrings["Details"] = "Details";
+/* Label for the details sidebar. */
+localizedStrings["Details @ Sidebar"] = "Details";
 localizedStrings["Device %d"] = "Device %d";
 localizedStrings["Device Settings"] = "Device Settings";
 localizedStrings["Diagnoses common accessibility problems affecting screen readers and other assistive technology."] = "Diagnoses common accessibility problems affecting screen readers and other assistive technology.";
@@ -508,6 +510,8 @@
 localizedStrings["Elements"] = "Elements";
 /* Name of Elements Tab */
 localizedStrings["Elements Tab Name"] = "Elements";
+/* Category label for experimental settings pertaining to the Elements tab */
+localizedStrings["Elements Tab: @ Experimental Settings"] = "Elements Tab:";
 localizedStrings["Emulate User Gesture"] = "Emulate User Gesture";
 localizedStrings["Enable Audit"] = "Enable Audit";
 localizedStrings["Enable Breakpoint"] = "Enable Breakpoint";
@@ -837,6 +841,8 @@
 localizedStrings["More information is available at <https://webkit.org/web-inspector/inspector-bootstrap-script/>."] = "More information is available at <https://webkit.org/web-inspector/inspector-bootstrap-script/>.";
 localizedStrings["Multi-Entry"] = "Multi-Entry";
 localizedStrings["Name"] = "Name";
+/* Label for the navigation sidebar. */
+localizedStrings["Navigation @ Sidebar"] = "Navigation";
 localizedStrings["Network"] = "Network";
 localizedStrings["Network Issue"] = "Network Issue";
 localizedStrings["Network Requests"] = "Network Requests";
@@ -1015,7 +1021,6 @@
 /* A type of canvas recording in the Graphics Tab */
 localizedStrings["Recording Type Canvas Bitmap Renderer"] = "Bitmap Renderer";
 localizedStrings["Recording Warning: %s"] = "Recording Warning: %s";
-localizedStrings["Recording stop requested \u2014 %s"] = "Recording stop requested \u2014 %s";
 localizedStrings["Recordings"] = "Recordings";
 localizedStrings["Redirect Response"] = "Redirect Response";
 localizedStrings["Redirects"] = "Redirects";
@@ -1189,6 +1194,7 @@
 localizedStrings["Show compositing borders"] = "Show compositing borders";
 localizedStrings["Show full certificate"] = "Show full certificate";
 localizedStrings["Show hidden tabs"] = "Show hidden tabs";
+localizedStrings["Show independent Styles sidebar"] = "Show independent Styles sidebar";
 localizedStrings["Show jump to effective property button"] = "Show jump to effective property button";
 localizedStrings["Show jump to variable declaration button"] = "Show jump to variable declaration button";
 localizedStrings["Show only for selected node"] = "Show only for selected node";

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -275,12 +275,14 @@
     WI.consoleContentView = WI.consoleDrawer.contentViewForRepresentedObject(WI._consoleRepresentedObject);
     WI.consoleLogViewController = WI.consoleContentView.logViewController;
 
-    // FIXME: The sidebars should be flipped in RTL languages.
-    WI.navigationSidebar = new WI.Sidebar(document.getElementById("navigation-sidebar"), WI.Sidebar.Sides.Left);
+    WI.navigationSidebar = new WI.SingleSidebar(document.getElementById("navigation-sidebar"), WI.Sidebar.Sides.Leading, WI.UIString("Navigation", "Navigation @ Sidebar", "Label for the navigation sidebar."));
     WI.navigationSidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, WI._sidebarWidthDidChange);
+    WI.navigationSidebar.addEventListener(WI.Sidebar.Event.CollapsedStateChanged, WI._sidebarWidthDidChange);
 
-    WI.detailsSidebar = new WI.Sidebar(document.getElementById("details-sidebar"), WI.Sidebar.Sides.Right, null, null, WI.UIString("Details"), true);
+    WI.detailsSidebar = new WI.MultiSidebar(document.getElementById("details-sidebar"), WI.Sidebar.Sides.Trailing, WI.UIString("Details", "Details @ Sidebar", "Label for the details sidebar."));
     WI.detailsSidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, WI._sidebarWidthDidChange);
+    WI.detailsSidebar.addEventListener(WI.Sidebar.Event.CollapsedStateChanged, WI._sidebarWidthDidChange);
+    WI.detailsSidebar.addEventListener(WI.MultiSidebar.Event.MultipleSidebarsVisibleChanged, WI._sidebarWidthDidChange);
 
     WI.searchKeyboardShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl | WI.KeyboardShortcut.Modifier.Shift, "F", WI._focusSearchField);
     WI._findKeyboardShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl, "F", WI._find);
@@ -1320,7 +1322,7 @@
 {
     console.assert(sidebar instanceof WI.Sidebar);
 
-    const minimumContentBrowserWidth = 100;
+    const minimumContentBrowserWidth = 100; // Keep in sync with `#tab-browser`
 
     let minimumWidth = window.innerWidth - minimumContentBrowserWidth;
     let tabContentView = WI.tabBrowser.selectedTabContentView;
@@ -1328,14 +1330,16 @@
     if (!tabContentView)
         return minimumWidth;
 
-    let otherSidebar = null;
-    if (sidebar === WI.navigationSidebar)
-        otherSidebar = tabContentView.detailsSidebarPanels.length ? WI.detailsSidebar : null;
-    else
-        otherSidebar = tabContentView.navigationSidebarPanel ? WI.navigationSidebar : null;
+    if (tabContentView.navigationSidebarPanel && sidebar !== WI.navigationSidebar)
+        minimumWidth -= WI.navigationSidebar.width;
 
-    if (otherSidebar)
-        minimumWidth -= otherSidebar.width;
+    if (tabContentView.detailsSidebarPanels && sidebar !== WI.detailsSidebar) {
+        // A sidebar within the detailsSidebar needs the minimum width of its sibilings.
+        for (let singleDetailsSidebar of WI.detailsSidebar.sidebars) {
+            if (sidebar !== singleDetailsSidebar)
+                minimumWidth -= singleDetailsSidebar.width;
+        }
+    }
 
     return minimumWidth;
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -222,6 +222,7 @@
     experimentalEnablePreviewFeatures: new WI.Setting("experimental-enable-preview-features", true),
     experimentalEnableStylesJumpToEffective: new WI.Setting("experimental-styles-jump-to-effective", false),
     experimentalEnableStyelsJumpToVariableDeclaration: new WI.Setting("experimental-styles-jump-to-variable-declaration", false),
+    experimentalEnableIndependentStylesPanel: new WI.Setting("experimental-independent-styles-panel", false),
 
     // Protocol
     protocolLogAsText: new WI.Setting("protocol-log-as-text", false),

Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Main.html	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html	2020-10-19 22:00:41 UTC (rev 268691)
@@ -148,6 +148,7 @@
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
+    <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
@@ -203,6 +204,7 @@
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
+    <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
     <link rel="stylesheet" href=""
@@ -521,6 +523,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""
@@ -747,6 +750,7 @@
     <script src=""
     <script src=""
     <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""
@@ -811,7 +815,7 @@
     <script src=""
     <script src=""
     <script src=""
-    <script src=""
+    <script src=""
     <script src=""
     <script src=""
     <script src=""

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -60,3 +60,11 @@
 .sidebar > .panel.details.css-style > .content > .computed .property:not(:hover) .go-to-arrow {
     display: none;
 }
+
+.multi-sidebar.showing-multiple > .sidebar > .panel.details.style-computed > .content > .pseudo-classes,
+.multi-sidebar.showing-multiple > .sidebar > .panel.details.style-computed > .options-container > .toggle-class-toggle,
+.multi-sidebar.showing-multiple > .sidebar > .panel.details.style-computed > .class-list-container {
+    display: none;
+}
+
+

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ElementsTabContentView.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -112,6 +112,11 @@
         WI.Frame.removeEventListener(null, null, this);
     }
 
+    get allowMultipleDetailSidebars()
+    {
+        return true;
+    }
+
     // Private
 
     _showDOMTreeContentView()

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/GeneralStyleDetailsSidebarPanel.css (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/GeneralStyleDetailsSidebarPanel.css	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/GeneralStyleDetailsSidebarPanel.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -64,11 +64,6 @@
     border-top: 1px solid var(--border-color);
 }
 
-.sidebar > .panel.details.css-style.style-rules > .content ~ :matches(.options-container, .class-list-container) {
-    /* Cover the border-bottom of the last CSS rule. */
-    transform: translateY(-1px);
-}
-
 .sidebar > .panel.details.css-style > .content ~ .options-container {
     height: var(--navigation-bar-height);
     padding-top: 2px;
@@ -213,3 +208,25 @@
         filter: var(--filter-invert);
     }
 }
+
+.panel.exclusive-presentation.details.css-style > .content > .pseudo-classes {
+    height: var(--navigation-bar-height);
+    flex-flow: row nowrap;
+    justify-content: center;
+}
+
+.panel.exclusive-presentation.details.css-style > .content > .pseudo-classes > .group {
+    flex-flow: row nowrap;
+    justify-content: center;
+    flex: 0;
+}
+
+.panel.exclusive-presentation.details.css-style > .content > .rules {
+    position: absolute; 
+    top: var(--navigation-bar-height);
+    left:0;
+    right:0;
+    bottom:0;
+    overflow-x: hidden;
+    overflow-y: auto;
+}
\ No newline at end of file

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/GeneralStyleDetailsSidebarPanel.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/GeneralStyleDetailsSidebarPanel.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/GeneralStyleDetailsSidebarPanel.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -45,7 +45,16 @@
 
     get minimumWidth()
     {
-        return Math.max(super.minimumWidth, this._panel.minimumWidth || 0);
+        let minimumWidth = Math.max(super.minimumWidth, this._panel.minimumWidth || 0);
+
+        if (this.exclusive) {
+            let pseudoClassMinimumWidth = 0;
+            for (let child of this._forcedPseudoClassContainer.children)
+                pseudoClassMinimumWidth += child.offsetWidth;
+            minimumWidth = Math.max(minimumWidth, pseudoClassMinimumWidth);
+        }
+
+        return minimumWidth;
     }
 
     supportsDOMNode(nodeToInspect)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Main.css (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/Main.css	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Main.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -182,10 +182,11 @@
 
 #tab-browser {
     flex: 1;
+    min-width: 100px; /* Keep in sync with `WI.getMaximumSidebarWidth(...) -> const minimumContentBrowserWidth` */
 }
 
 #details-sidebar {
-    width: 300px;
+    min-width: 250px; /* Keep in sync with `WI.Sidebar.AbsoluteMinimumWidth` */
     height: 100%;
 }
 

Copied: trunk/Source/WebInspectorUI/UserInterface/Views/MultiSidebar.css (from rev 268690, trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsSidebarPanel.js) (0 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/MultiSidebar.css	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MultiSidebar.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+.multi-sidebar {
+    position: relative;
+    display: flex;
+    background-color: var(--panel-background-color);
+}
+
+.multi-sidebar.trailing {
+    flex-direction: row-reverse;
+}
+
+.multi-sidebar.collapsed {
+    display: none;
+}

Added: trunk/Source/WebInspectorUI/UserInterface/Views/MultiSidebar.js (0 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/MultiSidebar.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/MultiSidebar.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2020 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.MultiSidebar = class MultiSidebar extends WI.Sidebar
+{
+    constructor(element, side, label)
+    {
+        super(element, side, label);
+
+        this.element.classList.add("multi-sidebar");
+
+        this._sidebars = [];
+        this._allowMultipleSidebars = false;
+        this._multipleSidebarsVisible = false;
+
+        this._ignoringSidebarCollapsedStateDidChangeEvents = false;
+
+        this.addSidebar(new WI.SingleSidebar(null, this.side, label, true));
+        this.primarySidebar.collapsed = false;
+
+        this._requiredMinimumWidthForMultipleSidebars = 0;
+
+        window.addEventListener("resize", this._handleWindowResize.bind(this));
+    }
+
+    // Public
+
+    get sidebars() { return this._sidebars; }
+
+    get primarySidebar()
+    {
+        return this._sidebars[0];
+    }
+
+    get allowMultipleSidebars()
+    {
+        return this._allowMultipleSidebars;
+    }
+
+    set allowMultipleSidebars(allow)
+    {
+        if (allow === this._allowMultipleSidebars)
+            return;
+
+        this._allowMultipleSidebars = !!allow;
+
+        this._updateMultipleSidebarLayout();
+    }
+
+    get multipleSidebarsVisible()
+    {
+        return this._multipleSidebarsVisible;
+    }
+
+    set multipleSidebarsVisible(visible)
+    {
+        if (visible === this._multipleSidebarsVisible)
+            return;
+
+        this._multipleSidebarsVisible = !!visible;
+
+        this.element.classList.toggle("showing-multiple", this._multipleSidebarsVisible);
+
+        this._updateMultipleSidebarLayout();
+
+        this.dispatchEventToListeners(WI.MultiSidebar.Event.MultipleSidebarsVisibleChanged);
+    }
+
+    addSidebar(sidebar)
+    {
+        console.assert(sidebar instanceof WI.Sidebar);
+        if (!(sidebar instanceof WI.Sidebar))
+            return;
+
+        this._addSidebarEventListeners(sidebar);
+        this._sidebars.push(sidebar);
+        this.addSubview(sidebar);
+
+        this.dispatchEventToListeners(WI.MultiSidebar.Event.SidebarAdded, {sidebar});
+    }
+
+    removeSidebar(sidebar)
+    {
+        console.assert(sidebar instanceof WI.Sidebar);
+        if (!(sidebar instanceof WI.Sidebar))
+            return;
+
+        console.assert(sidebar !== this.primarySidebar, "Primary sidebar can not be removed.");
+        if (sidebar === this.primarySidebar)
+            return;
+
+        this._removeSidebarEventListeners(sidebar);
+        this._sidebars.remove(sidebar);
+        this.removeSubview(sidebar);
+    }
+
+    get selectedSidebarPanel()
+    {
+        return this.primarySidebar.selectedSidebarPanel;
+    }
+
+    set selectedSidebarPanel(sidebarPanelOrIdentifierOrIndex)
+    {
+        this.primarySidebar.selectedSidebarPanel = sidebarPanelOrIdentifierOrIndex;
+    }
+
+    get collapsable()
+    {
+        return super.collapsable;
+    }
+
+    set collapsable(allow)
+    {
+        super.collapsable = allow;
+        this.primarySidebar.collapsable = this.collapsable;
+    }
+
+    get minimumWidth()
+    {
+        let minimumWidth = 0;
+        for (let sidebar of this.sidebars)
+            minimumWidth += sidebar.minimumWidth;
+        return minimumWidth;
+    }
+
+    get width()
+    {
+        return this.element.offsetWidth;
+    }
+
+    // Protected
+
+    didInsertSidebarPanel(sidebarPanel, index)
+    {
+        this._updateMinimumWidthForMultipleSidebars();
+        this._updateMultipleSidebarLayout();
+    }
+
+    didRemoveSidebarPanel(sidebarPanel)
+    {
+        let sidebar = this._findSidebarForSidebarPanel(sidebarPanel);
+        if (sidebar) {
+            sidebar.removeSidebarPanel(sidebarPanel);
+            if (sidebar !== this.primarySidebar)
+                this.removeSidebar(sidebar);
+        }
+
+        this._updateMinimumWidthForMultipleSidebars();
+        this._updateMultipleSidebarLayout();
+    }
+
+    didSetCollapsed(flag)
+    {
+        this.primarySidebar.collapsed = this.collapsed;
+        this._updateMultipleSidebarLayout();
+    }
+
+    // Private
+
+    get _canShowMultipleSidebars()
+    {
+        return this._allowMultipleSidebars && this._multipleSidebarsVisible && this._hasSidebarPanelSupportingExclusive && this.sidebarPanels.length >= 2;
+    }
+
+    _updateMinimumWidthForMultipleSidebars()
+    {
+        let requiredMinimumWidth = this.primarySidebar.minimumWidth;
+        for (let sidebarPanel of this.sidebarPanels) {
+            if (sidebarPanel.allowExclusivePresentation)
+                requiredMinimumWidth += Math.max(WI.Sidebar.AbsoluteMinimumWidth, sidebarPanel.minimumWidth);
+        }
+
+        this._requiredMinimumWidthForMultipleSidebars = requiredMinimumWidth;
+        this.multipleSidebarsVisible = this._hasWidthForMultipleSidebars;
+    }
+
+    get _hasWidthForMultipleSidebars()
+    {
+        return this._requiredMinimumWidthForMultipleSidebars < this.maximumWidth;
+    }
+
+    get _hasSidebarPanelSupportingExclusive()
+    {
+        return this.sidebarPanels.some((sidebarPanel) => sidebarPanel.allowExclusivePresentation);
+    }
+
+    _updateMultipleSidebarLayout()
+    {
+        for (let sidebarPanel of this.sidebarPanels) {
+            let sidebar = this._findSidebarForSidebarPanel(sidebarPanel);
+            if (this._canSidebarPanelBeExclusive(sidebarPanel) && (sidebar === this.primarySidebar || !sidebar))
+                this._makeSidebarPanelExclusive(sidebarPanel);
+            else if (!this._canSidebarPanelBeExclusive(sidebarPanel) && sidebar !== this.primarySidebar)
+                this._makeSidebarPanelNotExclusive(sidebarPanel);
+        }
+
+        this.primarySidebar.allowResizingToCollapse = this.sidebars.length <= 1;
+    }
+
+    _canSidebarPanelBeExclusive(sidebarPanel)
+    {
+        return sidebarPanel.allowExclusivePresentation && this._canShowMultipleSidebars;
+    }
+
+    _makeSidebarPanelExclusive(sidebarPanel)
+    {
+        let existingSidebar = this._findSidebarForSidebarPanel(sidebarPanel);
+
+        if (existingSidebar === this.primarySidebar)
+            existingSidebar.removeSidebarPanel(sidebarPanel);
+
+        sidebarPanel.exclusive = true;
+
+        let sidebar = new WI.SingleSidebar(null, this.side, sidebarPanel.navigationItem.label);
+        sidebar.addSidebarPanel(sidebarPanel);
+        sidebar.collapsable = false;
+        this.addSidebar(sidebar);
+
+        sidebar.selectedSidebarPanel = sidebarPanel;
+    }
+
+    _makeSidebarPanelNotExclusive(sidebarPanel)
+    {
+        let existingSidebar = this._findSidebarForSidebarPanel(sidebarPanel);
+
+        if (existingSidebar && existingSidebar !== this.primarySidebar) {
+            existingSidebar.removeSidebarPanel(sidebarPanel);
+
+            sidebarPanel.exclusive = false;
+
+            this.removeSidebar(existingSidebar);
+        }
+
+        this.primarySidebar.insertSidebarPanel(sidebarPanel, this._nonExclusiveIndexOfSidebarPanel(sidebarPanel));
+    }
+
+    _nonExclusiveIndexOfSidebarPanel(sidebarPanel)
+    {
+        let index = this._sidebarPanels.indexOf(sidebarPanel);
+
+        if (this.multipleSidebarsVisible) {
+            for (let i = index - 1; i >= 0; i--) {
+                if (this._canSidebarPanelBeExclusive(this._sidebarPanels[i]))
+                    index--;
+            }
+        }
+
+        return index;
+    }
+
+    _findSidebarForSidebarPanel(sidebarPanel)
+    {
+        // Panels that are not currently the selectedPanel for their sidebar will not have a parent.
+        if (sidebarPanel.parentSidebar)
+            return sidebarPanel.parentSidebar;
+
+        return this._sidebars.find((sidebar) => sidebar.sidebarPanels.includes(sidebarPanel)) || null;
+    }
+
+    _addSidebarEventListeners(sidebar)
+    {
+        sidebar.addEventListener(WI.Sidebar.Event.SidebarPanelSelected, this._handleSidebarPanelSelected, this);
+        sidebar.addEventListener(WI.Sidebar.Event.CollapsedStateDidChange, this._handleSidebarCollapsedStateDidChange, this);
+        sidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, this._handleSidebarWidthDidChange, this);
+    }
+
+    _removeSidebarEventListeners(sidebar)
+    {
+        sidebar.removeEventListener(WI.Sidebar.Event.SidebarPanelSelected, this._handleSidebarPanelSelected, this);
+        sidebar.removeEventListener(WI.Sidebar.Event.CollapsedStateDidChange, this._handleSidebarCollapsedStateDidChange, this);
+        sidebar.removeEventListener(WI.Sidebar.Event.WidthDidChange, this._handleSidebarWidthDidChange, this);
+    }
+
+    _handleSidebarPanelSelected(event)
+    {
+        if (event.target === this.primarySidebar)
+            this.dispatchEventToListeners(WI.Sidebar.Event.SidebarPanelSelected);
+    }
+
+    _handleSidebarCollapsedStateDidChange(event)
+    {
+        if (event.target === this.primarySidebar)
+            this.collapsed = event.target.collapsed;
+    }
+
+    _handleSidebarWidthDidChange(event)
+    {
+        this.dispatchEventToListeners(WI.Sidebar.Event.WidthDidChange, {sidebar: event.target, newWidth: event.data.newWidth});
+    }
+
+    _handleWindowResize(event)
+    {
+        if (!this.collapsed && this.allowMultipleSidebars)
+            this.multipleSidebarsVisible = this._hasWidthForMultipleSidebars;
+    }
+};
+
+WI.MultiSidebar.Event = {
+    MultipleSidebarsVisibleChanged: "multi-sidebar-multiple-sidebars-visible-changed",
+    SidebarAdded: "multi-sidebar-sidebar-added",
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsSidebarPanel.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsSidebarPanel.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsSidebarPanel.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -29,4 +29,11 @@
     {
         super("style-rules", WI.UIString("Styles"), WI.SpreadsheetRulesStyleDetailsPanel);
     }
+
+    // Public
+
+    get allowExclusivePresentation()
+    {
+        return WI.settings.experimentalEnableIndependentStylesPanel.value;
+    }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -395,6 +395,9 @@
             experimentalSettingsView.addSeparator();
         }
 
+        experimentalSettingsView.addSetting(WI.UIString("Elements Tab:", "Elements Tab: @ Experimental Settings", "Category label for experimental settings pertaining to the Elements tab"), WI.settings.experimentalEnableIndependentStylesPanel, WI.UIString("Show independent Styles sidebar"));
+        experimentalSettingsView.addSeparator();
+
         let reloadInspectorButton = document.createElement("button");
         reloadInspectorButton.textContent = WI.UIString("Reload Web Inspector");
         reloadInspectorButton.addEventListener("click", (event) => {
@@ -418,6 +421,8 @@
             listenForChange(WI.settings.experimentalEnableStyelsJumpToVariableDeclaration);
         }
 
+        listenForChange(WI.settings.experimentalEnableIndependentStylesPanel);
+
         this._createReferenceLink(experimentalSettingsView);
 
         this.addSettingsView(experimentalSettingsView);

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.css (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.css	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,7 +25,7 @@
 
 .sidebar {
     position: relative;
-
+    height: 100%;
     background-color: var(--panel-background-color);
 }
 
@@ -34,14 +34,6 @@
     border: none !important;
 }
 
-.sidebar > .navigation-bar {
-    position: absolute;
-    top: 0;
-    left: 0;
-    right: 0;
-    align-items: center;
-}
-
 .sidebar > .panel {
     display: flex;
     flex-direction: column;
@@ -53,30 +45,6 @@
     overflow: hidden;
 }
 
-.sidebar.has-navigation-bar > .panel {
-    top: var(--navigation-bar-height);
-}
-
-body[dir=ltr] .sidebar.right > .resizer,
-body[dir=rtl] .sidebar.left > .resizer {
-    left: -3px;
-}
-
-body[dir=ltr] .sidebar.left > .resizer,
-body[dir=rtl] .sidebar.right > .resizer {
-    right: -3px;
-}
-
 .sidebar.collapsed {
     display: none;
 }
-
-body[dir=ltr] .sidebar.left,
-body[dir=rtl] .sidebar.right {
-    border-right: 1px solid var(--border-color);
-}
-
-body[dir=ltr] .sidebar.right,
-body[dir=rtl] .sidebar.left {
-    border-left: 1px solid var(--border-color);
-}

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015, 2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,45 +25,37 @@
 
 WI.Sidebar = class Sidebar extends WI.View
 {
-    constructor(element, side, sidebarPanels, role, label, hasNavigationBar)
+    constructor(element, side, label)
     {
         super(element);
 
-        console.assert(!side || side === WI.Sidebar.Sides.Left || side === WI.Sidebar.Sides.Right);
-        this._side = side || WI.Sidebar.Sides.Left;
+        // This class should not be instantiated directly. Create a concrete subclass instead.
+        console.assert(this.constructor !== WI.Sidebar, this);
+
+        console.assert(Object.values(WI.Sidebar.Sides).includes(side), side);
+        this._side = side;
+
         this._collapsed = true;
+        this._collapsable = true;
 
         this.element.classList.add("sidebar", this._side, WI.Sidebar.CollapsedStyleClassName);
 
-        this.element.setAttribute("role", role || "group");
+        this.element.setAttribute("role", "group");
         if (label)
             this.element.setAttribute("aria-label", label);
 
-        if (hasNavigationBar) {
-            this.element.classList.add("has-navigation-bar");
-
-            const navigationBarElement = null;
-            this._navigationBar = new WI.NavigationBar(navigationBarElement, {role: "tablist"});
-            this._navigationBar.addEventListener(WI.NavigationBar.Event.NavigationItemSelected, this._navigationItemSelected, this);
-            this.addSubview(this._navigationBar);
-        }
-
-        this._resizer = new WI.Resizer(WI.Resizer.RuleOrientation.Vertical, this);
-        this.element.insertBefore(this._resizer.element, this.element.firstChild);
-
         this._sidebarPanels = [];
-
-        if (sidebarPanels) {
-            for (let sidebarPanel of sidebarPanels)
-                this.addSidebarPanel(sidebarPanel);
-        }
+        this._selectedSidebarPanel = null;
     }
 
     // Public
 
+    get sidebarPanels() { return this._sidebarPanels; }
+    get side() { return this._side; }
+
     addSidebarPanel(sidebarPanel)
     {
-        this.insertSidebarPanel(sidebarPanel, this._sidebarPanels.length);
+        this.insertSidebarPanel(sidebarPanel, this.sidebarPanels.length);
     }
 
     insertSidebarPanel(sidebarPanel, index)
@@ -72,99 +64,52 @@
         if (!(sidebarPanel instanceof WI.SidebarPanel))
             return;
 
-        if (sidebarPanel.parentSidebar && sidebarPanel.parentSidebar !== this) {
-            console.assert(false, "Failed to insert sidebar panel", sidebarPanel);
+        if (!this.shouldInsertSidebarPanel(sidebarPanel, index))
             return;
-        }
 
-        console.assert(index >= 0 && index <= this._sidebarPanels.length);
+        console.assert(index >= 0 && index <= this.sidebarPanels.length);
         this._sidebarPanels.splice(index, 0, sidebarPanel);
 
-        if (this._navigationBar) {
-            console.assert(sidebarPanel.navigationItem);
-            this._navigationBar.insertNavigationItem(sidebarPanel.navigationItem, index);
-        }
+        this.didInsertSidebarPanel(sidebarPanel, index);
     }
 
     removeSidebarPanel(sidebarPanelOrIdentifierOrIndex)
     {
-        let sidebarPanel = this.findSidebarPanel(sidebarPanelOrIdentifierOrIndex);
+        let sidebarPanel = this._findSidebarPanel(sidebarPanelOrIdentifierOrIndex);
         if (!sidebarPanel)
             return;
 
-        if (sidebarPanel.visible)
-            sidebarPanel.hidden();
+        this._sidebarPanels.remove(sidebarPanel);
 
-        sidebarPanel.selected = false;
+        if (this.selectedSidebarPanel === sidebarPanel)
+            this.selectedSidebarPanel = 0;
 
-        this._sidebarPanels.remove(sidebarPanel);
-
-        if (this._navigationBar) {
-            console.assert(sidebarPanel.navigationItem);
-            this._navigationBar.removeNavigationItem(sidebarPanel.navigationItem);
-        }
+        this.didRemoveSidebarPanel(sidebarPanel);
     }
 
     get selectedSidebarPanel()
     {
-        return this._selectedSidebarPanel || null;
+        return this._selectedSidebarPanel;
     }
 
     set selectedSidebarPanel(sidebarPanelOrIdentifierOrIndex)
     {
-        var sidebarPanel = this.findSidebarPanel(sidebarPanelOrIdentifierOrIndex);
+        let sidebarPanel = this._findSidebarPanel(sidebarPanelOrIdentifierOrIndex);
+        if (!sidebarPanel)
+            sidebarPanel = this._findSidebarPanel(0);
+
         if (this._selectedSidebarPanel === sidebarPanel)
             return;
 
-        if (this._selectedSidebarPanel) {
-            this._selectedSidebarPanel.hidden();
-            this._selectedSidebarPanel.selected = false;
-            this.removeSubview(this._selectedSidebarPanel);
-        }
+        this.willSetSelectedSidebarPanel(sidebarPanel);
 
-        this._selectedSidebarPanel = sidebarPanel || null;
+        this._selectedSidebarPanel = sidebarPanel;
 
-        if (this._navigationBar)
-            this._navigationBar.selectedNavigationItem = sidebarPanel ? sidebarPanel.navigationItem : null;
+        this.didSetSelectedSidebarPanel(sidebarPanel);
 
-        if (this._selectedSidebarPanel) {
-            this.addSubview(this._selectedSidebarPanel);
-            this._selectedSidebarPanel.selected = true;
-            if (!this.collapsed)
-                this._selectedSidebarPanel.shown();
-        }
-
         this.dispatchEventToListeners(WI.Sidebar.Event.SidebarPanelSelected);
     }
 
-    get minimumWidth()
-    {
-        let minimumWidth = WI.Sidebar.AbsoluteMinimumWidth;
-        if (this._navigationBar)
-            minimumWidth = Math.max(minimumWidth, this._navigationBar.minimumWidth);
-        if (this._selectedSidebarPanel)
-            minimumWidth = Math.max(minimumWidth, this._selectedSidebarPanel.minimumWidth);
-        return minimumWidth;
-    }
-
-    get maximumWidth()
-    {
-        return WI.getMaximumSidebarWidth(this);
-    }
-
-    get width()
-    {
-        return this.element.offsetWidth;
-    }
-
-    set width(newWidth)
-    {
-        if (newWidth === this.width)
-            return;
-
-        this._recalculateWidth(newWidth);
-    }
-
     get collapsed()
     {
         return this._collapsed;
@@ -175,12 +120,12 @@
         if (flag === this._collapsed)
             return;
 
+        if (flag && !this._collapsable)
+            return;
+
         this._collapsed = flag || false;
         this.element.classList.toggle(WI.Sidebar.CollapsedStyleClassName);
 
-        if (!this._collapsed && this._navigationBar)
-            this._navigationBar.needsLayout();
-
         if (this._selectedSidebarPanel) {
             if (this._selectedSidebarPanel.visible)
                 this._selectedSidebarPanel.shown();
@@ -188,105 +133,97 @@
                 this._selectedSidebarPanel.hidden();
         }
 
+        this.didSetCollapsed(flag);
+
         this.dispatchEventToListeners(WI.Sidebar.Event.CollapsedStateDidChange);
-        this.dispatchEventToListeners(WI.Sidebar.Event.WidthDidChange);
     }
 
-    get sidebarPanels()
+    get collapsable()
     {
-        return this._sidebarPanels;
+        return this._collapsable;
     }
 
-    get side()
+    set collapsable(allow) {
+        if (allow === this._collapsable)
+            return;
+
+        this._collapsable = !!allow;
+
+        if (!allow && this.collapsed)
+            this.collapsed = false;
+    }
+
+    get minimumWidth()
     {
-        return this._side;
+        let minimumWidth = WI.Sidebar.AbsoluteMinimumWidth;
+        if (this.selectedSidebarPanel)
+            minimumWidth = Math.max(minimumWidth, this.selectedSidebarPanel.minimumWidth);
+        return minimumWidth;
     }
 
-    findSidebarPanel(sidebarPanelOrIdentifierOrIndex)
+    get maximumWidth()
     {
-        var sidebarPanel = null;
-
-        if (sidebarPanelOrIdentifierOrIndex instanceof WI.SidebarPanel) {
-            if (this._sidebarPanels.includes(sidebarPanelOrIdentifierOrIndex))
-                sidebarPanel = sidebarPanelOrIdentifierOrIndex;
-        } else if (typeof sidebarPanelOrIdentifierOrIndex === "number") {
-            sidebarPanel = this._sidebarPanels[sidebarPanelOrIdentifierOrIndex];
-        } else if (typeof sidebarPanelOrIdentifierOrIndex === "string") {
-            for (var i = 0; i < this._sidebarPanels.length; ++i) {
-                if (this._sidebarPanels[i].identifier === sidebarPanelOrIdentifierOrIndex) {
-                    sidebarPanel = this._sidebarPanels[i];
-                    break;
-                }
-            }
-        }
-
-        return sidebarPanel;
+        return WI.getMaximumSidebarWidth(this);
     }
 
     // Protected
 
-    resizerDragStarted(resizer)
+    shouldInsertSidebarPanel(sidebarPanel, index)
     {
-        this._widthBeforeResize = this.width;
+        // Implemented by subclasses if needed.
+        return true;
     }
 
-    resizerDragging(resizer, positionDelta)
+    didInsertSidebarPanel(sidebarPanel, index)
     {
-        if (this._side === WI.Sidebar.Sides.Left)
-            positionDelta *= -1;
+        // Implemented by subclasses if needed.
+    }
 
-        if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL)
-            positionDelta *= -1;
+    didRemoveSidebarPanel(sidebarPanel)
+    {
+        // Implemented by subclasses if needed.
+    }
 
-        var newWidth = positionDelta + this._widthBeforeResize;
-        this.width = newWidth;
-        this.collapsed = newWidth < (this.minimumWidth / 2);
+    willSetSelectedSidebarPanel(sidebarPanel)
+    {
+        // Implemented by subclasses if needed.
     }
 
-    resizerDragEnded(resizer)
+    didSetSelectedSidebarPanel(sidebarPanel)
     {
-        if (this._widthBeforeResize === this.width)
-            return;
+        // Implemented by subclasses if needed.
+    }
 
-        if (!this.collapsed && this._navigationBar)
-            this._navigationBar.sizeDidChange();
-
-        if (!this.collapsed && this._selectedSidebarPanel)
-            this._selectedSidebarPanel.sizeDidChange();
+    didSetCollapsed(flag)
+    {
+        // Implemented by subclasses if needed.
     }
 
     // Private
 
-    _recalculateWidth(newWidth = this.width)
+    _findSidebarPanel(sidebarPanelOrIdentifierOrIndex)
     {
-        // Need to add 1 because of the 1px border-right.
-        newWidth = Math.ceil(Number.constrain(newWidth, this.minimumWidth + 1, this.maximumWidth));
-        this.element.style.width = `${newWidth}px`;
+        let sidebarPanel = null;
 
-        if (this.collapsed)
-            return;
+        if (sidebarPanelOrIdentifierOrIndex instanceof WI.SidebarPanel) {
+            if (this._sidebarPanels.includes(sidebarPanelOrIdentifierOrIndex))
+                sidebarPanel = sidebarPanelOrIdentifierOrIndex;
+        } else if (typeof sidebarPanelOrIdentifierOrIndex === "number") {
+            sidebarPanel = this._sidebarPanels[sidebarPanelOrIdentifierOrIndex];
+        } else if (typeof sidebarPanelOrIdentifierOrIndex === "string") {
+            sidebarPanel = this._sidebarPanels.find((existingSidebarPanel) => existingSidebarPanel.identifier === sidebarPanelOrIdentifierOrIndex) || null;
+        }
 
-        if (this._navigationBar)
-            this._navigationBar.updateLayout(WI.View.LayoutReason.Resize);
-
-        if (this._selectedSidebarPanel)
-            this._selectedSidebarPanel.updateLayout(WI.View.LayoutReason.Resize);
-
-        this.dispatchEventToListeners(WI.Sidebar.Event.WidthDidChange, {newWidth});
+        return sidebarPanel;
     }
-
-    _navigationItemSelected(event)
-    {
-        this.selectedSidebarPanel = event.target.selectedNavigationItem ? event.target.selectedNavigationItem.identifier : null;
-    }
 };
 
 WI.Sidebar.CollapsedStyleClassName = "collapsed";
-WI.Sidebar.AbsoluteMinimumWidth = 200;
+WI.Sidebar.AbsoluteMinimumWidth = 250; // Keep in sync with `#details-sidebar` in `Main.css`
 
 WI.Sidebar.Sides = {
-    Right: "right",
-    Left: "left"
+    Leading: "leading",
+    Trailing: "trailing",
 };
 
 WI.Sidebar.Event = {

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -32,6 +32,7 @@
         this._identifier = identifier;
         this._displayName = displayName;
         this._selected = false;
+        this._exclusive = false;
 
         this._savedScrollPosition = 0;
 
@@ -92,6 +93,27 @@
         return 0;
     }
 
+    get exclusive()
+    {
+        return this._exclusive;
+    }
+
+    set exclusive(exclusive)
+    {
+        if (exclusive === this._exclusive)
+            return;
+
+        this._exclusive = !!exclusive;
+
+        this.element.classList.toggle("exclusive-presentation", this._exclusive);
+    }
+
+    get allowExclusivePresentation()
+    {
+        // Implemented by subclasses.
+        return false;
+    }
+
     shown()
     {
         this.scrollElement.scrollTop = this._savedScrollPosition;

Copied: trunk/Source/WebInspectorUI/UserInterface/Views/SingleSidebar.css (from rev 268690, trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsSidebarPanel.js) (0 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SingleSidebar.css	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SingleSidebar.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013, 2020 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.
+ */
+
+.single-sidebar > .navigation-bar {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    align-items: center;
+}
+
+.single-sidebar.has-navigation-bar > .panel {
+    top: var(--navigation-bar-height);
+}
+
+body[dir=ltr] .single-sidebar.trailing > .resizer,
+body[dir=rtl] .single-sidebar.leading > .resizer {
+    left: -3px;
+}
+
+body[dir=ltr] .single-sidebar.leading > .resizer,
+body[dir=rtl] .single-sidebar.trailing > .resizer {
+    right: -3px;
+}
+
+.single-sidebar.leading {
+    border-inline-end: 1px solid var(--border-color);
+}
+
+.single-sidebar.trailing {
+    border-inline-start: 1px solid var(--border-color);
+}

Added: trunk/Source/WebInspectorUI/UserInterface/Views/SingleSidebar.js (0 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SingleSidebar.js	                        (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SingleSidebar.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013, 2015, 2020 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.SingleSidebar = class SingleSidebar extends WI.Sidebar
+{
+    constructor(element, side, label, supportsMultiplePanels)
+    {
+        super(element, side, label);
+
+        this.element.classList.add("single-sidebar");
+
+        this._allowResizingToCollapse = true;
+
+        if (supportsMultiplePanels) {
+            this.element.classList.add("has-navigation-bar");
+
+            const navigationBarElement = null;
+            this._navigationBar = new WI.NavigationBar(navigationBarElement, {role: "tablist"});
+            this._navigationBar.addEventListener(WI.NavigationBar.Event.NavigationItemSelected, this._handleNavigationItemSelected, this);
+
+            this.addSubview(this._navigationBar);
+        }
+
+        this._resizer = new WI.Resizer(WI.Resizer.RuleOrientation.Vertical, this);
+        this.element.insertBefore(this._resizer.element, this.element.firstChild);
+    }
+
+    // Public
+
+    get allowResizingToCollapse() { return this._allowResizingToCollapse; }
+    set allowResizingToCollapse(allow) { this._allowResizingToCollapse = !!allow; }
+
+    get minimumWidth()
+    {
+        let minimumWidth = super.minimumWidth;
+        if (this._navigationBar)
+            minimumWidth = Math.max(minimumWidth, this._navigationBar.minimumWidth);
+        return minimumWidth;
+    }
+
+    get width()
+    {
+        return this.element.offsetWidth;
+    }
+
+    set width(newWidth)
+    {
+        if (newWidth === this.width)
+            return;
+
+        this._recalculateWidth(newWidth);
+    }
+
+    // Protected
+
+    shouldInsertSidebarPanel(sidebarPanel, index)
+    {
+        console.assert(!sidebarPanel.parentSidebar || sidebarPanel.parentSidebar === this);
+        return !sidebarPanel.parentSidebar || sidebarPanel.parentSidebar === this;
+    }
+
+    didInsertSidebarPanel(sidebarPanel, index)
+    {
+        if (this._navigationBar) {
+            console.assert(sidebarPanel.navigationItem);
+            this._navigationBar.insertNavigationItem(sidebarPanel.navigationItem, index);
+        }
+    }
+
+    didRemoveSidebarPanel(sidebarPanel)
+    {
+        if (this._navigationBar) {
+            console.assert(sidebarPanel.navigationItem);
+            this._navigationBar.removeNavigationItem(sidebarPanel.navigationItem);
+        }
+    }
+
+    willSetSelectedSidebarPanel(sidebarPanel)
+    {
+        if (this.selectedSidebarPanel) {
+            this.selectedSidebarPanel.hidden();
+            this.selectedSidebarPanel.selected = false;
+            this.removeSubview(this.selectedSidebarPanel);
+        }
+    }
+
+    didSetSelectedSidebarPanel(sidebarPanel)
+    {
+        if (this.selectedSidebarPanel) {
+            this.addSubview(this.selectedSidebarPanel);
+            this.selectedSidebarPanel.selected = true;
+            if (!this.collapsed) {
+                this.selectedSidebarPanel.shown();
+            }
+        }
+
+        if (this._navigationBar)
+            this._navigationBar.selectedNavigationItem = this.selectedSidebarPanel?.navigationItem ?? null;
+    }
+
+    didSetCollapsed(flag)
+    {
+        if (!flag && this._navigationBar)
+            this._navigationBar.needsLayout();
+    }
+
+    resizerDragStarted(resizer)
+    {
+        this._widthBeforeResize = this.width;
+    }
+
+    resizerDragging(resizer, positionDelta)
+    {
+        if (this._side === WI.Sidebar.Sides.Leading)
+            positionDelta *= -1;
+
+        if (WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL)
+            positionDelta *= -1;
+
+        let newWidth = positionDelta + this._widthBeforeResize;
+        this.width = newWidth;
+
+        if (this.collapsable && this._allowResizingToCollapse)
+            this.collapsed = newWidth < (this.minimumWidth / 2);
+    }
+
+    resizerDragEnded(resizer)
+    {
+        if (this._widthBeforeResize === this.width)
+            return;
+
+        if (!this.collapsed && this._navigationBar)
+            this._navigationBar.sizeDidChange();
+
+        if (!this.collapsed && this._selectedSidebarPanel)
+            this._selectedSidebarPanel.sizeDidChange();
+    }
+
+    // Private
+
+    _recalculateWidth(newWidth = this.width)
+    {
+        // Need to add 1 because of the 1px border-inline-start or border-inline-end.
+        newWidth = Math.ceil(Number.constrain(newWidth, this.minimumWidth + 1, this.maximumWidth));
+        this.element.style.width = `${newWidth}px`;
+        this.element.style.minWidth = `${this.minimumWidth}px`;
+
+        if (this.collapsed)
+            return;
+
+        if (this._navigationBar)
+            this._navigationBar.updateLayout(WI.View.LayoutReason.Resize);
+
+        if (this.selectedSidebarPanel)
+            this.selectedSidebarPanel.updateLayout(WI.View.LayoutReason.Resize);
+
+        this.dispatchEventToListeners(WI.Sidebar.Event.WidthDidChange, {newWidth});
+    }
+
+    _handleNavigationItemSelected(event)
+    {
+        this.selectedSidebarPanel = event.target.selectedNavigationItem?.identifier ?? null;
+    }
+};

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.css (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.css	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.css	2020-10-19 22:00:41 UTC (rev 268691)
@@ -52,6 +52,11 @@
     display: none;
 }
 
+.sidebar > .panel.details.css-style > .content > .rules > :nth-last-child(1 of .spreadsheet-css-declaration) {
+    /* Unset border-bottom of last CSS rule. */
+    border-bottom: none;
+}
+
 @media (prefers-color-scheme: dark) {
     .sidebar > .panel.details.css-style > .content > .rules .section-header {
         color: var(--text-color-secondary);

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -38,14 +38,15 @@
         this._detailsSidebar = detailsSidebar || null;
 
         if (this._navigationSidebar) {
-            this._navigationSidebar.addEventListener(WI.Sidebar.Event.CollapsedStateDidChange, this._sidebarCollapsedStateDidChange, this);
-            this._navigationSidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
+            this._navigationSidebar.addEventListener(WI.Sidebar.Event.CollapsedStateDidChange, this._handleSidebarCollapsedStateDidChange, this);
+            this._navigationSidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, this._handleSidebarWidthDidChange, this);
         }
 
         if (this._detailsSidebar) {
-            this._detailsSidebar.addEventListener(WI.Sidebar.Event.CollapsedStateDidChange, this._sidebarCollapsedStateDidChange, this);
-            this._detailsSidebar.addEventListener(WI.Sidebar.Event.SidebarPanelSelected, this._sidebarPanelSelected, this);
-            this._detailsSidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, this._sidebarWidthDidChange, this);
+            this._detailsSidebar.addEventListener(WI.Sidebar.Event.CollapsedStateDidChange, this._handleSidebarCollapsedStateDidChange, this);
+            this._detailsSidebar.addEventListener(WI.Sidebar.Event.WidthDidChange, this._handleSidebarWidthDidChange, this);
+            this._detailsSidebar.addEventListener(WI.Sidebar.Event.SidebarPanelSelected, this._handleSidebarPanelSelected, this);
+            this._detailsSidebar.addEventListener(WI.MultiSidebar.Event.SidebarAdded, this._handleMultiSidebarSidebarAdded, this);
         }
 
         this._contentViewContainer = new WI.ContentViewContainer;
@@ -296,7 +297,7 @@
         console.assert(!this.selectedTabContentView || this.selectedTabContentView === this._recentTabContentViews[0]);
     }
 
-    _sidebarPanelSelected(event)
+    _handleSidebarPanelSelected(event)
     {
         if (this._ignoreSidebarEvents)
             return;
@@ -311,10 +312,10 @@
             return;
 
         var selectedSidebarPanel = this._detailsSidebar.selectedSidebarPanel;
-        tabContentView.detailsSidebarSelectedPanelSetting.value = selectedSidebarPanel ? selectedSidebarPanel.identifier : null;
+        tabContentView.detailsSidebarSelectedPanelSetting.value = selectedSidebarPanel?.identifier ?? null;
     }
 
-    _sidebarCollapsedStateDidChange(event)
+    _handleSidebarCollapsedStateDidChange(event)
     {
         if (this._ignoreSidebarEvents)
             return;
@@ -329,7 +330,7 @@
             tabContentView.detailsSidebarCollapsedSetting.value = this._detailsSidebar.collapsed;
     }
 
-    _sidebarWidthDidChange(event)
+    _handleSidebarWidthDidChange(event)
     {
         if (this._ignoreSidebarEvents || !event.data)
             return;
@@ -344,11 +345,31 @@
             break;
 
         case this._detailsSidebar:
-            tabContentView.detailsSidebarWidthSetting.value = event.data.newWidth;
+            if (event.data.sidebar && event.data.newWidth) {
+                let identifier = event.data.sidebar === this._detailsSidebar.primarySidebar ? WI.TabBrowser.SidebarWidthSettingPrimarySidebarIdentifier : (event.data.sidebar.sidebarPanels[0]?.identifier || null);
+                if (identifier) {
+                    tabContentView.detailsSidebarWidthSetting.value[identifier] = event.data.newWidth;
+                    tabContentView.detailsSidebarWidthSetting.save();
+                }
+            }
             break;
         }
     }
 
+    _handleMultiSidebarSidebarAdded(event)
+    {
+        let tabContentView = this.selectedTabContentView;
+        if (!tabContentView)
+            return;
+
+        if (event.target !== this._detailsSidebar)
+            return;
+
+        let sidebar = event.data.sidebar;
+        let identifier = event.data.sidebar === this._detailsSidebar.primarySidebar ? WI.TabBrowser.SidebarWidthSettingPrimarySidebarIdentifier : (event.data.sidebar.sidebarPanels[0]?.identifier || null);
+        sidebar.width = tabContentView.detailsSidebarWidthSetting.value[identifier] || WI.TabContentView.DefaultSidebarWidth;
+    }
+
     _saveFocusedNodeForTabContentView(tabContentView)
     {
         if (!tabContentView)
@@ -429,9 +450,13 @@
             return;
         }
 
-        if (tabContentView.detailsSidebarWidthSetting.value)
-            this._detailsSidebar.width = tabContentView.detailsSidebarWidthSetting.value;
+        for (let sidebar of this._detailsSidebar.sidebars) {
+            let identifier = sidebar === this._detailsSidebar.primarySidebar ? WI.TabBrowser.SidebarWidthSettingPrimarySidebarIdentifier : (sidebar.sidebarPanels[0]?.identifier || null);
+            sidebar.width = tabContentView.detailsSidebarWidthSetting.value[identifier] || WI.TabContentView.DefaultSidebarWidth;
+        }
 
+        this._detailsSidebar.allowMultipleSidebars = tabContentView.allowMultipleDetailSidebars;
+
         if (tabContentView.managesDetailsSidebarPanels) {
             tabContentView.showDetailsSidebarPanels();
             this._ignoreSidebarEvents = false;
@@ -489,6 +514,8 @@
 WI.TabBrowser.NeedsResizeLayoutSymbol = Symbol("needs-resize-layout");
 WI.TabBrowser.FocusedNodeSymbol = Symbol("focused-node");
 
+WI.TabBrowser.SidebarWidthSettingPrimarySidebarIdentifier = "primary-sidebar";
+
 WI.TabBrowser.TabNavigationInitiator = {
     // Initiated by clicking on the TabBar UI (switching, opening, closing).
     TabClick: "tab-browser-tab-navigation-initiator-tab-click",

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabContentView.js (268690 => 268691)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TabContentView.js	2020-10-19 21:46:04 UTC (rev 268690)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabContentView.js	2020-10-19 22:00:41 UTC (rev 268691)
@@ -37,14 +37,12 @@
         this._navigationSidebarPanelConstructor = navigationSidebarPanelConstructor || null;
         this._detailsSidebarPanelConstructors = detailsSidebarPanelConstructors || [];
 
-        const defaultSidebarWidth = 300;
-
         this._navigationSidebarCollapsedSetting = new WI.Setting(this._identifier + "-navigation-sidebar-collapsed", false);
-        this._navigationSidebarWidthSetting = new WI.Setting(this._identifier + "-navigation-sidebar-width", defaultSidebarWidth);
+        this._navigationSidebarWidthSetting = new WI.Setting(this._identifier + "-navigation-sidebar-width", WI.TabContentView.DefaultSidebarWidth);
 
         this._detailsSidebarCollapsedSetting = new WI.Setting(this._identifier + "-details-sidebar-collapsed", !this.detailsSidebarExpandedByDefault);
         this._detailsSidebarSelectedPanelSetting = new WI.Setting(this._identifier + "-details-sidebar-selected-panel", null);
-        this._detailsSidebarWidthSetting = new WI.Setting(this._identifier + "-details-sidebar-width", defaultSidebarWidth);
+        this._detailsSidebarWidthSetting = new WI.Setting(this._identifier + "-details-sidebar-widths", {});
 
         this._cookieSetting = new WI.Setting(this._identifier + "-tab-cookie", {});
 
@@ -121,6 +119,12 @@
         return false;
     }
 
+    get allowMultipleDetailSidebars()
+    {
+        // Can be overridden by subclasses.
+        return false;
+    }
+
     shown()
     {
         super.shown();
@@ -192,3 +196,5 @@
     get detailsSidebarSelectedPanelSetting() { return this._detailsSidebarSelectedPanelSetting; }
     get detailsSidebarWidthSetting() { return this._detailsSidebarWidthSetting; }
 };
+
+WI.TabContentView.DefaultSidebarWidth = 300;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to