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-1 flags">
47            <span class="mat-body-2">Flags:</span>
48            &ngsp;
49            {{ properties.flags }}
50          </p>
51          <span *ngFor="let summaryProperty of properties.summary" class="mat-body-1 summary inline">
52            <span class="mat-body-2">{{ 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-2">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-1 crop">
85            <span
86              class="mat-body-2"
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-1 final-bounds">
96            <span
97              class="mat-body-2"
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-2">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-1 crop">
114            <span class="mat-body-2">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-1 size">
127            <span class="mat-body-2">Size:</span>
128            &ngsp;
129            {{ properties.bufferSize }}
130          </p>
131          <p class="mat-body-1 frame-number">
132            <span class="mat-body-2">Frame Number:</span>
133            &ngsp;
134            {{ properties.frameNumber }}
135          </p>
136          <p class="mat-body-1 transform">
137            <span
138              class="mat-body-2"
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-1 dest-frame">
150            <span
151              class="mat-body-2"
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-1 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-1 z-order">
171            <span class="mat-body-2">z-order:</span>
172            &ngsp;
173            {{ properties.z }}
174          </p>
175          <p class="mat-body-1 rel-parent">
176            <span
177              class="mat-body-2"
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            >
182            &ngsp;
183            {{ properties.relativeParent }}
184          </p>
185        </div>
186      </div>
187
188      <mat-divider></mat-divider>
189
190      <div class="group effects">
191        <h3 class="group-header mat-subheading-2">Effects</h3>
192        <div class="left-column">
193          <p class="column-header mat-small">Calculated</p>
194          <p class="mat-body-1 color">
195            <span class="mat-body-2">Color:</span>
196            &ngsp;
197            {{ properties.calcColor }}
198          </p>
199          <p class="mat-body-1 corner-radius">
200            <span class="mat-body-2">Corner Radius:</span>
201            &ngsp;
202            {{ properties.calcCornerRadius }}
203          </p>
204          <p class="mat-body-1 shadow">
205            <span class="mat-body-2">Shadow:</span>
206            &ngsp;
207            {{ properties.calcShadowRadius }}
208          </p>
209          <p class="mat-body-1">
210            <span
211              class="mat-body-2"
212              matTooltip="Crop used to define the bounds of the corner radii. If the bounds
213                  are greater than the item bounds then the rounded corner will not
214                  be visible."
215              >Corner Radius Crop:</span
216            >
217            &ngsp;
218            {{ properties.calcCornerRadiusCrop }}
219          </p>
220          <p class="mat-body-1 blur">
221            <span class="mat-body-2">Blur:</span>
222            &ngsp;
223            {{ properties.backgroundBlurRadius }}
224          </p>
225        </div>
226        <div class="right-column">
227          <p class="column-header mat-small">Requested</p>
228          <p class="mat-body-1">
229            <span class="mat-body-2">Color:</span>
230            &ngsp;
231            {{ properties.reqColor }}
232          </p>
233          <p class="mat-body-1 corner-radius">
234            <span class="mat-body-2">Corner Radius:</span>
235            &ngsp;
236            {{ properties.reqCornerRadius }}
237          </p>
238        </div>
239      </div>
240
241      <mat-divider></mat-divider>
242
243      <div class="group inputs">
244        <h3 class="group-header mat-subheading-2">Input</h3>
245        <ng-container *ngIf="properties.hasInputChannel">
246          <div class="left-column">
247            <p class="property mat-body-2">To Display Transform:</p>
248            <transform-matrix
249              *ngIf="properties.inputTransform?.getAllChildren().length > 0"
250              [matTooltip]="getTransformType(properties.inputTransform)"
251              [matrix]="getTransformMatrix(properties.inputTransform)"></transform-matrix>
252            <p class="mat-body-1">
253              <span class="mat-body-2">Touchable Region:</span>
254              &ngsp;
255              {{ properties.inputRegion }}
256            </p>
257          </div>
258          <div class="right-column">
259            <p class="column-header mat-small">Config</p>
260            <p class="mat-body-1 focusable">
261              <span class="mat-body-2">Focusable:</span>
262              &ngsp;
263              {{ properties.focusable }}
264            </p>
265            <p class="mat-body-1 crop-touch-region">
266              <span class="mat-body-2">Crop touch region with item:</span>
267              &ngsp;
268              {{ properties.cropTouchRegionWithItem }}
269            </p>
270            <p class="mat-body-1 replace-touch-region">
271              <span class="mat-body-2">Replace touch region with crop:</span>
272              &ngsp;
273              {{ properties.replaceTouchRegionWithCrop }}
274            </p>
275            <p class="mat-body-1 input-config">
276              <span class="mat-body-2">Input Config:</span>
277              &ngsp;
278              {{ properties.inputConfig }}
279            </p>
280          </div>
281        </ng-container>
282        <div *ngIf="!properties.hasInputChannel" class="left-column">
283          <p class="mat-body-1">
284            <span class="mat-body-2">Input channel:</span>
285            &ngsp; not set
286          </p>
287        </div>
288      </div>
289    </div>
290  `,
291  styles: [
292    `
293      :host collapsible-section-title {
294        padding-bottom: 8px;
295      }
296      .placeholder-text {
297        padding: 8px 12px;
298      }
299
300      .property-groups-content {
301        overflow-y: auto;
302        padding: 0px 12px;
303      }
304
305      .group {
306        display: flex;
307        flex-direction: row;
308        padding: 8px;
309      }
310
311      .group-header {
312        width: 80px;
313        color: gray;
314      }
315
316      .left-column {
317        flex: 1;
318        padding: 0 5px;
319      }
320
321      .right-column {
322        flex: 1;
323        border: 1px solid var(--border-color);
324        border-left-width: 5px;
325        padding: 0 5px;
326      }
327
328      .column-header {
329        color: gray;
330      }
331
332      .summary {
333        display: block;
334      }
335    `,
336    inlineButtonStyle,
337    viewerCardInnerStyle,
338  ],
339})
340export class SurfaceFlingerPropertyGroupsComponent {
341  @Input() properties: SfCuratedProperties | undefined;
342
343  @Output() collapseButtonClicked = new EventEmitter();
344
345  constructor(@Inject(ElementRef) private elementRef: ElementRef) {}
346
347  getTransformType(transformNode: UiPropertyTreeNode): string {
348    const typeFlags = transformNode.formattedValue();
349    return typeFlags !== 'null' ? typeFlags : 'IDENTITY';
350  }
351
352  getTransformMatrix(transformNode: UiPropertyTreeNode): UiPropertyTreeNode {
353    return assertDefined(transformNode.getChildByName('matrix'));
354  }
355
356  onIdClicked(layerNodeId: string) {
357    const event = new CustomEvent(ViewerEvents.HighlightedIdChange, {
358      bubbles: true,
359      detail: {id: layerNodeId},
360    });
361    this.elementRef.nativeElement.dispatchEvent(event);
362  }
363}
364