Skip to content

Commit d0bfc54

Browse files
committed
Add tooltips when hovering the gap between two markers on the same flow.
1 parent 2b52d1a commit d0bfc54

File tree

2 files changed

+264
-83
lines changed

2 files changed

+264
-83
lines changed

src/components/flow-panel/Canvas.js

Lines changed: 148 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from 'firefox-profiler/components/shared/chart/Viewport';
1414
import { ChartCanvas } from 'firefox-profiler/components/shared/chart/Canvas';
1515
import { TooltipMarker } from 'firefox-profiler/components/tooltip/Marker';
16+
import { FlowGapTooltip } from 'firefox-profiler/components/tooltip/FlowGap';
1617
import TextMeasurement from 'firefox-profiler/utils/text-measurement';
1718
import { bisectionRight } from 'firefox-profiler/utils/bisect';
1819
import {
@@ -33,8 +34,10 @@ import type {
3334
FlowTimingRow,
3435
FlowTimingArrow,
3536
} from 'firefox-profiler/types';
36-
import { getStartEndRangeForMarker } from 'firefox-profiler/utils';
37-
import { ensureExists } from 'firefox-profiler/utils/flow';
37+
import {
38+
ensureExists,
39+
assertExhaustiveCheck,
40+
} from 'firefox-profiler/utils/flow';
3841
import { computeArrowsRelatedToMarker } from 'firefox-profiler/profile-logic/marker-data';
3942

4043
import type {
@@ -44,13 +47,28 @@ import type {
4447

4548
import type { WrapFunctionInDispatch } from 'firefox-profiler/utils/connect';
4649

47-
type HoveredFlowPanelItems = {|
50+
type FlowPanelHoverInfo = {|
4851
rowIndex: number | null,
4952
flowIndex: IndexIntoFlowTable | null,
50-
indexInFlowMarkers: number | null, // index into flows[flowIndex].flowMarkers
51-
threadIndex: ThreadIndex | null,
52-
markerIndex: MarkerIndex | null,
53-
flowMarkerIndex: number | null,
53+
hoveredItem: HoveredFlowPanelItem | null,
54+
|};
55+
56+
type HoveredFlowPanelItem =
57+
| {|
58+
type: 'SINGLE_MARKER',
59+
hoveredMarker: SingleHoveredFlowPanelItem,
60+
|}
61+
| {|
62+
type: 'BETWEEN_MARKERS',
63+
markerBeforeHoveredGap: SingleHoveredFlowPanelItem,
64+
markerAfterHoveredGap: SingleHoveredFlowPanelItem,
65+
|};
66+
67+
type SingleHoveredFlowPanelItem = {|
68+
indexInFlowMarkers: number, // index into flows[flowIndex].flowMarkers
69+
threadIndex: ThreadIndex,
70+
markerIndex: MarkerIndex,
71+
flowMarkerIndex: number,
5472
|};
5573

5674
type OwnProps = {|
@@ -90,7 +108,7 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
90108
drawCanvas = (
91109
ctx: CanvasRenderingContext2D,
92110
scale: ChartCanvasScale,
93-
hoverInfo: ChartCanvasHoverInfo<HoveredFlowPanelItems>
111+
hoverInfo: ChartCanvasHoverInfo<FlowPanelHoverInfo>
94112
) => {
95113
const {
96114
rowHeight,
@@ -124,15 +142,19 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
124142
this.drawSeparatorsAndLabels(ctx, startRow, endRow);
125143

126144
const { hoveredItem } = hoverInfo;
127-
if (hoveredItem !== null) {
128-
const { threadIndex, flowMarkerIndex } = hoveredItem;
145+
if (
146+
hoveredItem !== null &&
147+
hoveredItem.hoveredItem &&
148+
hoveredItem.hoveredItem.type === 'SINGLE_MARKER'
149+
) {
150+
const { threadIndex, flowMarkerIndex } =
151+
hoveredItem.hoveredItem.hoveredMarker;
129152
if (threadIndex !== null && flowMarkerIndex !== null) {
130153
const arrows = this._memoizedGetArrows(
131154
threadIndex,
132155
flowMarkerIndex,
133156
flowTiming
134157
);
135-
console.log({ arrows });
136158
this.drawArrows(ctx, arrows, startRow, endRow);
137159
}
138160
}
@@ -678,7 +700,7 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
678700
ctx.restore();
679701
}
680702

681-
hitTest = (x: CssPixels, y: CssPixels): HoveredFlowPanelItems | null => {
703+
hitTest = (x: CssPixels, y: CssPixels): FlowPanelHoverInfo | null => {
682704
const {
683705
rangeStart,
684706
rangeEnd,
@@ -695,11 +717,6 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
695717
return null;
696718
}
697719

698-
let markerIndex = null;
699-
let flowMarkerIndex = null;
700-
let threadIndex = null;
701-
let indexInFlowMarkers = null;
702-
703720
const markerContainerWidth = containerWidth - marginLeft - marginRight;
704721

705722
const rangeLength: Milliseconds = rangeEnd - rangeStart;
@@ -766,40 +783,85 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
766783
// 3. When we found the closest, we still have to check if it's in close
767784
// enough!
768785
if (isMarkerTimingInDotRadius(closest)) {
769-
markerIndex = markerTiming.markerIndex[closest];
770-
flowMarkerIndex = markerTiming.flowMarkerIndex[closest];
771-
threadIndex = markerTiming.threadIndex[closest];
772-
indexInFlowMarkers = closest;
786+
return {
787+
rowIndex,
788+
flowIndex,
789+
hoveredItem: {
790+
type: 'SINGLE_MARKER',
791+
hoveredMarker: {
792+
markerIndex: markerTiming.markerIndex[closest],
793+
flowMarkerIndex: markerTiming.flowMarkerIndex[closest],
794+
threadIndex: markerTiming.threadIndex[closest],
795+
indexInFlowMarkers: closest,
796+
},
797+
},
798+
};
773799
}
800+
801+
// The cursor is between two markers.
802+
return {
803+
rowIndex,
804+
flowIndex,
805+
hoveredItem: {
806+
type: 'BETWEEN_MARKERS',
807+
markerBeforeHoveredGap: {
808+
markerIndex: markerTiming.markerIndex[prevStartIndex],
809+
flowMarkerIndex: markerTiming.flowMarkerIndex[prevStartIndex],
810+
threadIndex: markerTiming.threadIndex[prevStartIndex],
811+
indexInFlowMarkers: prevStartIndex,
812+
},
813+
markerAfterHoveredGap: {
814+
markerIndex: markerTiming.markerIndex[nextStartIndex],
815+
flowMarkerIndex: markerTiming.flowMarkerIndex[nextStartIndex],
816+
threadIndex: markerTiming.threadIndex[nextStartIndex],
817+
indexInFlowMarkers: nextStartIndex,
818+
},
819+
},
820+
};
774821
} else if (nextStartIndex === 0) {
775822
// 4. Special case 1: the mouse cursor is at the left of all markers in
776823
// this line. Then, we have only 1 candidate, we can check if it's inside
777824
// our hit test range right away.
778825
if (isMarkerTimingInDotRadius(nextStartIndex)) {
779-
markerIndex = markerTiming.markerIndex[nextStartIndex];
780-
flowMarkerIndex = markerTiming.flowMarkerIndex[nextStartIndex];
781-
threadIndex = markerTiming.threadIndex[nextStartIndex];
782-
indexInFlowMarkers = nextStartIndex;
826+
return {
827+
rowIndex,
828+
flowIndex,
829+
hoveredItem: {
830+
type: 'SINGLE_MARKER',
831+
hoveredMarker: {
832+
markerIndex: markerTiming.markerIndex[nextStartIndex],
833+
flowMarkerIndex: markerTiming.flowMarkerIndex[nextStartIndex],
834+
threadIndex: markerTiming.threadIndex[nextStartIndex],
835+
indexInFlowMarkers: nextStartIndex,
836+
},
837+
},
838+
};
783839
}
784840
} else {
785841
// 5. Special case 2: the mouse cursor is at the right of all markers in
786842
// this line. Then we only have 1 candidate as well, let's check if it's
787843
// inside our hit test range.
788844
if (isMarkerTimingInDotRadius(nextStartIndex - 1)) {
789-
markerIndex = markerTiming.markerIndex[nextStartIndex - 1];
790-
flowMarkerIndex = markerTiming.flowMarkerIndex[nextStartIndex - 1];
791-
threadIndex = markerTiming.threadIndex[nextStartIndex - 1];
792-
indexInFlowMarkers = nextStartIndex - 1;
845+
return {
846+
rowIndex,
847+
flowIndex,
848+
hoveredItem: {
849+
type: 'SINGLE_MARKER',
850+
hoveredMarker: {
851+
markerIndex: markerTiming.markerIndex[nextStartIndex - 1],
852+
flowMarkerIndex: markerTiming.flowMarkerIndex[nextStartIndex - 1],
853+
threadIndex: markerTiming.threadIndex[nextStartIndex - 1],
854+
indexInFlowMarkers: nextStartIndex - 1,
855+
},
856+
},
857+
};
793858
}
794859
}
795860

796861
return {
797862
rowIndex,
798863
flowIndex,
799-
indexInFlowMarkers,
800-
markerIndex,
801-
flowMarkerIndex,
802-
threadIndex,
864+
hoveredItem: null,
803865
};
804866
};
805867

@@ -834,36 +896,9 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
834896
this.props.changeMouseTimePosition(null);
835897
};
836898

837-
onDoubleClickMarker = (hoveredItems: HoveredFlowPanelItems | null) => {
838-
const markerIndex = hoveredItems === null ? null : hoveredItems.markerIndex;
839-
const threadIndex = hoveredItems === null ? null : hoveredItems.threadIndex;
840-
if (markerIndex === null || threadIndex === null) {
841-
return;
842-
}
843-
const {
844-
fullMarkerListPerThread,
845-
updatePreviewSelection,
846-
rangeStart,
847-
rangeEnd,
848-
} = this.props;
849-
const marker = ensureExists(
850-
fullMarkerListPerThread[threadIndex][markerIndex]
851-
);
852-
const { start, end } = getStartEndRangeForMarker(
853-
rangeStart,
854-
rangeEnd,
855-
marker
856-
);
899+
onDoubleClickMarker = (_hoveredItems: FlowPanelHoverInfo | null) => {};
857900

858-
updatePreviewSelection({
859-
hasSelection: true,
860-
isModifying: false,
861-
selectionStart: start,
862-
selectionEnd: end,
863-
});
864-
};
865-
866-
onSelectItem = (hoveredItems: HoveredFlowPanelItems | null) => {
901+
onSelectItem = (hoveredItems: FlowPanelHoverInfo | null) => {
867902
const flowIndex = hoveredItems === null ? null : hoveredItems.flowIndex;
868903
if (flowIndex === null) {
869904
return;
@@ -873,35 +908,65 @@ class FlowPanelCanvasImpl extends React.PureComponent<Props> {
873908
changeActiveFlows([flowIndex]);
874909
};
875910

876-
onRightClickMarker = (_hoveredItems: HoveredFlowPanelItems | null) => {
911+
onRightClickMarker = (_hoveredItems: FlowPanelHoverInfo | null) => {
877912
// const markerIndex = hoveredItems === null ? null : hoveredItems.markerIndex;
878913
// const { changeRightClickedMarker, threadsKey } = this.props;
879914
// changeRightClickedMarker(threadsKey, markerIndex);
880915
};
881916

882-
getHoveredMarkerInfo = ({
883-
threadIndex,
884-
markerIndex,
885-
}: HoveredFlowPanelItems): React.Node => {
886-
if (
887-
!this.props.shouldDisplayTooltips() ||
888-
threadIndex === null ||
889-
markerIndex === null
890-
) {
917+
getHoveredMarkerInfo = (hoverInfo: FlowPanelHoverInfo): React.Node => {
918+
if (!this.props.shouldDisplayTooltips() || hoverInfo.hoveredItem === null) {
891919
return null;
892920
}
893921

894-
const marker = ensureExists(
895-
this.props.fullMarkerListPerThread[threadIndex][markerIndex]
896-
);
897-
return (
898-
<TooltipMarker
899-
markerIndex={markerIndex}
900-
marker={marker}
901-
threadsKey={threadIndex}
902-
restrictHeightWidth={true}
903-
/>
904-
);
922+
const { hoveredItem } = hoverInfo;
923+
924+
switch (hoveredItem.type) {
925+
case 'SINGLE_MARKER': {
926+
const { threadIndex, markerIndex } = hoveredItem.hoveredMarker;
927+
928+
const marker = ensureExists(
929+
this.props.fullMarkerListPerThread[threadIndex][markerIndex]
930+
);
931+
return (
932+
<TooltipMarker
933+
markerIndex={markerIndex}
934+
marker={marker}
935+
threadsKey={threadIndex}
936+
restrictHeightWidth={true}
937+
/>
938+
);
939+
}
940+
case 'BETWEEN_MARKERS': {
941+
const { markerBeforeHoveredGap, markerAfterHoveredGap } = hoveredItem;
942+
const beforeGapMarker = ensureExists(
943+
this.props.fullMarkerListPerThread[
944+
markerBeforeHoveredGap.threadIndex
945+
][markerBeforeHoveredGap.markerIndex]
946+
);
947+
const afterGapMarker = ensureExists(
948+
this.props.fullMarkerListPerThread[markerAfterHoveredGap.threadIndex][
949+
markerAfterHoveredGap.markerIndex
950+
]
951+
);
952+
return (
953+
<FlowGapTooltip
954+
beforeGapMarkerIndex={markerBeforeHoveredGap.markerIndex}
955+
beforeGapMarker={beforeGapMarker}
956+
beforeGapThreadIndex={markerBeforeHoveredGap.threadIndex}
957+
afterGapMarkerIndex={markerAfterHoveredGap.markerIndex}
958+
afterGapMarker={afterGapMarker}
959+
afterGapThreadIndex={markerAfterHoveredGap.threadIndex}
960+
/>
961+
);
962+
}
963+
default: {
964+
throw assertExhaustiveCheck(
965+
hoveredItem.type,
966+
'Unhandled HoveredFlowPanelItem type.'
967+
);
968+
}
969+
}
905970
};
906971

907972
render() {

0 commit comments

Comments
 (0)