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'; 30import { drawSelectionRange } from '../../../database/ui-worker/ProcedureWorkerCommon.js'; 31import { TraceRowConfig } from './TraceRowConfig.js'; 32 33export class RangeSelectStruct { 34 startX: number | undefined; 35 endX: number | undefined; 36 startNS: number | undefined; 37 endNS: number | undefined; 38} 39 40let collectList: Array<any> = []; 41let rowDragElement: EventTarget | undefined | null; 42let dragDirection: string = ''; 43 44@element('trace-row') 45export class TraceRow<T extends BaseStruct> extends HTMLElement { 46 static ROW_TYPE_CPU = 'cpu-data'; 47 static ROW_TYPE_CPU_STATE = 'cpu-state'; 48 static ROW_TYPE_CPU_FREQ = 'cpu-freq'; 49 static ROW_TYPE_CPU_FREQ_LIMIT = 'cpu-limit-freq'; 50 static ROW_TYPE_FPS = 'fps'; 51 static ROW_TYPE_NATIVE_MEMORY = 'native-memory'; 52 static ROW_TYPE_HIPERF = 'hiperf'; 53 static ROW_TYPE_DELIVER_INPUT_EVENT = 'DeliverInputEvent'; 54 static ROW_TYPE_HIPERF_CPU = 'hiperf-cpu'; 55 static ROW_TYPE_HIPERF_PROCESS = 'hiperf-process'; 56 static ROW_TYPE_HIPERF_THREAD = 'hiperf-thread'; 57 static ROW_TYPE_HIPERF_REPORT = 'hiperf-report'; 58 static ROW_TYPE_HIPERF_EVENT = 'hiperf-event'; 59 static ROW_TYPE_PROCESS = 'process'; 60 static ROW_TYPE_APP_STARTUP = 'app-startup'; 61 static ROW_TYPE_STATIC_INIT = 'static-init'; 62 static ROW_TYPE_THREAD = 'thread'; 63 static ROW_TYPE_MEM = 'mem'; 64 static ROW_TYPE_VIRTUAL_MEMORY_GROUP = 'virtual-memory-group'; 65 static ROW_TYPE_VIRTUAL_MEMORY = 'virtual-memory-cell'; 66 static ROW_TYPE_FILE_SYSTEM_GROUP = 'file-system-group'; 67 static ROW_TYPE_FILE_SYSTEM = 'file-system-cell'; 68 static ROW_TYPE_HEAP = 'heap'; 69 static ROW_TYPE_JS_MEMORY = 'js-memory'; 70 static ROW_TYPE_HEAP_SNAPSHOT = 'heap-snapshot'; 71 static ROW_TYPE_HEAP_TIMELINE = 'heap-timeline'; 72 static ROW_TYPE_FUNC = 'func'; 73 static ROW_TYPE_MONITOR = 'ability-monitor'; 74 static ROW_TYPE_CPU_ABILITY = 'cpu-ability'; 75 static ROW_TYPE_MEMORY_ABILITY = 'memory-ability'; 76 static ROW_TYPE_DISK_ABILITY = 'disk-ability'; 77 static ROW_TYPE_NETWORK_ABILITY = 'network-ability'; 78 static ROW_TYPE_SDK = 'sdk'; 79 static ROW_TYPE_SDK_COUNTER = 'sdk-counter'; 80 static ROW_TYPE_SDK_SLICE = 'sdk-slice'; 81 static ROW_TYPE_ENERGY = 'energy'; 82 static ROW_TYPE_ANOMALY_ENERGY = 'anomaly-energy'; 83 static ROW_TYPE_SYSTEM_ENERGY = 'system-energy'; 84 static ROW_TYPE_POWER_ENERGY = 'power-energy'; 85 static ROW_TYPE_STATE_ENERGY = 'state-energy'; 86 static ROW_TYPE_SMAPS = 'smaps'; 87 static ROW_TYPE_CLOCK_GROUP = 'clock-group'; 88 static ROW_TYPE_CLOCK = 'clock'; 89 static ROW_TYPE_IRQ_GROUP = 'irq-group'; 90 static ROW_TYPE_IRQ = 'irq'; 91 static ROW_TYPE_JANK = 'janks'; 92 static FRAME_WIDTH: number = 0; 93 static range: TimeRange | undefined | null; 94 static rangeSelectObject: RangeSelectStruct | undefined; 95 public obj: TraceRowObject<any> | undefined | null; 96 isHover: boolean = false; 97 hoverX: number = 0; 98 hoverY: number = 0; 99 index: number = 0; 100 public must: boolean = false; 101 public isTransferCanvas = false; 102 onComplete: Function | undefined; 103 isComplete: boolean = false; 104 public dataList: Array<T> = []; 105 public dataList2: Array<T> = []; 106 public dataListCache: Array<T> = []; 107 public describeEl: HTMLElement | null | undefined; 108 public canvas: Array<HTMLCanvasElement> = []; 109 public canvasContainer: HTMLDivElement | null | undefined; 110 public tipEL: HTMLDivElement | null | undefined; 111 public checkBoxEL: LitCheckBox | null | undefined; 112 public collectEL: LitIcon | null | undefined; 113 public onThreadHandler: ((useCache: boolean, buf: ArrayBuffer | undefined | null) => void) | undefined | null; 114 public onDrawTypeChangeHandler: ((type: number) => void) | undefined | null; 115 public supplier: (() => Promise<Array<T>>) | undefined | null; 116 public favoriteChangeHandler: ((fav: TraceRow<any>) => void) | undefined | null; 117 public selectChangeHandler: ((list: Array<TraceRow<any>>) => void) | undefined | null; 118 dpr = window.devicePixelRatio || 1; 119 // @ts-ignore 120 offscreen: Array<OffscreenCanvas | undefined> = []; 121 canvasWidth = 0; 122 canvasHeight = 0; 123 public _frame: Rect | undefined; 124 public isLoading: boolean = false; 125 public readonly args: any; 126 public templateType: Array<string> = []; 127 private rootEL: HTMLDivElement | null | undefined; 128 private nameEL: HTMLLabelElement | null | undefined; 129 private _rangeSelect: boolean = false; 130 private _drawType: number = 0; 131 private folderIconEL: LitIcon | null | undefined; 132 online: boolean = false; 133 static isUserInteraction: boolean; 134 asyncFuncName: string | undefined | null; 135 asyncFuncNamePID: number | undefined | null; 136 translateY: number = 0; //single canvas offsetY; 137 childrenList: Array<TraceRow<any>> = []; 138 parentRowEl: TraceRow<any> | undefined; 139 focusHandler?: (ev: MouseEvent) => void | undefined; 140 141 constructor( 142 args: { 143 canvasNumber: number; 144 alpha: boolean; 145 contextId: string; 146 isOffScreen: boolean; 147 skeleton?: boolean; 148 } = { 149 canvasNumber: 1, 150 alpha: false, 151 contextId: '2d', 152 isOffScreen: true, 153 skeleton: false, 154 } 155 ) { 156 super(); 157 this.args = args; 158 this.attachShadow({ mode: 'open' }).innerHTML = this.initHtml(); 159 this.initElements(); 160 } 161 162 static skeleton<T extends BaseStruct>(): TraceRow<T> { 163 let tr = new TraceRow<T>({ 164 alpha: false, 165 canvasNumber: 0, 166 contextId: '', 167 isOffScreen: false, 168 skeleton: true, 169 }); 170 tr.isTransferCanvas = true; 171 return tr; 172 } 173 174 static get observedAttributes() { 175 return [ 176 'folder', 177 'name', 178 'expansion', 179 'children', 180 'height', 181 'row-type', 182 'row-id', 183 'row-parent-id', 184 'sleeping', 185 'check-type', 186 'collect-type', 187 'disabled-check', 188 'row-discard', 189 ]; 190 } 191 192 get hasParentRowEl(): boolean { 193 return this.parentRowEl !== undefined; 194 } 195 196 get rowDiscard(): boolean { 197 return this.hasAttribute('row-discard'); 198 } 199 200 set rowDiscard(value: boolean) { 201 if (value) { 202 this.setAttribute('row-discard', ''); 203 this.style.display = 'none'; 204 } else { 205 this.removeAttribute('row-discard'); 206 this.style.display = 'block'; 207 } 208 } 209 210 get collect() { 211 return this.hasAttribute('collect-type'); 212 } 213 214 set collect(value) { 215 if (value) { 216 this.setAttribute('collect-type', ''); 217 } else { 218 this.removeAttribute('collect-type'); 219 } 220 } 221 222 get rangeSelect(): boolean { 223 return this._rangeSelect; 224 } 225 226 set rangeSelect(value: boolean) { 227 this._rangeSelect = value; 228 } 229 230 sleeping: boolean = false; 231 232 get rowType(): string | undefined | null { 233 return this.getAttribute('row-type'); 234 } 235 236 set rowType(val) { 237 this.setAttribute('row-type', val || ''); 238 } 239 240 get rowId(): string | undefined | null { 241 return this.getAttribute('row-id'); 242 } 243 244 set rowId(val) { 245 this.setAttribute('row-id', val || ''); 246 } 247 248 get rowParentId(): string | undefined | null { 249 return this.getAttribute('row-parent-id'); 250 } 251 252 set rowParentId(val) { 253 this.setAttribute('row-parent-id', val || ''); 254 } 255 256 set rowHidden(val: boolean) { 257 if (val) { 258 this.setAttribute('row-hidden', ''); 259 } else { 260 this.removeAttribute('row-hidden'); 261 } 262 } 263 264 get name(): string { 265 return this.getAttribute('name') || ''; 266 } 267 268 set name(value: string) { 269 this.setAttribute('name', value); 270 } 271 272 get folder(): boolean { 273 return this.hasAttribute('folder'); 274 } 275 276 set folder(value: boolean) { 277 if (value) { 278 this.setAttribute('folder', ''); 279 } else { 280 this.removeAttribute('folder'); 281 } 282 } 283 284 get expansion(): boolean { 285 return this.hasAttribute('expansion'); 286 } 287 288 set expansion(value) { 289 if (value === this.expansion) { 290 return; 291 } 292 let fragment: DocumentFragment | undefined = document.createDocumentFragment(); 293 if (value) { 294 this.childrenList.forEach((child: any) => { 295 child.rowHidden = false; 296 fragment!.appendChild(child); 297 }); 298 this.insertAfter(fragment, this); 299 } else { 300 this.childrenList.length = 0; 301 this.parentElement?.querySelectorAll<any>(`[row-parent-id='${this.rowId!}']`).forEach((it) => { 302 this.childrenList.push(it); 303 if (it.folder) { 304 it.expansion = value; 305 } 306 fragment!.appendChild(it); 307 }); 308 this.childrenList.forEach(child => { 309 fragment!.removeChild(child); 310 }); 311 } 312 fragment = undefined; 313 if (value) { 314 this.setAttribute('expansion', ''); 315 } else { 316 this.removeAttribute('expansion'); 317 } 318 this.dispatchEvent( 319 new CustomEvent('expansion-change', { 320 detail: { 321 expansion: this.expansion, 322 rowType: this.rowType, 323 rowId: this.rowId, 324 rowParentId: this.rowParentId, 325 }, 326 }) 327 ); 328 } 329 330 clearMemory() { 331 this.dataList2 = []; 332 this.dataList = []; 333 this.dataListCache = []; 334 if (this.rootEL) { 335 this.rootEL.innerHTML = '' 336 } 337 if (this.folder) { 338 this.childrenList.forEach(child => { 339 if (child.clearMemory !== undefined) { 340 child.clearMemory(); 341 } 342 }) 343 this.childrenList = []; 344 } 345 } 346 347 addTemplateTypes(...type: string[]) { 348 this.templateType.push(...type); 349 } 350 351 replaceTraceRow(newNode: any, oldNode: any) { 352 let oldIndex = this.childrenList.indexOf(oldNode); 353 if (oldIndex != -1) { 354 this.childrenList.splice(oldIndex, 1, newNode); 355 } 356 } 357 358 toParentAddTemplateType = (currentRowEl: TraceRow<any>) => { 359 let parentRow = currentRowEl.parentRowEl; 360 if (parentRow !== undefined) { 361 parentRow.templateType.push(...currentRowEl.templateType); 362 if (parentRow.parentRowEl !== undefined) { 363 this.toParentAddTemplateType(parentRow); 364 } 365 } 366 }; 367 368 addChildTraceRow(child: TraceRow<any>) { 369 TraceRowConfig.allTraceRowList.push(child); 370 child.parentRowEl = this; 371 this.toParentAddTemplateType(child); 372 child.setAttribute('scene', ''); 373 this.childrenList.push(child); 374 } 375 376 addChildTraceRowAfter(child: TraceRow<any>, targetRow: TraceRow<any>) { 377 TraceRowConfig.allTraceRowList.push(child); 378 child.parentRowEl = this; 379 let index = this.childrenList.indexOf(targetRow); 380 child.setAttribute('scene', ''); 381 if (index != -1) { 382 this.childrenList.splice(index + 1, 0, child); 383 } else { 384 this.childrenList.push(child); 385 } 386 } 387 388 addChildTraceRowSpecifyLocation(child: TraceRow<any>, index: number) { 389 TraceRowConfig.allTraceRowList.push(child); 390 child.parentRowEl = this; 391 child.setAttribute('scene', ''); 392 this.childrenList.splice(index, 0, child); 393 } 394 insertAfter(newEl: DocumentFragment, targetEl: HTMLElement) { 395 let parentEl = targetEl.parentNode; 396 if (parentEl!.lastChild == targetEl) { 397 parentEl!.appendChild(newEl); 398 } else { 399 parentEl!.insertBefore(newEl, targetEl.nextSibling); 400 } 401 } 402 403 set tip(value: string) { 404 if (this.tipEL) { 405 this.tipEL.innerHTML = value; 406 } 407 } 408 409 get frame(): Rect | any { 410 if (this._frame) { 411 this._frame.width = TraceRow.FRAME_WIDTH 412 this._frame.height = this.clientHeight; 413 return this._frame; 414 } else { 415 this._frame = new Rect( 416 0, 417 0, 418 TraceRow.FRAME_WIDTH, 419 this.clientHeight || 40 420 ); 421 return this._frame; 422 } 423 } 424 425 set frame(f: Rect) { 426 this._frame = f; 427 } 428 429 get disabledCheck(): boolean { 430 return this.hasAttribute('disabled-check'); 431 } 432 433 set disabledCheck(value: boolean) { 434 if (value) { 435 this.setAttribute('disabled-check', ''); 436 this.checkBoxEL!.style.display = 'none'; 437 } else { 438 this.removeAttribute('disabled-check'); 439 this.checkBoxEL!.style.display = 'flex'; 440 } 441 } 442 443 get checkType(): string { 444 return this.getAttribute('check-type') || ''; 445 } 446 447 set checkType(value: string) { 448 if (!value || value.length == 0) { 449 this.removeAttribute('check-type'); 450 return; 451 } 452 if (this.getAttribute('check-type') === value) { 453 return; 454 } 455 if (this.folder) { 456 this.childrenList.forEach((it) => (it.checkType = value)); 457 } 458 this.setAttribute('check-type', value); 459 if (this.hasAttribute('disabled-check')) { 460 this.checkBoxEL!.style.display = 'none'; 461 return; 462 } 463 switch (value) { 464 case '-1': 465 this.checkBoxEL!.style.display = 'none'; 466 this.rangeSelect = false; 467 break; 468 case '0': 469 this.checkBoxEL!.style.display = 'flex'; 470 this.checkBoxEL!.checked = false; 471 this.checkBoxEL!.indeterminate = false; 472 this.rangeSelect = false; 473 break; 474 case '1': 475 this.checkBoxEL!.style.display = 'flex'; 476 this.checkBoxEL!.checked = false; 477 this.checkBoxEL!.indeterminate = true; 478 this.rangeSelect = false; 479 break; 480 case '2': 481 this.rangeSelect = true; 482 this.checkBoxEL!.style.display = 'flex'; 483 this.checkBoxEL!.checked = true; 484 this.checkBoxEL!.indeterminate = false; 485 break; 486 } 487 } 488 489 get drawType(): number { 490 return this._drawType; 491 } 492 493 set drawType(value: number) { 494 this._drawType = value; 495 let radioList: NodeListOf<any> = this.shadowRoot!.querySelectorAll('input[type=radio][name=status]'); 496 if (radioList!.length > 0) { 497 radioList[Number(value)].checked = true; 498 } 499 } 500 501 get highlight(): boolean { 502 return this.hasAttribute('expansion'); 503 } 504 505 set highlight(value: boolean) { 506 if (value) { 507 this.setAttribute('highlight', ''); 508 } else { 509 this.removeAttribute('highlight'); 510 } 511 } 512 513 set folderPaddingLeft(value: number) { 514 this.folderIconEL!.style.marginLeft = value + 'px'; 515 } 516 517 initElements(): void { 518 this.rootEL = this.shadowRoot?.querySelector('.root'); 519 this.checkBoxEL = this.shadowRoot?.querySelector<LitCheckBox>('.lit-check-box'); 520 this.collectEL = this.shadowRoot?.querySelector<LitIcon>('.collect'); 521 this.describeEl = this.shadowRoot?.querySelector('.describe'); 522 this.folderIconEL = this.shadowRoot?.querySelector<LitIcon>('.icon'); 523 this.nameEL = this.shadowRoot?.querySelector('.name'); 524 this.canvasContainer = this.shadowRoot?.querySelector('.panel-container'); 525 this.tipEL = this.shadowRoot?.querySelector('.tip'); 526 let canvasNumber = this.args['canvasNumber']; 527 if (!this.args['skeleton']) { 528 for (let i = 0; i < canvasNumber; i++) { 529 let canvas = document.createElement('canvas'); 530 canvas.className = 'panel'; 531 this.canvas.push(canvas); 532 if (this.canvasContainer) { 533 this.canvasContainer.appendChild(canvas); 534 } 535 } 536 } 537 this.describeEl?.addEventListener('click', () => { 538 if (this.folder) { 539 this.expansion = !this.expansion; 540 } 541 }); 542 } 543 544 initCanvas(list: Array<HTMLCanvasElement>): void { 545 let timerShaftEL = document! 546 .querySelector('body > sp-application')! 547 .shadowRoot!.querySelector('#sp-system-trace')! 548 .shadowRoot!.querySelector('div > timer-shaft-element'); 549 let timerShaftCanvas = timerShaftEL!.shadowRoot!.querySelector<HTMLCanvasElement>('canvas'); 550 let tempHeight: number = 0; 551 if (this.rowType == TraceRow.ROW_TYPE_FUNC) { 552 tempHeight = 20; 553 } else if (this.rowType == TraceRow.ROW_TYPE_THREAD) { 554 tempHeight = 30; 555 } else if (this.rowType == TraceRow.ROW_TYPE_SYSTEM_ENERGY) { 556 tempHeight = 80; 557 } else if (this.rowType == TraceRow.ROW_TYPE_POWER_ENERGY) { 558 tempHeight = 200; 559 } else if (this.rowType == TraceRow.ROW_TYPE_ANOMALY_ENERGY) { 560 tempHeight = 55; 561 } else { 562 tempHeight = 40; 563 } 564 this.dpr = window.devicePixelRatio || 1; 565 list.forEach((canvas, i) => { 566 this.rootEL!.style.height = `${this.getAttribute('height') || '40'}px`; 567 canvas.style.width = timerShaftCanvas!.style.width; 568 canvas.style.height = tempHeight + 'px'; 569 this.canvasWidth = timerShaftCanvas!.width; 570 this.canvasHeight = Math.ceil(tempHeight * this.dpr); 571 canvas.width = this.canvasWidth; 572 canvas.height = this.canvasHeight; 573 // @ts-ignore 574 this.offscreen.push(canvas!.transferControlToOffscreen()); 575 }); 576 } 577 578 updateWidth(width: number) { 579 this.dpr = window.devicePixelRatio || 1; 580 let tempHeight: number = 0; 581 if (this.rowType == TraceRow.ROW_TYPE_FUNC) { 582 tempHeight = 20; 583 } else if (this.rowType == TraceRow.ROW_TYPE_THREAD) { 584 tempHeight = 30; 585 } else if (this.rowType == TraceRow.ROW_TYPE_SYSTEM_ENERGY) { 586 tempHeight = 90; 587 } else if (this.rowType == TraceRow.ROW_TYPE_POWER_ENERGY) { 588 tempHeight = 200; 589 } else if (this.rowType == TraceRow.ROW_TYPE_ANOMALY_ENERGY) { 590 tempHeight = 55; 591 } else { 592 tempHeight = 40; 593 } 594 if (this.canvas.length > 1) { 595 tempHeight = 20; 596 } 597 this.canvas.forEach((it) => { 598 this.canvasWidth = Math.ceil((width - (this.describeEl?.clientWidth || 248)) * this.dpr); 599 this.canvasHeight = Math.ceil(tempHeight * this.dpr); 600 it!.style.width = width - (this.describeEl?.clientWidth || 248) + 'px'; 601 if (this.args.isOffScreen) { 602 this.draw(true); 603 } 604 }); 605 } 606 607 drawLine(item: HTMLDivElement, direction: string /*string[top|bottom]*/) { 608 if (!item) return; 609 switch (direction) { 610 case 'top': 611 item.classList.remove('line-bottom'); 612 item.classList.add('line-top'); 613 break; 614 case 'bottom': 615 item.classList.remove('line-top'); 616 item.classList.add('line-bottom'); 617 break; 618 case '': 619 item.classList.remove('line-top'); 620 item.classList.remove('line-bottom'); 621 break; 622 } 623 } 624 625 connectedCallback() { 626 this.checkBoxEL!.onchange = (ev: any) => { 627 info('checkBoxEL onchange '); 628 if (!ev.target.checked) { 629 info('checkBoxEL target not checked'); 630 this.rangeSelect = false; 631 this.checkType = '0'; 632 } else { 633 this.rangeSelect = true; 634 this.checkType = '2'; 635 } 636 this.setCheckBox(ev.target.checked); 637 }; 638 this.describeEl!.ondragstart = (ev: DragEvent) => this.rowDragstart(ev); 639 this.describeEl!.ondragleave = (ev: any) => { 640 this.drawLine(ev.currentTarget, ''); 641 return undefined; 642 }; 643 this.describeEl!.ondragend = (ev: any) => { 644 rowDragElement = null; 645 ev.target.classList.remove('drag'); 646 this.drawLine(ev.currentTarget, ''); 647 return undefined; 648 }; 649 this.describeEl!.ondragover = (ev: any) => { 650 if (!this.collect) return; 651 if (rowDragElement === this) return; 652 let rect = ev.currentTarget.getBoundingClientRect(); 653 if (ev.clientY >= rect.top && ev.clientY < rect.top + rect.height / 2) { 654 //上面 655 dragDirection = 'top'; 656 this.drawLine(ev.currentTarget, 'top'); 657 } else if (ev.clientY <= rect.bottom && ev.clientY > rect.top + rect.height / 2) { 658 //下面 659 dragDirection = 'bottom'; 660 this.drawLine(ev.currentTarget, 'bottom'); 661 } 662 return undefined; 663 }; 664 this.describeEl!.ondrop = (ev: any) => { 665 if (!this.collect) return; 666 this.drawLine(ev.currentTarget, ''); 667 let spacer = this.parentElement!.previousElementSibling! as HTMLDivElement; 668 let startDragNode = collectList.findIndex((it) => it === rowDragElement); 669 let endDragNode = collectList.findIndex((it) => it === this); 670 if (startDragNode === -1 || endDragNode === -1) return; 671 if (startDragNode < endDragNode && dragDirection === 'top') { 672 endDragNode--; 673 } else if (startDragNode > endDragNode && dragDirection === 'bottom') { 674 endDragNode++; 675 } 676 collectList.splice(endDragNode, 0, ...collectList.splice(startDragNode, 1)); 677 collectList.forEach((it, i) => { 678 if (i == 0) { 679 it.style.top = `${spacer.offsetTop + 48}px`; 680 } else { 681 it.style.top = `${collectList[i - 1].offsetTop + collectList[i - 1].offsetHeight}px`; 682 } 683 }); 684 }; 685 this.collectEL!.onclick = (e) => { 686 if (this.isComplete) { 687 this.collect = !this.collect; 688 if (this.collect) { 689 this.describeEl!.draggable = false; 690 } else { 691 this.describeEl!.draggable = false; 692 } 693 document.dispatchEvent( 694 new CustomEvent('collect', { 695 detail: { 696 type: e.type, 697 row: this, 698 }, 699 }) 700 ); 701 this.favoriteChangeHandler?.(this); 702 } 703 }; 704 if (!this.args['skeleton']) { 705 this.initCanvas(this.canvas); 706 } 707 let radioList = this.shadowRoot!.querySelectorAll('input[type=radio][name=status]'); 708 let popover = this.shadowRoot!.querySelector<LitPopover>('.popover'); 709 this.shadowRoot?.querySelector<HTMLDivElement>('#first-radio')?.addEventListener('click', (e) => { 710 // @ts-ignore 711 radioList[0]!.checked = true; 712 // @ts-ignore 713 popover!.visible = false; 714 setTimeout(() => { 715 this.onDrawTypeChangeHandler?.(0); 716 }, 300); 717 }); 718 this.shadowRoot?.querySelector<HTMLDivElement>('#second-radio')?.addEventListener('click', (e) => { 719 // @ts-ignore 720 radioList[1]!.checked = true; 721 // @ts-ignore 722 popover!.visible = false; 723 setTimeout(() => { 724 this.onDrawTypeChangeHandler?.(1); 725 }, 300); 726 }); 727 } 728 729 rowDragstart(ev: any) { 730 rowDragElement = this; 731 ev.target.classList.add('drag'); 732 } 733 734 setCheckBox(isCheck: boolean) { 735 if (this.folder) { 736 // favorite row check change; 737 window.publish(window.SmartEvent.UI.CheckALL, { 738 rowId: this.rowId, 739 isCheck: isCheck, 740 }); 741 this.childrenList!.forEach((ck) => { 742 ck.setAttribute('check-type', isCheck ? '2' : '0'); 743 let allCheck: LitCheckBox | null | undefined = ck?.shadowRoot?.querySelector('.lit-check-box'); 744 if (allCheck) { 745 allCheck!.checked = isCheck; 746 } 747 }); 748 } else if (this.rowParentId == '' && !this.folder) { 749 let traceRowList: Array<TraceRow<any>> = []; 750 this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2'][folder]").forEach( 751 (it) => { 752 traceRowList.push( 753 ...it.childrenList.filter((it) => { 754 return it.checkType === '2'; 755 }) 756 ); 757 } 758 ); 759 this.selectChangeHandler?.([ 760 ...this.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2']"), 761 ...traceRowList, 762 ]); 763 return; 764 } 765 let checkList = this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>( 766 `trace-row[row-parent-id='${this.folder ? this.rowId : this.rowParentId}'][check-type="2"]` 767 ); 768 let checkList2: Array<TraceRow<any>> = []; 769 if (this.folder && !this.expansion) { 770 checkList2 = this.childrenList.filter((it) => { 771 return it.checkType === '2'; 772 }); 773 } 774 let unselectedList = this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>( 775 `trace-row[row-parent-id='${this.folder ? this.rowId : this.rowParentId}'][check-type="0"]` 776 ); 777 let unselectedList2: Array<TraceRow<any>> = []; 778 if (this.folder && !this.expansion) { 779 unselectedList2 = this.childrenList.filter((it) => { 780 return it.checkType === '0'; 781 }); 782 } 783 let parentCheck: LitCheckBox | null | undefined = this.parentRowEl?.shadowRoot?.querySelector('.lit-check-box'); 784 if (unselectedList?.length == 0 && unselectedList.length === 0) { 785 this.parentRowEl?.setAttribute('check-type', '2'); 786 if (parentCheck) { 787 parentCheck!.checked = true; 788 parentCheck!.indeterminate = false; 789 } 790 checkList?.forEach((rowItem) => { 791 rowItem.checkType = '2'; 792 rowItem.rangeSelect = true; 793 }); 794 checkList2?.forEach((it) => { 795 it.checkType = '2'; 796 it.rangeSelect = true; 797 }); 798 } else { 799 this.parentRowEl?.setAttribute('check-type', '1'); 800 if (parentCheck) { 801 parentCheck!.checked = false; 802 parentCheck!.indeterminate = true; 803 } 804 checkList?.forEach((it) => { 805 it.checkType = '2'; 806 it.rangeSelect = true; 807 }); 808 checkList2?.forEach((it) => { 809 it.checkType = '2'; 810 it.rangeSelect = true; 811 }); 812 unselectedList?.forEach((item) => { 813 item.checkType = '0'; 814 item.rangeSelect = false; 815 }); 816 unselectedList2?.forEach((it) => { 817 it.checkType = '0'; 818 it.rangeSelect = false; 819 }); 820 } 821 822 if (checkList?.length == 0 && checkList2?.length === 0) { 823 this.parentRowEl?.setAttribute('check-type', '0'); 824 if (parentCheck) { 825 parentCheck!.checked = false; 826 parentCheck!.indeterminate = false; 827 } 828 unselectedList?.forEach((it) => { 829 it.checkType = '0'; 830 it.rangeSelect = false; 831 }); 832 unselectedList2?.forEach((it) => { 833 it.checkType = '0'; 834 it.rangeSelect = false; 835 }); 836 } 837 let traceRowList: Array<TraceRow<any>> = []; 838 this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2'][folder]").forEach( 839 (it) => { 840 traceRowList.push( 841 ...it.childrenList.filter((it) => { 842 return it.checkType === '2'; 843 }) 844 ); 845 } 846 ); 847 this.selectChangeHandler?.([ 848 ...this.parentElement!.parentElement!.querySelectorAll<TraceRow<any>>("trace-row[check-type='2']"), 849 ...traceRowList, 850 ]); 851 } 852 853 onMouseHover(x: number, y: number, tip: boolean = true): T | undefined | null { 854 if (this.tipEL) { 855 this.tipEL.style.display = 'none'; 856 } 857 return null; 858 } 859 860 setTipLeft(x: number, struct: any) { 861 if (struct == null && this.tipEL) { 862 this.tipEL.style.display = 'none'; 863 return; 864 } 865 if (this.tipEL) { 866 this.tipEL.style.display = 'flex'; 867 if (x + this.tipEL.clientWidth > (this.canvasContainer!.clientWidth || 0)) { 868 this.tipEL.style.transform = `translateX(${x - this.tipEL.clientWidth - 1}px)`; 869 } else { 870 this.tipEL.style.transform = `translateX(${x}px)`; 871 } 872 } 873 } 874 875 onMouseLeave(x: number, y: number) { 876 if (this.tipEL) { 877 this.tipEL.style.display = 'none'; 878 } 879 } 880 881 draw(useCache: boolean = false) { 882 this.dpr = window.devicePixelRatio || 1; 883 if (this.sleeping) { 884 return; 885 } 886 if (this.online) { 887 if (!useCache && !TraceRow.isUserInteraction) { 888 this.supplier?.().then((res) => { 889 this.onThreadHandler?.(useCache, res as any); 890 }); 891 } 892 this.onThreadHandler?.(useCache, null); 893 return; 894 } 895 if (!this.isComplete) { 896 if (this.supplier && !this.isLoading) { 897 this.isLoading = true; 898 this.must = true; 899 if (this.supplier) { 900 let promise = this.supplier(); 901 if (promise) { 902 promise.then((res) => { 903 this.dataList = res; 904 if (this.onComplete) { 905 this.onComplete(); 906 } 907 window.publish(window.SmartEvent.UI.TraceRowComplete, this); 908 this.isComplete = true; 909 this.isLoading = false; 910 this.draw(false); 911 }); 912 } else { 913 this.isLoading = false; 914 this.draw(false); 915 } 916 } 917 } 918 } else { 919 if (!this.hasAttribute('row-hidden')) { 920 if (this.onThreadHandler && this.dataList) { 921 this.onThreadHandler!(false, null); 922 } 923 } 924 } 925 } 926 927 canvasSave(ctx: CanvasRenderingContext2D) { 928 ctx.save(); 929 ctx.translate(0, this.translateY); 930 const clipRect = new Path2D(); 931 clipRect.rect(0, 0, this.frame.width, this.frame.height); 932 ctx.clip(clipRect); 933 } 934 935 canvasRestore(ctx: CanvasRenderingContext2D) { 936 drawSelectionRange(ctx, this); 937 ctx.restore(); 938 } 939 940 clearCanvas(ctx: CanvasRenderingContext2D) { 941 if (ctx) { 942 this.canvas.forEach((it) => { 943 ctx.clearRect(0, 0, it!.clientWidth || 0, it!.clientHeight || 0); 944 }); 945 } 946 } 947 948 drawLines(ctx: CanvasRenderingContext2D) { 949 if (ctx) { 950 ctx.lineWidth = 1; 951 ctx.strokeStyle = this.getLineColor(); 952 TraceRow.range?.xs.forEach((it) => { 953 ctx.moveTo(Math.floor(it), 0); 954 ctx.lineTo(Math.floor(it), this.shadowRoot?.host.clientHeight || 0); 955 }); 956 ctx.stroke(); 957 } 958 } 959 960 getLineColor() { 961 return window.getComputedStyle(this.rootEL!, null).getPropertyValue('border-bottom-color'); 962 } 963 964 drawSelection(ctx: CanvasRenderingContext2D) { 965 if (this.rangeSelect) { 966 TraceRow.rangeSelectObject!.startX = Math.floor( 967 ns2x( 968 TraceRow.rangeSelectObject!.startNS!, 969 TraceRow.range!.startNS, 970 TraceRow.range!.endNS, 971 TraceRow.range!.totalNS!, 972 this.frame 973 ) 974 ); 975 TraceRow.rangeSelectObject!.endX = Math.floor( 976 ns2x( 977 TraceRow.rangeSelectObject!.endNS!, 978 TraceRow.range!.startNS, 979 TraceRow.range!.endNS, 980 TraceRow.range!.totalNS!, 981 this.frame 982 ) 983 ); 984 if (ctx) { 985 ctx.globalAlpha = 0.5; 986 ctx.fillStyle = '#666666'; 987 ctx.fillRect( 988 TraceRow.rangeSelectObject!.startX!, 989 this.frame.y, 990 TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!, 991 this.frame.height 992 ); 993 ctx.globalAlpha = 1; 994 } 995 } 996 } 997 998 isInTimeRange(startTime: number, duration: number): boolean { 999 return ( 1000 (startTime || 0) + (duration || 0) > (TraceRow.range?.startNS || 0) && 1001 (startTime || 0) < (TraceRow.range?.endNS || 0) 1002 ); 1003 } 1004 1005 buildArgs(obj: any) { 1006 let result: any = { 1007 list: this.must ? this.dataList : undefined, 1008 offscreen: !this.isTransferCanvas ? this.offscreen[0] : undefined, //是否离屏 1009 dpr: this.dpr, //屏幕dpr值 1010 xs: TraceRow.range?.xs, //线条坐标信息 1011 isHover: this.isHover, 1012 hoverX: this.hoverX, 1013 hoverY: this.hoverY, 1014 canvasWidth: this.canvasWidth, 1015 canvasHeight: this.canvasHeight, 1016 isRangeSelect: this.rangeSelect, 1017 rangeSelectObject: TraceRow.rangeSelectObject, 1018 lineColor: this.getLineColor(), 1019 chartColor: ColorUtils.MD_PALETTE[0], 1020 startNS: TraceRow.range?.startNS || 0, 1021 endNS: TraceRow.range?.endNS || 0, 1022 totalNS: TraceRow.range?.totalNS || 0, 1023 slicesTime: TraceRow.range?.slicesTime, 1024 range: TraceRow.range, 1025 frame: this.frame, 1026 flagMoveInfo: null, 1027 flagSelectedInfo: null, 1028 wakeupBean: null, 1029 }; 1030 Reflect.ownKeys(obj).forEach((it) => { 1031 result[it] = obj[it]; 1032 }); 1033 return result; 1034 } 1035 1036 getTransferArray() { 1037 let tsf = []; 1038 if (!this.isTransferCanvas) { 1039 tsf.push(this.offscreen[0]); 1040 } 1041 if (this.must && this.dataList instanceof ArrayBuffer) { 1042 tsf.push(this.dataList); 1043 } 1044 return tsf; 1045 } 1046 1047 attributeChangedCallback(name: string, oldValue: string, newValue: string) { 1048 switch (name) { 1049 case 'name': 1050 if (this.nameEL) { 1051 this.nameEL.textContent = newValue; 1052 this.nameEL.title = newValue; 1053 } 1054 break; 1055 case 'height': 1056 if (newValue != oldValue) { 1057 if (!this.args.isOffScreen) { 1058 } 1059 } 1060 break; 1061 case 'check-type': 1062 if (newValue === 'check') { 1063 this.checkBoxEL?.setAttribute('checked', ''); 1064 } else { 1065 this.checkBoxEL?.removeAttribute('checked'); 1066 } 1067 break; 1068 } 1069 } 1070 1071 focusContain(e: MouseEvent, inFavoriteArea: boolean): boolean { 1072 let _y = (e.currentTarget as HTMLElement).getBoundingClientRect().y; 1073 let myRect = this.getBoundingClientRect(); 1074 let x = e.offsetX; 1075 let y = e.offsetY + _y; 1076 if (x >= myRect.x && x <= myRect.x + myRect.width && y >= myRect.y && y <= myRect.y + myRect.height) { 1077 this.hoverX = x - this.describeEl!.clientWidth; 1078 this.hoverY = y - myRect.y; 1079 this.isHover = this.collect === inFavoriteArea; 1080 return true; 1081 } else { 1082 this.isHover = false; 1083 if (this.tipEL) { 1084 this.tipEL.style.display = 'none'; 1085 } 1086 return false; 1087 } 1088 } 1089 1090 initHtml(): string { 1091 return ` 1092 <style> 1093 *{ 1094 box-sizing: border-box; 1095 } 1096 :host(:not([row-hidden])){ 1097 box-sizing: border-box; 1098 display: flex; 1099 flex-direction: column; 1100 width: 100%; 1101 height: min-content; 1102 } 1103 :host([row-hidden]){ 1104 width: 100%; 1105 display: none; 1106 } 1107 .root{ 1108 height: 100%; 1109 width: 100%; 1110 display: grid; 1111 grid-template-rows: 100%; 1112 grid-template-columns: 248px 1fr; 1113 border-bottom: 1px solid var(--dark-border1,#dadada); 1114 border-right: 1px solid var(--dark-border1,#ffffff); 1115 box-sizing: border-box; 1116 } 1117 .root .drag{ 1118 background-color: var(--dark-background1,#eee); 1119 box-shadow: 0 4px 12px -4px #999 inset; 1120 } 1121 .root .line-top{ 1122 box-shadow: 0 4px 2px -1px #4d7ab3 inset; 1123 transition: all 0.2s; 1124 } 1125 .root .line-bottom{ 1126 box-shadow: 0 -4px 2px -1px #4d7ab3 inset; 1127 transition: all 0.2s; 1128 } 1129 .describe{ 1130 box-sizing: border-box; 1131 border-right: 1px solid var(--dark-border1,#c9d0da); 1132 background-color: var(--dark-background5,#ffffff); 1133 align-items: center; 1134 position: relative; 1135 } 1136 .panel{ 1137 width: 100%; 1138 height: 100%; 1139 overflow: visible; 1140 background-color: transparent; 1141 display: block; 1142 } 1143 .panel-container{ 1144 width: 100%; 1145 position: relative; 1146 pointer-events: none; 1147 } 1148 .name{ 1149 color: var(--dark-color1,#4b5766); 1150 margin-left: 10px; 1151 font-size: .9rem; 1152 font-weight: normal; 1153 flex: 1; 1154 max-height: 100%; 1155 text-align: left; 1156 overflow: hidden; 1157 user-select: none; 1158 text-overflow: ellipsis; 1159 white-space:nowrap 1160 } 1161 :host([highlight]) .name{ 1162 color: #4b5766; 1163 } 1164 .icon{ 1165 color: var(--dark-color1,#151515); 1166 margin-left: 10px; 1167 } 1168 .describe:hover { 1169 cursor: pointer; 1170 } 1171 :host([folder]) .describe:hover > .icon{ 1172 color:#ecb93f; 1173 margin-left: 10px; 1174 } 1175 :host([folder]){ 1176 /*background-color: var(--dark-background1,#f5fafb);*/ 1177 } 1178 :host([folder]) .icon{ 1179 display: flex; 1180 } 1181 :host(:not([folder])){ 1182 /*background-color: var(--dark-background,#FFFFFF);*/ 1183 } 1184 :host(:not([folder]):not([children])) { 1185 } 1186 :host(:not([folder]):not([children])) .icon{ 1187 display: none; 1188 } 1189 :host(:not([folder])[children]) .icon{ 1190 display: none; 1191 color:#fff 1192 } 1193 1194 :host(:not([folder])[children]) .name{ 1195 } 1196 :host([expansion]) { 1197 background-color: var(--bark-expansion,#0C65D1); 1198 } 1199 :host([expansion]) .name,:host([expansion]) .icon{ 1200 color: #fff; 1201 } 1202 :host([expansion]) .describe{ 1203 border-right: 0px; 1204 background-color: var(--bark-expansion,#0C65D1); 1205 } 1206 :host([expansion]:not(sleeping)) .panel-container{ 1207 display: none; 1208 } 1209 :host([expansion]) .children{ 1210 flex-direction: column; 1211 width: 100%; 1212 } 1213 :host([expansion]) .icon{ 1214 transform: rotateZ(0deg); 1215 } 1216 :host(:not([expansion])) .children{ 1217 display: none; 1218 flex-direction: column; 1219 width: 100%; 1220 } 1221 :host(:not([expansion])) .icon{ 1222 transform: rotateZ(-90deg); 1223 } 1224 :host([sleeping]) .describe{ 1225 display: none; 1226 } 1227 :host([sleeping]) .panel-container{ 1228 display: none; 1229 } 1230 :host([sleeping]) .children{ 1231 display: none; 1232 } 1233 :host(:not([sleeping])) .describe{ 1234 display: flex;; 1235 } 1236 :host(:not([sleeping])) .panel-container{ 1237 display: block; 1238 } 1239 :host(:not([sleeping])) .children{ 1240 display: flex; 1241 } 1242 :host([folder]) .lit-check-box{ 1243 display: none; 1244 } 1245 :host(:not([check-type])) .lit-check-box{ 1246 display: none; 1247 } 1248 :host([collect-type]) { 1249 /*position:fixed;*/ 1250 /*z-index:1000;*/ 1251 } 1252 :host(:not([collect-type])) { 1253 /*position:static;*/ 1254 } 1255 :host([collect-type]) .collect{ 1256 display: block; 1257 color: #5291FF; 1258 } 1259 :host(:not([collect-type])) .collect{ 1260 display: none; 1261 color: var(--dark-icon,#666666); 1262 } 1263 .collect{ 1264 margin-right: 5px; 1265 } 1266 :host(:not([folder])) .describe:hover .collect{ 1267 display: block; 1268 } 1269 :host([row-type="native-memory"]) #nativeRadioList{ 1270 display: flex; 1271 } 1272 .popover{ 1273 color: var(--dark-color1,#4b5766); 1274 display: none; 1275 justify-content: center; 1276 align-items: center; 1277 margin-right: 5px; 1278 } 1279 .radio{ 1280 margin-right: 10px; 1281 1282 } 1283 #setting{ 1284 color: var(--dark-color1,#606060); 1285 } 1286 :host([expansion]) #setting{ 1287 color: #FFFFFF; 1288 } 1289 :host([highlight]) .flash{ 1290 background-color: #ffe263; 1291 } 1292 1293 :host([row-type="energy"]) #appNameList{ 1294 display: flex; 1295 } 1296 1297 #listprocess::-webkit-scrollbar{ 1298 width: 6px; 1299 } 1300 1301 /*定义滑块 内阴影+圆角*/ 1302 #listprocess::-webkit-scrollbar-thumb 1303 { 1304 border-radius: 6px; 1305 background-color: var(--dark-background7,#e7c9c9); 1306 } 1307 1308 </style> 1309 <div class="root"> 1310 <div class="describe flash" style="position: inherit"> 1311 <lit-icon class="icon" name="caret-down" size="19"></lit-icon> 1312 <label class="name"></label> 1313 <lit-icon class="collect" name="star-fill" size="19"></lit-icon> 1314 <lit-popover placement="bottomLeft" trigger="click" id = "nativeRadioList" class="popover" haveRadio="true" style="z-index: 1;position: absolute;left: 230px"> 1315 <div style="display: block" slot="content"> 1316 <div id="first-radio" style="margin-bottom: 5px"> 1317 <input class="radio" name="status" type="radio" value="0" />Current Bytes</div> 1318 <div id="second-radio" style="margin-bottom: 5px"> 1319 <input class="radio" name="status" type="radio" value="1" />Native Memory Density</div> 1320 </div> 1321 <lit-icon name="setting" size="19" id="setting"></lit-icon> 1322 </lit-popover> 1323 <lit-popover placement="bottomLeft" trigger="click" id="appNameList" class="popover" haveRadio="true" style="z-index: 1;position: absolute;left: 230px"> 1324 <div slot="content" id="listprocess" style="height:200px;overflow-y:auto"> 1325 </div> 1326 <lit-icon name="setting" size="19" id="setting"></lit-icon> 1327 </lit-popover> 1328 <lit-check-box class="lit-check-box" style="margin-right: 10px;"></lit-check-box> 1329 </div> 1330 </div> 1331 `; 1332 } 1333} 1334