--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -608,14 +608,18 @@
:-moz-native-anonymous .shapes-rect {
fill: transparent;
stroke: var(--highlighter-guide-color);
shape-rendering: geometricPrecision;
vector-effect: non-scaling-stroke;
}
:-moz-native-anonymous .shapes-markers {
- fill: var(--highlighter-marker-color);
+ fill: #fff;
+}
+
+:-moz-native-anonymous .shapes-markers-outline {
+ fill: var(--highlighter-guide-color);
}
:-moz-native-anonymous .shapes-marker-hover {
fill: var(--highlighter-guide-color);
}
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -12,17 +12,17 @@ const {
getDistance,
clickedOnEllipseEdge,
distanceToLine,
projection,
clickedOnPoint
} = require("devtools/server/actors/utils/shapes-geometry-utils");
const EventEmitter = require("devtools/shared/old-event-emitter");
-const BASE_MARKER_SIZE = 10;
+const BASE_MARKER_SIZE = 5;
// the width of the area around highlighter lines that can be clicked, in px
const LINE_CLICK_WIDTH = 5;
const DOM_EVENTS = ["mousedown", "mousemove", "mouseup", "dblclick"];
const _dragging = Symbol("shapes/dragging");
/**
* The ShapesHighlighter draws an outline shapes in the page.
* The idea is to have something that is able to wrap complex shapes for css properties
@@ -115,16 +115,26 @@ class ShapesHighlighter extends AutoRefr
prefix: this.ID_CLASS_PREFIX
});
// Append a path to display the markers for the shape.
createSVGNode(this.win, {
nodeType: "path",
parent: mainSvg,
attributes: {
+ "id": "markers-outline",
+ "class": "markers-outline",
+ },
+ prefix: this.ID_CLASS_PREFIX
+ });
+
+ createSVGNode(this.win, {
+ nodeType: "path",
+ parent: mainSvg,
+ attributes: {
"id": "markers",
"class": "markers",
},
prefix: this.ID_CLASS_PREFIX
});
createSVGNode(this.win, {
nodeType: "path",
@@ -188,34 +198,21 @@ class ShapesHighlighter extends AutoRefr
this._handlePolygonClick(pageX, pageY);
} else if (this.shapeType === "circle") {
this._handleCircleClick(pageX, pageY);
} else if (this.shapeType === "ellipse") {
this._handleEllipseClick(pageX, pageY);
} else if (this.shapeType === "inset") {
this._handleInsetClick(pageX, pageY);
}
- // Currently, changes to shape-outside do not become visible unless a reflow
- // is forced (bug 1359834). This is a hack to force a reflow so changes made
- // using the highlighter can be seen: we change the width of the element
- // slightly on mousedown on a point, and restore the original width on mouseup.
- if (this.property === "shape-outside" && this[_dragging]) {
- let { width } = this.zoomAdjustedDimensions;
- let origWidth = getDefinedShapeProperties(this.currentNode, "width");
- this.currentNode.style.setProperty("width", `${width + 1}px`);
- this[_dragging].origWidth = origWidth;
- }
event.stopPropagation();
event.preventDefault();
break;
case "mouseup":
if (this[_dragging]) {
- if (this.property === "shape-outside") {
- this.currentNode.style.setProperty("width", this[_dragging].origWidth);
- }
this[_dragging] = null;
}
break;
case "mousemove":
if (!this[_dragging]) {
this._handleMouseMoveNotDragging(pageX, pageY);
return;
}
@@ -670,17 +667,17 @@ class ShapesHighlighter extends AutoRefr
this._drawHoverMarker(coords);
}
}
_drawHoverMarker(points) {
let { width, height } = this.zoomAdjustedDimensions;
let zoom = getCurrentZoom(this.win);
let path = points.map(([x, y]) => {
- return getCirclePath(x, y, width, height, zoom);
+ return getCirclePath(BASE_MARKER_SIZE, x, y, width, height, zoom);
}).join(" ");
let markerHover = this.getElement("marker-hover");
markerHover.setAttribute("d", path);
markerHover.removeAttribute("hidden");
}
_emitHoverEvent(point) {
@@ -1269,16 +1266,17 @@ class ShapesHighlighter extends AutoRefr
/**
* Hide all elements used to highlight CSS different shapes.
*/
_hideShapes() {
this.getElement("ellipse").setAttribute("hidden", true);
this.getElement("polygon").setAttribute("hidden", true);
this.getElement("rect").setAttribute("hidden", true);
this.getElement("markers").setAttribute("d", "");
+ this.getElement("markers-outline").setAttribute("d", "");
}
/**
* Update the highlighter for the current node. Called whenever the element's quads
* or CSS shape has changed.
* @returns {Boolean} whether the highlighter was successfully updated
*/
_update() {
@@ -1397,20 +1395,24 @@ class ShapesHighlighter extends AutoRefr
* Draw markers for the given coordinates.
* @param {Array} coords an array of coordinate arrays, of form [[x, y] ...]
* @param {Number} width the width of the element markers are being drawn for
* @param {Number} height the height of the element markers are being drawn for
* @param {Number} zoom the zoom level of the window
*/
_drawMarkers(coords, width, height, zoom) {
let markers = coords.map(([x, y]) => {
- return getCirclePath(x, y, width, height, zoom);
+ return getCirclePath(BASE_MARKER_SIZE, x, y, width, height, zoom);
+ }).join(" ");
+ let outline = coords.map(([x, y]) => {
+ return getCirclePath(BASE_MARKER_SIZE + 2, x, y, width, height, zoom);
}).join(" ");
this.getElement("markers").setAttribute("d", markers);
+ this.getElement("markers-outline").setAttribute("d", outline);
}
/**
* Hide the highlighter, the outline and the infobar.
*/
_hide() {
setIgnoreLayoutChanges(true);
@@ -1526,31 +1528,32 @@ exports.evalCalcExpression = evalCalcExp
const shapeModeToCssPropertyName = mode => {
let property = mode.substring(3);
return property.substring(0, 1).toLowerCase() + property.substring(1);
};
exports.shapeModeToCssPropertyName = shapeModeToCssPropertyName;
/**
* Get the SVG path definition for a circle with given attributes.
+ * @param {Number} size the radius of the circle in pixels
* @param {Number} cx the x coordinate of the centre of the circle
* @param {Number} cy the y coordinate of the centre of the circle
* @param {Number} width the width of the element the circle is being drawn for
* @param {Number} height the height of the element the circle is being drawn for
* @param {Number} zoom the zoom level of the window the circle is drawn in
* @returns {String} the definition of the circle in SVG path description format.
*/
-const getCirclePath = (cx, cy, width, height, zoom) => {
+const getCirclePath = (size, cx, cy, width, height, zoom) => {
// We use a viewBox of 100x100 for shape-container so it's easy to position things
// based on their percentage, but this makes it more difficult to create circles.
// Therefor, 100px is the base size of shape-container. In order to make the markers'
// size scale properly, we must adjust the radius based on zoom and the width/height of
// the element being highlighted, then calculate a radius for both x/y axes based
// on the aspect ratio of the element.
- let radius = BASE_MARKER_SIZE * (100 / Math.max(width, height)) / zoom;
+ let radius = size * (100 / Math.max(width, height)) / zoom;
let ratio = width / height;
let rx = (ratio > 1) ? radius : radius / ratio;
let ry = (ratio > 1) ? radius * ratio : radius;
// a circle is drawn as two arc lines, starting at the leftmost point of the circle.
return `M${cx - rx},${cy}a${rx},${ry} 0 1,0 ${rx * 2},0` +
`a${rx},${ry} 0 1,0 ${rx * -2},0`;
};
exports.getCirclePath = getCirclePath;
--- a/devtools/server/tests/unit/test_shapes_highlighter_helpers.js
+++ b/devtools/server/tests/unit/test_shapes_highlighter_helpers.js
@@ -101,35 +101,35 @@ function test_shape_mode_to_css_property
for (let { desc, expr, expected } of tests) {
equal(shapeModeToCssPropertyName(expr), expected, desc);
}
}
function test_get_circle_path() {
const tests = [{
- desc: "getCirclePath with no resizing, no zoom, 1:1 ratio",
- cx: 0, cy: 0, width: 100, height: 100, zoom: 1,
- expected: "M-10,0a10,10 0 1,0 20,0a10,10 0 1,0 -20,0"
- }, {
- desc: "getCirclePath with resizing, no zoom, 1:1 ratio",
- cx: 0, cy: 0, width: 200, height: 200, zoom: 1,
+ desc: "getCirclePath with size 5, no resizing, no zoom, 1:1 ratio",
+ size: 5, cx: 0, cy: 0, width: 100, height: 100, zoom: 1,
expected: "M-5,0a5,5 0 1,0 10,0a5,5 0 1,0 -10,0"
}, {
- desc: "getCirclePath with resizing, zoom, 1:1 ratio",
- cx: 0, cy: 0, width: 200, height: 200, zoom: 2,
- expected: "M-2.5,0a2.5,2.5 0 1,0 5,0a2.5,2.5 0 1,0 -5,0"
+ desc: "getCirclePath with size 7, resizing, no zoom, 1:1 ratio",
+ size: 7, cx: 0, cy: 0, width: 200, height: 200, zoom: 1,
+ expected: "M-3.5,0a3.5,3.5 0 1,0 7,0a3.5,3.5 0 1,0 -7,0"
}, {
- desc: "getCirclePath with resizing, zoom, non-square ratio",
- cx: 0, cy: 0, width: 100, height: 200, zoom: 2,
- expected: "M-5,0a5,2.5 0 1,0 10,0a5,2.5 0 1,0 -10,0"
+ desc: "getCirclePath with size 5, resizing, zoom, 1:1 ratio",
+ size: 5, cx: 0, cy: 0, width: 200, height: 200, zoom: 2,
+ expected: "M-1.25,0a1.25,1.25 0 1,0 2.5,0a1.25,1.25 0 1,0 -2.5,0"
+ }, {
+ desc: "getCirclePath with size 5, resizing, zoom, non-square ratio",
+ size: 5, cx: 0, cy: 0, width: 100, height: 200, zoom: 2,
+ expected: "M-2.5,0a2.5,1.25 0 1,0 5,0a2.5,1.25 0 1,0 -5,0"
}];
- for (let { desc, cx, cy, width, height, zoom, expected } of tests) {
- equal(getCirclePath(cx, cy, width, height, zoom), expected, desc);
+ for (let { desc, size, cx, cy, width, height, zoom, expected } of tests) {
+ equal(getCirclePath(size, cx, cy, width, height, zoom), expected, desc);
}
}
function test_get_unit() {
const tests = [{
desc: "getUnit with %",
expr: "30%", expected: "%"
}, {