• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import {element} from "../../../../base-ui/BaseElement.js";
17import {TimeRange} from "../timer-shaft/RangeRuler.js";
18import '../../../../base-ui/icon/LitIcon.js'
19import {Rect} from "../timer-shaft/Rect.js";
20import {BaseStruct} from "../../../bean/BaseStruct.js";
21import {SpSystemTrace} from "../../SpSystemTrace.js";
22import {ns2x} from "../TimerShaftElement.js";
23import {TraceRowObject} from "./TraceRowObject.js";
24import {LitCheckBox} from "../../../../base-ui/checkbox/LitCheckBox.js";
25import {LitIcon} from "../../../../base-ui/icon/LitIcon";
26import "../../../../base-ui/popover/LitPopoverV.js"
27import {LitPopover} from "../../../../base-ui/popover/LitPopoverV.js";
28import {info} from "../../../../log/Log.js";
29import {ColorUtils} from "./ColorUtils.js";
30
31export class RangeSelectStruct {
32    startX: number | undefined
33    endX: number | undefined
34    startNS: number | undefined
35    endNS: number | undefined
36}
37
38let collectList: Array<any> = [];
39let rowDragElement: EventTarget | undefined | null;
40let dragDirection: string = "";
41
42@element('trace-row')
43export class TraceRow<T extends BaseStruct> extends HTMLElement {
44    static ROW_TYPE_CPU = "cpu-data"
45    static ROW_TYPE_CPU_STATE = "cpu-state"
46    static ROW_TYPE_CPU_FREQ = "cpu-freq"
47    static ROW_TYPE_CPU_FREQ_LIMIT = "cpu-limit-freq"
48    static ROW_TYPE_FPS = "fps"
49    static ROW_TYPE_NATIVE_MEMORY = "native-memory"
50    static ROW_TYPE_HIPERF = "hiperf"
51    static ROW_TYPE_HIPERF_CPU = "hiperf-cpu"
52    static ROW_TYPE_HIPERF_PROCESS = "hiperf-process"
53    static ROW_TYPE_HIPERF_THREAD = "hiperf-thread"
54    static ROW_TYPE_HIPERF_REPORT = "hiperf-report"
55    static ROW_TYPE_HIPERF_EVENT = "hiperf-event"
56    static ROW_TYPE_PROCESS = "process"
57    static ROW_TYPE_THREAD = "thread"
58    static ROW_TYPE_MEM = "mem"
59    static ROW_TYPE_VIRTUAL_MEMORY_GROUP = "virtual-memory-group"
60    static ROW_TYPE_VIRTUAL_MEMORY = "virtual-memory-cell"
61    static ROW_TYPE_FILE_SYSTEM_GROUP = "file-system-group"
62    static ROW_TYPE_FILE_SYSTEM = "file-system-cell"
63    static ROW_TYPE_HEAP = "heap"
64    static ROW_TYPE_FUNC = "func"
65    static ROW_TYPE_MONITOR = "ability-monitor"
66    static ROW_TYPE_CPU_ABILITY = "cpu-ability"
67    static ROW_TYPE_MEMORY_ABILITY = "memory-ability"
68    static ROW_TYPE_DISK_ABILITY = "disk-ability"
69    static ROW_TYPE_NETWORK_ABILITY = "network-ability"
70    static ROW_TYPE_SDK = "sdk"
71    static ROW_TYPE_SDK_COUNTER = "sdk-counter"
72    static ROW_TYPE_SDK_SLICE = "sdk-slice"
73    static ROW_TYPE_ENERGY = "energy"
74    static ROW_TYPE_ANOMALY_ENERGY = "anomaly-energy"
75    static ROW_TYPE_SYSTEM_ENERGY = "system-energy"
76    static ROW_TYPE_POWER_ENERGY = "power-energy"
77    static ROW_TYPE_STATE_ENERGY = "state-energy"
78    static ROW_TYPE_SMAPS = "smaps"
79    static range: TimeRange | undefined | null;
80    static rangeSelectObject: RangeSelectStruct | undefined
81    public obj: TraceRowObject<any> | undefined | null;
82    isHover: boolean = false;
83    hoverX: number = 0;
84    hoverY: number = 0;
85    index: number = 0;
86    public must: boolean = false;
87    public isTransferCanvas = false;
88    onComplete: Function | undefined;
89    isComplete: boolean = false;
90    public dataList: undefined | Array<T>;
91    public describeEl: HTMLElement | null | undefined;
92    public canvas: Array<HTMLCanvasElement> = [];
93    public canvasContainer: HTMLDivElement | null | undefined;
94    public tipEL: HTMLDivElement | null | undefined;
95    public checkBoxEL: LitCheckBox | null | undefined;
96    public collectEL: LitIcon | null | undefined;
97    public onThreadHandler: ((useCache: boolean, buf: ArrayBuffer | undefined | null) => void) | undefined | null
98    public onDrawTypeChangeHandler: ((type: number) => void) | undefined | null
99    public supplier: (() => Promise<Array<T>>) | undefined | null
100    public favoriteChangeHandler: ((fav: TraceRow<any>) => void) | undefined | null
101    public selectChangeHandler: ((list: Array<TraceRow<any>>) => void) | undefined | null
102    dpr = window.devicePixelRatio || 1;
103    // @ts-ignore
104    offscreen: Array<OffscreenCanvas | undefined> = [];
105    canvasWidth = 0
106    canvasHeight = 0
107    public _frame: Rect | undefined;
108    public isLoading: boolean = false
109    public readonly args: any;
110    private rootEL: HTMLDivElement | null | undefined;
111    private nameEL: HTMLLabelElement | null | undefined;
112    private _rangeSelect: boolean = false;
113    private _drawType: number = 0
114    private folderIconEL: LitIcon | null | undefined;
115    online: boolean = false;
116    static isUserInteraction: boolean;
117    asyncFuncName: string | undefined | null;
118    asyncFuncNamePID: number | undefined | null;
119
120    constructor(args: { canvasNumber: number, alpha: boolean, contextId: string, isOffScreen: boolean, skeleton?: boolean } = {
121        canvasNumber: 1, alpha: false, contextId: "2d", isOffScreen: true, skeleton: false
122    }) {
123        super();
124        this.args = args;
125        this.attachShadow({mode: 'open'}).innerHTML = this.initHtml();
126        this.initElements();
127    }
128
129   static skeleton<T extends BaseStruct>(): TraceRow<T> {
130        let tr= new TraceRow<T>({
131            alpha: false, canvasNumber: 0, contextId: "", isOffScreen: false, skeleton: true
132        });
133        tr.isTransferCanvas=true;
134        return tr;
135    }
136
137    static get observedAttributes() {
138        return ["folder", "name", "expansion", "children", "height", "row-type", "row-id", "row-parent-id", "sleeping",
139            "check-type",
140            "collect-type",
141            "disabled-check",
142            "row-discard",
143        ];
144    }
145
146    get rowDiscard(): boolean {
147        return this.hasAttribute("row-discard");
148    }
149
150    set rowDiscard(value: boolean) {
151        if (value) {
152            this.setAttribute("row-discard", "")
153            this.style.display = "none";
154        } else {
155            this.removeAttribute("row-discard")
156            this.style.display = "block";
157        }
158    }
159
160    get collect() {
161        return this.hasAttribute("collect-type")
162    }
163
164    set collect(value) {
165        if (value) {
166            this.setAttribute("collect-type", "")
167        } else {
168            this.removeAttribute("collect-type")
169        }
170    }
171
172    get rangeSelect(): boolean {
173        return this._rangeSelect;
174    }
175
176    set rangeSelect(value: boolean) {
177        this._rangeSelect = value;
178    }
179
180    get sleeping(): boolean {
181        return this.hasAttribute("sleeping");
182    }
183
184    set sleeping(value: boolean) {
185        if (value) {
186            this.setAttribute("sleeping", "")
187        } else {
188            this.removeAttribute("sleeping")
189            this.draw();
190        }
191    }
192
193    get rowType(): string | undefined | null {
194        return this.getAttribute("row-type");
195    }
196
197    set rowType(val) {
198        this.setAttribute("row-type", val || "")
199    }
200
201    get rowId(): string | undefined | null {
202        return this.getAttribute("row-id");
203    }
204
205    set rowId(val) {
206        this.setAttribute("row-id", val || "")
207    }
208
209    get rowParentId(): string | undefined | null {
210        return this.getAttribute("row-parent-id");
211    }
212
213    set rowParentId(val) {
214        this.setAttribute("row-parent-id", val || "")
215    }
216
217    set rowHidden(val: boolean) {
218        if (val) {
219            this.setAttribute("row-hidden", "")
220        } else {
221            this.removeAttribute("row-hidden")
222        }
223    }
224
225    get name(): string {
226        return this.getAttribute("name") || ""
227    }
228
229    set name(value: string) {
230        this.setAttribute("name", value)
231    }
232
233    get folder(): boolean {
234        return this.hasAttribute("folder");
235    }
236
237    set folder(value: boolean) {
238        if (value) {
239            this.setAttribute("folder", '')
240        } else {
241            this.removeAttribute('folder')
242        }
243    }
244
245    get expansion(): boolean {
246        return this.hasAttribute("expansion")
247    }
248
249    set expansion(value) {
250        if (value) {
251            this.setAttribute("expansion", '');
252        } else {
253            this.removeAttribute('expansion')
254        }
255        this.parentElement?.querySelectorAll<TraceRow<any>>(`trace-row[row-parent-id='${this.rowId}']`).forEach(it => {
256            if (!it.collect) {
257                it.rowHidden = !this.expansion;
258            }
259            if (it.folder && !value && it.expansion) {
260                it.expansion = value;
261            }
262        })
263        this.dispatchEvent(new CustomEvent("expansion-change", {
264            detail: {
265                expansion: this.expansion,
266                rowType: this.rowType,
267                rowId: this.rowId,
268                rowParentId: this.rowParentId
269            }
270        }))
271    }
272
273    set tip(value: string) {
274        if (this.tipEL) {
275            this.tipEL.innerHTML = value;
276        }
277    }
278
279    get frame(): Rect | any {
280        let cHeight = 0;
281        this.canvas.forEach(it => {
282            cHeight += (it?.clientHeight || 40);
283        })
284        if(cHeight == 0) cHeight = 40;
285        if (this._frame) {
286            this._frame.width = (this.parentElement?.clientWidth || 0) - 248 - SpSystemTrace.scrollViewWidth;
287            this._frame.height = cHeight;
288            return this._frame;
289        } else {
290            this._frame = new Rect(0, 0, (this.parentElement?.clientWidth || 0) - 248 - SpSystemTrace.scrollViewWidth, cHeight);
291            return this._frame;
292        }
293    }
294
295    set frame(f: Rect) {
296        this._frame = f;
297    }
298
299    get disabledCheck(): boolean {
300        return this.hasAttribute("disabled-check");
301    }
302
303    set disabledCheck(value: boolean) {
304        if (value) {
305            this.setAttribute("disabled-check", '')
306            this.checkBoxEL!.style.display = "none";
307        } else {
308            this.removeAttribute('disabled-check')
309            this.checkBoxEL!.style.display = "flex";
310        }
311    }
312
313    get checkType(): string {
314        return this.getAttribute("check-type") || "";
315    }
316
317    set checkType(value: string) {
318        if (!value || value.length == 0) {
319            this.removeAttribute("check-type");
320            return;
321        }
322        this.setAttribute("check-type", value);
323        if (this.hasAttribute("disabled-check")) {
324            this.checkBoxEL!.style.display = "none";
325            return;
326        }
327        switch (value) {
328            case "-1":
329                this.checkBoxEL!.style.display = "none";
330                this.rangeSelect = false;
331                break;
332            case "0":
333                this.checkBoxEL!.style.display = "flex";
334                this.checkBoxEL!.checked = false;
335                this.checkBoxEL!.indeterminate = false;
336                this.rangeSelect = false;
337                break;
338            case "1":
339                this.checkBoxEL!.style.display = "flex";
340                this.checkBoxEL!.checked = false
341                this.checkBoxEL!.indeterminate = true;
342                this.rangeSelect = false;
343                break;
344            case "2":
345                this.rangeSelect = true;
346                this.checkBoxEL!.style.display = "flex";
347                this.checkBoxEL!.checked = true;
348                this.checkBoxEL!.indeterminate = false;
349                break;
350        }
351    }
352
353    get drawType(): number {
354        return this._drawType;
355    }
356
357    set drawType(value: number) {
358        this._drawType = value;
359        let radioList: NodeListOf<any> = this.shadowRoot!.querySelectorAll("input[type=radio][name=status]")
360        if (radioList!.length > 0) {
361            radioList[Number(value)].checked = true
362        }
363    }
364
365    get highlight(): boolean {
366        return this.hasAttribute("expansion");
367    }
368
369    set highlight(value: boolean) {
370        if (value) {
371            this.setAttribute("highlight", '')
372        } else {
373            this.removeAttribute('highlight')
374        }
375    }
376
377    set folderPaddingLeft(value: number) {
378        this.folderIconEL!.style.marginLeft = value + "px";
379    }
380
381    initElements(): void {
382        this.rootEL = this.shadowRoot?.querySelector('.root')
383        this.checkBoxEL = this.shadowRoot?.querySelector<LitCheckBox>('.lit-check-box')
384        this.collectEL = this.shadowRoot?.querySelector<LitIcon>('.collect')
385        this.describeEl = this.shadowRoot?.querySelector('.describe')
386        this.folderIconEL = this.shadowRoot?.querySelector<LitIcon>('.icon')
387        this.nameEL = this.shadowRoot?.querySelector('.name')
388        this.canvasContainer = this.shadowRoot?.querySelector('.panel-container')
389        this.tipEL = this.shadowRoot?.querySelector('.tip')
390        let canvasNumber = this.args["canvasNumber"];
391        if (!this.args["skeleton"]) {
392            for (let i = 0; i < canvasNumber; i++) {
393                let canvas = document.createElement('canvas');
394                canvas.className = "panel";
395                this.canvas.push(canvas);
396                this.canvasContainer!.appendChild(canvas);
397            }
398        }
399        this.describeEl?.addEventListener('click', () => {
400            if (this.folder) {
401                this.expansion = !this.expansion
402            }
403        })
404    }
405
406    initCanvas(list: Array<HTMLCanvasElement>): void {
407        let timerShaftEL = document!.querySelector("body > sp-application")!.shadowRoot!.querySelector("#sp-system-trace")!.shadowRoot!.querySelector("div > timer-shaft-element");
408        let timerShaftCanvas = timerShaftEL!.shadowRoot!.querySelector<HTMLCanvasElement>("canvas");
409        let tempHeight: number = 0;
410        if (this.rowType == TraceRow.ROW_TYPE_FUNC) {
411            tempHeight = 20;
412        } else if (this.rowType == TraceRow.ROW_TYPE_THREAD) {
413            tempHeight = 30;
414        } else if (this.rowType == TraceRow.ROW_TYPE_SYSTEM_ENERGY) {
415            tempHeight = 90;
416        } else if (this.rowType == TraceRow.ROW_TYPE_POWER_ENERGY) {
417            tempHeight = 200;
418        } else if (this.rowType == TraceRow.ROW_TYPE_ANOMALY_ENERGY) {
419            tempHeight = 55;
420        } else {
421            tempHeight = 40;
422        }
423        this.dpr = window.devicePixelRatio || 1;
424        list.forEach((canvas, i) => {
425            this.rootEL!.style.height = `${this.getAttribute("height") || '40'}px`
426            canvas.style.width = timerShaftCanvas!.style.width;
427            canvas.style.height = tempHeight + 'px';
428            this.canvasWidth = timerShaftCanvas!.width;
429            this.canvasHeight = Math.ceil(tempHeight * this.dpr);
430            canvas.width = this.canvasWidth;
431            canvas.height = this.canvasHeight;
432            // @ts-ignore
433            this.offscreen.push(canvas!.transferControlToOffscreen());
434        })
435    }
436
437    updateWidth(width: number) {
438        this.dpr = window.devicePixelRatio || 1;
439        let tempHeight: number = 0;
440        if (this.rowType == TraceRow.ROW_TYPE_FUNC) {
441            tempHeight = 20;
442        } else if (this.rowType == TraceRow.ROW_TYPE_THREAD) {
443            tempHeight = 30;
444        } else if (this.rowType == TraceRow.ROW_TYPE_SYSTEM_ENERGY) {
445            tempHeight = 90;
446        } else if (this.rowType == TraceRow.ROW_TYPE_POWER_ENERGY) {
447            tempHeight = 200;
448        } else if (this.rowType == TraceRow.ROW_TYPE_ANOMALY_ENERGY) {
449            tempHeight = 55;
450        } else {
451            tempHeight = 40;
452        }
453        if (this.canvas.length > 1) {
454            tempHeight = 20;
455        }
456        this.canvas.forEach(it => {
457            this.canvasWidth = Math.ceil((width - (this.describeEl?.clientWidth || 248)) * this.dpr);
458            this.canvasHeight = Math.ceil(tempHeight * this.dpr);
459            it!.style.width = (width - (this.describeEl?.clientWidth || 248)) + 'px';
460            if (this.args.isOffScreen) {
461                this.draw(true);
462            }
463        })
464    }
465
466    drawLine(item: HTMLDivElement, direction: string/*string[top|bottom]*/) {
467        if (!item) return;
468        switch (direction) {
469            case "top":
470                item.classList.remove("line-bottom");
471                item.classList.add("line-top");
472                break;
473            case "bottom":
474                item.classList.remove("line-top");
475                item.classList.add("line-bottom");
476                break;
477            case "":
478                item.classList.remove("line-top");
479                item.classList.remove("line-bottom");
480                break;
481        }
482    }
483
484    connectedCallback() {
485        this.checkBoxEL!.onchange = (ev: any) => {
486            info("checkBoxEL onchange ");
487            if (!ev.target.checked) {
488                info("checkBoxEL target not checked");
489                this.rangeSelect = false;
490                this.checkType = "0"
491                this.draw();
492            } else {
493                this.rangeSelect = true;
494                this.checkType = "2"
495                this.draw();
496            }
497            this.setCheckBox(ev.target.checked);
498        }
499        this.describeEl!.ondragstart = (ev: DragEvent) => this.rowDragstart(ev);
500        this.describeEl!.ondragleave = (ev: any) => {
501            this.drawLine(ev.currentTarget, '');
502            return undefined;
503        }
504        this.describeEl!.ondragend = (ev: any) => {
505            rowDragElement = null;
506            ev.target.classList.remove("drag")
507            this.drawLine(ev.currentTarget, '');
508            return undefined;
509        }
510        this.describeEl!.ondragover = (ev: any) => {
511            if (!this.collect) return;
512            if (rowDragElement === this) return;
513            let rect = ev.currentTarget.getBoundingClientRect();
514            if (ev.clientY >= rect.top && ev.clientY < rect.top + rect.height / 2) {//上面
515                dragDirection = 'top';
516                this.drawLine(ev.currentTarget, 'top');
517            } else if (ev.clientY <= rect.bottom && ev.clientY > rect.top + rect.height / 2) {//下面
518                dragDirection = 'bottom';
519                this.drawLine(ev.currentTarget, 'bottom');
520            }
521            return undefined;
522        }
523        this.describeEl!.ondrop = (ev: any) => {
524            if (!this.collect) return;
525            this.drawLine(ev.currentTarget, '');
526            let spacer = this.parentElement!.previousElementSibling! as HTMLDivElement;
527            let startDragNode = collectList.findIndex((it) => it === rowDragElement);
528            let endDragNode = collectList.findIndex((it) => it === this);
529            if (startDragNode === -1 || endDragNode === -1) return;
530            if (startDragNode < endDragNode && dragDirection === "top") {
531                endDragNode--;
532            } else if (startDragNode > endDragNode && dragDirection === "bottom") {
533                endDragNode++;
534            }
535            collectList.splice(endDragNode, 0, ...collectList.splice(startDragNode, 1))
536            collectList.forEach((it, i) => {
537                if (i == 0) {
538                    it.style.top = `${spacer.offsetTop + 48}px`;
539                } else {
540                    it.style.top = `${collectList[i - 1].offsetTop + collectList[i - 1].offsetHeight}px`;
541                }
542            })
543        }
544        this.collectEL!.onclick = (e) => {
545            this.collect = !this.collect;
546            if (this.collect) {
547                this.describeEl!.draggable = true;
548            } else {
549                this.describeEl!.draggable = false;
550            }
551            let spacer = this.parentElement!.previousElementSibling! as HTMLDivElement;
552            if (this.collect) {
553                let nodeList = this.parentElement!.querySelectorAll<TraceRow<any>>(`trace-row[collect-type]`);
554                if (nodeList.length == 1) collectList = [];
555                collectList.push(this);
556                spacer.style.height = `${spacer.offsetHeight + this.offsetHeight!}px`;
557            } else {
558                collectList.splice(collectList.findIndex((it) => it === this), 1);
559                spacer.style.height = `${spacer.offsetHeight - this.offsetHeight!}px`;
560                let parent = this.parentElement!.querySelector<TraceRow<any>>(`trace-row[row-id='${this.rowParentId}']`);
561                if (parent) {
562                    this.rowHidden = !parent.expansion;
563                }
564            }
565            collectList.forEach((it, i) => {
566                if (i == 0) {
567                    it.style.top = `${spacer.offsetTop + 48}px`;
568                } else {
569                    it.style.top = `${collectList[i - 1].offsetTop + collectList[i - 1].offsetHeight}px`;
570                }
571            })
572            this.favoriteChangeHandler?.(this)
573        }
574        if (!this.args["skeleton"]) {
575            this.initCanvas(this.canvas);
576        }
577        let radioList = this.shadowRoot!.querySelectorAll("input[type=radio][name=status]")
578        let popover = this.shadowRoot!.querySelector<LitPopover>(".popover")
579        this.shadowRoot?.querySelector<HTMLDivElement>("#first-radio")?.addEventListener("click", (e) => {
580            // @ts-ignore
581            radioList[0]!.checked = true;
582            // @ts-ignore
583            popover!.visible = false
584            setTimeout(() => {
585                this.onDrawTypeChangeHandler?.(0);
586            }, 300);
587        })
588        this.shadowRoot?.querySelector<HTMLDivElement>("#second-radio")?.addEventListener('click', (e) => {
589            // @ts-ignore
590            radioList[1]!.checked = true;
591            // @ts-ignore
592            popover!.visible = false
593            setTimeout(() => {
594                this.onDrawTypeChangeHandler?.(1);
595            }, 300);
596        })
597    }
598
599    rowDragstart(ev: any) {
600        rowDragElement = this;
601        ev.target.classList.add("drag")
602    }
603
604    setCheckBox(isCheck: boolean) {
605        if (this.folder) {
606            let allRow = this.parentElement?.querySelectorAll<TraceRow<any>>(`trace-row[row-parent-id='${this.rowId}'][check-type]`)
607            allRow!.forEach((ck) => {
608                ck.setAttribute("check-type", isCheck ? "2" : "0")
609                let allCheck: LitCheckBox | null | undefined = ck?.shadowRoot?.querySelector(".lit-check-box")
610                allCheck!.checked = isCheck
611            })
612        } else if (this.rowParentId == "" && !this.folder) {
613            this.selectChangeHandler?.([...this.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2']")])
614            return;
615        }
616        let checkList = this.parentElement?.querySelectorAll<TraceRow<any>>(`trace-row[row-parent-id='${this.folder ? this.rowId : this.rowParentId}'][check-type="2"]`)
617        let unselectedList = this.parentElement?.querySelectorAll<TraceRow<any>>(`trace-row[row-parent-id='${this.folder ? this.rowId : this.rowParentId}'][check-type="0"]`)
618        let parentRow = this.parentElement?.querySelector<TraceRow<any>>(`trace-row[row-id='${this.folder ? this.rowId : this.rowParentId}'][folder]`)
619        let parentCheck: LitCheckBox | null | undefined = parentRow?.shadowRoot?.querySelector(".lit-check-box")
620
621        if (unselectedList!.length == 0) {
622            parentRow!.setAttribute("check-type", "2")
623            parentCheck!.checked = true
624            parentCheck!.indeterminate = false;
625            checkList?.forEach((it) => {
626                it.checkType = "2";
627                it.rangeSelect = true;
628                it.draw()
629            })
630        } else {
631            parentRow!.setAttribute("check-type", "1")
632            parentCheck!.checked = false
633            parentCheck!.indeterminate = true;
634            checkList?.forEach((it) => {
635                it.checkType = "2";
636                it.rangeSelect = true;
637                it.draw()
638            })
639            unselectedList?.forEach((it) => {
640                it.checkType = "0";
641                it.rangeSelect = false;
642                it.draw()
643            })
644        }
645
646        if (checkList!.length == 0) {
647            parentRow!.setAttribute("check-type", "0")
648            parentCheck!.checked = false
649            parentCheck!.indeterminate = false;
650            unselectedList?.forEach((it) => {
651                it.checkType = "0";
652                it.rangeSelect = false;
653                it.draw()
654            })
655        }
656        this.selectChangeHandler?.([...this.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2']")])
657    }
658
659    onMouseHover(x: number, y: number, tip: boolean = true): T | undefined | null {
660        if (this.tipEL) {
661            this.tipEL.style.display = 'none';
662        }
663        return null;
664    }
665
666    setTipLeft(x: number, struct: any) {
667        if (struct == null && this.tipEL) {
668            this.tipEL.style.display = 'none';
669            return
670        }
671        if (this.tipEL) {
672            this.tipEL.style.display = 'flex';
673            if (x + this.tipEL.clientWidth > (this.canvas[0]!.clientWidth || 0)) {
674                this.tipEL.style.transform = `translateX(${x - this.tipEL.clientWidth - 1}px)`;
675            } else {
676                this.tipEL.style.transform = `translateX(${x}px)`;
677            }
678        }
679    }
680
681    onMouseLeave(x: number, y: number) {
682        if (this.tipEL) {
683            this.tipEL.style.display = 'none';
684        }
685    }
686
687    draw(useCache: boolean = false) {
688        this.dpr = window.devicePixelRatio || 1;
689        if (this.sleeping) {
690            return;
691        }
692        if (this.online) {
693            if (!useCache && !TraceRow.isUserInteraction) {
694                this.supplier?.().then(res => {
695                    this.onThreadHandler?.(useCache, res as any);
696                });
697            }
698            this.onThreadHandler?.(useCache, null);
699            return;
700        }
701        if (!this.isComplete) {
702            if (this.supplier && !this.isLoading) {
703                this.isLoading = true;
704                this.must = true;
705                if (this.supplier) {
706                    let promise = this.supplier();
707                    if (promise) {
708                        promise.then(res => {
709                            this.dataList = res
710                            if (this.onComplete) {
711                                this.onComplete();
712                            }
713                            this.isComplete = true;
714                            this.isLoading = false;
715                            this.draw(false);
716                        })
717                    } else {
718                        this.isLoading = false;
719                        this.draw(false);
720                    }
721                }
722            }
723        } else {
724            if (this.onThreadHandler && this.dataList) {
725                this.onThreadHandler!(useCache, null);
726            }
727        }
728    }
729
730    clearCanvas(ctx: CanvasRenderingContext2D) {
731        if (ctx) {
732            this.canvas.forEach(it => {
733                ctx.clearRect(0, 0, it!.clientWidth || 0, it!.clientHeight || 0)
734            })
735        }
736    }
737
738    drawLines(ctx: CanvasRenderingContext2D) {
739        if (ctx) {
740            ctx.lineWidth = 1;
741            ctx.strokeStyle = this.getLineColor();
742            TraceRow.range?.xs.forEach(it => {
743                ctx.moveTo(Math.floor(it), 0)
744                ctx.lineTo(Math.floor(it), this.shadowRoot?.host.clientHeight || 0)
745            })
746            ctx.stroke();
747        }
748    }
749
750    getLineColor() {
751        return window.getComputedStyle(this.rootEL!, null).getPropertyValue("border-bottom-color")
752    }
753
754    drawSelection(ctx: CanvasRenderingContext2D) {
755        if (this.rangeSelect) {
756            TraceRow.rangeSelectObject!.startX = Math.floor(ns2x(TraceRow.rangeSelectObject!.startNS!, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS!, this.frame));
757            TraceRow.rangeSelectObject!.endX = Math.floor(ns2x(TraceRow.rangeSelectObject!.endNS!, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS!, this.frame));
758            if (ctx) {
759                ctx.globalAlpha = 0.5
760                ctx.fillStyle = "#666666"
761                ctx.fillRect(TraceRow.rangeSelectObject!.startX!, this.frame.y, TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!, this.frame.height)
762                ctx.globalAlpha = 1
763            }
764        }
765    }
766
767    isInTimeRange(startTime: number, duration: number): boolean {
768        return ((startTime || 0) + (duration || 0) > (TraceRow.range?.startNS || 0) && (startTime || 0) < (TraceRow.range?.endNS || 0));
769    }
770
771    buildArgs(obj: any) {
772        let result: any = {
773            list: this.must ? this.dataList : undefined,
774            offscreen: !this.isTransferCanvas ? this.offscreen[0] : undefined,//是否离屏
775            dpr: this.dpr,//屏幕dpr值
776            xs: TraceRow.range?.xs,//线条坐标信息
777            isHover: this.isHover,
778            hoverX: this.hoverX,
779            hoverY: this.hoverY,
780            canvasWidth: this.canvasWidth,
781            canvasHeight: this.canvasHeight,
782            isRangeSelect: this.rangeSelect,
783            rangeSelectObject: TraceRow.rangeSelectObject,
784            lineColor: this.getLineColor(),
785            chartColor: ColorUtils.MD_PALETTE[0],
786            startNS: TraceRow.range?.startNS || 0,
787            endNS: TraceRow.range?.endNS || 0,
788            totalNS: TraceRow.range?.totalNS || 0,
789            slicesTime: TraceRow.range?.slicesTime,
790            range: TraceRow.range,
791            frame: this.frame,
792            flagMoveInfo: null,
793            flagSelectedInfo: null,
794            wakeupBean: null
795        };
796        Reflect.ownKeys(obj).forEach(it => {
797            result[it] = obj[it];
798        })
799        return result;
800    }
801
802    getTransferArray() {
803        let tsf = [];
804        if (!this.isTransferCanvas) {
805            tsf.push(this.offscreen[0]);
806        }
807        if (this.must && this.dataList instanceof ArrayBuffer) {
808            tsf.push(this.dataList);
809        }
810        return tsf;
811    }
812
813    attributeChangedCallback(name: string, oldValue: string, newValue: string) {
814        switch (name) {
815            case "name":
816                if (this.nameEL) {
817                    this.nameEL.textContent = newValue;
818                    this.nameEL.title = newValue;
819                }
820                break;
821            case "height":
822                if (newValue != oldValue) {
823                    if (!this.args.isOffScreen) {
824                    }
825                }
826                break;
827            case "check-type":
828                if (newValue === "check") {
829                    this.checkBoxEL?.setAttribute("checked", "");
830                } else {
831                    this.checkBoxEL?.removeAttribute("checked");
832                }
833                break;
834        }
835    }
836
837    initHtml(): string {
838        return `
839        <style>
840        *{
841            box-sizing: border-box;
842        }
843        :host(:not([row-hidden])){
844            box-sizing: border-box;
845            display: block;
846            width: 100%;
847            height: min-content;
848        }
849        :host([row-hidden]){
850            width: 100%;
851            display: none;
852        }
853        .root{
854            height: 40px;
855            width: 100%;
856            display: grid;
857            grid-template-rows: 100%;
858            grid-template-columns: 248px 1fr;
859            border-bottom: 1px solid var(--dark-border1,#dadada);
860            box-sizing: border-box;
861        }
862        .root .drag{
863            background-color: var(--dark-background1,#eee);
864            box-shadow: 0 4px 12px -4px #999 inset;
865        }
866        .root .line-top{
867            box-shadow: 0 4px 2px -1px #4d7ab3 inset;
868            transition: all 0.2s;
869        }
870        .root .line-bottom{
871            box-shadow: 0 -4px 2px -1px #4d7ab3 inset;
872            transition: all 0.2s;
873        }
874        .describe{
875            box-sizing: border-box;
876            border-right: 1px solid var(--dark-border1,#c9d0da);
877            background-color: transparent;
878            align-items: center;
879            position: relative;
880        }
881        .panel{
882            width: 100%;
883            height: 100%;
884            overflow: visible;
885            background-color: transparent;
886            display: block;
887        }
888        .panel-container{
889            width: 100%;
890            position: relative;
891            pointer-events: none;
892        }
893        .tip{
894            position:absolute;
895            top: 0;
896            left: 0;
897            height: 100%;
898            background-color: white;
899            border: 1px solid #f9f9f9;
900            width: auto;
901            font-size: 8px;
902            color: #50809e;
903            flex-direction: column;
904            justify-content: center;
905            align-items: flex-start;
906            padding: 2px 10px;
907            display: none;
908            user-select: none;
909        }
910        .name{
911            color: var(--dark-color1,#4b5766);
912            margin-left: 10px;
913            font-size: .9rem;
914            font-weight: normal;
915            width: 100%;
916            max-height: 100%;
917            text-align: left;
918            overflow: hidden;
919            user-select: none;
920            text-overflow: ellipsis;
921        }
922        :host([highlight]) .name{
923            color: #4b5766;
924        }
925        .icon{
926            color: var(--dark-color1,#151515);
927            margin-left: 10px;
928        }
929        .describe:hover {
930            cursor: pointer;
931        }
932        :host([folder]) .describe:hover > .icon{
933            color:#ecb93f;
934            margin-left: 10px;
935        }
936        :host([folder]){
937            /*background-color: var(--dark-background1,#f5fafb);*/
938        }
939        :host([folder]) .icon{
940            display: flex;
941        }
942        :host(:not([folder])){
943            /*background-color: var(--dark-background,#FFFFFF);*/
944        }
945        :host(:not([folder]):not([children])) {
946        }
947        :host(:not([folder]):not([children])) .icon{
948            display: none;
949        }
950        :host(:not([folder])[children]) .icon{
951            visibility: hidden;
952            color:#fff
953        }
954
955        :host(:not([folder])[children]) .name{
956        }
957        :host([expansion]) {
958            background-color: var(--bark-expansion,#0C65D1);
959        }
960        :host([expansion]) .name,:host([expansion]) .icon{
961            color: #fff;
962        }
963        :host([expansion]) .describe{
964            border-right: 0px;
965        }
966        :host([expansion]:not(sleeping)) .panel-container{
967            display: none;
968        }
969        :host([expansion]) .children{
970            flex-direction: column;
971            width: 100%;
972        }
973        :host([expansion]) .icon{
974            transform: rotateZ(0deg);
975        }
976        :host(:not([expansion])) .children{
977            display: none;
978            flex-direction: column;
979            width: 100%;
980        }
981        :host(:not([expansion])) .icon{
982            transform: rotateZ(-90deg);
983        }
984        :host([sleeping]) .describe{
985            display: none;
986        }
987        :host([sleeping]) .panel-container{
988            display: none;
989        }
990        :host([sleeping]) .children{
991            display: none;
992        }
993        :host(:not([sleeping])) .describe{
994            display: flex;;
995        }
996        :host(:not([sleeping])) .panel-container{
997            display: block;
998        }
999        :host(:not([sleeping])) .children{
1000            display: flex;
1001        }
1002        :host([folder]) .lit-check-box{
1003            display: none;
1004        }
1005        :host(:not([check-type])) .lit-check-box{
1006            display: none;
1007        }
1008        :host([collect-type]) {
1009            position:fixed;
1010            z-index:1000;
1011        }
1012        :host(:not([collect-type])) {
1013            position:static;
1014        }
1015        :host([collect-type]) .collect{
1016            display: block;
1017            color: #5291FF;
1018        }
1019        :host(:not([collect-type])) .collect{
1020            display: none;
1021            color: var(--dark-icon,#666666);
1022        }
1023        .collect{
1024            margin-right: 5px;
1025        }
1026        :host(:not([folder])) .describe:hover .collect{
1027            display: block;
1028        }
1029        :host([row-type="native-memory"]) #nativeRadioList{
1030            display: flex;
1031        }
1032        .popover{
1033            color: var(--dark-color1,#4b5766);
1034            display: none;
1035            justify-content: center;
1036            align-items: center;
1037            margin-right: 5px;
1038        }
1039        .radio{
1040            margin-right: 10px;
1041
1042        }
1043        #setting{
1044            color: var(--dark-color1,#606060);
1045        }
1046        :host([expansion]) #setting{
1047            color: #FFFFFF;
1048        }
1049        :host([highlight]) .flash{
1050            background-color: #ffe263;
1051        }
1052
1053        :host([row-type="energy"]) #appNameList{
1054            display: flex;
1055        }
1056
1057         #listprocess::-webkit-scrollbar{
1058         width: 6px;
1059        }
1060
1061        /*定义滑块 内阴影+圆角*/
1062        #listprocess::-webkit-scrollbar-thumb
1063        {
1064          border-radius: 6px;
1065          background-color: var(--dark-background7,#e7c9c9);
1066        }
1067
1068        </style>
1069        <div class="root">
1070            <div class="describe flash">
1071                <lit-icon class="icon" name="caret-down" size="13"></lit-icon>
1072                <label class="name"></label>
1073                <lit-icon class="collect" name="star-fill" size="17"></lit-icon>
1074                <lit-popover placement="bottomLeft" trigger="click" id = "nativeRadioList" class="popover" haveRadio="true">
1075                    <div style="display: block" slot="content">
1076                        <div id="first-radio" style="margin-bottom: 5px">
1077                        <input class="radio" name="status" type="radio" value="0" />Current Bytes</div>
1078                        <div id="second-radio" style="margin-bottom: 5px">
1079                        <input class="radio" name="status" type="radio" value="1" />Native Memory Density</div>
1080                    </div>
1081                    <lit-icon name="setting" size="17" id="setting"></lit-icon>
1082                </lit-popover>
1083                <lit-popover placement="bottomLeft" trigger="click" id="appNameList" class="popover" haveRadio="true">
1084                    <div slot="content" id="listprocess" style="height:200px;overflow-y:auto">
1085                    </div>
1086                    <lit-icon name="setting" size="17" id="setting"></lit-icon>
1087                </lit-popover>
1088                <lit-check-box class="lit-check-box"></lit-check-box>
1089            </div>
1090            <div class="panel-container">
1091                <div class="tip">
1092                    P:process [1573]<br>
1093                    T:Thread [675]
1094                </div>
1095            </div>
1096        </div>
1097        `;
1098    }
1099}
1100