Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/components/marker-chart/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import memoize from 'memoize-immutable';
import {
typeof updatePreviewSelection as UpdatePreviewSelection,
typeof changeRightClickedMarker as ChangeRightClickedMarker,
typeof changeMouseTimePosition as ChangeMouseTimePosition,
} from 'firefox-profiler/actions/profile-view';
import { TIMELINE_MARGIN_LEFT } from 'firefox-profiler/app-logic/constants';
import type {
Expand Down Expand Up @@ -79,6 +80,7 @@ type OwnProps = {|
+getMarker: (MarkerIndex) => Marker,
+threadsKey: ThreadsKey,
+updatePreviewSelection: WrapFunctionInDispatch<UpdatePreviewSelection>,
+changeMouseTimePosition: ChangeMouseTimePosition,
+changeRightClickedMarker: ChangeRightClickedMarker,
+marginLeft: CssPixels,
+marginRight: CssPixels,
Expand Down Expand Up @@ -757,6 +759,37 @@ class MarkerChartCanvasImpl extends React.PureComponent<Props> {
return { markerIndex, rowIndexOfLabel };
};

onMouseMove = (event: { nativeEvent: MouseEvent }) => {
const {
changeMouseTimePosition,
rangeStart,
rangeEnd,
marginLeft,
marginRight,
viewport: { viewportLeft, viewportRight, containerWidth },
} = this.props;
const viewportLength: UnitIntervalOfProfileRange =
viewportRight - viewportLeft;
const markerContainerWidth = containerWidth - marginLeft - marginRight;
// This is the x position in terms of unit interval (so, between 0 and 1).
const xInUnitInterval: UnitIntervalOfProfileRange =
viewportLeft +
viewportLength *
((event.nativeEvent.offsetX - marginLeft) / markerContainerWidth);

if (xInUnitInterval < 0 || xInUnitInterval > 1) {
changeMouseTimePosition(null);
} else {
const rangeLength: Milliseconds = rangeEnd - rangeStart;
const xInTime: Milliseconds = rangeStart + xInUnitInterval * rangeLength;
changeMouseTimePosition(xInTime);
}
};

onMouseLeave = () => {
this.props.changeMouseTimePosition(null);
};

onDoubleClickMarker = (hoveredItems: HoveredMarkerChartItems | null) => {
const markerIndex = hoveredItems === null ? null : hoveredItems.markerIndex;
if (markerIndex === null) {
Expand Down Expand Up @@ -818,6 +851,8 @@ class MarkerChartCanvasImpl extends React.PureComponent<Props> {
getHoveredItemInfo={this.getHoveredMarkerInfo}
drawCanvas={this.drawCanvas}
hitTest={this.hitTest}
onMouseMove={this.onMouseMove}
onMouseLeave={this.onMouseLeave}
/>
);
}
Expand Down
10 changes: 9 additions & 1 deletion src/components/marker-chart/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getTimelineMarginLeft } from 'firefox-profiler/selectors/app';
import {
updatePreviewSelection,
changeRightClickedMarker,
changeMouseTimePosition,
} from 'firefox-profiler/actions/profile-view';
import { ContextMenuTrigger } from 'firefox-profiler/components/shared/ContextMenuTrigger';

Expand All @@ -47,6 +48,7 @@ const ROW_HEIGHT = 16;
type DispatchProps = {|
+updatePreviewSelection: typeof updatePreviewSelection,
+changeRightClickedMarker: typeof changeRightClickedMarker,
+changeMouseTimePosition: typeof changeMouseTimePosition,
|};

type StateProps = {|
Expand Down Expand Up @@ -106,6 +108,7 @@ class MarkerChartImpl extends React.PureComponent<Props> {
getMarker,
previewSelection,
updatePreviewSelection,
changeMouseTimePosition,
changeRightClickedMarker,
rightClickedMarkerIndex,
timelineMarginLeft,
Expand Down Expand Up @@ -150,6 +153,7 @@ class MarkerChartImpl extends React.PureComponent<Props> {
getMarker,
// $FlowFixMe Error introduced by upgrading to v0.96.0. See issue #1936.
updatePreviewSelection,
changeMouseTimePosition,
changeRightClickedMarker,
rangeStart: timeRange.start,
rangeEnd: timeRange.end,
Expand Down Expand Up @@ -194,6 +198,10 @@ export const MarkerChart = explicitConnect<{||}, StateProps, DispatchProps>({
timelineTrackOrganization: getTimelineTrackOrganization(state),
};
},
mapDispatchToProps: { updatePreviewSelection, changeRightClickedMarker },
mapDispatchToProps: {
updatePreviewSelection,
changeMouseTimePosition,
changeRightClickedMarker,
},
component: MarkerChartImpl,
});
16 changes: 16 additions & 0 deletions src/components/shared/chart/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ type Props<HoveredItem> = {|
// Default to true. Set to false if the chart should be redrawn right away after
// rerender.
+drawCanvasAfterRaf?: boolean,

+onMouseMove?: (e: { nativeEvent: MouseEvent }) => mixed,
+onMouseLeave?: (e: { nativeEvent: MouseEvent }) => mixed,
|};

// The naming of the X and Y coordinates here correspond to the ones
Expand Down Expand Up @@ -223,13 +226,25 @@ export class ChartCanvas<HoveredItem> extends React.Component<
}
};

_onMouseLeave = (
event: { nativeEvent: MouseEvent } & SyntheticMouseEvent<>
) => {
if (this.props.onMouseLeave) {
this.props.onMouseLeave(event);
}
};

_onMouseMove = (
event: { nativeEvent: MouseEvent } & SyntheticMouseEvent<>
) => {
if (!this._canvas) {
return;
}

if (this.props.onMouseMove) {
this.props.onMouseMove(event);
}

this._offsetX = event.nativeEvent.offsetX;
this._offsetY = event.nativeEvent.offsetY;
// event.buttons is a bitfield representing which buttons are pressed at the
Expand Down Expand Up @@ -347,6 +362,7 @@ export class ChartCanvas<HoveredItem> extends React.Component<
ref={this._takeCanvasRef}
onMouseDown={this._onMouseDown}
onClick={this._onClick}
onMouseLeave={this._onMouseLeave}
onMouseMove={this._onMouseMove}
onMouseOut={this._onMouseOut}
onDoubleClick={this._onDoubleClick}
Expand Down
38 changes: 37 additions & 1 deletion src/components/stack-chart/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import { ChartCanvas } from '../shared/chart/Canvas';
import { FastFillStyle } from '../../utils';
import TextMeasurement from '../../utils/text-measurement';
import { formatMilliseconds } from '../../utils/format-numbers';
import { updatePreviewSelection } from '../../actions/profile-view';
import {
updatePreviewSelection,
typeof changeMouseTimePosition as ChangeMouseTimePosition,
} from '../../actions/profile-view';
import { mapCategoryColorNameToStackChartStyles } from '../../utils/colors';
import { TooltipCallNode } from '../tooltip/CallNode';
import { TooltipMarker } from '../tooltip/Marker';
Expand Down Expand Up @@ -63,6 +66,7 @@ type OwnProps = {|
+updatePreviewSelection: WrapFunctionInDispatch<
typeof updatePreviewSelection,
>,
+changeMouseTimePosition: ChangeMouseTimePosition,
+getMarker: (MarkerIndex) => Marker,
+categories: CategoryList,
+callNodeInfo: CallNodeInfo,
Expand Down Expand Up @@ -579,6 +583,36 @@ class StackChartCanvasImpl extends React.PureComponent<Props> {
return null;
};

onMouseMove = (event: { nativeEvent: MouseEvent }) => {
const {
changeMouseTimePosition,
rangeStart,
rangeEnd,
marginLeft,
viewport: { viewportLeft, viewportRight, containerWidth },
} = this.props;

const innerDevicePixelsWidth =
containerWidth - marginLeft - TIMELINE_MARGIN_RIGHT;
const rangeLength: Milliseconds = rangeEnd - rangeStart;
const viewportLength: UnitIntervalOfProfileRange =
viewportRight - viewportLeft;
const unitIntervalTime: UnitIntervalOfProfileRange =
viewportLeft +
viewportLength *
((event.nativeEvent.offsetX - marginLeft) / innerDevicePixelsWidth);
if (unitIntervalTime < 0 || unitIntervalTime > 1) {
changeMouseTimePosition(null);
} else {
const time: Milliseconds = rangeStart + unitIntervalTime * rangeLength;
changeMouseTimePosition(time);
}
};

onMouseLeave = () => {
this.props.changeMouseTimePosition(null);
};

render() {
const { containerWidth, containerHeight, isDragging } = this.props.viewport;

Expand All @@ -593,6 +627,8 @@ class StackChartCanvasImpl extends React.PureComponent<Props> {
getHoveredItemInfo={this._getHoveredStackInfo}
drawCanvas={this._drawCanvas}
hitTest={this._hitTest}
onMouseMove={this.onMouseMove}
onMouseLeave={this.onMouseLeave}
onSelectItem={this._onSelectItem}
onRightClick={this._onRightClick}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/components/stack-chart/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
changeRightClickedCallNode,
handleCallNodeTransformShortcut,
updateBottomBoxContentsAndMaybeOpen,
changeMouseTimePosition,
} from '../../actions/profile-view';

import { getCallNodePathFromIndex } from '../../profile-logic/profile-data';
Expand Down Expand Up @@ -94,6 +95,7 @@ type DispatchProps = {|
+updatePreviewSelection: typeof updatePreviewSelection,
+handleCallNodeTransformShortcut: typeof handleCallNodeTransformShortcut,
+updateBottomBoxContentsAndMaybeOpen: typeof updateBottomBoxContentsAndMaybeOpen,
+changeMouseTimePosition: typeof changeMouseTimePosition,
|};

type Props = ConnectedProps<{||}, StateProps, DispatchProps>;
Expand Down Expand Up @@ -209,6 +211,7 @@ class StackChartImpl extends React.PureComponent<Props> {
interval,
previewSelection,
updatePreviewSelection,
changeMouseTimePosition,
callNodeInfo,
categories,
selectedCallNodeIndex,
Expand Down Expand Up @@ -263,6 +266,7 @@ class StackChartImpl extends React.PureComponent<Props> {
getMarker,
// $FlowFixMe Error introduced by upgrading to v0.96.0. See issue #1936.
updatePreviewSelection,
changeMouseTimePosition,
rangeStart: timeRange.start,
rangeEnd: timeRange.end,
stackFrameHeight: STACK_FRAME_HEIGHT,
Expand Down Expand Up @@ -324,6 +328,7 @@ export const StackChart = explicitConnect<{||}, StateProps, DispatchProps>({
updatePreviewSelection,
handleCallNodeTransformShortcut,
updateBottomBoxContentsAndMaybeOpen,
changeMouseTimePosition,
},
component: StackChartImpl,
});
Expand Down
5 changes: 0 additions & 5 deletions src/components/timeline/Selection.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,11 @@
z-index: 1;
top: 0;
bottom: 0;
display: none;
width: 1px;
background: rgb(0 0 0 / 0.4);
pointer-events: none;
}

.timelineSelection:hover > .timelineSelectionHoverLine {
display: block;
}

.timelineSelectionOverlay {
position: absolute;
z-index: 2;
Expand Down
5 changes: 5 additions & 0 deletions src/components/timeline/Selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ class TimelineRulerAndSelection extends React.PureComponent<Props> {
}
};

_onMouseLeave = () => {
this.props.changeMouseTimePosition(null);
};

_makeOnMove =
(fun: (number) => { startDelta: number, endDelta: number }) =>
(
Expand Down Expand Up @@ -412,6 +416,7 @@ class TimelineRulerAndSelection extends React.PureComponent<Props> {
ref={this._containerCreated}
onMouseDown={this._onMouseDown}
onMouseMove={this._onMouseMove}
onMouseLeave={this._onMouseLeave}
>
{children}
{previewSelection.hasSelection
Expand Down
1 change: 0 additions & 1 deletion src/reducers/profile-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,6 @@ const hoveredMarker: Reducer<MarkerReference | null> = (
};

/**
* TODO: This is not used yet, see issue #222
* This is for tracking mouse position in timeline-axis
*/
const mouseTimePosition: Reducer<Milliseconds | null> = (
Expand Down
52 changes: 52 additions & 0 deletions src/test/components/MarkerChart.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,58 @@ describe('MarkerChart', function () {
expect(drawLogBefore.length > drawLogAfter.length * 2).toBe(true);
});

it('changes the mouse time position when the mouse moves', () => {
window.devicePixelRatio = 1;

const profile = getProfileWithMarkers(MARKERS);
const { flushRafCalls, getState, dispatch, fireMouseEvent } =
setupWithProfile(profile);

dispatch(changeSelectedTab('marker-chart'));
flushRafCalls();

const drawLogBefore = flushDrawLog();

// Expect the mouseTimePosition to not be set at the beginning of the test.
expect(getState().profileView.viewOptions.mouseTimePosition).toBeNull();

// Move the mouse on top of an item, ensure mouseTimePosition is set.
const { x, y } = findFillTextPositionFromDrawLog(drawLogBefore, 'Marker B');
fireMouseEvent('mousemove', {
offsetX: x,
offsetY: y,
pageX: x,
pageY: y,
});
const mouseTimePosition =
getState().profileView.viewOptions.mouseTimePosition;
expect(typeof mouseTimePosition).toEqual('number');

// Move the mouse on top of another item, ensure mouseTimePosition changed.
const { x: x2, y: y2 } = findFillTextPositionFromDrawLog(
drawLogBefore,
'Marker A'
);
expect(x2).not.toEqual(x);
fireMouseEvent('mousemove', {
offsetX: x2,
offsetY: y2,
pageX: x2,
pageY: y2,
});
expect(getState().profileView.viewOptions.mouseTimePosition).not.toEqual(
mouseTimePosition
);

// Move the mouse out of the marker chart, ensure mouseTimePosition is no
// longer set.
// React uses mouseover/mouseout events to implement mouseenter/mouseleave.
// See https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31
fireMouseEvent('mouseout', {});

expect(getState().profileView.viewOptions.mouseTimePosition).toBeNull();
});

describe('context menus', () => {
beforeEach(() => {
// Always use fake timers when dealing with context menus.
Expand Down