1// 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 15import * as m from 'mithril'; 16 17import {assertExists} from '../base/logging'; 18import {Actions} from '../common/actions'; 19import {TrackGroupState, TrackState} from '../common/state'; 20 21import {globals} from './globals'; 22import {drawGridLines} from './gridline_helper'; 23import {Panel, PanelSize} from './panel'; 24import {Track} from './track'; 25import {TrackContent} from './track_panel'; 26import {trackRegistry} from './track_registry'; 27import {drawVerticalSelection, 28 drawVerticalLineAtTime} from './vertical_line_helper'; 29 30 31interface Attrs { 32 trackGroupId: string; 33} 34 35export class TrackGroupPanel extends Panel<Attrs> { 36 private readonly trackGroupId: string; 37 private shellWidth = 0; 38 private backgroundColor = '#ffffff'; // Updated from CSS later. 39 private summaryTrack: Track; 40 41 constructor({attrs}: m.CVnode<Attrs>) { 42 super(); 43 this.trackGroupId = attrs.trackGroupId; 44 const trackCreator = trackRegistry.get(this.summaryTrackState.kind); 45 this.summaryTrack = trackCreator.create(this.summaryTrackState); 46 } 47 48 get trackGroupState(): TrackGroupState { 49 return assertExists(globals.state.trackGroups[this.trackGroupId]); 50 } 51 52 get summaryTrackState(): TrackState { 53 return assertExists( 54 globals.state.tracks[this.trackGroupState.summaryTrackId]); 55 } 56 57 view({attrs}: m.CVnode<Attrs>) { 58 const collapsed = this.trackGroupState.collapsed; 59 const name = StripPathFromExecutable(this.trackGroupState.name); 60 return m( 61 `.track-group-panel[collapsed=${collapsed}]`, 62 m('.shell', 63 m('h1', 64 { 65 title: name, 66 }, 67 name, 68 m.trust('‎')), 69 m('.fold-button', 70 { 71 onclick: (e: MouseEvent) => { 72 globals.dispatch(Actions.toggleTrackGroupCollapsed({ 73 trackGroupId: attrs.trackGroupId, 74 })), 75 e.stopPropagation(); 76 } 77 }, 78 m('i.material-icons', 79 this.trackGroupState.collapsed ? 'expand_more' : 'expand_less'))), 80 this.summaryTrack ? m(TrackContent, {track: this.summaryTrack}) : null); 81 } 82 83 oncreate(vnode: m.CVnodeDOM<Attrs>) { 84 this.onupdate(vnode); 85 } 86 87 onupdate({dom}: m.CVnodeDOM<Attrs>) { 88 const shell = assertExists(dom.querySelector('.shell')); 89 this.shellWidth = shell.getBoundingClientRect().width; 90 this.backgroundColor = 91 getComputedStyle(dom).getPropertyValue('--collapsed-background'); 92 } 93 94 renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) { 95 const collapsed = this.trackGroupState.collapsed; 96 if (!collapsed) return; 97 98 ctx.save(); 99 100 ctx.fillStyle = this.backgroundColor; 101 ctx.fillRect(0, 0, size.width, size.height); 102 103 drawGridLines( 104 ctx, 105 globals.frontendLocalState.timeScale, 106 globals.frontendLocalState.visibleWindowTime, 107 size.width, 108 size.height); 109 110 ctx.translate(this.shellWidth, 0); 111 if (this.summaryTrack) { 112 this.summaryTrack.renderCanvas(ctx); 113 } 114 ctx.restore(); 115 116 const localState = globals.frontendLocalState; 117 // Draw vertical line when hovering on the the notes panel. 118 if (localState.showNotePreview) { 119 drawVerticalLineAtTime(ctx, 120 localState.timeScale, 121 localState.hoveredTimestamp, 122 size.height, 123 `#aaa`); 124 } 125 // Draw vertical line when shift is pressed. 126 if (localState.showTimeSelectPreview) { 127 drawVerticalLineAtTime(ctx, 128 localState.timeScale, 129 localState.hoveredTimestamp, 130 size.height, 131 `rgb(52,69,150)`); 132 } 133 if (globals.state.currentSelection !== null) { 134 if (globals.state.currentSelection.kind === 'NOTE') { 135 const note = globals.state.notes[globals.state.currentSelection.id]; 136 drawVerticalLineAtTime(ctx, 137 localState.timeScale, 138 note.timestamp, 139 size.height, 140 note.color); 141 } 142 if (globals.state.currentSelection.kind === 'TIMESPAN') { 143 drawVerticalSelection(ctx, 144 localState.timeScale, 145 globals.state.currentSelection.startTs, 146 globals.state.currentSelection.endTs, 147 size.height, 148 `rgba(52,69,150,0.3)`); 149 } 150 if (globals.state.currentSelection.kind === 'SLICE' && 151 globals.sliceDetails.wakeupTs !== undefined) { 152 drawVerticalLineAtTime( 153 ctx, 154 localState.timeScale, 155 globals.sliceDetails.wakeupTs, 156 size.height, 157 `black`); 158 } 159 } 160 } 161} 162 163function StripPathFromExecutable(path: string) { 164 return path.split('/').slice(-1)[0]; 165} 166