• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}