• 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  SimpleChanges,
25  ViewChild,
26} from '@angular/core';
27import {TimelineData} from 'app/timeline_data';
28import {assertDefined} from 'common/assert_utils';
29import {Timestamp} from 'trace/timestamp';
30import {Traces} from 'trace/traces';
31import {TracePosition} from 'trace/trace_position';
32import {TraceType} from 'trace/trace_type';
33import {MiniCanvasDrawer, MiniCanvasDrawerInput} from './mini_canvas_drawer';
34
35@Component({
36  selector: 'mini-timeline',
37  template: `
38    <div id="mini-timeline-wrapper" #miniTimelineWrapper>
39      <canvas #canvas></canvas>
40    </div>
41  `,
42  styles: [
43    `
44      #mini-timeline-wrapper {
45        width: 100%;
46        min-height: 5em;
47        height: 100%;
48      }
49    `,
50  ],
51})
52export class MiniTimelineComponent {
53  @Input() timelineData!: TimelineData;
54  @Input() currentTracePosition!: TracePosition;
55  @Input() selectedTraces!: TraceType[];
56
57  @Output() onTracePositionUpdate = new EventEmitter<TracePosition>();
58  @Output() onSeekTimestampUpdate = new EventEmitter<Timestamp | undefined>();
59
60  @ViewChild('miniTimelineWrapper', {static: false}) miniTimelineWrapper!: ElementRef;
61  @ViewChild('canvas', {static: false}) canvasRef!: ElementRef;
62  get canvas(): HTMLCanvasElement {
63    return this.canvasRef.nativeElement;
64  }
65
66  private drawer: MiniCanvasDrawer | undefined = undefined;
67
68  ngAfterViewInit(): void {
69    this.makeHiPPICanvas();
70
71    const updateTimestampCallback = (timestamp: Timestamp) => {
72      this.onSeekTimestampUpdate.emit(undefined);
73      this.onTracePositionUpdate.emit(TracePosition.fromTimestamp(timestamp));
74    };
75
76    this.drawer = new MiniCanvasDrawer(
77      this.canvas,
78      () => this.getMiniCanvasDrawerInput(),
79      (position) => {
80        const timestampType = this.timelineData.getTimestampType()!;
81        this.onSeekTimestampUpdate.emit(position);
82      },
83      updateTimestampCallback,
84      (selection) => {
85        const timestampType = this.timelineData.getTimestampType()!;
86        this.timelineData.setSelectionTimeRange(selection);
87      },
88      updateTimestampCallback
89    );
90    this.drawer.draw();
91  }
92
93  ngOnChanges(changes: SimpleChanges) {
94    if (this.drawer !== undefined) {
95      this.drawer.draw();
96    }
97  }
98
99  private getMiniCanvasDrawerInput() {
100    return new MiniCanvasDrawerInput(
101      this.timelineData.getFullTimeRange(),
102      this.currentTracePosition.timestamp,
103      this.timelineData.getSelectionTimeRange(),
104      this.getTracesToShow()
105    );
106  }
107
108  private getTracesToShow(): Traces {
109    const traces = new Traces();
110    this.selectedTraces
111      .filter((type) => this.timelineData.getTraces().getTrace(type) !== undefined)
112      .forEach((type) => {
113        traces.setTrace(type, assertDefined(this.timelineData.getTraces().getTrace(type)));
114      });
115    return traces;
116  }
117
118  private makeHiPPICanvas() {
119    // Reset any size before computing new size to avoid it interfering with size computations
120    this.canvas.width = 0;
121    this.canvas.height = 0;
122    this.canvas.style.width = 'auto';
123    this.canvas.style.height = 'auto';
124
125    const width = this.miniTimelineWrapper.nativeElement.offsetWidth;
126    const height = this.miniTimelineWrapper.nativeElement.offsetHeight;
127
128    const HiPPIwidth = window.devicePixelRatio * width;
129    const HiPPIheight = window.devicePixelRatio * height;
130
131    this.canvas.width = HiPPIwidth;
132    this.canvas.height = HiPPIheight;
133    this.canvas.style.width = width + 'px';
134    this.canvas.style.height = height + 'px';
135
136    // ensure all drawing operations are scaled
137    if (window.devicePixelRatio !== 1) {
138      const context = this.canvas.getContext('2d')!;
139      context.scale(window.devicePixelRatio, window.devicePixelRatio);
140    }
141  }
142
143  @HostListener('window:resize', ['$event'])
144  onResize(event: Event) {
145    this.makeHiPPICanvas();
146    this.drawer?.draw();
147  }
148}
149