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 {assertDefined} from 'common/assert_utils';
25import {SfCuratedProperties} from 'viewers/common/curated_properties';
26import {UiPropertyTreeNode} from 'viewers/common/ui_property_tree_node';
27import {ViewerEvents} from 'viewers/common/viewer_events';
28import {inlineButtonStyle} from './styles/clickable_property.styles';
29import {viewerCardInnerStyle} from './styles/viewer_card.styles';
30
31@Component({
32  selector: 'surface-flinger-property-groups',
33  template: `
34    <div class="title-section">
35      <collapsible-section-title
36        title="PROPERTIES"
37        (collapseButtonClicked)="collapseButtonClicked.emit()"></collapsible-section-title>
38    </div>
39
40    <span class="mat-body-1 placeholder-text" *ngIf="!properties"> Layer not selected. </span>
41
42    <div class="property-groups-content" *ngIf="properties">
43      <div class="group">
44        <h3 class="group-header mat-subheading-2">Visibility</h3>
45        <div class="left-column">
46          <p class="mat-body-2 flags">
47            <span class="mat-body-1">Flags:</span>
48            &ngsp;
49            {{ properties.flags }}
50          </p>
51          <span *ngFor="let summaryProperty of properties.summary" class="mat-body-2 summary inline">
52            <span class="mat-body-1" [matTooltip]="summaryProperty.desc" [matTooltipShowDelay]="400">{{ summaryProperty.key }}:</span>
53            <ng-container *ngIf="summaryProperty.simpleValue">
54              {{ summaryProperty.simpleValue }}
55            </ng-container>
56            <ng-container *ngIf="summaryProperty.layerValues">
57              &ngsp;
58              <ng-container *ngFor="let layer of summaryProperty.layerValues; index as i">
59                <button
60                  mat-button
61                  color="primary"
62                  [matTooltip]="layer.name"
63                  (click)="onIdClicked(layer.nodeId)">
64                  {{ layer.layerId }}
65                </button>
66                {{(i === summaryProperty.layerValues.length - 1) ? '' : ', '}}
67              </ng-container>
68            </ng-container>
69          </span>
70        </div>
71      </div>
72
73      <mat-divider></mat-divider>
74
75      <div class="group geometry">
76        <h3 class="group-header mat-subheading-2">Geometry</h3>
77        <div class="left-column">
78          <p class="column-header mat-small">Calculated</p>
79          <p class="property mat-body-1">Transform:</p>
80          <transform-matrix
81            *ngIf="properties.calcTransform?.getAllChildren().length > 0"
82            [matTooltip]="getTransformType(properties.calcTransform)"
83            [matrix]="getTransformMatrix(properties.calcTransform)"></transform-matrix>
84          <p class="mat-body-2 crop">
85            <span
86              class="mat-body-1"
87              matTooltip="Raw value read from proto.bounds. This is the buffer size or
88                  requested crop cropped by parent bounds."
89              >Crop:</span
90            >
91            &ngsp;
92            {{ properties.calcCrop }}
93          </p>
94
95          <p class="mat-body-2 final-bounds">
96            <span
97              class="mat-body-1"
98              matTooltip="Raw value read from proto.screenBounds. This is the calculated crop
99                  transformed."
100              >Final Bounds:</span
101            >
102            &ngsp;
103            {{ properties.finalBounds }}
104          </p>
105        </div>
106        <div class="right-column">
107          <p class="column-header mat-small">Requested</p>
108          <p class="property mat-body-1">Transform:</p>
109          <transform-matrix
110            *ngIf="properties.reqTransform?.getAllChildren().length > 0"
111            [matTooltip]="getTransformType(properties.reqTransform)"
112            [matrix]="getTransformMatrix(properties.reqTransform)"></transform-matrix>
113          <p class="mat-body-2 crop">
114            <span class="mat-body-1">Crop:</span>
115            &ngsp;
116            {{ properties.reqCrop }}
117          </p>
118        </div>
119      </div>
120
121      <mat-divider></mat-divider>
122
123      <div class="group buffer">
124        <h3 class="group-header mat-subheading-2">Buffer</h3>
125        <div class="left-column">
126          <p class="mat-body-2 size">
127            <span class="mat-body-1">Size:</span>
128            &ngsp;
129            {{ properties.bufferSize }}
130          </p>
131          <p class="mat-body-2 frame-number">
132            <span class="mat-body-1">Frame Number:</span>
133            &ngsp;
134            {{ properties.frameNumber }}
135          </p>
136          <p class="mat-body-2 transform">
137            <span
138              class="mat-body-1"
139              matTooltip="Rotates or flips the buffer in place. Used with display transform
140                  hint to cancel out any buffer transformation when sending to
141                  HWC."
142              >Transform:</span
143            >
144            &ngsp;
145            {{ properties.bufferTransformType }}
146          </p>
147        </div>
148        <div class="right-column">
149          <p class="mat-body-2 dest-frame">
150            <span
151              class="mat-body-1"
152              matTooltip="Scales buffer to the frame by overriding the requested transform
153                  for this item."
154              >Destination Frame:</span
155            >
156            &ngsp;
157            {{ properties.destinationFrame }}
158          </p>
159          <p *ngIf="properties.ignoreDestinationFrame" class="mat-body-2 ignore-frame">
160            Destination Frame ignored because item has eIgnoreDestinationFrame flag set.
161          </p>
162        </div>
163      </div>
164
165      <mat-divider></mat-divider>
166
167      <div class="group hierarchy-info">
168        <h3 class="group-header mat-subheading-2">Hierarchy</h3>
169        <div class="left-column">
170          <p class="mat-body-2 z-order">
171            <span class="mat-body-1">z-order:</span>
172            &ngsp;
173            {{ properties.z }}
174          </p>
175          <p class="mat-body-2 rel-parent inline">
176            <span
177              class="mat-body-1"
178              matTooltip="item is z-ordered relative to its relative parents but its bounds
179                  and other properties are inherited from its parents."
180              >relative parent:</span>
181            &ngsp;
182            <ng-container *ngIf="!properties.relativeParent.nodeId">
183              {{ properties.relativeParent }}
184            </ng-container>
185            <ng-container *ngIf="properties.relativeParent.nodeId">
186              <button
187                mat-button
188                color="primary"
189                [matTooltip]="properties.relativeParent.name"
190                (click)="onIdClicked(properties.relativeParent.nodeId)">
191                {{ properties.relativeParent.layerId }}
192              </button>
193            </ng-container>
194          </p>
195          <span class="mat-body-2 rel-children inline">
196            <span class="mat-body-1">relative children:</span>
197            &ngsp;
198            <ng-container *ngIf="properties.relativeChildren.length === 0">
199              none
200            </ng-container>
201            <ng-container *ngFor="let layer of properties.relativeChildren; index as i">
202              <button
203                mat-button
204                color="primary"
205                [matTooltip]="layer.name"
206                (click)="onIdClicked(layer.nodeId)">
207                {{ layer.layerId }}
208              </button>
209              {{(i === properties.relativeChildren.length - 1) ? '' : ', '}}
210            </ng-container>
211          </span>
212        </div>
213      </div>
214
215      <mat-divider></mat-divider>
216
217      <div class="group effects">
218        <h3 class="group-header mat-subheading-2">Effects</h3>
219        <div class="left-column">
220          <p class="column-header mat-small">Calculated</p>
221          <p class="mat-body-2 color">
222            <span class="mat-body-1">Color:</span>
223            &ngsp;
224            {{ properties.calcColor }}
225          </p>
226          <p class="mat-body-2 corner-radius">
227            <span class="mat-body-1">Corner Radius:</span>
228            &ngsp;
229            {{ properties.calcCornerRadius }}
230          </p>
231          <p class="mat-body-2 shadow">
232            <span class="mat-body-1">Shadow:</span>
233            &ngsp;
234            {{ properties.calcShadowRadius }}
235          </p>
236          <p class="mat-body-2">
237            <span
238              class="mat-body-1"
239              matTooltip="Crop used to define the bounds of the corner radii. If the bounds
240                  are greater than the item bounds then the rounded corner will not
241                  be visible."
242              >Corner Radius Crop:</span
243            >
244            &ngsp;
245            {{ properties.calcCornerRadiusCrop }}
246          </p>
247          <p class="mat-body-2 blur">
248            <span class="mat-body-1">Blur:</span>
249            &ngsp;
250            {{ properties.backgroundBlurRadius }}
251          </p>
252        </div>
253        <div class="right-column">
254          <p class="column-header mat-small">Requested</p>
255          <p class="mat-body-2">
256            <span class="mat-body-1">Color:</span>
257            &ngsp;
258            {{ properties.reqColor }}
259          </p>
260          <p class="mat-body-2 corner-radius">
261            <span class="mat-body-1">Corner Radius:</span>
262            &ngsp;
263            {{ properties.reqCornerRadius }}
264          </p>
265        </div>
266      </div>
267
268      <mat-divider></mat-divider>
269
270      <div class="group inputs">
271        <h3 class="group-header mat-subheading-2">Input</h3>
272        <ng-container *ngIf="properties.hasInputChannel">
273          <div class="left-column">
274            <p class="property mat-body-1">To Display Transform:</p>
275            <transform-matrix
276              *ngIf="properties.inputTransform?.getAllChildren().length > 0"
277              [matTooltip]="getTransformType(properties.inputTransform)"
278              [matrix]="getTransformMatrix(properties.inputTransform)"></transform-matrix>
279            <p class="mat-body-2">
280              <span class="mat-body-1">Touchable Region:</span>
281              &ngsp;
282              {{ properties.inputRegion }}
283            </p>
284          </div>
285          <div class="right-column">
286            <p class="column-header mat-small">Config</p>
287            <p class="mat-body-2 focusable">
288              <span class="mat-body-1">Focusable:</span>
289              &ngsp;
290              {{ properties.focusable }}
291            </p>
292            <p class="mat-body-2 crop-touch-region">
293              <span class="mat-body-1">Crop touch region with item:</span>
294              &ngsp;
295              {{ properties.cropTouchRegionWithItem }}
296            </p>
297            <p class="mat-body-2 replace-touch-region">
298              <span class="mat-body-1">Replace touch region with crop:</span>
299              &ngsp;
300              {{ properties.replaceTouchRegionWithCrop }}
301            </p>
302            <p class="mat-body-2 input-config">
303              <span class="mat-body-1">Input Config:</span>
304              &ngsp;
305              {{ properties.inputConfig }}
306            </p>
307          </div>
308        </ng-container>
309        <div *ngIf="!properties.hasInputChannel" class="left-column">
310          <p class="mat-body-2">
311            <span class="mat-body-1">Input channel:</span>
312            &ngsp; not set
313          </p>
314        </div>
315      </div>
316    </div>
317  `,
318  styles: [
319    `
320      :host collapsible-section-title {
321        padding-bottom: 8px;
322      }
323      .placeholder-text {
324        padding: 8px 12px;
325      }
326
327      .property-groups-content {
328        overflow-y: auto;
329        padding: 0px 12px;
330      }
331
332      .group {
333        display: flex;
334        flex-direction: row;
335        padding: 8px;
336      }
337
338      .group-header {
339        width: 80px;
340        color: gray;
341      }
342
343      .left-column {
344        flex: 1;
345        padding: 0 5px;
346      }
347
348      .right-column {
349        flex: 1;
350        border: 1px solid var(--border-color);
351        border-left-width: 5px;
352        padding: 0 5px;
353      }
354
355      .column-header {
356        color: gray;
357      }
358
359      .summary {
360        display: block;
361      }
362    `,
363    inlineButtonStyle,
364    viewerCardInnerStyle,
365  ],
366})
367export class SurfaceFlingerPropertyGroupsComponent {
368  @Input() properties: SfCuratedProperties | undefined;
369
370  @Output() collapseButtonClicked = new EventEmitter();
371
372  constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
373
374  getTransformType(transformNode: UiPropertyTreeNode): string {
375    const typeFlags = transformNode.formattedValue();
376    return typeFlags !== 'null' ? typeFlags : 'IDENTITY';
377  }
378
379  getTransformMatrix(transformNode: UiPropertyTreeNode): UiPropertyTreeNode {
380    return assertDefined(transformNode.getChildByName('matrix'));
381  }
382
383  onIdClicked(layerNodeId: string) {
384    const event = new CustomEvent(ViewerEvents.HighlightedIdChange, {
385      bubbles: true,
386      detail: {id: layerNodeId},
387    });
388    this.elementRef.nativeElement.dispatchEvent(event);
389  }
390}
391