Web Inspector: Canvas Tab: canvases overview should support navigation via keyboard
authorwebkit@devinrousso.com <webkit@devinrousso.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Oct 2017 23:41:23 +0000 (23:41 +0000)
committerwebkit@devinrousso.com <webkit@devinrousso.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Oct 2017 23:41:23 +0000 (23:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178800
<rdar://problem/35175856>

Reviewed by Brian Burg.

Create a KeyboardShorcut for each of the following:
 - Up: selects the previous canvas in the selected column
 - Down: selects the next canvas in the selected column
 - Right: selects the next canvas in the selected row
 - Left: selects the previous canvas in the selected row
 - Space: toggle recording of the selected canvas
 - Shift+Space: toggle single-frame recording of the selected canvas

The calculation for selected row/colum is based on the `offsetWidth` of the parent element
and the selected item's content view element. Since this view uses a flexbox, all of the
items are expected to have the same dimensions, meaning that this value is uniform. The
intended functionality is that of a spreadsheet, where pressing pressing left/right will
never change the selected row and up/down will never change the selected column.

* UserInterface/Views/CanvasOverviewContentView.css:
(.content-view.canvas-overview):
(.content-view.canvas-overview .content-view.canvas):
Move margin value to a CSS variable so that it can be easily retrieved via JavaScript.

* UserInterface/Views/CanvasOverviewContentView.js:
(WI.CanvasOverviewContentView):
(WI.CanvasOverviewContentView.prototype.contentViewAdded):
(WI.CanvasOverviewContentView.prototype.contentViewRemoved):
(WI.CanvasOverviewContentView.prototype.attached):
(WI.CanvasOverviewContentView.prototype.detached):
(WI.CanvasOverviewContentView.prototype.get _itemMargin):
(WI.CanvasOverviewContentView.prototype._changeSelectedItemVertically):
(WI.CanvasOverviewContentView.prototype._changeSelectedItemHorizontally):
(WI.CanvasOverviewContentView.prototype._updateNavigationItems):
(WI.CanvasOverviewContentView.prototype._handleUp):
(WI.CanvasOverviewContentView.prototype._handleRight):
(WI.CanvasOverviewContentView.prototype._handleDown):
(WI.CanvasOverviewContentView.prototype._handleLeft):
(WI.CanvasOverviewContentView.prototype._handleSpace):
(WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange):
Drive-by: call `_updateNavigationItems` whenever an item is added/removed so that if there
are no items the navigation items cannot be clicked.

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

Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.css
Source/WebInspectorUI/UserInterface/Views/CanvasOverviewContentView.js

index 1c2835c..fd099e6 100644 (file)
@@ -1,3 +1,49 @@
+2017-10-26  Devin Rousso  <webkit@devinrousso.com>
+
+        Web Inspector: Canvas Tab: canvases overview should support navigation via keyboard
+        https://bugs.webkit.org/show_bug.cgi?id=178800
+        <rdar://problem/35175856>
+
+        Reviewed by Brian Burg.
+
+        Create a KeyboardShorcut for each of the following:
+         - Up: selects the previous canvas in the selected column
+         - Down: selects the next canvas in the selected column
+         - Right: selects the next canvas in the selected row
+         - Left: selects the previous canvas in the selected row
+         - Space: toggle recording of the selected canvas
+         - Shift+Space: toggle single-frame recording of the selected canvas
+
+        The calculation for selected row/colum is based on the `offsetWidth` of the parent element
+        and the selected item's content view element. Since this view uses a flexbox, all of the
+        items are expected to have the same dimensions, meaning that this value is uniform. The
+        intended functionality is that of a spreadsheet, where pressing pressing left/right will
+        never change the selected row and up/down will never change the selected column.
+
+        * UserInterface/Views/CanvasOverviewContentView.css:
+        (.content-view.canvas-overview):
+        (.content-view.canvas-overview .content-view.canvas):
+        Move margin value to a CSS variable so that it can be easily retrieved via JavaScript.
+
+        * UserInterface/Views/CanvasOverviewContentView.js:
+        (WI.CanvasOverviewContentView):
+        (WI.CanvasOverviewContentView.prototype.contentViewAdded):
+        (WI.CanvasOverviewContentView.prototype.contentViewRemoved):
+        (WI.CanvasOverviewContentView.prototype.attached):
+        (WI.CanvasOverviewContentView.prototype.detached):
+        (WI.CanvasOverviewContentView.prototype.get _itemMargin):
+        (WI.CanvasOverviewContentView.prototype._changeSelectedItemVertically):
+        (WI.CanvasOverviewContentView.prototype._changeSelectedItemHorizontally):
+        (WI.CanvasOverviewContentView.prototype._updateNavigationItems):
+        (WI.CanvasOverviewContentView.prototype._handleUp):
+        (WI.CanvasOverviewContentView.prototype._handleRight):
+        (WI.CanvasOverviewContentView.prototype._handleDown):
+        (WI.CanvasOverviewContentView.prototype._handleLeft):
+        (WI.CanvasOverviewContentView.prototype._handleSpace):
+        (WI.CanvasOverviewContentView.prototype._supplementalRepresentedObjectsDidChange):
+        Drive-by: call `_updateNavigationItems` whenever an item is added/removed so that if there
+        are no items the navigation items cannot be clicked.
+
 2017-10-26  Nikita Vasilyev  <nvasilyev@apple.com>
 
         Web Inspector: Styles Redesign: Make "Style Attribute" text darker and non-focusable
index 9a72a3e..86be362 100644 (file)
     justify-content: center;
     align-items: flex-start;
     background-color: hsl(0, 0%, 90%);
+
+    --item-margin: 10px;
 }
 
 .content-view.canvas-overview .content-view.canvas {
     flex-grow: 0;
-    margin: 10px;
+    margin: var(--item-margin);
     width: 400px;
     background-color: white;
 }
index 9e037e8..36da16b 100644 (file)
@@ -34,15 +34,27 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
         this.element.classList.add("canvas-overview");
 
         this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("refresh-all", WI.UIString("Refresh all"), "Images/ReloadFull.svg", 13, 13);
-        this._refreshButtonNavigationItem.disabled = true;
+        this._refreshButtonNavigationItem.enabled = false;
         this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshPreviews, this);
 
         this._showGridButtonNavigationItem = new WI.ActivateButtonNavigationItem("show-grid", WI.UIString("Show transparency grid"), WI.UIString("Hide Grid"), "Images/NavigationItemCheckers.svg", 13, 13);
         this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
-        this._showGridButtonNavigationItem.disabled = true;
+        this._showGridButtonNavigationItem.enabled = false;
         this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this);
 
         this.selectionEnabled = true;
+
+        this._keyboardShortcuts = [
+            new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Up, this._handleUp.bind(this)),
+            new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Right, this._handleRight.bind(this)),
+            new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Down, this._handleDown.bind(this)),
+            new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Left, this._handleLeft.bind(this)),
+            new WI.KeyboardShortcut(null, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this)),
+            new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.Shift, WI.KeyboardShortcut.Key.Space, this._handleSpace.bind(this)),
+        ];
+
+        for (let shortcut of this._keyboardShortcuts)
+            shortcut.disabled = true;
     }
 
     // Public
@@ -86,12 +98,16 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
     {
         contentView.element.addEventListener("mouseenter", this._contentViewMouseEnter);
         contentView.element.addEventListener("mouseleave", this._contentViewMouseLeave);
+
+        this._updateNavigationItems();
     }
 
     contentViewRemoved(contentView)
     {
         contentView.element.removeEventListener("mouseenter", this._contentViewMouseEnter);
         contentView.element.removeEventListener("mouseleave", this._contentViewMouseLeave);
+
+        this._updateNavigationItems();
     }
 
     attached()
@@ -101,6 +117,9 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
         WI.settings.showImageGrid.addEventListener(WI.Setting.Event.Changed, this._updateShowImageGrid, this);
 
         this.addEventListener(WI.ContentView.Event.SupplementalRepresentedObjectsDidChange, this._supplementalRepresentedObjectsDidChange, this);
+
+        for (let shortcut of this._keyboardShortcuts)
+            shortcut.disabled = false;
     }
 
     detached()
@@ -109,17 +128,69 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
 
         this.removeEventListener(null, null, this);
 
+        for (let shortcut of this._keyboardShortcuts)
+            shortcut.disabled = true;
+
         super.detached();
     }
 
     // Private
 
+    get _itemMargin()
+    {
+        return parseInt(window.getComputedStyle(this.element).getPropertyValue("--item-margin"));
+    }
+
     _refreshPreviews()
     {
         for (let canvasContentView of this.subviews)
             canvasContentView.refresh();
     }
 
+    _changeSelectedItemVertically(shift)
+    {
+        let itemElementWidth = this.element.firstElementChild.offsetWidth + (2 * this._itemMargin);
+        let itemsPerRow = Math.floor(this.element.offsetWidth / itemElementWidth);
+
+        let items = Array.from(this.representedObject.items);
+        let index = items.indexOf(this._selectedItem);
+        if (index === -1)
+            index = shift < 0 ? items.length + 1 : itemsPerRow;
+
+        index += shift * itemsPerRow;
+        if (index < 0)
+            index = items.length + index;
+
+        this.setSelectedItem(items[index % items.length]);
+    }
+
+    _changeSelectedItemHorizontally(shift)
+    {
+        let itemElementWidth = this.element.firstElementChild.offsetWidth + (2 * this._itemMargin);
+        let itemsPerRow = Math.floor(this.element.offsetWidth / itemElementWidth);
+
+        let items = Array.from(this.representedObject.items);
+        let index = items.indexOf(this._selectedItem);
+        if (index === -1)
+            index = shift >= 0 ? itemsPerRow - 1 : 0;
+
+        let selectedRow = Math.floor(index / itemsPerRow);
+        index += shift;
+        if (index < selectedRow * itemsPerRow)
+            index += itemsPerRow;
+        else if (index >= (selectedRow + 1) * itemsPerRow)
+            index -= itemsPerRow;
+
+        this.setSelectedItem(items[index]);
+    }
+
+    _updateNavigationItems()
+    {
+        let hasItems = !!this.representedObject.items.size;
+        this._refreshButtonNavigationItem.enabled = hasItems;
+        this._showGridButtonNavigationItem.enabled = hasItems;
+    }
+
     _selectionPathComponentsChanged(event)
     {
         let pathComponent = event.data.pathComponent;
@@ -132,16 +203,39 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
         WI.settings.showImageGrid.value = !this._showGridButtonNavigationItem.activated;
     }
 
-    _supplementalRepresentedObjectsDidChange()
+    _handleUp(event)
     {
-        this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
+        this._changeSelectedItemVertically(-1);
     }
 
-    _updateNavigationItems()
+    _handleRight(event)
+    {
+        let shift = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? -1 : 1;
+        this._changeSelectedItemHorizontally(shift);
+    }
+
+    _handleDown(event)
+    {
+        this._changeSelectedItemVertically(1);
+    }
+
+    _handleLeft(event)
+    {
+        let shift = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? 1 : -1;
+        this._changeSelectedItemHorizontally(shift);
+    }
+
+    _handleSpace(event)
     {
-        let disabled = !this.representedObject.items.size;
-        this._refreshButtonNavigationItem.disabled = disabled;
-        this._showGridButtonNavigationItem.disabled = disabled;
+        if (!this._selectedItem)
+            return;
+
+        if (this._selectedItem.isRecording)
+            WI.canvasManager.stopRecording();
+        else if (!WI.canvasManager.recordingCanvas) {
+            let singleFrame = !!event.shiftKey;
+            WI.canvasManager.startRecording(this._selectedItem, singleFrame);
+        }
     }
 
     _updateShowImageGrid()
@@ -149,6 +243,11 @@ WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.Collec
         this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value;
     }
 
+    _supplementalRepresentedObjectsDidChange()
+    {
+        this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
+    }
+
     _contentViewMouseEnter(event)
     {
         let contentView = WI.View.fromElement(event.target);