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