mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Bug 1121194 - Support vertical panning for the flamegraph in the new performance tool, r=jsantell
This commit is contained in:
parent
036f96099f
commit
b83c9518ec
@ -19,6 +19,7 @@ support-files =
|
||||
[browser_flame-graph-02.js]
|
||||
[browser_flame-graph-03a.js]
|
||||
[browser_flame-graph-03b.js]
|
||||
[browser_flame-graph-03c.js]
|
||||
[browser_flame-graph-04.js]
|
||||
[browser_flame-graph-utils-01.js]
|
||||
[browser_flame-graph-utils-02.js]
|
||||
|
@ -24,6 +24,8 @@ function* performTest() {
|
||||
let graph = new FlameGraph(doc.body, 1);
|
||||
graph.fixedWidth = TEST_WIDTH;
|
||||
graph.fixedHeight = TEST_HEIGHT;
|
||||
graph.horizontalPanThreshold = 0;
|
||||
graph.verticalPanThreshold = 0;
|
||||
|
||||
yield graph.ready();
|
||||
|
||||
|
139
browser/devtools/shared/test/browser_flame-graph-03c.js
Normal file
139
browser/devtools/shared/test/browser_flame-graph-03c.js
Normal file
@ -0,0 +1,139 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that vertical panning in the flame graph widget works properly.
|
||||
|
||||
let TEST_DATA = [{ color: "#f00", blocks: [{ x: 0, y: 0, width: 50, height: 20, text: "FOO" }, { x: 50, y: 0, width: 100, height: 20, text: "BAR" }] }, { color: "#00f", blocks: [{ x: 0, y: 30, width: 30, height: 20, text: "BAZ" }] }];
|
||||
let TEST_BOUNDS = { startTime: 0, endTime: 150 };
|
||||
let TEST_WIDTH = 200;
|
||||
let TEST_HEIGHT = 100;
|
||||
let TEST_DPI_DENSITIY = 2;
|
||||
|
||||
let {FlameGraph} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
|
||||
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
|
||||
|
||||
add_task(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
yield performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let [host, win, doc] = yield createHost();
|
||||
doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
|
||||
|
||||
let graph = new FlameGraph(doc.body, TEST_DPI_DENSITIY);
|
||||
graph.fixedWidth = TEST_WIDTH;
|
||||
graph.fixedHeight = TEST_HEIGHT;
|
||||
|
||||
yield graph.ready();
|
||||
|
||||
testGraph(graph);
|
||||
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraph(graph) {
|
||||
graph.setData({ data: TEST_DATA, bounds: TEST_BOUNDS });
|
||||
|
||||
// Drag up vertically only.
|
||||
|
||||
dragStart(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (1).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (1).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||
"The vertical offset is correct (1).");
|
||||
|
||||
hover(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2 - 50);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (2).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (2).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 17,
|
||||
"The vertical offset is correct (2).");
|
||||
|
||||
dragStop(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2 - 100);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (3).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (3).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 42,
|
||||
"The vertical offset is correct (3).");
|
||||
|
||||
// Drag down strongly vertically and slightly horizontally.
|
||||
|
||||
dragStart(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (4).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (4).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 42,
|
||||
"The vertical offset is correct (4).");
|
||||
|
||||
hover(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2 + 50);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (5).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (5).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 25,
|
||||
"The vertical offset is correct (5).");
|
||||
|
||||
dragStop(graph, TEST_WIDTH / 2 + 100, TEST_HEIGHT / 2 + 500);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (6).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (6).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||
"The vertical offset is correct (6).");
|
||||
|
||||
// Drag up slightly vertically and strongly horizontally.
|
||||
|
||||
dragStart(graph, TEST_WIDTH / 2, TEST_HEIGHT / 2);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (7).");
|
||||
is(graph.getViewRange().endTime | 0, 150,
|
||||
"The selection end boundary is correct (7).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||
"The vertical offset is correct (7).");
|
||||
|
||||
hover(graph, TEST_WIDTH / 2 + 50, TEST_HEIGHT / 2);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (8).");
|
||||
is(graph.getViewRange().endTime | 0, 116,
|
||||
"The selection end boundary is correct (8).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||
"The vertical offset is correct (8).");
|
||||
|
||||
dragStop(graph, TEST_WIDTH / 2 + 500, TEST_HEIGHT / 2 + 100);
|
||||
is(graph.getViewRange().startTime | 0, 0,
|
||||
"The selection start boundary is correct (9).");
|
||||
is(graph.getViewRange().endTime | 0, 0,
|
||||
"The selection end boundary is correct (9).");
|
||||
is(graph.getViewRange().verticalOffset | 0, 0,
|
||||
"The vertical offset is correct (9).");
|
||||
}
|
||||
|
||||
// EventUtils just doesn't work!
|
||||
|
||||
function hover(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseDown({ clientX: x, clientY: y });
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ clientX: x, clientY: y });
|
||||
graph._onMouseUp({ clientX: x, clientY: y });
|
||||
}
|
@ -26,11 +26,15 @@ const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00035;
|
||||
const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.5;
|
||||
const GRAPH_MIN_SELECTION_WIDTH = 0.001; // ms
|
||||
|
||||
const GRAPH_HORIZONTAL_PAN_THRESHOLD = 10; // px
|
||||
const GRAPH_VERTICAL_PAN_THRESHOLD = 30; // px
|
||||
|
||||
const FIND_OPTIMAL_TICK_INTERVAL_MAX_ITERS = 100;
|
||||
const TIMELINE_TICKS_MULTIPLE = 5; // ms
|
||||
const TIMELINE_TICKS_SPACING_MIN = 75; // px
|
||||
|
||||
const OVERVIEW_HEADER_HEIGHT = 16; // px
|
||||
const OVERVIEW_HEADER_BACKGROUND = "rgba(255,255,255,0.7)";
|
||||
const OVERVIEW_HEADER_TEXT_COLOR = "#18191a";
|
||||
const OVERVIEW_HEADER_TEXT_FONT_SIZE = 9; // px
|
||||
const OVERVIEW_HEADER_TEXT_FONT_FAMILY = "sans-serif";
|
||||
@ -121,9 +125,11 @@ function FlameGraph(parent, sharpness) {
|
||||
this._height = canvas.height = bounds.height * this._pixelRatio;
|
||||
this._ctx = canvas.getContext("2d");
|
||||
|
||||
this._bounds = new GraphSelection();
|
||||
this._selection = new GraphSelection();
|
||||
this._selectionDragger = new GraphSelectionDragger();
|
||||
this._bounds = new GraphArea();
|
||||
this._selection = new GraphArea();
|
||||
this._selectionDragger = new GraphAreaDragger();
|
||||
this._verticalOffset = 0;
|
||||
this._verticalOffsetDragger = new GraphAreaDragger(0);
|
||||
|
||||
// Calculating text widths is necessary to trim the text inside the blocks
|
||||
// while the scaling changes (e.g. via scrolling). This is very expensive,
|
||||
@ -198,6 +204,8 @@ FlameGraph.prototype = {
|
||||
this._bounds = null;
|
||||
this._selection = null;
|
||||
this._selectionDragger = null;
|
||||
this._verticalOffset = null;
|
||||
this._verticalOffsetDragger = null;
|
||||
this._textWidthsCache = null;
|
||||
|
||||
this._data = null;
|
||||
@ -208,6 +216,7 @@ FlameGraph.prototype = {
|
||||
/**
|
||||
* Rendering options. Subclasses should override these.
|
||||
*/
|
||||
overviewHeaderBackgroundColor: OVERVIEW_HEADER_BACKGROUND,
|
||||
overviewHeaderTextColor: OVERVIEW_HEADER_TEXT_COLOR,
|
||||
overviewTimelineStrokes: OVERVIEW_TIMELINE_STROKES,
|
||||
blockTextColor: FLAME_GRAPH_BLOCK_TEXT_COLOR,
|
||||
@ -219,6 +228,12 @@ FlameGraph.prototype = {
|
||||
fixedWidth: null,
|
||||
fixedHeight: null,
|
||||
|
||||
/**
|
||||
* How much preliminar drag is necessary to determine the panning direction.
|
||||
*/
|
||||
horizontalPanThreshold: GRAPH_HORIZONTAL_PAN_THRESHOLD,
|
||||
verticalPanThreshold: GRAPH_VERTICAL_PAN_THRESHOLD,
|
||||
|
||||
/**
|
||||
* The units used in the overhead ticks. Could be "ms", for example.
|
||||
* Overwrite this with your own localized format.
|
||||
@ -278,12 +293,13 @@ FlameGraph.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the selection (i.e. the 'view range') bounds.
|
||||
* Sets the selection and vertical offset (i.e. the 'view range').
|
||||
* @return number
|
||||
*/
|
||||
setViewRange: function({ startTime, endTime }) {
|
||||
setViewRange: function({ startTime, endTime }, verticalOffset = 0) {
|
||||
this._selection.start = startTime * this._pixelRatio;
|
||||
this._selection.end = endTime * this._pixelRatio;
|
||||
this._verticalOffset = verticalOffset * this._pixelRatio;
|
||||
this._shouldRedraw = true;
|
||||
},
|
||||
|
||||
@ -299,13 +315,14 @@ FlameGraph.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current selection (i.e. the 'view range').
|
||||
* Gets the current selection and vertical offset (i.e. the 'view range').
|
||||
* @return number
|
||||
*/
|
||||
getViewRange: function() {
|
||||
return {
|
||||
startTime: this._selection.start / this._pixelRatio,
|
||||
endTime: this._selection.end / this._pixelRatio
|
||||
endTime: this._selection.end / this._pixelRatio,
|
||||
verticalOffset: this._verticalOffset / this._pixelRatio
|
||||
};
|
||||
},
|
||||
|
||||
@ -366,8 +383,8 @@ FlameGraph.prototype = {
|
||||
let selection = this._selection;
|
||||
let selectionWidth = selection.end - selection.start;
|
||||
let selectionScale = canvasWidth / selectionWidth;
|
||||
this._drawPyramid(this._data, this._verticalOffset, selection.start, selectionScale);
|
||||
this._drawTicks(selection.start, selectionScale);
|
||||
this._drawPyramid(this._data, selection.start, selectionScale);
|
||||
|
||||
this._shouldRedraw = false;
|
||||
},
|
||||
@ -385,6 +402,9 @@ FlameGraph.prototype = {
|
||||
let canvasHeight = this._height;
|
||||
let scaledOffset = dataOffset * dataScale;
|
||||
|
||||
ctx.fillStyle = this.overviewHeaderBackgroundColor;
|
||||
ctx.fillRect(0, 0, canvasWidth, OVERVIEW_HEADER_HEIGHT * this._pixelRatio);
|
||||
|
||||
let fontSize = OVERVIEW_HEADER_TEXT_FONT_SIZE * this._pixelRatio;
|
||||
let fontFamily = OVERVIEW_HEADER_TEXT_FONT_FAMILY;
|
||||
let textPaddingLeft = OVERVIEW_HEADER_TEXT_PADDING_LEFT * this._pixelRatio;
|
||||
@ -415,48 +435,50 @@ FlameGraph.prototype = {
|
||||
*
|
||||
* @param object dataSource
|
||||
* The data source. See the constructor for more information.
|
||||
* @param number verticalOffset
|
||||
* Offsets the drawing vertically by the specified amount.
|
||||
* @param number dataOffset, dataScale
|
||||
* Offsets and scales the data source by the specified amount.
|
||||
* This is used for scrolling the visualization.
|
||||
*/
|
||||
_drawPyramid: function(dataSource, dataOffset, dataScale) {
|
||||
_drawPyramid: function(dataSource, verticalOffset, dataOffset, dataScale) {
|
||||
let ctx = this._ctx;
|
||||
|
||||
let fontSize = FLAME_GRAPH_BLOCK_TEXT_FONT_SIZE * this._pixelRatio;
|
||||
let fontFamily = FLAME_GRAPH_BLOCK_TEXT_FONT_FAMILY;
|
||||
let visibleBlocks = this._drawPyramidFill(dataSource, dataOffset, dataScale);
|
||||
let visibleBlocksInfo = this._drawPyramidFill(dataSource, verticalOffset, dataOffset, dataScale);
|
||||
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.font = fontSize + "px " + fontFamily;
|
||||
ctx.fillStyle = this.blockTextColor;
|
||||
|
||||
this._drawPyramidText(visibleBlocks, dataOffset, dataScale);
|
||||
this._drawPyramidText(visibleBlocksInfo, verticalOffset, dataOffset, dataScale);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills all block inside this graph's pyramid.
|
||||
* @see FlameGraph.prototype._drawPyramid
|
||||
*/
|
||||
_drawPyramidFill: function(dataSource, dataOffset, dataScale) {
|
||||
let visibleBlocksStore = [];
|
||||
_drawPyramidFill: function(dataSource, verticalOffset, dataOffset, dataScale) {
|
||||
let visibleBlocksInfoStore = [];
|
||||
let minVisibleBlockWidth = this._overflowCharWidth;
|
||||
|
||||
for (let { color, blocks } of dataSource) {
|
||||
this._drawBlocksFill(
|
||||
color, blocks, dataOffset, dataScale,
|
||||
visibleBlocksStore, minVisibleBlockWidth);
|
||||
color, blocks, verticalOffset, dataOffset, dataScale,
|
||||
visibleBlocksInfoStore, minVisibleBlockWidth);
|
||||
}
|
||||
|
||||
return visibleBlocksStore;
|
||||
return visibleBlocksInfoStore;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the text for all block inside this graph's pyramid.
|
||||
* @see FlameGraph.prototype._drawPyramid
|
||||
*/
|
||||
_drawPyramidText: function(blocks, dataOffset, dataScale) {
|
||||
for (let block of blocks) {
|
||||
this._drawBlockText(block, dataOffset, dataScale);
|
||||
_drawPyramidText: function(blocksInfo, verticalOffset, dataOffset, dataScale) {
|
||||
for (let { block, rect } of blocksInfo) {
|
||||
this._drawBlockText(block, rect, verticalOffset, dataOffset, dataScale);
|
||||
}
|
||||
},
|
||||
|
||||
@ -468,19 +490,22 @@ FlameGraph.prototype = {
|
||||
* @param array blocks
|
||||
* A list of { x, y, width, height } objects visually representing
|
||||
* all the blocks sharing this particular style.
|
||||
* @param number verticalOffset
|
||||
* Offsets the drawing vertically by the specified amount.
|
||||
* @param number dataOffset, dataScale
|
||||
* Offsets and scales the data source by the specified amount.
|
||||
* This is used for scrolling the visualization.
|
||||
* @param array visibleBlocksStore
|
||||
* An array to store all the visible blocks into, after drawing them.
|
||||
* @param array visibleBlocksInfoStore
|
||||
* An array to store all the visible blocks into, along with the
|
||||
* final baked coordinates and dimensions, after drawing them.
|
||||
* The provided array will be populated.
|
||||
* @param number minVisibleBlockWidth
|
||||
* The minimum width of the blocks that will be added into
|
||||
* the `visibleBlocksStore`.
|
||||
* the `visibleBlocksInfoStore`.
|
||||
*/
|
||||
_drawBlocksFill: function(
|
||||
color, blocks, dataOffset, dataScale,
|
||||
visibleBlocksStore, minVisibleBlockWidth)
|
||||
color, blocks, verticalOffset, dataOffset, dataScale,
|
||||
visibleBlocksInfoStore, minVisibleBlockWidth)
|
||||
{
|
||||
let ctx = this._ctx;
|
||||
let canvasWidth = this._width;
|
||||
@ -493,13 +518,14 @@ FlameGraph.prototype = {
|
||||
for (let block of blocks) {
|
||||
let { x, y, width, height } = block;
|
||||
let rectLeft = x * this._pixelRatio * dataScale - scaledOffset;
|
||||
let rectTop = (y + OVERVIEW_HEADER_HEIGHT) * this._pixelRatio;
|
||||
let rectTop = (y - verticalOffset + OVERVIEW_HEADER_HEIGHT) * this._pixelRatio;
|
||||
let rectWidth = width * this._pixelRatio * dataScale;
|
||||
let rectHeight = height * this._pixelRatio;
|
||||
|
||||
if (rectLeft > canvasWidth || // Too far right.
|
||||
rectLeft < -rectWidth || // Too far left.
|
||||
rectTop > canvasHeight) { // Too far bottom.
|
||||
rectTop > canvasHeight || // Too far bottom.
|
||||
rectTop < -rectHeight) { // Too far top.
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -524,7 +550,10 @@ FlameGraph.prototype = {
|
||||
// Populate the visible blocks store with this block if the width
|
||||
// is longer than a given threshold.
|
||||
if (rectWidth > minVisibleBlockWidth) {
|
||||
visibleBlocksStore.push(block);
|
||||
visibleBlocksInfoStore.push({
|
||||
block: block,
|
||||
rect: { rectLeft, rectTop, rectWidth, rectHeight }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,24 +566,29 @@ FlameGraph.prototype = {
|
||||
* @param object block
|
||||
* A single { x, y, width, height, text } object visually representing
|
||||
* the block containing the text.
|
||||
* @param object rect
|
||||
* A single { rectLeft, rectTop, rectWidth, rectHeight } object
|
||||
* representing the final baked coordinates of the drawn rectangle.
|
||||
* Think of them as screen-space values, vs. object-space values. These
|
||||
* differ from the scalars in `block` when the graph is scaled/panned.
|
||||
* @param number verticalOffset
|
||||
* Offsets the drawing vertically by the specified amount.
|
||||
* @param number dataOffset, dataScale
|
||||
* Offsets and scales the data source by the specified amount.
|
||||
* This is used for scrolling the visualization.
|
||||
*/
|
||||
_drawBlockText: function(block, dataOffset, dataScale) {
|
||||
_drawBlockText: function(block, rect, verticalOffset, dataOffset, dataScale) {
|
||||
let ctx = this._ctx;
|
||||
let scaledOffset = dataOffset * dataScale;
|
||||
|
||||
let { x, y, width, height, text } = block;
|
||||
let { rectLeft, rectTop, rectWidth, rectHeight } = rect;
|
||||
|
||||
let paddingTop = FLAME_GRAPH_BLOCK_TEXT_PADDING_TOP * this._pixelRatio;
|
||||
let paddingLeft = FLAME_GRAPH_BLOCK_TEXT_PADDING_LEFT * this._pixelRatio;
|
||||
let paddingRight = FLAME_GRAPH_BLOCK_TEXT_PADDING_RIGHT * this._pixelRatio;
|
||||
let totalHorizontalPadding = paddingLeft + paddingRight;
|
||||
|
||||
let rectLeft = x * this._pixelRatio * dataScale - scaledOffset;
|
||||
let rectWidth = width * this._pixelRatio * dataScale;
|
||||
|
||||
// Clamp the blocks position to start at 0. Avoid negative X coords,
|
||||
// to properly place the text inside the blocks.
|
||||
if (rectLeft < 0) {
|
||||
@ -563,7 +597,7 @@ FlameGraph.prototype = {
|
||||
}
|
||||
|
||||
let textLeft = rectLeft + paddingLeft;
|
||||
let textTop = (y + height / 2 + OVERVIEW_HEADER_HEIGHT) * this._pixelRatio + paddingTop;
|
||||
let textTop = rectTop + rectHeight / 2 + paddingTop;
|
||||
let textAvailableWidth = rectWidth - totalHorizontalPadding;
|
||||
|
||||
// Massage the text to fit inside a given width. This clamps the string
|
||||
@ -672,6 +706,7 @@ FlameGraph.prototype = {
|
||||
_onMouseMove: function(e) {
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||
|
||||
let canvasWidth = this._width;
|
||||
let canvasHeight = this._height;
|
||||
@ -680,14 +715,41 @@ FlameGraph.prototype = {
|
||||
let selectionWidth = selection.end - selection.start;
|
||||
let selectionScale = canvasWidth / selectionWidth;
|
||||
|
||||
let dragger = this._selectionDragger;
|
||||
if (dragger.origin != null) {
|
||||
selection.start = dragger.anchor.start + (dragger.origin - mouseX) / selectionScale;
|
||||
selection.end = dragger.anchor.end + (dragger.origin - mouseX) / selectionScale;
|
||||
let horizDrag = this._selectionDragger;
|
||||
let vertDrag = this._verticalOffsetDragger;
|
||||
|
||||
// Avoid dragging both horizontally and vertically at the same time,
|
||||
// as this doesn't feel natural. Based on a minimum distance, enable either
|
||||
// one, and remember the drag direction to offset the mouse coords later.
|
||||
if (!this._horizontalDragEnabled && !this._verticalDragEnabled) {
|
||||
let horizDiff = Math.abs(horizDrag.origin - mouseX);
|
||||
if (horizDiff > this.horizontalPanThreshold) {
|
||||
this._horizontalDragDirection = Math.sign(horizDrag.origin - mouseX);
|
||||
this._horizontalDragEnabled = true;
|
||||
}
|
||||
let vertDiff = Math.abs(vertDrag.origin - mouseY);
|
||||
if (vertDiff > this.verticalPanThreshold) {
|
||||
this._verticalDragDirection = Math.sign(vertDrag.origin - mouseY);
|
||||
this._verticalDragEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (horizDrag.origin != null && this._horizontalDragEnabled) {
|
||||
let relativeX = mouseX + this._horizontalDragDirection * this.horizontalPanThreshold;
|
||||
selection.start = horizDrag.anchor.start + (horizDrag.origin - relativeX) / selectionScale;
|
||||
selection.end = horizDrag.anchor.end + (horizDrag.origin - relativeX) / selectionScale;
|
||||
this._normalizeSelectionBounds();
|
||||
this._shouldRedraw = true;
|
||||
this.emit("selecting");
|
||||
}
|
||||
|
||||
if (vertDrag.origin != null && this._verticalDragEnabled) {
|
||||
let relativeY = mouseY + this._verticalDragDirection * this.verticalPanThreshold;
|
||||
this._verticalOffset = vertDrag.anchor + (vertDrag.origin - relativeY) / this._pixelRatio;
|
||||
this._normalizeVerticalOffset();
|
||||
this._shouldRedraw = true;
|
||||
this.emit("panning-vertically");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -696,11 +758,19 @@ FlameGraph.prototype = {
|
||||
_onMouseDown: function(e) {
|
||||
let offset = this._getContainerOffset();
|
||||
let mouseX = (e.clientX - offset.left) * this._pixelRatio;
|
||||
let mouseY = (e.clientY - offset.top) * this._pixelRatio;
|
||||
|
||||
this._selectionDragger.origin = mouseX;
|
||||
this._selectionDragger.anchor.start = this._selection.start;
|
||||
this._selectionDragger.anchor.end = this._selection.end;
|
||||
this._canvas.setAttribute("input", "adjusting-selection-boundary");
|
||||
|
||||
this._verticalOffsetDragger.origin = mouseY;
|
||||
this._verticalOffsetDragger.anchor = this._verticalOffset;
|
||||
|
||||
this._horizontalDragEnabled = false;
|
||||
this._verticalDragEnabled = false;
|
||||
|
||||
this._canvas.setAttribute("input", "adjusting-view-area");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -708,6 +778,11 @@ FlameGraph.prototype = {
|
||||
*/
|
||||
_onMouseUp: function() {
|
||||
this._selectionDragger.origin = null;
|
||||
this._verticalOffsetDragger.origin = null;
|
||||
this._horizontalDragEnabled = false;
|
||||
this._horizontalDragDirection = 0;
|
||||
this._verticalDragEnabled = false;
|
||||
this._verticalDragDirection = 0;
|
||||
this._canvas.removeAttribute("input");
|
||||
},
|
||||
|
||||
@ -782,6 +857,14 @@ FlameGraph.prototype = {
|
||||
this._selection.end = selectionEnd;
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes sure that the current vertical offset is within the allowed
|
||||
* panning range.
|
||||
*/
|
||||
_normalizeVerticalOffset: function() {
|
||||
this._verticalOffset = Math.max(this._verticalOffset, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Finds the optimal tick interval between time markers in this graph.
|
||||
|
@ -12,9 +12,9 @@ const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"GraphCursor",
|
||||
"GraphSelection",
|
||||
"GraphSelectionDragger",
|
||||
"GraphSelectionResizer",
|
||||
"GraphArea",
|
||||
"GraphAreaDragger",
|
||||
"GraphAreaResizer",
|
||||
"AbstractCanvasGraph",
|
||||
"LineGraphWidget",
|
||||
"BarGraphWidget",
|
||||
@ -101,17 +101,17 @@ this.GraphCursor = function() {
|
||||
this.y = null;
|
||||
};
|
||||
|
||||
this.GraphSelection = function() {
|
||||
this.GraphArea = function() {
|
||||
this.start = null;
|
||||
this.end = null;
|
||||
};
|
||||
|
||||
this.GraphSelectionDragger = function() {
|
||||
this.GraphAreaDragger = function(anchor = new GraphArea()) {
|
||||
this.origin = null;
|
||||
this.anchor = new GraphSelection();
|
||||
this.anchor = anchor;
|
||||
};
|
||||
|
||||
this.GraphSelectionResizer = function() {
|
||||
this.GraphAreaResizer = function() {
|
||||
this.margin = null;
|
||||
};
|
||||
|
||||
@ -178,9 +178,9 @@ this.AbstractCanvasGraph = function(parent, name, sharpness) {
|
||||
this._ctx.mozImageSmoothingEnabled = false;
|
||||
|
||||
this._cursor = new GraphCursor();
|
||||
this._selection = new GraphSelection();
|
||||
this._selectionDragger = new GraphSelectionDragger();
|
||||
this._selectionResizer = new GraphSelectionResizer();
|
||||
this._selection = new GraphArea();
|
||||
this._selectionDragger = new GraphAreaDragger();
|
||||
this._selectionResizer = new GraphAreaResizer();
|
||||
|
||||
this._onAnimationFrame = this._onAnimationFrame.bind(this);
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
|
@ -864,6 +864,10 @@
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.graph-widget-canvas[input=adjusting-view-area] {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.graph-widget-canvas[input=hovering-selection-contents] {
|
||||
cursor: grab;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user