• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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