1">
@@ -92,8 +92,8 @@ import { ViewerEvents } from "viewers/common/viewer_events";
"@import 'https://fonts.googleapis.com/icon?family=Material+Icons';",
".rects-content {position: relative}",
".canvas-container {height: 40rem; width: 100%; position: relative}",
- "#rects-canvas {height: 40rem; width: 100%; cursor: pointer; position: absolute; top: 0px}",
- "#labels-canvas {height: 40rem; width: 100%; position: absolute; top: 0px}",
+ ".rects-canvas {height: 40rem; width: 100%; cursor: pointer; position: absolute; top: 0px}",
+ ".labels-canvas {height: 40rem; width: 100%; position: absolute; top: 0px}",
".view-controls {display: inline-block; position: relative; min-height: 4rem; width: 100%;}",
".slider-view-controls {display: inline-block; position: relative; height: 3rem; width: 100%;}",
".slider {display: inline-block}",
@@ -131,10 +131,15 @@ export class RectsComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnInit() {
- window.addEventListener('resize', () => this.refreshCanvas());
+ window.addEventListener("resize", () => this.refreshCanvas());
}
ngOnChanges(changes: SimpleChanges) {
+ if (changes["displayIds"]) {
+ if (!this.displayIds.includes(this.currentDisplayId)) {
+ this.currentDisplayId = this.displayIds[0];
+ }
+ }
if (changes["highlightedItems"]) {
this.canvasGraphics.updateHighlightedItems(this.highlightedItems);
}
@@ -157,7 +162,7 @@ export class RectsComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnDestroy() {
- window.removeEventListener('resize', () => this.refreshCanvas());
+ window.removeEventListener("resize", () => this.refreshCanvas());
}
onRectClick(event:MouseEvent) {
@@ -193,7 +198,7 @@ export class RectsComponent implements OnInit, OnChanges, OnDestroy {
}
drawRects() {
- const canvas = document.getElementById("rects-canvas") as HTMLCanvasElement;
+ const canvas = this.elementRef.nativeElement.querySelector(".rects-canvas") as HTMLCanvasElement;
this.canvasGraphics.initialise(canvas);
this.refreshCanvas();
}
diff --git a/tools/winscope-ng/src/viewers/styles/node.styles.ts b/tools/winscope-ng/src/viewers/components/styles/node.styles.ts
similarity index 87%
rename from tools/winscope-ng/src/viewers/styles/node.styles.ts
rename to tools/winscope-ng/src/viewers/components/styles/node.styles.ts
index b74b5dd02..0f03eaf20 100644
--- a/tools/winscope-ng/src/viewers/styles/node.styles.ts
+++ b/tools/winscope-ng/src/viewers/components/styles/node.styles.ts
@@ -17,23 +17,18 @@ export const nodeStyles = `
.node {position: relative;display: inline-block;padding: 2px; height: 100%; width: 100%;}
.node.clickable {cursor: pointer;}
.node:not(.selected).added,
- .node:not(.selected).addedMove,
- .expand-tree-btn.added,
- .expand-tree-btn.addedMove {
+ .node:not(.selected).addedMove {
background: #03ff35;
}
.node:not(.selected).deleted,
- .node:not(.selected).deletedMove,
- .expand-tree-btn.deleted,
- .expand-tree-btn.deletedMove {
+ .node:not(.selected).deletedMove {
background: #ff6b6b;
}
.node:hover:not(.selected) {background: #f1f1f1;}
- .node:not(.selected).modified,
- .expand-tree-btn.modified {
+ .node:not(.selected).modified {
background: cyan;
}
@@ -50,7 +45,7 @@ export const nodeStyles = `
.selected {background-color: #365179;color: white;}
`;
-export const treeNodeStyles = `
+export const treeNodeDataViewStyles = `
.node.shaded:not(:hover):not(.selected):not(.added):not(.addedMove):not(.deleted):not(.deletedMove):not(.modified) {background: #f8f9fa}
.node.selected + .children {border-left: 1px solid rgb(200, 200, 200);}
.node.child-hover + .children {border-left: 1px solid #b4b4b4;}
@@ -65,4 +60,24 @@ export const nodeInnerItemStyles = `
.description {align-items: center; flex: 1 1 auto; vertical-align: middle; word-break: break-all;}
.leaf-node-icon-wrapper{padding-left: 6px; padding-right: 6px; min-height: 24px; width: 24px; position:relative; align-content: center; vertical-align: middle;}
.icon-button { background: none;border: none;display: inline-block;vertical-align: middle;}
+
+ .expand-tree-btn {
+ float: right;
+ padding-left: 0px;
+ padding-right: 0px;
+ }
+
+ .expand-tree-btn.modified {
+ background: cyan;
+ }
+
+ .expand-tree-btn.deleted,
+ .expand-tree-btn.deletedMove {
+ background: #ff6b6b;
+ }
+
+ .expand-tree-btn.added,
+ .expand-tree-btn.addedMove {
+ background: #03ff35;
+ }
`;
\ No newline at end of file
diff --git a/tools/winscope-ng/src/viewers/styles/tree_element.styles.ts b/tools/winscope-ng/src/viewers/components/styles/tree_node_data_view.styles.ts
similarity index 78%
rename from tools/winscope-ng/src/viewers/styles/tree_element.styles.ts
rename to tools/winscope-ng/src/viewers/components/styles/tree_node_data_view.styles.ts
index 8b1cc61f8..cea15e028 100644
--- a/tools/winscope-ng/src/viewers/styles/tree_element.styles.ts
+++ b/tools/winscope-ng/src/viewers/components/styles/tree_node_data_view.styles.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-export const treeElementStyles = `
+export const treeNodeDataViewStyles = `
.kind {font-weight: bold}
span {overflow-wrap: break-word; flex: 1 1 auto; width: 0; word-break: break-all}
@@ -49,3 +49,24 @@ export const treeElementStyles = `
color: black;
}
`;
+
+export const treeNodePropertiesDataViewStyles = `
+ .key {
+ color: #4b4b4b;
+ }
+ .value {
+ color: #8A2BE2;
+ }
+ .value.null {
+ color: #e1e1e1;
+ }
+ .value.number {
+ color: #4c75fd;
+ }
+ .value.true {
+ color: #2ECC40;
+ }
+ .value.false {
+ color: #FF4136;
+ }
+`;
\ No newline at end of file
diff --git a/tools/winscope-ng/src/viewers/components/tree_element.component.spec.ts b/tools/winscope-ng/src/viewers/components/transform_matrix.component.spec.ts
similarity index 80%
rename from tools/winscope-ng/src/viewers/components/tree_element.component.spec.ts
rename to tools/winscope-ng/src/viewers/components/transform_matrix.component.spec.ts
index 8322e8929..45bfe61ac 100644
--- a/tools/winscope-ng/src/viewers/components/tree_element.component.spec.ts
+++ b/tools/winscope-ng/src/viewers/components/transform_matrix.component.spec.ts
@@ -14,13 +14,13 @@
* limitations under the License.
*/
import {ComponentFixture, TestBed} from "@angular/core/testing";
-import { TreeElementComponent } from "./tree_element.component";
+import { TransformMatrixComponent } from "./transform_matrix.component";
import { ComponentFixtureAutoDetect } from "@angular/core/testing";
import { NO_ERRORS_SCHEMA } from "@angular/core";
-describe("TreeElementComponent", () => {
- let fixture: ComponentFixture
;
- let component: TreeElementComponent;
+describe("TransformMatrixComponent", () => {
+ let fixture: ComponentFixture;
+ let component: TransformMatrixComponent;
let htmlElement: HTMLElement;
beforeAll(async () => {
@@ -29,14 +29,14 @@ describe("TreeElementComponent", () => {
{ provide: ComponentFixtureAutoDetect, useValue: true }
],
declarations: [
- TreeElementComponent
+ TransformMatrixComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(TreeElementComponent);
+ fixture = TestBed.createComponent(TransformMatrixComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
});
diff --git a/tools/winscope-ng/src/viewers/components/transform_matrix.component.ts b/tools/winscope-ng/src/viewers/components/transform_matrix.component.ts
new file mode 100644
index 000000000..015fc27ee
--- /dev/null
+++ b/tools/winscope-ng/src/viewers/components/transform_matrix.component.ts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, Input } from "@angular/core";
+import { Transform } from "common/trace/flickerlib/common";
+
+@Component({
+ selector: "transform-matrix",
+ template: `
+
+
{{ formatFloat(transform.matrix.dsdx) }}
+
{{ formatFloat(transform.matrix.dsdy) }}
+
+ {{ formatFloat(transform.matrix.tx) }}
+
+
+
{{ formatFloat(transform.matrix.dtdx) }}
+
{{ formatFloat(transform.matrix.dtdy) }}
+
+ {{ formatFloat(transform.matrix.ty) }}
+
+
+
0
+
0
+
1
+
+ `,
+ styles: [
+ `
+ .matrix {
+ display: grid;
+ grid-gap: 1px;
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .cell {
+ padding-left: 10px;
+ background-color: #F8F9FA;
+ }
+ `
+ ],
+})
+
+export class TransformMatrixComponent {
+ @Input() transform!: Transform;
+ @Input() formatFloat!: (num: number) => number;
+}
diff --git a/tools/winscope-ng/src/viewers/components/tree.component.spec.ts b/tools/winscope-ng/src/viewers/components/tree.component.spec.ts
index 4732dc629..22f62b7fc 100644
--- a/tools/winscope-ng/src/viewers/components/tree.component.spec.ts
+++ b/tools/winscope-ng/src/viewers/components/tree.component.spec.ts
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {ComponentFixture, TestBed} from "@angular/core/testing";
+import { ComponentFixture, TestBed, ComponentFixtureAutoDetect } from "@angular/core/testing";
import { TreeComponent } from "./tree.component";
-import { ComponentFixtureAutoDetect } from "@angular/core/testing";
-import { NO_ERRORS_SCHEMA } from "@angular/core";
+import { Component, ViewChild, NO_ERRORS_SCHEMA } from "@angular/core";
+import { PersistentStore } from "common/persistent_store";
describe("TreeComponent", () => {
- let fixture: ComponentFixture;
- let component: TreeComponent;
+ let fixture: ComponentFixture;
+ let component: TestHostComponent;
let htmlElement: HTMLElement;
beforeAll(async () => {
@@ -29,28 +29,16 @@ describe("TreeComponent", () => {
{ provide: ComponentFixtureAutoDetect, useValue: true }
],
declarations: [
- TreeComponent
+ TreeComponent, TestHostComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(TreeComponent);
+ fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
- component.isFlattened = true;
- component.item = {
- simplifyNames: false,
- kind: "entry",
- name: "BaseLayerTraceEntry",
- shortName: "BLTE",
- chips: [],
- children: [{kind: "3", id: "3", name: "Child1"}]
- };
- component.diffClass = jasmine.createSpy().and.returnValue("none");
- component.isHighlighted = jasmine.createSpy().and.returnValue(false);
- component.hasChildren = jasmine.createSpy().and.returnValue(true);
});
it("can be created", () => {
@@ -58,9 +46,35 @@ describe("TreeComponent", () => {
expect(component).toBeTruthy();
});
- it("creates node element", () => {
- fixture.detectChanges();
- const nodeElement = htmlElement.querySelector(".node");
- expect(nodeElement).toBeTruthy();
- });
+ @Component({
+ selector: "host-component",
+ template: `
+
+ `
+ })
+ class TestHostComponent {
+ isFlattened = true;
+ item = {
+ simplifyNames: false,
+ kind: "entry",
+ name: "BaseLayerTraceEntry",
+ shortName: "BLTE",
+ chips: [],
+ children: [{kind: "3", id: "3", name: "Child1"}]
+ };
+ store = new PersistentStore();
+ diffClass = jasmine.createSpy().and.returnValue("none");
+ isHighlighted = jasmine.createSpy().and.returnValue(false);
+ hasChildren = jasmine.createSpy().and.returnValue(true);
+
+ @ViewChild(TreeComponent)
+ public treeComponent!: TreeComponent;
+ }
});
diff --git a/tools/winscope-ng/src/viewers/components/tree.component.ts b/tools/winscope-ng/src/viewers/components/tree.component.ts
index 0c1e705b0..b5574ac90 100644
--- a/tools/winscope-ng/src/viewers/components/tree.component.ts
+++ b/tools/winscope-ng/src/viewers/components/tree.component.ts
@@ -15,9 +15,10 @@
*/
import { Component, Inject, Input, Output, ElementRef, EventEmitter } from "@angular/core";
import { PersistentStore } from "common/persistent_store";
-import { nodeStyles, treeNodeStyles } from "viewers/styles/node.styles";
-import { Tree, diffClass, isHighlighted } from "viewers/common/tree_utils";
+import { nodeStyles, treeNodeDataViewStyles } from "viewers/components/styles/node.styles";
+import { Tree, diffClass, isHighlighted, PropertiesTree, Terminal } from "viewers/common/tree_utils";
import { TraceType } from "common/trace/trace_type";
+import { TreeNodePropertiesDataViewComponent } from "./tree_node_properties_data_view.component";
@Component({
selector: "tree-view",
@@ -25,41 +26,48 @@ import { TraceType } from "common/trace/trace_type";
-
+
`,
- styles: [nodeStyles, treeNodeStyles]
+ styles: [nodeStyles, treeNodeDataViewStyles]
})
export class TreeComponent {
diffClass = diffClass;
isHighlighted = isHighlighted;
- @Input() item!: Tree;
+ @Input() item!: Tree | PropertiesTree | Terminal;
@Input() dependencies: Array
= [];
@Input() store!: PersistentStore;
@Input() isFlattened? = false;
@@ -85,8 +93,13 @@ export class TreeComponent {
@Input() pinnedItems?: Array = [];
@Input() itemsClickable?: boolean;
@Input() useGlobalCollapsedState?: boolean;
+ @Input() isPropertiesTree?: boolean;
+ @Input() isAlwaysCollapsed?: boolean;
+ @Input() showNode: (item?: any) => boolean = () => true;
+ @Input() isLeaf: (item: any) => boolean = (item: any) => !item.children || item.children.length === 0;
@Output() highlightedItemChange = new EventEmitter();
+ @Output() selectedTreeChange = new EventEmitter();
@Output() pinnedItemChange = new EventEmitter();
@Output() hoverStart = new EventEmitter();
@Output() hoverEnd = new EventEmitter();
@@ -99,7 +112,7 @@ export class TreeComponent {
nodeElement: HTMLElement;
constructor(
- @Inject(ElementRef) elementRef: ElementRef,
+ @Inject(ElementRef) public elementRef: ElementRef,
) {
this.nodeElement = elementRef.nativeElement.querySelector(".node");
this.nodeElement?.addEventListener("mousedown", this.nodeMouseDownEventListener);
@@ -107,6 +120,18 @@ export class TreeComponent {
this.nodeElement?.addEventListener("mouseleave", this.nodeMouseLeaveEventListener);
}
+ ngOnInit() {
+ if (this.isCollapsedByDefault) {
+ this.setCollapseValue(this.isCollapsedByDefault);
+ }
+ }
+
+ ngOnChanges() {
+ if (isHighlighted(this.item, this.highlightedItems)) {
+ this.selectedTreeChange.emit(this.item);
+ }
+ }
+
ngOnDestroy() {
this.nodeElement?.removeEventListener("mousedown", this.nodeMouseDownEventListener);
this.nodeElement?.removeEventListener("mouseenter", this.nodeMouseEnterEventListener);
@@ -119,7 +144,7 @@ export class TreeComponent {
return;
}
- if (!this.isLeaf() && event.detail % 2 === 0) {
+ if (!this.isLeaf(this.item) && event.detail % 2 === 0) {
// Double click collapsable node
event.preventDefault();
this.toggleTree();
@@ -140,6 +165,8 @@ export class TreeComponent {
updateHighlightedItems() {
if (this.item && this.item.id) {
this.highlightedItemChange.emit(`${this.item.id}`);
+ } else if (!this.item.id) {
+ this.selectedTreeChange.emit(this.item);
}
}
@@ -150,20 +177,20 @@ export class TreeComponent {
return false;
}
- sendNewHighlightedItemToHierarchy(newId: string) {
+ propagateNewHighlightedItem(newId: string) {
this.highlightedItemChange.emit(newId);
}
- sendNewPinnedItemToHierarchy(newPinnedItem: Tree) {
+ propagateNewPinnedItem(newPinnedItem: Tree) {
this.pinnedItemChange.emit(newPinnedItem);
}
- isLeaf() {
- return !this.item.children || this.item.children.length === 0;
+ propagateNewSelectedTree(newTree: Tree) {
+ this.selectedTreeChange.emit(newTree);
}
isClickable() {
- return !this.isLeaf() || this.itemsClickable;
+ return !this.isLeaf(this.item) || this.itemsClickable;
}
toggleTree() {
@@ -171,19 +198,18 @@ export class TreeComponent {
}
expandTree() {
- this.setCollapseValue(false);
+ this.setCollapseValue(true);
}
isCollapsed() {
- if (this.isLeaf()) {
- return false;
+ if (this.isAlwaysCollapsed || this.isLeaf(this.item)) {
+ return true;
}
if (this.useGlobalCollapsedState) {
return this.store.getFromStore(`collapsedState.item.${this.dependencies}.${this.item.id}`)==="true"
?? this.isCollapsedByDefault;
}
-
return this.localCollapsedState;
}
@@ -193,10 +219,10 @@ export class TreeComponent {
hasChildren() {
const isParentEntryInFlatView = this.item.kind === "entry" && this.isFlattened;
- return (!this.isFlattened || isParentEntryInFlatView) && !this.isLeaf();
+ return (!this.isFlattened || isParentEntryInFlatView) && !this.isLeaf(this.item);
}
- setCollapseValue(isCollapsed:boolean) {
+ setCollapseValue(isCollapsed: boolean) {
if (this.useGlobalCollapsedState) {
this.store.addToStore(`collapsedState.item.${this.dependencies}.${this.item.id}`, `${isCollapsed}`);
} else {
diff --git a/tools/winscope-ng/src/viewers/components/tree_node.component.spec.ts b/tools/winscope-ng/src/viewers/components/tree_node.component.spec.ts
index d8cd4bb1d..b76035a90 100644
--- a/tools/winscope-ng/src/viewers/components/tree_node.component.spec.ts
+++ b/tools/winscope-ng/src/viewers/components/tree_node.component.spec.ts
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {ComponentFixture, TestBed} from "@angular/core/testing";
+import { ComponentFixture, TestBed, ComponentFixtureAutoDetect } from "@angular/core/testing";
import { TreeNodeComponent } from "./tree_node.component";
-import { ComponentFixtureAutoDetect } from "@angular/core/testing";
-import { NO_ERRORS_SCHEMA } from "@angular/core";
+import { Component, ViewChild, NO_ERRORS_SCHEMA } from "@angular/core";
describe("TreeNodeComponent", () => {
- let fixture: ComponentFixture;
- let component: TreeNodeComponent;
+ let fixture: ComponentFixture;
+ let component: TestHostComponent;
let htmlElement: HTMLElement;
beforeAll(async () => {
@@ -29,27 +28,16 @@ describe("TreeNodeComponent", () => {
{ provide: ComponentFixtureAutoDetect, useValue: true }
],
declarations: [
- TreeNodeComponent
+ TreeNodeComponent, TestHostComponent
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(TreeNodeComponent);
+ fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
- component.item = {
- simplifyNames: false,
- kind: "entry",
- name: "BaseLayerTraceEntry",
- shortName: "BLTE",
- chips: [],
- };
- component.isCollapsed = true;
- component.hasChildren = false;
- component.isPinned = false;
- component.isInPinnedSection = false;
});
it("can be created", () => {
@@ -57,9 +45,29 @@ describe("TreeNodeComponent", () => {
expect(component).toBeTruthy();
});
- it("creates tree element", () => {
- fixture.detectChanges();
- const treeElement = htmlElement.querySelector("tree-element");
- expect(treeElement).toBeTruthy();
- });
-});
+ @Component({
+ selector: "host-component",
+ template: `
+
+ `
+ })
+ class TestHostComponent {
+ item = {
+ simplifyNames: false,
+ kind: "entry",
+ name: "BaseLayerTraceEntry",
+ shortName: "BLTE",
+ chips: [],
+ };
+
+ @ViewChild(TreeNodeComponent)
+ public treeNodeComponent!: TreeNodeComponent;
+ }
+});
\ No newline at end of file
diff --git a/tools/winscope-ng/src/viewers/components/tree_node.component.ts b/tools/winscope-ng/src/viewers/components/tree_node.component.ts
index 44c44d3d3..f51b342a9 100644
--- a/tools/winscope-ng/src/viewers/components/tree_node.component.ts
+++ b/tools/winscope-ng/src/viewers/components/tree_node.component.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
import { Component, Input, Output, EventEmitter } from "@angular/core";
-import { nodeInnerItemStyles } from "viewers/styles/node.styles";
-import { Tree } from "viewers/common/tree_utils";
+import { nodeInnerItemStyles } from "viewers/components/styles/node.styles";
+import { PropertiesTree, Tree, DiffType } from "viewers/common/tree_utils";
@Component({
selector: "tree-node",
@@ -26,7 +26,7 @@ import { Tree } from "viewers/common/tree_utils";
*ngIf="showChevron()"
>
- {{isCollapsed ? "chevron_right" : "arrow_drop_down"}}
+ {{isCollapsed ? "arrow_drop_down" : "chevron_right"}}
@@ -40,7 +40,7 @@ import { Tree } from "viewers/common/tree_utils";
-
+ *ngIf="!isPropertiesTreeNode"
+ >
+
`,
- styles: [ treeElementStyles ]
+ styles: [ treeNodeDataViewStyles ]
})
-export class TreeElementComponent {
+export class TreeNodeDataViewComponent {
@Input() item!: Tree;
showShortName() {
diff --git a/tools/winscope-ng/src/viewers/components/tree_node_properties_data_view.component.spec.ts b/tools/winscope-ng/src/viewers/components/tree_node_properties_data_view.component.spec.ts
new file mode 100644
index 000000000..1d913d4ce
--- /dev/null
+++ b/tools/winscope-ng/src/viewers/components/tree_node_properties_data_view.component.spec.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {ComponentFixture, TestBed} from "@angular/core/testing";
+import { TreeNodePropertiesDataViewComponent } from "./tree_node_properties_data_view.component";
+import { ComponentFixtureAutoDetect } from "@angular/core/testing";
+
+describe("TreeNodePropertiesDataViewComponent", () => {
+ let fixture: ComponentFixture
;
+ let component: TreeNodePropertiesDataViewComponent;
+ let htmlElement: HTMLElement;
+
+ beforeAll(async () => {
+ await TestBed.configureTestingModule({
+ providers: [
+ { provide: ComponentFixtureAutoDetect, useValue: true }
+ ],
+ declarations: [
+ TreeNodePropertiesDataViewComponent
+ ],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TreeNodePropertiesDataViewComponent);
+ component = fixture.componentInstance;
+ htmlElement = fixture.nativeElement;
+ });
+
+ it("can be created", () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/tools/winscope-ng/src/viewers/components/tree_node_properties_data_view.component.ts b/tools/winscope-ng/src/viewers/components/tree_node_properties_data_view.component.ts
new file mode 100644
index 000000000..589981c55
--- /dev/null
+++ b/tools/winscope-ng/src/viewers/components/tree_node_properties_data_view.component.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, Input } from "@angular/core";
+import { treeNodePropertiesDataViewStyles } from "viewers/components/styles/tree_node_data_view.styles";
+import { PropertiesTree } from "viewers/common/tree_utils";
+
+@Component({
+ selector: "tree-node-properties-data-view",
+ template: `
+
+ {{ item.propertyKey }}
+ :
+ {{ item.propertyValue }}
+
+ `,
+ styles: [ treeNodePropertiesDataViewStyles ]
+})
+
+export class TreeNodePropertiesDataViewComponent {
+ @Input() item!: PropertiesTree;
+
+ valueClass() {
+ if (!this.item.propertyValue) {
+ return null;
+ }
+
+ if (this.item.propertyValue == "null") {
+ return "null";
+ }
+
+ if (this.item.propertyValue == "true") {
+ return "true";
+ }
+
+ if (this.item.propertyValue == "false") {
+ return "false";
+ }
+
+ if (!isNaN(this.item.propertyValue)) {
+ return "number";
+ }
+ return null;
+ }
+}
diff --git a/tools/winscope-ng/src/viewers/viewer.ts b/tools/winscope-ng/src/viewers/viewer.ts
index 5ad1857e0..009fa80ba 100644
--- a/tools/winscope-ng/src/viewers/viewer.ts
+++ b/tools/winscope-ng/src/viewers/viewer.ts
@@ -16,7 +16,6 @@
import { TraceType } from "common/trace/trace_type";
interface Viewer {
- //TODO: add TraceEntry data type
notifyCurrentTraceEntries(entries: Map): void;
getView(): HTMLElement;
getTitle(): string;
diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts
index d4ccd4bbc..f2ffaf887 100644
--- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts
+++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts
@@ -16,19 +16,20 @@
import { Rectangle, RectMatrix, RectTransform, UiData } from "viewers/viewer_surface_flinger/ui_data";
import { TraceType } from "common/trace/trace_type";
import { UserOptions } from "viewers/common/user_options";
-import { TreeGenerator, getFilter, FilterType, Tree } from "viewers/common/tree_utils";
+import { getFilter, FilterType, Tree, TreeSummary } from "viewers/common/tree_utils";
+import { TreeGenerator } from "viewers/common/tree_generator";
+import { TreeTransformer } from "viewers/common/tree_transformer";
type NotifyViewCallbackType = (uiData: UiData) => void;
-class Presenter {
+export class Presenter {
constructor(notifyViewCallback: NotifyViewCallbackType) {
this.notifyViewCallback = notifyViewCallback;
this.uiData = new UiData();
this.notifyViewCallback(this.uiData);
}
- public updatePinnedItems(event: CustomEvent) {
- const pinnedItem = event.detail.pinnedItem;
+ public updatePinnedItems(pinnedItem: Tree) {
const pinnedId = `${pinnedItem.id}`;
if (this.pinnedItems.map(item => `${item.id}`).includes(pinnedId)) {
this.pinnedItems = this.pinnedItems.filter(pinned => `${pinned.id}` != pinnedId);
@@ -40,8 +41,7 @@ class Presenter {
this.notifyViewCallback(this.uiData);
}
- public updateHighlightedItems(event: CustomEvent) {
- const id = `${event.detail.id}`;
+ public updateHighlightedItems(id: string) {
if (this.highlightedItems.includes(id)) {
this.highlightedItems = this.highlightedItems.filter(hl => hl != id);
} else {
@@ -52,23 +52,86 @@ class Presenter {
this.notifyViewCallback(this.uiData);
}
- public updateHierarchyTree(event: CustomEvent) {
- this.hierarchyUserOptions = event.detail.userOptions;
+ public updateHierarchyTree(userOptions: UserOptions) {
+ this.hierarchyUserOptions = userOptions;
this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
this.uiData.tree = this.generateTree();
this.notifyViewCallback(this.uiData);
}
- public filterHierarchyTree(event: CustomEvent) {
- this.hierarchyFilter = getFilter(event.detail.filterString);
+ public filterHierarchyTree(filterString: string) {
+ this.hierarchyFilter = getFilter(filterString);
this.uiData.tree = this.generateTree();
this.notifyViewCallback(this.uiData);
}
+ public updatePropertiesTree(userOptions: UserOptions) {
+ this.propertiesUserOptions = userOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
+ this.updateSelectedTreeUiData();
+ }
+
+ public filterPropertiesTree(filterString: string) {
+ this.propertiesFilter = getFilter(filterString);
+ this.updateSelectedTreeUiData();
+ }
+
+ public newPropertiesTree(selectedItem: any) {
+ this.selectedTree = selectedItem;
+ this.updateSelectedTreeUiData();
+ }
+
+ private updateSelectedTreeUiData() {
+ this.uiData.selectedTree = this.getTreeWithTransformedProperties(this.selectedTree);
+ this.uiData.selectedTreeSummary = this.getSelectedTreeSummary(this.selectedTree);
+ this.notifyViewCallback(this.uiData);
+ }
+
+ private getSelectedTreeSummary(layer: Tree): TreeSummary | undefined {
+ const summary = [];
+
+ if (layer?.visibilityReason?.length > 0) {
+ let reason = "";
+ if (Array.isArray(layer.visibilityReason)) {
+ reason = layer.visibilityReason.join(", ");
+ } else {
+ reason = layer.visibilityReason;
+ }
+
+ summary.push({key: "Invisible due to", value: reason});
+ }
+
+ if (layer?.occludedBy?.length > 0) {
+ summary.push({key: "Occluded by", value: layer.occludedBy.map((it:Tree) => it.id).join(", ")});
+ }
+
+ if (layer?.partiallyOccludedBy?.length > 0) {
+ summary.push({
+ key: "Partially occluded by",
+ value: layer.partiallyOccludedBy.map((it:Tree) => it.id).join(", "),
+ });
+ }
+
+ if (layer?.coveredBy?.length > 0) {
+ summary.push({key: "Covered by", value: layer.coveredBy.map((it:Tree) => it.id).join(", ")});
+ }
+
+ if (summary.length === 0) {
+ return undefined;
+ }
+
+ return summary;
+ }
+
public notifyCurrentTraceEntries(entries: Map) {
this.uiData = new UiData();
const entry = entries.get(TraceType.SURFACE_FLINGER)[0];
- this.uiData.rects = [];
+ this.previousEntry = entries.get(TraceType.SURFACE_FLINGER)[1];
+
+ this.uiData = new UiData();
+
+ this.uiData.highlightedItems = this.highlightedItems;
+
const displayRects = entry.displays.map((display: any) => {
const rect = display.layerStackSpace;
rect.label = display.name;
@@ -78,7 +141,6 @@ class Presenter {
rect.isVirtual = display.isVirtual ?? false;
return rect;
}) ?? [];
-
this.displayIds = [];
const rects = entry.visibleLayers
.sort((a: any, b: any) => (b.absoluteZ > a.absoluteZ) ? 1 : (a.absoluteZ == b.absoluteZ) ? 0 : -1)
@@ -92,13 +154,12 @@ class Presenter {
});
this.uiData.rects = this.rectsToUiData(rects.concat(displayRects));
this.uiData.displayIds = this.displayIds;
- this.uiData.highlightedItems = this.highlightedItems;
- this.uiData.rects = this.rectsToUiData(entry.rects.concat(displayRects));
- this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
- this.previousEntry = entries.get(TraceType.SURFACE_FLINGER)[1];
- this.entry = entry;
+ this.entry = entry;
+ this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
+ this.uiData.propertiesUserOptions = this.propertiesUserOptions;
this.uiData.tree = this.generateTree();
+
this.notifyViewCallback(this.uiData);
}
@@ -106,7 +167,11 @@ class Presenter {
if (!this.entry) {
return null;
}
- const generator = new TreeGenerator(this.entry, this.hierarchyUserOptions, this.hierarchyFilter, this.pinnedIds)
+
+ const generator = new TreeGenerator(this.entry, this.hierarchyFilter, this.pinnedIds)
+ .setIsOnlyVisibleView(this.hierarchyUserOptions["onlyVisible"]?.enabled)
+ .setIsSimplifyNames(this.hierarchyUserOptions["simplifyNames"]?.enabled)
+ .setIsFlatView(this.hierarchyUserOptions["flat"]?.enabled)
.withUniqueNodeId();
let tree: Tree;
if (!this.hierarchyUserOptions["showDiff"]?.enabled) {
@@ -172,13 +237,26 @@ class Presenter {
}
}
+ private getTreeWithTransformedProperties(selectedTree: Tree) {
+ const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
+ .setIsShowDefaults(this.propertiesUserOptions["showDefaults"]?.enabled)
+ .setIsShowDiff(this.propertiesUserOptions["showDiff"]?.enabled)
+ .setTransformerOptions({skip: selectedTree.skip})
+ .setDiffProperties(this.previousEntry);
+ this.uiData.selectedLayer = transformer.getOriginalLayer(this.entry, selectedTree.stableId);
+ const transformedTree = transformer.transform();
+ return transformedTree;
+ }
+
private readonly notifyViewCallback: NotifyViewCallbackType;
private uiData: UiData;
- private displayIds: Array = [];
private hierarchyFilter: FilterType = getFilter("");
+ private propertiesFilter: FilterType = getFilter("");
private highlightedItems: Array = [];
+ private displayIds: Array = [];
private pinnedItems: Array = [];
private pinnedIds: Array = [];
+ private selectedTree: any = null;
private previousEntry: any = null;
private entry: any = null;
private hierarchyUserOptions: UserOptions = {
@@ -199,6 +277,20 @@ class Presenter {
enabled: false
}
};
-}
-export {Presenter};
+ private propertiesUserOptions: UserOptions = {
+ showDiff: {
+ name: "Show diff",
+ enabled: false
+ },
+ showDefaults: {
+ name: "Show defaults",
+ enabled: true,
+ tooltip: `
+ If checked, shows the value of all properties.
+ Otherwise, hides all properties whose value is
+ the default for its data type.
+ `
+ },
+ };
+}
diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts
index b318c7f69..ebd56defd 100644
--- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts
+++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { TraceType } from "common/trace/trace_type";
-import { Tree } from "viewers/common/tree_utils";
+import { Tree, TreeSummary } from "viewers/common/tree_utils";
import { UserOptions } from "viewers/common/user_options";
+import { Layer } from "common/trace/flickerlib/common";
+import { TraceType } from "common/trace/trace_type";
export class UiData {
dependencies: Array = [TraceType.SURFACE_FLINGER];
@@ -24,7 +25,11 @@ export class UiData {
highlightedItems?: Array = [];
pinnedItems?: Array = [];
hierarchyUserOptions?: UserOptions = {};
+ propertiesUserOptions?: UserOptions = {};
tree?: Tree | null = null;
+ selectedTree?: any = {};
+ selectedLayer?: Layer = {};
+ selectedTreeSummary?: TreeSummary = [];
}
export interface Rectangle {
diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts
index 52dd48119..9d1e0c06e 100644
--- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts
+++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts
@@ -31,6 +31,7 @@ import { PersistentStore } from "common/persistent_store";
[rects]="inputData?.rects ?? []"
[displayIds]="inputData?.displayIds ?? []"
[highlightedItems]="inputData?.highlightedItems ?? []"
+ [displayIds]="inputData?.displayIds ?? []"
>
@@ -45,7 +46,13 @@ import { PersistentStore } from "common/persistent_store";
>
-
+
diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
index 20fea48ac..bf38e87f4 100644
--- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
+++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.ts
@@ -25,10 +25,13 @@ class ViewerSurfaceFlinger implements Viewer {
this.presenter = new Presenter((uiData: UiData) => {
(this.view as any).inputData = uiData;
});
- this.view.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) => this.presenter.updatePinnedItems((event as CustomEvent)));
- this.view.addEventListener(ViewerEvents.HighlightedChange, (event) => this.presenter.updateHighlightedItems((event as CustomEvent)));
- this.view.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) => this.presenter.updateHierarchyTree((event as CustomEvent)));
- this.view.addEventListener(ViewerEvents.HierarchyFilterChange, (event) => this.presenter.filterHierarchyTree((event as CustomEvent)));
+ this.view.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) => this.presenter.updatePinnedItems(((event as CustomEvent).detail.pinnedItem)));
+ this.view.addEventListener(ViewerEvents.HighlightedChange, (event) => this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`));
+ this.view.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) => this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions));
+ this.view.addEventListener(ViewerEvents.HierarchyFilterChange, (event) => this.presenter.filterHierarchyTree((event as CustomEvent).detail.filterString));
+ this.view.addEventListener(ViewerEvents.PropertiesUserOptionsChange, (event) => this.presenter.updatePropertiesTree((event as CustomEvent).detail.userOptions));
+ this.view.addEventListener(ViewerEvents.PropertiesFilterChange, (event) => this.presenter.filterPropertiesTree((event as CustomEvent).detail.filterString));
+ this.view.addEventListener(ViewerEvents.SelectedTreeChange, (event) => this.presenter.newPropertiesTree((event as CustomEvent).detail.selectedItem));
}
public notifyCurrentTraceEntries(entries: Map): void {