/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Component, ElementRef, EventEmitter, HostListener, Input, Output, SimpleChanges, ViewChild, } from '@angular/core'; import {TimelineData} from 'app/timeline_data'; import {assertDefined} from 'common/assert_utils'; import {Timestamp} from 'trace/timestamp'; import {Traces} from 'trace/traces'; import {TracePosition} from 'trace/trace_position'; import {TraceType} from 'trace/trace_type'; import {MiniCanvasDrawer, MiniCanvasDrawerInput} from './mini_canvas_drawer'; @Component({ selector: 'mini-timeline', template: `
`, styles: [ ` #mini-timeline-wrapper { width: 100%; min-height: 5em; height: 100%; } `, ], }) export class MiniTimelineComponent { @Input() timelineData!: TimelineData; @Input() currentTracePosition!: TracePosition; @Input() selectedTraces!: TraceType[]; @Output() onTracePositionUpdate = new EventEmitter(); @Output() onSeekTimestampUpdate = new EventEmitter(); @ViewChild('miniTimelineWrapper', {static: false}) miniTimelineWrapper!: ElementRef; @ViewChild('canvas', {static: false}) canvasRef!: ElementRef; get canvas(): HTMLCanvasElement { return this.canvasRef.nativeElement; } private drawer: MiniCanvasDrawer | undefined = undefined; ngAfterViewInit(): void { this.makeHiPPICanvas(); const updateTimestampCallback = (timestamp: Timestamp) => { this.onSeekTimestampUpdate.emit(undefined); this.onTracePositionUpdate.emit(TracePosition.fromTimestamp(timestamp)); }; this.drawer = new MiniCanvasDrawer( this.canvas, () => this.getMiniCanvasDrawerInput(), (position) => { const timestampType = this.timelineData.getTimestampType()!; this.onSeekTimestampUpdate.emit(position); }, updateTimestampCallback, (selection) => { const timestampType = this.timelineData.getTimestampType()!; this.timelineData.setSelectionTimeRange(selection); }, updateTimestampCallback ); this.drawer.draw(); } ngOnChanges(changes: SimpleChanges) { if (this.drawer !== undefined) { this.drawer.draw(); } } private getMiniCanvasDrawerInput() { return new MiniCanvasDrawerInput( this.timelineData.getFullTimeRange(), this.currentTracePosition.timestamp, this.timelineData.getSelectionTimeRange(), this.getTracesToShow() ); } private getTracesToShow(): Traces { const traces = new Traces(); this.selectedTraces .filter((type) => this.timelineData.getTraces().getTrace(type) !== undefined) .forEach((type) => { traces.setTrace(type, assertDefined(this.timelineData.getTraces().getTrace(type))); }); return traces; } private makeHiPPICanvas() { // Reset any size before computing new size to avoid it interfering with size computations this.canvas.width = 0; this.canvas.height = 0; this.canvas.style.width = 'auto'; this.canvas.style.height = 'auto'; const width = this.miniTimelineWrapper.nativeElement.offsetWidth; const height = this.miniTimelineWrapper.nativeElement.offsetHeight; const HiPPIwidth = window.devicePixelRatio * width; const HiPPIheight = window.devicePixelRatio * height; this.canvas.width = HiPPIwidth; this.canvas.height = HiPPIheight; this.canvas.style.width = width + 'px'; this.canvas.style.height = height + 'px'; // ensure all drawing operations are scaled if (window.devicePixelRatio !== 1) { const context = this.canvas.getContext('2d')!; context.scale(window.devicePixelRatio, window.devicePixelRatio); } } @HostListener('window:resize', ['$event']) onResize(event: Event) { this.makeHiPPICanvas(); this.drawer?.draw(); } }