Bug 1422554 - Display flex-direction when highlighting flexbox r?gl draft
authorDarren Hobin <darrenhobin@live.com>
Sat, 02 Dec 2017 20:13:43 -0500
changeset 715120 b4254c00859b6345396f1a64221769388ac58516
parent 715114 eaf0381dcd0bb755818e1a043457c57a1be87a03
child 744708 5c146fa90f3d795ab1973063655da38f545092a4
push id94067
push userbmo:darrenhobin@live.com
push dateMon, 01 Jan 2018 19:31:33 +0000
reviewersgl
bugs1422554
milestone59.0a1
Bug 1422554 - Display flex-direction when highlighting flexbox r?gl MozReview-Commit-ID: 3h974apPAat
devtools/server/actors/highlighters/flexbox.js
devtools/server/actors/highlighters/utils/canvas.js
--- a/devtools/server/actors/highlighters/flexbox.js
+++ b/devtools/server/actors/highlighters/flexbox.js
@@ -3,25 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { AutoRefreshHighlighter } = require("./auto-refresh");
 const {
   CANVAS_SIZE,
   DEFAULT_COLOR,
+  DEFAULT_FONT,
   clearRect,
   drawRect,
+  drawText,
   getCurrentMatrix,
   updateCanvasElement,
   updateCanvasPosition,
 } = require("./utils/canvas");
 const {
   CanvasFrameAnonymousContentHelper,
   createNode,
+  getComputedStyle,
 } = require("./utils/markup");
 const {
   getAdjustedQuads,
   getDisplayPixelRatio,
   setIgnoreLayoutChanges,
 } = require("devtools/shared/layout/utils");
 
 const FLEXBOX_LINES_PROPERTIES = {
@@ -34,16 +37,18 @@ const FLEXBOX_LINES_PROPERTIES = {
     alpha: 1,
   },
 };
 
 const FLEXBOX_CONTAINER_PATTERN_WIDTH = 14; // px
 const FLEXBOX_CONTAINER_PATTERN_HEIGHT = 14; // px
 const FLEXBOX_CONTAINER_PATTERN_LINE_DISH = [5, 3]; // px
 
+const FLEXBOX_TEXT_PADDING = 5;
+
 /**
  * Cached used by `FlexboxHighlighter.getFlexContainerPattern`.
  */
 const gCachedFlexboxPattern = new Map();
 
 const FLEXBOX = "flexbox";
 
 class FlexboxHighlighter extends AutoRefreshHighlighter {
@@ -180,16 +185,27 @@ class FlexboxHighlighter extends AutoRef
     ctx.stroke();
     ctx.restore();
 
     let pattern = ctx.createPattern(canvas, "repeat");
     flexboxPatternMap.set(FLEXBOX, pattern);
     gCachedFlexboxPattern.set(devicePixelRatio, flexboxPatternMap);
 
     return pattern;
+}
+
+  /**
+   * Checks if the current node is a flexbox.
+   *
+   * @return {Boolean} true if the current node is a flexbox, false otherwise.
+   */
+  isFlexbox() {
+    let computedStyle = getComputedStyle(this.currentNode);
+    let display = computedStyle.getPropertyValue("display");
+    return display === "flex";
   }
 
   /**
    * The AutoRefreshHighlighter's _hasMoved method returns true only if the
    * element's quads have changed. Override it so it also returns true if the
    * element and its flex items have changed.
    */
   _hasMoved() {
@@ -221,16 +237,21 @@ class FlexboxHighlighter extends AutoRef
       this._winDimensions);
 
     if (hasUpdated) {
       this._update();
     }
   }
 
   _show() {
+    if (!this.isFlexbox()) {
+      this.hide();
+      return false;
+    }
+
     this._hide();
     return this._update();
   }
 
   _showFlexbox() {
     this.getElement("canvas").removeAttribute("hidden");
   }
 
@@ -252,16 +273,117 @@ class FlexboxHighlighter extends AutoRef
   onWillNavigate({ isTopLevel }) {
     this.clearCache();
 
     if (isTopLevel) {
       this.hide();
     }
   }
 
+  renderFlexDirection() {
+    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+      return;
+    }
+
+    let { devicePixelRatio } = this.win;
+    let lineWidth = getDisplayPixelRatio(this.win);
+    let offset = (lineWidth / 2) % 1;
+
+    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
+    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
+
+    this.ctx.save();
+    this.ctx.translate(offset - canvasX, offset - canvasY);
+    this.ctx.globalAlpha = FLEXBOX_LINES_PROPERTIES.edge.alpha;
+    this.ctx.font = DEFAULT_FONT;
+    this.ctx.fillStyle = DEFAULT_COLOR;
+    this.ctx.strokeStyle = DEFAULT_COLOR;
+
+    let { bounds } = this.currentQuads.content[0];
+
+    let computedStyle = getComputedStyle(this.currentNode);
+    let direction = computedStyle.getPropertyValue("flex-direction");
+
+    if (direction === "row") {
+      this.renderFlexDirectionRow(bounds, offset);
+    } else if (direction === "column"){
+      this.renderFlexDirectionColumn(bounds, offset);
+    } else if (direction === "row-reverse") {
+      this.renderFlexDirectionRowReverse(bounds, offset);
+    } else {
+      this.renderFlexDirectionColumnReverse(bounds, offset);
+    }
+
+    this.ctx.restore();
+  }
+
+  renderFlexDirectionColumn(bounds, offset) {
+    this.ctx.save();
+
+    this.ctx.textAlign = "end";
+    drawText(this.ctx, "🡐 COLUMN", -FLEXBOX_TEXT_PADDING, 0, "vertical",
+      this.currentMatrix);
+    this.ctx.textAlign = "center";
+    drawText(this.ctx, "START", bounds.width / 2, -FLEXBOX_TEXT_PADDING,
+      "horizontal", this.currentMatrix);
+    this.ctx.textBaseline = "top";
+    drawText(this.ctx, "END", bounds.width / 2,
+      bounds.height + FLEXBOX_TEXT_PADDING + offset, "horizontal",
+      this.currentMatrix);
+
+    this.ctx.restore();
+  }
+
+  renderFlexDirectionColumnReverse(bounds, offset) {
+    this.ctx.save();
+
+    this.ctx.textAlign = "end";
+    drawText(this.ctx, "COLUMN 🡒", -FLEXBOX_TEXT_PADDING, 0, "vertical",
+      this.currentMatrix);
+    this.ctx.textAlign = "center";
+    drawText(this.ctx, "END", bounds.width / 2, -FLEXBOX_TEXT_PADDING,
+      "horizontal", this.currentMatrix);
+    this.ctx.textBaseline = "top";
+    drawText(this.ctx, "START", bounds.width / 2,
+      bounds.height + FLEXBOX_TEXT_PADDING + offset, "horizontal",
+      this.currentMatrix);
+
+    this.ctx.restore();
+  }
+
+  renderFlexDirectionRow(bounds, offset) {
+    this.ctx.save();
+
+    drawText(this.ctx, "ROW 🡒", 0, -FLEXBOX_TEXT_PADDING, "horizontal",
+      this.currentMatrix);
+    this.ctx.textAlign = "center";
+    drawText(this.ctx, "START", -FLEXBOX_TEXT_PADDING, bounds.height / 2,
+      "vertical", this.currentMatrix);
+    this.ctx.textBaseline = "top";
+    drawText(this.ctx, "END", bounds.width + FLEXBOX_TEXT_PADDING + offset,
+      bounds.height / 2, "vertical", this.currentMatrix);
+
+    this.ctx.restore();
+  }
+
+  renderFlexDirectionRowReverse(bounds, offset) {
+    this.ctx.save();
+
+    drawText(this.ctx, "🡐 ROW", 0, -FLEXBOX_TEXT_PADDING, "horizontal",
+      this.currentMatrix);
+    this.ctx.textAlign = "center";
+    drawText(this.ctx, "END", -FLEXBOX_TEXT_PADDING, bounds.height / 2,
+      "vertical", this.currentMatrix);
+    this.ctx.textBaseline = "top";
+    drawText(this.ctx, "START", bounds.width + FLEXBOX_TEXT_PADDING + offset,
+      bounds.height / 2, "vertical", this.currentMatrix);
+
+    this.ctx.restore();
+  }
+
   renderFlexContainer() {
     if (!this.currentQuads.content || !this.currentQuads.content[0]) {
       return;
     }
 
     let { devicePixelRatio } = this.win;
     let lineWidth = getDisplayPixelRatio(this.win);
     let offset = (lineWidth / 2) % 1;
@@ -348,16 +470,17 @@ class FlexboxHighlighter extends AutoRef
 
     // Update the current matrix used in our canvas' rendering
     let { currentMatrix, hasNodeTransformations } = getCurrentMatrix(this.currentNode,
       this.win);
     this.currentMatrix = currentMatrix;
     this.hasNodeTransformations = hasNodeTransformations;
 
     this.renderFlexContainer();
+    this.renderFlexDirection();
     this.renderFlexItems();
 
     this._showFlexbox();
 
     root.setAttribute("style",
       `position: absolute; width: ${width}px; height: ${height}px; overflow: hidden`);
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
--- a/devtools/server/actors/highlighters/utils/canvas.js
+++ b/devtools/server/actors/highlighters/utils/canvas.js
@@ -33,16 +33,19 @@ const { getViewportDimensions } = requir
 // the displayport API instead.
 //
 // Using a fixed value should also solve bug 1348293.
 const CANVAS_SIZE = 4096;
 
 // The default color used for the canvas' font, fill and stroke colors.
 const DEFAULT_COLOR = "#9400FF";
 
+// The default font used for the canvas' font.
+const DEFAULT_FONT = "10px sans-serif";
+
 /**
  * Draws a rect to the context given and applies a transformation matrix if passed.
  * The coordinates are the start and end points of the rectangle's diagonal.
  *
  * @param  {CanvasRenderingContext2D} ctx
  *         The 2D canvas context.
  * @param  {Number} x1
  *         The x-axis coordinate of the rectangle's diagonal start point.
@@ -225,16 +228,47 @@ function drawRoundedRect(ctx, x, y, widt
   ctx.arcTo(x + width, y, x + width - radius, y, radius);
   ctx.lineTo(x + radius, y);
   ctx.arcTo(x, y, x, y + radius, radius);
   ctx.stroke();
   ctx.fill();
 }
 
 /**
+ * Draws text in the provided canvas context.
+ *
+ * @param  {CanvasRenderingContext2D} ctx
+ *         The 2D canvas context.
+ * @param  {String} content
+ *         The text to print on the canvas.
+ * @param  {Number} x
+ *         The x-axis origin of the text.
+ * @param  {Number} y
+ *         The y-axis origin of the text.
+ * @param  {String} direction
+ *         The direction that the text should be printed in.
+ * @param  {Array} [matrix=identity()]
+ *         The transformation matrix to apply.
+ */
+function drawText(ctx, content, x, y, direction, matrix = identity()) {
+  let p = apply(matrix, [x, y]);
+
+  ctx.save();
+
+  ctx.translate(p[0], p[1]);
+  if (direction === "vertical") {
+    ctx.rotate(Math.PI * 1.5);
+  }
+
+  ctx.fillText(content, 0, 0);
+
+  ctx.restore();
+}
+
+/**
  * Given an array of four points and returns a DOMRect-like object representing the
  * boundaries defined by the four points.
  *
  * @param  {Array} points
  *         An array with 4 pointer objects {x, y} representing the box quads.
  * @return {Object} DOMRect-like object of the 4 points.
  */
 function getBoundsFromPoints(points) {
@@ -447,19 +481,21 @@ function updateCanvasPosition(canvasPosi
   canvasPosition.x = canvasX;
   canvasPosition.y = canvasY;
 
   return hasUpdated;
 }
 
 exports.CANVAS_SIZE = CANVAS_SIZE;
 exports.DEFAULT_COLOR = DEFAULT_COLOR;
+exports.DEFAULT_FONT = DEFAULT_FONT;
 exports.clearRect = clearRect;
 exports.drawBubbleRect = drawBubbleRect;
 exports.drawLine = drawLine;
 exports.drawRect = drawRect;
 exports.drawRoundedRect = drawRoundedRect;
+exports.drawText = drawText;
 exports.getBoundsFromPoints = getBoundsFromPoints;
 exports.getCurrentMatrix = getCurrentMatrix;
 exports.getPathDescriptionFromPoints = getPathDescriptionFromPoints;
 exports.getPointsFromDiagonal = getPointsFromDiagonal;
 exports.updateCanvasElement = updateCanvasElement;
 exports.updateCanvasPosition = updateCanvasPosition;