Update Main Winscope UI.
Update to match figma designs for the viewer section - app toolbar, tabs for different viewers, etc. Bug: b/244715720 Test: npm run test:all. also upload any trace to see what it looks like Change-Id: I85b64e4a660fd30c6d71eb8ef3cb3b45c37a2943
This commit is contained in:
@@ -19,6 +19,7 @@ import { HttpClientModule } from "@angular/common/http";
|
||||
import { MatSliderModule } from "@angular/material/slider";
|
||||
import { MatTooltipModule } from "@angular/material/tooltip";
|
||||
import { MatToolbarModule } from "@angular/material/toolbar";
|
||||
import { MatTabsModule } from "@angular/material/tabs";
|
||||
|
||||
import { AppComponent } from "./components/app.component";
|
||||
import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component";
|
||||
@@ -31,7 +32,6 @@ import { UploadTracesComponent } from "./components/upload_traces.component";
|
||||
import { HierarchyComponent } from "viewers/components/hierarchy.component";
|
||||
import { PropertiesComponent } from "viewers/components/properties.component";
|
||||
import { RectsComponent } from "viewers/components/rects/rects.component";
|
||||
import { TraceViewHeaderComponent } from "./components/trace_view_header.component";
|
||||
import { TraceViewComponent } from "./components/trace_view.component";
|
||||
import { TreeComponent } from "viewers/components/tree.component";
|
||||
import { TreeNodeComponent } from "viewers/components/tree_node.component";
|
||||
@@ -53,7 +53,6 @@ import { TransformMatrixComponent } from "viewers/components/transform_matrix.co
|
||||
HierarchyComponent,
|
||||
PropertiesComponent,
|
||||
RectsComponent,
|
||||
TraceViewHeaderComponent,
|
||||
TraceViewComponent,
|
||||
TreeComponent,
|
||||
TreeNodeComponent,
|
||||
@@ -83,7 +82,8 @@ import { TransformMatrixComponent } from "viewers/components/transform_matrix.co
|
||||
MatSliderModule,
|
||||
MatRadioModule,
|
||||
MatTooltipModule,
|
||||
MatToolbarModule
|
||||
MatToolbarModule,
|
||||
MatTabsModule
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
||||
@@ -29,6 +29,7 @@ import { AdbProxyComponent } from "./adb_proxy.component";
|
||||
import { WebAdbComponent } from "./web_adb.component";
|
||||
import { TraceConfigComponent } from "./trace_config.component";
|
||||
import { ViewerSurfaceFlingerComponent } from "viewers/viewer_surface_flinger/viewer_surface_flinger.component";
|
||||
import { TraceViewComponent } from "./trace_view.component";
|
||||
|
||||
describe("AppComponent", () => {
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
@@ -55,7 +56,8 @@ describe("AppComponent", () => {
|
||||
AdbProxyComponent,
|
||||
WebAdbComponent,
|
||||
TraceConfigComponent,
|
||||
ViewerSurfaceFlingerComponent
|
||||
ViewerSurfaceFlingerComponent,
|
||||
TraceViewComponent
|
||||
],
|
||||
}).overrideComponent(AppComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
|
||||
@@ -13,25 +13,25 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, Injector, Inject, ViewEncapsulation, Input } from "@angular/core";
|
||||
import { Component, Injector, Inject, ViewEncapsulation, Input, ViewChild } from "@angular/core";
|
||||
import { createCustomElement } from "@angular/elements";
|
||||
import { TraceCoordinator } from "../trace_coordinator";
|
||||
import { proxyClient, ProxyState } from "trace_collection/proxy_client";
|
||||
import { PersistentStore } from "common/persistent_store";
|
||||
import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component";
|
||||
import { ViewerSurfaceFlingerComponent } from "viewers/viewer_surface_flinger/viewer_surface_flinger.component";
|
||||
import { TraceViewComponent } from "./trace_view.component";
|
||||
import { Timestamp } from "common/trace/timestamp";
|
||||
import { MatSliderChange } from "@angular/material/slider";
|
||||
import { Viewer } from "viewers/viewer";
|
||||
|
||||
@Component({
|
||||
selector: "app-root",
|
||||
template: `
|
||||
<mat-toolbar class="app-toolbar">
|
||||
<span id="app-title">Winscope</span>
|
||||
<button mat-raised-button *ngIf="dataLoaded" (click)="clearData()">Back to Home</button>
|
||||
<span class="toolbar-wrapper">
|
||||
<button mat-raised-button *ngIf="dataLoaded" (click)="toggleTimestamp()">Start/End Timestamp</button>
|
||||
<button class="upload-new-btn white-btn" mat-raised-button *ngIf="dataLoaded" (click)="clearData()">Upload New</button>
|
||||
</span>
|
||||
</mat-toolbar>
|
||||
|
||||
<div class="welcome-info" *ngIf="!dataLoaded">
|
||||
@@ -48,6 +48,10 @@ import { Viewer } from "viewers/viewer";
|
||||
</div>
|
||||
|
||||
<div id="viewers" [class]="showViewers()">
|
||||
<trace-view
|
||||
[store]="store"
|
||||
[traceCoordinator]="traceCoordinator"
|
||||
></trace-view>
|
||||
</div>
|
||||
|
||||
<div id="timescrub">
|
||||
@@ -67,15 +71,32 @@ import { Viewer } from "viewers/viewer";
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.time-slider {width: 100%}
|
||||
.upload-new-btn {float: right}
|
||||
.time-slider {
|
||||
width: 100%
|
||||
}
|
||||
.upload-new-btn {
|
||||
float: right;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.app-toolbar {
|
||||
border-bottom: 1px solid var(--default-border);
|
||||
box-shadow: none;
|
||||
background-color: rgba(1, 1, 1, 0);
|
||||
height: 56px;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.toolbar-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.welcome-info {
|
||||
text-align: center;
|
||||
font: inherit;
|
||||
@@ -90,11 +111,10 @@ export class AppComponent {
|
||||
traceCoordinator: TraceCoordinator;
|
||||
states = ProxyState;
|
||||
store: PersistentStore = new PersistentStore();
|
||||
@Input() dataLoaded = false;
|
||||
viewersCreated = false;
|
||||
currentTimestamp?: Timestamp;
|
||||
currentTimestampIndex = 0;
|
||||
allTimestamps: Timestamp[] = [];
|
||||
@Input() dataLoaded = false;
|
||||
|
||||
constructor(
|
||||
@Inject(Injector) injector: Injector
|
||||
@@ -108,52 +128,9 @@ export class AppComponent {
|
||||
customElements.define("viewer-surface-flinger",
|
||||
createCustomElement(ViewerSurfaceFlingerComponent, {injector}));
|
||||
}
|
||||
if (!customElements.get("trace-view")) {
|
||||
customElements.define("trace-view",
|
||||
createCustomElement(TraceViewComponent, {injector}));
|
||||
}
|
||||
}
|
||||
|
||||
onDataLoadedChange(dataLoaded: boolean) {
|
||||
if (dataLoaded && !this.viewersCreated) {
|
||||
this.allTimestamps = this.traceCoordinator.getTimestamps();
|
||||
this.traceCoordinator.createViewers();
|
||||
this.createViewerElements();
|
||||
this.currentTimestampIndex = 0;
|
||||
this.notifyCurrentTimestamp();
|
||||
this.viewersCreated = true;
|
||||
this.dataLoaded = dataLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
createViewerElements() {
|
||||
const viewersDiv = document.querySelector("div#viewers")!;
|
||||
viewersDiv.innerHTML = "";
|
||||
|
||||
let cardCounter = 0;
|
||||
this.traceCoordinator.getViewers().forEach((viewer: Viewer) => {
|
||||
const traceView = document.createElement("trace-view");
|
||||
(traceView as any).title = viewer.getTitle();
|
||||
(traceView as any).dependencies = viewer.getDependencies();
|
||||
(traceView as any).showTrace = true;
|
||||
traceView.addEventListener("saveTraces", ($event: any) => {
|
||||
this.traceCoordinator.saveTraces($event.detail);
|
||||
});
|
||||
viewersDiv.appendChild(traceView);
|
||||
|
||||
const traceCard = traceView.querySelector(".trace-card")!;
|
||||
traceCard.id = `card-${cardCounter}`;
|
||||
(traceView as any).cardId = cardCounter;
|
||||
cardCounter++;
|
||||
|
||||
const traceCardContent = traceCard.querySelector(".trace-card-content")!;
|
||||
const view = viewer.getView();
|
||||
(view as any).store = this.store;
|
||||
traceCardContent.appendChild(view);
|
||||
});
|
||||
}
|
||||
|
||||
updateCurrentTimestamp(event: MatSliderChange) {
|
||||
public updateCurrentTimestamp(event: MatSliderChange) {
|
||||
if (event.value) {
|
||||
this.currentTimestampIndex = event.value;
|
||||
this.notifyCurrentTimestamp();
|
||||
@@ -176,7 +153,6 @@ export class AppComponent {
|
||||
|
||||
public clearData() {
|
||||
this.dataLoaded = false;
|
||||
this.viewersCreated = false;
|
||||
this.traceCoordinator.clearData();
|
||||
proxyClient.adbData = [];
|
||||
}
|
||||
@@ -185,4 +161,14 @@ export class AppComponent {
|
||||
const isShown = this.dataLoaded ? "show" : "hide";
|
||||
return ["viewers", isShown];
|
||||
}
|
||||
|
||||
public onDataLoadedChange(dataLoaded: boolean) {
|
||||
if (dataLoaded && !(this.traceCoordinator.getViewers().length > 0)) {
|
||||
this.allTimestamps = this.traceCoordinator.getTimestamps();
|
||||
this.traceCoordinator.createViewers();
|
||||
this.currentTimestampIndex = 0;
|
||||
this.notifyCurrentTimestamp();
|
||||
this.dataLoaded = dataLoaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { TraceViewComponent } from "./trace_view.component";
|
||||
import { MatCardModule } from "@angular/material/card";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
import { TraceCoordinator } from "app/trace_coordinator";
|
||||
|
||||
describe("TraceViewComponent", () => {
|
||||
let fixture: ComponentFixture<TraceViewComponent>;
|
||||
@@ -36,26 +36,55 @@ describe("TraceViewComponent", () => {
|
||||
}).compileComponents();
|
||||
fixture = TestBed.createComponent(TraceViewComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.traceCoordinator = new TraceCoordinator();
|
||||
component.viewerTabs = [
|
||||
{
|
||||
label: "Surface Flinger",
|
||||
cardId: 0,
|
||||
},
|
||||
{
|
||||
label: "Window Manager",
|
||||
cardId: 1,
|
||||
}
|
||||
];
|
||||
htmlElement = fixture.nativeElement;
|
||||
component.dependencies = [TraceType.SURFACE_FLINGER, TraceType.WINDOW_MANAGER];
|
||||
component.showTrace = true;
|
||||
});
|
||||
|
||||
it("can be created", () => {
|
||||
fixture.detectChanges();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it("check that mat card title and contents are displayed", () => {
|
||||
it("creates viewer tabs", () => {
|
||||
fixture.detectChanges();
|
||||
const title = htmlElement.querySelector(".trace-card-title");
|
||||
expect(title).toBeTruthy();
|
||||
const header = title?.querySelector("trace-view-header");
|
||||
expect(header).toBeTruthy();
|
||||
const tabs = htmlElement.querySelectorAll(".viewer-tab");
|
||||
expect(tabs.length).toEqual(2);
|
||||
expect(component.activeViewerCardId).toEqual(0);
|
||||
});
|
||||
|
||||
it("check that card content is created", () => {
|
||||
it("changes active viewer on click", async () => {
|
||||
fixture.detectChanges();
|
||||
const content = htmlElement.querySelector(".trace-card-content") as HTMLElement;
|
||||
expect(content).toBeTruthy();
|
||||
expect(component.activeViewerCardId).toEqual(0);
|
||||
const tabs = htmlElement.querySelectorAll(".viewer-tab");
|
||||
tabs[0].dispatchEvent(new Event("click"));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const firstId = component.activeViewerCardId;
|
||||
tabs[1].dispatchEvent(new Event("click"));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const secondId = component.activeViewerCardId;
|
||||
expect(firstId !== secondId).toBeTrue;
|
||||
});
|
||||
|
||||
it("downloads all traces", async () => {
|
||||
spyOn(component, "saveTraces").and.callThrough();
|
||||
fixture.detectChanges();
|
||||
const downloadButton: HTMLButtonElement | null = htmlElement.querySelector(".save-btn");
|
||||
expect(downloadButton).toBeInstanceOf(HTMLButtonElement);
|
||||
downloadButton?.dispatchEvent(new Event("click"));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(component.saveTraces).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,42 +16,145 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter
|
||||
Inject,
|
||||
ElementRef,
|
||||
} from "@angular/core";
|
||||
import { TRACE_INFO } from "../trace_info";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
import { TraceCoordinator } from "app/trace_coordinator";
|
||||
import { PersistentStore } from "common/persistent_store";
|
||||
import { Viewer } from "viewers/viewer";
|
||||
|
||||
@Component({
|
||||
selector: "trace-view",
|
||||
template: `
|
||||
<mat-card class="trace-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title class="trace-card-title" *ngIf="dependencies">
|
||||
<trace-view-header
|
||||
[title]="title"
|
||||
[(showTrace)]="showTrace"
|
||||
[dependencies]="dependencies"
|
||||
[cardId]="cardId"
|
||||
(saveTraceChange)="onSaveTraces($event)"
|
||||
></trace-view-header>
|
||||
</mat-card-title>
|
||||
<mat-card-header class="trace-view-header">
|
||||
<span class="header-items-wrapper">
|
||||
<nav mat-tab-nav-bar class="viewer-nav-bar">
|
||||
<a
|
||||
mat-tab-link
|
||||
*ngFor="let tab of viewerTabs"
|
||||
[active]="isCurrentActiveCard(tab.cardId)"
|
||||
(click)="showViewer(tab.cardId)"
|
||||
class="viewer-tab"
|
||||
>{{tab.label}}</a>
|
||||
</nav>
|
||||
<button
|
||||
mat-raised-button
|
||||
class="icon-button white-btn save-btn"
|
||||
(click)="saveTraces()"
|
||||
>Download all traces</button>
|
||||
</span>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="trace-card-content" [hidden]="!showTrace">
|
||||
<mat-card-content class="trace-view-content">
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
:host /deep/ .trace-view-header .mat-card-header-text {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-items-wrapper {
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.viewer-nav-bar {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
float: right;
|
||||
vertical-align: middle;
|
||||
border: none;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
`
|
||||
]
|
||||
})
|
||||
export class TraceViewComponent {
|
||||
@Input() title!: string;
|
||||
@Input() dependencies!: TraceType[];
|
||||
@Input() showTrace = true;
|
||||
@Input() cardId = 0;
|
||||
@Output() saveTraces = new EventEmitter<TraceType[]>();
|
||||
@Input() store!: PersistentStore;
|
||||
@Input() traceCoordinator!: TraceCoordinator;
|
||||
viewerTabs: ViewerTab[] = [];
|
||||
viewersAdded = false;
|
||||
activeViewerCardId = 0;
|
||||
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
constructor(
|
||||
@Inject(ElementRef) private elementRef: ElementRef,
|
||||
) {}
|
||||
|
||||
onSaveTraces(dependencies: TraceType[]) {
|
||||
this.saveTraces.emit(dependencies);
|
||||
ngDoCheck() {
|
||||
if (this.traceCoordinator.getViewers().length > 0 && !this.viewersAdded) {
|
||||
let cardCounter = 0;
|
||||
this.viewerTabs = [];
|
||||
this.traceCoordinator.getViewers().forEach((viewer: Viewer) => {
|
||||
// create tab for viewer nav bar
|
||||
const tab = {
|
||||
label: viewer.getTitle(),
|
||||
cardId: cardCounter,
|
||||
};
|
||||
this.viewerTabs.push(tab);
|
||||
|
||||
// add properties to view and add view to trace view card
|
||||
const view = viewer.getView();
|
||||
(view as any).store = this.store;
|
||||
view.id = `card-${cardCounter}`;
|
||||
view.style.display = this.isActiveViewerCard(cardCounter) ? "" : "none";
|
||||
|
||||
const traceViewContent = this.elementRef.nativeElement.querySelector(".trace-view-content")!;
|
||||
traceViewContent.appendChild(view);
|
||||
cardCounter++;
|
||||
});
|
||||
this.viewersAdded = true;
|
||||
} else if (this.traceCoordinator.getViewers().length === 0 && this.viewersAdded) {
|
||||
this.activeViewerCardId = 0;
|
||||
this.viewersAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
public showViewer(cardId: number) {
|
||||
this.changeViewerVisibility(false);
|
||||
this.activeViewerCardId = cardId;
|
||||
this.changeViewerVisibility(true);
|
||||
}
|
||||
|
||||
public isCurrentActiveCard(cardId: number) {
|
||||
return this.activeViewerCardId === cardId;
|
||||
}
|
||||
|
||||
public async saveTraces() {
|
||||
const zipFile = await this.traceCoordinator.saveTracesAsZip();
|
||||
const zipFileName = "winscope.zip";
|
||||
const a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
const url = window.URL.createObjectURL(zipFile);
|
||||
a.href = url;
|
||||
a.download = zipFileName;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
|
||||
private isActiveViewerCard(cardId: number) {
|
||||
return this.activeViewerCardId === cardId;
|
||||
}
|
||||
|
||||
private changeViewerVisibility(show: boolean) {
|
||||
const view = document.querySelector(`#card-${this.activeViewerCardId}`);
|
||||
if (view) {
|
||||
(view as HTMLElement).style.display = show ? "" : "none";
|
||||
(view as any).active = show;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ViewerTab {
|
||||
label: string,
|
||||
cardId: number
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* 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 { CommonModule } from "@angular/common";
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { TraceViewHeaderComponent } from "./trace_view_header.component";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
import { MatButtonModule } from "@angular/material/button";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
|
||||
describe("TraceViewHeaderComponent", () => {
|
||||
let fixture: ComponentFixture<TraceViewHeaderComponent>;
|
||||
let component: TraceViewHeaderComponent;
|
||||
let htmlElement: HTMLElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatIconModule,
|
||||
MatButtonModule
|
||||
],
|
||||
declarations: [TraceViewHeaderComponent]
|
||||
}).compileComponents();
|
||||
fixture = TestBed.createComponent(TraceViewHeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
htmlElement = fixture.nativeElement;
|
||||
component.dependencies = [TraceType.SURFACE_FLINGER, TraceType.WINDOW_MANAGER];
|
||||
});
|
||||
|
||||
it("can be created", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it("check that toggle button is displayed, expanded on default", () => {
|
||||
component.showTrace = true;
|
||||
fixture.detectChanges();
|
||||
const toggleButton = htmlElement.querySelector(".toggle-btn");
|
||||
expect(toggleButton).toBeTruthy();
|
||||
const chevronIcon = toggleButton?.querySelector("mat-icon");
|
||||
expect(chevronIcon).toBeTruthy;
|
||||
});
|
||||
|
||||
it("check that toggle button icon is a right chevron when minimised ", () => {
|
||||
component.showTrace = false;
|
||||
fixture.detectChanges();
|
||||
const toggleButton = htmlElement.querySelector(".toggle-btn");
|
||||
const chevronIcon = toggleButton?.querySelector("mat-icon");
|
||||
expect(chevronIcon).toBeTruthy;
|
||||
});
|
||||
|
||||
it("check that clicking toggle button causes view to minimise", async () => {
|
||||
component.showTrace = true;
|
||||
fixture.detectChanges();
|
||||
spyOn(component, "toggleView").and.callThrough();
|
||||
const button: HTMLButtonElement | null = htmlElement.querySelector(".toggle-btn");
|
||||
expect(button).toBeInstanceOf(HTMLButtonElement);
|
||||
button?.dispatchEvent(new Event("click"));
|
||||
await fixture.whenStable();
|
||||
expect(component.toggleView).toHaveBeenCalled();
|
||||
fixture.detectChanges();
|
||||
expect (htmlElement.querySelector(".toggle-btn")?.querySelector("mat-icon")?.innerHTML).toContain("chevron_right");
|
||||
});
|
||||
|
||||
it("check that dependency icons show", () => {
|
||||
fixture.detectChanges();
|
||||
const dependencyIcons = htmlElement.querySelectorAll(".dep-icon");
|
||||
expect(dependencyIcons).toBeTruthy();
|
||||
expect(dependencyIcons.length).toBe(2);
|
||||
});
|
||||
|
||||
it("check that title is displayed", () => {
|
||||
component.title = "Surface Flinger, Window Manager";
|
||||
fixture.detectChanges();
|
||||
const title = htmlElement.querySelector(".trace-card-title-text");
|
||||
expect(title).toBeTruthy();
|
||||
expect(title?.innerHTML).toContain("Surface Flinger");
|
||||
expect(title?.innerHTML).toContain("Window Manager");
|
||||
});
|
||||
|
||||
it("check that save button is displayed", () => {
|
||||
fixture.detectChanges();
|
||||
const saveButton = htmlElement.querySelectorAll(".save-btn");
|
||||
expect(saveButton).toBeTruthy();
|
||||
});
|
||||
|
||||
it("check that clicking save button emits", async () => {
|
||||
spyOn(component, "saveTraces").and.callThrough();
|
||||
spyOn(component.saveTraceChange, "emit");
|
||||
const button: HTMLButtonElement | null = htmlElement.querySelector(".save-btn");
|
||||
expect(button).toBeInstanceOf(HTMLButtonElement);
|
||||
button?.dispatchEvent(new Event("click"));
|
||||
await fixture.whenStable();
|
||||
expect(component.saveTraces).toHaveBeenCalled();
|
||||
expect(component.saveTraceChange.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("check that screenshot button is displayed", () => {
|
||||
fixture.detectChanges();
|
||||
const screenshotButton = htmlElement.querySelectorAll("#screenshot-btn");
|
||||
expect(screenshotButton).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from "@angular/core";
|
||||
import { TRACE_INFO } from "../trace_info";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
import html2canvas from "html2canvas";
|
||||
|
||||
@Component({
|
||||
selector: "trace-view-header",
|
||||
template: `
|
||||
<button class="icon-button toggle-btn" (click)="toggleView()">
|
||||
<mat-icon aria-hidden="true">
|
||||
{{ showTrace ? "arrow_drop_down" : "chevron_right" }}
|
||||
</mat-icon>
|
||||
</button>
|
||||
<mat-icon *ngFor="let dep of dependencies" aria-hidden="true" class="icon-button dep-icon">{{TRACE_INFO[dep].icon}}</mat-icon>
|
||||
<span class="trace-card-title-text">
|
||||
{{title}}
|
||||
</span>
|
||||
<button class="icon-button save-btn" (click)="saveTraces()">
|
||||
<mat-icon aria-hidden="true">save_alt</mat-icon>
|
||||
</button>
|
||||
<button id="screenshot-btn" (click)="takeScreenshot()" class="icon-button">
|
||||
<mat-icon aria-hidden="true">camera_alt</mat-icon>
|
||||
</button>
|
||||
`,
|
||||
styles: [
|
||||
".trace-card-title-text {font: inherit; display: inline-block; vertical-align: middle;}",
|
||||
]
|
||||
})
|
||||
export class TraceViewHeaderComponent {
|
||||
@Input() title?: string;
|
||||
@Input() dependencies?: TraceType[];
|
||||
@Input() showTrace = true;
|
||||
@Input() cardId!: number ;
|
||||
|
||||
@Output() showTraceChange = new EventEmitter<boolean>();
|
||||
@Output() saveTraceChange = new EventEmitter<TraceType[]>();
|
||||
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
|
||||
toggleView() {
|
||||
this.showTrace = !this.showTrace;
|
||||
this.showTraceChange.emit(this.showTrace);
|
||||
}
|
||||
|
||||
public saveTraces() {
|
||||
this.saveTraceChange.emit(this.dependencies);
|
||||
}
|
||||
|
||||
public takeScreenshot() {
|
||||
const el = document.querySelector(`#card-${this.cardId}`);
|
||||
if (el) {
|
||||
html2canvas((el as HTMLElement)).then((canvas) => {
|
||||
const uri = canvas.toDataURL();
|
||||
const filename = "Winscope-Screenshot.png";
|
||||
const link = document.createElement("a");
|
||||
if (typeof link.download === "string") {
|
||||
link.href = uri;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} else {
|
||||
window.open(uri);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { ViewerFactory } from "viewers/viewer_factory";
|
||||
import { LoadedTrace } from "app/loaded_trace";
|
||||
import { TRACE_INFO } from "./trace_info";
|
||||
import { TimestampUtils } from "common/trace/timestamp_utils";
|
||||
import { FileUtils } from "common/utils/file_utils";
|
||||
|
||||
class TraceCoordinator {
|
||||
private parsers: Parser[];
|
||||
@@ -132,25 +133,27 @@ class TraceCoordinator {
|
||||
setTraces.dataReady = false;
|
||||
}
|
||||
|
||||
saveTraces(traceTypes: TraceType[]) {
|
||||
const blobs: Blob[] = [];
|
||||
traceTypes.forEach(type => {
|
||||
const trace = this.findParser(type)?.getTrace();
|
||||
getTraceTypeBlobMap(): Map<TraceType, Blob> {
|
||||
const traceTypeBlobMap: Map<TraceType, Blob> = new Map();
|
||||
this.parsers.forEach(parser => {
|
||||
const type = parser.getTraceType();
|
||||
const trace = parser.getTrace();
|
||||
if (trace) {
|
||||
blobs.push(trace);
|
||||
traceTypeBlobMap.set(type, trace);
|
||||
}
|
||||
});
|
||||
blobs.forEach((blob, idx) => {
|
||||
const a = document.createElement("a");
|
||||
document.body.appendChild(a);
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = (blob as any).name ?? `${TRACE_INFO[traceTypes[idx]].name}.pb`;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
});
|
||||
return traceTypeBlobMap;
|
||||
}
|
||||
|
||||
async saveTracesAsZip(): Promise<Blob> {
|
||||
const traceTypeBlobMap: Map<TraceType, Blob> = this.getTraceTypeBlobMap();
|
||||
const fileNameBlobMap = new Map<string, Blob>();
|
||||
for (const [traceType, blob] of traceTypeBlobMap.entries()) {
|
||||
fileNameBlobMap.set(TRACE_INFO[traceType].name, blob);
|
||||
}
|
||||
|
||||
return await FileUtils.createZipArchive(fileNameBlobMap);
|
||||
}
|
||||
}
|
||||
|
||||
export { TraceCoordinator };
|
||||
export { TraceCoordinator };
|
||||
|
||||
30
tools/winscope-ng/src/common/utils/file_utils.ts
Normal file
30
tools/winscope-ng/src/common/utils/file_utils.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 JSZip from "jszip";
|
||||
|
||||
class FileUtils {
|
||||
static async createZipArchive(fileNameBlobMap: Map<string, Blob>): Promise<Blob> {
|
||||
const zip = new JSZip();
|
||||
for (const [fileName, blob] of fileNameBlobMap.entries()) {
|
||||
const traceFolder = zip.folder(fileName);
|
||||
traceFolder?.file(fileName, blob);
|
||||
}
|
||||
const zipFile = await zip.generateAsync({type: "blob"});
|
||||
return zipFile;
|
||||
}
|
||||
}
|
||||
|
||||
export {FileUtils};
|
||||
@@ -66,7 +66,10 @@ button {
|
||||
}
|
||||
|
||||
.trace-card {
|
||||
border: 1px solid var(--default-border);
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: none;
|
||||
box-shadow: none !important;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
@@ -101,8 +104,8 @@ mat-icon {
|
||||
|
||||
.file-icon {
|
||||
vertical-align: middle;
|
||||
outline: none !important;
|
||||
background: none !important;
|
||||
outline: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
@@ -125,7 +128,7 @@ button.mat-raised-button span {
|
||||
}
|
||||
|
||||
.input {
|
||||
opacity: 0 !important;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
@@ -150,9 +153,6 @@ button.mat-raised-button span {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
background: none;
|
||||
@@ -237,7 +237,7 @@ app-root .white-btn {
|
||||
.mat-slider-thumb-label-text{font-family:"Google Sans", sans-serif}
|
||||
.mat-stepper-vertical,.mat-stepper-horizontal{font-family:"Google Sans", sans-serif}
|
||||
.mat-tab-group{font-family:"Google Sans", sans-serif}
|
||||
.mat-tab-label,.mat-tab-link{font-family:"Google Sans", sans-serif;}
|
||||
.mat-tab-label,.mat-tab-link{font-family:"Google Sans", sans-serif; font-weight: 500;}
|
||||
.mat-toolbar,.mat-toolbar h1,.mat-toolbar h2,.mat-toolbar h3,.mat-toolbar h4,.mat-toolbar h5,.mat-toolbar h6{font-family:"Google Sans", sans-serif;}
|
||||
.mat-tooltip{font-family:"Google Sans", sans-serif;}
|
||||
.mat-list-item{font-family:"Google Sans", sans-serif}
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("Viewer SurfaceFlinger", () => {
|
||||
const loadData = element(by.css(".load-btn"));
|
||||
loadData.click();
|
||||
|
||||
const surfaceFlingerCard: ElementFinder = element(by.css(".trace-card-title-text"));
|
||||
expect(surfaceFlingerCard.getText()).toContain("Surface Flinger");
|
||||
const surfaceFlingerViewer: ElementFinder = element(by.css("viewer-surface-flinger"));
|
||||
expect(surfaceFlingerViewer).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
selector: "hierarchy-view",
|
||||
template: `
|
||||
<mat-card-header class="view-header">
|
||||
<mat-card-title class="title-filter">
|
||||
<div class="title-filter">
|
||||
<span class="hierarchy-title">Hierarchy</span>
|
||||
<mat-form-field class="filter-field">
|
||||
<mat-label>Filter...</mat-label>
|
||||
@@ -36,7 +36,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
name="filter"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</mat-card-title>
|
||||
</div>
|
||||
<div class="view-controls">
|
||||
<mat-checkbox
|
||||
*ngFor="let option of objectKeys(userOptions)"
|
||||
@@ -84,6 +84,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
styles: [
|
||||
`
|
||||
.view-header {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 3.75rem;
|
||||
@@ -96,6 +97,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hierarchy-title {
|
||||
@@ -114,7 +116,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
margin-left: 5px
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.hierarchy-content {
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Layer } from "common/trace/flickerlib/common";
|
||||
selector: "properties-view",
|
||||
template: `
|
||||
<mat-card-header class="view-header">
|
||||
<mat-card-title class="title-filter">
|
||||
<div class="title-filter">
|
||||
<span class="properties-title">Properties</span>
|
||||
<mat-form-field class="filter-field">
|
||||
<mat-label>Filter...</mat-label>
|
||||
@@ -34,7 +34,7 @@ import { Layer } from "common/trace/flickerlib/common";
|
||||
name="filter"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</mat-card-title>
|
||||
</div>
|
||||
<div class="view-controls">
|
||||
<mat-checkbox
|
||||
*ngFor="let option of objectKeys(userOptions)"
|
||||
@@ -80,6 +80,7 @@ import { Layer } from "common/trace/flickerlib/common";
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.properties-title {
|
||||
@@ -90,14 +91,14 @@ import { Layer } from "common/trace/flickerlib/common";
|
||||
font-size: 16px;
|
||||
transform: scale(0.7);
|
||||
right: 0px;
|
||||
position: absolute
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.view-controls {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
margin-left: 5px
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.properties-content{
|
||||
|
||||
@@ -144,7 +144,8 @@ export class CanvasGraphics {
|
||||
this.clearLabelElements();
|
||||
this.rects.forEach(rect => {
|
||||
const mustNotDrawInVisibleView = this.visibleView && !rect.isVisible;
|
||||
const mustNotDrawInXrayViewWithoutVirtualDisplays = !this.visibleView && !this.showVirtualDisplays && rect.isDisplay && rect.isVirtual;
|
||||
const mustNotDrawInXrayViewWithoutVirtualDisplays =
|
||||
!this.visibleView && !this.showVirtualDisplays && rect.isDisplay && rect.isVirtual;
|
||||
if (mustNotDrawInVisibleView || mustNotDrawInXrayViewWithoutVirtualDisplays) {
|
||||
rectCounter++;
|
||||
return;
|
||||
@@ -264,7 +265,7 @@ export class CanvasGraphics {
|
||||
|
||||
//add rectangle label
|
||||
const rectLabelDiv: HTMLElement = document.createElement("div");
|
||||
this.labelElements.push(rectLabelDiv);
|
||||
rectLabelDiv.className = "rect-label";
|
||||
rectLabelDiv.textContent = labelText;
|
||||
rectLabelDiv.style.fontSize = "10px";
|
||||
if (isGrey) {
|
||||
@@ -275,6 +276,7 @@ export class CanvasGraphics {
|
||||
|
||||
const textCanvas = document.createElement("canvas");
|
||||
const labelContext = textCanvas.getContext("2d");
|
||||
|
||||
let labelWidth = 0;
|
||||
if (labelContext?.font) {
|
||||
labelContext.font = rectLabelDiv.style.font;
|
||||
@@ -288,7 +290,9 @@ export class CanvasGraphics {
|
||||
);
|
||||
} else {
|
||||
rectLabel.position.set(
|
||||
endPos.x - labelWidth * this.labelXFactor, endPos.y - this.labelShift * labelWidth * this.labelXFactor, endPos.z
|
||||
endPos.x - labelWidth * this.labelXFactor,
|
||||
endPos.y - this.labelShift * labelWidth * this.labelXFactor,
|
||||
endPos.z
|
||||
);
|
||||
}
|
||||
|
||||
@@ -355,7 +359,7 @@ export class CanvasGraphics {
|
||||
}
|
||||
|
||||
clearLabelElements() {
|
||||
this.labelElements.forEach(el => el.remove());
|
||||
document.querySelectorAll(".rect-label").forEach(el => el.remove());
|
||||
}
|
||||
|
||||
updateZoom(isZoomIn: boolean) {
|
||||
@@ -414,7 +418,6 @@ export class CanvasGraphics {
|
||||
private highlightedItems: Array<string> = [];
|
||||
private camera: THREE.OrthographicCamera;
|
||||
private rects: Rectangle[] = [];
|
||||
private labelElements: HTMLElement[] = [];
|
||||
private targetObjects: any[] = [];
|
||||
private canvas?: HTMLCanvasElement;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import { ViewerEvents } from "viewers/common/viewer_events";
|
||||
selector: "rects-view",
|
||||
template: `
|
||||
<mat-card-header class="view-controls">
|
||||
<mat-card-title><span>Layers</span></mat-card-title>
|
||||
<div class="rects-title">
|
||||
<span>Layers</span>
|
||||
</div>
|
||||
<div class="top-view-controls">
|
||||
<div class="top-view-controls">
|
||||
<mat-checkbox
|
||||
@@ -89,37 +91,104 @@ import { ViewerEvents } from "viewers/common/viewer_events";
|
||||
</mat-card-content>
|
||||
`,
|
||||
styles: [
|
||||
"@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}",
|
||||
".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}",
|
||||
".slider.spacing {float: right}",
|
||||
".slider span, .slider mat-slider { display: block; padding-left: 0px; padding-top: 0px; font-weight: bold}",
|
||||
".top-view-controls {height: 3rem; width: 100%; position: relative; display: inline-block; vertical-align: middle;}",
|
||||
".zoom-container {position: relative; vertical-align: middle; float: right}",
|
||||
".zoom-btn {position:relative; display: inline-flex; background: none; border: none; padding: 0}",
|
||||
"mat-card-title {font-size: 16px !important; font-weight: medium; font-family: inherit;}",
|
||||
":host /deep/ .mat-card-header-text {width: 100%; margin: 0;}",
|
||||
"mat-radio-group {vertical-align: middle}",
|
||||
"mat-radio-button {font-size: 16px; font-weight: normal}",
|
||||
".mat-radio-button, .mat-radio-button-frame {transform: scale(0.8);}",
|
||||
".rects-checkbox {font-size: 14px; font-weight: normal}",
|
||||
"mat-icon {margin: 5px}",
|
||||
"mat-checkbox {margin-left: 5px;}",
|
||||
".mat-checkbox .mat-checkbox-frame { transform: scale(0.7);}",
|
||||
".mat-checkbox-checked .mat-checkbox-background {transform: scale(0.7);}",
|
||||
".mat-checkbox-indeterminate .mat-checkbox-background {transform: scale(0.7);}",
|
||||
".slider-label {position: absolute; top: 0}",
|
||||
".control-item {position: relative; display: inline-block;vertical-align: middle;align-items: center;}"
|
||||
`
|
||||
@import 'https://fonts.googleapis.com/icon?family=Material+Icons';
|
||||
|
||||
:host /deep/ .mat-card-header-text {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.rects-title {
|
||||
font-size: 16px;
|
||||
font-weight: medium;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.rects-content {
|
||||
position: relative;
|
||||
}
|
||||
.canvas-container {
|
||||
height: 40rem;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.labels-canvas, .rects-canvas {
|
||||
height: 40rem;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
}
|
||||
.rects-canvas {
|
||||
cursor: pointer;
|
||||
}
|
||||
.view-controls {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
min-height: 4rem;
|
||||
width: 100%;
|
||||
}
|
||||
.slider-view-controls, .top-view-controls {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
.top-view-controls {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.slider {
|
||||
display: inline-block;
|
||||
}
|
||||
.slider-label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.slider.spacing {
|
||||
float: right;
|
||||
}
|
||||
.slider span, .slider mat-slider {
|
||||
display: block;
|
||||
padding-left: 0px;
|
||||
padding-top: 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.zoom-container {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
float: right;
|
||||
}
|
||||
.zoom-btn {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.rects-checkbox {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin-left: 5px;
|
||||
}
|
||||
mat-icon {
|
||||
margin: 5px
|
||||
}
|
||||
.mat-checkbox .mat-checkbox-frame, .mat-checkbox-checked .mat-checkbox-background, .mat-checkbox-indeterminate .mat-checkbox-background {
|
||||
transform: scale(0.7);
|
||||
}
|
||||
.control-item {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
align-items: center;
|
||||
}
|
||||
`
|
||||
]
|
||||
})
|
||||
|
||||
export class RectsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() rects!: Rectangle[];
|
||||
@Input() forceRefresh = false;
|
||||
@Input() displayIds: Array<number> = [];
|
||||
@Input() highlightedItems: Array<string> = [];
|
||||
|
||||
@@ -143,9 +212,8 @@ export class RectsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
if (changes["highlightedItems"]) {
|
||||
this.canvasGraphics.updateHighlightedItems(this.highlightedItems);
|
||||
}
|
||||
if (this.rects.length > 0) {
|
||||
if (this.rects.length > 0 || changes["forceRefresh"]?.currentValue) {
|
||||
//change in rects so they must undergo transformation and scaling before canvas refreshed
|
||||
this.canvasGraphics.clearLabelElements();
|
||||
this.rects = this.rects.filter(rect => rect.isVisible || rect.isDisplay);
|
||||
this.displayRects = this.rects.filter(rect => rect.isDisplay);
|
||||
this.computeBounds();
|
||||
@@ -217,7 +285,6 @@ export class RectsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
onChangeView(visible: boolean) {
|
||||
this.canvasGraphics.updateVisibleView(visible);
|
||||
this.canvasGraphics.clearLabelElements();
|
||||
this.refreshCanvas();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
import {
|
||||
Component,
|
||||
Input
|
||||
Input,
|
||||
SimpleChanges
|
||||
} from "@angular/core";
|
||||
import { UiData } from "./ui_data";
|
||||
import { TRACE_INFO } from "app/trace_info";
|
||||
@@ -32,6 +33,7 @@ import { PersistentStore } from "common/persistent_store";
|
||||
[displayIds]="inputData?.displayIds ?? []"
|
||||
[highlightedItems]="inputData?.highlightedItems ?? []"
|
||||
[displayIds]="inputData?.displayIds ?? []"
|
||||
[forceRefresh]="active"
|
||||
></rects-view>
|
||||
</mat-card>
|
||||
<div fxLayout="row wrap" fxLayoutGap="10px grid" class="card-grid">
|
||||
@@ -96,15 +98,16 @@ import { PersistentStore } from "common/persistent_store";
|
||||
|
||||
.rects-view {
|
||||
font: inherit;
|
||||
flex: none !important;
|
||||
flex: none;
|
||||
width: 350px;
|
||||
height: 52.5rem;
|
||||
margin: 0px;
|
||||
border: 1px solid var(--default-border);
|
||||
border-top: 1px solid var(--default-border);
|
||||
border-right: 1px solid var(--default-border);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.hierarchy-view, .properties-view {
|
||||
.hierarchy-view {
|
||||
font: inherit;
|
||||
margin: 0px;
|
||||
width: 50%;
|
||||
@@ -112,7 +115,17 @@ import { PersistentStore } from "common/persistent_store";
|
||||
border-radius: 0;
|
||||
border-top: 1px solid var(--default-border);
|
||||
border-right: 1px solid var(--default-border);
|
||||
border-bottom: 1px solid var(--default-border);
|
||||
border-left: 1px solid var(--default-border);
|
||||
}
|
||||
|
||||
.properties-view {
|
||||
font: inherit;
|
||||
margin: 0px;
|
||||
width: 50%;
|
||||
height: 52.5rem;
|
||||
border-radius: 0;
|
||||
border-top: 1px solid var(--default-border);
|
||||
border-left: 1px solid var(--default-border);
|
||||
}
|
||||
`,
|
||||
]
|
||||
@@ -120,6 +133,7 @@ import { PersistentStore } from "common/persistent_store";
|
||||
export class ViewerSurfaceFlingerComponent {
|
||||
@Input() inputData?: UiData;
|
||||
@Input() store: PersistentStore = new PersistentStore();
|
||||
@Input() active = false;
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
TraceType = TraceType;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user