/* * 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 {CdkVirtualScrollViewport} from '@angular/cdk/scrolling'; import { Component, ElementRef, HostListener, Inject, Input, ViewChild, } from '@angular/core'; import {MatSelectChange} from '@angular/material/select'; import {TraceType} from 'trace/trace_type'; import {CollapsibleSections} from 'viewers/common/collapsible_sections'; import {CollapsibleSectionType} from 'viewers/common/collapsible_section_type'; import {TimestampClickDetail, ViewerEvents} from 'viewers/common/viewer_events'; import {timeButtonStyle} from 'viewers/components/styles/clickable_property.styles'; import {currentElementStyle} from 'viewers/components/styles/current_element.styles'; import {selectedElementStyle} from 'viewers/components/styles/selected_element.styles'; import {viewerCardStyle} from 'viewers/components/styles/viewer_card.styles'; import {UiData, UiDataEntry} from './ui_data'; @Component({ selector: 'viewer-transactions', template: `
{{ entry.transactionId }}
{{ entry.vsyncId }}
{{ entry.pid }}
{{ entry.uid }}
{{ entry.type }}
{{ entry.layerOrDisplayId }}
{{ entry.what }}
`, styles: [ ` .properties-view { flex: 1; } .entries .filters { display: flex; flex-direction: row; } .entries .scroll { flex: 1; height: 100%; } .scroll .entry { display: flex; flex-direction: row; } .filters div, .entries div { padding: 4px; } .time { flex: 0 1 250px; } .id { flex: none; width: 125px; } .vsyncid { flex: none; width: 110px; } .pid { flex: none; width: 75px; } .uid { flex: none; width: 75px; } .type { width: 200px; } .what { flex: 2 0 250px; } .filters .what { margin-right: 16px; } .go-to-current-time { flex: none; margin-top: 4px; font-size: 12px; height: 65%; width: fit-content; } `, selectedElementStyle, currentElementStyle, timeButtonStyle, viewerCardStyle, ], }) class ViewerTransactionsComponent { objectKeys = Object.keys; uiData: UiData = UiData.EMPTY; private lastClicked = ''; propertiesTitle = 'PROPERTIES - PROTO DUMP'; CollapsibleSectionType = CollapsibleSectionType; sections = new CollapsibleSections([ { type: CollapsibleSectionType.PROPERTIES, label: this.propertiesTitle, isCollapsed: false, }, ]); @ViewChild(CdkVirtualScrollViewport) scrollComponent?: CdkVirtualScrollViewport; constructor(@Inject(ElementRef) private elementRef: ElementRef) {} @Input() set inputData(data: UiData) { this.uiData = data; if ( this.uiData.scrollToIndex !== undefined && this.scrollComponent && this.lastClicked !== this.uiData.entries[this.uiData.scrollToIndex].time.formattedValue() ) { this.scrollComponent.scrollToIndex(this.uiData.scrollToIndex); } } onVSyncIdFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.VSyncIdFilterChanged, event.value); } onPidFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.PidFilterChanged, event.value); } onUidFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.UidFilterChanged, event.value); } onTypeFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.TypeFilterChanged, event.value); } onLayerIdFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.LayerIdFilterChanged, event.value); } onWhatFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.WhatFilterChanged, event.value); } onTransactionIdFilterChanged(event: MatSelectChange) { this.emitEvent(ViewerEvents.TransactionIdFilterChanged, event.value); } onEntryClicked(index: number) { this.emitEvent(ViewerEvents.LogClicked, index); } onGoToCurrentTimeClick() { if (this.uiData.currentEntryIndex !== undefined && this.scrollComponent) { this.scrollComponent.scrollToIndex(this.uiData.currentEntryIndex); } } onTimestampClicked(entry: UiDataEntry) { this.emitEvent( ViewerEvents.TimestampClick, new TimestampClickDetail(entry.time.getValue(), entry.traceIndex), ); } @HostListener('document:keydown', ['$event']) async handleKeyboardEvent(event: KeyboardEvent) { const index = this.uiData.selectedEntryIndex ?? this.uiData.currentEntryIndex; if (index === undefined) { return; } if (event.key === 'ArrowDown' && index < this.uiData.entries.length - 1) { event.preventDefault(); this.emitEvent(ViewerEvents.LogChangedByKeyPress, index + 1); } if (event.key === 'ArrowUp' && index > 0) { event.preventDefault(); this.emitEvent(ViewerEvents.LogChangedByKeyPress, index - 1); } } isCurrentEntry(index: number): boolean { return index === this.uiData.currentEntryIndex; } isSelectedEntry(index: number): boolean { return index === this.uiData.selectedEntryIndex; } private emitEvent(event: string, data: any) { const customEvent = new CustomEvent(event, { bubbles: true, detail: data, }); this.elementRef.nativeElement.dispatchEvent(customEvent); } } export {ViewerTransactionsComponent};