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