Bug 1208204 - Pressing space in the animation inspector should toggle play/pause. r?pbrosset draft keyboard_space
authorNicolas Chevobbe <chevobbe.nicolas@gmail.com>
Sat, 13 Feb 2016 17:07:05 +0100
changeset 331859 dacc05065713b3ebf1ee37921effcda4c3ae1f58
parent 329852 62437a8123876ca69e54c68a40659704139ce2d5
child 514481 a5e2d25853817641a21f7bad95da4c9362cfc94a
push id11094
push userchevobbe.nicolas@gmail.com
push dateThu, 18 Feb 2016 12:43:43 +0000
reviewerspbrosset
bugs1208204
milestone47.0a1
Bug 1208204 - Pressing space in the animation inspector should toggle play/pause. r?pbrosset MozReview-Commit-ID: 3NxNIaPjemt
devtools/client/animationinspector/animation-controller.js
devtools/client/animationinspector/animation-panel.js
devtools/client/animationinspector/test/browser.ini
devtools/client/animationinspector/test/browser_animation_spacebar_toggles_animations.js
devtools/client/animationinspector/test/browser_animation_spacebar_toggles_node_animations.js
--- a/devtools/client/animationinspector/animation-controller.js
+++ b/devtools/client/animationinspector/animation-controller.js
@@ -121,17 +121,17 @@ var getServerTraits = Task.async(functio
  * function onPlayers() {
  *   for (let player of AnimationsController.animationPlayers) {
  *     // do something with player
  *   }
  * }
  */
 var AnimationsController = {
   PLAYERS_UPDATED_EVENT: "players-updated",
-
+  ALL_ANIMATIONS_TOGGLED_EVENT: "all-animations-toggled",
   initialize: Task.async(function*() {
     if (this.initialized) {
       yield this.initialized.promise;
       return;
     }
     this.initialized = promise.defer();
 
     this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
@@ -250,17 +250,19 @@ var AnimationsController = {
   /**
    * Toggle (pause/play) all animations in the current target.
    */
   toggleAll: function() {
     if (!this.traits.hasToggleAll) {
       return promise.resolve();
     }
 
-    return this.animationsFront.toggleAll().catch(e => console.error(e));
+    return this.animationsFront.toggleAll()
+      .then(() => this.emit(this.ALL_ANIMATIONS_TOGGLED_EVENT, this))
+      .catch(e => console.error(e));
   },
 
   /**
    * Similar to toggleAll except that it only plays/pauses the currently known
    * animations (those listed in this.animationPlayers).
    * @param {Boolean} shouldPause True if the animations should be paused, false
    * if they should be played.
    * @return {Promise} Resolves when the playState has been changed.
--- a/devtools/client/animationinspector/animation-panel.js
+++ b/devtools/client/animationinspector/animation-panel.js
@@ -47,20 +47,20 @@ var AnimationsPanel = {
 
     // If the server doesn't support toggling all animations at once, hide the
     // whole global toolbar.
     if (!AnimationsController.traits.hasToggleAll) {
       $("#global-toolbar").style.display = "none";
     }
 
     // Binding functions that need to be called in scope.
-    for (let functionName of ["onPickerStarted", "onPickerStopped",
-      "refreshAnimationsUI", "toggleAll", "onTabNavigated",
-      "onTimelineDataChanged", "playPauseTimeline", "rewindTimeline",
-      "onRateChanged"]) {
+    for (let functionName of ["onKeyDown", "onPickerStarted",
+      "onPickerStopped", "refreshAnimationsUI", "onToggleAllClicked",
+      "onTabNavigated", "onTimelineDataChanged", "onTimelinePlayClicked",
+      "onTimelineRewindClicked", "onRateChanged"]) {
       this[functionName] = this[functionName].bind(this);
     }
     let hUtils = gToolbox.highlighterUtils;
     this.togglePicker = hUtils.togglePicker.bind(hUtils);
 
     this.animationsTimelineComponent = new AnimationsTimeline(gInspector);
     this.animationsTimelineComponent.init(this.playersEl);
 
@@ -109,19 +109,23 @@ var AnimationsPanel = {
   startListeners: function() {
     AnimationsController.on(AnimationsController.PLAYERS_UPDATED_EVENT,
       this.refreshAnimationsUI);
 
     this.pickerButtonEl.addEventListener("click", this.togglePicker);
     gToolbox.on("picker-started", this.onPickerStarted);
     gToolbox.on("picker-stopped", this.onPickerStopped);
 
-    this.toggleAllButtonEl.addEventListener("click", this.toggleAll);
-    this.playTimelineButtonEl.addEventListener("click", this.playPauseTimeline);
-    this.rewindTimelineButtonEl.addEventListener("click", this.rewindTimeline);
+    this.toggleAllButtonEl.addEventListener("click", this.onToggleAllClicked);
+    this.playTimelineButtonEl.addEventListener(
+      "click", this.onTimelinePlayClicked);
+    this.rewindTimelineButtonEl.addEventListener(
+      "click", this.onTimelineRewindClicked);
+
+    document.addEventListener("keydown", this.onKeyDown, false);
 
     gToolbox.target.on("navigate", this.onTabNavigated);
 
     this.animationsTimelineComponent.on("timeline-data-changed",
       this.onTimelineDataChanged);
 
     if (this.rateSelectorComponent) {
       this.rateSelectorComponent.on("rate-changed", this.onRateChanged);
@@ -131,30 +135,50 @@ var AnimationsPanel = {
   stopListeners: function() {
     AnimationsController.off(AnimationsController.PLAYERS_UPDATED_EVENT,
       this.refreshAnimationsUI);
 
     this.pickerButtonEl.removeEventListener("click", this.togglePicker);
     gToolbox.off("picker-started", this.onPickerStarted);
     gToolbox.off("picker-stopped", this.onPickerStopped);
 
-    this.toggleAllButtonEl.removeEventListener("click", this.toggleAll);
-    this.playTimelineButtonEl.removeEventListener("click", this.playPauseTimeline);
-    this.rewindTimelineButtonEl.removeEventListener("click", this.rewindTimeline);
+    this.toggleAllButtonEl.removeEventListener("click", this.onToggleAllClicked);
+    this.playTimelineButtonEl.removeEventListener(
+      "click", this.onTimelinePlayClicked);
+    this.rewindTimelineButtonEl.removeEventListener(
+      "click", this.onTimelineRewindClicked);
+
+    document.removeEventListener("keydown", this.onKeyDown, false);
 
     gToolbox.target.off("navigate", this.onTabNavigated);
 
     this.animationsTimelineComponent.off("timeline-data-changed",
       this.onTimelineDataChanged);
 
     if (this.rateSelectorComponent) {
       this.rateSelectorComponent.off("rate-changed", this.onRateChanged);
     }
   },
 
+  onKeyDown: function(event) {
+    let keyEvent = Ci.nsIDOMKeyEvent;
+
+    // If the space key is pressed, it should toggle the play state of
+    // the animations displayed in the panel, or of all the animations on
+    // the page if the selected node does not have any animation on it.
+    if (event.keyCode === keyEvent.DOM_VK_SPACE) {
+      if (AnimationsController.animationPlayers.length > 0) {
+        this.playPauseTimeline().catch(ex => console.error(ex));
+      } else {
+        this.toggleAll().catch(ex => console.error(ex));
+      }
+      event.preventDefault();
+    }
+  },
+
   togglePlayers: function(isVisible) {
     if (isVisible) {
       document.body.removeAttribute("empty");
       document.body.setAttribute("timeline", "true");
     } else {
       document.body.setAttribute("empty", "true");
       document.body.removeAttribute("timeline");
     }
@@ -163,52 +187,73 @@ var AnimationsPanel = {
   onPickerStarted: function() {
     this.pickerButtonEl.setAttribute("checked", "true");
   },
 
   onPickerStopped: function() {
     this.pickerButtonEl.removeAttribute("checked");
   },
 
+  onToggleAllClicked: function() {
+    this.toggleAll().catch(ex => console.error(ex));
+  },
+
+  /**
+   * Toggle (pause/play) all animations in the current target
+   * and update the UI the toggleAll button.
+   */
   toggleAll: Task.async(function*() {
     this.toggleAllButtonEl.classList.toggle("paused");
     yield AnimationsController.toggleAll();
   }),
 
+  onTimelinePlayClicked: function() {
+    this.playPauseTimeline().catch(ex => console.error(ex));
+  },
+
   /**
    * Depending on the state of the timeline either pause or play the animations
    * displayed in it.
    * If the animations are finished, this will play them from the start again.
    * If the animations are playing, this will pause them.
    * If the animations are paused, this will resume them.
+   *
+   * @return {Promise} Resolves when the playState is changed and the UI
+   * is refreshed
    */
   playPauseTimeline: function() {
-    AnimationsController.toggleCurrentAnimations(this.timelineData.isMoving)
-                        .then(() => this.refreshAnimationsStateAndUI())
-                        .catch(e => console.error(e));
+    return AnimationsController
+      .toggleCurrentAnimations(this.timelineData.isMoving)
+      .then(() => this.refreshAnimationsStateAndUI());
+  },
+
+  onTimelineRewindClicked: function() {
+    this.rewindTimeline().catch(ex => console.error(ex));
   },
 
   /**
    * Reset the startTime of all current animations shown in the timeline and
    * pause them.
+   *
+   * @return {Promise} Resolves when currentTime is set and the UI is refreshed
    */
   rewindTimeline: function() {
-    AnimationsController.setCurrentTimeAll(0, true)
-                        .then(() => this.refreshAnimationsStateAndUI())
-                        .catch(e => console.error(e));
+    return AnimationsController
+      .setCurrentTimeAll(0, true)
+      .then(() => this.refreshAnimationsStateAndUI());
   },
 
   /**
    * Set the playback rate of all current animations shown in the timeline to
    * the value of this.rateSelectorEl.
    */
   onRateChanged: function(e, rate) {
     AnimationsController.setPlaybackRateAll(rate)
                         .then(() => this.refreshAnimationsStateAndUI())
-                        .catch(e => console.error(e));
+                        .catch(ex => console.error(ex));
   },
 
   onTabNavigated: function() {
     this.toggleAllButtonEl.classList.remove("paused");
   },
 
   onTimelineDataChanged: function(e, data) {
     this.timelineData = data;
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -25,16 +25,18 @@ skip-if = os == "linux" && !debug # Bug 
 [browser_animation_playerWidgets_target_nodes.js]
 [browser_animation_refresh_on_added_animation.js]
 [browser_animation_refresh_on_removed_animation.js]
 skip-if = os == "linux" && !debug # Bug 1227792
 [browser_animation_refresh_when_active.js]
 [browser_animation_running_on_compositor.js]
 [browser_animation_same_nb_of_playerWidgets_and_playerFronts.js]
 [browser_animation_shows_player_on_valid_node.js]
+[browser_animation_spacebar_toggles_animations.js]
+[browser_animation_spacebar_toggles_node_animations.js]
 [browser_animation_target_highlight_select.js]
 [browser_animation_target_highlighter_lock.js]
 [browser_animation_timeline_currentTime.js]
 [browser_animation_timeline_header.js]
 [browser_animation_timeline_pause_button.js]
 skip-if = os == "linux" && bits == 32 # Bug 1220974
 [browser_animation_timeline_rate_selector.js]
 [browser_animation_timeline_rewind_button.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_spacebar_toggles_animations.js
@@ -0,0 +1,43 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the spacebar key press toggles the toggleAll button state
+// when a node with no animation is selected.
+// This test doesn't need to test if animations actually pause/resume
+// because there's an other test that does this :
+// browser_animation_toggle_button_toggles_animation.js
+
+add_task(function*() {
+  yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
+  let {panel, inspector, window, controller} = yield openAnimationInspector();
+  let {toggleAllButtonEl} = panel;
+
+  // select a node without animations
+  yield selectNode(".still", inspector);
+
+  // ensure the focus is on the animation panel
+  window.focus();
+
+  info("Simulate spacebar stroke and check toggleAll button" +
+       " is in paused state");
+
+  // sending the key will lead to a ALL_ANIMATIONS_TOGGLED_EVENT
+  let onToggled = once(controller, controller.ALL_ANIMATIONS_TOGGLED_EVENT);
+  EventUtils.sendKey("SPACE", window);
+  yield onToggled;
+  ok(toggleAllButtonEl.classList.contains("paused"),
+   "The toggle all button is in its paused state");
+
+  info("Simulate spacebar stroke and check toggleAll button" +
+       " is in playing state");
+
+  // sending the key will lead to a ALL_ANIMATIONS_TOGGLED_EVENT
+  onToggled = once(controller, controller.ALL_ANIMATIONS_TOGGLED_EVENT);
+  EventUtils.sendKey("SPACE", window);
+  yield onToggled;
+  ok(!toggleAllButtonEl.classList.contains("paused"),
+   "The toggle all button is in its playing state again");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/animationinspector/test/browser_animation_spacebar_toggles_node_animations.js
@@ -0,0 +1,38 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the spacebar key press toggles the play/resume button state.
+// This test doesn't need to test if animations actually pause/resume
+// because there's an other test that does this.
+
+add_task(function*() {
+  yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
+  let {panel, window} = yield openAnimationInspector();
+  let {playTimelineButtonEl} = panel;
+
+  // ensure the focus is on the animation panel
+  window.focus();
+
+  info("Simulate spacebar stroke and check playResume button" +
+       " is in paused state");
+
+  // sending the key will lead to a UI_UPDATE_EVENT
+  let onUpdated = panel.once(panel.UI_UPDATED_EVENT);
+  EventUtils.sendKey("SPACE", window);
+  yield onUpdated;
+  ok(playTimelineButtonEl.classList.contains("paused"),
+    "The play/resume button is in its paused state");
+
+  info("Simulate spacebar stroke and check playResume button" +
+       " is in playing state");
+
+  // sending the key will lead to a UI_UPDATE_EVENT
+  onUpdated = panel.once(panel.UI_UPDATED_EVENT);
+  EventUtils.sendKey("SPACE", window);
+  yield onUpdated;
+  ok(!playTimelineButtonEl.classList.contains("paused"),
+    "The play/resume button is in its play state again");
+});