1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import { 18 Component, 19 ElementRef, 20 EventEmitter, 21 HostListener, 22 Input, 23 Output, 24 QueryList, 25 ViewChild, 26 ViewChildren, 27} from '@angular/core'; 28import {TimelineData} from 'app/timeline_data'; 29import {TRACE_INFO} from 'app/trace_info'; 30import {Timestamp} from 'trace/timestamp'; 31import {Trace} from 'trace/trace'; 32import {TracePosition} from 'trace/trace_position'; 33import {SingleTimelineComponent} from './single_timeline_component'; 34 35@Component({ 36 selector: 'expanded-timeline', 37 template: ` 38 <div id="expanded-timeline-wrapper" #expandedTimelineWrapper> 39 <div 40 *ngFor="let trace of this.timelineData.getTraces(); trackBy: trackTraceBySelectedTimestamp" 41 class="timeline"> 42 <div class="icon-wrapper"> 43 <mat-icon 44 class="icon" 45 [matTooltip]="TRACE_INFO[trace.type].name" 46 [style]="{color: TRACE_INFO[trace.type].color}"> 47 {{ TRACE_INFO[trace.type].icon }} 48 </mat-icon> 49 </div> 50 <single-timeline 51 [color]="TRACE_INFO[trace.type].color" 52 [trace]="trace" 53 [selectedEntry]="timelineData.findCurrentEntryFor(trace.type)" 54 [selectionRange]="timelineData.getSelectionTimeRange()" 55 (onTracePositionUpdate)="onTracePositionUpdate.emit($event)" 56 class="single-timeline"></single-timeline> 57 <div class="icon-wrapper"> 58 <mat-icon class="icon placeholder-icon"></mat-icon> 59 </div> 60 </div> 61 62 <!-- A filler row matching the format and colors of filled rows but with no content --> 63 <div class="timeline units-row"> 64 <div class="icon-wrapper"> 65 <mat-icon class="icon placeholder-icon"></mat-icon> 66 </div> 67 <div class="single-timeline"></div> 68 <div class="icon-wrapper"> 69 <mat-icon class="icon placeholder-icon"></mat-icon> 70 </div> 71 </div> 72 73 <!-- TODO: Implement properly later when we have more time --> 74 <!-- <div id="pointer-overlay" class="timeline"> 75 <div class="icon-wrapper" [style]="{ visibility: 'hidden' }"> 76 <mat-icon class="icon placeholder-icon">home</mat-icon> 77 </div> 78 <selection-cursor 79 class="selection-cursor" 80 [currentTimestamp]="currentTimestamp" 81 [from]="presenter.selection.from" 82 [to]="presenter.selection.to" 83 ></selection-cursor> 84 </div> --> 85 </div> 86 `, 87 styles: [ 88 ` 89 #expanded-timeline-wrapper { 90 display: flex; 91 flex-direction: column; 92 height: 100%; 93 position: relative; 94 } 95 #pointer-overlay { 96 pointer-events: none; 97 position: absolute; 98 top: 0; 99 bottom: 0; 100 left: 0; 101 right: 0; 102 display: flex; 103 align-items: stretch; 104 } 105 .timeline { 106 display: flex; 107 flex-direction: row; 108 align-items: center; 109 justify-content: center; 110 width: 100%; 111 } 112 .timeline .single-timeline { 113 flex-grow: 1; 114 } 115 .selection-cursor { 116 flex-grow: 1; 117 } 118 .timeline { 119 border-bottom: 1px solid #f1f3f4; 120 } 121 .icon-wrapper { 122 background-color: #f1f3f4; 123 align-self: stretch; 124 display: flex; 125 justify-content: center; 126 } 127 .icon { 128 margin: 1rem; 129 align-self: center; 130 } 131 .units-row { 132 flex-grow: 1; 133 align-self: baseline; 134 } 135 .units-row .placeholder-icon { 136 visibility: hidden; 137 } 138 `, 139 ], 140}) 141export class ExpandedTimelineComponent { 142 @Input() timelineData!: TimelineData; 143 @Output() onTracePositionUpdate = new EventEmitter<TracePosition>(); 144 145 @ViewChild('canvas', {static: false}) canvasRef!: ElementRef<HTMLCanvasElement>; 146 @ViewChild('expandedTimelineWrapper', {static: false}) warpperRef!: ElementRef; 147 @ViewChildren(SingleTimelineComponent) singleTimelines!: QueryList<SingleTimelineComponent>; 148 149 TRACE_INFO = TRACE_INFO; 150 151 @HostListener('window:resize', ['$event']) 152 onResize(event: Event) { 153 this.resizeCanvases(); 154 } 155 156 trackTraceBySelectedTimestamp = (index: number, trace: Trace<{}>): Timestamp | undefined => { 157 return this.timelineData.findCurrentEntryFor(trace.type)?.getTimestamp(); 158 }; 159 160 private resizeCanvases() { 161 // Reset any size before computing new size to avoid it interfering with size computations. 162 // Needs to be done together because otherwise the sizes of each timeline will interfere with 163 // each other, since if one timeline is still too big the container will stretch to that size. 164 for (const timeline of this.singleTimelines) { 165 timeline.canvas.width = 0; 166 timeline.canvas.height = 0; 167 timeline.canvas.style.width = 'auto'; 168 timeline.canvas.style.height = 'auto'; 169 } 170 171 for (const timeline of this.singleTimelines) { 172 timeline.initializeCanvas(); 173 timeline.canvas.height = 0; 174 timeline.canvas.style.width = 'auto'; 175 timeline.canvas.style.height = 'auto'; 176 } 177 } 178} 179