1/* 2 * Copyright (C) 2024 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 */ 16import { 17 Component, 18 ElementRef, 19 EventEmitter, 20 Inject, 21 Input, 22 Output, 23} from '@angular/core'; 24import {PersistentStore} from 'common/persistent_store'; 25import {Analytics} from 'logging/analytics'; 26import {TraceType} from 'trace/trace_type'; 27import {CollapsibleSectionType} from 'viewers/common/collapsible_section_type'; 28import {CuratedProperties} from 'viewers/common/curated_properties'; 29import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node'; 30import {UserOptions} from 'viewers/common/user_options'; 31import {ViewerEvents} from 'viewers/common/viewer_events'; 32import {nodeStyles} from 'viewers/components/styles/node.styles'; 33import {searchBoxStyle} from './styles/search_box.styles'; 34import {viewerCardInnerStyle} from './styles/viewer_card.styles'; 35 36@Component({ 37 selector: 'properties-view', 38 template: ` 39 <div class="view-header"> 40 <div class="title-section"> 41 <collapsible-section-title 42 class="properties-title" 43 [class.padded-title]="!hasUserOptions()" 44 [title]="title" 45 (collapseButtonClicked)="collapseButtonClicked.emit()"></collapsible-section-title> 46 47 <mat-form-field *ngIf="showFilter" class="search-box" (keydown.enter)="$event.target.blur()"> 48 <mat-label>Search</mat-label> 49 50 <input matInput [(ngModel)]="filterString" (ngModelChange)="filterTree()" name="filter" /> 51 </mat-form-field> 52 </div> 53 54 <user-options 55 *ngIf="hasUserOptions()" 56 class="view-controls" 57 [userOptions]="userOptions" 58 [eventType]="ViewerEvents.PropertiesUserOptionsChange" 59 [traceType]="traceType" 60 [logCallback]="Analytics.Navigation.logPropertiesSettingsChanged"> 61 </user-options> 62 </div> 63 64 <mat-divider *ngIf="hasUserOptions()"></mat-divider> 65 66 <ng-container *ngIf="showViewCaptureFormat()"> 67 <view-capture-property-groups 68 class="property-groups" 69 [properties]="curatedProperties"></view-capture-property-groups> 70 71 <mat-divider *ngIf="showPropertiesTree()"></mat-divider> 72 </ng-container> 73 74 <div *ngIf="showPropertiesTree()" class="properties-content"> 75 <div class="tree-wrapper"> 76 <tree-view 77 [node]="propertiesTree" 78 [useStoredExpandedState]="!!store" 79 [itemsClickable]="true" 80 [highlightedItem]="highlightedProperty" 81 (highlightedChange)="onHighlightedPropertyChange($event)"></tree-view> 82 </div> 83 </div> 84 85 <span class="mat-body-1 placeholder-text" *ngIf="!showPropertiesTree() && placeholderText"> {{ placeholderText }} </span> 86 `, 87 styles: [ 88 ` 89 .view-header { 90 display: flex; 91 flex-direction: column; 92 } 93 94 .padded-title { 95 padding-bottom: 8px; 96 } 97 98 .property-groups { 99 overflow-y: auto; 100 } 101 102 .properties-content { 103 flex: 1; 104 display: flex; 105 flex-direction: column; 106 overflow-y: auto; 107 padding: 0px 12px; 108 } 109 110 .placeholder-text { 111 padding: 8px 12px; 112 } 113 `, 114 nodeStyles, 115 searchBoxStyle, 116 viewerCardInnerStyle, 117 ], 118}) 119export class PropertiesComponent { 120 Analytics = Analytics; 121 CollapsibleSectionType = CollapsibleSectionType; 122 filterString = ''; 123 ViewerEvents = ViewerEvents; 124 125 @Input() title = 'PROPERTIES'; 126 @Input() showFilter = true; 127 @Input() userOptions: UserOptions = {}; 128 @Input() placeholderText = ''; 129 @Input() propertiesTree: UiPropertyTreeNode | undefined; 130 @Input() highlightedProperty = ''; 131 @Input() curatedProperties: CuratedProperties | undefined; 132 @Input() displayPropertyGroups = false; 133 @Input() isProtoDump = false; 134 @Input() traceType: TraceType | undefined; 135 @Input() store: PersistentStore | undefined; 136 137 @Output() collapseButtonClicked = new EventEmitter(); 138 139 constructor(@Inject(ElementRef) private elementRef: ElementRef) {} 140 141 filterTree() { 142 const event = new CustomEvent(ViewerEvents.PropertiesFilterChange, { 143 bubbles: true, 144 detail: {filterString: this.filterString}, 145 }); 146 this.elementRef.nativeElement.dispatchEvent(event); 147 } 148 149 onHighlightedPropertyChange(newNode: UiPropertyTreeNode) { 150 const event = new CustomEvent(ViewerEvents.HighlightedPropertyChange, { 151 bubbles: true, 152 detail: {id: newNode.id}, 153 }); 154 this.elementRef.nativeElement.dispatchEvent(event); 155 } 156 157 hasUserOptions() { 158 return Object.keys(this.userOptions).length > 0; 159 } 160 161 showViewCaptureFormat(): boolean { 162 return ( 163 this.traceType === TraceType.VIEW_CAPTURE && 164 this.filterString === '' && 165 // Todo: Highlight Inline in formatted ViewCapture Properties Component. 166 !this.userOptions['showDiff']?.enabled && 167 this.curatedProperties !== undefined 168 ); 169 } 170 171 showPropertiesTree(): boolean { 172 return !!this.propertiesTree && !this.showViewCaptureFormat(); 173 } 174} 175