1/** 2 * Copyright (c) 2023-2024 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 */ 15import hilog from '@ohos.hilog'; 16import { Theme } from '@ohos.arkui.theme'; 17import { LengthMetrics } from '@ohos.arkui.node'; 18import { common } from '@kit.AbilityKit'; 19import resourceManager from '@ohos.resourceManager'; 20 21const IMAGE_NODE_HEIGHT: number = 24; 22const IMAGE_NODE_WIDTH: number = 24; 23const ITEM_WIDTH: number = 0; 24const ITEM_HEIGHT: number = 48; 25const ITEM_HEIGHT_INPUT: number = 32; 26const BORDER_WIDTH_HAS: number = 2; 27const BORDER_WIDTH_NONE: number = 0; 28const NODE_HEIGHT: number = 48; 29const LIST_ITEM_HEIGHT_NONE: number = 0; 30const LIST_ITEM_HEIGHT: number = 48; 31const SHADOW_OFFSETY: number = 10; 32const FLAG_NUMBER: number = 2; 33const DRAG_OPACITY: number = 0.4; 34const DRAG_OPACITY_NONE: number = 1; 35const MIN_FONT_SCALE: number = 1; 36const MAX_FONT_SCALE: number = 2; 37const FLAG_LINE_HEIGHT: string = '1.0vp'; 38const X_OFF_SET: string = '0vp'; 39const Y_OFF_SET: string = '2.75vp'; 40const Y_BOTTOM_OFF_SET: string = '-1.25vp'; 41const Y_BASE_PLATE_OFF_SET: string = '1.5vp'; 42const COLOR_IMAGE_EDIT: string = '#FFFFFF'; 43const COLOR_IMAGE_ROW: string = '#00000000'; 44const COLOR_SELECT: string = '#1A0A59F7'; 45const SHADOW_COLOR: string = '#00001E'; 46const GRAG_POP_UP_HEIGHT: string = '48'; 47const FLOOR_MIN_WIDTH: string = '128vp'; 48const FLOOR_MAX_WIDTH: string = '208vp'; 49const TEXT_MIN_WIDTH: string = '80vp'; 50const TEXT_MAX_WIDTH: string = '160vp'; 51const MIN_WIDTH: string = '112vp'; 52const MAX_WIDTH: string = '192vp'; 53const TRANS_COLOR: string = '#00FFFFFF'; 54const DELAY_TIME: number = 100; 55const LEVEL_MARGIN: number = 12; 56const MARGIN_OFFSET: number = 8; 57const TAG: string = 'TreeView'; 58const LOG_CODE: number = 0x3900; 59 60const ARROW_DOWN: Resource | string = $r('sys.media.ohos_ic_public_arrow_down'); 61 62const ARROW_DOWN_WITHE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIGNIUk0AAHomAAC' + 63 'AhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAl' + 64 'wSFlzAAAOxAAADsQBlSsOGwAAAKVJREFUeNpjYBgFo2AU0Bww4pL4////diC1hZGRcSo+A4DqWIDUZCB+AVTbiC7PhEfvByCeAjQgn4Dhy4E' + 65 '4BYgvYFODz4JYIF4DxBOwWYJkeAAQRwBdvxGbIcy4TG9sbPzX0NCwHsjUAuIiIPsDUOwkDsPXkhwHWFwaAsQlQAwyrJsYw4myAIslIPCHGMP' + 66 'xBhGO4PoGxF+AOA9o+NbRTDgKRgFxAAAzj0Grm3RjyAAAAABJRU5ErkJggg==' 67 68const ARROW_RIGHT: Resource | string = $r('sys.media.ohos_ic_public_arrow_right'); 69 70const ARROW_RIGHT_WITHE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIGNIUk0AAHomAA' + 71 'CAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAA' + 72 'lwSFlzAAAOxAAADsQBlSsOGwAAAKFJREFUeNpjYBgFowAE/v//bwHEPOToZSJS3XIg3k6OJcRaUALEFuRYwkyMosbGxusNDQ3XgMwCIHYAsl' + 73 'cDxX5RzQJKLGEmxbvkWMJEaqQxMjKuBVI5QGwDxOnUimR08AFK81DdAmAqArl8DhDfAOKpVLUAavh2IH4CxI7A4HpDMEgpMPwFUXFGS8NJCa' + 74 'L55BgOAixEqqsB4oOkGj4KRggAAN4STB9zyhGzAAAAAElFTkSuQmCC' 75 76enum Event { 77 TOUCH_DOWN = 0, 78 TOUCH_UP = 1, 79 HOVER = 3, 80 HOVER_OVER = 4, 81 FOCUS = 5, 82 BLUR = 6, 83 MOUSE_BUTTON_RIGHT = 7, 84 DRAG = 8, 85} 86 87enum MenuOperation { 88 ADD_NODE = 0, 89 REMOVE_NODE = 1, 90 MODIFY_NODE = 2, 91 COMMIT_NODE = 3, 92} 93 94enum PopUpType { 95 HINTS = 0, 96 WARNINGS = 1, 97} 98 99enum InputError { 100 INVALID_ERROR = 0, 101 LENGTH_ERROR = 1, 102 NONE = 2, 103} 104 105enum Flag { 106 DOWN_FLAG = 0, 107 UP_FLAG = 1, 108 NONE = 2, 109} 110 111export enum NodeStatus { 112 EXPAND = 0, 113 COLLAPSE, 114} 115 116export enum InteractionStatus { 117 NORMAL = 0, 118 SELECTED, 119 EDIT, 120 FINISH_EDIT, 121 DRAG_INSERT, 122 FINISH_DRAG_INSERT, 123} 124 125enum CollapseImageType { 126 ARROW_DOWN = 0, 127 ARROW_RIGHT, 128 ARROW_DOWN_WHITE, 129 ARROW_RIGHT_WHITE, 130} 131 132interface ChildNodeInfo { 133 isHasChildNode: boolean; 134 childNum: number; 135 allChildNum: number; 136} 137 138interface NodeItemView { 139 imageNode?: ImageNode; 140 inputText: InputText; 141 mainTitleNode: MainTitleNode; 142 imageCollapse?: CollapseImageNode; 143 fontColor?: ResourceColor; 144} 145 146interface Status { 147 normal: ResourceColor; 148 hover: ResourceColor; 149 press: ResourceColor; 150 selected: ResourceColor; 151 highLight?: ResourceColor; 152} 153 154interface NodeBorder { 155 borderWidth: Resource | number; 156 borderColor: ResourceColor; 157 borderRadius: Resource; 158} 159 160interface PopUpInfo { 161 popUpIsShow: boolean; 162 popUpEnableArrow: boolean; 163 popUpColor?: ResourceColor; 164 popUpText?: string | Resource; 165 popUpTextColor?: ResourceColor; 166} 167 168interface BorderWidth { 169 has: Resource | number; 170 none: Resource | number; 171} 172 173interface TextSetting { 174 fontColor: ResourceColor; 175 fontSize: Resource; 176 fontWeight: FontWeight; 177} 178 179interface NodeInfoView { 180 itemId?: number; 181 itemIcon?: Resource | string; 182 itemTitle?: ResourceStr; 183 isFolder?: boolean; 184} 185 186interface FloorConstraintSize { 187 minWidth: string; 188 maxWidth: string; 189} 190 191interface TextConstraintSize { 192 minWidth1: string; 193 maxWidth1: string; 194 minWidth2: string; 195 maxWidth2: string; 196} 197 198interface Padding { 199 left: Resource; 200 right: Resource; 201} 202 203interface ItemPadding { 204 left: Resource; 205 right: Resource; 206 top: Resource; 207 bottom: Resource; 208} 209 210interface Shadow { 211 radius: Resource; 212 color: string; 213 offsetX?: number; 214 offsetY?: number; 215} 216 217interface DragPopup { 218 floorConstraintSize: FloorConstraintSize; 219 textConstraintSize: TextConstraintSize; 220 padding: Padding; 221 backgroundColor: ResourceColor; 222 height: string; 223 shadow: Shadow; 224 borderRadius: Resource; 225 fontColor: ResourceColor; 226 fontSize: Resource; 227 fontWeight: FontWeight; 228 imageOpacity: Resource; 229} 230 231interface DragNodeParam { 232 parentId: number, 233 currentId: number, 234 data: NodeParam, 235} 236 237interface FlagLine { 238 flagLineHeight: string; 239 flagLineColor: Resource; 240 xOffset: string; 241 yTopOffset: string; 242 yBottomOffset: string; 243 yBasePlateOffset: string; 244} 245 246interface SubTitleStyle { 247 normalFontColor: ResourceColor; 248 highLightFontColor: ResourceColor; 249 fontSize: Resource; 250 fontWeight: FontWeight; 251 margin: Padding; 252} 253 254interface NodeItemViewFactory { 255 createNode: () => NodeItemView; 256 createNodeByNodeParam: (nodeParam: NodeParam) => NodeItemView; 257} 258 259class TreeViewNodeItemFactory implements NodeItemViewFactory { 260 private static instance: TreeViewNodeItemFactory; 261 262 private constructor() { 263 } 264 265 /** 266 * TreeViewNodeItemFactory singleton function 267 * 268 * @returns TreeViewNodeItemFactory 269 */ 270 public static getInstance(): TreeViewNodeItemFactory { 271 if (!TreeViewNodeItemFactory.instance) { 272 TreeViewNodeItemFactory.instance = new TreeViewNodeItemFactory(); 273 } 274 return TreeViewNodeItemFactory.instance; 275 } 276 277 /** 278 * TreeViewNodeItemFactory create default node 279 * 280 * @returns NodeItemView 281 */ 282 public createNode(): NodeItemView { 283 return { 284 imageNode: undefined, 285 inputText: new InputText(), 286 mainTitleNode: new MainTitleNode(''), 287 imageCollapse: undefined, 288 fontColor: undefined, 289 }; 290 } 291 292 /** 293 * TreeViewNodeItemFactory create node by node parameter 294 * 295 * @param nodeParam node parameter 296 * @returns NodeItemView 297 */ 298 public createNodeByNodeParam(nodeParam: NodeParam): NodeItemView { 299 let nodeItemView: NodeItemView = this.createNode(); 300 if (nodeParam.icon) { 301 nodeItemView.imageNode = new ImageNode( 302 nodeParam.icon, 303 $r('sys.float.ohos_id_alpha_content_fourth'), 304 IMAGE_NODE_HEIGHT, 305 IMAGE_NODE_WIDTH, 306 nodeParam.selectedIcon, 307 nodeParam.editIcon, 308 ); 309 } 310 if (nodeParam.primaryTitle) { 311 nodeItemView.mainTitleNode = new MainTitleNode(nodeParam.primaryTitle); 312 } 313 return nodeItemView; 314 } 315} 316 317let emptyNodeInfo: NodeParam = { 318 isFolder: true, 319 icon: '', 320 selectedIcon: '', 321 editIcon: '', 322 container: () => { 323 }, 324 secondaryTitle: '', 325 primaryTitle: '', 326 parentNodeId: -1, 327 currentNodeId: -1, 328} 329 330class TreeViewTheme { 331 private static instance: TreeViewTheme; 332 public itemSelectedBgColor: ResourceColor = '#1A0A59F7'; 333 public primaryTitleFontColor: ResourceColor = $r('sys.color.ohos_id_color_primary'); 334 public secondaryTitleFontColor: ResourceColor = $r('sys.color.ohos_id_color_secondary'); 335 public primaryTitleActiveFontColor: ResourceColor = $r('sys.color.ohos_id_color_text_primary_activated'); 336 public itemPressedBgColor: ResourceColor = $r('sys.color.ohos_id_color_click_effect'); 337 public itemHoverBgColor: ResourceColor = $r('sys.color.ohos_id_color_hover'); 338 public borderFocusedColor: ResourceColor = $r('sys.color.ohos_id_color_focused_outline'); 339 public leftIconColor: ResourceColor = $r('sys.color.icon_secondary'); 340 public leftIconActiveColor: ResourceColor = $r('sys.color.icon_secondary'); 341 public arrowIconColor: ResourceColor = $r('sys.color.icon_tertiary'); 342 343 private constructor() { 344 345 } 346 347 /** 348 * TreeViewTheme singleton function 349 * 350 * @returns TreeViewNodeItemFactory 351 */ 352 public static getInstance(): TreeViewTheme { 353 if (!TreeViewTheme.instance) { 354 TreeViewTheme.instance = new TreeViewTheme(); 355 } 356 return TreeViewTheme.instance; 357 } 358} 359 360@Observed 361export class NodeInfo { 362 public imageSource: Resource | string | undefined = ''; 363 private nodeHeight: Resource | number; 364 private nodeItemView: NodeItemView; 365 private nodeLeftPadding: number; 366 private nodeColor: ResourceColor; 367 private nodeIsShow: boolean; 368 private status: Status; 369 private nodeBorder: NodeBorder; 370 private popUpInfo: PopUpInfo; 371 private listItemHeight: number; 372 private isShowTitle: boolean; 373 private isShowInputText: boolean; 374 private isSelected: boolean; 375 public readonly borderWidth: BorderWidth = 376 { has: BORDER_WIDTH_HAS/* 2vp */, none: BORDER_WIDTH_NONE/* 0vp */ } 377 /* parameter of the drag event.*/ 378 private nodeParam: NodeParam; 379 private node: NodeItem; 380 private canShowFlagLine: boolean = false; 381 private isOverBorder: boolean = false; 382 private canShowBottomFlagLine: boolean = false; 383 private isHighLight: boolean = false; 384 private flagLineLeftMargin: number; 385 private isModify: boolean = false; 386 private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance(); 387 public fontColor: ResourceColor = ''; 388 389 constructor(node: NodeItem, nodeParam: NodeParam) { 390 this.node = node; 391 this.nodeParam = nodeParam; 392 this.nodeItemView = TreeViewNodeItemFactory.getInstance().createNodeByNodeParam(nodeParam); 393 this.popUpInfo = { 394 popUpIsShow: false, 395 popUpEnableArrow: false, 396 popUpColor: undefined, 397 popUpText: '', 398 popUpTextColor: undefined, 399 }; 400 this.nodeHeight = NODE_HEIGHT; 401 this.nodeLeftPadding = node.nodeLevel * LEVEL_MARGIN + MARGIN_OFFSET; // calculate left padding 402 this.nodeColor = $r('sys.color.ohos_id_color_background'); 403 this.nodeIsShow = (this.node.nodeLevel > 0) ? false : true; 404 this.listItemHeight = (this.node.nodeLevel > 0) ? LIST_ITEM_HEIGHT_NONE : LIST_ITEM_HEIGHT; 405 this.isShowTitle = true; 406 this.isShowInputText = false; 407 this.isSelected = false; 408 this.status = { 409 normal: $r('sys.color.ohos_id_color_background_transparent'), 410 hover: this.treeViewTheme.itemHoverBgColor, 411 press: this.treeViewTheme.itemPressedBgColor, 412 selected: this.treeViewTheme.itemSelectedBgColor, 413 highLight: $r('sys.color.ohos_id_color_activated') 414 }; 415 this.nodeBorder = { 416 borderWidth: BORDER_WIDTH_NONE, 417 borderColor: this.treeViewTheme.borderFocusedColor, 418 borderRadius: $r('sys.float.ohos_id_corner_radius_clicked') 419 }; 420 this.flagLineLeftMargin = node.nodeLevel * LEVEL_MARGIN + MARGIN_OFFSET; 421 } 422 423 /** 424 * NodeInfo add collapse image 425 * 426 * @param isHasChildNode whether node has child node 427 */ 428 addImageCollapse(isHasChildNode: boolean): void { 429 if (isHasChildNode) { 430 this.nodeItemView.imageCollapse = 431 CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(CollapseImageType.ARROW_RIGHT); 432 } else { 433 this.nodeItemView.imageCollapse = undefined; 434 } 435 } 436 437 setFontColor(color: ResourceColor): void { 438 this.fontColor = color 439 } 440 441 getFontColor(): ResourceColor { 442 return this.fontColor; 443 } 444 445 getPopUpInfo(): PopUpInfo { 446 return this.popUpInfo; 447 } 448 449 setPopUpIsShow(isShow: boolean): void { 450 this.popUpInfo.popUpIsShow = isShow; 451 } 452 453 setPopUpEnableArrow(popUpEnableArrow: boolean): void { 454 this.popUpInfo.popUpEnableArrow = popUpEnableArrow; 455 } 456 457 setPopUpColor(color: ResourceColor): void { 458 this.popUpInfo.popUpColor = color; 459 } 460 461 setPopUpText(text: string | Resource | undefined): void { 462 this.popUpInfo.popUpText = text; 463 } 464 465 setPopUpTextColor(popUpTextColor: ResourceColor): void { 466 this.popUpInfo.popUpTextColor = popUpTextColor; 467 } 468 469 getIsShowTitle(): boolean { 470 return this.isShowTitle; 471 } 472 473 getIsShowInputText(): boolean { 474 return this.isShowInputText; 475 } 476 477 setTitleAndInputTextStatus(isModify: boolean): void { 478 if (isModify) { 479 this.isShowTitle = false; 480 this.isShowInputText = true; 481 } else { 482 this.isShowTitle = true; 483 this.isShowInputText = false; 484 } 485 } 486 487 handleImageCollapseAfterAddNode(isAddImageCollapse: boolean): void { 488 // listTree this node already has ImageCollapse. 489 if (isAddImageCollapse) { 490 this.nodeItemView.imageCollapse = 491 CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(CollapseImageType.ARROW_DOWN); 492 } else { 493 this.nodeItemView.imageCollapse = undefined; 494 } 495 } 496 497 setNodeColor(nodeColor: ResourceColor | undefined): void { 498 if (nodeColor === undefined) { 499 return; 500 } 501 this.nodeColor = nodeColor; 502 } 503 504 getNodeColor(): ResourceColor { 505 return this.nodeColor; 506 } 507 508 setListItemHeight(listItemHeight: number): void { 509 this.listItemHeight = listItemHeight; 510 } 511 512 getListItemHeight(): number { 513 return this.listItemHeight; 514 } 515 516 getNodeCurrentNodeId(): number { 517 return this.node.currentNodeId; 518 } 519 520 getNodeParentNodeId(): number { 521 return this.node.parentNodeId; 522 } 523 524 getNodeLeftPadding(): number { 525 return this.nodeLeftPadding; 526 } 527 528 getNodeHeight(): Resource | number { 529 return this.nodeHeight; 530 } 531 532 setNodeIsShow(nodeIsShow: boolean): void { 533 this.nodeIsShow = nodeIsShow; 534 } 535 536 getNodeIsShow(): boolean { 537 return this.nodeIsShow; 538 } 539 540 getNodeItem(): NodeItemView { 541 return this.nodeItemView; 542 } 543 544 getNodeStatus(): Status { 545 return this.status; 546 } 547 548 getNodeBorder(): NodeBorder { 549 return this.nodeBorder; 550 } 551 552 setNodeBorder(isClearFocusStatus: boolean): void { 553 this.nodeBorder.borderWidth = isClearFocusStatus ? this.borderWidth.has : this.borderWidth.none; 554 } 555 556 getChildNodeInfo(): ChildNodeInfo { 557 return this.node.childNodeInfo; 558 } 559 560 getMenu(): () => void { 561 return this.nodeParam.container as () => void; 562 } 563 564 setIsSelected(isSelected: boolean): void { 565 this.isSelected = isSelected; 566 } 567 568 getIsSelected(): boolean { 569 return this.isSelected; 570 } 571 572 /* To gain the information while to alter node. */ 573 getNodeInfoData(): NodeParam { 574 return this.nodeParam; 575 } 576 577 /* To gain the tree Node(NodeItem) while to alter node. */ 578 public getNodeInfoNode(): NodeItem { 579 return this.node; 580 } 581 582 public getIsFolder(): boolean | undefined { 583 return this.nodeParam.isFolder; 584 } 585 586 public setCanShowFlagLine(canShowFlagLine: boolean): void { 587 this.canShowFlagLine = canShowFlagLine; 588 } 589 590 public getCanShowFlagLine(): boolean { 591 return this.canShowFlagLine; 592 } 593 594 public setFlagLineLeftMargin(currentNodeLevel: number | undefined): void { 595 if (currentNodeLevel === undefined) { 596 return; 597 } 598 this.flagLineLeftMargin = currentNodeLevel * LEVEL_MARGIN + MARGIN_OFFSET; // calculate 599 } 600 601 public getFlagLineLeftMargin(): number { 602 return this.flagLineLeftMargin; 603 } 604 605 public getNodeLevel(): number { 606 return this.node.nodeLevel; 607 } 608 609 public setIsOverBorder(isOverBorder: boolean): void { 610 this.isOverBorder = isOverBorder; 611 } 612 613 public getIsOverBorder(): boolean { 614 return this.isOverBorder; 615 } 616 617 public setCanShowBottomFlagLine(canShowBottomFlagLine: boolean): void { 618 this.canShowBottomFlagLine = canShowBottomFlagLine; 619 } 620 621 public getCanShowBottomFlagLine(): boolean { 622 return this.canShowBottomFlagLine; 623 } 624 625 public setIsHighLight(isHighLight: boolean): void { 626 this.isHighLight = isHighLight; 627 } 628 629 public getIsHighLight(): boolean { 630 return this.isHighLight; 631 } 632 633 public setIsModify(isModify: boolean): void { 634 this.isModify = isModify; 635 } 636 637 public getIsModify(): boolean { 638 return this.isModify; 639 } 640} 641 642/** 643 * Control style of operation element. 644 * @enum { TreeListenType } 645 * @syscap SystemCapability.ArkUI.ArkUI.Full 646 * @since 10 647 */ 648/** 649 * Control style of operation element. 650 * @enum { TreeListenType } 651 * @syscap SystemCapability.ArkUI.ArkUI.Full 652 * @atomicservice 653 * @since 11 654 */ 655export enum TreeListenType { 656 /** 657 * register listener after a node is clicked. 658 * @syscap SystemCapability.ArkUI.ArkUI.Full 659 * @since 10 660 */ 661 /** 662 * register listener after a node is clicked. 663 * @syscap SystemCapability.ArkUI.ArkUI.Full 664 * @atomicservice 665 * @since 11 666 */ 667 NODE_CLICK = 'NodeClick', 668 669 /** 670 * register listener after a node is add. 671 * @syscap SystemCapability.ArkUI.ArkUI.Full 672 * @since 10 673 */ 674 /** 675 * register listener after a node is add. 676 * @syscap SystemCapability.ArkUI.ArkUI.Full 677 * @atomicservice 678 * @since 11 679 */ 680 NODE_ADD = 'NodeAdd', 681 682 /** 683 * register listener after a node is deleted. 684 * @syscap SystemCapability.ArkUI.ArkUI.Full 685 * @since 10 686 */ 687 /** 688 * register listener after a node is deleted. 689 * @syscap SystemCapability.ArkUI.ArkUI.Full 690 * @atomicservice 691 * @since 11 692 */ 693 NODE_DELETE = 'NodeDelete', 694 695 /** 696 * register listener after a node is modified. 697 * @syscap SystemCapability.ArkUI.ArkUI.Full 698 * @since 10 699 */ 700 /** 701 * register listener after a node is modified. 702 * @syscap SystemCapability.ArkUI.ArkUI.Full 703 * @atomicservice 704 * @since 11 705 */ 706 NODE_MODIFY = 'NodeModify', 707 708 /** 709 * register listener after a node is moved. 710 * @syscap SystemCapability.ArkUI.ArkUI.Full 711 * @since 10 712 */ 713 /** 714 * register listener after a node is moved. 715 * @syscap SystemCapability.ArkUI.ArkUI.Full 716 * @atomicservice 717 * @since 11 718 */ 719 NODE_MOVE = 'NodeMove', 720} 721 722/** 723 * Declare class TreeListener. 724 * @syscap SystemCapability.ArkUI.ArkUI.Full 725 * @since 10 726 */ 727/** 728 * Declare class TreeListener. 729 * @syscap SystemCapability.ArkUI.ArkUI.Full 730 * @atomicservice 731 * @since 11 732 */ 733export class TreeListener { 734 public _events: [(callbackParam: CallbackParam) => void] | [] = []; 735 _once_events: [(callbackParam: CallbackParam) => void] | [] = []; 736 737 constructor() { 738 } 739 740 /** 741 * Event registration and processing. 742 * 743 * The event will not be destroyed after being processed. 744 * 745 * @param { type } event Registered Events. 746 * @param callback. 747 * @syscap SystemCapability.ArkUI.ArkUI.Full 748 * @since 10 749 */ 750 /** 751 * Event registration and processing. 752 * 753 * The event will not be destroyed after being processed. 754 * 755 * @param { type } event Registered Events. 756 * @param callback. 757 * @syscap SystemCapability.ArkUI.ArkUI.Full 758 * @atomicservice 759 * @since 11 760 */ 761 public on(type: TreeListenType, callback: (callbackParam: CallbackParam) => void): void { 762 if (Array.isArray(type)) { 763 for (let i = 0, l = type.length; i < l; i++) { 764 this.on((type as TreeListenType[])[i], callback); 765 } 766 } else { 767 (this._events[type] || (this._events[type] = [])).push(callback); 768 } 769 } 770 771 /** 772 * Event registration and processing. 773 * 774 * After the event is processed once, it will be destroyed. 775 * 776 * @param { type } event Registered Events. 777 * @param callback. 778 * @syscap SystemCapability.ArkUI.ArkUI.Full 779 * @since 10 780 */ 781 /** 782 * Event registration and processing. 783 * 784 * After the event is processed once, it will be destroyed. 785 * 786 * @param { type } event Registered Events. 787 * @param callback. 788 * @syscap SystemCapability.ArkUI.ArkUI.Full 789 * @atomicservice 790 * @since 11 791 */ 792 public once(type: TreeListenType, callback?: (callbackParam: CallbackParam) => void): void { 793 if (Array.isArray(type)) { 794 this.off(type, callback); 795 } else { 796 (this._once_events[type] || (this._once_events[type] = [])).push(callback); 797 } 798 } 799 800 /** 801 * Destroy event. 802 * 803 * @param type Registered Events. 804 * @param callback Event callback. 805 * @since 10 806 */ 807 public off(type: TreeListenType, callback?: (callbackParam: CallbackParam) => void): void { 808 if (type === null) { 809 this._events = []; 810 } 811 if (Array.isArray(type)) { 812 for (let i: number = 0, l: number = type.length; i < l; i++) { 813 this.off((type as TreeListenType[])[i], callback); 814 } 815 } 816 let cbs: [(callbackParam: CallbackParam) => void] = this._events[type]; 817 if (!cbs) { 818 return; 819 } 820 if (callback === null) { 821 this._events[type] = null; 822 } 823 let i: number = cbs.length; 824 while (i--) { 825 let cb: (callbackParam: CallbackParam) => void = cbs[i]; 826 if (cb === callback) { 827 cbs.splice(i, 1); 828 break; 829 } 830 } 831 } 832 833 /** 834 * Triggers all callbacks of an event with parameters. 835 * 836 * @param event Registered Events. 837 * @param argument Parameters returned by the callback event. 838 * @since 10 839 */ 840 public emit(event: TreeListenType, argument: CallbackParam) { 841 if (this._once_events[event]) { 842 let cbsOnce: ((callbackParam: CallbackParam) => void)[] = 843 Array.from<(callbackParam: CallbackParam) => void>(this._once_events[event]); 844 if (cbsOnce) { 845 for (let i: number = 0, l: number = cbsOnce.length; i < l; i++) { 846 try { 847 cbsOnce[i](argument); 848 } catch (e) { 849 throw new Error('once function callbacks error.'); 850 } 851 } 852 this._once_events[event] = null; 853 } 854 } else if (this._events[event]) { 855 let cbsOn: ((callbackParam: CallbackParam) => void)[] = 856 Array.from<(callbackParam: CallbackParam) => void>(this._events[event]); 857 if (cbsOn) { 858 for (let i: number = 0, l: number = cbsOn.length; i < l; i++) { 859 try { 860 cbsOn[i](argument); 861 } catch (e) { 862 throw new Error('on function callbacks error.'); 863 } 864 } 865 } 866 } 867 } 868} 869 870/** 871 * Declare class TreeListenerManager. 872 * @syscap SystemCapability.ArkUI.ArkUI.Full 873 * @since 10 874 */ 875/** 876 * Declare class TreeListenerManager. 877 * @syscap SystemCapability.ArkUI.ArkUI.Full 878 * @atomicservice 879 * @since 11 880 */ 881export class TreeListenerManager { 882 public static readonly APP_KEY_EVENT_BUS = 'app_key_event_bus'; 883 private appEventBus: TreeListener; 884 885 private constructor() { 886 this.appEventBus = new TreeListener(); 887 } 888 889 /** 890 * Get instance of treeListenerManager. 891 * @return treeListenerManager instance. 892 * @static 893 * @syscap SystemCapability.ArkUI.ArkUI.Full 894 * @since 10 895 */ 896 /** 897 * Get instance of treeListenerManager. 898 * @return treeListenerManager instance. 899 * @static 900 * @syscap SystemCapability.ArkUI.ArkUI.Full 901 * @atomicservice 902 * @since 11 903 */ 904 static getInstance(): TreeListenerManager { 905 if (AppStorage.Get('app_key_event_bus') === undefined) { 906 AppStorage.SetOrCreate('app_key_event_bus', new TreeListenerManager()) 907 } 908 return AppStorage.Get('app_key_event_bus') as TreeListenerManager; 909 } 910 911 /** 912 * Get treeListener. 913 * @return treeListener object 914 * @syscap SystemCapability.ArkUI.ArkUI.Full 915 * @since 10 916 */ 917 /** 918 * Get treeListener. 919 * @return treeListener object 920 * @syscap SystemCapability.ArkUI.ArkUI.Full 921 * @atomicservice 922 * @since 11 923 */ 924 public getTreeListener(): TreeListener { 925 return this.appEventBus; 926 } 927} 928 929/** 930 * Declare TreeView Component 931 * @syscap SystemCapability.ArkUI.ArkUI.Full 932 * @since 10 933 */ 934@Component 935export struct TreeView { 936 /** 937 * Node data source of TreeView 938 * @type TreeController 939 * @syscap SystemCapability.ArkUI.ArkUI.Full 940 * @since 10 941 */ 942 /** 943 * Node data source of TreeView 944 * @type TreeController 945 * @syscap SystemCapability.ArkUI.ArkUI.Full 946 * @atomicservice 947 * @since 11 948 */ 949 treeController: TreeController = new TreeController(); 950 @State nodeList: NodeInfo[] = []; 951 listNodeDataSource: ListNodeDataSource = new ListNodeDataSource(); 952 @State item: NodeInfo[] | null = null; 953 @State touchCount: number = 0; 954 @State dropSelectedIndex: number = 0; 955 @State viewLastIndex: number = -1; 956 @State followingSystemFontScale: boolean = false; 957 @State maxAppFontScale: number = 1; 958 @State listItemBgColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent'); 959 @Provide treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance(); 960 961 @Builder 962 NullBuilder() { 963 }; 964 965 @BuilderParam private listTreeViewMenu: () => void = this.NullBuilder; 966 private readonly MAX_CN_LENGTH: number = 254; 967 private readonly MAX_EN_LENGTH: number = 255; 968 private readonly INITIAL_INVALID_VALUE = -1; 969 private readonly MAX_TOUCH_DOWN_COUNT = 0; 970 private isMultiPress: boolean = false; 971 private touchDownCount: number = this.INITIAL_INVALID_VALUE; 972 private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener(); 973 private readonly itemPadding: ItemPadding = { 974 left: $r('sys.float.ohos_id_card_margin_start'), 975 right: $r('sys.float.ohos_id_card_margin_end'), 976 top: $r('sys.float.ohos_id_text_margin_vertical'), 977 bottom: $r('sys.float.ohos_id_text_margin_vertical'), 978 }; 979 private readonly textInputPadding: ItemPadding = 980 { left: $r('sys.float.padding_level0'), right: $r('sys.float.padding_level0'), 981 top: $r('sys.float.padding_level0'), bottom: $r('sys.float.padding_level0') } 982 983 onWillApplyTheme(theme: Theme) { 984 this.treeViewTheme.itemSelectedBgColor = theme.colors.interactiveSelect; 985 this.treeViewTheme.itemPressedBgColor = theme.colors.interactivePressed; 986 this.treeViewTheme.itemHoverBgColor = theme.colors.interactiveHover; 987 this.treeViewTheme.primaryTitleFontColor = theme.colors.fontPrimary; 988 this.treeViewTheme.secondaryTitleFontColor = theme.colors.fontSecondary; 989 this.treeViewTheme.primaryTitleActiveFontColor = theme.colors.interactiveActive; 990 this.treeViewTheme.borderFocusedColor = theme.colors.interactiveFocus; 991 this.treeViewTheme.leftIconColor = theme.colors.iconSecondary; 992 this.treeViewTheme.leftIconActiveColor = theme.colors.interactiveActive; 993 this.treeViewTheme.arrowIconColor = theme.colors.iconPrimary; 994 this.treeController.treeViewTheme = this.treeViewTheme; 995 } 996 997 aboutToAppear(): void { 998 if (this.treeController !== null) { 999 this.listNodeDataSource = this.treeController.getListNodeDataSource(); 1000 this.nodeList = this.treeController.getListNodeDataSource().listNode; 1001 this.item = this.treeController.getListNodeDataSource().listNode; 1002 } 1003 let uiContent: UIContext = this.getUIContext(); 1004 this.followingSystemFontScale = uiContent.isFollowingSystemFontScale(); 1005 this.maxAppFontScale = uiContent.getMaxFontScale(); 1006 } 1007 1008 decideFontScale() { 1009 let uiContent: UIContext = this.getUIContext(); 1010 let systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; 1011 if (!this.followingSystemFontScale) { 1012 return 1; 1013 } 1014 return Math.min(systemFontScale, this.maxAppFontScale, MAX_FONT_SCALE) 1015 } 1016 1017 @Builder 1018 popupForShowTitle(text: string | Resource, backgroundColor: Resource, fontColor: Resource) { 1019 Row() { 1020 Text(text) 1021 .fontSize($r('sys.float.ohos_id_text_size_body2')) 1022 .fontWeight('regular').fontColor(fontColor) 1023 .minFontScale(MIN_FONT_SCALE) 1024 .maxFontScale(this.decideFontScale()) 1025 }.backgroundColor(backgroundColor) 1026 .border({ radius: $r('sys.float.ohos_id_elements_margin_horizontal_l') }) 1027 .padding({ 1028 left: $r('sys.float.ohos_id_elements_margin_horizontal_l'), 1029 right: $r('sys.float.ohos_id_elements_margin_horizontal_l'), 1030 top: $r('sys.float.ohos_id_card_margin_middle'), 1031 bottom: $r('sys.float.ohos_id_card_margin_middle'), 1032 }) 1033 } 1034 1035 @Builder 1036 builder() { 1037 this.listTreeViewMenu() 1038 } 1039 1040 /* Set the popup of dragging node. */ 1041 @Builder 1042 draggingPopup(item: NodeInfo) { 1043 Row() { 1044 if (item.getNodeItem().imageNode) { 1045 Row() { 1046 Image(item.getNodeItem().imageNode?.normalSource) 1047 .objectFit(ImageFit.Contain) 1048 .height(item.getNodeItem().imageNode?.itemHeight) 1049 .width(item.getNodeItem().imageNode?.itemWidth) 1050 .opacity(this.listNodeDataSource.getDragPopupPara().imageOpacity) 1051 .matchTextDirection((item.getNodeItem().imageCollapse?.collapseSource === ARROW_RIGHT || item.getNodeItem() 1052 .imageCollapse?.collapseSource === ARROW_RIGHT_WITHE) ? true : false) 1053 } 1054 .backgroundColor(COLOR_IMAGE_ROW) 1055 .margin({ end: getLengthMetricsByResourceOrNumber(item.getNodeItem().imageNode?.itemRightMargin) }) 1056 .height(item.getNodeItem().imageNode?.itemHeight) 1057 .width(item.getNodeItem().imageNode?.itemWidth) 1058 } 1059 1060 Row() { 1061 if (item.getNodeItem().mainTitleNode && item.getIsShowTitle()) { 1062 Text(item.getNodeItem().mainTitleNode?.title) 1063 .maxLines(1) 1064 .minFontScale(MIN_FONT_SCALE) 1065 .maxFontScale(this.decideFontScale()) 1066 .fontSize(item.getNodeItem().mainTitleNode?.size) 1067 .fontColor(this.listNodeDataSource.getDragPopupPara().fontColor) 1068 .fontWeight(this.listNodeDataSource.getDragPopupPara().fontWeight) 1069 .textOverflow({ overflow: TextOverflow.Ellipsis }) 1070 } 1071 } 1072 .constraintSize({ 1073 minWidth: item.getNodeItem().imageNode ? 1074 this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth1 : 1075 this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth2, 1076 maxWidth: item.getNodeItem().imageNode ? 1077 this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth1 : 1078 this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth2, 1079 }) 1080 } 1081 .constraintSize({ 1082 minWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.minWidth, 1083 maxWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.maxWidth, 1084 }) 1085 .height(this.listNodeDataSource.getDragPopupPara().height) 1086 .backgroundColor(this.listNodeDataSource.getDragPopupPara().backgroundColor) 1087 .padding({ 1088 start: LengthMetrics.resource(this.listNodeDataSource.getDragPopupPara().padding.left), 1089 end: LengthMetrics.resource(this.listNodeDataSource.getDragPopupPara().padding.right), 1090 }) 1091 .shadow({ 1092 radius: $r('sys.float.ohos_id_corner_radius_default_m'), 1093 color: SHADOW_COLOR, 1094 offsetY: 0, 1095 }) 1096 .borderRadius(this.listNodeDataSource.getDragPopupPara().borderRadius) // need to doubleCheck. 1097 } 1098 1099 clearLastIndexColor(): void { 1100 if (this.viewLastIndex === -1 || this.viewLastIndex >= this.nodeList.length) { 1101 return; 1102 } 1103 this.setImageSources(this.viewLastIndex, InteractionStatus.NORMAL); 1104 1105 this.nodeList[this.viewLastIndex].setNodeColor($r('sys.color.ohos_id_color_background_transparent')) 1106 this.nodeList[this.viewLastIndex].fontColor = this.treeViewTheme.primaryTitleFontColor; 1107 this.listNodeDataSource.listNode[this.viewLastIndex].setNodeColor($r('sys.color.ohos_id_color_background_transparent')); 1108 this.listNodeDataSource.listNode[this.viewLastIndex].fontColor = this.treeViewTheme.primaryTitleFontColor; 1109 this.listNodeDataSource.listNode[this.viewLastIndex].setIsSelected(false); 1110 this.listNodeDataSource.setImageSource(this.viewLastIndex, InteractionStatus.NORMAL); 1111 } 1112 1113 setImageSources(index: number, interactionStatus: InteractionStatus): void { 1114 let nodeInfo: NodeInfo = this.nodeList[index]; 1115 nodeInfo.setIsSelected(interactionStatus === InteractionStatus.SELECTED || 1116 interactionStatus === InteractionStatus.EDIT || interactionStatus === InteractionStatus.FINISH_EDIT); 1117 if (nodeInfo.getNodeItem().mainTitleNode !== null && interactionStatus !== InteractionStatus.DRAG_INSERT && 1118 interactionStatus !== InteractionStatus.FINISH_DRAG_INSERT) { 1119 nodeInfo.getNodeItem().mainTitleNode?.setMainTitleSelected(interactionStatus === InteractionStatus.SELECTED || 1120 interactionStatus === InteractionStatus.FINISH_EDIT); 1121 } 1122 if (nodeInfo.getNodeItem().imageNode !== null) { 1123 nodeInfo.getNodeItem().imageNode?.setImageSource(interactionStatus); 1124 } 1125 } 1126 1127 build() { 1128 List({}) { 1129 LazyForEach(this.listNodeDataSource, (itemInner: NodeInfo) => { 1130 ListItem() { 1131 Row() { 1132 TreeViewInner({ 1133 item: itemInner, 1134 listNodeDataSource: this.listNodeDataSource, 1135 index: this.listNodeDataSource.findIndex(itemInner.getNodeCurrentNodeId()), 1136 listTreeViewMenu: this.listTreeViewMenu, 1137 }) 1138 } 1139 .onTouch((event: TouchEvent) => { 1140 this.viewLastIndex = this.listNodeDataSource.getLastIndex(); 1141 let index: number = this.listNodeDataSource.findIndex(itemInner.getNodeCurrentNodeId()); 1142 1143 if (event.type === TouchType.Down) { 1144 if (index !== this.viewLastIndex) { 1145 this.clearLastIndexColor(); 1146 this.listNodeDataSource.lastIndex = index; 1147 this.listNodeDataSource.setClickIndex(index); 1148 } 1149 } 1150 if (event.type === TouchType.Up) { 1151 this.listNodeDataSource.listNode[index].setIsSelected(true); 1152 this.listNodeDataSource.setImageSource(index, InteractionStatus.SELECTED); 1153 if (this.listNodeDataSource.listNode[index].getNodeItem().imageNode !== null) { 1154 this.listNodeDataSource.listNode[index].imageSource = this.listNodeDataSource.listNode[index] 1155 .getNodeItem().imageNode?.source; 1156 } 1157 1158 if (index !== this.viewLastIndex) { 1159 this.clearLastIndexColor(); 1160 this.listNodeDataSource.lastIndex = index; 1161 this.listNodeDataSource.setClickIndex(index); 1162 } 1163 this.viewLastIndex = index; 1164 } 1165 1166 if (this.listNodeDataSource.getLastIndex() !== -1 && index !== this.listNodeDataSource.getLastIndex()) { 1167 this.listNodeDataSource.setPopUpInfo( 1168 PopUpType.WARNINGS, 1169 InputError.NONE, 1170 false, 1171 this.listNodeDataSource.getLastIndex() 1172 ); 1173 this.listNodeDataSource.setItemVisibilityOnEdit( 1174 this.listNodeDataSource.getLastIndex(), 1175 MenuOperation.COMMIT_NODE 1176 ); 1177 } 1178 }) 1179 } 1180 .width('100%') 1181 .height(itemInner.getListItemHeight()) 1182 .padding({ 1183 start: LengthMetrics.resource(this.itemPadding.left), 1184 end: LengthMetrics.resource(this.itemPadding.right) 1185 }) 1186 .align(Alignment.Start) 1187 .onDragStart((event: DragEvent, extraParams: string) => { 1188 if (this.listNodeDataSource.getIsDrag() || this.listNodeDataSource.getIsInnerDrag() || this.isMultiPress) { 1189 hilog.error(LOG_CODE, TAG, 'drag error, a item has been dragged'); 1190 return; 1191 } 1192 this.dropSelectedIndex = JSON.parse(extraParams).selectedIndex; 1193 let currentNodeIndex: number = JSON.parse(extraParams).selectedIndex; 1194 let currentNodeInfo: NodeInfo = this.listNodeDataSource.getData(currentNodeIndex) as NodeInfo; 1195 let currentItemNodeId: number = itemInner.getNodeCurrentNodeId(); 1196 /* handle the situation of drag error, currentNodeIndex is not found in onDragStart. */ 1197 if (currentNodeIndex >= this.listNodeDataSource.totalCount() || currentNodeIndex === undefined) { 1198 hilog.error(LOG_CODE, TAG, 'drag error, currentNodeIndex is not found in onDragStart'); 1199 return; 1200 } 1201 1202 this.listNodeDataSource.setIsInnerDrag(true); 1203 this.listNodeDataSource.setIsDrag(true); 1204 this.listNodeDataSource.setCurrentNodeInfo(currentNodeInfo); 1205 this.listNodeDataSource.setDraggingCurrentNodeId(currentNodeInfo?.getNodeCurrentNodeId()); 1206 this.listNodeDataSource.setDraggingParentNodeId(currentNodeInfo?.getNodeParentNodeId()); 1207 1208 /* set the opacity of the dragging node. */ 1209 let draggingNodeOpacity: number = DRAG_OPACITY; 1210 this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity); 1211 this.listNodeDataSource.notifyDataChange(currentNodeIndex); 1212 1213 /** 1214 * handle the situation of drag is too fast,it attribute a fault to OH. 1215 * OH has Solved on real machine. 1216 */ 1217 if (currentItemNodeId !== currentNodeInfo?.getNodeCurrentNodeId()) { 1218 hilog.error(LOG_CODE, TAG, 'drag is too fast, it attribute a fault to OH'); 1219 this.listNodeDataSource.setIsDrag(false); 1220 return; 1221 } 1222 1223 return this.draggingPopup(currentNodeInfo); 1224 }) 1225 }, (item: NodeInfo) => JSON.stringify(item)) 1226 } 1227 1228 /* Move the dragged node. */ 1229 .onDragMove((event: DragEvent, extraParams: string) => { 1230 if (this.isMultiPress) { 1231 hilog.error(LOG_CODE, TAG, 'drag error, a item has been dragged'); 1232 return; 1233 } 1234 let nodeHeight: number = LIST_ITEM_HEIGHT; 1235 1236 /* flag the position of the focus on the node. */ 1237 let flag: Flag = Math.floor( 1238 event.getY() / 1239 (nodeHeight / FLAG_NUMBER)) % 1240 FLAG_NUMBER ? Flag.DOWN_FLAG : Flag.UP_FLAG; 1241 1242 /* Record the node position to which the dragged node moves. */ 1243 let index: number = JSON.parse(extraParams).insertIndex; 1244 1245 /* Handle the situation where the focus(index) exceeds the list area. */ 1246 let isOverBorder: boolean = false; 1247 if (index >= this.listNodeDataSource.totalCount()) { 1248 flag = Flag.DOWN_FLAG; 1249 index = this.listNodeDataSource.totalCount() - 1; 1250 this.listNodeDataSource.getData(index)?.setIsOverBorder(true); 1251 isOverBorder = true; 1252 } else { 1253 this.listNodeDataSource.getData(index)?.setIsOverBorder(false); 1254 } 1255 1256 let currentNodeInfo: NodeInfo | undefined = this.listNodeDataSource.getData(index); 1257 let currentNodeId: number | undefined = currentNodeInfo?.getNodeCurrentNodeId(); 1258 1259 /** 1260 * handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId"; 1261 * do not perform some functions. 1262 */ 1263 if (index !== this.listNodeDataSource.getLastPassIndex() && this.listNodeDataSource.getIsInnerDrag()) { 1264 let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(currentNodeId); 1265 if (isParentNodeOfInsertNode) { 1266 this.listNodeDataSource.setPassIndex(index); 1267 if (currentNodeId !== undefined) { 1268 this.listNodeDataSource.clearTimeOutAboutDelayHighLightAndExpand( 1269 this.listNodeDataSource.findIndex(currentNodeId)); 1270 } 1271 this.listNodeDataSource.setFlag(Flag.NONE); 1272 return; 1273 } 1274 } 1275 this.listNodeDataSource.setLastPassIndex(index); 1276 1277 /* Set the visibility of the flag line. */ 1278 this.listNodeDataSource.setVisibility(flag, index - 1, isOverBorder); 1279 1280 /* Automatically HighLight one second delay and expand after two second delay. */ 1281 if (currentNodeId !== undefined && currentNodeId !== this.listNodeDataSource.getDraggingCurrentNodeId()) { 1282 this.listNodeDataSource.delayHighLightAndExpandNode(this.listNodeDataSource.findIndex(currentNodeId), 1283 currentNodeId, index); 1284 } 1285 }) 1286 1287 /* DragEvent Enter. */ 1288 .onDragEnter((event: DragEvent, extraParams: string) => { 1289 if (this.listNodeDataSource.getIsInnerDrag()) { 1290 this.listNodeDataSource.setIsDrag(true); 1291 1292 /* set the opacity of the dragging node. */ 1293 let draggingNodeOpacity: number = DRAG_OPACITY; 1294 this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity); 1295 } 1296 }) 1297 1298 /* DragEvent Leave. */ 1299 .onDragLeave((event: DragEvent, extraParams: string) => { 1300 this.listNodeDataSource.hideLastLine(); 1301 this.listNodeDataSource.clearLastTimeoutHighLight(); 1302 this.listNodeDataSource.clearLastTimeoutExpand(); 1303 let draggingNodeOpacity: number = DRAG_OPACITY_NONE; 1304 this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity); 1305 this.listNodeDataSource.setIsDrag(false); 1306 this.listNodeDataSource.notifyDataReload(); 1307 }) 1308 1309 /* DragEvent Drop. */ 1310 .onDrop((event: DragEvent, extraParams: string) => { 1311 this.listNodeDataSource.clearLastTimeoutExpand(); 1312 let draggingNodeOpacity: number = DRAG_OPACITY_NONE; 1313 this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity); 1314 let insertNodeIndex: number = JSON.parse(extraParams).insertIndex; 1315 let currentNodeIndex: number = this.dropSelectedIndex; 1316 1317 if (currentNodeIndex - 1 > this.listNodeDataSource.totalCount() || currentNodeIndex === undefined) { 1318 hilog.error(LOG_CODE, TAG, 'drag error, currentNodeIndex is not found'); 1319 this.listNodeDataSource.setIsDrag(false); 1320 return; 1321 } 1322 1323 if (insertNodeIndex === this.listNodeDataSource.totalCount()) { 1324 hilog.info(LOG_CODE, TAG, 'need to insert into the position of the last line'); 1325 insertNodeIndex -= 1; 1326 } 1327 1328 let insertNodeInfo: NodeInfo | undefined = this.listNodeDataSource.getData(insertNodeIndex); 1329 if (insertNodeInfo === undefined) { 1330 return; 1331 } 1332 let insertNodeCurrentNodeId: number = insertNodeInfo.getNodeCurrentNodeId(); 1333 1334 /* outer node is move in. */ 1335 if (!this.listNodeDataSource.getIsDrag() || !this.listNodeDataSource.getIsInnerDrag()) { 1336 this.listNodeDataSource.clearLastTimeoutHighLight(); 1337 this.listNodeDataSource.setIsInnerDrag(false); 1338 this.listNodeDataSource.hideLastLine(); 1339 this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex(); 1340 this.listNodeDataSource.refreshSubtitle(insertNodeCurrentNodeId); 1341 this.listNodeDataSource.notifyDataReload(); 1342 return; 1343 } 1344 1345 let currentNodeInfo: NodeInfo | null = this.listNodeDataSource.getCurrentNodeInfo(); 1346 let insertNodeParentNodeId: number = insertNodeInfo.getNodeParentNodeId(); 1347 let draggingCurrentNodeId: number = this.listNodeDataSource.getDraggingCurrentNodeId(); 1348 let draggingParentNodeId: number = this.listNodeDataSource.getDraggingParentNodeId(); 1349 1350 /** 1351 * handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId". 1352 * drag is fail. 1353 */ 1354 let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(insertNodeCurrentNodeId); 1355 if (isParentNodeOfInsertNode) { 1356 this.listNodeDataSource.clearLastTimeoutHighLight(); 1357 this.listNodeDataSource.setIsInnerDrag(false); 1358 this.listNodeDataSource.hideLastLine(); 1359 this.listNodeDataSource.notifyDataChange(insertNodeIndex); 1360 this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex(); 1361 this.listNodeDataSource.setIsDrag(false); 1362 1363 /* set the position of focus. */ 1364 let currentFocusIndex: number = this.listNodeDataSource.findIndex(draggingCurrentNodeId); 1365 this.listNodeDataSource.setClickIndex(currentFocusIndex); 1366 this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex); 1367 return; 1368 } 1369 1370 /* Collapse drag node. */ 1371 if (this.listNodeDataSource.getExpandAndCollapseInfo(draggingCurrentNodeId) === NodeStatus.EXPAND) { 1372 this.listNodeDataSource.expandAndCollapseNode( 1373 this.listNodeDataSource.findIndex(draggingCurrentNodeId)); 1374 } 1375 1376 let flag: boolean = false; 1377 1378 /* Expand insert node. */ 1379 if (this.listNodeDataSource.getExpandAndCollapseInfo(insertNodeCurrentNodeId) === NodeStatus.COLLAPSE) { 1380 let currentIndex: number = this.listNodeDataSource.findIndex(insertNodeCurrentNodeId); 1381 if (this.listNodeDataSource.listNode[currentIndex].getIsHighLight()) { 1382 this.listNodeDataSource.expandAndCollapseNode(currentIndex); 1383 } 1384 flag = true; 1385 } 1386 1387 /* alter dragNode. */ 1388 this.listNodeDataSource.setLastDelayHighLightId(); 1389 if (currentNodeInfo !== null && draggingCurrentNodeId !== insertNodeCurrentNodeId) { 1390 this.listNodeDataSource.alterDragNode(insertNodeParentNodeId, insertNodeCurrentNodeId, 1391 draggingParentNodeId, draggingCurrentNodeId, currentNodeInfo); 1392 this.listNodeDataSource.hideLastLine(); 1393 } else { 1394 /*the position of dragNode is equal with the position of insertNode. */ 1395 this.listNodeDataSource.hideLastLine(); 1396 this.listNodeDataSource.setLastPassId(draggingCurrentNodeId); 1397 this.listNodeDataSource.hideLastLine(); 1398 } 1399 let lastDelayHighLightIndex: number = 1400 this.listNodeDataSource.findIndex(this.listNodeDataSource.getLastDelayHighLightId()); 1401 this.listNodeDataSource.setLastDelayHighLightIndex(lastDelayHighLightIndex); 1402 this.listNodeDataSource.clearLastTimeoutHighLight(); 1403 this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex(); 1404 this.listNodeDataSource.setIsDrag(false); 1405 1406 /* set the position of focus. */ 1407 let currentFocusIndex: number = this.listNodeDataSource.findIndex(draggingCurrentNodeId); 1408 this.listNodeDataSource.setClickIndex(currentFocusIndex); 1409 this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex); 1410 1411 /* innerDrag is over. */ 1412 this.listNodeDataSource.setIsInnerDrag(false); 1413 this.listNodeDataSource.notifyDataReload(); 1414 1415 this.listNodeDataSource.listNode[currentFocusIndex].fontColor = this.treeViewTheme.primaryTitleActiveFontColor; 1416 if (this.viewLastIndex !== -1 && currentNodeIndex !== this.viewLastIndex) { 1417 this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem() 1418 .mainTitleNode?.setMainTitleSelected(false); 1419 this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem() 1420 .mainTitleNode?.setMainTitleHighLight(false); 1421 } 1422 1423 if (this.listNodeDataSource.listNode[this.viewLastIndex] !== null) { 1424 this.listNodeDataSource.listNode[this.viewLastIndex].fontColor = this.treeViewTheme.primaryTitleFontColor; 1425 } 1426 1427 this.listNodeDataSource.lastIndex = this.viewLastIndex; 1428 if (this.listNodeDataSource.listNode[this.viewLastIndex]) { 1429 if (this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem() 1430 .imageNode !== null) { 1431 1432 this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem() 1433 .imageNode?.setImageSource(InteractionStatus.NORMAL); 1434 this.listNodeDataSource.listNode[this.viewLastIndex].imageSource = 1435 this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem() 1436 .imageNode?.source; 1437 } 1438 } 1439 1440 if (this.listNodeDataSource.listNode[this.viewLastIndex]) { 1441 this.listNodeDataSource.listNode[this.viewLastIndex] 1442 .setNodeColor($r('sys.color.ohos_id_color_background_transparent')); 1443 } 1444 1445 this.listNodeDataSource.lastIndex = currentFocusIndex; 1446 }) 1447 } 1448} 1449 1450/** 1451 * Declare CallbackParam 1452 * @type CallbackParam 1453 * @syscap SystemCapability.ArkUI.ArkUI.Full 1454 * @since 10 1455 */ 1456/** 1457 * Declare CallbackParam 1458 * @type CallbackParam 1459 * @syscap SystemCapability.ArkUI.ArkUI.Full 1460 * @atomicservice 1461 * @since 11 1462 */ 1463export interface CallbackParam { 1464 /** 1465 * Get the currentNodeId. 1466 * @type { number } 1467 * @syscap SystemCapability.ArkUI.ArkUI.Full 1468 * @since 10 1469 */ 1470 /** 1471 * Get the currentNodeId. 1472 * @type { number } 1473 * @syscap SystemCapability.ArkUI.ArkUI.Full 1474 * @atomicservice 1475 * @since 11 1476 */ 1477 currentNodeId: number, 1478 1479 /** 1480 * Get the parentNodeId. 1481 * @type { number } 1482 * @syscap SystemCapability.ArkUI.ArkUI.Full 1483 * @since 10 1484 */ 1485 /** 1486 * Get the parentNodeId. 1487 * @type { number } 1488 * @syscap SystemCapability.ArkUI.ArkUI.Full 1489 * @atomicservice 1490 * @since 11 1491 */ 1492 parentNodeId?: number, 1493 1494 /** 1495 * Get the childIndex. 1496 * @type { number } 1497 * @syscap SystemCapability.ArkUI.ArkUI.Full 1498 * @since 10 1499 */ 1500 /** 1501 * Get the childIndex. 1502 * @type { number } 1503 * @syscap SystemCapability.ArkUI.ArkUI.Full 1504 * @atomicservice 1505 * @since 11 1506 */ 1507 childIndex?: number, 1508} 1509 1510/** 1511 * Declare NodeParam 1512 * @typedef NodeParam 1513 * @syscap SystemCapability.ArkUI.ArkUI.Full 1514 * @since 10 1515 */ 1516/** 1517 * Declare NodeParam 1518 * @typedef NodeParam 1519 * @syscap SystemCapability.ArkUI.ArkUI.Full 1520 * @atomicservice 1521 * @since 11 1522 */ 1523export interface NodeParam { 1524 /** 1525 * Set the parentNodeId. 1526 * @type { number } 1527 * @syscap SystemCapability.ArkUI.ArkUI.Full 1528 * @since 10 1529 */ 1530 /** 1531 * Set the parentNodeId. 1532 * @type { number } 1533 * @syscap SystemCapability.ArkUI.ArkUI.Full 1534 * @atomicservice 1535 * @since 11 1536 */ 1537 parentNodeId?: number, 1538 1539 /** 1540 * Set currentNodeId. 1541 * @type { number } 1542 * @syscap SystemCapability.ArkUI.ArkUI.Full 1543 * @since 10 1544 */ 1545 /** 1546 * Set currentNodeId. 1547 * @type { number } 1548 * @syscap SystemCapability.ArkUI.ArkUI.Full 1549 * @atomicservice 1550 * @since 11 1551 */ 1552 currentNodeId?: number, 1553 1554 /** 1555 * Set catalog whether is floder. 1556 * @type { boolean } 1557 * @syscap SystemCapability.ArkUI.ArkUI.Full 1558 * @since 10 1559 */ 1560 /** 1561 * Set catalog whether is floder. 1562 * @type { boolean } 1563 * @syscap SystemCapability.ArkUI.ArkUI.Full 1564 * @atomicservice 1565 * @since 11 1566 */ 1567 isFolder?: boolean, 1568 1569 /** 1570 * Set the icon resource. 1571 * @type { ResourceStr } 1572 * @syscap SystemCapability.ArkUI.ArkUI.Full 1573 * @since 10 1574 */ 1575 /** 1576 * Set the icon resource. 1577 * @type { ResourceStr } 1578 * @syscap SystemCapability.ArkUI.ArkUI.Full 1579 * @atomicservice 1580 * @since 11 1581 */ 1582 icon?: ResourceStr, 1583 1584 /** 1585 * Set selected icon resource. 1586 * @type { ResourceStr } 1587 * @syscap SystemCapability.ArkUI.ArkUI.Full 1588 * @since 10 1589 */ 1590 /** 1591 * Set selected icon resource. 1592 * @type { ResourceStr } 1593 * @syscap SystemCapability.ArkUI.ArkUI.Full 1594 * @atomicservice 1595 * @since 11 1596 */ 1597 selectedIcon?: ResourceStr, 1598 1599 /** 1600 * Set edit icon resource. 1601 * @type { ResourceStr } 1602 * @syscap SystemCapability.ArkUI.ArkUI.Full 1603 * @since 10 1604 */ 1605 /** 1606 * Set edit icon resource. 1607 * @type { ResourceStr } 1608 * @syscap SystemCapability.ArkUI.ArkUI.Full 1609 * @atomicservice 1610 * @since 11 1611 */ 1612 editIcon?: ResourceStr, 1613 1614 /** 1615 * Set primary title content. 1616 * @type { ResourceStr } 1617 * @syscap SystemCapability.ArkUI.ArkUI.Full 1618 * @since 10 1619 */ 1620 /** 1621 * Set primary title content. 1622 * @type { ResourceStr } 1623 * @syscap SystemCapability.ArkUI.ArkUI.Full 1624 * @atomicservice 1625 * @since 11 1626 */ 1627 primaryTitle?: ResourceStr, 1628 1629 /** 1630 * Set secondary title content. 1631 * @type { ResourceStr } 1632 * @syscap SystemCapability.ArkUI.ArkUI.Full 1633 * @since 10 1634 */ 1635 /** 1636 * Set secondary title content. 1637 * @type { ResourceStr } 1638 * @syscap SystemCapability.ArkUI.ArkUI.Full 1639 * @atomicservice 1640 * @since 11 1641 */ 1642 secondaryTitle?: ResourceStr, 1643 1644 /** 1645 * Set subcomponent binded on tree item. 1646 * @type { () => void } 1647 * @syscap SystemCapability.ArkUI.ArkUI.Full 1648 * @since 10 1649 */ 1650 /** 1651 * Set subcomponent binded on tree item. 1652 * @type { () => void } 1653 * @syscap SystemCapability.ArkUI.ArkUI.Full 1654 * @atomicservice 1655 * @since 11 1656 */ 1657 container?: () => void, 1658} 1659 1660/** 1661 * Declare TreeController. 1662 * @syscap SystemCapability.ArkUI.ArkUI.Full 1663 * @since 10 1664 */ 1665/** 1666 * Declare TreeController. 1667 * @syscap SystemCapability.ArkUI.ArkUI.Full 1668 * @atomicservice 1669 * @since 11 1670 */ 1671export class TreeController { 1672 public readonly ROOT_NODE_ID: number = -1; 1673 private nodeIdList: number[] = []; 1674 private listNodeDataSource: ListNodeDataSource = new ListNodeDataSource(); 1675 private initBuild: boolean = true; 1676 public treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance(); 1677 1678 public getListNodeDataSource(): ListNodeDataSource { 1679 return this.listNodeDataSource; 1680 } 1681 1682 public getClickNodeChildrenInfo(): NodeInfoView[] { 1683 let clickNodeId: number = this.listNodeDataSource.getClickNodeId(); 1684 return this.listNodeDataSource.getClickNodeChildrenInfo(clickNodeId); 1685 } 1686 1687 public getChildrenId(): number[] { 1688 let clickNodeId: number = this.listNodeDataSource.getClickNodeId(); 1689 return this.listNodeDataSource.getClickChildId(clickNodeId); 1690 } 1691 1692 /** 1693 * Delete a node. 1694 * Register an ON_ITEM_DELETE callback through the EventBus mechanism to obtain the IDs of all deleted nodes. 1695 * @syscap SystemCapability.ArkUI.ArkUI.Full 1696 * @since 10 1697 */ 1698 /** 1699 * Delete a node. 1700 * Register an ON_ITEM_DELETE callback through the EventBus mechanism to obtain the IDs of all deleted nodes. 1701 * @syscap SystemCapability.ArkUI.ArkUI.Full 1702 * @atomicservice 1703 * @since 11 1704 */ 1705 public removeNode(): void { 1706 let clickNodeId: number = this.listNodeDataSource.getClickNodeId(); 1707 if (clickNodeId < 0) { 1708 return; 1709 } 1710 let parentNodeId: number = this.listNodeDataSource.findParentNodeId(clickNodeId); 1711 let removeNodeIdList: number[] = this.listNodeDataSource.removeNode(clickNodeId, parentNodeId); 1712 this.listNodeDataSource.refreshData( 1713 MenuOperation.REMOVE_NODE, 1714 parentNodeId, 1715 removeNodeIdList 1716 ); 1717 this.nodeIdList.splice(this.nodeIdList.indexOf(clickNodeId), 1); 1718 this.listNodeDataSource.lastIndex = -1; 1719 } 1720 1721 /** 1722 * Modify the node name. 1723 * Register an ON_ITEM_MODIFY callback to obtain the ID, parent node ID, and node name of the modified node. 1724 * @syscap SystemCapability.ArkUI.ArkUI.Full 1725 * @since 10 1726 */ 1727 /** 1728 * Modify the node name. 1729 * Register an ON_ITEM_MODIFY callback to obtain the ID, parent node ID, and node name of the modified node. 1730 * @syscap SystemCapability.ArkUI.ArkUI.Full 1731 * @atomicservice 1732 * @since 11 1733 */ 1734 public modifyNode(): void { 1735 let clickNodeId: number = this.listNodeDataSource.getClickNodeId(); 1736 this.listNodeDataSource.setItemVisibilityOnEdit(clickNodeId, MenuOperation.MODIFY_NODE); 1737 } 1738 1739 /** 1740 * add new node 1741 * 1742 * @param initBuild whether is in initialization process 1743 */ 1744 public add(initBuild: boolean): void { 1745 let clickNodeId: number = this.listNodeDataSource.getClickNodeId(); 1746 if (clickNodeId === this.listNodeDataSource.ROOT_NODE_ID || !this.listNodeDataSource.getIsFolder(clickNodeId)) { 1747 return; 1748 } 1749 let newNodeParam: NodeParam = this.listNodeDataSource.getNewNodeParam(clickNodeId); 1750 this.nodeIdList.push(this.nodeIdList[this.nodeIdList.length - 1] + 1); 1751 let newNodeId: number = this.nodeIdList[this.nodeIdList.length - 1]; 1752 let addNodeResult: boolean = this.listNodeDataSource.addNode(clickNodeId, newNodeId, 1753 { 1754 isFolder: newNodeParam.isFolder, 1755 icon: newNodeParam.icon, 1756 selectedIcon: newNodeParam.selectedIcon, 1757 editIcon: newNodeParam.editIcon, 1758 primaryTitle: '新建文件夹', 1759 container: newNodeParam.container, 1760 secondaryTitle: newNodeParam.secondaryTitle as ResourceStr, 1761 }, initBuild); 1762 if (!addNodeResult) { 1763 return; 1764 } 1765 this.listNodeDataSource.refreshData(MenuOperation.ADD_NODE, clickNodeId, [newNodeId]); 1766 1767 this.listNodeDataSource.setPopUpInfo( 1768 PopUpType.WARNINGS, 1769 InputError.NONE, 1770 false, 1771 this.listNodeDataSource.getLastIndex() 1772 ); 1773 this.listNodeDataSource.setItemVisibilityOnEdit( 1774 this.listNodeDataSource.getLastIndex(), 1775 MenuOperation.COMMIT_NODE 1776 ); 1777 this.listNodeDataSource.listNode[this.listNodeDataSource.getLastIndex()] 1778 .setFontColor(this.treeViewTheme.primaryTitleFontColor); 1779 let newNodeIndex: number = this.listNodeDataSource.findIndex(newNodeId); 1780 this.listNodeDataSource.setClickIndex(newNodeIndex); 1781 this.listNodeDataSource.handleEvent(Event.TOUCH_UP, newNodeIndex); 1782 } 1783 1784 /** 1785 * Initialize the interface of the tree view. This interface is used to generate ListNodeDataSource data. 1786 * addNode is only designed for initialization. It can only be invoked during initialization. 1787 * 1788 * A maximum of 50 directory levels can be added. 1789 * 1790 * @param nodeParam Configuration information of the newly added node. 1791 * 1792 * For details, see the comment description of NodeParam. 1793 * @return ListTreeNode Tree view component proxy class. 1794 * @syscap SystemCapability.ArkUI.ArkUI.Full 1795 * @since 10 1796 */ 1797 /** 1798 * Initialize the interface of the tree view. This interface is used to generate ListNodeDataSource data. 1799 * addNode is only designed for initialization. It can only be invoked during initialization. 1800 * 1801 * A maximum of 50 directory levels can be added. 1802 * 1803 * @param nodeParam Configuration information of the newly added node. 1804 * 1805 * For details, see the comment description of NodeParam. 1806 * @return ListTreeNode Tree view component proxy class. 1807 * @syscap SystemCapability.ArkUI.ArkUI.Full 1808 * @atomicservice 1809 * @since 11 1810 */ 1811 public addNode(nodeParam?: NodeParam): TreeController { 1812 if (nodeParam === undefined) { 1813 this.add(this.initBuild); 1814 return this; 1815 } else { 1816 let addNodeResult: boolean = false; 1817 if (nodeParam.primaryTitle !== undefined && 1818 !this.listNodeDataSource.checkMainTitleIsValid(nodeParam.primaryTitle.toString())) { 1819 throw new Error('ListTreeNode[addNode]: ' + 1820 'The directory name cannot contain the following characters\ /: *? "< > | or exceeds the maximum length.'); 1821 return this; 1822 } 1823 if (nodeParam.primaryTitle === null && nodeParam.icon === null) { 1824 throw new Error('ListTreeNode[addNode]: ' + 1825 'The icon and directory name cannot be empty at the same time.'); 1826 return this; 1827 } 1828 if (nodeParam.currentNodeId === this.ROOT_NODE_ID || nodeParam.currentNodeId === null) { 1829 throw new Error('ListTreeNode[addNode]: currentNodeId can not be -1 or null.'); 1830 return this; 1831 } 1832 if (nodeParam.currentNodeId !== undefined) { 1833 this.nodeIdList.push(nodeParam.currentNodeId); 1834 } 1835 if (nodeParam.parentNodeId !== undefined) { 1836 if (nodeParam.currentNodeId !== undefined) { 1837 addNodeResult = 1838 this.listNodeDataSource.addNode(nodeParam.parentNodeId, nodeParam.currentNodeId, nodeParam, this.initBuild); 1839 } 1840 } 1841 if (!addNodeResult) { 1842 return this; 1843 } 1844 if (!this.initBuild && nodeParam.parentNodeId !== undefined) { 1845 let newNodeId: number = this.nodeIdList[this.nodeIdList.length - 1]; 1846 this.listNodeDataSource.refreshData( 1847 MenuOperation.ADD_NODE, 1848 nodeParam.parentNodeId, 1849 [newNodeId] 1850 ); 1851 } 1852 return this; 1853 } 1854 } 1855 1856 /** 1857 * this interface is called when a secondaryTitle needs to be updated. 1858 * 1859 * @Param parentId ID of the parent node. 1860 * @Param parentSubTitle secondaryTitle of parent node. 1861 * @Param currentSubTitle secondaryTitle of current node. 1862 * 1863 * @syscap SystemCapability.ArkUI.ArkUI.Full 1864 * @since 10 1865 */ 1866 /** 1867 * this interface is called when a secondaryTitle needs to be updated. 1868 * 1869 * @Param parentId ID of the parent node. 1870 * @Param parentSubTitle secondaryTitle of parent node. 1871 * @Param currentSubTitle secondaryTitle of current node. 1872 * 1873 * @syscap SystemCapability.ArkUI.ArkUI.Full 1874 * @atomicservice 1875 * @since 11 1876 */ 1877 public refreshNode(parentId: number, parentSubTitle: ResourceStr, CurrentSubtitle: ResourceStr): void { 1878 this.listNodeDataSource.setNodeSubtitlePara(parentId, parentSubTitle, CurrentSubtitle); 1879 } 1880 1881 /** 1882 * After the initialization is complete by calling the addNode interface, 1883 * call this interface to complete initialization. 1884 * 1885 * This interface must be called when you finish initializing the ListTreeView by addNode. 1886 * @syscap SystemCapability.ArkUI.ArkUI.Full 1887 * @since 10 1888 */ 1889 /** 1890 * After the initialization is complete by calling the addNode interface, 1891 * call this interface to complete initialization. 1892 * 1893 * This interface must be called when you finish initializing the ListTreeView by addNode. 1894 * @syscap SystemCapability.ArkUI.ArkUI.Full 1895 * @atomicservice 1896 * @since 11 1897 */ 1898 public buildDone(): void { 1899 this.listNodeDataSource.initSection(); 1900 this.listNodeDataSource.delayInit(); 1901 this.listNodeDataSource.updateAllChildNum(); 1902 delaySortNodeIdList(this.nodeIdList); 1903 this.initBuild = false; 1904 } 1905} 1906 1907class BasicDataSource implements IDataSource { 1908 private listeners: DataChangeListener[] = [] 1909 1910 public totalCount(): number { 1911 return 0; 1912 } 1913 1914 public getData(index: number): NodeInfo | undefined { 1915 return undefined; 1916 } 1917 1918 registerDataChangeListener(listener: DataChangeListener): void { 1919 if (this.listeners.indexOf(listener) < 0) { 1920 this.listeners.push(listener); 1921 } 1922 } 1923 1924 unregisterDataChangeListener(listener: DataChangeListener): void { 1925 const pos = this.listeners.indexOf(listener); 1926 if (pos >= 0) { 1927 this.listeners.splice(pos, 1); 1928 } 1929 } 1930 1931 notifyDataReload(): void { 1932 this.listeners.forEach(listener => { 1933 listener.onDataReloaded(); 1934 }) 1935 } 1936 1937 notifyDataAdd(index: number): void { 1938 this.listeners.forEach(listener => { 1939 listener.onDataAdd(index); 1940 }) 1941 } 1942 1943 notifyDataChange(index: number | undefined): void { 1944 if (index === undefined) { 1945 return; 1946 } 1947 this.listeners.forEach(listener => { 1948 listener.onDataChange(index); 1949 }) 1950 } 1951 1952 notifyDataDelete(index: number): void { 1953 this.listeners.forEach(listener => { 1954 listener.onDataDelete(index); 1955 }) 1956 } 1957 1958 notifyDataMove(from: number, to: number): void { 1959 this.listeners.forEach(listener => { 1960 listener.onDataMove(from, to); 1961 }) 1962 } 1963} 1964 1965/** 1966 * delay update all parentnodes childNum 1967 * 1968 * @param isAdd whether addNode or delete node 1969 * @param count node count 1970 * @param nodeIdNodeItemMap nodeId and nodeItem relation map 1971 * @param updateNodeIdList nodeId list whose childNum need update 1972 */ 1973function delayUpdateParentChildNum(isAdd: boolean, count: number, 1974 nodeIdNodeItemMap: Map<number, NodeItem>, updateNodeIdList: Array<number>): void { 1975 let taskId: number = setTimeout(() => { 1976 updateNodeIdList.forEach((parentNodeId) => { 1977 updateParentChildNumHandler(parentNodeId, nodeIdNodeItemMap, isAdd, count); 1978 }); 1979 clearTimeout(taskId); 1980 }, DELAY_TIME); 1981} 1982 1983/** 1984 * delay update all parentnodes child number handler 1985 * 1986 * @param parentNodeId parent node id 1987 * @param nodeIdNodeItemMap nodeId and nodeItem relation map 1988 * @param isAdd whether addNode or delete node 1989 * @param count node count 1990 */ 1991function updateParentChildNumHandler(parentNodeId: number, nodeIdNodeItemMap: Map<number, NodeItem>, 1992 isAdd: boolean, count: number) { 1993 let tmpParentNodeId: number = parentNodeId; 1994 while (tmpParentNodeId >= 0) { 1995 if (nodeIdNodeItemMap.has(tmpParentNodeId)) { 1996 let parent: NodeItem = nodeIdNodeItemMap.get(tmpParentNodeId) as NodeItem; 1997 parent.getChildNodeInfo().allChildNum = 1998 isAdd ? parent.getChildNodeInfo().allChildNum + count : parent.getChildNodeInfo().allChildNum - count; 1999 tmpParentNodeId = parent.parentNodeId; 2000 } else { 2001 hilog.error(LOG_CODE, TAG, 'updateParentChildNumHandler: parent node not found'); 2002 break; 2003 } 2004 } 2005} 2006 2007/** 2008 * delay sort nodeId list 2009 * 2010 * @param nodeIdList nodeId list 2011 */ 2012function delaySortNodeIdList(nodeIdList: Array<number>): void { 2013 let taskId: number = setTimeout(() => { 2014 nodeIdList.sort((a, b) => a - b); 2015 clearTimeout(taskId); 2016 }, DELAY_TIME); 2017} 2018 2019class ListNodeDataSource extends BasicDataSource { 2020 public readonly ROOT_NODE_ID = -1; 2021 public _root: NodeItem = new NodeItem(emptyNodeInfo); 2022 private readonly maxNodeLevel: number = 50; 2023 private readonly MAX_CN_LENGTH: number = 254; 2024 private readonly MAX_EN_LENGTH: number = 255; 2025 private readonly INITIAL_INVALID_VALUE = -1; 2026 public listNode: NodeInfo[] = []; 2027 public loadedListNode: NodeInfo[] = []; 2028 public nodeIdNodeItemMap: Map<number, NodeItem> = new Map<number, NodeItem>(); 2029 public nodeIdNodeParamMap: Map<number, NodeParam> = new Map<number, NodeParam>(); 2030 public lastIndex: number = -1; // record the last focused node. 2031 public thisIndex: number = -1; // records clicked nodes in the current period. 2032 private modifyNodeIndex: number = -1; // records the nodes edited in the current period. 2033 public modifyNodeId: number = -1; 2034 private currentOperation?: MenuOperation; 2035 private expandAndCollapseInfo: Map<number, NodeStatus> = new Map<number, NodeStatus>(); 2036 public loadedNodeIdAndIndexMap: Map<number, number> = new Map<number, number>(); 2037 public nodeIdAndNodeIndexMap: Map<number, number> = new Map<number, number>(); 2038 private isTouchDown: boolean = false; 2039 private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener(); 2040 /* parameter of the drag event. */ 2041 private isInnerDrag: boolean = false; // Judge whether it is an internal drag event. 2042 // It is used to handle events(For example, prevent press events) during global drag. 2043 private isDrag: boolean = false; 2044 private draggingCurrentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the current ID of the dragged node. 2045 private draggingParentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the parent ID of the dragged node. 2046 private currentNodeInfo: NodeInfo | null = null; // To solve the problem of currentIndex missed in onDrop event. 2047 private listItemOpacity: number = 1; // It is used to set the opacity of the node when dragged. 2048 private lastPassIndex: number = this.INITIAL_INVALID_VALUE; // record the last passing node index in drag. 2049 private lastPassId?: number = this.INITIAL_INVALID_VALUE; // record the last passing node Id in drag. 2050 private thisPassIndex: number = this.INITIAL_INVALID_VALUE; // record the current passing node in drag. 2051 // record last passing node in delay expand event. 2052 private lastDelayExpandIndex: number = this.INITIAL_INVALID_VALUE; 2053 private timeoutExpandId: number = this.INITIAL_INVALID_VALUE; 2054 private lastTimeoutExpandId: number = this.INITIAL_INVALID_VALUE; 2055 private clearTimeoutExpandId: number = this.INITIAL_INVALID_VALUE; 2056 private timeoutHighLightId: number = this.INITIAL_INVALID_VALUE; 2057 private lastTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE; 2058 private clearTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE; 2059 // record last passing node in HighLight event. 2060 private lastDelayHighLightIndex: number = this.INITIAL_INVALID_VALUE; 2061 //record last passing node Id in HighLight event. 2062 private lastDelayHighLightId: number = this.INITIAL_INVALID_VALUE; 2063 private nodeIdAndSubtitleMap: Map<number, ResourceStr> = new Map<number, ResourceStr>(); 2064 private flag: Flag = Flag.NONE; 2065 private selectedParentNodeId: number = this.INITIAL_INVALID_VALUE; 2066 private selectedParentNodeSubtitle: ResourceStr = ''; 2067 private insertNodeSubtitle: ResourceStr = ''; 2068 private currentFocusNodeId: number = this.INITIAL_INVALID_VALUE; 2069 private lastFocusNodeId: number = this.INITIAL_INVALID_VALUE; 2070 private addFocusNodeId: number = this.INITIAL_INVALID_VALUE; 2071 private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance(); 2072 public updateNodeIdList: number[] = []; 2073 public readonly FLAG_LINE: FlagLine = { 2074 flagLineHeight: FLAG_LINE_HEIGHT, 2075 flagLineColor: $r('sys.color.ohos_id_color_emphasize'), 2076 xOffset: X_OFF_SET, 2077 yTopOffset: Y_OFF_SET, 2078 yBottomOffset: Y_BOTTOM_OFF_SET, 2079 yBasePlateOffset: Y_BASE_PLATE_OFF_SET, 2080 } 2081 private readonly DRAG_POPUP: DragPopup = { 2082 floorConstraintSize: { minWidth: FLOOR_MIN_WIDTH, maxWidth: FLOOR_MAX_WIDTH }, 2083 textConstraintSize: { 2084 minWidth1: TEXT_MIN_WIDTH, 2085 maxWidth1: TEXT_MAX_WIDTH, 2086 minWidth2: MIN_WIDTH, 2087 maxWidth2: MAX_WIDTH, 2088 }, 2089 padding: { left: $r('sys.float.padding_level4'), right: $r('sys.float.padding_level4') }, 2090 backgroundColor: COLOR_IMAGE_EDIT, 2091 height: GRAG_POP_UP_HEIGHT, 2092 shadow: { 2093 radius: $r('sys.float.ohos_id_corner_radius_default_m'), 2094 color: SHADOW_COLOR, 2095 offsetX: 0, 2096 offsetY: SHADOW_OFFSETY, 2097 }, 2098 borderRadius: $r('sys.float.ohos_id_corner_radius_clicked'), 2099 fontColor: this.treeViewTheme.primaryTitleFontColor, 2100 fontSize: $r('sys.float.ohos_id_text_size_body1'), 2101 fontWeight: FontWeight.Regular, 2102 imageOpacity: $r('sys.float.ohos_id_alpha_content_fourth') 2103 }; 2104 private readonly subTitle: SubTitleStyle = { 2105 normalFontColor: this.treeViewTheme.secondaryTitleFontColor, 2106 highLightFontColor: $r('sys.color.ohos_id_color_primary_contrary'), 2107 fontSize: $r('sys.float.ohos_id_text_size_body2'), 2108 fontWeight: FontWeight.Regular, 2109 margin: { left: $r('sys.float.padding_level2'), right: $r('sys.float.padding_level12') } 2110 } 2111 2112 constructor() { 2113 super(); 2114 this._root.nodeLevel = -1; 2115 this.nodeIdNodeItemMap.set(-1, this._root); 2116 this.nodeIdNodeParamMap.set(-1, emptyNodeInfo); 2117 } 2118 2119 private checkIndex(index: number): boolean { 2120 if (index < 0 || index >= this.listNode.length) { 2121 hilog.error(LOG_CODE, TAG, 'check index fail'); 2122 return false; 2123 } 2124 return true; 2125 } 2126 2127 public changeNodeColor(index: number, color: ResourceColor | undefined): void { 2128 if (!this.checkIndex(index)) { 2129 return; 2130 } 2131 this.listNode[index].setNodeColor(color); 2132 this.listNode[index].setNodeBorder(false); 2133 } 2134 2135 private getNodeColor(index: number): ResourceColor { 2136 return this.listNode[index].getNodeColor(); 2137 } 2138 2139 private handleFocusEffect(index: number, isClearFocusStatus: boolean): void { 2140 if (this.listNode[index].getNodeIsShow()) { 2141 this.listNode[index].setNodeBorder(isClearFocusStatus); 2142 } 2143 } 2144 2145 public setImageSource(index: number, interactionStatus: InteractionStatus): void { 2146 if (!this.checkIndex(index)) { 2147 return; 2148 } 2149 let nodeInfo: NodeInfo = this.listNode[index]; 2150 nodeInfo.setIsSelected(interactionStatus === InteractionStatus.SELECTED || 2151 interactionStatus === InteractionStatus.EDIT || interactionStatus === InteractionStatus.FINISH_EDIT); 2152 if (nodeInfo.getNodeItem().mainTitleNode !== null && interactionStatus !== InteractionStatus.DRAG_INSERT && 2153 interactionStatus !== InteractionStatus.FINISH_DRAG_INSERT) { 2154 nodeInfo.getNodeItem().mainTitleNode?.setMainTitleSelected(interactionStatus === InteractionStatus.SELECTED || 2155 interactionStatus === InteractionStatus.FINISH_EDIT); 2156 } 2157 if (nodeInfo.getNodeItem().imageNode !== null) { 2158 nodeInfo.getNodeItem().imageNode?.setImageSource(interactionStatus); 2159 } 2160 } 2161 2162 private setImageCollapseSource(index: number, interactionStatus: InteractionStatus): void { 2163 let nodeInfo: NodeInfo = this.listNode[index]; 2164 if (nodeInfo.getNodeItem().imageCollapse !== undefined) { 2165 nodeInfo.getNodeItem().imageCollapse = CollapseImageNodeFlyweightFactory.getCollapseImageNode(interactionStatus, 2166 this.expandAndCollapseInfo.get(nodeInfo.getNodeCurrentNodeId()), nodeInfo.getNodeItem().imageCollapse?.type); 2167 } 2168 } 2169 2170 public clearLastIndexStatus(): void { 2171 if (!this.checkIndex(this.lastIndex)) { 2172 return; 2173 } 2174 this.setImageSource(this.lastIndex, InteractionStatus.NORMAL); 2175 this.changeNodeColor(this.lastIndex, this.listNode[this.lastIndex].getNodeStatus().normal); 2176 this.handleFocusEffect(this.lastIndex, false); 2177 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[this.lastIndex].getNodeCurrentNodeId())); 2178 } 2179 2180 private loadedListNodeFunction(): void { 2181 let index: number = 0; 2182 this.loadedNodeIdAndIndexMap.clear(); 2183 this.nodeIdAndNodeIndexMap.clear(); 2184 this.loadedListNode.splice(0, this.loadedListNode.length); 2185 for (let i: number = 0; i < this.listNode.length; i++) { 2186 this.nodeIdAndNodeIndexMap.set(this.listNode[i].getNodeCurrentNodeId(), i); 2187 if (this.listNode[i].getNodeIsShow()) { 2188 this.loadedNodeIdAndIndexMap.set(this.listNode[i].getNodeCurrentNodeId(), index++); 2189 this.loadedListNode.push(this.listNode[i]); 2190 } 2191 } 2192 } 2193 2194 private changeNodeStatus(clickIndex: number): void { 2195 if (clickIndex >= this.listNode.length) { 2196 hilog.error(LOG_CODE, TAG, 'changeNodeStatus clickIndex error.'); 2197 return; 2198 } 2199 let thisIndex: number = clickIndex; 2200 let nodeId: number = this.listNode[clickIndex].getNodeCurrentNodeId(); 2201 if (this.expandAndCollapseInfo.get(nodeId) === NodeStatus.EXPAND) { 2202 this.expandAndCollapseInfo.set(nodeId, NodeStatus.COLLAPSE); 2203 this.listNode[thisIndex].getNodeItem() 2204 .imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.COLLAPSE, 2205 this.listNode[thisIndex].getNodeItem().imageCollapse?.isCollapse); 2206 } else if (this.expandAndCollapseInfo.get(nodeId) === NodeStatus.COLLAPSE) { 2207 this.expandAndCollapseInfo.set(nodeId, NodeStatus.EXPAND); 2208 this.listNode[thisIndex].getNodeItem() 2209 .imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.EXPAND, 2210 this.listNode[thisIndex].getNodeItem().imageCollapse?.isCollapse); 2211 } 2212 } 2213 2214 private handleExpandAndCollapse(clickIndex: number, isRefreshList: boolean): void { 2215 if (clickIndex >= this.listNode.length) { 2216 hilog.error(LOG_CODE, TAG, 'handleExpandAndCollapse clickIndex error.'); 2217 return; 2218 } 2219 let thisIndex: number = clickIndex; 2220 let nodeId: number = this.listNode[thisIndex].getNodeCurrentNodeId(); 2221 if (!this.expandAndCollapseInfo.has(nodeId)) { 2222 return; 2223 } 2224 2225 let rootNodeStatus: NodeStatus | undefined = this.expandAndCollapseInfo.get(nodeId); 2226 if (this.listNode[thisIndex].getChildNodeInfo().isHasChildNode && rootNodeStatus === NodeStatus.COLLAPSE) { 2227 for (let i: number = 0; i < this.listNode[thisIndex].getChildNodeInfo().allChildNum; i++) { 2228 this.listNode[thisIndex + 1 + i].setNodeIsShow(false); 2229 this.listNode[thisIndex + 1 + i].setListItemHeight(LIST_ITEM_HEIGHT_NONE); 2230 } 2231 this.loadedListNodeFunction(); 2232 this.notifyDataReload(); 2233 return; 2234 } 2235 2236 let childNum: number[] | null = new Array(this.listNode[thisIndex].getChildNodeInfo().childNum); 2237 childNum[0] = thisIndex + 1; 2238 let index: number = 1; 2239 while (index < this.listNode[thisIndex].getChildNodeInfo().childNum) { 2240 childNum[index] = childNum[index - 1] + this.listNode[childNum[index - 1]].getChildNodeInfo().allChildNum + 1; 2241 index++; 2242 } 2243 if (rootNodeStatus === NodeStatus.EXPAND) { 2244 for (let i: number = 0; i < childNum.length; i++) { 2245 this.listNode[childNum[i]].setNodeIsShow(true); 2246 this.listNode[childNum[i]].setListItemHeight(LIST_ITEM_HEIGHT); 2247 let nodeId: number = this.listNode[childNum[i]].getNodeCurrentNodeId(); 2248 if (this.expandAndCollapseInfo.get(nodeId) === NodeStatus.EXPAND) { 2249 this.handleExpandAndCollapse(childNum[i], false); 2250 } 2251 } 2252 } 2253 childNum = null; 2254 if (isRefreshList) { 2255 this.loadedListNodeFunction(); 2256 this.notifyDataReload(); 2257 } 2258 } 2259 2260 /** 2261 * update all parentNodes childNum 2262 */ 2263 public updateAllChildNum(): void { 2264 delayUpdateParentChildNum(true, 1, this.nodeIdNodeItemMap, this.updateNodeIdList); 2265 } 2266 2267 private resetData(listNode: Array<NodeInfo>): void { 2268 listNode.splice(0, listNode.length); 2269 this.loadedNodeIdAndIndexMap.clear(); 2270 this.loadedListNode.splice(0, this.loadedListNode.length); 2271 this.nodeIdAndNodeIndexMap.clear(); 2272 this.nodeIdAndSubtitleMap.clear(); 2273 } 2274 2275 private initHandler(listNode: Array<NodeInfo>, startLevel: number, endLevel?: number): void { 2276 let index: number = 0; 2277 let listIndex: number = 0; 2278 this.resetData(listNode); 2279 try { 2280 this.traverseSectionNodeDF((node: NodeItem): boolean => { 2281 if (node.getCurrentNodeId() >= 0 && this.nodeIdNodeParamMap.has(node.getCurrentNodeId())) { 2282 let nodeInfo: NodeInfo = 2283 new NodeInfo(node, this.nodeIdNodeParamMap.get(node.getCurrentNodeId()) as NodeParam); 2284 nodeInfo.addImageCollapse(node.getChildNodeInfo().isHasChildNode); 2285 listNode.push(nodeInfo); 2286 this.nodeIdAndNodeIndexMap.set(nodeInfo.getNodeCurrentNodeId(), listIndex++); 2287 index = this.nodeDFHandler(nodeInfo, index); 2288 } 2289 return false; 2290 }, this._root, startLevel, endLevel); 2291 } catch (err) { 2292 hilog.error(LOG_CODE, TAG, 'traverseSectionNodeDF function callbacks error.'); 2293 this.resetData(listNode); 2294 } 2295 } 2296 2297 private nodeDFHandler(nodeInfo: NodeInfo, index: number): number { 2298 if (nodeInfo.getChildNodeInfo().isHasChildNode) { 2299 this.expandAndCollapseInfo.set(nodeInfo.getNodeCurrentNodeId(), NodeStatus.COLLAPSE); 2300 } 2301 if (nodeInfo.getNodeIsShow()) { 2302 this.loadedNodeIdAndIndexMap.set(nodeInfo.getNodeCurrentNodeId(), index++); 2303 this.loadedListNode.push(nodeInfo); 2304 } 2305 if (nodeInfo.getIsFolder()) { 2306 if (nodeInfo.getNodeInfoData().secondaryTitle !== undefined) { 2307 this.nodeIdAndSubtitleMap.set( 2308 nodeInfo.getNodeCurrentNodeId(), 2309 nodeInfo.getNodeInfoData().secondaryTitle as ResourceStr 2310 ); 2311 } else { 2312 this.nodeIdAndSubtitleMap.set(nodeInfo.getNodeCurrentNodeId(), ''); 2313 } 2314 } 2315 return index; 2316 } 2317 2318 /** 2319 * update delay init all nodes 2320 */ 2321 public delayInit(): void { 2322 let timeId: number = setTimeout(() => { 2323 let listNode: NodeInfo[] = []; 2324 this.initHandler(listNode, 0); 2325 this.listNode.splice(0, this.listNode.length); 2326 this.listNode.push(...listNode); 2327 this.listNode.forEach((value: NodeInfo, index: number) => { 2328 this.notifyDataDelete(index); 2329 this.notifyDataAdd(index); 2330 }); 2331 clearTimeout(timeId); 2332 }, DELAY_TIME); 2333 } 2334 2335 /** 2336 * update delay init some nodes 2337 */ 2338 public initSection(): void { 2339 this.initHandler(this.listNode, 0, 1); 2340 } 2341 2342 private refreshRemoveNodeData(removeNodeIdList: number[], parentNodeInfo: NodeInfo): void { 2343 let deleteIndexList: number[] = []; 2344 if (removeNodeIdList.length === 0) { 2345 return; 2346 } 2347 let startIndex: number | undefined = undefined; 2348 for (let i: number = 0; i < removeNodeIdList.length; i++) { 2349 if (this.loadedNodeIdAndIndexMap.has(removeNodeIdList[i])) { 2350 let loadedIndex: number = this.loadedNodeIdAndIndexMap.get(removeNodeIdList[i]) as number; 2351 deleteIndexList.push(loadedIndex); 2352 } 2353 if (startIndex === undefined && this.nodeIdAndNodeIndexMap.has(removeNodeIdList[i])) { 2354 startIndex = this.nodeIdAndNodeIndexMap.get(removeNodeIdList[i]); 2355 } 2356 if (startIndex !== undefined) { 2357 let deleteNode: NodeInfo[] | null = this.listNode.splice(startIndex, 1); 2358 deleteNode = null; 2359 } 2360 if (this.expandAndCollapseInfo.has(removeNodeIdList[i])) { 2361 this.expandAndCollapseInfo.delete(removeNodeIdList[i]); // delete deleteNode expandAndCollapseInfo. 2362 } 2363 } 2364 deleteIndexList.forEach((value) => { 2365 this.notifyDataDelete(value); // notifyDataDelete do not update data. 2366 this.notifyDataChange(value); // call notifyDataChange to update data. 2367 }) 2368 if (parentNodeInfo.getNodeItem().imageCollapse === null) { 2369 if (this.nodeIdAndNodeIndexMap.has(parentNodeInfo.getNodeCurrentNodeId())) { 2370 let parentIndex: number = this.nodeIdAndNodeIndexMap.get(parentNodeInfo.getNodeCurrentNodeId()) as number; 2371 this.listNode[parentIndex]?.handleImageCollapseAfterAddNode(false); 2372 } 2373 // delete deleteNode parentNode expandAndCollapseInfo. 2374 this.expandAndCollapseInfo.delete(parentNodeInfo.getNodeCurrentNodeId()); 2375 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(parentNodeInfo.getNodeCurrentNodeId())); 2376 } 2377 let callbackParam: CallbackParam = { 2378 currentNodeId: parentNodeInfo.getNodeCurrentNodeId(), 2379 parentNodeId: parentNodeInfo.getNodeParentNodeId(), 2380 }; 2381 this.loadedListNodeFunction(); 2382 this.appEventBus.emit(TreeListenType.NODE_DELETE, callbackParam); 2383 } 2384 2385 private refreshAddNodeData(addNodeIdList: number[]): void { 2386 let addNodeInfo: NodeInfo = new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo); 2387 if (this.nodeIdNodeItemMap.has(addNodeIdList[0])) { 2388 let node: NodeItem = this.nodeIdNodeItemMap.get(addNodeIdList[0]) as NodeItem; 2389 addNodeInfo = new NodeInfo(node, this.nodeIdNodeParamMap.get(addNodeIdList[0]) as NodeParam); 2390 addNodeInfo.addImageCollapse(node.getChildNodeInfo().isHasChildNode); 2391 } 2392 addNodeInfo.setIsModify(true); 2393 2394 let index: number = 0; 2395 for (let i: number = 0; i < this.listNode.length; i++) { 2396 if (this.listNode[i].getNodeCurrentNodeId() === addNodeInfo.getNodeParentNodeId()) { 2397 index = i; 2398 if (this.listNode[i].getNodeItem().imageCollapse === null) { 2399 this.listNode[i].handleImageCollapseAfterAddNode(true); 2400 this.notifyDataChange(index); 2401 } else if (this.expandAndCollapseInfo.get(this.listNode[i].getNodeCurrentNodeId()) === NodeStatus.COLLAPSE) { 2402 this.changeNodeStatus(index); 2403 } 2404 this.listNode.splice(i + 1, 0, addNodeInfo); 2405 this.listNode[i + 1].setTitleAndInputTextStatus(true); 2406 this.listNode[i + 1].setNodeIsShow(true); 2407 this.listNode[i + 1].setListItemHeight(LIST_ITEM_HEIGHT); 2408 this.nodeIdAndNodeIndexMap.set(addNodeIdList[0], i + 1); 2409 this.setImageSource(i + 1, InteractionStatus.EDIT); 2410 this.currentOperation = MenuOperation.ADD_NODE; 2411 this.notifyDataAdd(i + 1); 2412 this.notificationNodeInfo(i + 1, this.currentOperation); 2413 break; 2414 } 2415 } 2416 this.modifyNodeIndex = index + 1; 2417 this.setClickIndex(index); 2418 this.lastIndex = index; 2419 this.expandAndCollapseInfo.set(addNodeInfo.getNodeParentNodeId(), NodeStatus.EXPAND); 2420 this.handleExpandAndCollapse(index, true); 2421 } 2422 2423 public refreshData(operation: MenuOperation, 2424 parentNodeId: number, changeNodeIdList: number[]): void { 2425 let parentNodeInfo: NodeInfo = new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo); 2426 if (this.nodeIdNodeItemMap.has(parentNodeId)) { 2427 let parentNode: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem; 2428 parentNodeInfo = new NodeInfo(parentNode, this.nodeIdNodeParamMap.get(parentNodeId) as NodeParam); 2429 parentNodeInfo.addImageCollapse(parentNode.getChildNodeInfo().isHasChildNode); 2430 } 2431 2432 if (operation === MenuOperation.REMOVE_NODE) { 2433 this.nodeIdAndSubtitleMap.set(parentNodeId, this.selectedParentNodeSubtitle); 2434 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(parentNodeId)); 2435 this.refreshRemoveNodeData(changeNodeIdList, parentNodeInfo); 2436 } 2437 2438 if (operation === MenuOperation.ADD_NODE) { 2439 this.addFocusNodeId = changeNodeIdList[0]; 2440 this.nodeIdAndSubtitleMap.set(this.getClickNodeId(), this.selectedParentNodeSubtitle); 2441 this.nodeIdAndSubtitleMap.set(changeNodeIdList[0], this.insertNodeSubtitle); 2442 this.refreshAddNodeData(changeNodeIdList); 2443 } 2444 } 2445 2446 public setClickIndex(index: number): void { 2447 this.thisIndex = index; 2448 } 2449 2450 public getClickNodeId(): number { 2451 if (!this.checkIndex(this.thisIndex)) { 2452 return -1; 2453 } 2454 return this.listNode[this.thisIndex].getNodeCurrentNodeId(); 2455 } 2456 2457 public expandAndCollapseNode(clickIndex: number): void { 2458 this.changeNodeStatus(clickIndex); 2459 this.handleExpandAndCollapse(clickIndex, true); 2460 } 2461 2462 public getIsTouchDown(): boolean { 2463 return this.isTouchDown; 2464 } 2465 2466 public getLastIndex(): number { 2467 return this.lastIndex; 2468 } 2469 2470 public findIndex(currentNodeId: number): number { 2471 let thisIndex: number = -1; 2472 if (this.nodeIdAndNodeIndexMap.has(currentNodeId)) { 2473 thisIndex = this.nodeIdAndNodeIndexMap.get(currentNodeId) as number; 2474 } 2475 return thisIndex; 2476 } 2477 2478 public handleEventDrag(index: number): void { 2479 if (!this.checkIndex(index)) { 2480 return; 2481 } 2482 this.setImageSource(index, InteractionStatus.NORMAL); 2483 this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal); 2484 this.handleFocusEffect(index, false); 2485 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[index].getNodeCurrentNodeId())); 2486 } 2487 2488 public handleEvent(event: Event, index: number): void { 2489 /* Return while the event is dragging event. */ 2490 if (this.isDrag) { 2491 return; 2492 } 2493 if (!this.checkIndex(index)) { 2494 return; 2495 } 2496 2497 if (event === Event.TOUCH_DOWN || event === Event.TOUCH_UP || event === Event.MOUSE_BUTTON_RIGHT) { 2498 if (index !== this.lastIndex) { 2499 this.clearLastIndexStatus(); 2500 } 2501 } 2502 2503 this.eventHandler(index, event); 2504 } 2505 2506 private eventHandler(index: number, event: Event): void { 2507 let lazyForEachIndex: number = 2508 this.loadedNodeIdAndIndexMap.get(this.listNode[index].getNodeCurrentNodeId()) as number; 2509 switch (event) { 2510 case Event.TOUCH_DOWN: 2511 this.isTouchDown = true; 2512 this.changeNodeColor(index, this.listNode[index].getNodeStatus().press); 2513 this.notifyDataChange(lazyForEachIndex); 2514 break; 2515 case Event.TOUCH_UP: { 2516 this.touchUpHandler(index, lazyForEachIndex); 2517 break; 2518 } 2519 case Event.HOVER: 2520 if (this.getNodeColor(index) !== this.listNode[index].getNodeStatus().selected) { 2521 this.changeNodeColor(index, this.listNode[index].getNodeStatus().hover); 2522 this.notifyDataChange(lazyForEachIndex); 2523 } 2524 break; 2525 case Event.HOVER_OVER: 2526 if (this.getNodeColor(index) !== this.listNode[index].getNodeStatus().selected) { 2527 this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal); 2528 this.notifyDataChange(lazyForEachIndex); 2529 } 2530 break; 2531 case Event.FOCUS: 2532 this.handleFocusEffect(index, true); 2533 this.notifyDataChange(lazyForEachIndex); 2534 break; 2535 case Event.BLUR: 2536 this.handleFocusEffect(index, false); 2537 this.notifyDataChange(lazyForEachIndex); 2538 break; 2539 case Event.MOUSE_BUTTON_RIGHT: 2540 this.lastIndex = index; 2541 this.finishEditing(); 2542 break; 2543 case Event.DRAG: 2544 this.isTouchDown = false; 2545 let nodeInfo: NodeInfo = this.listNode[index]; 2546 this.setImageSource(index, InteractionStatus.SELECTED); 2547 this.lastIndex = index; 2548 this.changeNodeColor(index, nodeInfo.getNodeStatus().selected); 2549 this.notifyDataChange(lazyForEachIndex); 2550 break; 2551 default: 2552 break; 2553 } 2554 } 2555 2556 private touchUpHandler(index: number, lazyForEachIndex: number): void { 2557 if (this.isInnerDrag) { 2558 this.isInnerDrag = false; 2559 } 2560 this.isTouchDown = false; 2561 let nodeInfo: NodeInfo = this.listNode[index]; 2562 this.setImageSource(index, InteractionStatus.SELECTED); 2563 nodeInfo.setFontColor(this.treeViewTheme.primaryTitleFontColor); 2564 this.lastIndex = index; 2565 this.changeNodeColor(index, nodeInfo.getNodeStatus().selected); 2566 this.notifyDataChange(lazyForEachIndex); 2567 } 2568 2569 private notificationNodeInfo(addNodeId: number, operation: MenuOperation | undefined): void { 2570 if (operation === MenuOperation.MODIFY_NODE) { 2571 let modifyNodeInfo: NodeInfo = this.listNode[this.modifyNodeIndex]; 2572 let backParamModify: CallbackParam = { 2573 currentNodeId: modifyNodeInfo?.getNodeCurrentNodeId(), 2574 parentNodeId: modifyNodeInfo?.getNodeParentNodeId(), 2575 }; 2576 this.appEventBus.emit(TreeListenType.NODE_MODIFY, backParamModify); 2577 } else if (operation === MenuOperation.ADD_NODE) { 2578 let addNodeInfo: NodeInfo = this.listNode[addNodeId]; 2579 if (addNodeInfo === undefined) { 2580 return; 2581 } 2582 let icon: Resource | string | undefined = (addNodeInfo.getNodeItem().imageNode !== undefined) ? 2583 addNodeInfo.getNodeItem().imageNode?.source : undefined; 2584 let selectedIcon: Resource | string | undefined = (addNodeInfo.getNodeItem().imageNode !== undefined) ? 2585 addNodeInfo.getNodeItem().imageNode?.selectedSource : undefined; 2586 let editIcon: Resource | string | undefined = (addNodeInfo.getNodeItem().imageNode !== undefined) ? 2587 addNodeInfo.getNodeItem().imageNode?.editSource : undefined; 2588 let callbackParam: CallbackParam = { 2589 currentNodeId: addNodeInfo?.getNodeCurrentNodeId(), 2590 parentNodeId: addNodeInfo?.getNodeParentNodeId(), 2591 }; 2592 this.appEventBus.emit(TreeListenType.NODE_ADD, callbackParam); 2593 } 2594 } 2595 2596 public finishEditing(): void { 2597 if (this.modifyNodeIndex !== -1) { 2598 this.setImageSource(this.modifyNodeIndex, InteractionStatus.FINISH_EDIT); 2599 this.setImageCollapseSource(this.modifyNodeIndex, InteractionStatus.FINISH_EDIT); 2600 this.listNode[this.modifyNodeIndex].setIsModify(false); 2601 this.listNode[this.modifyNodeIndex].setTitleAndInputTextStatus(false); 2602 this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation); 2603 this.notifyDataChange(this.modifyNodeIndex); 2604 } 2605 } 2606 2607 public setItemVisibilityOnEdit(nodeId: number, operation: MenuOperation): void { 2608 let index: number = -1; 2609 if (nodeId === -1) { 2610 return; 2611 } 2612 if (operation === MenuOperation.MODIFY_NODE) { 2613 for (let i: number = 0; i < this.listNode.length; i++) { // nodeId to find index 2614 if (this.listNode[i]?.getNodeCurrentNodeId() === nodeId) { 2615 index = i; 2616 break; 2617 } 2618 } 2619 let nodeInfo: NodeInfo = this.listNode[index]; 2620 if (nodeInfo === undefined) { 2621 return; 2622 } 2623 nodeInfo.setIsModify(true); 2624 if (nodeInfo.getNodeItem().mainTitleNode === null) { 2625 return; // no title 2626 } 2627 2628 this.currentOperation = MenuOperation.MODIFY_NODE; 2629 nodeInfo.setTitleAndInputTextStatus(true); 2630 this.setImageSource(index, InteractionStatus.EDIT); 2631 this.setImageCollapseSource(index, InteractionStatus.EDIT); 2632 this.modifyNodeIndex = index; 2633 if (nodeInfo.getNodeItem().inputText) { 2634 if (nodeInfo.getNodeItem().imageCollapse !== null) { 2635 nodeInfo.getNodeItem().inputText.rightMargin = 2636 $r('sys.float.ohos_id_text_paragraph_margin_xs'); 2637 } else { 2638 nodeInfo.getNodeItem().inputText.rightMargin = 2639 $r('sys.float.ohos_id_elements_margin_horizontal_m'); 2640 } 2641 } 2642 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeId)); 2643 } 2644 index = nodeId; 2645 if (operation === MenuOperation.COMMIT_NODE) { 2646 let nodeInfo: NodeInfo = this.listNode[index]; 2647 if (nodeInfo === undefined) { 2648 return; 2649 } 2650 nodeInfo.setTitleAndInputTextStatus(false); 2651 nodeInfo.setIsModify(false); 2652 this.setImageSource(index, InteractionStatus.FINISH_EDIT); 2653 this.setImageCollapseSource(index, InteractionStatus.FINISH_EDIT); 2654 this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation); 2655 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeInfo?.getNodeCurrentNodeId())); 2656 } 2657 } 2658 2659 public setPopUpInfo(popUpType: PopUpType, inputError: InputError, isShow: boolean, index: number): void { 2660 if (!this.checkIndex(index)) { 2661 return; 2662 } 2663 let nodeInfo: NodeInfo = this.listNode[index]; 2664 if (nodeInfo === undefined) { 2665 return; 2666 } 2667 nodeInfo.setPopUpIsShow(isShow); 2668 // this.listNode index to lazyForEach index. 2669 let lazyForEachIndex: number = this.loadedNodeIdAndIndexMap.get(nodeInfo.getNodeCurrentNodeId()) as number; 2670 if (!isShow) { 2671 this.notifyDataChange(lazyForEachIndex); 2672 return; 2673 } 2674 if (popUpType === PopUpType.HINTS) { 2675 if (nodeInfo.getNodeItem().mainTitleNode !== null) { 2676 nodeInfo.setPopUpText(nodeInfo.getNodeItem().mainTitleNode?.title); 2677 } else { 2678 nodeInfo.setPopUpText(''); 2679 nodeInfo.setPopUpIsShow(false); 2680 } 2681 nodeInfo.setPopUpEnableArrow(false); 2682 nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_background')); 2683 nodeInfo.setPopUpTextColor(this.treeViewTheme.secondaryTitleFontColor); 2684 } else if (popUpType === PopUpType.WARNINGS) { 2685 if (nodeInfo.getNodeItem().inputText !== null) { 2686 if (inputError === InputError.INVALID_ERROR) { 2687 nodeInfo.setPopUpText('invalid error'); 2688 } else if (inputError === InputError.LENGTH_ERROR) { 2689 nodeInfo.setPopUpText('length error'); 2690 } 2691 nodeInfo.setPopUpEnableArrow(true); 2692 nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_help_tip_bg')); 2693 nodeInfo.setPopUpTextColor($r('sys.color.ohos_id_color_text_hint_contrary')); 2694 } 2695 } 2696 this.notifyDataChange(lazyForEachIndex); 2697 } 2698 2699 public setShowPopUpTimeout(timeout: number, index: number): void { 2700 if (!this.checkIndex(index)) { 2701 return; 2702 } 2703 if (this.listNode[index].getNodeItem().mainTitleNode !== null) { 2704 this.listNode[index].getNodeItem().mainTitleNode.popUpTimeout = timeout; 2705 } 2706 let lazyForEachIndex: number = 2707 this.loadedNodeIdAndIndexMap.get(this.listNode[index].getNodeCurrentNodeId()) as number; 2708 this.notifyDataChange(lazyForEachIndex); 2709 } 2710 2711 public setMainTitleNameOnEdit(index: number, text: string): void { 2712 this.modifyNodeIndex = index; 2713 if (this.listNode[index].getNodeItem().mainTitleNode !== null) { 2714 this.listNode[index].getNodeItem().mainTitleNode.title = text; 2715 } 2716 } 2717 2718 public totalCount(): number { 2719 return this.loadedNodeIdAndIndexMap.size; 2720 } 2721 2722 public getData(index: number): NodeInfo | undefined { 2723 if (index < 0 || index >= this.loadedListNode.length) { 2724 return undefined; 2725 } 2726 return this.loadedListNode[index]; 2727 } 2728 2729 public addData(index: number, data: NodeInfo): void { 2730 if (!this.checkIndex(index)) { 2731 return; 2732 } 2733 this.listNode.splice(index, 0, data); 2734 this.nodeIdAndNodeIndexMap.set(data.getNodeCurrentNodeId(), index); 2735 this.loadedListNodeFunction(); 2736 this.notifyDataAdd(index); 2737 } 2738 2739 public pushData(data: NodeInfo): void { 2740 this.listNode.push(data); 2741 this.nodeIdAndNodeIndexMap.set(data.getNodeCurrentNodeId(), this.listNode.length); 2742 this.loadedListNodeFunction(); 2743 this.notifyDataAdd(this.listNode.length - 1); 2744 } 2745 2746 public setIsInnerDrag(isInnerDrag: boolean): void { 2747 this.isInnerDrag = isInnerDrag; 2748 } 2749 2750 public getIsInnerDrag(): boolean { 2751 return this.isInnerDrag; 2752 } 2753 2754 public setIsDrag(isDrag: boolean): void { 2755 this.isDrag = isDrag; 2756 } 2757 2758 public getIsDrag(): boolean { 2759 return this.isDrag; 2760 } 2761 2762 public setCurrentNodeInfo(currentNodeInfo: NodeInfo | undefined): void { 2763 if (currentNodeInfo === undefined) { 2764 return; 2765 } 2766 this.currentNodeInfo = currentNodeInfo; 2767 } 2768 2769 public getCurrentNodeInfo(): NodeInfo | null { 2770 return this.currentNodeInfo; 2771 } 2772 2773 public setDraggingParentNodeId(draggingParentNodeId: number | undefined): void { 2774 if (draggingParentNodeId === undefined) { 2775 return; 2776 } 2777 this.draggingParentNodeId = draggingParentNodeId; 2778 } 2779 2780 public getDraggingParentNodeId(): number { 2781 return this.draggingParentNodeId; 2782 } 2783 2784 public getDraggingCurrentNodeId(): number { 2785 return this.draggingCurrentNodeId; 2786 } 2787 2788 public setDraggingCurrentNodeId(draggingCurrentNodeId: number | undefined): void { 2789 if (draggingCurrentNodeId === undefined) { 2790 return; 2791 } 2792 this.draggingCurrentNodeId = draggingCurrentNodeId; 2793 } 2794 2795 public setListItemOpacity(listItemOpacity: number): void { 2796 this.listItemOpacity = listItemOpacity; 2797 } 2798 2799 public getListItemOpacity(item: NodeInfo): number { 2800 return item.getNodeCurrentNodeId() === this.getDraggingCurrentNodeId() ? this.listItemOpacity : 1; 2801 } 2802 2803 public getDragPopupPara(): DragPopup { 2804 return this.DRAG_POPUP; 2805 } 2806 2807 public setLastPassIndex(lastPassIndex: number): void { 2808 this.lastPassIndex = lastPassIndex; 2809 } 2810 2811 public getLastPassIndex(): number { 2812 return this.lastPassIndex; 2813 } 2814 2815 public getIsParentOfInsertNode(insertNodeId: number | undefined): boolean { 2816 if (this.currentNodeInfo === null || insertNodeId === undefined) { 2817 return false; 2818 } 2819 let selectedNodeItem: NodeItem = this.currentNodeInfo.getNodeInfoNode(); 2820 let parentId: number = selectedNodeItem.currentNodeId; 2821 let insertParentId: number | undefined = this.nodeIdNodeItemMap.get(insertNodeId as number)?.parentNodeId; 2822 while (insertParentId !== undefined && insertParentId !== -1) { 2823 if (parentId === insertParentId) { 2824 return true; 2825 } else { 2826 insertParentId = this.nodeIdNodeItemMap.get(insertParentId as number)?.parentNodeId; 2827 } 2828 } 2829 return false; 2830 } 2831 2832 public setPassIndex(thisPassIndex: number): void { 2833 this.thisPassIndex = thisPassIndex; 2834 } 2835 2836 public getPassIndex(): number { 2837 return this.thisPassIndex; 2838 } 2839 2840 public clearTimeOutAboutDelayHighLightAndExpand(currentIndex: number): void { 2841 if (this.lastPassId !== this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId as number)) { 2842 let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId as number) as number; 2843 this.listNode.forEach((value) => { 2844 if (value.getNodeCurrentNodeId() === this.lastPassId) { 2845 value.setCanShowFlagLine(false); 2846 return; 2847 } 2848 }) 2849 this.notifyDataChange(index); 2850 } 2851 2852 if ((this.lastTimeoutHighLightId !== this.INITIAL_INVALID_VALUE && 2853 this.clearTimeoutHighLightId !== this.lastTimeoutHighLightId)) { 2854 clearTimeout(this.lastTimeoutHighLightId); 2855 if (this.lastDelayHighLightIndex !== this.INITIAL_INVALID_VALUE) { 2856 this.clearHighLight(this.lastDelayHighLightIndex); 2857 let index: number = this.loadedNodeIdAndIndexMap 2858 .get(this.listNode[this.lastDelayHighLightIndex].getNodeCurrentNodeId()) as number; 2859 this.notifyDataChange(index); 2860 } 2861 this.clearTimeoutHighLightId = this.lastTimeoutHighLightId; 2862 } 2863 this.lastTimeoutHighLightId = this.timeoutHighLightId; 2864 this.lastDelayHighLightIndex = currentIndex; 2865 2866 if ((this.lastTimeoutExpandId !== this.INITIAL_INVALID_VALUE && 2867 this.clearTimeoutExpandId !== this.lastTimeoutExpandId)) { 2868 clearTimeout(this.lastTimeoutExpandId); 2869 this.clearTimeoutExpandId = this.lastTimeoutExpandId; 2870 } 2871 this.lastTimeoutExpandId = this.timeoutExpandId; 2872 this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE; 2873 } 2874 2875 public clearHighLight(currentIndex: number): void { 2876 if (!this.checkIndex(currentIndex)) { 2877 return; 2878 } 2879 this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().normal); 2880 this.changeNodeHighLightColor(currentIndex, false); 2881 this.setImageSource(currentIndex, InteractionStatus.FINISH_DRAG_INSERT); 2882 this.setImageCollapseSource(currentIndex, InteractionStatus.FINISH_DRAG_INSERT); 2883 this.listNode[currentIndex].setIsHighLight(false); 2884 } 2885 2886 private changeNodeHighLightColor(index: number, isHighLight: boolean): void { 2887 if (this.listNode[index].getNodeItem().mainTitleNode && this.listNode[index].getIsShowTitle()) { 2888 this.listNode[index].getNodeItem().mainTitleNode?.setMainTitleHighLight(isHighLight); 2889 } 2890 } 2891 2892 public setVisibility(flag: Flag, index: number, isOverBorder: boolean): void { 2893 let isChanged: boolean = (this.thisPassIndex !== index || this.flag !== flag) ? true : false; 2894 this.thisPassIndex = index; 2895 if ((isChanged || isOverBorder) && this.isInnerDrag) { 2896 this.flag = flag; 2897 let currentNodeId: number | undefined = this.getData(index)?.getNodeCurrentNodeId(); 2898 let currentNodeLevel: number | undefined = this.getData(index)?.getNodeLevel(); 2899 if (currentNodeId !== undefined) { 2900 currentNodeLevel = (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.EXPAND && 2901 this.flag === Flag.DOWN_FLAG) ? (currentNodeLevel ? currentNodeLevel + 1 : undefined) : currentNodeLevel; 2902 if (this.lastPassId !== this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId as number)) { 2903 let lastIndex: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId as number) as number; 2904 this.listNode.forEach((value) => { 2905 if (value.getNodeCurrentNodeId() === this.lastPassId) { 2906 value.setCanShowFlagLine(false); 2907 } 2908 }) 2909 this.notifyDataChange(lastIndex); 2910 } 2911 if (this.flag === Flag.DOWN_FLAG && index < this.totalCount() - 1) { 2912 this.getData(index)?.setCanShowFlagLine(false); 2913 this.getData(index + 1)?.setCanShowFlagLine(true); 2914 this.getData(index)?.setCanShowBottomFlagLine(false); 2915 this.getData(index + 1)?.setFlagLineLeftMargin(currentNodeLevel); 2916 this.notifyDataChange(index); 2917 this.notifyDataChange(index + 1); 2918 this.lastPassId = this.getData(index + 1)?.getNodeCurrentNodeId(); 2919 } else if (this.flag === Flag.UP_FLAG && index < this.totalCount() - 1) { 2920 this.getData(index)?.setCanShowFlagLine(true); 2921 this.getData(index + 1)?.setCanShowFlagLine(false); 2922 this.getData(index)?.setCanShowBottomFlagLine(false); 2923 this.getData(index)?.setFlagLineLeftMargin(currentNodeLevel); 2924 this.notifyDataChange(index); 2925 this.notifyDataChange(index + 1); 2926 this.lastPassId = this.getData(index)?.getNodeCurrentNodeId(); 2927 } else if (index >= this.totalCount() - 1) { 2928 if (this.flag === Flag.DOWN_FLAG) { 2929 this.getData(index)?.setCanShowFlagLine(false); 2930 this.getData(index)?.setCanShowBottomFlagLine(true); 2931 } else { 2932 this.getData(index)?.setCanShowFlagLine(true); 2933 this.getData(index)?.setCanShowBottomFlagLine(false); 2934 } 2935 this.getData(index)?.setFlagLineLeftMargin(currentNodeLevel); 2936 this.notifyDataChange(index); 2937 this.lastPassId = this.getData(index)?.getNodeCurrentNodeId(); 2938 } 2939 } 2940 } 2941 } 2942 2943 public delayHighLightAndExpandNode(currentIndex: number, currentNodeId: number, showIndex: number): void { 2944 let isChangIndex: boolean = currentIndex !== this.lastDelayExpandIndex ? true : false; 2945 let isOverBorder: boolean | undefined = this.getData(showIndex)?.getIsOverBorder(); 2946 if (isOverBorder) { 2947 this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE; 2948 } else { 2949 this.lastDelayExpandIndex = currentIndex; 2950 } 2951 if (isOverBorder || isChangIndex) { 2952 2953 /* highLight node time-out. */ 2954 let canDelayHighLight: boolean | undefined = !isOverBorder && (!this.isInnerDrag || 2955 (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.COLLAPSE && this.isInnerDrag) || 2956 (!this.expandAndCollapseInfo.has(currentNodeId) && this.listNode[currentIndex].getIsFolder())); 2957 if (canDelayHighLight) { 2958 /* set hoverState color before highLight. */ 2959 this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().hover); 2960 this.notifyDataChange(showIndex); 2961 2962 let delayHighLightTime: number = this.isInnerDrag ? 1000 : 0; // ms 2963 this.timeoutHighLightId = setTimeout(() => { 2964 this.delayHighLight(currentIndex); 2965 }, delayHighLightTime); 2966 } 2967 if (isOverBorder || (this.lastTimeoutHighLightId !== this.INITIAL_INVALID_VALUE && 2968 this.clearTimeoutHighLightId !== this.lastTimeoutHighLightId)) { 2969 clearTimeout(this.lastTimeoutHighLightId); 2970 if (this.lastDelayHighLightIndex !== this.INITIAL_INVALID_VALUE) { 2971 this.clearHighLight(this.lastDelayHighLightIndex); 2972 this.notifyDataReload(); 2973 } 2974 this.clearTimeoutHighLightId = this.lastTimeoutHighLightId; 2975 } 2976 this.lastTimeoutHighLightId = this.timeoutHighLightId; 2977 this.lastDelayHighLightIndex = currentIndex; 2978 2979 /* alter flagLine and expand node time-out. */ 2980 if (!isOverBorder && this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.COLLAPSE) { 2981 let firstChildNodeId: number | undefined = 2982 this.getData(showIndex)?.getNodeInfoNode().children[0]?.currentNodeId; 2983 let delayAlterFlagLineAndExpandNodeTime: number = 2000; // ms 2984 this.timeoutExpandId = setTimeout(() => { 2985 this.clearHighLight(this.lastDelayHighLightIndex); 2986 if (firstChildNodeId !== undefined) { 2987 this.alterFlagLineAndExpandNode(currentIndex, firstChildNodeId); 2988 } 2989 }, delayAlterFlagLineAndExpandNodeTime); 2990 } 2991 if (isOverBorder || (this.lastTimeoutExpandId !== this.INITIAL_INVALID_VALUE && 2992 this.clearTimeoutExpandId !== this.lastTimeoutExpandId)) { 2993 clearTimeout(this.lastTimeoutExpandId); 2994 this.clearTimeoutExpandId = this.lastTimeoutExpandId; 2995 } 2996 this.lastTimeoutExpandId = this.timeoutExpandId; 2997 } 2998 } 2999 3000 public delayHighLight(currentIndex: number): void { 3001 this.listNode.forEach((value) => { 3002 if (value.getNodeCurrentNodeId() === this.lastPassId) { 3003 value.setCanShowFlagLine(false); 3004 value.setCanShowBottomFlagLine(false); 3005 return; 3006 } 3007 }) 3008 this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().highLight); 3009 this.listNode[currentIndex].setIsHighLight(true); 3010 this.changeNodeHighLightColor(currentIndex, true); 3011 this.setImageSource(currentIndex, InteractionStatus.DRAG_INSERT); 3012 this.setImageCollapseSource(currentIndex, InteractionStatus.DRAG_INSERT); 3013 this.notifyDataReload(); 3014 } 3015 3016 public alterFlagLineAndExpandNode(currentIndex: number, firstChildNodeId: number): void { 3017 this.listNode.forEach((value) => { 3018 if (value.getNodeCurrentNodeId() === this.lastPassId) { 3019 value.setCanShowFlagLine(false); 3020 value.setCanShowBottomFlagLine(false); 3021 } 3022 }) 3023 this.listNode.forEach((value) => { 3024 if (this.isInnerDrag && value.getNodeCurrentNodeId() === firstChildNodeId) { 3025 value.setCanShowFlagLine(true); 3026 } 3027 }) 3028 this.changeNodeStatus(currentIndex); 3029 this.handleExpandAndCollapse(currentIndex, true); 3030 this.lastPassId = firstChildNodeId; 3031 } 3032 3033 public hideLastLine(): void { 3034 if (this.lastPassId !== this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId as number)) { 3035 this.listNode.forEach((value) => { 3036 if (value.getNodeCurrentNodeId() === this.lastPassId) { 3037 value.setCanShowFlagLine(false); 3038 value.setCanShowBottomFlagLine(false); 3039 return; 3040 } 3041 }) 3042 let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId as number) as number; 3043 this.notifyDataChange(index); 3044 } 3045 } 3046 3047 public clearLastTimeoutHighLight(): void { 3048 if (this.lastTimeoutHighLightId !== this.INITIAL_INVALID_VALUE && 3049 this.clearTimeoutHighLightId !== this.lastTimeoutHighLightId) { 3050 clearTimeout(this.lastTimeoutHighLightId); 3051 if (this.lastDelayHighLightIndex !== this.INITIAL_INVALID_VALUE) { 3052 this.clearHighLight(this.lastDelayHighLightIndex); 3053 } 3054 } 3055 } 3056 3057 public clearLastTimeoutExpand(): void { 3058 if (this.lastTimeoutExpandId !== this.INITIAL_INVALID_VALUE && 3059 this.clearTimeoutExpandId !== this.lastTimeoutExpandId) { 3060 clearTimeout(this.lastTimeoutExpandId); 3061 } 3062 } 3063 3064 public getSubtitle(currentNodeId: number): string | undefined { 3065 if (this.nodeIdAndSubtitleMap.has(currentNodeId)) { 3066 if (typeof this.nodeIdAndSubtitleMap.get(currentNodeId) === 'number') { 3067 return this.nodeIdAndSubtitleMap.get(currentNodeId)?.toString(); 3068 } else { 3069 return this.nodeIdAndSubtitleMap.get(currentNodeId) as string; 3070 } 3071 } else { 3072 return ''; 3073 } 3074 } 3075 3076 public hasSubtitle(currentNodeId: number): boolean { 3077 return this.nodeIdAndSubtitleMap.has(currentNodeId); 3078 } 3079 3080 public initialParameterAboutDelayHighLightAndExpandIndex(): void { 3081 this.lastDelayHighLightIndex = this.INITIAL_INVALID_VALUE; 3082 this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE; 3083 this.lastPassIndex = this.INITIAL_INVALID_VALUE; 3084 this.draggingCurrentNodeId = this.INITIAL_INVALID_VALUE; 3085 this.flag = Flag.NONE; 3086 } 3087 3088 public refreshSubtitle(insertNodeCurrentNodeId: number): void { 3089 this.nodeIdAndSubtitleMap.set(this.selectedParentNodeId, this.selectedParentNodeSubtitle); 3090 this.nodeIdAndSubtitleMap.set(insertNodeCurrentNodeId, this.insertNodeSubtitle); 3091 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.selectedParentNodeId)); 3092 this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(insertNodeCurrentNodeId)); 3093 } 3094 3095 public setNodeSubtitlePara( 3096 selectedParentNodeId: number, 3097 selectedParentNodeSubtitle: ResourceStr, 3098 insertNodeSubtitle: ResourceStr): void { 3099 this.selectedParentNodeId = selectedParentNodeId; 3100 this.selectedParentNodeSubtitle = selectedParentNodeSubtitle; 3101 this.insertNodeSubtitle = insertNodeSubtitle; 3102 } 3103 3104 public getInsertNodeSubtitle(): ResourceStr { 3105 return this.insertNodeSubtitle; 3106 } 3107 3108 public getExpandAndCollapseInfo(currentNodeId: number): NodeStatus | undefined { 3109 return this.expandAndCollapseInfo.get(currentNodeId); 3110 } 3111 3112 public getLastDelayHighLightId(): number { 3113 return this.lastDelayHighLightId; 3114 } 3115 3116 public setLastDelayHighLightId(): void { 3117 this.listNode.forEach((value, index) => { 3118 if (index === this.lastDelayHighLightIndex) { 3119 this.lastDelayHighLightId = value.getNodeCurrentNodeId(); 3120 } 3121 }) 3122 } 3123 3124 public setLastPassId(lastPassId: number): void { 3125 this.lastPassId = lastPassId; 3126 } 3127 3128 public setLastDelayHighLightIndex(lastDelayHighLightIndex: number): void { 3129 this.lastDelayHighLightIndex = lastDelayHighLightIndex; 3130 } 3131 3132 /** 3133 * Alter the current node location to a needful position. 3134 * 1.Create an array named 'dragNodeParam' to store dragging node information. 3135 * 2.Delete the dragging node from the tree. 3136 * 3.Add the dragging node to the tree. 3137 */ 3138 public alterDragNode(rearParentNodeId: number, rearCurrentNodeId: number, 3139 dragParentNodeId: number, dragCurrentNodeId: number, frontNodeInfoItem: NodeInfo): void { 3140 let dragNodeParam: DragNodeParam[] = []; 3141 let parentNodeId: number = rearParentNodeId; 3142 let currentNodeId: number = dragCurrentNodeId; 3143 let nodeParam: NodeParam = frontNodeInfoItem.getNodeInfoData(); 3144 let nodeInfo: NodeInfo | null = null; 3145 let nodeInfoNode: NodeItem = frontNodeInfoItem.getNodeInfoNode(); 3146 let isHighLight: boolean = false; 3147 let insertChildIndex: number = this.INITIAL_INVALID_VALUE; 3148 let currentChildIndex: number = this.INITIAL_INVALID_VALUE; 3149 let isDownFlag: boolean = this.flag === Flag.DOWN_FLAG ? true : false; 3150 3151 currentChildIndex = this.getChildIndex(dragParentNodeId, dragCurrentNodeId); 3152 3153 insertChildIndex = this.getChildIndex(rearParentNodeId, rearCurrentNodeId) + 1; 3154 3155 if (rearParentNodeId !== dragParentNodeId) { 3156 insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex; 3157 } else { 3158 if (insertChildIndex > currentChildIndex) { 3159 insertChildIndex = isDownFlag ? insertChildIndex : insertChildIndex - 1; 3160 } else { 3161 insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex; 3162 } 3163 } 3164 3165 for (let i: number = 0; i < this.listNode.length; i++) { 3166 if (this.listNode[i].getNodeCurrentNodeId() === rearCurrentNodeId) { 3167 isHighLight = this.listNode[i].getIsHighLight(); 3168 if (this.flag === Flag.DOWN_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) === NodeStatus.EXPAND) { 3169 parentNodeId = rearCurrentNodeId; 3170 insertChildIndex = 0; 3171 } else if (this.flag === Flag.UP_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) === 3172 NodeStatus.EXPAND && 3173 this.listNode[i].getCanShowFlagLine() === false) { 3174 parentNodeId = rearCurrentNodeId; 3175 insertChildIndex = 0; 3176 } else if (isHighLight) { 3177 parentNodeId = rearCurrentNodeId; 3178 insertChildIndex = 0; 3179 } 3180 break; 3181 } 3182 } 3183 3184 let callbackParam: CallbackParam = { 3185 currentNodeId: currentNodeId, 3186 parentNodeId: parentNodeId, 3187 childIndex: insertChildIndex, 3188 } 3189 3190 /* export inner drag node Id. */ 3191 this.appEventBus.emit(TreeListenType.NODE_MOVE, callbackParam); 3192 3193 /* To store dragging node information by the array named 'dragNodeParam'. */ 3194 dragNodeParam.push({ parentId: parentNodeId, currentId: currentNodeId, data: nodeParam }); 3195 3196 let callback: (node: NodeItem, listNode: NodeInfo[]) => boolean = (node: NodeItem, listNode: NodeInfo[]): boolean => { 3197 if (node) { 3198 parentNodeId = node.parentNodeId; 3199 currentNodeId = node.currentNodeId; 3200 for (let i: number = 0; i < listNode.length; i++) { 3201 if (listNode[i].getNodeCurrentNodeId() === currentNodeId) { 3202 nodeInfo = listNode[i]; 3203 break; 3204 } 3205 } 3206 if (nodeInfo === null) { 3207 return false; 3208 } 3209 let nodeParam: NodeParam = nodeInfo.getNodeInfoData(); 3210 if (parentNodeId !== dragParentNodeId) { 3211 dragNodeParam.push({ parentId: parentNodeId, currentId: currentNodeId, data: nodeParam }); 3212 } 3213 return false; 3214 } 3215 return false; 3216 } 3217 this.dragTraverseNodeDF(callback, nodeInfoNode, this.listNode); 3218 3219 /* Delete the dragging node from the tree. */ 3220 let removeNodeIdList: number[] = this.removeNode(dragCurrentNodeId, dragParentNodeId); 3221 if (removeNodeIdList.length === 0) { 3222 return; 3223 } 3224 3225 /** 3226 * Add the dragging node to the tree 3227 * 1.The first dragging node is added singly, because it needs to distinguish the position to insert 3228 * 3229 * Add first node. 3230 */ 3231 let insertCurrentNodeId: number = rearCurrentNodeId; 3232 let isAfter: boolean = isDownFlag; 3233 if (this.expandAndCollapseInfo.get(rearCurrentNodeId) === NodeStatus.EXPAND) { 3234 isAfter = false; 3235 this.listNode.forEach((value) => { 3236 if (value.getNodeCurrentNodeId() === rearCurrentNodeId && value.getCanShowFlagLine() === false) { 3237 if (value.getNodeInfoNode().children.length) { 3238 insertCurrentNodeId = value.getNodeInfoNode().children[0].currentNodeId; 3239 } else { 3240 insertCurrentNodeId = this.INITIAL_INVALID_VALUE; 3241 } 3242 } 3243 }) 3244 } else if (!this.expandAndCollapseInfo.get(rearCurrentNodeId) && isHighLight) { 3245 this.expandAndCollapseInfo.set(rearCurrentNodeId, NodeStatus.EXPAND); 3246 } 3247 3248 let addDragNodeResult: boolean = 3249 this.addDragNode(dragNodeParam[0].parentId, dragNodeParam[0].currentId, insertCurrentNodeId, 3250 isAfter, dragNodeParam[0].data); 3251 3252 if (!addDragNodeResult) { 3253 return; 3254 } 3255 /* Add remaining node. */ 3256 for (let j: number = 1; j < dragNodeParam.length; j++) { 3257 let addNodeResult: boolean = 3258 this.addNode(dragNodeParam[j].parentId, dragNodeParam[j].currentId, dragNodeParam[j].data, false); 3259 if (!addNodeResult) { 3260 return; 3261 } 3262 } 3263 3264 /* Update node data and reload the array named 'listNode'. */ 3265 for (let i: number = 0; i < this.listNode.length; i++) { 3266 if (this.listNode[i].getNodeCurrentNodeId() === dragParentNodeId) { 3267 if (this.listNode[i].getNodeItem().imageCollapse === null) { 3268 this.listNode[i].handleImageCollapseAfterAddNode(false); 3269 this.expandAndCollapseInfo.delete(dragParentNodeId); 3270 break; 3271 } 3272 } 3273 } 3274 let tmp: NodeInfo[] = [...this.listNode]; 3275 this.reloadListNode(tmp); 3276 } 3277 3278 /** 3279 * Reload the array named 'listNode' 3280 * @param tmp 3281 */ 3282 public reloadListNode(tmp: NodeInfo[]): void { 3283 let index: number = 0; 3284 let listIndex: number = 0; 3285 this.listNode.splice(0, this.listNode.length); 3286 this.loadedNodeIdAndIndexMap.clear(); 3287 this.loadedListNode.splice(0, this.loadedListNode.length); 3288 this.traverseNodeDF((node: NodeItem): boolean => { 3289 let currentNodeId: number = node.currentNodeId; 3290 if (currentNodeId >= 0) { 3291 if (this.nodeIdNodeParamMap.has(currentNodeId)) { 3292 let nodeInfo: NodeInfo = new NodeInfo(node, this.nodeIdNodeParamMap.get(currentNodeId) as NodeParam); 3293 nodeInfo.addImageCollapse(node.getChildNodeInfo().isHasChildNode); 3294 this.listNode.push(nodeInfo); 3295 this.nodeIdAndNodeIndexMap.set(nodeInfo.getNodeCurrentNodeId(), listIndex++); 3296 if (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.EXPAND) { 3297 nodeInfo.getNodeItem() 3298 .imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.EXPAND, 3299 nodeInfo.getNodeItem().imageCollapse?.isCollapse); 3300 } else if (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.COLLAPSE) { 3301 nodeInfo.getNodeItem() 3302 .imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.COLLAPSE, 3303 nodeInfo.getNodeItem().imageCollapse?.isCollapse); 3304 } 3305 3306 for (let i: number = 0; i < tmp.length; i++) { 3307 if (tmp[i].getNodeCurrentNodeId() === nodeInfo.getNodeCurrentNodeId()) { 3308 nodeInfo.setNodeIsShow(tmp[i].getNodeIsShow()); 3309 nodeInfo.setListItemHeight(tmp[i].getListItemHeight()); 3310 if (nodeInfo.getNodeItem().mainTitleNode && nodeInfo.getIsShowTitle()) { 3311 nodeInfo.getNodeItem().mainTitleNode.title = tmp[i].getNodeItem().mainTitleNode?.title as string; 3312 } 3313 break; 3314 } 3315 } 3316 if (nodeInfo.getNodeIsShow()) { 3317 this.loadedNodeIdAndIndexMap.set(nodeInfo.getNodeCurrentNodeId(), index++); 3318 this.loadedListNode.push(nodeInfo); 3319 } 3320 } 3321 } 3322 return false; 3323 }); 3324 } 3325 3326 public getFlagLine(): FlagLine { 3327 return this.FLAG_LINE; 3328 } 3329 3330 public getVisibility(nodeInfo: NodeInfo): Visibility { 3331 let lastShowIndex: number = this.loadedNodeIdAndIndexMap.get(nodeInfo.getNodeCurrentNodeId()) as number - 1; 3332 if (lastShowIndex > this.INITIAL_INVALID_VALUE) { 3333 let lastNodeInfo: NodeInfo | undefined = this.getData(lastShowIndex); 3334 return (nodeInfo.getCanShowFlagLine() === true && !nodeInfo.getIsHighLight() && !lastNodeInfo?.getIsHighLight()) ? 3335 Visibility.Visible : Visibility.Hidden; 3336 } else { 3337 return (nodeInfo.getCanShowFlagLine() === true && !nodeInfo.getIsHighLight()) ? 3338 Visibility.Visible : Visibility.Hidden; 3339 } 3340 } 3341 3342 public getSubTitlePara(): SubTitleStyle { 3343 return this.subTitle; 3344 } 3345 3346 public getIsFolder(nodeId: number): boolean | undefined { 3347 if (this.loadedNodeIdAndIndexMap.has(nodeId)) { 3348 return this.getData(this.loadedNodeIdAndIndexMap.get(nodeId) as number)?.getIsFolder(); 3349 } 3350 return false; 3351 } 3352 3353 public getSubTitleFontColor(isHighLight: boolean): ResourceColor { 3354 return isHighLight ? this.subTitle.highLightFontColor : this.treeViewTheme.secondaryTitleFontColor; 3355 } 3356 3357 private getChildIndex(rearParentNodeId: number, rearCurrentNodeId: number): number { 3358 let insertChildIndex: number = this.INITIAL_INVALID_VALUE; 3359 if (this.nodeIdNodeItemMap.has(rearParentNodeId)) { 3360 let node: NodeItem = this.nodeIdNodeItemMap.get(rearParentNodeId) as NodeItem; 3361 if (node.getCurrentNodeId() === rearParentNodeId) { 3362 node.children.forEach((value, index) => { 3363 if (value.getCurrentNodeId() === rearCurrentNodeId) { 3364 insertChildIndex = index; 3365 return; 3366 } 3367 }) 3368 } 3369 } 3370 return insertChildIndex; 3371 } 3372 3373 public setCurrentFocusNodeId(focusNodeId: number): void { 3374 this.currentFocusNodeId = focusNodeId; 3375 } 3376 3377 public getCurrentFocusNodeId(): number { 3378 return this.currentFocusNodeId; 3379 } 3380 3381 public setLastFocusNodeId(focusNodeId: number): void { 3382 this.lastFocusNodeId = focusNodeId; 3383 } 3384 3385 public getLastFocusNodeId(): number { 3386 return this.lastFocusNodeId; 3387 } 3388 3389 public getAddFocusNodeId(): number { 3390 return this.addFocusNodeId; 3391 } 3392 3393 public setFlag(flag: Flag): void { 3394 this.flag = flag; 3395 } 3396 3397 private traverseNodeDF(callback: (currentNode: NodeItem) => boolean, root: NodeItem = this._root): void { 3398 let stack: NodeItem[] = []; 3399 let found: boolean = false; 3400 stack.unshift(root); 3401 let currentNode: NodeItem = stack.shift() as NodeItem; 3402 while (!found && currentNode) { 3403 found = callback(currentNode) === true; 3404 if (!found) { 3405 stack.unshift(...currentNode.children); 3406 currentNode = stack.shift() as NodeItem; 3407 } 3408 } 3409 } 3410 3411 private traverseSectionNodeDF(callback: (currentNode: NodeItem) => boolean, root: NodeItem = this._root, 3412 startLevel?: number, endLevel?: number): void { 3413 let stack: NodeItem[] = []; 3414 let found: boolean = false; 3415 let isPassNode: boolean = false; 3416 stack.unshift(root); 3417 let currentNode: NodeItem = stack.shift() as NodeItem; 3418 while (!found && currentNode) { 3419 try { 3420 if (startLevel !== undefined && currentNode.nodeLevel < startLevel) { 3421 isPassNode = true; 3422 } 3423 if (endLevel !== undefined && currentNode.nodeLevel > endLevel) { 3424 isPassNode = true; 3425 } 3426 if (!isPassNode) { 3427 found = callback(currentNode); 3428 } 3429 } catch (err) { 3430 throw new Error('traverseSectionNodeDF function callbacks error'); 3431 } 3432 if (!found) { 3433 stack.unshift(...currentNode.children); 3434 currentNode = stack.shift() as NodeItem; 3435 isPassNode = false; 3436 } 3437 } 3438 } 3439 3440 private updateParentChildNum(parentNode: NodeItem, isAdd: boolean, count: number): void { 3441 let parentNodeId: number = parentNode.parentNodeId; 3442 while (parentNodeId >= 0) { 3443 if (this.nodeIdNodeItemMap.has(parentNodeId)) { 3444 let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem; 3445 parent.getChildNodeInfo().allChildNum = 3446 isAdd ? parent.getChildNodeInfo().allChildNum + count : parent.getChildNodeInfo().allChildNum - count; 3447 parentNodeId = parent.parentNodeId; 3448 } else { 3449 hilog.error(LOG_CODE, TAG, 'updateParentChildNum: parent node not found.'); 3450 break; 3451 } 3452 } 3453 } 3454 3455 /** 3456 * find parent node id 3457 * 3458 * @param currentNodeId current node id 3459 * @returns parent node id 3460 */ 3461 public findParentNodeId(currentNodeId: number): number { 3462 let current: NodeItem = new NodeItem(emptyNodeInfo); 3463 if (this.nodeIdNodeItemMap.has(currentNodeId)) { 3464 current = this.nodeIdNodeItemMap.get(currentNodeId) as NodeItem; 3465 } 3466 return current.parentNodeId; 3467 } 3468 3469 /** 3470 * add nodeItem in params 3471 * 3472 * @param parentNodeId parent node id 3473 * @param currentNodeId current node id 3474 * @param data node param 3475 * @param initBuild whether in initialization process 3476 */ 3477 public addNode(parentNodeId: number, 3478 currentNodeId: number, 3479 data: NodeParam, initBuild: boolean): boolean { 3480 if (this._root === null) { 3481 this._root = new NodeItem(emptyNodeInfo); 3482 this._root.nodeLevel = -1; 3483 this.nodeIdNodeItemMap.set(-1, this._root); 3484 this.nodeIdNodeParamMap.set(-1, emptyNodeInfo); 3485 } 3486 if (this.nodeIdNodeItemMap.has(parentNodeId)) { 3487 let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem; 3488 let currentNode: NodeItem = new NodeItem(data); 3489 if (parent.nodeLevel > this.maxNodeLevel) { 3490 hilog.error(LOG_CODE, TAG, 'ListDataSource[addNode]: The level of the tree view cannot exceed 50.'); 3491 return false; 3492 } 3493 currentNode.nodeLevel = parent.nodeLevel + 1; 3494 currentNode.parentNodeId = parentNodeId; 3495 currentNode.currentNodeId = currentNodeId; 3496 currentNode.indexOfParent = parent.children.length; 3497 data.parentNodeId = parentNodeId; 3498 data.currentNodeId = currentNodeId; 3499 parent.children.push(currentNode); 3500 parent.getChildNodeInfo().isHasChildNode = true; 3501 parent.getChildNodeInfo().childNum = parent.children.length; 3502 parent.getChildNodeInfo().allChildNum += 1; 3503 if (initBuild) { 3504 this.updateNodeIdList.push(parent.parentNodeId); 3505 } else { 3506 let updateNodeIdList: number[] = []; 3507 updateNodeIdList.push(parent.parentNodeId); 3508 delayUpdateParentChildNum(true, 1, this.nodeIdNodeItemMap, updateNodeIdList); 3509 } 3510 this.nodeIdNodeParamMap.set(currentNodeId, data); 3511 this.nodeIdNodeItemMap.set(currentNodeId, currentNode); 3512 return true; 3513 } else { 3514 hilog.error(LOG_CODE, TAG, 'ListDataSource[addNode]: Parent node not found.'); 3515 return false; 3516 } 3517 } 3518 3519 private freeNodeMemory(rootNode: NodeItem, removeNodeIdList: number[]): void { 3520 let deleteNode: NodeItem[] = []; 3521 let callback = (node: NodeItem): boolean => { 3522 deleteNode.push(node); 3523 return false; 3524 }; 3525 this.traverseNodeDF(callback, rootNode); 3526 deleteNode.forEach((value: NodeItem) => { 3527 removeNodeIdList.push(value.getCurrentNodeId()); 3528 this.nodeIdNodeItemMap.delete(value.getCurrentNodeId()); 3529 this.nodeIdNodeParamMap.delete(value.getCurrentNodeId()); 3530 value = new NodeItem(emptyNodeInfo); 3531 }) 3532 } 3533 3534 /** 3535 * remove node 3536 * 3537 * @param currentNodeId current node id 3538 * @param parentNodeId parent node id 3539 * @returns node id list which is removed 3540 */ 3541 public removeNode(currentNodeId: number, parentNodeId: number): number[] { 3542 if (this.nodeIdNodeItemMap.has(parentNodeId) && this.nodeIdNodeItemMap.has(currentNodeId)) { 3543 let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem; 3544 let current: NodeItem = this.nodeIdNodeItemMap.get(currentNodeId) as NodeItem; 3545 let removeNodeIdList: number[] = []; 3546 let index: number = current.indexOfParent; 3547 let deleteNodeAllChildNum: number = 0; 3548 if (index < 0) { 3549 hilog.error(LOG_CODE, TAG, 'node does not exist.'); 3550 return []; 3551 } else { 3552 deleteNodeAllChildNum = parent.children[index].getChildNodeInfo().allChildNum + 1; 3553 this.freeNodeMemory(parent.children[index], removeNodeIdList); 3554 for (let i: number = index; i < parent.children.length; i++) { 3555 parent.children[i].indexOfParent -= 1; 3556 } 3557 let node: NodeItem[] | null = parent.children.splice(index, 1); 3558 node = null; 3559 if (parent.children.length === 0) { 3560 if (this.nodeIdAndNodeIndexMap.has(parentNodeId)) { 3561 let parentIndex: number = this.nodeIdAndNodeIndexMap.get(parentNodeId) as number; 3562 this.listNode[parentIndex]?.addImageCollapse(false); 3563 } 3564 } 3565 } 3566 parent.getChildNodeInfo().childNum = parent.children.length; 3567 parent.getChildNodeInfo().allChildNum -= (deleteNodeAllChildNum); 3568 let updateNodeIdList: number[] = []; 3569 updateNodeIdList.push(parent.parentNodeId); 3570 delayUpdateParentChildNum(false, deleteNodeAllChildNum, this.nodeIdNodeItemMap, updateNodeIdList); 3571 return removeNodeIdList; 3572 } else { 3573 hilog.error(LOG_CODE, TAG, 'parent does not exist.'); 3574 return []; 3575 } 3576 } 3577 3578 private getNodeInfoByNodeItem(nodeItem: NodeItem): NodeInfo { 3579 if (nodeItem?.currentNodeId === undefined) { 3580 hilog.error(LOG_CODE, TAG, 'getNodeInfoByNodeItem: currentId is undefined'); 3581 return new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo); 3582 } 3583 if (!this.nodeIdAndNodeIndexMap.has(nodeItem.currentNodeId)) { 3584 hilog.error(LOG_CODE, TAG, 'getNodeInfoByNodeItem: not has nodeItem.'); 3585 return new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo); 3586 } 3587 let index: number = this.nodeIdAndNodeIndexMap.get(nodeItem.currentNodeId) as number; 3588 return this.listNode[index]; 3589 } 3590 3591 /** 3592 * get node param by node id 3593 * @param nodeId node id 3594 * @returns node param 3595 */ 3596 public getNewNodeParam(nodeId: number): NodeParam { 3597 let parent: NodeItem = new NodeItem(emptyNodeInfo); 3598 if (this.nodeIdNodeItemMap.has(nodeId)) { 3599 parent = this.nodeIdNodeItemMap.get(nodeId) as NodeItem; 3600 } 3601 let newNodeParam: NodeParam = emptyNodeInfo; 3602 if (parent) { 3603 let nodeInfo: NodeInfo = this.getNodeInfoByNodeItem(parent); 3604 if (parent.children.length === 0) { 3605 if (nodeInfo.getNodeItem().imageNode !== undefined) { 3606 newNodeParam.icon = nodeInfo.getNodeItem().imageNode?.normalSource; 3607 newNodeParam.selectedIcon = nodeInfo.getNodeItem().imageNode?.selectedSource; 3608 newNodeParam.editIcon = nodeInfo.getNodeItem().imageNode?.editSource; 3609 newNodeParam.container = nodeInfo.getMenu(); 3610 } else { 3611 newNodeParam.icon = undefined; 3612 newNodeParam.selectedIcon = undefined; 3613 newNodeParam.editIcon = undefined; 3614 newNodeParam.container = nodeInfo.getMenu(); 3615 } 3616 } else if (parent.children.length > 0) { 3617 let childNodeInfo: NodeInfo = this.getNodeInfoByNodeItem(parent.children[0]); 3618 if (nodeInfo.getNodeItem().imageNode !== null) { 3619 newNodeParam.icon = (childNodeInfo.getNodeItem().imageNode !== undefined) ? 3620 childNodeInfo.getNodeItem().imageNode?.normalSource : undefined; 3621 newNodeParam.selectedIcon = (childNodeInfo.getNodeItem().imageNode !== undefined) ? 3622 childNodeInfo.getNodeItem().imageNode?.selectedSource : undefined; 3623 newNodeParam.editIcon = (childNodeInfo.getNodeItem().imageNode !== undefined) ? 3624 childNodeInfo.getNodeItem().imageNode?.editSource : undefined; 3625 newNodeParam.container = childNodeInfo.getMenu(); 3626 } else { 3627 newNodeParam.icon = undefined; 3628 newNodeParam.selectedIcon = undefined; 3629 newNodeParam.editIcon = undefined; 3630 newNodeParam.container = childNodeInfo.getMenu(); 3631 } 3632 } 3633 } 3634 return newNodeParam; 3635 } 3636 3637 /** 3638 * get child node ids by node id 3639 * 3640 * @param nodeId node id 3641 * @returns child node ids 3642 */ 3643 public getClickChildId(nodeId: number): number[] { 3644 let parent: NodeItem = new NodeItem(emptyNodeInfo); 3645 if (this.nodeIdNodeItemMap.has(nodeId)) { 3646 parent = this.nodeIdNodeItemMap.get(nodeId) as NodeItem; 3647 } 3648 if (parent) { 3649 if (parent.children.length === 0) { 3650 return []; 3651 } else if (parent.children.length > 0) { 3652 let childrenNodeInfo: number[] = new Array(parent.children.length); 3653 for (let i: number = 0; i < childrenNodeInfo.length; i++) { 3654 childrenNodeInfo[i] = 0; 3655 } 3656 for (let i: number = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) { 3657 childrenNodeInfo[i] = parent.children[i].currentNodeId; 3658 } 3659 return childrenNodeInfo; 3660 } 3661 } 3662 return []; 3663 } 3664 3665 /** 3666 * get child nodeInfo views by node id 3667 * 3668 * @param nodeId node id 3669 * @returns child nodeInfo views 3670 */ 3671 public getClickNodeChildrenInfo(nodeId: number): NodeInfoView[] { 3672 let parent: NodeItem = new NodeItem(emptyNodeInfo); 3673 if (this.nodeIdNodeItemMap.has(nodeId)) { 3674 parent = this.nodeIdNodeItemMap.get(nodeId) as NodeItem; 3675 } 3676 if (parent) { 3677 if (parent.children.length === 0) { 3678 return []; 3679 } else if (parent.children.length > 0) { 3680 let childrenNodeInfo: NodeInfoView[] = new Array(parent.children.length); 3681 for (let i: number = 0; i < childrenNodeInfo.length; i++) { 3682 childrenNodeInfo[i] = {}; 3683 } 3684 for (let i: number = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) { 3685 childrenNodeInfo[i].itemId = parent.children[i].currentNodeId; 3686 let nodeInfo: NodeInfo = this.getNodeInfoByNodeItem(parent.children[i]); 3687 if (nodeInfo.getNodeItem().imageNode) { 3688 childrenNodeInfo[i].itemIcon = nodeInfo.getNodeItem().imageNode?.source; 3689 } 3690 if (nodeInfo.getNodeItem().mainTitleNode) { 3691 childrenNodeInfo[i].itemTitle = nodeInfo.getNodeItem().mainTitleNode?.title; 3692 } 3693 childrenNodeInfo[i].isFolder = nodeInfo.getIsFolder(); 3694 } 3695 return childrenNodeInfo; 3696 } 3697 } 3698 return []; 3699 } 3700 3701 /** 3702 * check main title is valid 3703 * 3704 * @param title main title 3705 * @returns check result 3706 */ 3707 public checkMainTitleIsValid(title: string): boolean { 3708 if (new RegExp('/[\\\/:*?"<>|]/').test(title)) { 3709 return false; 3710 } 3711 if ((new RegExp('/^[\u4e00-\u9fa5]+$/').test(title) && title.length > this.MAX_CN_LENGTH) || 3712 (!new RegExp('/^[\u4e00-\u9fa5]+$/').test(title) && title.length > this.MAX_EN_LENGTH)) { 3713 return false; 3714 } 3715 return true; 3716 } 3717 3718 /** 3719 * DFS: Depth first traversal in drag event. 3720 * 3721 * @param callback dfs callback fuction 3722 */ 3723 dragTraverseNodeDF(callback: (node: NodeItem, listNode: NodeInfo[]) => boolean, 3724 root: NodeItem = this._root, listNode: NodeInfo[]): void { 3725 let stack: NodeItem[] = []; 3726 let found: boolean = false; 3727 stack.unshift(root); 3728 let currentNode: NodeItem = stack.shift() as NodeItem; 3729 while (!found && currentNode) { 3730 found = callback(currentNode, listNode) === true; 3731 if (!found) { 3732 stack.unshift(...currentNode.children); 3733 currentNode = stack.shift() as NodeItem; 3734 } 3735 } 3736 } 3737 3738 private updateChildIndexOfParent(insertIndex: number, parent: NodeItem): void { 3739 for (let i: number = insertIndex; i < parent.children.length; i++) { 3740 parent.children[i].indexOfParent += 1; 3741 } 3742 } 3743 3744 /** 3745 * Add the first dragging node in dragging nodes 3746 * 1.the first dragging node needs to distinguish the position to insert 3747 */ 3748 private addDragNode(parentNodeId: number, 3749 currentNodeId: number, 3750 insertCurrentNodeId: number, 3751 isAfter: boolean, 3752 data: NodeParam): boolean { 3753 3754 if (this._root === null) { 3755 this._root = new NodeItem(emptyNodeInfo); 3756 this._root.nodeLevel = this.INITIAL_INVALID_VALUE; 3757 } 3758 3759 if (this.nodeIdNodeItemMap.has(parentNodeId)) { 3760 let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem; 3761 let currentNode: NodeItem = new NodeItem(data); 3762 if (parent.nodeLevel > this.maxNodeLevel) { 3763 hilog.error(LOG_CODE, TAG, 'addDragNode: The level of the tree view cannot exceed 50.'); 3764 return false; 3765 } 3766 currentNode.nodeLevel = parent.nodeLevel + 1; 3767 currentNode.parentNodeId = parentNodeId; 3768 currentNode.currentNodeId = currentNodeId; 3769 data.parentNodeId = parentNodeId; 3770 data.currentNodeId = currentNodeId; 3771 let insertIndex: number = this.INITIAL_INVALID_VALUE; 3772 if (parent.children.length) { 3773 for (let i: number = 0; i < parent.children.length; i++) { 3774 if (parent.children[i].getCurrentNodeId() === insertCurrentNodeId) { 3775 insertIndex = i; 3776 break; 3777 } 3778 } 3779 if (isAfter) { 3780 currentNode.indexOfParent = insertIndex + 1; 3781 this.updateChildIndexOfParent(currentNode.indexOfParent, parent); 3782 parent.children.splice(insertIndex + 1, 0, currentNode); 3783 } else { 3784 currentNode.indexOfParent = insertIndex < 0 ? parent.children.length + insertIndex : insertIndex; 3785 this.updateChildIndexOfParent(currentNode.indexOfParent, parent); 3786 parent.children.splice(insertIndex, 0, currentNode); 3787 } 3788 } else { 3789 currentNode.indexOfParent = parent.children.length; 3790 parent.children.push(currentNode); 3791 } 3792 parent.getChildNodeInfo().isHasChildNode = true; 3793 parent.getChildNodeInfo().childNum = parent.children.length; 3794 parent.getChildNodeInfo().allChildNum += 1; 3795 this.updateParentChildNum(parent, true, 1); 3796 this.nodeIdNodeItemMap.set(currentNodeId, currentNode); 3797 this.nodeIdNodeParamMap.set(currentNodeId, data); 3798 return true; 3799 } else { 3800 hilog.error(LOG_CODE, TAG, 'addDragNode: Parent node not found.'); 3801 return false; 3802 } 3803 } 3804} 3805 3806@Component 3807export struct TreeViewInner { 3808 @ObjectLink item: NodeInfo; 3809 listNodeDataSource: ListNodeDataSource = new ListNodeDataSource(); 3810 @State columnWidth: number = 0; 3811 @State isFocused: boolean = false; 3812 @State index: number = -1; 3813 @State lastIndex: number = -1; 3814 @State count: number = 0; 3815 @State followingSystemFontScale: boolean = false; 3816 @State maxAppFontScale: number = 1; 3817 @Consume treeViewTheme: TreeViewTheme; 3818 @BuilderParam private listTreeViewMenu: () => void; 3819 private readonly MAX_CN_LENGTH: number = 254; 3820 private readonly MAX_EN_LENGTH: number = 255; 3821 private readonly INITIAL_INVALID_VALUE = -1; 3822 private readonly MAX_TOUCH_DOWN_COUNT = 0; 3823 private isMultiPress: boolean = false; 3824 private touchDownCount: number = this.INITIAL_INVALID_VALUE; 3825 private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener(); 3826 private readonly itemPadding: ItemPadding = { 3827 left: $r('sys.float.ohos_id_card_margin_start'), 3828 right: $r('sys.float.ohos_id_card_margin_end'), 3829 top: $r('sys.float.ohos_id_text_margin_vertical'), 3830 bottom: $r('sys.float.padding_level0'), 3831 }; 3832 private readonly textInputPadding: ItemPadding = { 3833 left: $r('sys.float.padding_level0'), 3834 right: $r('sys.float.padding_level0'), 3835 top: $r('sys.float.padding_level0'), 3836 bottom: $r('sys.float.padding_level0') }; 3837 private inputFontSize: number = resourceManager.getSystemResourceManager().getNumberByName('ohos_id_text_size_body1'); 3838 3839 aboutToAppear(): void { 3840 if (this.item.getNodeItem().imageNode) { 3841 this.item.imageSource = this.item.getNodeItem().imageNode?.source; 3842 } 3843 let uiContent: UIContext = this.getUIContext(); 3844 this.followingSystemFontScale = uiContent.isFollowingSystemFontScale(); 3845 this.maxAppFontScale = uiContent.getMaxFontScale(); 3846 } 3847 3848 decideFontScale() { 3849 let uiContent: UIContext = this.getUIContext(); 3850 let systemFontScale = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1; 3851 if (!this.followingSystemFontScale) { 3852 return 1; 3853 } 3854 return Math.min(systemFontScale, this.maxAppFontScale, MAX_FONT_SCALE) 3855 } 3856 3857 getInputTextMaxFontSize() { 3858 let inputTextMaxFontSize = this.decideFontScale() * this.inputFontSize + 'vp'; 3859 return inputTextMaxFontSize; 3860 } 3861 3862 getLeftIconColor(): ResourceColor { 3863 if (this.item.getIsModify()) { 3864 return $r('sys.color.icon_on_primary'); 3865 } else if (this.item.getIsSelected()) { 3866 return this.treeViewTheme.leftIconActiveColor; 3867 } else { 3868 return this.treeViewTheme.leftIconColor; 3869 } 3870 } 3871 3872 private checkInvalidPattern(title: string): boolean { 3873 return new RegExp('/[\\\/:*?"<>|]/').test(title); 3874 } 3875 3876 private checkIsAllCN(title: string): boolean { 3877 return new RegExp('/^[\u4e00-\u9fa5]+$/').test(title); 3878 } 3879 3880 @Builder 3881 popupForShowTitle(text: string | Resource | undefined, backgroundColor: ResourceColor, fontColor: ResourceColor) { 3882 Row() { 3883 Text(text) 3884 .fontSize($r('sys.float.ohos_id_text_size_body2')) 3885 .fontWeight('regular') 3886 .fontColor(fontColor) 3887 .minFontScale(MIN_FONT_SCALE) 3888 .maxFontScale(this.decideFontScale()) 3889 } 3890 .backgroundColor(backgroundColor) 3891 .border({ radius: $r('sys.float.ohos_id_elements_margin_horizontal_l') }) 3892 .padding({ 3893 left: $r('sys.float.ohos_id_elements_margin_horizontal_l'), 3894 right: $r('sys.float.ohos_id_elements_margin_horizontal_l'), 3895 top: $r('sys.float.ohos_id_card_margin_middle'), 3896 bottom: $r('sys.float.ohos_id_card_margin_middle'), 3897 }) 3898 } 3899 3900 @Builder 3901 builder() { 3902 if (this.listTreeViewMenu) { 3903 this.listTreeViewMenu() 3904 } 3905 } 3906 3907 build() { 3908 if (this.item.getNodeIsShow()) { 3909 Stack() { 3910 Column() { 3911 Stack({ alignContent: Alignment.Bottom }) { 3912 Divider() 3913 .height(this.listNodeDataSource.getFlagLine().flagLineHeight) 3914 .color(this.listNodeDataSource.getFlagLine().flagLineColor) 3915 .visibility(this.listNodeDataSource.getVisibility(this.item)) 3916 .lineCap(LineCapStyle.Round) 3917 .margin({ start: LengthMetrics.vp(this.item.getFlagLineLeftMargin()) }) 3918 .focusable(true) 3919 Row({}) { 3920 if (this.item.getNodeItem().imageNode) { 3921 Row() { 3922 Image(this.item.imageSource) 3923 .objectFit(ImageFit.Contain) 3924 .height(this.item.getNodeItem().imageNode?.itemHeight) 3925 .width(this.item.getNodeItem().imageNode?.itemWidth) 3926 .opacity(!this.item.getIsSelected() && !this.item.getIsHighLight() ? 3927 this.item.getNodeItem().imageNode?.opacity : this.item.getNodeItem().imageNode?.noOpacity) 3928 .focusable(this.item.getNodeItem().mainTitleNode !== null ? false : true) 3929 .fillColor(this.getLeftIconColor()) 3930 .matchTextDirection((this.item.getNodeItem() 3931 .imageCollapse?.collapseSource === ARROW_RIGHT || this.item.getNodeItem() 3932 .imageCollapse?.collapseSource === ARROW_RIGHT_WITHE) ? true : false) 3933 } 3934 .focusable(true) 3935 .backgroundColor(COLOR_IMAGE_ROW) 3936 .margin({ 3937 end: getLengthMetricsByResourceOrNumber(this.item.getNodeItem().imageNode?.itemRightMargin) 3938 }) 3939 .height(this.item.getNodeItem().imageNode?.itemHeight) 3940 .width(this.item.getNodeItem().imageNode?.itemWidth) 3941 } 3942 Row() { 3943 if (this.item.getNodeItem().mainTitleNode && this.item.getIsShowTitle()) { 3944 Text(this.item.getNodeItem().mainTitleNode?.title) 3945 .minFontScale(MIN_FONT_SCALE) 3946 .maxFontScale(this.decideFontScale()) 3947 .maxLines(1)// max line 3948 .fontSize(this.item.getNodeItem().mainTitleNode?.size) 3949 .fontColor(this.item.getIsSelected() ? 3950 this.treeViewTheme.primaryTitleActiveFontColor : this.treeViewTheme.primaryTitleFontColor) 3951 .margin({ 3952 end: getLengthMetricsByResourceOrNumber(this.item.getNodeItem() 3953 .mainTitleNode?.itemRightMargin) 3954 }) 3955 .textOverflow({ overflow: TextOverflow.Ellipsis }) 3956 .fontWeight(this.item.getNodeItem().mainTitleNode?.weight) 3957 .focusable(true) 3958 } 3959 if (this.item.getNodeItem().mainTitleNode && this.item.getNodeItem().inputText && 3960 this.item.getIsShowInputText()) { 3961 Row() { 3962 TextInput({ text: this.item.getNodeItem().mainTitleNode?.title }) 3963 .height(this.item.getNodeItem().inputText?.itemHeight) 3964 .fontSize(this.getInputTextMaxFontSize()) 3965 .fontColor(this.item.getNodeItem().inputText?.color) 3966 .borderRadius(this.item.getNodeItem().inputText?.borderRadius) 3967 .backgroundColor(this.item.getNodeItem().inputText?.backgroundColor) 3968 .enterKeyType(EnterKeyType.Done) 3969 .focusable(true) 3970 .padding({ 3971 start: LengthMetrics.resource(this.textInputPadding.left), 3972 end: LengthMetrics.resource(this.textInputPadding.right), 3973 top: LengthMetrics.resource(this.textInputPadding.top), 3974 bottom: LengthMetrics.resource(this.textInputPadding.bottom), 3975 }) 3976 .onChange((value: string) => { 3977 let thisIndex: number = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId()); 3978 let res: string = ''; 3979 let isInvalidError: boolean = false; 3980 let isLengthError: boolean = false; 3981 if (this.checkInvalidPattern(value)) { 3982 for (let i: number = 0; i < value.length; i++) { 3983 if (!this.checkInvalidPattern(value[i])) { 3984 res += value[i]; 3985 } 3986 } 3987 isInvalidError = true; 3988 this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, 3989 InputError.INVALID_ERROR, true, thisIndex); 3990 } else { 3991 res = value; 3992 isInvalidError = false; 3993 } 3994 if ((this.checkIsAllCN(res) && res.length > this.MAX_CN_LENGTH) || 3995 (!this.checkIsAllCN(res) && res.length > this.MAX_EN_LENGTH)) { 3996 res = this.checkIsAllCN(res) ? 3997 res.substr(0, this.MAX_CN_LENGTH) : res.substr(0, this.MAX_EN_LENGTH); 3998 isLengthError = true; 3999 this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, 4000 InputError.LENGTH_ERROR, true, thisIndex); 4001 } else { 4002 isLengthError = false; 4003 } 4004 if (!isLengthError && !isInvalidError) { 4005 this.listNodeDataSource.setMainTitleNameOnEdit(thisIndex, res); 4006 } 4007 }) 4008 .onSubmit((enterKey: EnterKeyType) => { 4009 let thisIndex: number = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId()); 4010 this.listNodeDataSource.setPopUpInfo( 4011 PopUpType.WARNINGS, 4012 InputError.NONE, 4013 false, 4014 thisIndex 4015 ); 4016 this.listNodeDataSource.setItemVisibilityOnEdit(thisIndex, MenuOperation.COMMIT_NODE); 4017 }) 4018 }.backgroundColor(this.item.getNodeItem().inputText?.backgroundColor) 4019 .borderRadius(this.item.getNodeItem().inputText?.borderRadius) 4020 .margin({ 4021 end: getLengthMetricsByResourceOrNumber(this.item.getNodeItem() 4022 .inputText?.itemRightMargin) 4023 }) 4024 } 4025 Blank() 4026 } 4027 .layoutWeight(1) 4028 .focusable(true) 4029 4030 if (this.listNodeDataSource.hasSubtitle(this.item.getNodeCurrentNodeId())) { 4031 Row() { 4032 Text(this.listNodeDataSource.getSubtitle(this.item.getNodeCurrentNodeId())) 4033 .minFontScale(MIN_FONT_SCALE) 4034 .maxFontScale(this.decideFontScale()) 4035 .fontSize(this.listNodeDataSource.getSubTitlePara().fontSize) 4036 .fontColor(this.item.getIsHighLight() || this.item.getIsModify() ? 4037 $r('sys.color.ohos_id_color_primary_contrary') : this.treeViewTheme.secondaryTitleFontColor) 4038 .fontWeight(this.listNodeDataSource.getSubTitlePara().fontWeight) 4039 } 4040 .focusable(true) 4041 .margin({ 4042 start: LengthMetrics.resource(this.listNodeDataSource.getSubTitlePara().margin.left), 4043 end: this.item.getNodeItem().imageCollapse ? 4044 LengthMetrics.resource($r('sys.float.padding_level0')) : LengthMetrics.resource(this.listNodeDataSource.getSubTitlePara().margin.right) 4045 }) 4046 } 4047 4048 if (this.item.getNodeItem().imageCollapse) { 4049 Row() { 4050 Image(this.item.getNodeItem().imageCollapse?.collapseSource) 4051 .fillColor(this.item.getNodeItem().imageCollapse?.isCollapse ? 4052 this.treeViewTheme.arrowIconColor : COLOR_IMAGE_EDIT) 4053 .align(Alignment.End) 4054 .objectFit(ImageFit.Contain) 4055 .height(this.item.getNodeItem().imageCollapse?.itemHeight) 4056 .width(this.item.getNodeItem().imageCollapse?.itemWidth) 4057 .opacity(!this.item.getIsHighLight() ? 4058 this.item.getNodeItem().imageCollapse?.opacity : this.item.getNodeItem().imageCollapse?.noOpacity) 4059 .onTouch((event: TouchEvent) => { 4060 if (event.type === TouchType.Down) { 4061 this.listNodeDataSource.expandAndCollapseNode( 4062 this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId())); 4063 this.listNodeDataSource.setCurrentFocusNodeId(this.item.getNodeCurrentNodeId()); 4064 } 4065 event.stopPropagation(); 4066 }) 4067 .focusable(true) 4068 .matchTextDirection((this.item.getNodeItem() 4069 .imageCollapse?.collapseSource === ARROW_RIGHT || this.item.getNodeItem() 4070 .imageCollapse?.collapseSource === ARROW_RIGHT_WITHE) ? true : false) 4071 } 4072 .focusable(true) 4073 .height(this.item.getNodeItem().imageCollapse?.itemHeight) 4074 .width(this.item.getNodeItem().imageCollapse?.itemWidth) 4075 } 4076 } 4077 .focusable(true) 4078 .width('100%') 4079 .gesture( 4080 TapGesture({ count: 2 }) 4081 .onAction((event: GestureEvent) => { 4082 this.listNodeDataSource.expandAndCollapseNode( 4083 this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId())); 4084 }) 4085 ) 4086 .height(this.item.getNodeHeight()) 4087 .padding({ start: LengthMetrics.vp(this.item.getNodeLeftPadding()) }) 4088 .bindContextMenu(this.builder, ResponseType.RightClick) 4089 4090 }.focusable(true) 4091 } 4092 .opacity(this.listNodeDataSource.getListItemOpacity(this.item)) 4093 .onHover((isHover: boolean) => { 4094 if (isHover) { 4095 this.item.setNodeColor(this.treeViewTheme.itemHoverBgColor) 4096 } else { 4097 this.item.setNodeColor($r('sys.color.ohos_id_color_background_transparent')) 4098 } 4099 }) 4100 .onTouch((event) => { 4101 this.count++; 4102 if (this.count > 1) { 4103 this.count--; 4104 return; 4105 } 4106 4107 this.index = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId()) 4108 this.listNodeDataSource.setClickIndex(this.index); 4109 let currentId: number = this.item.getNodeCurrentNodeId(); 4110 4111 if (event.type === TouchType.Down) { 4112 this.item.setNodeColor(this.treeViewTheme.itemPressedBgColor); 4113 } 4114 4115 else if (event.type === TouchType.Up) { 4116 if (!(typeof this.treeViewTheme.itemSelectedBgColor === 'string')) { 4117 this.item.setNodeColor(COLOR_SELECT); 4118 } else { 4119 this.item.setNodeColor(this.treeViewTheme.itemSelectedBgColor); 4120 } 4121 if (this.item.getNodeItem().imageNode !== null) { 4122 this.item.getNodeItem().imageNode?.setImageSource(InteractionStatus.SELECTED); 4123 this.listNodeDataSource.setImageSource(this.index, InteractionStatus.SELECTED); 4124 this.item.imageSource = this.item.getNodeItem().imageNode?.source; 4125 } 4126 4127 this.item.getNodeItem().mainTitleNode?.setMainTitleSelected(true); 4128 let callParam: CallbackParam = { currentNodeId: currentId }; 4129 this.appEventBus.emit(TreeListenType.NODE_CLICK, callParam); 4130 } 4131 4132 if (this.listNodeDataSource.getLastIndex() !== -1 && this.index !== this.listNodeDataSource.getLastIndex()) { 4133 this.listNodeDataSource.setPopUpInfo( 4134 PopUpType.WARNINGS, 4135 InputError.NONE, 4136 false, 4137 this.listNodeDataSource.getLastIndex() 4138 ); 4139 this.listNodeDataSource.setItemVisibilityOnEdit( 4140 this.listNodeDataSource.getLastIndex(), 4141 MenuOperation.COMMIT_NODE 4142 ); 4143 } 4144 this.lastIndex = this.index; 4145 this.count--; 4146 }) 4147 /* backgroundColor when editing and in other states. */ 4148 .backgroundColor((this.item.getNodeItem().mainTitleNode && this.item.getNodeItem().inputText && 4149 this.item.getIsShowInputText()) ? this.item.getNodeItem().inputText?.editColor : this.item.getNodeColor()) 4150 .border({ 4151 width: this.item.getNodeBorder().borderWidth, 4152 color: this.item.getNodeBorder().borderColor, 4153 radius: this.item.getNodeBorder().borderRadius, 4154 }) 4155 .height(LIST_ITEM_HEIGHT) 4156 .focusable(true) 4157 .onMouse((event: MouseEvent) => { 4158 let thisIndex: number = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId()); 4159 if (event.button === MouseButton.Right) { 4160 this.listNodeDataSource.handleEvent(Event.MOUSE_BUTTON_RIGHT, 4161 this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId())); 4162 this.listTreeViewMenu = this.item.getMenu(); 4163 this.listNodeDataSource.setClickIndex(thisIndex); 4164 clearTimeout(this.item.getNodeItem().mainTitleNode?.popUpTimeout); 4165 } 4166 event.stopPropagation(); 4167 }) 4168 .padding({ top: 0, bottom: 0 }) 4169 .bindPopup(this.item.getPopUpInfo().popUpIsShow, { 4170 builder: this.popupForShowTitle(this.item.getPopUpInfo().popUpText, this.item.getPopUpInfo().popUpColor, 4171 this.item.getPopUpInfo().popUpTextColor), 4172 placement: Placement.BottomLeft, 4173 placementOnTop: false, 4174 popupColor: this.item.getPopUpInfo().popUpColor, 4175 autoCancel: true, 4176 enableArrow: this.item.getPopUpInfo().popUpEnableArrow, 4177 }) 4178 .onAreaChange((oldValue: Area, newValue: Area) => { 4179 let columnWidthNum: number = Number.parseInt(newValue.width.toString()); 4180 this.columnWidth = columnWidthNum; 4181 }) 4182 } 4183 .stateStyles({ 4184 focused: { 4185 .border({ 4186 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 4187 width: FLAG_NUMBER, 4188 color: this.treeViewTheme.borderFocusedColor, 4189 style: BorderStyle.Solid, 4190 }) 4191 }, 4192 normal: { 4193 .border({ 4194 radius: $r('sys.float.ohos_id_corner_radius_clicked'), 4195 width: 0, 4196 }) 4197 } 4198 }) 4199 } 4200 } 4201} 4202 4203export class NodeItem { 4204 public childNodeInfo: ChildNodeInfo; 4205 public nodeLevel: number; 4206 public children: NodeItem[]; 4207 public indexOfParent: number; 4208 public parentNodeId: number; 4209 public currentNodeId: number; 4210 public isFolder?: boolean; 4211 4212 constructor(nodeParam: NodeParam) { 4213 this.currentNodeId = nodeParam.currentNodeId ?? -1; 4214 this.parentNodeId = nodeParam.parentNodeId ?? -1; 4215 this.isFolder = nodeParam.isFolder; 4216 this.nodeLevel = -1; 4217 this.indexOfParent = -1; 4218 this.childNodeInfo = { isHasChildNode: false, childNum: 0, allChildNum: 0 }; 4219 this.children = []; 4220 } 4221 4222 getChildNodeInfo(): ChildNodeInfo { 4223 return this.childNodeInfo; 4224 } 4225 4226 getCurrentNodeId(): number { 4227 return this.currentNodeId; 4228 } 4229 4230 getIsFolder(): boolean | undefined { 4231 return this.isFolder; 4232 } 4233} 4234 4235class NodeBaseInfo { 4236 public rightMargin: Resource | number = -1; 4237 private width: number = -1; 4238 private height: number = -1; 4239 4240 constructor() { 4241 } 4242 4243 set itemWidth(width: number) { 4244 this.width = width; 4245 } 4246 4247 get itemWidth(): number { 4248 return this.width; 4249 } 4250 4251 set itemHeight(height: number) { 4252 this.height = height; 4253 } 4254 4255 get itemHeight(): number { 4256 return this.height; 4257 } 4258 4259 set itemRightMargin(rightMargin: Resource | number) { 4260 this.rightMargin = rightMargin; 4261 } 4262 4263 get itemRightMargin(): Resource | number { 4264 return this.rightMargin; 4265 } 4266} 4267 4268export class CollapseImageNode extends NodeBaseInfo { 4269 private imageSource: Resource | string; 4270 private imageOpacity: Resource; 4271 private imageCollapseSource: Resource | string; 4272 private isImageCollapse: boolean; 4273 private collapseImageType: CollapseImageType; 4274 4275 constructor( 4276 imageSource: Resource | string, 4277 imageOpacity: Resource, 4278 itemWidth: number, 4279 itemHeight: number, 4280 itemRightMargin: Resource | number, 4281 isImageCollapse: boolean, 4282 collapseImageType: CollapseImageType 4283 ) { 4284 super(); 4285 this.rightMargin = $r('sys.float.ohos_id_elements_margin_horizontal_m'); 4286 this.imageSource = imageSource; 4287 this.rightMargin = itemRightMargin; 4288 this.imageOpacity = imageOpacity; 4289 this.itemWidth = itemWidth; 4290 this.itemHeight = itemHeight; 4291 this.imageCollapseSource = imageSource; 4292 this.isImageCollapse = isImageCollapse; 4293 this.collapseImageType = collapseImageType; 4294 } 4295 4296 get source(): Resource | string { 4297 return this.imageSource; 4298 } 4299 4300 get opacity(): Resource { 4301 return this.imageOpacity; 4302 } 4303 4304 get noOpacity(): number { 4305 return 1; 4306 } 4307 4308 get collapseSource(): Resource | string { 4309 return this.imageCollapseSource; 4310 } 4311 4312 get isCollapse(): boolean { 4313 return this.isImageCollapse; 4314 } 4315 4316 get type(): CollapseImageType { 4317 return this.collapseImageType; 4318 } 4319} 4320 4321class CollapseImageNodeFactory { 4322 private static instance: CollapseImageNodeFactory; 4323 4324 private constructor() { 4325 } 4326 4327 /** 4328 * CollapseImageNodeFactory singleton function 4329 * 4330 * @returns CollapseImageNodeFactory 4331 */ 4332 public static getInstance(): CollapseImageNodeFactory { 4333 if (!CollapseImageNodeFactory.instance) { 4334 CollapseImageNodeFactory.instance = new CollapseImageNodeFactory(); 4335 } 4336 return CollapseImageNodeFactory.instance; 4337 } 4338 4339 /** 4340 * create collapse image node by type 4341 * 4342 * @param type collapse image type 4343 * @returns collapse image node 4344 */ 4345 public createCollapseImageNodeByType(type: CollapseImageType): CollapseImageNode { 4346 let imageSource: Resource | string; 4347 switch (type) { 4348 case CollapseImageType.ARROW_RIGHT_WHITE: 4349 imageSource = ARROW_RIGHT_WITHE; 4350 break; 4351 case CollapseImageType.ARROW_RIGHT: 4352 imageSource = ARROW_RIGHT; 4353 break; 4354 case CollapseImageType.ARROW_DOWN_WHITE: 4355 imageSource = ARROW_DOWN_WITHE; 4356 break; 4357 default: 4358 imageSource = ARROW_DOWN; 4359 } 4360 return new CollapseImageNode( 4361 imageSource, 4362 $r('sys.float.ohos_id_alpha_content_tertiary'), 4363 IMAGE_NODE_HEIGHT, 4364 IMAGE_NODE_WIDTH, 4365 $r('sys.float.ohos_id_text_paragraph_margin_xs'), 4366 (type === CollapseImageType.ARROW_RIGHT_WHITE || type === CollapseImageType.ARROW_DOWN_WHITE) ? false : true, 4367 type 4368 ); 4369 } 4370} 4371 4372class CollapseImageNodeFlyweightFactory { 4373 private static nodeMap: Map<CollapseImageType, CollapseImageNode> = new Map<CollapseImageType, CollapseImageNode>(); 4374 4375 /** 4376 * get collapse image node by type 4377 * 4378 * @param type collapse image node type 4379 * @returns collapse image node 4380 */ 4381 static getCollapseImageNodeByType(type: CollapseImageType): CollapseImageNode { 4382 let node: CollapseImageNode | undefined = CollapseImageNodeFlyweightFactory.nodeMap.get(type); 4383 if (node === undefined) { 4384 node = CollapseImageNodeFactory.getInstance().createCollapseImageNodeByType(type); 4385 CollapseImageNodeFlyweightFactory.nodeMap.set(type, node); 4386 } 4387 return node; 4388 } 4389 4390 /** 4391 * get collapse image node by interactionStatus and nodeStatus 4392 * 4393 * @param interactionStatus interaction status 4394 * @param nodeStatus node status 4395 * @param defaultType default collapse image type 4396 * @returns collapse image node 4397 */ 4398 static getCollapseImageNode(interactionStatus: InteractionStatus, nodeStatus: NodeStatus | undefined, 4399 defaultType?: CollapseImageType): CollapseImageNode | undefined { 4400 if (defaultType === undefined) { 4401 return undefined; 4402 } 4403 let type: CollapseImageType = defaultType; 4404 if (interactionStatus == InteractionStatus.EDIT || 4405 interactionStatus === InteractionStatus.DRAG_INSERT) { 4406 if (nodeStatus === NodeStatus.COLLAPSE) { 4407 type = CollapseImageType.ARROW_RIGHT_WHITE; 4408 } else { 4409 type = CollapseImageType.ARROW_DOWN_WHITE; 4410 } 4411 } else if (interactionStatus === InteractionStatus.FINISH_EDIT || 4412 interactionStatus === InteractionStatus.FINISH_DRAG_INSERT) { 4413 if (nodeStatus === NodeStatus.COLLAPSE) { 4414 type = CollapseImageType.ARROW_RIGHT; 4415 } else { 4416 type = CollapseImageType.ARROW_DOWN; 4417 } 4418 } 4419 return CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(type); 4420 } 4421 4422 /** 4423 * change collapse image node source 4424 * 4425 * @param nodeStatus node status 4426 * @param isImageCollapse whether collapse image or white collapse image 4427 * @returns collapse image node 4428 */ 4429 static changeImageCollapseSource(nodeStatus: NodeStatus, isImageCollapse?: boolean): CollapseImageNode | undefined { 4430 if (isImageCollapse === undefined) { 4431 return undefined; 4432 } 4433 let type: CollapseImageType; 4434 if (!isImageCollapse) { 4435 if (nodeStatus === NodeStatus.COLLAPSE) { 4436 type = CollapseImageType.ARROW_RIGHT_WHITE; 4437 } else { 4438 type = CollapseImageType.ARROW_DOWN_WHITE; 4439 } 4440 } else { 4441 if (nodeStatus === NodeStatus.COLLAPSE) { 4442 type = CollapseImageType.ARROW_RIGHT; 4443 } else { 4444 type = CollapseImageType.ARROW_DOWN; 4445 } 4446 } 4447 return CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(type); 4448 } 4449} 4450 4451export class ImageNode extends NodeBaseInfo { 4452 private imageSource: Resource | string; 4453 private imageNormalSource: Resource | string; 4454 private imageSelectedSource: Resource | string; 4455 private imageEditSource: Resource | string; 4456 private imageOpacity: Resource; 4457 private currentInteractionStatus: InteractionStatus; 4458 private imageCollapseSource: Resource | string; 4459 private imageCollapseDownSource: Resource | string; 4460 private isImageCollapse: boolean; 4461 private imageCollapseRightSource: Resource | string; 4462 4463 constructor( 4464 imageSource: Resource | string, 4465 imageOpacity: Resource, 4466 itemWidth: number, 4467 itemHeight: number, 4468 itemSelectedIcon?: Resource | string, 4469 itemEditIcon?: Resource | string, 4470 ) { 4471 super(); 4472 this.rightMargin = $r('sys.float.ohos_id_elements_margin_horizontal_m'); 4473 this.imageSource = imageSource; 4474 this.imageNormalSource = imageSource; 4475 if (itemSelectedIcon !== undefined) { 4476 this.imageSelectedSource = itemSelectedIcon; 4477 } else { 4478 this.imageSelectedSource = this.imageNormalSource; 4479 } 4480 if (itemEditIcon !== undefined) { 4481 this.imageEditSource = itemEditIcon; 4482 } else { 4483 this.imageEditSource = this.imageNormalSource; 4484 } 4485 this.imageOpacity = imageOpacity; 4486 this.itemWidth = itemWidth; 4487 this.itemHeight = itemHeight; 4488 this.imageCollapseSource = imageSource; 4489 this.imageCollapseDownSource = ARROW_DOWN; 4490 this.imageCollapseRightSource = ARROW_RIGHT; 4491 this.isImageCollapse = true; 4492 this.currentInteractionStatus = InteractionStatus.NORMAL; 4493 } 4494 4495 get source(): Resource | string { 4496 return this.imageSource; 4497 } 4498 4499 get normalSource(): Resource | string { 4500 return this.imageNormalSource; 4501 } 4502 4503 get selectedSource(): Resource | string { 4504 return this.imageSelectedSource; 4505 } 4506 4507 get editSource(): Resource | string { 4508 return this.imageEditSource; 4509 } 4510 4511 get opacity(): Resource { 4512 return this.imageOpacity; 4513 } 4514 4515 get noOpacity(): number { 4516 return 1; 4517 } 4518 4519 get collapseSource(): Resource | string { 4520 return this.imageCollapseSource; 4521 } 4522 4523 get isCollapse(): boolean { 4524 return this.isImageCollapse; 4525 } 4526 4527 changeImageCollapseSource(nodeStatus: NodeStatus): void { 4528 if (nodeStatus === NodeStatus.EXPAND) { 4529 this.imageCollapseSource = this.imageCollapseDownSource; 4530 } else if (nodeStatus === NodeStatus.COLLAPSE) { 4531 this.imageCollapseSource = this.imageCollapseRightSource; 4532 } 4533 } 4534 4535 setImageCollapseSource(interactionStatus: InteractionStatus, nodeStatus: NodeStatus | undefined): void { 4536 if (interactionStatus === InteractionStatus.EDIT || interactionStatus === InteractionStatus.DRAG_INSERT) { 4537 this.imageCollapseDownSource = ARROW_DOWN_WITHE; 4538 this.imageCollapseRightSource = ARROW_RIGHT_WITHE; 4539 this.isImageCollapse = false; 4540 } else if (interactionStatus === InteractionStatus.FINISH_EDIT || 4541 interactionStatus === InteractionStatus.FINISH_DRAG_INSERT) { 4542 this.imageCollapseDownSource = ARROW_DOWN 4543 this.imageCollapseRightSource = ARROW_RIGHT 4544 this.isImageCollapse = true; 4545 } 4546 this.imageCollapseSource = (nodeStatus === NodeStatus.COLLAPSE) ? 4547 this.imageCollapseRightSource : this.imageCollapseDownSource; 4548 } 4549 4550 setImageSource(interactionStatus: InteractionStatus): void { 4551 switch (interactionStatus) { 4552 case InteractionStatus.NORMAL: 4553 this.imageSource = this.imageNormalSource; 4554 this.currentInteractionStatus = interactionStatus; 4555 break; 4556 case InteractionStatus.SELECTED: 4557 if (this.currentInteractionStatus !== InteractionStatus.EDIT) { 4558 this.imageSource = this.imageSelectedSource; 4559 this.currentInteractionStatus = interactionStatus; 4560 } 4561 break; 4562 case InteractionStatus.EDIT: 4563 this.imageSource = this.imageEditSource; 4564 this.currentInteractionStatus = interactionStatus; 4565 break; 4566 case InteractionStatus.FINISH_EDIT: 4567 this.imageSource = this.imageSelectedSource; 4568 this.currentInteractionStatus = interactionStatus; 4569 break; 4570 case InteractionStatus.DRAG_INSERT: 4571 this.imageSource = this.imageEditSource; 4572 this.currentInteractionStatus = interactionStatus; 4573 break; 4574 case InteractionStatus.FINISH_DRAG_INSERT: 4575 this.imageSource = this.imageNormalSource; 4576 this.currentInteractionStatus = interactionStatus; 4577 break; 4578 default: 4579 break; 4580 } 4581 } 4582} 4583 4584class MainTitleNode extends NodeBaseInfo { 4585 private mainTitleName: ResourceStr; 4586 public mainTitleSetting: TextSetting; 4587 private showPopUpTimeout: number; 4588 private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance(); 4589 4590 constructor(mainTitleName: ResourceStr) { 4591 super(); 4592 this.mainTitleName = mainTitleName; 4593 this.itemWidth = ITEM_WIDTH; 4594 this.itemHeight = ITEM_HEIGHT; 4595 this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs'); 4596 this.mainTitleSetting = { 4597 fontColor: this.treeViewTheme.primaryTitleFontColor, 4598 fontSize: $r('sys.float.ohos_id_text_size_body1'), 4599 fontWeight: FontWeight.Normal, 4600 }; 4601 this.showPopUpTimeout = 0; 4602 } 4603 4604 setMainTitleSelected(isSelected: boolean): void { 4605 if (isSelected) { 4606 this.mainTitleSetting = { 4607 fontColor: this.treeViewTheme.primaryTitleActiveFontColor, 4608 fontSize: $r('sys.float.ohos_id_text_size_body1'), 4609 fontWeight: FontWeight.Regular, 4610 }; 4611 } else { 4612 this.mainTitleSetting = { 4613 fontColor: this.treeViewTheme.primaryTitleFontColor, 4614 fontSize: $r('sys.float.ohos_id_text_size_body1'), 4615 fontWeight: FontWeight.Normal, 4616 }; 4617 } 4618 } 4619 4620 set title(text: ResourceStr) { 4621 this.mainTitleName = text; 4622 } 4623 4624 get title(): ResourceStr { 4625 return this.mainTitleName; 4626 } 4627 4628 set popUpTimeout(showPopUpTimeout: number) { 4629 this.showPopUpTimeout = showPopUpTimeout; 4630 } 4631 4632 get popUpTimeout(): number { 4633 return this.showPopUpTimeout; 4634 } 4635 4636 get color(): ResourceColor { 4637 return this.mainTitleSetting.fontColor; 4638 } 4639 4640 get size(): Resource { 4641 return this.mainTitleSetting.fontSize; 4642 } 4643 4644 get weight(): FontWeight { 4645 return this.mainTitleSetting.fontWeight; 4646 } 4647 4648 setMainTitleHighLight(isHighLight: boolean): void { 4649 if (isHighLight) { 4650 this.mainTitleSetting = { 4651 fontColor: this.treeViewTheme.primaryTitleActiveFontColor, 4652 fontSize: $r('sys.float.ohos_id_text_size_body1'), 4653 fontWeight: FontWeight.Regular, 4654 }; 4655 } else { 4656 this.mainTitleSetting = { 4657 fontColor: this.treeViewTheme.primaryTitleFontColor, 4658 fontSize: $r('sys.float.ohos_id_text_size_body1'), 4659 fontWeight: FontWeight.Normal, 4660 }; 4661 } 4662 } 4663} 4664 4665export class InputText extends NodeBaseInfo { 4666 private inputTextSetting: TextSetting; 4667 private status?: Status = undefined; 4668 private statusColor: Resource = $r('sys.color.ohos_id_color_background'); 4669 private editItemColor: Resource = $r('sys.color.ohos_id_color_emphasize'); 4670 private radius: Resource = $r('sys.float.ohos_id_corner_radius_default_xs'); 4671 private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance(); 4672 4673 constructor() { 4674 super(); 4675 this.itemWidth = ITEM_WIDTH; 4676 this.itemHeight = ITEM_HEIGHT_INPUT; 4677 this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs'); 4678 this.inputTextSetting = { 4679 fontColor: this.treeViewTheme.primaryTitleFontColor, 4680 fontSize: $r('sys.float.ohos_id_text_size_body1'), 4681 fontWeight: FontWeight.Normal, 4682 }; 4683 } 4684 4685 get color(): ResourceColor { 4686 return this.inputTextSetting.fontColor; 4687 } 4688 4689 get size(): Resource { 4690 return this.inputTextSetting.fontSize; 4691 } 4692 4693 get weight(): FontWeight { 4694 return this.inputTextSetting.fontWeight; 4695 } 4696 4697 get borderRadius(): Resource { 4698 return this.radius; 4699 } 4700 4701 get backgroundColor(): Resource { 4702 return this.statusColor; 4703 } 4704 4705 get editColor(): Resource { 4706 return this.editItemColor; 4707 } 4708 4709 get textInputStatusColor(): Status | undefined { 4710 return this.status; 4711 } 4712} 4713 4714/** 4715 * get LengthMetrics 4716 * 4717 * @param Resource | number type 4718 * @returns LengthMetrics 4719 */ 4720function getLengthMetricsByResourceOrNumber(resourceOrNumber: Resource | number): LengthMetrics { 4721 if (!resourceOrNumber) { 4722 return LengthMetrics.vp(0); 4723 } else if (typeof resourceOrNumber === 'number') { 4724 return LengthMetrics.vp(resourceOrNumber); 4725 } else { 4726 return LengthMetrics.resource(resourceOrNumber); 4727 } 4728}