blob: 0116ae7f78a1e58991880ae49e6aa08a603c832b [file] [log] [blame]
Deepanjan Roy9d95a252018-08-09 10:10:19 -04001// Copyright (C) 2018 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Isabelle Taylorc538e242020-01-27 17:34:25 +000015import {hex} from 'color-convert';
Steve Golton50388462023-04-05 16:14:38 +010016import m from 'mithril';
Deepanjan Roy9d95a252018-08-09 10:10:19 -040017
Steve Goltonfeaec372023-09-22 12:57:29 +010018import {currentTargetOffset} from '../base/dom_utils';
Steve Goltond4e5bbe2023-09-07 08:17:38 +010019import {Icons} from '../base/semantic_icons';
Steve Golton278b7f02023-09-06 16:26:23 +010020import {duration, Span, time} from '../base/time';
Isabelle Taylor6b871e92019-09-16 13:46:29 +010021import {Actions} from '../common/actions';
Steve Goltonf095db12023-08-25 10:21:06 +010022import {pluginManager} from '../common/plugins';
Deepanjan Roy9d95a252018-08-09 10:10:19 -040023import {TrackState} from '../common/state';
Hector Dearmanba396db2023-07-03 18:25:08 +010024import {raf} from '../core/raf_scheduler';
Steve Golton8c581fb2023-12-11 09:58:50 +000025import {Migrate, SliceRect, Track, TrackContext, TrackTags} from '../public';
Deepanjan Roy9d95a252018-08-09 10:10:19 -040026
Steve Golton8c581fb2023-12-11 09:58:50 +000027import {checkerboard} from './checkerboard';
Kenzie Schmoll9dd91562022-06-16 14:40:42 -070028import {SELECTION_FILL_COLOR, TRACK_SHELL_WIDTH} from './css_constants';
Deepanjan Roy9d95a252018-08-09 10:10:19 -040029import {globals} from './globals';
30import {drawGridLines} from './gridline_helper';
Deepanjan Roy1f658fe2018-09-11 08:38:17 -040031import {Panel, PanelSize} from './panel';
Isabelle Taylor15fef6e2019-10-16 13:26:37 +010032import {verticalScrollToTrack} from './scroll_helper';
Lalit Maganti666e41e2023-06-27 19:02:32 +010033import {PxSpan, TimeScale} from './time_scale';
Isabelle Taylor5a254b52019-05-16 14:57:30 +010034import {
35 drawVerticalLineAtTime,
Isabelle Taylor5a254b52019-05-16 14:57:30 +010036} from './vertical_line_helper';
Deepanjan Roy9d95a252018-08-09 10:10:19 -040037
Hector Dearmaneaad2042023-04-27 19:33:02 +010038function getTitleSize(title: string): string|undefined {
39 const length = title.length;
40 if (length > 55) {
41 return '9px';
42 }
43 if (length > 50) {
44 return '10px';
45 }
46 if (length > 45) {
47 return '11px';
48 }
49 if (length > 40) {
50 return '12px';
51 }
52 if (length > 35) {
53 return '13px';
54 }
55 return undefined;
56}
57
Hector Dearmana926f4e2018-09-17 13:33:30 +010058function isPinned(id: string) {
59 return globals.state.pinnedTracks.indexOf(id) !== -1;
60}
61
Isabelle Taylorf16d6422020-01-31 11:36:34 +000062function isSelected(id: string) {
Isabelle Taylor797f4832020-09-08 17:52:59 +010063 const selection = globals.state.currentSelection;
64 if (selection === null || selection.kind !== 'AREA') return false;
65 const selectedArea = globals.state.areas[selection.areaId];
66 return selectedArea.tracks.includes(id);
Isabelle Taylorf16d6422020-01-31 11:36:34 +000067}
68
Steve Golton11c0c982023-10-03 08:30:15 +010069interface TrackChipAttrs {
70 text: string;
Hector Dearman453110d2023-07-21 12:19:56 -070071}
72
Steve Golton11c0c982023-10-03 08:30:15 +010073class TrackChip implements m.ClassComponent<TrackChipAttrs> {
74 view({attrs}: m.CVnode<TrackChipAttrs>) {
75 return m('span.chip', attrs.text);
Hector Dearman453110d2023-07-21 12:19:56 -070076 }
77}
78
Steve Golton8c581fb2023-12-11 09:58:50 +000079export function renderChips(tags?: TrackTags) {
80 return [
81 tags?.metric && m(TrackChip, {text: 'metric'}),
82 tags?.debuggable && m(TrackChip, {text: 'debuggable'}),
83 ];
Steve Golton11c0c982023-10-03 08:30:15 +010084}
85
Deepanjan Roy97f63242018-09-20 15:32:01 -040086interface TrackShellAttrs {
Steve Golton8c581fb2023-12-11 09:58:50 +000087 trackKey: string;
88 title: string;
89 buttons: m.Children;
90 tags?: TrackTags;
Deepanjan Roy97f63242018-09-20 15:32:01 -040091}
Primiano Tuccie7b32e52018-11-05 01:24:31 -080092
Deepanjan Roy97f63242018-09-20 15:32:01 -040093class TrackShell implements m.ClassComponent<TrackShellAttrs> {
Primiano Tuccie7b32e52018-11-05 01:24:31 -080094 // Set to true when we click down and drag the
95 private dragging = false;
96 private dropping: 'before'|'after'|undefined = undefined;
Primiano Tuccie7b32e52018-11-05 01:24:31 -080097
Deepanjan Roy97f63242018-09-20 15:32:01 -040098 view({attrs}: m.CVnode<TrackShellAttrs>) {
Isabelle Taylor613d38c2019-10-24 12:35:31 +010099 // The shell should be highlighted if the current search result is inside
100 // this track.
101 let highlightClass = '';
Hector Dearman301ce392021-07-16 13:51:33 +0100102 const searchIndex = globals.state.searchIndex;
Isabelle Taylor613d38c2019-10-24 12:35:31 +0100103 if (searchIndex !== -1) {
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100104 const trackKey = globals.currentSearchResults.trackKeys[searchIndex];
Steve Golton8c581fb2023-12-11 09:58:50 +0000105 if (trackKey === attrs.trackKey) {
Isabelle Taylor613d38c2019-10-24 12:35:31 +0100106 highlightClass = 'flash';
107 }
108 }
109
110 const dragClass = this.dragging ? `drag` : '';
111 const dropClass = this.dropping ? `drop-${this.dropping}` : '';
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400112 return m(
Isabelle Taylor613d38c2019-10-24 12:35:31 +0100113 `.track-shell[draggable=true]`,
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800114 {
Isabelle Taylorf16d6422020-01-31 11:36:34 +0000115 class: `${highlightClass} ${dragClass} ${dropClass}`,
Steve Golton8c581fb2023-12-11 09:58:50 +0000116 ondragstart: (e: DragEvent) => this.ondragstart(e, attrs.trackKey),
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800117 ondragend: this.ondragend.bind(this),
118 ondragover: this.ondragover.bind(this),
119 ondragleave: this.ondragleave.bind(this),
Steve Golton8c581fb2023-12-11 09:58:50 +0000120 ondrop: (e: DragEvent) => this.ondrop(e, attrs.trackKey),
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800121 },
Hector Dearman9cf9e632021-08-23 10:27:52 +0100122 m(
123 'h1',
124 {
Steve Golton8c581fb2023-12-11 09:58:50 +0000125 title: attrs.title,
Hector Dearmaneaad2042023-04-27 19:33:02 +0100126 style: {
Steve Golton8c581fb2023-12-11 09:58:50 +0000127 'font-size': getTitleSize(attrs.title),
Hector Dearmaneaad2042023-04-27 19:33:02 +0100128 },
Hector Dearman9cf9e632021-08-23 10:27:52 +0100129 },
Steve Golton8c581fb2023-12-11 09:58:50 +0000130 attrs.title,
131 renderChips(attrs.tags),
Hector Dearman9cf9e632021-08-23 10:27:52 +0100132 ),
Isabelle Taylorf16d6422020-01-31 11:36:34 +0000133 m('.track-buttons',
Steve Golton8c581fb2023-12-11 09:58:50 +0000134 attrs.buttons,
Isabelle Taylorf16d6422020-01-31 11:36:34 +0000135 m(TrackButton, {
136 action: () => {
137 globals.dispatch(
Steve Golton8c581fb2023-12-11 09:58:50 +0000138 Actions.toggleTrackPinned({trackKey: attrs.trackKey}));
Isabelle Taylorf16d6422020-01-31 11:36:34 +0000139 },
Steve Goltond4e5bbe2023-09-07 08:17:38 +0100140 i: Icons.Pin,
Steve Golton8c581fb2023-12-11 09:58:50 +0000141 filledIcon: isPinned(attrs.trackKey),
142 tooltip: isPinned(attrs.trackKey) ? 'Unpin' : 'Pin to top',
143 showButton: isPinned(attrs.trackKey),
Mingjing Zhang85022272022-07-06 20:00:12 +0000144 fullHeight: true,
Isabelle Taylorf16d6422020-01-31 11:36:34 +0000145 }),
Isabelle Taylor797f4832020-09-08 17:52:59 +0100146 globals.state.currentSelection !== null &&
147 globals.state.currentSelection.kind === 'AREA' ?
148 m(TrackButton, {
Steve Goltonfeaec372023-09-22 12:57:29 +0100149 action: (e: MouseEvent) => {
Isabelle Taylor797f4832020-09-08 17:52:59 +0100150 globals.dispatch(Actions.toggleTrackSelection(
Steve Golton8c581fb2023-12-11 09:58:50 +0000151 {id: attrs.trackKey, isTrackGroup: false}));
Isabelle Taylor797f4832020-09-08 17:52:59 +0100152 e.stopPropagation();
153 },
Steve Golton8c581fb2023-12-11 09:58:50 +0000154 i: isSelected(attrs.trackKey) ? Icons.Checkbox :
155 Icons.BlankCheckbox,
156 tooltip: isSelected(attrs.trackKey) ? 'Remove track' :
157 'Add track to selection',
Isabelle Taylor797f4832020-09-08 17:52:59 +0100158 showButton: true,
159 }) :
160 ''));
Deepanjan Roy97f63242018-09-20 15:32:01 -0400161 }
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800162
Steve Golton8c581fb2023-12-11 09:58:50 +0000163 ondragstart(e: DragEvent, trackKey: string) {
Isabelle Taylor2f1b2722019-02-15 14:37:07 +0000164 const dataTransfer = e.dataTransfer;
165 if (dataTransfer === null) return;
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800166 this.dragging = true;
Hector Dearmanba396db2023-07-03 18:25:08 +0100167 raf.scheduleFullRedraw();
Steve Golton8c581fb2023-12-11 09:58:50 +0000168 dataTransfer.setData('perfetto/track', `${trackKey}`);
Isabelle Taylor2f1b2722019-02-15 14:37:07 +0000169 dataTransfer.setDragImage(new Image(), 0, 0);
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800170 }
171
172 ondragend() {
173 this.dragging = false;
Hector Dearmanba396db2023-07-03 18:25:08 +0100174 raf.scheduleFullRedraw();
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800175 }
176
177 ondragover(e: DragEvent) {
178 if (this.dragging) return;
179 if (!(e.target instanceof HTMLElement)) return;
Isabelle Taylor2f1b2722019-02-15 14:37:07 +0000180 const dataTransfer = e.dataTransfer;
181 if (dataTransfer === null) return;
182 if (!dataTransfer.types.includes('perfetto/track')) return;
183 dataTransfer.dropEffect = 'move';
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800184 e.preventDefault();
185
186 // Apply some hysteresis to the drop logic so that the lightened border
187 // changes only when we get close enough to the border.
188 if (e.offsetY < e.target.scrollHeight / 3) {
189 this.dropping = 'before';
190 } else if (e.offsetY > e.target.scrollHeight / 3 * 2) {
191 this.dropping = 'after';
192 }
Hector Dearmanba396db2023-07-03 18:25:08 +0100193 raf.scheduleFullRedraw();
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800194 }
195
196 ondragleave() {
197 this.dropping = undefined;
Hector Dearmanba396db2023-07-03 18:25:08 +0100198 raf.scheduleFullRedraw();
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800199 }
200
Steve Golton8c581fb2023-12-11 09:58:50 +0000201 ondrop(e: DragEvent, trackKey: string) {
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800202 if (this.dropping === undefined) return;
Isabelle Taylor2f1b2722019-02-15 14:37:07 +0000203 const dataTransfer = e.dataTransfer;
204 if (dataTransfer === null) return;
Hector Dearmanba396db2023-07-03 18:25:08 +0100205 raf.scheduleFullRedraw();
Isabelle Taylor2f1b2722019-02-15 14:37:07 +0000206 const srcId = dataTransfer.getData('perfetto/track');
Steve Golton8c581fb2023-12-11 09:58:50 +0000207 const dstId = trackKey;
Primiano Tuccie7b32e52018-11-05 01:24:31 -0800208 globals.dispatch(Actions.moveTrack({srcId, op: this.dropping, dstId}));
209 this.dropping = undefined;
210 }
Deepanjan Roy97f63242018-09-20 15:32:01 -0400211}
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400212
Steve Goltonf095db12023-08-25 10:21:06 +0100213export interface TrackContentAttrs {
Steve Golton211d6a32023-10-05 13:08:43 +0100214 track: Track;
Steve Goltonf095db12023-08-25 10:21:06 +0100215}
Hector Dearmanc1a703e2019-02-06 11:35:30 +0000216export class TrackContent implements m.ClassComponent<TrackContentAttrs> {
Isabelle Taylor797f4832020-09-08 17:52:59 +0100217 private mouseDownX?: number;
218 private mouseDownY?: number;
219 private selectionOccurred = false;
220
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000221 view(node: m.CVnode<TrackContentAttrs>) {
222 const attrs = node.attrs;
223 return m(
224 '.track-content',
225 {
Steve Goltonfeaec372023-09-22 12:57:29 +0100226 onmousemove: (e: MouseEvent) => {
227 attrs.track.onMouseMove(currentTargetOffset(e));
Hector Dearmanba396db2023-07-03 18:25:08 +0100228 raf.scheduleRedraw();
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000229 },
230 onmouseout: () => {
231 attrs.track.onMouseOut();
Hector Dearmanba396db2023-07-03 18:25:08 +0100232 raf.scheduleRedraw();
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000233 },
Steve Goltonfeaec372023-09-22 12:57:29 +0100234 onmousedown: (e: MouseEvent) => {
235 const {x, y} = currentTargetOffset(e);
236 this.mouseDownX = x;
237 this.mouseDownY = y;
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000238 },
Steve Goltonfeaec372023-09-22 12:57:29 +0100239 onmouseup: (e: MouseEvent) => {
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000240 if (this.mouseDownX === undefined ||
241 this.mouseDownY === undefined) {
242 return;
243 }
Steve Goltonfeaec372023-09-22 12:57:29 +0100244 const {x, y} = currentTargetOffset(e);
245 if (Math.abs(x - this.mouseDownX) > 1 ||
246 Math.abs(y - this.mouseDownY) > 1) {
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000247 this.selectionOccurred = true;
248 }
249 this.mouseDownX = undefined;
250 this.mouseDownY = undefined;
251 },
Steve Goltonfeaec372023-09-22 12:57:29 +0100252 onclick: (e: MouseEvent) => {
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000253 // This click event occurs after any selection mouse up/drag events
254 // so we have to look if the mouse moved during this click to know
255 // if a selection occurred.
256 if (this.selectionOccurred) {
257 this.selectionOccurred = false;
258 return;
259 }
260 // Returns true if something was selected, so stop propagation.
Steve Goltonfeaec372023-09-22 12:57:29 +0100261 if (attrs.track.onMouseClick(currentTargetOffset(e))) {
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000262 e.stopPropagation();
263 }
Hector Dearmanba396db2023-07-03 18:25:08 +0100264 raf.scheduleRedraw();
Hector Dearman23c078b2022-06-05 12:00:25 +0100265 },
Andrew Shulaevee2fbdf2021-11-15 09:02:40 +0000266 },
267 node.children);
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400268 }
Deepanjan Roy97f63242018-09-20 15:32:01 -0400269}
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400270
Deepanjan Roy97f63242018-09-20 15:32:01 -0400271interface TrackComponentAttrs {
Steve Golton8c581fb2023-12-11 09:58:50 +0000272 trackKey: string;
273 heightPx?: number;
274 title: string;
275 buttons?: m.Children;
276 tags?: TrackTags;
277 track?: Track;
Deepanjan Roy97f63242018-09-20 15:32:01 -0400278}
Steve Golton8c581fb2023-12-11 09:58:50 +0000279
Deepanjan Roy97f63242018-09-20 15:32:01 -0400280class TrackComponent implements m.ClassComponent<TrackComponentAttrs> {
281 view({attrs}: m.CVnode<TrackComponentAttrs>) {
Hector Dearman97510d52023-03-10 16:21:12 +0000282 // TODO(hjd): The min height below must match the track_shell_title
283 // max height in common.scss so we should read it from CSS to avoid
284 // them going out of sync.
Isabelle Taylorbb1cc592019-07-25 15:15:27 +0100285 return m(
286 '.track',
287 {
288 style: {
Steve Golton8c581fb2023-12-11 09:58:50 +0000289 height: `${Math.max(18, attrs.heightPx ?? 0)}px`,
Isabelle Taylor0fa95e82019-09-17 15:41:10 +0100290 },
Steve Golton8c581fb2023-12-11 09:58:50 +0000291 id: 'track_' + attrs.trackKey,
Isabelle Taylorbb1cc592019-07-25 15:15:27 +0100292 },
293 [
Steve Golton8c581fb2023-12-11 09:58:50 +0000294 m(TrackShell, {
295 buttons: attrs.buttons,
296 title: attrs.title,
297 trackKey: attrs.trackKey,
298 tags: attrs.tags,
299 }),
300 attrs.track && m(TrackContent, {track: attrs.track}),
Isabelle Taylorbb1cc592019-07-25 15:15:27 +0100301 ]);
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400302 }
Isabelle Taylor15fef6e2019-10-16 13:26:37 +0100303
Isabelle Taylor77fe3f22019-10-25 11:43:20 +0100304 oncreate({attrs}: m.CVnode<TrackComponentAttrs>) {
Steve Golton8c581fb2023-12-11 09:58:50 +0000305 if (globals.scrollToTrackKey === attrs.trackKey) {
306 verticalScrollToTrack(attrs.trackKey);
Steve Golton99b88322023-12-07 18:54:14 +0000307 globals.scrollToTrackKey = undefined;
Isabelle Taylor15fef6e2019-10-16 13:26:37 +0100308 }
309 }
Deepanjan Roy97f63242018-09-20 15:32:01 -0400310}
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400311
Isabelle Taylor6b871e92019-09-16 13:46:29 +0100312export interface TrackButtonAttrs {
Steve Goltonfeaec372023-09-22 12:57:29 +0100313 action: (e: MouseEvent) => void;
Deepanjan Roy97f63242018-09-20 15:32:01 -0400314 i: string;
Isabelle Taylor6b871e92019-09-16 13:46:29 +0100315 tooltip: string;
Isabelle Taylorf16d6422020-01-31 11:36:34 +0000316 showButton: boolean;
Mingjing Zhang85022272022-07-06 20:00:12 +0000317 fullHeight?: boolean;
Hector Dearman42849e62023-03-06 08:43:47 +0000318 filledIcon?: boolean;
Deepanjan Roy97f63242018-09-20 15:32:01 -0400319}
Isabelle Taylor6b871e92019-09-16 13:46:29 +0100320export class TrackButton implements m.ClassComponent<TrackButtonAttrs> {
Deepanjan Roy97f63242018-09-20 15:32:01 -0400321 view({attrs}: m.CVnode<TrackButtonAttrs>) {
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400322 return m(
Hector Dearman42849e62023-03-06 08:43:47 +0000323 'i.track-button',
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400324 {
Mingjing Zhang85022272022-07-06 20:00:12 +0000325 class: [
326 (attrs.showButton ? 'show' : ''),
327 (attrs.fullHeight ? 'full-height' : ''),
Hector Dearman42849e62023-03-06 08:43:47 +0000328 (attrs.filledIcon ? 'material-icons-filled' : 'material-icons'),
Mingjing Zhang85022272022-07-06 20:00:12 +0000329 ].filter(Boolean)
330 .join(' '),
Isabelle Taylor6b871e92019-09-16 13:46:29 +0100331 onclick: attrs.action,
332 title: attrs.tooltip,
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400333 },
Hector Dearmana926f4e2018-09-17 13:33:30 +0100334 attrs.i);
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400335 }
Deepanjan Roy97f63242018-09-20 15:32:01 -0400336}
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400337
Deepanjan Roy1f658fe2018-09-11 08:38:17 -0400338interface TrackPanelAttrs {
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100339 trackKey: string;
Isabelle Taylor9b4a81d2020-01-31 11:11:02 +0000340 selectable: boolean;
Deepanjan Roy1f658fe2018-09-11 08:38:17 -0400341}
342
343export class TrackPanel extends Panel<TrackPanelAttrs> {
Steve Goltonb752fbd2023-10-05 12:57:40 +0100344 // TODO(hjd): It would be nicer if these could not be undefined here.
345 // We should implement a NullTrack which can be used if the trackState
346 // has disappeared.
Steve Golton211d6a32023-10-05 13:08:43 +0100347 private track: Track|undefined;
Steve Goltonb752fbd2023-10-05 12:57:40 +0100348 private trackState: TrackState|undefined;
Steve Golton8c581fb2023-12-11 09:58:50 +0000349 private tags: TrackTags|undefined;
Hector Dearmancb9bb182021-10-18 11:35:46 +0100350
Steve Goltonb47576c2023-09-26 18:36:21 +0100351 private tryLoadTrack(vnode: m.CVnode<TrackPanelAttrs>) {
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100352 const trackKey = vnode.attrs.trackKey;
353 const trackState = globals.state.tracks[trackKey];
Steve Goltonf095db12023-08-25 10:21:06 +0100354
355 if (!trackState) return;
356
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100357 const {uri, params} = trackState;
Steve Golton11c0c982023-10-03 08:30:15 +0100358
359 const trackCtx: TrackContext = {
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100360 trackKey,
Steve Golton11c0c982023-10-03 08:30:15 +0100361 mountStore: <T>(migrate: Migrate<T>) => {
362 const {store, state} = globals;
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100363 const migratedState = migrate(state.tracks[trackKey].state);
Steve Golton11c0c982023-10-03 08:30:15 +0100364 globals.store.edit((draft) => {
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100365 draft.tracks[trackKey].state = migratedState;
Steve Golton11c0c982023-10-03 08:30:15 +0100366 });
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100367 return store.createProxy<T>(['tracks', trackKey, 'state']);
Steve Golton11c0c982023-10-03 08:30:15 +0100368 },
Steve Goltond29ef3c2023-10-26 12:36:54 +0100369 params,
Steve Golton11c0c982023-10-03 08:30:15 +0100370 };
371
Steve Golton2f93d472023-10-17 14:48:59 +0100372 this.track = pluginManager.createTrack(uri, trackCtx);
Steve Golton8c581fb2023-12-11 09:58:50 +0000373 this.tags = pluginManager.resolveTrackInfo(uri)?.tags;
Steve Golton700e41d2023-10-03 13:53:09 +0100374
Steve Goltond29ef3c2023-10-26 12:36:54 +0100375 this.track?.onCreate(trackCtx);
Steve Goltonb752fbd2023-10-05 12:57:40 +0100376 this.trackState = trackState;
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400377 }
378
Steve Goltonb47576c2023-09-26 18:36:21 +0100379 view(vnode: m.CVnode<TrackPanelAttrs>) {
380 if (!this.track) {
381 this.tryLoadTrack(vnode);
382 }
383
Hector Dearmancb9bb182021-10-18 11:35:46 +0100384 if (this.track === undefined || this.trackState === undefined) {
Steve Golton8c581fb2023-12-11 09:58:50 +0000385 return m(TrackComponent, {
386 trackKey: vnode.attrs.trackKey,
387 title: this.trackState?.name ?? 'Loading...',
388 });
Hector Dearmancb9bb182021-10-18 11:35:46 +0100389 }
Steve Golton8c581fb2023-12-11 09:58:50 +0000390 return m(TrackComponent, {
391 tags: this.tags,
392 heightPx: this.track.getHeight(),
393 title: this.trackState.name,
394 trackKey: this.trackState.key,
395 buttons: this.track.getTrackShellButtons(),
396 track: this.track,
397 });
Primiano Tucci9b5f13e2018-08-10 00:36:19 +0100398 }
399
Hector Dearmanfe604122021-11-25 13:19:18 +0000400 oncreate() {
401 if (this.track !== undefined) {
402 this.track.onFullRedraw();
403 }
404 }
405
406 onupdate() {
407 if (this.track !== undefined) {
408 this.track.onFullRedraw();
409 }
410 }
411
Hector Dearman53687812022-03-31 12:26:41 +0100412 onremove() {
413 if (this.track !== undefined) {
Steve Goltonb752fbd2023-10-05 12:57:40 +0100414 this.track.onDestroy();
Hector Dearman53687812022-03-31 12:26:41 +0100415 this.track = undefined;
416 }
417 }
418
Isabelle Taylor01254f72020-01-27 15:29:14 +0000419 highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
Steve Goltonf3897e22023-05-11 14:18:30 +0100420 const {visibleTimeScale} = globals.frontendLocalState;
Isabelle Taylor797f4832020-09-08 17:52:59 +0100421 const selection = globals.state.currentSelection;
Hector Dearmancb9bb182021-10-18 11:35:46 +0100422 const trackState = this.trackState;
423 if (!selection || selection.kind !== 'AREA' || trackState === undefined) {
424 return;
425 }
Isabelle Taylor797f4832020-09-08 17:52:59 +0100426 const selectedArea = globals.state.areas[selection.areaId];
Steve Goltonf3897e22023-05-11 14:18:30 +0100427 const selectedAreaDuration = selectedArea.end - selectedArea.start;
Steve Goltoncfe3e3d2023-10-26 16:24:31 +0100428 if (selectedArea.tracks.includes(trackState.key)) {
Kenzie Schmoll9dd91562022-06-16 14:40:42 -0700429 ctx.fillStyle = SELECTION_FILL_COLOR;
Isabelle Taylor01254f72020-01-27 15:29:14 +0000430 ctx.fillRect(
Steve Goltonb3a389d2023-07-10 11:03:17 +0100431 visibleTimeScale.timeToPx(selectedArea.start) + TRACK_SHELL_WIDTH,
Isabelle Taylor01254f72020-01-27 15:29:14 +0000432 0,
Steve Goltonf3897e22023-05-11 14:18:30 +0100433 visibleTimeScale.durationToPx(selectedAreaDuration),
Isabelle Taylor01254f72020-01-27 15:29:14 +0000434 size.height);
435 }
436 }
437
Deepanjan Roy1f658fe2018-09-11 08:38:17 -0400438 renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
Deepanjan Roy9a906ed2018-09-20 11:06:00 -0400439 ctx.save();
Isabelle Taylor01254f72020-01-27 15:29:14 +0000440
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400441 drawGridLines(
442 ctx,
Hector Dearmanea002ea2019-01-21 11:43:45 +0000443 size.width,
Deepanjan Roy1f658fe2018-09-11 08:38:17 -0400444 size.height);
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400445
Hector Dearmanea002ea2019-01-21 11:43:45 +0000446 ctx.translate(TRACK_SHELL_WIDTH, 0);
Hector Dearmancb9bb182021-10-18 11:35:46 +0100447 if (this.track !== undefined) {
448 this.track.render(ctx);
Steve Golton8c581fb2023-12-11 09:58:50 +0000449 } else {
450 checkerboard(ctx, size.height, 0, size.width - TRACK_SHELL_WIDTH);
Hector Dearmancb9bb182021-10-18 11:35:46 +0100451 }
Deepanjan Roy9a906ed2018-09-20 11:06:00 -0400452 ctx.restore();
Isabelle Taylorb1b818e2019-02-08 14:57:43 +0000453
Isabelle Taylor797f4832020-09-08 17:52:59 +0100454 this.highlightIfTrackSelected(ctx, size);
455
Steve Goltonf3897e22023-05-11 14:18:30 +0100456 const {visibleTimeScale} = globals.frontendLocalState;
Hector Dearman2442d612019-06-04 18:05:23 +0100457 // Draw vertical line when hovering on the notes panel.
Steve Goltonf3897e22023-05-11 14:18:30 +0100458 if (globals.state.hoveredNoteTimestamp !== -1n) {
Isabelle Taylora16dec22019-12-03 16:34:13 +0000459 drawVerticalLineAtTime(
460 ctx,
Steve Goltonf3897e22023-05-11 14:18:30 +0100461 visibleTimeScale,
Hector Dearman301ce392021-07-16 13:51:33 +0100462 globals.state.hoveredNoteTimestamp,
Isabelle Taylora16dec22019-12-03 16:34:13 +0000463 size.height,
464 `#aaa`);
Isabelle Taylor1ae33bc2019-02-22 11:44:33 +0000465 }
Steve Goltonf3897e22023-05-11 14:18:30 +0100466 if (globals.state.hoverCursorTimestamp !== -1n) {
Isabelle Taylor3da305e2020-02-11 11:46:28 +0000467 drawVerticalLineAtTime(
468 ctx,
Steve Goltonf3897e22023-05-11 14:18:30 +0100469 visibleTimeScale,
Steve Goltoncef26532023-03-15 16:23:00 +0000470 globals.state.hoverCursorTimestamp,
Isabelle Taylor3da305e2020-02-11 11:46:28 +0000471 size.height,
Isabelle Taylor797f4832020-09-08 17:52:59 +0100472 `#344596`);
Isabelle Taylorb0c0c0c2019-09-23 14:57:37 +0100473 }
Isabelle Taylorc538e242020-01-27 17:34:25 +0000474
Steve Golton9f56c2f2023-03-30 16:13:52 +0100475 if (globals.state.currentSelection !== null) {
Isabelle Taylorbbd0b1c2019-04-05 15:14:19 +0100476 if (globals.state.currentSelection.kind === 'SLICE' &&
477 globals.sliceDetails.wakeupTs !== undefined) {
478 drawVerticalLineAtTime(
479 ctx,
Steve Goltonf3897e22023-05-11 14:18:30 +0100480 visibleTimeScale,
Isabelle Taylorbbd0b1c2019-04-05 15:14:19 +0100481 globals.sliceDetails.wakeupTs,
482 size.height,
483 `black`);
484 }
Isabelle Taylorb1b818e2019-02-08 14:57:43 +0000485 }
Isabelle Taylorc538e242020-01-27 17:34:25 +0000486 // All marked areas should have semi-transparent vertical lines
487 // marking the start and end.
488 for (const note of Object.values(globals.state.notes)) {
489 if (note.noteType === 'AREA') {
490 const transparentNoteColor =
491 'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
492 drawVerticalLineAtTime(
493 ctx,
Steve Goltonf3897e22023-05-11 14:18:30 +0100494 visibleTimeScale,
495 globals.state.areas[note.areaId].start,
Isabelle Taylorc538e242020-01-27 17:34:25 +0000496 size.height,
497 transparentNoteColor,
498 1);
499 drawVerticalLineAtTime(
500 ctx,
Steve Goltonf3897e22023-05-11 14:18:30 +0100501 visibleTimeScale,
502 globals.state.areas[note.areaId].end,
Isabelle Taylorc538e242020-01-27 17:34:25 +0000503 size.height,
504 transparentNoteColor,
505 1);
Steve Golton9f56c2f2023-03-30 16:13:52 +0100506 } else if (note.noteType === 'DEFAULT') {
507 drawVerticalLineAtTime(
Steve Goltonf3897e22023-05-11 14:18:30 +0100508 ctx, visibleTimeScale, note.timestamp, size.height, note.color);
Isabelle Taylorc538e242020-01-27 17:34:25 +0000509 }
510 }
Deepanjan Roy9d95a252018-08-09 10:10:19 -0400511 }
Andrii698d4f32020-09-11 13:09:06 +0300512
Lalit Maganti666e41e2023-06-27 19:02:32 +0100513 getSliceRect(
Steve Goltonb3a389d2023-07-10 11:03:17 +0100514 visibleTimeScale: TimeScale, visibleWindow: Span<time, duration>,
515 windowSpan: PxSpan, tStart: time, tDur: time, depth: number): SliceRect
516 |undefined {
Hector Dearmancb9bb182021-10-18 11:35:46 +0100517 if (this.track === undefined) {
518 return undefined;
519 }
Lalit Maganti666e41e2023-06-27 19:02:32 +0100520 return this.track.getSliceRect(
Steve Goltonab880912023-06-28 15:47:23 +0100521 visibleTimeScale, visibleWindow, windowSpan, tStart, tDur, depth);
Andrii698d4f32020-09-11 13:09:06 +0300522 }
Deepanjan Royabd79aa2018-08-28 07:29:15 -0400523}