xref: /aosp_15_r20/development/tools/winscope/src/viewers/components/properties_component.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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  ViewChild,
24} from '@angular/core';
25import {PersistentStore} from 'common/persistent_store';
26import {Analytics} from 'logging/analytics';
27import {TraceType} from 'trace/trace_type';
28import {CollapsibleSectionType} from 'viewers/common/collapsible_section_type';
29import {CuratedProperties} from 'viewers/common/curated_properties';
30import {TextFilter} from 'viewers/common/text_filter';
31import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
32import {UserOptions} from 'viewers/common/user_options';
33import {ViewerEvents} from 'viewers/common/viewer_events';
34import {nodeStyles} from 'viewers/components/styles/node.styles';
35import {SearchBoxComponent} from './search_box_component';
36import {viewerCardInnerStyle} from './styles/viewer_card.styles';
37
38@Component({
39  selector: 'properties-view',
40  template: `
41    <div class="view-header">
42      <div class="title-section">
43       <collapsible-section-title
44          class="properties-title"
45          [class.padded-title]="!hasUserOptions()"
46          [title]="title"
47          (collapseButtonClicked)="collapseButtonClicked.emit()"></collapsible-section-title>
48        <search-box
49          formFieldClass="applied-field"
50          [textFilter]="textFilter"
51          (filterChange)="onFilterChange($event)"></search-box>
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="showPlaceholderText()"> {{ placeholderText }} </span>
86  `,
87  styles: [
88    `
89      .view-header {
90        display: flex;
91        flex-direction: column;
92      }
93
94      .property-groups {
95        overflow-y: auto;
96      }
97
98      .properties-content {
99        flex: 1;
100        display: flex;
101        flex-direction: column;
102        overflow-y: auto;
103        padding: 0px 12px;
104      }
105    `,
106    nodeStyles,
107    viewerCardInnerStyle,
108  ],
109})
110export class PropertiesComponent {
111  Analytics = Analytics;
112  CollapsibleSectionType = CollapsibleSectionType;
113  ViewerEvents = ViewerEvents;
114
115  @Input() title = 'PROPERTIES';
116  @Input() userOptions: UserOptions = {};
117  @Input() placeholderText = '';
118  @Input() propertiesTree: UiPropertyTreeNode | undefined;
119  @Input() highlightedProperty = '';
120  @Input() curatedProperties: CuratedProperties | undefined;
121  @Input() isProtoDump = false;
122  @Input() traceType: TraceType | undefined;
123  @Input() store: PersistentStore | undefined;
124  @Input() textFilter: TextFilter | undefined;
125  @Input() filterEventName = ViewerEvents.PropertiesFilterChange;
126
127  @Output() collapseButtonClicked = new EventEmitter();
128
129  @ViewChild(SearchBoxComponent) searchBox: SearchBoxComponent | undefined;
130
131  constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
132
133  onFilterChange(detail: TextFilter) {
134    const event = new CustomEvent(this.filterEventName, {
135      bubbles: true,
136      detail,
137    });
138    this.elementRef.nativeElement.dispatchEvent(event);
139  }
140
141  onHighlightedPropertyChange(newNode: UiPropertyTreeNode) {
142    const event = new CustomEvent(ViewerEvents.HighlightedPropertyChange, {
143      bubbles: true,
144      detail: {id: newNode.id},
145    });
146    this.elementRef.nativeElement.dispatchEvent(event);
147  }
148
149  hasUserOptions() {
150    return Object.keys(this.userOptions).length > 0;
151  }
152
153  showViewCaptureFormat(): boolean {
154    return (
155      this.traceType === TraceType.VIEW_CAPTURE &&
156      this.textFilter?.filterString === '' &&
157      // Todo: Highlight Inline in formatted ViewCapture Properties Component.
158      !this.userOptions['showDiff']?.enabled &&
159      this.curatedProperties !== undefined
160    );
161  }
162
163  showPropertiesTree(): boolean {
164    return !!this.propertiesTree && !this.showViewCaptureFormat();
165  }
166
167  showPlaceholderText(): boolean {
168    return (
169      !this.propertiesTree && !this.curatedProperties && !!this.placeholderText
170    );
171  }
172}
173