--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -16,19 +16,21 @@ const {
const {
getCurrentZoom,
setIgnoreLayoutChanges,
getWindowDimensions
} = require("devtools/shared/layout/utils");
const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
+
const DEFAULT_GRID_COLOR = "#4B0082";
+
+const COLUMNS = "cols";
const ROWS = "rows";
-const COLUMNS = "cols";
const GRID_LINES_PROPERTIES = {
"edge": {
lineDash: [0, 0],
alpha: 1,
},
"explicit": {
lineDash: [5, 3],
@@ -64,41 +66,56 @@ const COLUMN_KEY = {};
* h.show(node, options);
* h.hide();
* h.destroy();
*
* Available Options:
* - color(colorValue)
* @param {String} colorValue
* The color that should be used to draw the highlighter for this grid.
+ * - showAllGridAreas(isShown)
+ * @param {Boolean} isShown
+ * Shows all the grid area highlights for the current grid if isShown is true.
* - showGridArea(areaName)
* @param {String} areaName
* Shows the grid area highlight for the given area name.
- * - showAllGridAreas
- * Shows all the grid area highlights for the current grid.
+ * - showGridCell({ gridFragmentIndex: Number, rowNumber: Number, columnNumber: Number })
+ * @param {Object} { gridFragmentIndex: Number, rowNumber: Number,
+ * columnNumber: Number }
+ * An object containing the grid fragment index, row and column numbers to the
+ * corresponding grid cell to highlight for the current grid.
* - showGridLineNumbers(isShown)
- * @param {Boolean}
+ * @param {Boolean} isShown
* Displays the grid line numbers on the grid lines if isShown is true.
* - showInfiniteLines(isShown)
* @param {Boolean} isShown
* Displays an infinite line to represent the grid lines if isShown is true.
*
* Structure:
* <div class="highlighter-container">
* <canvas id="css-grid-canvas" class="css-grid-canvas">
* <svg class="css-grid-elements" hidden="true">
* <g class="css-grid-regions">
* <path class="css-grid-areas" points="..." />
+ * <path class="css-grid-cells" points="..." />
* </g>
* </svg>
- * <div class="css-grid-infobar-container">
+ * <div class="css-grid-area-infobar-container">
* <div class="css-grid-infobar">
* <div class="css-grid-infobar-text">
- * <span class="css-grid-infobar-areaname">Grid Area Name</span>
- * <span class="css-grid-infobar-dimensions"Grid Area Dimensions></span>
+ * <span class="css-grid-area-infobar-name">Grid Area Name</span>
+ * <span class="css-grid-area-infobar-dimensions"Grid Area Dimensions></span>
+ * </div>
+ * </div>
+ * </div>
+ * <div class="css-grid-cell-infobar-container">
+ * <div class="css-grid-infobar">
+ * <div class="css-grid-infobar-text">
+ * <span class="css-grid-cell-infobar-position">Grid Cell Position</span>
+ * <span class="css-grid-cell-infobar-dimensions"Grid Cell Dimensions></span>
* </div>
* </div>
* </div>
* </div>
*/
function CssGridHighlighter(highlighterEnv) {
AutoRefreshHighlighter.call(this, highlighterEnv);
@@ -178,58 +195,114 @@ CssGridHighlighter.prototype = extend(Au
parent: regions,
attributes: {
"class": "areas",
"id": "areas"
},
prefix: this.ID_CLASS_PREFIX
});
- // Building the grid infobar markup
- let infobarContainer = createNode(this.win, {
+ createSVGNode(this.win, {
+ nodeType: "path",
+ parent: regions,
+ attributes: {
+ "class": "cells",
+ "id": "cells"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ // Building the grid area infobar markup
+ let areaInfobarContainer = createNode(this.win, {
parent: container,
attributes: {
- "class": "infobar-container",
- "id": "infobar-container",
+ "class": "area-infobar-container",
+ "id": "area-infobar-container",
"position": "top",
"hidden": "true"
},
prefix: this.ID_CLASS_PREFIX
});
- let infobar = createNode(this.win, {
- parent: infobarContainer,
+ let areaInfobar = createNode(this.win, {
+ parent: areaInfobarContainer,
attributes: {
"class": "infobar"
},
prefix: this.ID_CLASS_PREFIX
});
- let textbox = createNode(this.win, {
- parent: infobar,
+ let areaTextbox = createNode(this.win, {
+ parent: areaInfobar,
attributes: {
"class": "infobar-text"
},
prefix: this.ID_CLASS_PREFIX
});
createNode(this.win, {
nodeType: "span",
- parent: textbox,
+ parent: areaTextbox,
attributes: {
- "class": "infobar-areaname",
- "id": "infobar-areaname"
+ "class": "area-infobar-name",
+ "id": "area-infobar-name"
},
prefix: this.ID_CLASS_PREFIX
});
createNode(this.win, {
nodeType: "span",
- parent: textbox,
+ parent: areaTextbox,
+ attributes: {
+ "class": "area-infobar-dimensions",
+ "id": "area-infobar-dimensions"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ // Building the grid cell infobar markup
+ let cellInfobarContainer = createNode(this.win, {
+ parent: container,
+ attributes: {
+ "class": "cell-infobar-container",
+ "id": "cell-infobar-container",
+ "position": "top",
+ "hidden": "true"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ let cellInfobar = createNode(this.win, {
+ parent: cellInfobarContainer,
attributes: {
- "class": "infobar-dimensions",
- "id": "infobar-dimensions"
+ "class": "infobar"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ let cellTextbox = createNode(this.win, {
+ parent: cellInfobar,
+ attributes: {
+ "class": "infobar-text"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+ createNode(this.win, {
+ nodeType: "span",
+ parent: cellTextbox,
+ attributes: {
+ "class": "cell-infobar-position",
+ "id": "cell-infobar-position"
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+ createNode(this.win, {
+ nodeType: "span",
+ parent: cellTextbox,
+ attributes: {
+ "class": "cell-infobar-dimensions",
+ "id": "cell-infobar-dimensions"
},
prefix: this.ID_CLASS_PREFIX
});
return container;
},
destroy() {
@@ -339,33 +412,53 @@ CssGridHighlighter.prototype = extend(Au
/**
* Shows the grid area highlight for the given area name.
*
* @param {String} areaName
* Grid area name.
*/
showGridArea(areaName) {
this.renderGridArea(areaName);
- this._showGridArea();
},
/**
* Shows all the grid area highlights for the current grid.
*/
showAllGridAreas() {
this.renderGridArea();
- this._showGridArea();
},
/**
* Clear the grid area highlights.
*/
clearGridAreas() {
- let box = this.getElement("areas");
- box.setAttribute("d", "");
+ let areas = this.getElement("areas");
+ areas.setAttribute("d", "");
+ },
+
+ /**
+ * Shows the grid cell highlight for the given grid cell options.
+ *
+ * @param {Number} options.gridFragmentIndex
+ * Index of the grid fragment to render the grid cell highlight.
+ * @param {Number} options.rowNumber
+ * Row number of the grid cell to highlight.
+ * @param {Number} options.columnNumber
+ * Column number of the grid cell to highlight.
+ */
+ showGridCell({ gridFragmentIndex, rowNumber, columnNumber }) {
+ this.renderGridCell(gridFragmentIndex, rowNumber, columnNumber);
+ },
+
+ /**
+ * Clear the grid cell highlights.
+ */
+ clearGridCell() {
+ let cells = this.getElement("cells");
+ cells.setAttribute("d", "");
},
/**
* Checks if the current node has a CSS Grid layout.
*
* @return {Boolean} true if the current node has a CSS grid layout, false otherwise.
*/
isGrid() {
@@ -417,76 +510,97 @@ CssGridHighlighter.prototype = extend(Au
// Display the grid area highlights if needed.
if (this.options.showAllGridAreas) {
this.showAllGridAreas();
} else if (this.options.showGridArea) {
this.showGridArea(this.options.showGridArea);
}
+ // Display the grid cell highlights if needed.
+ if (this.options.showGridCell) {
+ this.showGridCell(this.options.showGridCell);
+ }
+
this._showGrid();
+ this._showGridElements();
root.setAttribute("style",
`position:absolute; width:${width}px;height:${height}px; overflow:hidden`);
setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
return true;
},
/**
- * Update the grid information displayed in the grid info bar.
+ * Update the grid information displayed in the grid area info bar.
*
* @param {GridArea} area
* The grid area object.
* @param {Number} x1
* The first x-coordinate of the grid area rectangle.
* @param {Number} x2
* The second x-coordinate of the grid area rectangle.
* @param {Number} y1
* The first y-coordinate of the grid area rectangle.
* @param {Number} y2
* The second y-coordinate of the grid area rectangle.
*/
- _updateInfobar(area, x1, x2, y1, y2) {
+ _updateGridAreaInfobar(area, x1, x2, y1, y2) {
let width = x2 - x1;
let height = y2 - y1;
let dim = parseFloat(width.toPrecision(6)) +
" \u00D7 " +
parseFloat(height.toPrecision(6));
- this.getElement("infobar-areaname").setTextContent(area.name);
- this.getElement("infobar-dimensions").setTextContent(dim);
+ this.getElement("area-infobar-name").setTextContent(area.name);
+ this.getElement("area-infobar-dimensions").setTextContent(dim);
+
+ let container = this.getElement("area-infobar-container");
+ this._moveInfobar(container, x1, x2, y1, y2);
+ },
- this._moveInfobar(x1, x2, y1, y2);
+ _updateGridCellInfobar(rowNumber, columnNumber, x1, x2, y1, y2) {
+ let width = x2 - x1;
+ let height = y2 - y1;
+ let dim = parseFloat(width.toPrecision(6)) +
+ " \u00D7 " +
+ parseFloat(height.toPrecision(6));
+ let position = `${rowNumber}\/${columnNumber}`;
+
+ this.getElement("cell-infobar-position").setTextContent(position);
+ this.getElement("cell-infobar-dimensions").setTextContent(dim);
+
+ let container = this.getElement("cell-infobar-container");
+ this._moveInfobar(container, x1, x2, y1, y2);
},
/**
- * Move the grid infobar to the right place in the highlighter.
+ * Move the given grid infobar to the right place in the highlighter.
*
* @param {Number} x1
- * The first x-coordinate of the grid area rectangle.
+ * The first x-coordinate of the grid rectangle.
* @param {Number} x2
- * The second x-coordinate of the grid area rectangle.
+ * The second x-coordinate of the grid rectangle.
* @param {Number} y1
- * The first y-coordinate of the grid area rectangle.
+ * The first y-coordinate of the grid rectangle.
* @param {Number} y2
- * The second y-coordinate of the grid area rectangle.
+ * The second y-coordinate of the grid rectangle.
*/
- _moveInfobar(x1, x2, y1, y2) {
+ _moveInfobar(container, x1, x2, y1, y2) {
let bounds = {
bottom: y2,
height: y2 - y1,
left: x1,
right: x2,
top: y1,
width: x2 - x1,
x: x1,
y: y1,
};
- let container = this.getElement("infobar-container");
moveInfobar(container, bounds, this.win);
},
clearCanvas(width, height) {
let ratio = parseFloat((this.win.devicePixelRatio || 1).toFixed(2));
// Resize the canvas taking the dpr into account so as to have crisp lines.
@@ -610,17 +724,17 @@ CssGridHighlighter.prototype = extend(Au
* The line position along the x-axis for a column grid line and
* y-axis for a row grid line.
* @param {Number} startPos
* The start position of the cross side of the grid line.
* @param {Number} endPos
* The end position of the cross side of the grid line.
* @param {String} dimensionType
* The grid dimension type which is either the constant COLUMNS or ROWS.
- * @param {[type]} lineType
+ * @param {String} lineType
* The grid line type - "edge", "explicit", or "implicit".
*/
renderLine(linePos, startPos, endPos, dimensionType, lineType) {
this.ctx.save();
this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
this.ctx.beginPath();
this.ctx.translate(.5, .5);
@@ -734,56 +848,108 @@ CssGridHighlighter.prototype = extend(Au
let path = "M" + x1 + "," + y1 + " " +
"L" + x2 + "," + y1 + " " +
"L" + x2 + "," + y2 + " " +
"L" + x1 + "," + y2;
paths.push(path);
// Update and show the info bar when only displaying a single grid area.
if (areaName) {
- this._updateInfobar(area, x1, x2, y1, y2);
- this._showInfoBar();
+ this._updateGridAreaInfobar(area, x1, x2, y1, y2);
+ this._showGridAreaInfoBar();
}
}
}
- let box = this.getElement("areas");
- box.setAttribute("d", paths.join(" "));
+ let areas = this.getElement("areas");
+ areas.setAttribute("d", paths.join(" "));
},
/**
- * Hide the highlighter, the canvas and the infobar.
+ * Render the grid cell highlight for the given grid fragment index, row and column
+ * number.
+ *
+ * @param {Number} gridFragmentIndex
+ * Index of the grid fragment to render the grid cell highlight.
+ * @param {Number} rowNumber
+ * Row number of the grid cell to highlight.
+ * @param {Number} columnNumber
+ * Column number of the grid cell to highlight.
+ */
+ renderGridCell(gridFragmentIndex, rowNumber, columnNumber) {
+ let fragment = this.gridData[gridFragmentIndex];
+ if (!fragment) {
+ return;
+ }
+
+ let row = fragment.rows.tracks[rowNumber - 1];
+ let column = fragment.cols.tracks[columnNumber - 1];
+
+ if (!row || !column) {
+ return;
+ }
+
+ let currentZoom = getCurrentZoom(this.win);
+ let {bounds} = this.currentQuads.content[gridFragmentIndex];
+
+ let x1 = column.start + (bounds.left / currentZoom);
+ let x2 = column.start + column.breadth + (bounds.left / currentZoom);
+ let y1 = row.start + (bounds.top / currentZoom);
+ let y2 = row.start + row.breadth + (bounds.top / currentZoom);
+
+ let path = "M" + x1 + "," + y1 + " " +
+ "L" + x2 + "," + y1 + " " +
+ "L" + x2 + "," + y2 + " " +
+ "L" + x1 + "," + y2;
+ let cells = this.getElement("cells");
+ cells.setAttribute("d", path);
+
+ this._updateGridCellInfobar(rowNumber, columnNumber, x1, x2, y1, y2);
+ this._showGridCellInfoBar();
+ },
+
+ /**
+ * Hide the highlighter, the canvas and the infobars.
*/
_hide() {
setIgnoreLayoutChanges(true);
this._hideGrid();
- this._hideGridArea();
- this._hideInfoBar();
+ this._hideGridElements();
+ this._hideGridAreaInfoBar();
+ this._hideGridCellInfoBar();
setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
},
_hideGrid() {
this.getElement("canvas").setAttribute("hidden", "true");
},
_showGrid() {
this.getElement("canvas").removeAttribute("hidden");
},
- _hideGridArea() {
+ _hideGridElements() {
this.getElement("elements").setAttribute("hidden", "true");
},
- _showGridArea() {
+ _showGridElements() {
this.getElement("elements").removeAttribute("hidden");
},
- _hideInfoBar() {
- this.getElement("infobar-container").setAttribute("hidden", "true");
+ _hideGridAreaInfoBar() {
+ this.getElement("area-infobar-container").setAttribute("hidden", "true");
},
- _showInfoBar() {
- this.getElement("infobar-container").removeAttribute("hidden");
+ _showGridAreaInfoBar() {
+ this.getElement("area-infobar-container").removeAttribute("hidden");
+ },
+
+ _hideGridCellInfoBar() {
+ this.getElement("cell-infobar-container").setAttribute("hidden", "true");
+ },
+
+ _showGridCellInfoBar() {
+ this.getElement("cell-infobar-container").removeAttribute("hidden");
},
});
exports.CssGridHighlighter = CssGridHighlighter;