• 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 */
16import {Component, ElementRef, Inject, Input} from '@angular/core';
17import {PersistentStore} from 'common/persistent_store';
18import {TraceType} from 'trace/trace_type';
19import {TableProperties} from 'viewers/common/table_properties';
20import {HierarchyTreeNode, UiTreeNode, UiTreeUtils} from 'viewers/common/ui_tree_utils';
21import {UserOptions} from 'viewers/common/user_options';
22import {ViewerEvents} from 'viewers/common/viewer_events';
23import {nodeStyles} from 'viewers/components/styles/node.styles';
24
25@Component({
26  selector: 'hierarchy-view',
27  template: `
28    <div class="view-header">
29      <div class="title-filter">
30        <h2 class="hierarchy-title mat-title">Hierarchy</h2>
31        <mat-form-field>
32          <mat-label>Filter...</mat-label>
33          <input matInput [(ngModel)]="filterString" (ngModelChange)="filterTree()" name="filter" />
34        </mat-form-field>
35      </div>
36      <div class="view-controls">
37        <mat-checkbox
38          *ngFor="let option of objectKeys(userOptions)"
39          color="primary"
40          [(ngModel)]="userOptions[option].enabled"
41          (ngModelChange)="updateTree()"
42          >{{ userOptions[option].name }}</mat-checkbox
43        >
44      </div>
45      <properties-table
46        *ngIf="tableProperties"
47        class="properties-table"
48        [properties]="tableProperties"></properties-table>
49      <div *ngIf="pinnedItems.length > 0" class="pinned-items">
50        <tree-node
51          *ngFor="let pinnedItem of pinnedItems"
52          class="node"
53          [class]="diffClass(pinnedItem)"
54          [class.selected]="isHighlighted(pinnedItem, highlightedItems)"
55          [class.clickable]="true"
56          [item]="pinnedItem"
57          [isPinned]="true"
58          [isInPinnedSection]="true"
59          (pinNodeChange)="pinnedItemChange($event)"
60          (click)="onPinnedNodeClick($event, pinnedItem)"></tree-node>
61      </div>
62    </div>
63    <mat-divider></mat-divider>
64    <div class="hierarchy-content">
65      <tree-view
66        *ngIf="tree"
67        [isFlattened]="isFlattened()"
68        [item]="tree"
69        [dependencies]="dependencies"
70        [store]="store"
71        [useGlobalCollapsedState]="true"
72        [itemsClickable]="true"
73        [highlightedItems]="highlightedItems"
74        [pinnedItems]="pinnedItems"
75        (highlightedItemChange)="highlightedItemChange($event)"
76        (pinnedItemChange)="pinnedItemChange($event)"
77        (selectedTreeChange)="selectedTreeChange($event)"></tree-view>
78    </div>
79  `,
80  styles: [
81    `
82      .view-header {
83        display: flex;
84        flex-direction: column;
85        margin-bottom: 12px;
86      }
87
88      .title-filter {
89        display: flex;
90        flex-direction: row;
91        flex-wrap: wrap;
92        justify-content: space-between;
93      }
94
95      .view-controls {
96        display: flex;
97        flex-direction: row;
98        flex-wrap: wrap;
99        column-gap: 10px;
100      }
101
102      .properties-table {
103        padding-top: 5px;
104      }
105
106      .hierarchy-content {
107        height: 100%;
108        overflow: auto;
109      }
110
111      .pinned-items {
112        width: 100%;
113        box-sizing: border-box;
114        border: 2px solid yellow;
115      }
116
117      tree-view {
118        overflow: auto;
119      }
120    `,
121    nodeStyles,
122  ],
123})
124export class HierarchyComponent {
125  objectKeys = Object.keys;
126  filterString = '';
127  diffClass = UiTreeUtils.diffClass;
128  isHighlighted = UiTreeUtils.isHighlighted;
129
130  @Input() tree!: HierarchyTreeNode | null;
131  @Input() tableProperties?: TableProperties | null;
132  @Input() dependencies: TraceType[] = [];
133  @Input() highlightedItems: string[] = [];
134  @Input() pinnedItems: HierarchyTreeNode[] = [];
135  @Input() store!: PersistentStore;
136  @Input() userOptions: UserOptions = {};
137
138  constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
139
140  isFlattened() {
141    return this.userOptions['flat']?.enabled;
142  }
143
144  onPinnedNodeClick(event: MouseEvent, pinnedItem: HierarchyTreeNode) {
145    event.preventDefault();
146    if (window.getSelection()?.type === 'range') {
147      return;
148    }
149    if (pinnedItem.id) this.highlightedItemChange(`${pinnedItem.id}`);
150    this.selectedTreeChange(pinnedItem);
151  }
152
153  updateTree() {
154    const event: CustomEvent = new CustomEvent(ViewerEvents.HierarchyUserOptionsChange, {
155      bubbles: true,
156      detail: {userOptions: this.userOptions},
157    });
158    this.elementRef.nativeElement.dispatchEvent(event);
159  }
160
161  filterTree() {
162    const event: CustomEvent = new CustomEvent(ViewerEvents.HierarchyFilterChange, {
163      bubbles: true,
164      detail: {filterString: this.filterString},
165    });
166    this.elementRef.nativeElement.dispatchEvent(event);
167  }
168
169  highlightedItemChange(newId: string) {
170    const event: CustomEvent = new CustomEvent(ViewerEvents.HighlightedChange, {
171      bubbles: true,
172      detail: {id: newId},
173    });
174    this.elementRef.nativeElement.dispatchEvent(event);
175  }
176
177  selectedTreeChange(item: UiTreeNode) {
178    if (!(item instanceof HierarchyTreeNode)) {
179      return;
180    }
181    const event: CustomEvent = new CustomEvent(ViewerEvents.SelectedTreeChange, {
182      bubbles: true,
183      detail: {selectedItem: item},
184    });
185    this.elementRef.nativeElement.dispatchEvent(event);
186  }
187
188  pinnedItemChange(item: UiTreeNode) {
189    if (!(item instanceof HierarchyTreeNode)) {
190      return;
191    }
192    const event: CustomEvent = new CustomEvent(ViewerEvents.HierarchyPinnedChange, {
193      bubbles: true,
194      detail: {pinnedItem: item},
195    });
196    this.elementRef.nativeElement.dispatchEvent(event);
197  }
198}
199