Skip to content

Commit ba928c3

Browse files
Merge pull request certinia#709 from lukecotter/feat-timeline-frame-selection
refactor: streamline selection renderer by removing unused state and simplifying context setting
2 parents 5410393 + 790c69a commit ba928c3

2 files changed

Lines changed: 26 additions & 99 deletions

File tree

log-viewer/src/features/timeline/optimised/FlameChart.ts

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,7 @@ export class FlameChart<E extends EventNode = EventNode> {
344344
if (this.worldContainer) {
345345
this.selectionRenderer = new SelectionHighlightRenderer(this.worldContainer);
346346
// Set marker context for marker selection highlighting
347-
this.selectionRenderer.setMarkerContext(
348-
this.markers,
349-
this.index.totalDuration,
350-
this.index.maxDepth,
351-
);
347+
this.selectionRenderer.setMarkerContext(this.markers, this.index.totalDuration);
352348
}
353349

354350
// Setup interaction handler
@@ -494,9 +490,6 @@ export class FlameChart<E extends EventNode = EventNode> {
494490

495491
// Initialize search renderer (for borders/overlays on current match)
496492
this.searchRenderer = new SearchHighlightRenderer(this.worldContainer);
497-
498-
// Ensure zIndex layering is honored for highlight graphics
499-
this.worldContainer.sortableChildren = true;
500493
}
501494

502495
/**
@@ -1105,11 +1098,6 @@ export class FlameChart<E extends EventNode = EventNode> {
11051098
private selectFrame(node: TreeNode<E>): void {
11061099
this.selectionManager?.select(node);
11071100

1108-
// Update selection renderer
1109-
if (this.selectionRenderer) {
1110-
this.selectionRenderer.setSelection(node as TreeNode<EventNode>);
1111-
}
1112-
11131101
// Notify callback
11141102
if (this.callbacks.onSelect) {
11151103
this.callbacks.onSelect(node.data);
@@ -1158,11 +1146,6 @@ export class FlameChart<E extends EventNode = EventNode> {
11581146
private selectMarker(marker: TimelineMarker): void {
11591147
this.selectionManager?.selectMarker(marker);
11601148

1161-
// Update selection renderer
1162-
if (this.selectionRenderer) {
1163-
this.selectionRenderer.setMarkerSelection(marker);
1164-
}
1165-
11661149
// Notify callback
11671150
if (this.callbacks.onMarkerSelect) {
11681151
this.callbacks.onMarkerSelect(marker);
@@ -1187,13 +1170,8 @@ export class FlameChart<E extends EventNode = EventNode> {
11871170
// Navigate and get the new marker (or null if at boundary)
11881171
const nextMarker = this.selectionManager.navigateMarker(direction);
11891172

1190-
// If navigation found a valid marker, update renderer and center viewport
1173+
// If navigation found a valid marker, center viewport and notify
11911174
if (nextMarker) {
1192-
// Update selection renderer
1193-
if (this.selectionRenderer) {
1194-
this.selectionRenderer.setMarkerSelection(nextMarker);
1195-
}
1196-
11971175
// Notify callback
11981176
if (this.callbacks.onMarkerSelect) {
11991177
this.callbacks.onMarkerSelect(nextMarker);
@@ -1235,13 +1213,8 @@ export class FlameChart<E extends EventNode = EventNode> {
12351213
// Navigate and get the new node (or null if at boundary)
12361214
const nextNode = this.selectionManager.navigate(direction);
12371215

1238-
// If navigation found a valid node, update renderer and center viewport
1216+
// If navigation found a valid node, center viewport and notify
12391217
if (nextNode) {
1240-
// Update selection renderer
1241-
if (this.selectionRenderer) {
1242-
this.selectionRenderer.setSelection(nextNode as TreeNode<EventNode>);
1243-
}
1244-
12451218
// Notify callback
12461219
if (this.callbacks.onSelect) {
12471220
this.callbacks.onSelect(nextNode.data);
@@ -1586,7 +1559,10 @@ export class FlameChart<E extends EventNode = EventNode> {
15861559
const highlightMode = this.getHighlightMode();
15871560
if (highlightMode === 'selection') {
15881561
// Selection highlight mode: show selection highlight
1589-
this.selectionRenderer?.render(viewportState);
1562+
// Pass selection state from SelectionManager (single source of truth)
1563+
const selectedNode = this.selectionManager?.getSelected() as TreeNode<EventNode> | null;
1564+
const selectedMarker = this.selectionManager?.getSelectedMarker() ?? null;
1565+
this.selectionRenderer?.render(viewportState, selectedNode, selectedMarker);
15901566
this.searchRenderer?.clear();
15911567
} else if (highlightMode === 'search') {
15921568
// Search highlight mode: show search match highlight

log-viewer/src/features/timeline/optimised/selection/SelectionHighlightRenderer.ts

Lines changed: 19 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,12 @@ export class SelectionHighlightRenderer {
6161
/** Graphics for marker selection highlight (renders behind frames) */
6262
private markerGraphics: PIXI.Graphics;
6363

64-
/** Currently selected node (frame) */
65-
private selectedNode: TreeNode<EventNode> | null = null;
66-
67-
/** Currently selected marker */
68-
private selectedMarker: TimelineMarker | null = null;
69-
7064
/** All markers for duration calculation */
7165
private markers: TimelineMarker[] = [];
7266

7367
/** Timeline end time for last marker duration calculation */
7468
private timelineEnd = 0;
7569

76-
/** Max depth in timeline for marker height calculation */
77-
private maxDepth = 0;
78-
7970
/** Highlight colors extracted from CSS variables (same as search) */
8071
private colors: HighlightColors;
8172

@@ -97,84 +88,49 @@ export class SelectionHighlightRenderer {
9788
this.colors = extractHighlightColors();
9889
}
9990

100-
/**
101-
* Set the currently selected node (frame).
102-
* Clears marker selection (mutually exclusive).
103-
*
104-
* @param node - TreeNode to select, or null to clear selection
105-
*/
106-
public setSelection(node: TreeNode<EventNode> | null): void {
107-
this.selectedNode = node;
108-
this.selectedMarker = null; // Clear marker selection
109-
}
110-
111-
/**
112-
* Set the currently selected marker.
113-
* Clears frame selection (mutually exclusive).
114-
*
115-
* @param marker - TimelineMarker to select, or null to clear selection
116-
*/
117-
public setMarkerSelection(marker: TimelineMarker | null): void {
118-
this.selectedMarker = marker;
119-
this.selectedNode = null; // Clear frame selection
120-
}
121-
12291
/**
12392
* Set markers array and timeline parameters for marker selection.
12493
* Required for calculating marker duration.
12594
*
12695
* @param markers - Array of timeline markers (sorted by startTime)
12796
* @param timelineEnd - End time of timeline in nanoseconds
128-
* @param maxDepth - Maximum depth in timeline
12997
*/
130-
public setMarkerContext(markers: TimelineMarker[], timelineEnd: number, maxDepth: number): void {
98+
public setMarkerContext(markers: TimelineMarker[], timelineEnd: number): void {
13199
this.markers = markers;
132100
this.timelineEnd = timelineEnd;
133-
this.maxDepth = maxDepth;
134-
}
135-
136-
/**
137-
* Get the currently selected node (frame).
138-
*
139-
* @returns Currently selected TreeNode, or null if none
140-
*/
141-
public getSelection(): TreeNode<EventNode> | null {
142-
return this.selectedNode;
143-
}
144-
145-
/**
146-
* Get the currently selected marker.
147-
*
148-
* @returns Currently selected TimelineMarker, or null if none
149-
*/
150-
public getMarkerSelection(): TimelineMarker | null {
151-
return this.selectedMarker;
152101
}
153102

154103
/**
155104
* Render the selection highlight (frame or marker).
105+
* This renderer is stateless - selection state is passed in from SelectionManager.
156106
*
157107
* @param viewport - Viewport state for culling and transforms
108+
* @param selectedNode - Currently selected frame node, or null
109+
* @param selectedMarker - Currently selected marker, or null
158110
*/
159-
public render(viewport: ViewportState): void {
111+
public render(
112+
viewport: ViewportState,
113+
selectedNode: TreeNode<EventNode> | null,
114+
selectedMarker: TimelineMarker | null,
115+
): void {
160116
// Clear both graphics
161117
this.frameGraphics.clear();
162118
this.markerGraphics.clear();
163119

164120
// Render marker selection if present (uses markerGraphics - behind frames)
165-
if (this.selectedMarker) {
166-
this.renderMarkerHighlight(viewport);
121+
if (selectedMarker) {
122+
this.renderMarkerHighlight(viewport, selectedMarker);
167123
return;
168124
}
169125

170126
// Render frame selection if present (uses frameGraphics - on top of frames)
171-
if (!this.selectedNode) {
127+
if (!selectedNode) {
172128
return;
173129
}
174130

175131
const bounds = this.calculateBounds(viewport);
176-
const event = this.selectedNode.data;
177-
const depth = this.selectedNode.depth ?? 0;
132+
const event = selectedNode.data;
133+
const depth = selectedNode.depth ?? 0;
178134

179135
// Check visibility
180136
if (!this.isVisible(event, depth, bounds)) {
@@ -197,20 +153,17 @@ export class SelectionHighlightRenderer {
197153
* Markers render as full-height vertical bands that cover the entire viewport height.
198154
*
199155
* @param viewport - Viewport state for transforms
156+
* @param selectedMarker - The marker to highlight
200157
*/
201-
private renderMarkerHighlight(viewport: ViewportState): void {
202-
if (!this.selectedMarker) {
203-
return;
204-
}
205-
158+
private renderMarkerHighlight(viewport: ViewportState, selectedMarker: TimelineMarker): void {
206159
// Calculate marker duration (extends to next marker or timeline end)
207-
const markerIndex = this.markers.findIndex((m) => m.id === this.selectedMarker!.id);
160+
const markerIndex = this.markers.findIndex((m) => m.id === selectedMarker.id);
208161
const nextMarker = this.markers[markerIndex + 1];
209162
const markerEnd = nextMarker?.startTime ?? this.timelineEnd;
210-
const duration = markerEnd - this.selectedMarker.startTime;
163+
const duration = markerEnd - selectedMarker.startTime;
211164

212165
// Calculate screen position
213-
const screenX = this.selectedMarker.startTime * viewport.zoom;
166+
const screenX = selectedMarker.startTime * viewport.zoom;
214167
const screenWidth = duration * viewport.zoom;
215168

216169
// Full height to cover entire visible viewport regardless of pan position
@@ -253,8 +206,6 @@ export class SelectionHighlightRenderer {
253206
public clear(): void {
254207
this.frameGraphics.clear();
255208
this.markerGraphics.clear();
256-
this.selectedNode = null;
257-
this.selectedMarker = null;
258209
}
259210

260211
/**

0 commit comments

Comments
 (0)