1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2018 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Workerimport {hex} from 'color-convert'; 16*6dbdd20aSAndroid Build Coastguard Workerimport m from 'mithril'; 17*6dbdd20aSAndroid Build Coastguard Workerimport {removeFalsyValues} from '../base/array_utils'; 18*6dbdd20aSAndroid Build Coastguard Workerimport {canvasClip, canvasSave} from '../base/canvas_utils'; 19*6dbdd20aSAndroid Build Coastguard Workerimport {findRef, toHTMLElement} from '../base/dom_utils'; 20*6dbdd20aSAndroid Build Coastguard Workerimport {Size2D, VerticalBounds} from '../base/geom'; 21*6dbdd20aSAndroid Build Coastguard Workerimport {assertExists} from '../base/logging'; 22*6dbdd20aSAndroid Build Coastguard Workerimport {clamp} from '../base/math_utils'; 23*6dbdd20aSAndroid Build Coastguard Workerimport {Time, TimeSpan} from '../base/time'; 24*6dbdd20aSAndroid Build Coastguard Workerimport {TimeScale} from '../base/time_scale'; 25*6dbdd20aSAndroid Build Coastguard Workerimport {featureFlags} from '../core/feature_flags'; 26*6dbdd20aSAndroid Build Coastguard Workerimport {raf} from '../core/raf_scheduler'; 27*6dbdd20aSAndroid Build Coastguard Workerimport {TrackNode} from '../public/workspace'; 28*6dbdd20aSAndroid Build Coastguard Workerimport {TRACK_BORDER_COLOR, TRACK_SHELL_WIDTH} from './css_constants'; 29*6dbdd20aSAndroid Build Coastguard Workerimport {renderFlows} from './flow_events_renderer'; 30*6dbdd20aSAndroid Build Coastguard Workerimport {generateTicks, getMaxMajorTicks, TickType} from './gridline_helper'; 31*6dbdd20aSAndroid Build Coastguard Workerimport {NotesPanel} from './notes_panel'; 32*6dbdd20aSAndroid Build Coastguard Workerimport {OverviewTimelinePanel} from './overview_timeline_panel'; 33*6dbdd20aSAndroid Build Coastguard Workerimport {PanAndZoomHandler} from './pan_and_zoom_handler'; 34*6dbdd20aSAndroid Build Coastguard Workerimport { 35*6dbdd20aSAndroid Build Coastguard Worker PanelContainer, 36*6dbdd20aSAndroid Build Coastguard Worker PanelOrGroup, 37*6dbdd20aSAndroid Build Coastguard Worker RenderedPanelInfo, 38*6dbdd20aSAndroid Build Coastguard Worker} from './panel_container'; 39*6dbdd20aSAndroid Build Coastguard Workerimport {TabPanel} from './tab_panel'; 40*6dbdd20aSAndroid Build Coastguard Workerimport {TickmarkPanel} from './tickmark_panel'; 41*6dbdd20aSAndroid Build Coastguard Workerimport {TimeAxisPanel} from './time_axis_panel'; 42*6dbdd20aSAndroid Build Coastguard Workerimport {TimeSelectionPanel} from './time_selection_panel'; 43*6dbdd20aSAndroid Build Coastguard Workerimport {TrackPanel} from './track_panel'; 44*6dbdd20aSAndroid Build Coastguard Workerimport {drawVerticalLineAtTime} from './vertical_line_helper'; 45*6dbdd20aSAndroid Build Coastguard Workerimport {TraceImpl} from '../core/trace_impl'; 46*6dbdd20aSAndroid Build Coastguard Workerimport {PageWithTraceImplAttrs} from '../core/page_manager'; 47*6dbdd20aSAndroid Build Coastguard Workerimport {AppImpl} from '../core/app_impl'; 48*6dbdd20aSAndroid Build Coastguard Worker 49*6dbdd20aSAndroid Build Coastguard Workerconst OVERVIEW_PANEL_FLAG = featureFlags.register({ 50*6dbdd20aSAndroid Build Coastguard Worker id: 'overviewVisible', 51*6dbdd20aSAndroid Build Coastguard Worker name: 'Overview Panel', 52*6dbdd20aSAndroid Build Coastguard Worker description: 'Show the panel providing an overview of the trace', 53*6dbdd20aSAndroid Build Coastguard Worker defaultValue: true, 54*6dbdd20aSAndroid Build Coastguard Worker}); 55*6dbdd20aSAndroid Build Coastguard Worker 56*6dbdd20aSAndroid Build Coastguard Worker// Checks if the mousePos is within 3px of the start or end of the 57*6dbdd20aSAndroid Build Coastguard Worker// current selected time range. 58*6dbdd20aSAndroid Build Coastguard Workerfunction onTimeRangeBoundary( 59*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 60*6dbdd20aSAndroid Build Coastguard Worker timescale: TimeScale, 61*6dbdd20aSAndroid Build Coastguard Worker mousePos: number, 62*6dbdd20aSAndroid Build Coastguard Worker): 'START' | 'END' | null { 63*6dbdd20aSAndroid Build Coastguard Worker const selection = trace.selection.selection; 64*6dbdd20aSAndroid Build Coastguard Worker if (selection.kind === 'area') { 65*6dbdd20aSAndroid Build Coastguard Worker // If frontend selectedArea exists then we are in the process of editing the 66*6dbdd20aSAndroid Build Coastguard Worker // time range and need to use that value instead. 67*6dbdd20aSAndroid Build Coastguard Worker const area = trace.timeline.selectedArea 68*6dbdd20aSAndroid Build Coastguard Worker ? trace.timeline.selectedArea 69*6dbdd20aSAndroid Build Coastguard Worker : selection; 70*6dbdd20aSAndroid Build Coastguard Worker const start = timescale.timeToPx(area.start); 71*6dbdd20aSAndroid Build Coastguard Worker const end = timescale.timeToPx(area.end); 72*6dbdd20aSAndroid Build Coastguard Worker const startDrag = mousePos - TRACK_SHELL_WIDTH; 73*6dbdd20aSAndroid Build Coastguard Worker const startDistance = Math.abs(start - startDrag); 74*6dbdd20aSAndroid Build Coastguard Worker const endDistance = Math.abs(end - startDrag); 75*6dbdd20aSAndroid Build Coastguard Worker const range = 3 * window.devicePixelRatio; 76*6dbdd20aSAndroid Build Coastguard Worker // We might be within 3px of both boundaries but we should choose 77*6dbdd20aSAndroid Build Coastguard Worker // the closest one. 78*6dbdd20aSAndroid Build Coastguard Worker if (startDistance < range && startDistance <= endDistance) return 'START'; 79*6dbdd20aSAndroid Build Coastguard Worker if (endDistance < range && endDistance <= startDistance) return 'END'; 80*6dbdd20aSAndroid Build Coastguard Worker } 81*6dbdd20aSAndroid Build Coastguard Worker return null; 82*6dbdd20aSAndroid Build Coastguard Worker} 83*6dbdd20aSAndroid Build Coastguard Worker 84*6dbdd20aSAndroid Build Coastguard Workerinterface SelectedContainer { 85*6dbdd20aSAndroid Build Coastguard Worker readonly containerClass: string; 86*6dbdd20aSAndroid Build Coastguard Worker readonly dragStartAbsY: number; 87*6dbdd20aSAndroid Build Coastguard Worker readonly dragEndAbsY: number; 88*6dbdd20aSAndroid Build Coastguard Worker} 89*6dbdd20aSAndroid Build Coastguard Worker 90*6dbdd20aSAndroid Build Coastguard Worker/** 91*6dbdd20aSAndroid Build Coastguard Worker * Top-most level component for the viewer page. Holds tracks, brush timeline, 92*6dbdd20aSAndroid Build Coastguard Worker * panels, and everything else that's part of the main trace viewer page. 93*6dbdd20aSAndroid Build Coastguard Worker */ 94*6dbdd20aSAndroid Build Coastguard Workerexport class ViewerPage implements m.ClassComponent<PageWithTraceImplAttrs> { 95*6dbdd20aSAndroid Build Coastguard Worker private zoomContent?: PanAndZoomHandler; 96*6dbdd20aSAndroid Build Coastguard Worker // Used to prevent global deselection if a pan/drag select occurred. 97*6dbdd20aSAndroid Build Coastguard Worker private keepCurrentSelection = false; 98*6dbdd20aSAndroid Build Coastguard Worker 99*6dbdd20aSAndroid Build Coastguard Worker private overviewTimelinePanel: OverviewTimelinePanel; 100*6dbdd20aSAndroid Build Coastguard Worker private timeAxisPanel: TimeAxisPanel; 101*6dbdd20aSAndroid Build Coastguard Worker private timeSelectionPanel: TimeSelectionPanel; 102*6dbdd20aSAndroid Build Coastguard Worker private notesPanel: NotesPanel; 103*6dbdd20aSAndroid Build Coastguard Worker private tickmarkPanel: TickmarkPanel; 104*6dbdd20aSAndroid Build Coastguard Worker private timelineWidthPx?: number; 105*6dbdd20aSAndroid Build Coastguard Worker private selectedContainer?: SelectedContainer; 106*6dbdd20aSAndroid Build Coastguard Worker private showPanningHint = false; 107*6dbdd20aSAndroid Build Coastguard Worker 108*6dbdd20aSAndroid Build Coastguard Worker private readonly PAN_ZOOM_CONTENT_REF = 'pan-and-zoom-content'; 109*6dbdd20aSAndroid Build Coastguard Worker 110*6dbdd20aSAndroid Build Coastguard Worker constructor(vnode: m.CVnode<PageWithTraceImplAttrs>) { 111*6dbdd20aSAndroid Build Coastguard Worker this.notesPanel = new NotesPanel(vnode.attrs.trace); 112*6dbdd20aSAndroid Build Coastguard Worker this.timeAxisPanel = new TimeAxisPanel(vnode.attrs.trace); 113*6dbdd20aSAndroid Build Coastguard Worker this.timeSelectionPanel = new TimeSelectionPanel(vnode.attrs.trace); 114*6dbdd20aSAndroid Build Coastguard Worker this.tickmarkPanel = new TickmarkPanel(vnode.attrs.trace); 115*6dbdd20aSAndroid Build Coastguard Worker this.overviewTimelinePanel = new OverviewTimelinePanel(vnode.attrs.trace); 116*6dbdd20aSAndroid Build Coastguard Worker this.notesPanel = new NotesPanel(vnode.attrs.trace); 117*6dbdd20aSAndroid Build Coastguard Worker this.timeSelectionPanel = new TimeSelectionPanel(vnode.attrs.trace); 118*6dbdd20aSAndroid Build Coastguard Worker } 119*6dbdd20aSAndroid Build Coastguard Worker 120*6dbdd20aSAndroid Build Coastguard Worker oncreate({dom, attrs}: m.CVnodeDOM<PageWithTraceImplAttrs>) { 121*6dbdd20aSAndroid Build Coastguard Worker const panZoomElRaw = findRef(dom, this.PAN_ZOOM_CONTENT_REF); 122*6dbdd20aSAndroid Build Coastguard Worker const panZoomEl = toHTMLElement(assertExists(panZoomElRaw)); 123*6dbdd20aSAndroid Build Coastguard Worker 124*6dbdd20aSAndroid Build Coastguard Worker const {top: panTop} = panZoomEl.getBoundingClientRect(); 125*6dbdd20aSAndroid Build Coastguard Worker this.zoomContent = new PanAndZoomHandler({ 126*6dbdd20aSAndroid Build Coastguard Worker element: panZoomEl, 127*6dbdd20aSAndroid Build Coastguard Worker onPanned: (pannedPx: number) => { 128*6dbdd20aSAndroid Build Coastguard Worker const timeline = attrs.trace.timeline; 129*6dbdd20aSAndroid Build Coastguard Worker 130*6dbdd20aSAndroid Build Coastguard Worker if (this.timelineWidthPx === undefined) return; 131*6dbdd20aSAndroid Build Coastguard Worker 132*6dbdd20aSAndroid Build Coastguard Worker this.keepCurrentSelection = true; 133*6dbdd20aSAndroid Build Coastguard Worker const timescale = new TimeScale(timeline.visibleWindow, { 134*6dbdd20aSAndroid Build Coastguard Worker left: 0, 135*6dbdd20aSAndroid Build Coastguard Worker right: this.timelineWidthPx, 136*6dbdd20aSAndroid Build Coastguard Worker }); 137*6dbdd20aSAndroid Build Coastguard Worker const tDelta = timescale.pxToDuration(pannedPx); 138*6dbdd20aSAndroid Build Coastguard Worker timeline.panVisibleWindow(tDelta); 139*6dbdd20aSAndroid Build Coastguard Worker }, 140*6dbdd20aSAndroid Build Coastguard Worker onZoomed: (zoomedPositionPx: number, zoomRatio: number) => { 141*6dbdd20aSAndroid Build Coastguard Worker const timeline = attrs.trace.timeline; 142*6dbdd20aSAndroid Build Coastguard Worker // TODO(hjd): Avoid hardcoding TRACK_SHELL_WIDTH. 143*6dbdd20aSAndroid Build Coastguard Worker // TODO(hjd): Improve support for zooming in overview timeline. 144*6dbdd20aSAndroid Build Coastguard Worker const zoomPx = zoomedPositionPx - TRACK_SHELL_WIDTH; 145*6dbdd20aSAndroid Build Coastguard Worker const rect = dom.getBoundingClientRect(); 146*6dbdd20aSAndroid Build Coastguard Worker const centerPoint = zoomPx / (rect.width - TRACK_SHELL_WIDTH); 147*6dbdd20aSAndroid Build Coastguard Worker timeline.zoomVisibleWindow(1 - zoomRatio, centerPoint); 148*6dbdd20aSAndroid Build Coastguard Worker raf.scheduleCanvasRedraw(); 149*6dbdd20aSAndroid Build Coastguard Worker }, 150*6dbdd20aSAndroid Build Coastguard Worker editSelection: (currentPx: number) => { 151*6dbdd20aSAndroid Build Coastguard Worker if (this.timelineWidthPx === undefined) return false; 152*6dbdd20aSAndroid Build Coastguard Worker const timescale = new TimeScale(attrs.trace.timeline.visibleWindow, { 153*6dbdd20aSAndroid Build Coastguard Worker left: 0, 154*6dbdd20aSAndroid Build Coastguard Worker right: this.timelineWidthPx, 155*6dbdd20aSAndroid Build Coastguard Worker }); 156*6dbdd20aSAndroid Build Coastguard Worker return onTimeRangeBoundary(attrs.trace, timescale, currentPx) !== null; 157*6dbdd20aSAndroid Build Coastguard Worker }, 158*6dbdd20aSAndroid Build Coastguard Worker onSelection: ( 159*6dbdd20aSAndroid Build Coastguard Worker dragStartX: number, 160*6dbdd20aSAndroid Build Coastguard Worker dragStartY: number, 161*6dbdd20aSAndroid Build Coastguard Worker prevX: number, 162*6dbdd20aSAndroid Build Coastguard Worker currentX: number, 163*6dbdd20aSAndroid Build Coastguard Worker currentY: number, 164*6dbdd20aSAndroid Build Coastguard Worker editing: boolean, 165*6dbdd20aSAndroid Build Coastguard Worker ) => { 166*6dbdd20aSAndroid Build Coastguard Worker const traceTime = attrs.trace.traceInfo; 167*6dbdd20aSAndroid Build Coastguard Worker const timeline = attrs.trace.timeline; 168*6dbdd20aSAndroid Build Coastguard Worker 169*6dbdd20aSAndroid Build Coastguard Worker if (this.timelineWidthPx === undefined) return; 170*6dbdd20aSAndroid Build Coastguard Worker 171*6dbdd20aSAndroid Build Coastguard Worker // TODO(stevegolton): Don't get the windowSpan from globals, get it from 172*6dbdd20aSAndroid Build Coastguard Worker // here! 173*6dbdd20aSAndroid Build Coastguard Worker const {visibleWindow} = timeline; 174*6dbdd20aSAndroid Build Coastguard Worker const timespan = visibleWindow.toTimeSpan(); 175*6dbdd20aSAndroid Build Coastguard Worker this.keepCurrentSelection = true; 176*6dbdd20aSAndroid Build Coastguard Worker 177*6dbdd20aSAndroid Build Coastguard Worker const timescale = new TimeScale(timeline.visibleWindow, { 178*6dbdd20aSAndroid Build Coastguard Worker left: 0, 179*6dbdd20aSAndroid Build Coastguard Worker right: this.timelineWidthPx, 180*6dbdd20aSAndroid Build Coastguard Worker }); 181*6dbdd20aSAndroid Build Coastguard Worker 182*6dbdd20aSAndroid Build Coastguard Worker if (editing) { 183*6dbdd20aSAndroid Build Coastguard Worker const selection = attrs.trace.selection.selection; 184*6dbdd20aSAndroid Build Coastguard Worker if (selection.kind === 'area') { 185*6dbdd20aSAndroid Build Coastguard Worker const area = attrs.trace.timeline.selectedArea 186*6dbdd20aSAndroid Build Coastguard Worker ? attrs.trace.timeline.selectedArea 187*6dbdd20aSAndroid Build Coastguard Worker : selection; 188*6dbdd20aSAndroid Build Coastguard Worker let newTime = timescale 189*6dbdd20aSAndroid Build Coastguard Worker .pxToHpTime(currentX - TRACK_SHELL_WIDTH) 190*6dbdd20aSAndroid Build Coastguard Worker .toTime(); 191*6dbdd20aSAndroid Build Coastguard Worker // Have to check again for when one boundary crosses over the other. 192*6dbdd20aSAndroid Build Coastguard Worker const curBoundary = onTimeRangeBoundary( 193*6dbdd20aSAndroid Build Coastguard Worker attrs.trace, 194*6dbdd20aSAndroid Build Coastguard Worker timescale, 195*6dbdd20aSAndroid Build Coastguard Worker prevX, 196*6dbdd20aSAndroid Build Coastguard Worker ); 197*6dbdd20aSAndroid Build Coastguard Worker if (curBoundary == null) return; 198*6dbdd20aSAndroid Build Coastguard Worker const keepTime = curBoundary === 'START' ? area.end : area.start; 199*6dbdd20aSAndroid Build Coastguard Worker // Don't drag selection outside of current screen. 200*6dbdd20aSAndroid Build Coastguard Worker if (newTime < keepTime) { 201*6dbdd20aSAndroid Build Coastguard Worker newTime = Time.max(newTime, timespan.start); 202*6dbdd20aSAndroid Build Coastguard Worker } else { 203*6dbdd20aSAndroid Build Coastguard Worker newTime = Time.min(newTime, timespan.end); 204*6dbdd20aSAndroid Build Coastguard Worker } 205*6dbdd20aSAndroid Build Coastguard Worker // When editing the time range we always use the saved tracks, 206*6dbdd20aSAndroid Build Coastguard Worker // since these will not change. 207*6dbdd20aSAndroid Build Coastguard Worker timeline.selectArea( 208*6dbdd20aSAndroid Build Coastguard Worker Time.max(Time.min(keepTime, newTime), traceTime.start), 209*6dbdd20aSAndroid Build Coastguard Worker Time.min(Time.max(keepTime, newTime), traceTime.end), 210*6dbdd20aSAndroid Build Coastguard Worker selection.trackUris, 211*6dbdd20aSAndroid Build Coastguard Worker ); 212*6dbdd20aSAndroid Build Coastguard Worker } 213*6dbdd20aSAndroid Build Coastguard Worker } else { 214*6dbdd20aSAndroid Build Coastguard Worker let startPx = Math.min(dragStartX, currentX) - TRACK_SHELL_WIDTH; 215*6dbdd20aSAndroid Build Coastguard Worker let endPx = Math.max(dragStartX, currentX) - TRACK_SHELL_WIDTH; 216*6dbdd20aSAndroid Build Coastguard Worker if (startPx < 0 && endPx < 0) return; 217*6dbdd20aSAndroid Build Coastguard Worker startPx = clamp(startPx, 0, this.timelineWidthPx); 218*6dbdd20aSAndroid Build Coastguard Worker endPx = clamp(endPx, 0, this.timelineWidthPx); 219*6dbdd20aSAndroid Build Coastguard Worker timeline.selectArea( 220*6dbdd20aSAndroid Build Coastguard Worker timescale.pxToHpTime(startPx).toTime('floor'), 221*6dbdd20aSAndroid Build Coastguard Worker timescale.pxToHpTime(endPx).toTime('ceil'), 222*6dbdd20aSAndroid Build Coastguard Worker ); 223*6dbdd20aSAndroid Build Coastguard Worker 224*6dbdd20aSAndroid Build Coastguard Worker const absStartY = dragStartY + panTop; 225*6dbdd20aSAndroid Build Coastguard Worker const absCurrentY = currentY + panTop; 226*6dbdd20aSAndroid Build Coastguard Worker if (this.selectedContainer === undefined) { 227*6dbdd20aSAndroid Build Coastguard Worker for (const c of dom.querySelectorAll('.pf-panel-container')) { 228*6dbdd20aSAndroid Build Coastguard Worker const {top, bottom} = c.getBoundingClientRect(); 229*6dbdd20aSAndroid Build Coastguard Worker if (top <= absStartY && absCurrentY <= bottom) { 230*6dbdd20aSAndroid Build Coastguard Worker const stack = assertExists(c.querySelector('.pf-panel-stack')); 231*6dbdd20aSAndroid Build Coastguard Worker const stackTop = stack.getBoundingClientRect().top; 232*6dbdd20aSAndroid Build Coastguard Worker this.selectedContainer = { 233*6dbdd20aSAndroid Build Coastguard Worker containerClass: Array.from(c.classList).filter( 234*6dbdd20aSAndroid Build Coastguard Worker (x) => x !== 'pf-panel-container', 235*6dbdd20aSAndroid Build Coastguard Worker )[0], 236*6dbdd20aSAndroid Build Coastguard Worker dragStartAbsY: -stackTop + absStartY, 237*6dbdd20aSAndroid Build Coastguard Worker dragEndAbsY: -stackTop + absCurrentY, 238*6dbdd20aSAndroid Build Coastguard Worker }; 239*6dbdd20aSAndroid Build Coastguard Worker break; 240*6dbdd20aSAndroid Build Coastguard Worker } 241*6dbdd20aSAndroid Build Coastguard Worker } 242*6dbdd20aSAndroid Build Coastguard Worker } else { 243*6dbdd20aSAndroid Build Coastguard Worker const c = assertExists( 244*6dbdd20aSAndroid Build Coastguard Worker dom.querySelector(`.${this.selectedContainer.containerClass}`), 245*6dbdd20aSAndroid Build Coastguard Worker ); 246*6dbdd20aSAndroid Build Coastguard Worker const {top, bottom} = c.getBoundingClientRect(); 247*6dbdd20aSAndroid Build Coastguard Worker const boundedCurrentY = Math.min( 248*6dbdd20aSAndroid Build Coastguard Worker Math.max(top, absCurrentY), 249*6dbdd20aSAndroid Build Coastguard Worker bottom, 250*6dbdd20aSAndroid Build Coastguard Worker ); 251*6dbdd20aSAndroid Build Coastguard Worker const stack = assertExists(c.querySelector('.pf-panel-stack')); 252*6dbdd20aSAndroid Build Coastguard Worker const stackTop = stack.getBoundingClientRect().top; 253*6dbdd20aSAndroid Build Coastguard Worker this.selectedContainer = { 254*6dbdd20aSAndroid Build Coastguard Worker ...this.selectedContainer, 255*6dbdd20aSAndroid Build Coastguard Worker dragEndAbsY: -stackTop + boundedCurrentY, 256*6dbdd20aSAndroid Build Coastguard Worker }; 257*6dbdd20aSAndroid Build Coastguard Worker } 258*6dbdd20aSAndroid Build Coastguard Worker this.showPanningHint = true; 259*6dbdd20aSAndroid Build Coastguard Worker } 260*6dbdd20aSAndroid Build Coastguard Worker raf.scheduleCanvasRedraw(); 261*6dbdd20aSAndroid Build Coastguard Worker }, 262*6dbdd20aSAndroid Build Coastguard Worker endSelection: (edit: boolean) => { 263*6dbdd20aSAndroid Build Coastguard Worker this.selectedContainer = undefined; 264*6dbdd20aSAndroid Build Coastguard Worker const area = attrs.trace.timeline.selectedArea; 265*6dbdd20aSAndroid Build Coastguard Worker // If we are editing we need to pass the current id through to ensure 266*6dbdd20aSAndroid Build Coastguard Worker // the marked area with that id is also updated. 267*6dbdd20aSAndroid Build Coastguard Worker if (edit) { 268*6dbdd20aSAndroid Build Coastguard Worker const selection = attrs.trace.selection.selection; 269*6dbdd20aSAndroid Build Coastguard Worker if (selection.kind === 'area' && area) { 270*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.selection.selectArea({...area}); 271*6dbdd20aSAndroid Build Coastguard Worker } 272*6dbdd20aSAndroid Build Coastguard Worker } else if (area) { 273*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.selection.selectArea({...area}); 274*6dbdd20aSAndroid Build Coastguard Worker } 275*6dbdd20aSAndroid Build Coastguard Worker // Now the selection has ended we stored the final selected area in the 276*6dbdd20aSAndroid Build Coastguard Worker // global state and can remove the in progress selection from the 277*6dbdd20aSAndroid Build Coastguard Worker // timeline. 278*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.timeline.deselectArea(); 279*6dbdd20aSAndroid Build Coastguard Worker // Full redraw to color track shell. 280*6dbdd20aSAndroid Build Coastguard Worker raf.scheduleFullRedraw(); 281*6dbdd20aSAndroid Build Coastguard Worker }, 282*6dbdd20aSAndroid Build Coastguard Worker }); 283*6dbdd20aSAndroid Build Coastguard Worker } 284*6dbdd20aSAndroid Build Coastguard Worker 285*6dbdd20aSAndroid Build Coastguard Worker onremove() { 286*6dbdd20aSAndroid Build Coastguard Worker if (this.zoomContent) this.zoomContent[Symbol.dispose](); 287*6dbdd20aSAndroid Build Coastguard Worker } 288*6dbdd20aSAndroid Build Coastguard Worker 289*6dbdd20aSAndroid Build Coastguard Worker view({attrs}: m.CVnode<PageWithTraceImplAttrs>) { 290*6dbdd20aSAndroid Build Coastguard Worker const scrollingPanels = renderToplevelPanels(attrs.trace); 291*6dbdd20aSAndroid Build Coastguard Worker 292*6dbdd20aSAndroid Build Coastguard Worker const result = m( 293*6dbdd20aSAndroid Build Coastguard Worker '.page.viewer-page', 294*6dbdd20aSAndroid Build Coastguard Worker m( 295*6dbdd20aSAndroid Build Coastguard Worker '.pan-and-zoom-content', 296*6dbdd20aSAndroid Build Coastguard Worker { 297*6dbdd20aSAndroid Build Coastguard Worker ref: this.PAN_ZOOM_CONTENT_REF, 298*6dbdd20aSAndroid Build Coastguard Worker onclick: () => { 299*6dbdd20aSAndroid Build Coastguard Worker // We don't want to deselect when panning/drag selecting. 300*6dbdd20aSAndroid Build Coastguard Worker if (this.keepCurrentSelection) { 301*6dbdd20aSAndroid Build Coastguard Worker this.keepCurrentSelection = false; 302*6dbdd20aSAndroid Build Coastguard Worker return; 303*6dbdd20aSAndroid Build Coastguard Worker } 304*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.selection.clear(); 305*6dbdd20aSAndroid Build Coastguard Worker }, 306*6dbdd20aSAndroid Build Coastguard Worker }, 307*6dbdd20aSAndroid Build Coastguard Worker m( 308*6dbdd20aSAndroid Build Coastguard Worker '.pf-timeline-header', 309*6dbdd20aSAndroid Build Coastguard Worker m(PanelContainer, { 310*6dbdd20aSAndroid Build Coastguard Worker trace: attrs.trace, 311*6dbdd20aSAndroid Build Coastguard Worker className: 'header-panel-container', 312*6dbdd20aSAndroid Build Coastguard Worker panels: removeFalsyValues([ 313*6dbdd20aSAndroid Build Coastguard Worker OVERVIEW_PANEL_FLAG.get() && this.overviewTimelinePanel, 314*6dbdd20aSAndroid Build Coastguard Worker this.timeAxisPanel, 315*6dbdd20aSAndroid Build Coastguard Worker this.timeSelectionPanel, 316*6dbdd20aSAndroid Build Coastguard Worker this.notesPanel, 317*6dbdd20aSAndroid Build Coastguard Worker this.tickmarkPanel, 318*6dbdd20aSAndroid Build Coastguard Worker ]), 319*6dbdd20aSAndroid Build Coastguard Worker selectedYRange: this.getYRange('header-panel-container'), 320*6dbdd20aSAndroid Build Coastguard Worker }), 321*6dbdd20aSAndroid Build Coastguard Worker m('.scrollbar-spacer-vertical'), 322*6dbdd20aSAndroid Build Coastguard Worker ), 323*6dbdd20aSAndroid Build Coastguard Worker m(PanelContainer, { 324*6dbdd20aSAndroid Build Coastguard Worker trace: attrs.trace, 325*6dbdd20aSAndroid Build Coastguard Worker className: 'pinned-panel-container', 326*6dbdd20aSAndroid Build Coastguard Worker panels: AppImpl.instance.isLoadingTrace 327*6dbdd20aSAndroid Build Coastguard Worker ? [] 328*6dbdd20aSAndroid Build Coastguard Worker : attrs.trace.workspace.pinnedTracks.map((trackNode) => { 329*6dbdd20aSAndroid Build Coastguard Worker if (trackNode.uri) { 330*6dbdd20aSAndroid Build Coastguard Worker const tr = attrs.trace.tracks.getTrackRenderer(trackNode.uri); 331*6dbdd20aSAndroid Build Coastguard Worker return new TrackPanel({ 332*6dbdd20aSAndroid Build Coastguard Worker trace: attrs.trace, 333*6dbdd20aSAndroid Build Coastguard Worker reorderable: true, 334*6dbdd20aSAndroid Build Coastguard Worker node: trackNode, 335*6dbdd20aSAndroid Build Coastguard Worker trackRenderer: tr, 336*6dbdd20aSAndroid Build Coastguard Worker revealOnCreate: true, 337*6dbdd20aSAndroid Build Coastguard Worker indentationLevel: 0, 338*6dbdd20aSAndroid Build Coastguard Worker topOffsetPx: 0, 339*6dbdd20aSAndroid Build Coastguard Worker }); 340*6dbdd20aSAndroid Build Coastguard Worker } else { 341*6dbdd20aSAndroid Build Coastguard Worker return new TrackPanel({ 342*6dbdd20aSAndroid Build Coastguard Worker trace: attrs.trace, 343*6dbdd20aSAndroid Build Coastguard Worker node: trackNode, 344*6dbdd20aSAndroid Build Coastguard Worker revealOnCreate: true, 345*6dbdd20aSAndroid Build Coastguard Worker indentationLevel: 0, 346*6dbdd20aSAndroid Build Coastguard Worker topOffsetPx: 0, 347*6dbdd20aSAndroid Build Coastguard Worker }); 348*6dbdd20aSAndroid Build Coastguard Worker } 349*6dbdd20aSAndroid Build Coastguard Worker }), 350*6dbdd20aSAndroid Build Coastguard Worker renderUnderlay: (ctx, size) => renderUnderlay(attrs.trace, ctx, size), 351*6dbdd20aSAndroid Build Coastguard Worker renderOverlay: (ctx, size, panels) => 352*6dbdd20aSAndroid Build Coastguard Worker renderOverlay( 353*6dbdd20aSAndroid Build Coastguard Worker attrs.trace, 354*6dbdd20aSAndroid Build Coastguard Worker ctx, 355*6dbdd20aSAndroid Build Coastguard Worker size, 356*6dbdd20aSAndroid Build Coastguard Worker panels, 357*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.workspace.pinnedTracksNode, 358*6dbdd20aSAndroid Build Coastguard Worker ), 359*6dbdd20aSAndroid Build Coastguard Worker selectedYRange: this.getYRange('pinned-panel-container'), 360*6dbdd20aSAndroid Build Coastguard Worker }), 361*6dbdd20aSAndroid Build Coastguard Worker m(PanelContainer, { 362*6dbdd20aSAndroid Build Coastguard Worker trace: attrs.trace, 363*6dbdd20aSAndroid Build Coastguard Worker className: 'scrolling-panel-container', 364*6dbdd20aSAndroid Build Coastguard Worker panels: AppImpl.instance.isLoadingTrace ? [] : scrollingPanels, 365*6dbdd20aSAndroid Build Coastguard Worker onPanelStackResize: (width) => { 366*6dbdd20aSAndroid Build Coastguard Worker const timelineWidth = width - TRACK_SHELL_WIDTH; 367*6dbdd20aSAndroid Build Coastguard Worker this.timelineWidthPx = timelineWidth; 368*6dbdd20aSAndroid Build Coastguard Worker }, 369*6dbdd20aSAndroid Build Coastguard Worker renderUnderlay: (ctx, size) => renderUnderlay(attrs.trace, ctx, size), 370*6dbdd20aSAndroid Build Coastguard Worker renderOverlay: (ctx, size, panels) => 371*6dbdd20aSAndroid Build Coastguard Worker renderOverlay( 372*6dbdd20aSAndroid Build Coastguard Worker attrs.trace, 373*6dbdd20aSAndroid Build Coastguard Worker ctx, 374*6dbdd20aSAndroid Build Coastguard Worker size, 375*6dbdd20aSAndroid Build Coastguard Worker panels, 376*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.workspace.tracks, 377*6dbdd20aSAndroid Build Coastguard Worker ), 378*6dbdd20aSAndroid Build Coastguard Worker selectedYRange: this.getYRange('scrolling-panel-container'), 379*6dbdd20aSAndroid Build Coastguard Worker }), 380*6dbdd20aSAndroid Build Coastguard Worker ), 381*6dbdd20aSAndroid Build Coastguard Worker m(TabPanel, { 382*6dbdd20aSAndroid Build Coastguard Worker trace: attrs.trace, 383*6dbdd20aSAndroid Build Coastguard Worker }), 384*6dbdd20aSAndroid Build Coastguard Worker this.showPanningHint && m(HelpPanningNotification), 385*6dbdd20aSAndroid Build Coastguard Worker ); 386*6dbdd20aSAndroid Build Coastguard Worker 387*6dbdd20aSAndroid Build Coastguard Worker attrs.trace.tracks.flushOldTracks(); 388*6dbdd20aSAndroid Build Coastguard Worker return result; 389*6dbdd20aSAndroid Build Coastguard Worker } 390*6dbdd20aSAndroid Build Coastguard Worker 391*6dbdd20aSAndroid Build Coastguard Worker private getYRange(cls: string): VerticalBounds | undefined { 392*6dbdd20aSAndroid Build Coastguard Worker if (this.selectedContainer?.containerClass !== cls) { 393*6dbdd20aSAndroid Build Coastguard Worker return undefined; 394*6dbdd20aSAndroid Build Coastguard Worker } 395*6dbdd20aSAndroid Build Coastguard Worker const {dragStartAbsY, dragEndAbsY} = this.selectedContainer; 396*6dbdd20aSAndroid Build Coastguard Worker return { 397*6dbdd20aSAndroid Build Coastguard Worker top: Math.min(dragStartAbsY, dragEndAbsY), 398*6dbdd20aSAndroid Build Coastguard Worker bottom: Math.max(dragStartAbsY, dragEndAbsY), 399*6dbdd20aSAndroid Build Coastguard Worker }; 400*6dbdd20aSAndroid Build Coastguard Worker } 401*6dbdd20aSAndroid Build Coastguard Worker} 402*6dbdd20aSAndroid Build Coastguard Worker 403*6dbdd20aSAndroid Build Coastguard Workerfunction renderUnderlay( 404*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 405*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 406*6dbdd20aSAndroid Build Coastguard Worker canvasSize: Size2D, 407*6dbdd20aSAndroid Build Coastguard Worker): void { 408*6dbdd20aSAndroid Build Coastguard Worker const size = { 409*6dbdd20aSAndroid Build Coastguard Worker width: canvasSize.width - TRACK_SHELL_WIDTH, 410*6dbdd20aSAndroid Build Coastguard Worker height: canvasSize.height, 411*6dbdd20aSAndroid Build Coastguard Worker }; 412*6dbdd20aSAndroid Build Coastguard Worker 413*6dbdd20aSAndroid Build Coastguard Worker using _ = canvasSave(ctx); 414*6dbdd20aSAndroid Build Coastguard Worker ctx.translate(TRACK_SHELL_WIDTH, 0); 415*6dbdd20aSAndroid Build Coastguard Worker 416*6dbdd20aSAndroid Build Coastguard Worker const timewindow = trace.timeline.visibleWindow; 417*6dbdd20aSAndroid Build Coastguard Worker const timescale = new TimeScale(timewindow, {left: 0, right: size.width}); 418*6dbdd20aSAndroid Build Coastguard Worker 419*6dbdd20aSAndroid Build Coastguard Worker // Just render the gridlines - these should appear underneath all tracks 420*6dbdd20aSAndroid Build Coastguard Worker drawGridLines(trace, ctx, timewindow.toTimeSpan(), timescale, size); 421*6dbdd20aSAndroid Build Coastguard Worker} 422*6dbdd20aSAndroid Build Coastguard Worker 423*6dbdd20aSAndroid Build Coastguard Workerfunction renderOverlay( 424*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 425*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 426*6dbdd20aSAndroid Build Coastguard Worker canvasSize: Size2D, 427*6dbdd20aSAndroid Build Coastguard Worker panels: ReadonlyArray<RenderedPanelInfo>, 428*6dbdd20aSAndroid Build Coastguard Worker trackContainer: TrackNode, 429*6dbdd20aSAndroid Build Coastguard Worker): void { 430*6dbdd20aSAndroid Build Coastguard Worker const size = { 431*6dbdd20aSAndroid Build Coastguard Worker width: canvasSize.width - TRACK_SHELL_WIDTH, 432*6dbdd20aSAndroid Build Coastguard Worker height: canvasSize.height, 433*6dbdd20aSAndroid Build Coastguard Worker }; 434*6dbdd20aSAndroid Build Coastguard Worker 435*6dbdd20aSAndroid Build Coastguard Worker using _ = canvasSave(ctx); 436*6dbdd20aSAndroid Build Coastguard Worker ctx.translate(TRACK_SHELL_WIDTH, 0); 437*6dbdd20aSAndroid Build Coastguard Worker canvasClip(ctx, 0, 0, size.width, size.height); 438*6dbdd20aSAndroid Build Coastguard Worker 439*6dbdd20aSAndroid Build Coastguard Worker // TODO(primiano): plumb the TraceImpl obj throughout the viwer page. 440*6dbdd20aSAndroid Build Coastguard Worker renderFlows(trace, ctx, size, panels, trackContainer); 441*6dbdd20aSAndroid Build Coastguard Worker 442*6dbdd20aSAndroid Build Coastguard Worker const timewindow = trace.timeline.visibleWindow; 443*6dbdd20aSAndroid Build Coastguard Worker const timescale = new TimeScale(timewindow, {left: 0, right: size.width}); 444*6dbdd20aSAndroid Build Coastguard Worker 445*6dbdd20aSAndroid Build Coastguard Worker renderHoveredNoteVertical(trace, ctx, timescale, size); 446*6dbdd20aSAndroid Build Coastguard Worker renderHoveredCursorVertical(trace, ctx, timescale, size); 447*6dbdd20aSAndroid Build Coastguard Worker renderWakeupVertical(trace, ctx, timescale, size); 448*6dbdd20aSAndroid Build Coastguard Worker renderNoteVerticals(trace, ctx, timescale, size); 449*6dbdd20aSAndroid Build Coastguard Worker} 450*6dbdd20aSAndroid Build Coastguard Worker 451*6dbdd20aSAndroid Build Coastguard Worker// Render the toplevel "scrolling" tracks and track groups 452*6dbdd20aSAndroid Build Coastguard Workerfunction renderToplevelPanels(trace: TraceImpl): PanelOrGroup[] { 453*6dbdd20aSAndroid Build Coastguard Worker return renderNodes(trace, trace.workspace.children, 0, 0); 454*6dbdd20aSAndroid Build Coastguard Worker} 455*6dbdd20aSAndroid Build Coastguard Worker 456*6dbdd20aSAndroid Build Coastguard Worker// Given a list of tracks and a filter term, return a list pf panels filtered by 457*6dbdd20aSAndroid Build Coastguard Worker// the filter term 458*6dbdd20aSAndroid Build Coastguard Workerfunction renderNodes( 459*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 460*6dbdd20aSAndroid Build Coastguard Worker nodes: ReadonlyArray<TrackNode>, 461*6dbdd20aSAndroid Build Coastguard Worker indent: number, 462*6dbdd20aSAndroid Build Coastguard Worker topOffsetPx: number, 463*6dbdd20aSAndroid Build Coastguard Worker): PanelOrGroup[] { 464*6dbdd20aSAndroid Build Coastguard Worker return nodes.flatMap((node) => { 465*6dbdd20aSAndroid Build Coastguard Worker if (node.headless) { 466*6dbdd20aSAndroid Build Coastguard Worker // Render children as if this node doesn't exist 467*6dbdd20aSAndroid Build Coastguard Worker return renderNodes(trace, node.children, indent, topOffsetPx); 468*6dbdd20aSAndroid Build Coastguard Worker } else if (node.children.length === 0) { 469*6dbdd20aSAndroid Build Coastguard Worker return renderTrackPanel(trace, node, indent, topOffsetPx); 470*6dbdd20aSAndroid Build Coastguard Worker } else { 471*6dbdd20aSAndroid Build Coastguard Worker const headerPanel = renderTrackPanel(trace, node, indent, topOffsetPx); 472*6dbdd20aSAndroid Build Coastguard Worker const isSticky = node.isSummary; 473*6dbdd20aSAndroid Build Coastguard Worker const nextTopOffsetPx = isSticky 474*6dbdd20aSAndroid Build Coastguard Worker ? topOffsetPx + headerPanel.heightPx 475*6dbdd20aSAndroid Build Coastguard Worker : topOffsetPx; 476*6dbdd20aSAndroid Build Coastguard Worker return { 477*6dbdd20aSAndroid Build Coastguard Worker kind: 'group', 478*6dbdd20aSAndroid Build Coastguard Worker collapsed: node.collapsed, 479*6dbdd20aSAndroid Build Coastguard Worker header: headerPanel, 480*6dbdd20aSAndroid Build Coastguard Worker sticky: isSticky, // && node.collapsed?? 481*6dbdd20aSAndroid Build Coastguard Worker topOffsetPx, 482*6dbdd20aSAndroid Build Coastguard Worker childPanels: node.collapsed 483*6dbdd20aSAndroid Build Coastguard Worker ? [] 484*6dbdd20aSAndroid Build Coastguard Worker : renderNodes(trace, node.children, indent + 1, nextTopOffsetPx), 485*6dbdd20aSAndroid Build Coastguard Worker }; 486*6dbdd20aSAndroid Build Coastguard Worker } 487*6dbdd20aSAndroid Build Coastguard Worker }); 488*6dbdd20aSAndroid Build Coastguard Worker} 489*6dbdd20aSAndroid Build Coastguard Worker 490*6dbdd20aSAndroid Build Coastguard Workerfunction renderTrackPanel( 491*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 492*6dbdd20aSAndroid Build Coastguard Worker trackNode: TrackNode, 493*6dbdd20aSAndroid Build Coastguard Worker indent: number, 494*6dbdd20aSAndroid Build Coastguard Worker topOffsetPx: number, 495*6dbdd20aSAndroid Build Coastguard Worker) { 496*6dbdd20aSAndroid Build Coastguard Worker let tr = undefined; 497*6dbdd20aSAndroid Build Coastguard Worker if (trackNode.uri) { 498*6dbdd20aSAndroid Build Coastguard Worker tr = trace.tracks.getTrackRenderer(trackNode.uri); 499*6dbdd20aSAndroid Build Coastguard Worker } 500*6dbdd20aSAndroid Build Coastguard Worker return new TrackPanel({ 501*6dbdd20aSAndroid Build Coastguard Worker trace, 502*6dbdd20aSAndroid Build Coastguard Worker node: trackNode, 503*6dbdd20aSAndroid Build Coastguard Worker trackRenderer: tr, 504*6dbdd20aSAndroid Build Coastguard Worker indentationLevel: indent, 505*6dbdd20aSAndroid Build Coastguard Worker topOffsetPx, 506*6dbdd20aSAndroid Build Coastguard Worker }); 507*6dbdd20aSAndroid Build Coastguard Worker} 508*6dbdd20aSAndroid Build Coastguard Worker 509*6dbdd20aSAndroid Build Coastguard Workerexport function drawGridLines( 510*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 511*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 512*6dbdd20aSAndroid Build Coastguard Worker timespan: TimeSpan, 513*6dbdd20aSAndroid Build Coastguard Worker timescale: TimeScale, 514*6dbdd20aSAndroid Build Coastguard Worker size: Size2D, 515*6dbdd20aSAndroid Build Coastguard Worker): void { 516*6dbdd20aSAndroid Build Coastguard Worker ctx.strokeStyle = TRACK_BORDER_COLOR; 517*6dbdd20aSAndroid Build Coastguard Worker ctx.lineWidth = 1; 518*6dbdd20aSAndroid Build Coastguard Worker 519*6dbdd20aSAndroid Build Coastguard Worker if (size.width > 0 && timespan.duration > 0n) { 520*6dbdd20aSAndroid Build Coastguard Worker const maxMajorTicks = getMaxMajorTicks(size.width); 521*6dbdd20aSAndroid Build Coastguard Worker const offset = trace.timeline.timestampOffset(); 522*6dbdd20aSAndroid Build Coastguard Worker for (const {type, time} of generateTicks(timespan, maxMajorTicks, offset)) { 523*6dbdd20aSAndroid Build Coastguard Worker const px = Math.floor(timescale.timeToPx(time)); 524*6dbdd20aSAndroid Build Coastguard Worker if (type === TickType.MAJOR) { 525*6dbdd20aSAndroid Build Coastguard Worker ctx.beginPath(); 526*6dbdd20aSAndroid Build Coastguard Worker ctx.moveTo(px + 0.5, 0); 527*6dbdd20aSAndroid Build Coastguard Worker ctx.lineTo(px + 0.5, size.height); 528*6dbdd20aSAndroid Build Coastguard Worker ctx.stroke(); 529*6dbdd20aSAndroid Build Coastguard Worker } 530*6dbdd20aSAndroid Build Coastguard Worker } 531*6dbdd20aSAndroid Build Coastguard Worker } 532*6dbdd20aSAndroid Build Coastguard Worker} 533*6dbdd20aSAndroid Build Coastguard Worker 534*6dbdd20aSAndroid Build Coastguard Workerexport function renderHoveredCursorVertical( 535*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 536*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 537*6dbdd20aSAndroid Build Coastguard Worker timescale: TimeScale, 538*6dbdd20aSAndroid Build Coastguard Worker size: Size2D, 539*6dbdd20aSAndroid Build Coastguard Worker) { 540*6dbdd20aSAndroid Build Coastguard Worker if (trace.timeline.hoverCursorTimestamp !== undefined) { 541*6dbdd20aSAndroid Build Coastguard Worker drawVerticalLineAtTime( 542*6dbdd20aSAndroid Build Coastguard Worker ctx, 543*6dbdd20aSAndroid Build Coastguard Worker timescale, 544*6dbdd20aSAndroid Build Coastguard Worker trace.timeline.hoverCursorTimestamp, 545*6dbdd20aSAndroid Build Coastguard Worker size.height, 546*6dbdd20aSAndroid Build Coastguard Worker `#344596`, 547*6dbdd20aSAndroid Build Coastguard Worker ); 548*6dbdd20aSAndroid Build Coastguard Worker } 549*6dbdd20aSAndroid Build Coastguard Worker} 550*6dbdd20aSAndroid Build Coastguard Worker 551*6dbdd20aSAndroid Build Coastguard Workerexport function renderHoveredNoteVertical( 552*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 553*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 554*6dbdd20aSAndroid Build Coastguard Worker timescale: TimeScale, 555*6dbdd20aSAndroid Build Coastguard Worker size: Size2D, 556*6dbdd20aSAndroid Build Coastguard Worker) { 557*6dbdd20aSAndroid Build Coastguard Worker if (trace.timeline.hoveredNoteTimestamp !== undefined) { 558*6dbdd20aSAndroid Build Coastguard Worker drawVerticalLineAtTime( 559*6dbdd20aSAndroid Build Coastguard Worker ctx, 560*6dbdd20aSAndroid Build Coastguard Worker timescale, 561*6dbdd20aSAndroid Build Coastguard Worker trace.timeline.hoveredNoteTimestamp, 562*6dbdd20aSAndroid Build Coastguard Worker size.height, 563*6dbdd20aSAndroid Build Coastguard Worker `#aaa`, 564*6dbdd20aSAndroid Build Coastguard Worker ); 565*6dbdd20aSAndroid Build Coastguard Worker } 566*6dbdd20aSAndroid Build Coastguard Worker} 567*6dbdd20aSAndroid Build Coastguard Worker 568*6dbdd20aSAndroid Build Coastguard Workerexport function renderWakeupVertical( 569*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 570*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 571*6dbdd20aSAndroid Build Coastguard Worker timescale: TimeScale, 572*6dbdd20aSAndroid Build Coastguard Worker size: Size2D, 573*6dbdd20aSAndroid Build Coastguard Worker) { 574*6dbdd20aSAndroid Build Coastguard Worker const selection = trace.selection.selection; 575*6dbdd20aSAndroid Build Coastguard Worker if (selection.kind === 'track_event' && selection.wakeupTs) { 576*6dbdd20aSAndroid Build Coastguard Worker drawVerticalLineAtTime( 577*6dbdd20aSAndroid Build Coastguard Worker ctx, 578*6dbdd20aSAndroid Build Coastguard Worker timescale, 579*6dbdd20aSAndroid Build Coastguard Worker selection.wakeupTs, 580*6dbdd20aSAndroid Build Coastguard Worker size.height, 581*6dbdd20aSAndroid Build Coastguard Worker `black`, 582*6dbdd20aSAndroid Build Coastguard Worker ); 583*6dbdd20aSAndroid Build Coastguard Worker } 584*6dbdd20aSAndroid Build Coastguard Worker} 585*6dbdd20aSAndroid Build Coastguard Worker 586*6dbdd20aSAndroid Build Coastguard Workerexport function renderNoteVerticals( 587*6dbdd20aSAndroid Build Coastguard Worker trace: TraceImpl, 588*6dbdd20aSAndroid Build Coastguard Worker ctx: CanvasRenderingContext2D, 589*6dbdd20aSAndroid Build Coastguard Worker timescale: TimeScale, 590*6dbdd20aSAndroid Build Coastguard Worker size: Size2D, 591*6dbdd20aSAndroid Build Coastguard Worker) { 592*6dbdd20aSAndroid Build Coastguard Worker // All marked areas should have semi-transparent vertical lines 593*6dbdd20aSAndroid Build Coastguard Worker // marking the start and end. 594*6dbdd20aSAndroid Build Coastguard Worker for (const note of trace.notes.notes.values()) { 595*6dbdd20aSAndroid Build Coastguard Worker if (note.noteType === 'SPAN') { 596*6dbdd20aSAndroid Build Coastguard Worker const transparentNoteColor = 597*6dbdd20aSAndroid Build Coastguard Worker 'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)'; 598*6dbdd20aSAndroid Build Coastguard Worker drawVerticalLineAtTime( 599*6dbdd20aSAndroid Build Coastguard Worker ctx, 600*6dbdd20aSAndroid Build Coastguard Worker timescale, 601*6dbdd20aSAndroid Build Coastguard Worker note.start, 602*6dbdd20aSAndroid Build Coastguard Worker size.height, 603*6dbdd20aSAndroid Build Coastguard Worker transparentNoteColor, 604*6dbdd20aSAndroid Build Coastguard Worker 1, 605*6dbdd20aSAndroid Build Coastguard Worker ); 606*6dbdd20aSAndroid Build Coastguard Worker drawVerticalLineAtTime( 607*6dbdd20aSAndroid Build Coastguard Worker ctx, 608*6dbdd20aSAndroid Build Coastguard Worker timescale, 609*6dbdd20aSAndroid Build Coastguard Worker note.end, 610*6dbdd20aSAndroid Build Coastguard Worker size.height, 611*6dbdd20aSAndroid Build Coastguard Worker transparentNoteColor, 612*6dbdd20aSAndroid Build Coastguard Worker 1, 613*6dbdd20aSAndroid Build Coastguard Worker ); 614*6dbdd20aSAndroid Build Coastguard Worker } else if (note.noteType === 'DEFAULT') { 615*6dbdd20aSAndroid Build Coastguard Worker drawVerticalLineAtTime( 616*6dbdd20aSAndroid Build Coastguard Worker ctx, 617*6dbdd20aSAndroid Build Coastguard Worker timescale, 618*6dbdd20aSAndroid Build Coastguard Worker note.timestamp, 619*6dbdd20aSAndroid Build Coastguard Worker size.height, 620*6dbdd20aSAndroid Build Coastguard Worker note.color, 621*6dbdd20aSAndroid Build Coastguard Worker ); 622*6dbdd20aSAndroid Build Coastguard Worker } 623*6dbdd20aSAndroid Build Coastguard Worker } 624*6dbdd20aSAndroid Build Coastguard Worker} 625*6dbdd20aSAndroid Build Coastguard Worker 626*6dbdd20aSAndroid Build Coastguard Workerclass HelpPanningNotification implements m.ClassComponent { 627*6dbdd20aSAndroid Build Coastguard Worker private readonly PANNING_HINT_KEY = 'dismissedPanningHint'; 628*6dbdd20aSAndroid Build Coastguard Worker private dismissed = localStorage.getItem(this.PANNING_HINT_KEY) === 'true'; 629*6dbdd20aSAndroid Build Coastguard Worker 630*6dbdd20aSAndroid Build Coastguard Worker view() { 631*6dbdd20aSAndroid Build Coastguard Worker // Do not show the help notification in embedded mode because local storage 632*6dbdd20aSAndroid Build Coastguard Worker // does not persist for iFrames. The host is responsible for communicating 633*6dbdd20aSAndroid Build Coastguard Worker // to users that they can press '?' for help. 634*6dbdd20aSAndroid Build Coastguard Worker if (AppImpl.instance.embeddedMode || this.dismissed) { 635*6dbdd20aSAndroid Build Coastguard Worker return; 636*6dbdd20aSAndroid Build Coastguard Worker } 637*6dbdd20aSAndroid Build Coastguard Worker return m( 638*6dbdd20aSAndroid Build Coastguard Worker '.helpful-hint', 639*6dbdd20aSAndroid Build Coastguard Worker m( 640*6dbdd20aSAndroid Build Coastguard Worker '.hint-text', 641*6dbdd20aSAndroid Build Coastguard Worker 'Are you trying to pan? Use the WASD keys or hold shift to click ' + 642*6dbdd20aSAndroid Build Coastguard Worker "and drag. Press '?' for more help.", 643*6dbdd20aSAndroid Build Coastguard Worker ), 644*6dbdd20aSAndroid Build Coastguard Worker m( 645*6dbdd20aSAndroid Build Coastguard Worker 'button.hint-dismiss-button', 646*6dbdd20aSAndroid Build Coastguard Worker { 647*6dbdd20aSAndroid Build Coastguard Worker onclick: () => { 648*6dbdd20aSAndroid Build Coastguard Worker this.dismissed = true; 649*6dbdd20aSAndroid Build Coastguard Worker localStorage.setItem(this.PANNING_HINT_KEY, 'true'); 650*6dbdd20aSAndroid Build Coastguard Worker raf.scheduleFullRedraw(); 651*6dbdd20aSAndroid Build Coastguard Worker }, 652*6dbdd20aSAndroid Build Coastguard Worker }, 653*6dbdd20aSAndroid Build Coastguard Worker 'Dismiss', 654*6dbdd20aSAndroid Build Coastguard Worker ), 655*6dbdd20aSAndroid Build Coastguard Worker ); 656*6dbdd20aSAndroid Build Coastguard Worker } 657*6dbdd20aSAndroid Build Coastguard Worker} 658