Bug 1406285 - Part 13: Implement tooltip. r=gl
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Thu, 18 Jan 2018 13:03:57 +0900
changeset 454269 217f71d0cb61b3adcf3aa2c4763e1cdb0d72d31d
parent 454268 2f2996b6664eb6fa684f6fad9c092fa48f439d76
child 454270 0f89a44387fd30d262f7a22a962f6d9ecf8d0287
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1406285
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1406285 - Part 13: Implement tooltip. r=gl MozReview-Commit-ID: 3Icr9lJUQhl
devtools/client/inspector/animation/components/graph/SummaryGraph.js
devtools/client/inspector/animation/utils/l10n.js
--- a/devtools/client/inspector/animation/components/graph/SummaryGraph.js
+++ b/devtools/client/inspector/animation/components/graph/SummaryGraph.js
@@ -8,35 +8,134 @@ const { createFactory, PureComponent } =
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const AnimationName = createFactory(require("./AnimationName"));
 const DelaySign = createFactory(require("./DelaySign"));
 const EndDelaySign = createFactory(require("./EndDelaySign"));
 const SummaryGraphPath = createFactory(require("./SummaryGraphPath"));
 
+const { getFormatStr, getStr, numberWithDecimals } = require("../../utils/l10n");
+
 class SummaryGraph extends PureComponent {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
       simulateAnimation: PropTypes.func.isRequired,
       timeScale: PropTypes.object.isRequired,
     };
   }
 
+  getTitleText(state) {
+    const getTime =
+      time => getFormatStr("player.timeLabel", numberWithDecimals(time / 1000, 2));
+
+    let text = "";
+
+    // Adding the name.
+    text += getFormattedTitle(state);
+    text += "\n";
+
+    // Adding the delay.
+    if (state.delay) {
+      text += getStr("player.animationDelayLabel") + " ";
+      text += getTime(state.delay);
+      text += "\n";
+    }
+
+    // Adding the duration.
+    text += getStr("player.animationDurationLabel") + " ";
+    text += getTime(state.duration);
+    text += "\n";
+
+    // Adding the endDelay.
+    if (state.endDelay) {
+      text += getStr("player.animationEndDelayLabel") + " ";
+      text += getTime(state.endDelay);
+      text += "\n";
+    }
+
+    // Adding the iteration count (the infinite symbol, or an integer).
+    if (state.iterationCount !== 1) {
+      text += getStr("player.animationIterationCountLabel") + " ";
+      text += state.iterationCount || getStr("player.infiniteIterationCountText");
+      text += "\n";
+    }
+
+    // Adding the iteration start.
+    if (state.iterationStart !== 0) {
+      const iterationStartTime = state.iterationStart * state.duration / 1000;
+      text += getFormatStr("player.animationIterationStartLabel",
+                           state.iterationStart,
+                           numberWithDecimals(iterationStartTime, 2));
+      text += "\n";
+    }
+
+    // Adding the easing if it is not "linear".
+    if (state.easing && state.easing !== "linear") {
+      text += getStr("player.animationOverallEasingLabel") + " ";
+      text += state.easing;
+      text += "\n";
+    }
+
+    // Adding the fill mode.
+    if (state.fill && state.fill !== "none") {
+      text += getStr("player.animationFillLabel") + " ";
+      text += state.fill;
+      text += "\n";
+    }
+
+    // Adding the direction mode if it is not "normal".
+    if (state.direction && state.direction !== "normal") {
+      text += getStr("player.animationDirectionLabel") + " ";
+      text += state.direction;
+      text += "\n";
+    }
+
+    // Adding the playback rate if it's different than 1.
+    if (state.playbackRate !== 1) {
+      text += getStr("player.animationRateLabel") + " ";
+      text += state.playbackRate;
+      text += "\n";
+    }
+
+    // Adding the animation-timing-function
+    // if it is not "ease" which is default value for CSS Animations.
+    if (state.animationTimingFunction && state.animationTimingFunction !== "ease") {
+      text += getStr("player.animationTimingFunctionLabel") + " ";
+      text += state.animationTimingFunction;
+      text += "\n";
+    }
+
+    // Adding a note that the animation is running on the compositor thread if
+    // needed.
+    if (state.propertyState) {
+      if (state.propertyState.every(propState => propState.runningOnCompositor)) {
+        text += getStr("player.allPropertiesOnCompositorTooltip");
+      } else if (state.propertyState.some(propState => propState.runningOnCompositor)) {
+        text += getStr("player.somePropertiesOnCompositorTooltip");
+      }
+    } else if (state.isRunningOnCompositor) {
+      text += getStr("player.runningOnCompositorTooltip");
+    }
+
+    return text;
+  }
+
   render() {
     const {
       animation,
       simulateAnimation,
       timeScale,
     } = this.props;
 
     return dom.div(
       {
         className: "animation-summary-graph",
+        title: this.getTitleText(animation.state),
       },
       SummaryGraphPath(
         {
           animation,
           simulateAnimation,
           timeScale,
         }
       ),
@@ -65,9 +164,34 @@ class SummaryGraph extends PureComponent
           }
         )
       :
       null
     );
   }
 }
 
+/**
+ * Get a formatted title for this animation. This will be either:
+ * "%S", "%S : CSS Transition", "%S : CSS Animation",
+ * "%S : Script Animation", or "Script Animation", depending
+ * if the server provides the type, what type it is and if the animation
+ * has a name.
+ *
+ * @param {Object} state
+ */
+function getFormattedTitle(state) {
+  // Older servers don't send a type, and only know about
+  // CSSAnimations and CSSTransitions, so it's safe to use
+  // just the name.
+  if (!state.type) {
+    return state.name;
+  }
+
+  // Script-generated animations may not have a name.
+  if (state.type === "scriptanimation" && !state.name) {
+    return getStr("timeline.scriptanimation.unnamedLabel");
+  }
+
+  return getFormatStr(`timeline.${state.type}.nameLabel`, state.name);
+}
+
 module.exports = SummaryGraph;
--- a/devtools/client/inspector/animation/utils/l10n.js
+++ b/devtools/client/inspector/animation/utils/l10n.js
@@ -5,9 +5,11 @@
 "use strict";
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const L10N =
   new LocalizationHelper("devtools/client/locales/animationinspector.properties");
 
 module.exports = {
   getFormatStr: (...args) => L10N.getFormatStr(...args),
+  getStr: (...args) => L10N.getStr(...args),
+  numberWithDecimals: (...args) => L10N.numberWithDecimals(...args),
 };