• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const IMAGE_NODE_HEIGHT: number = 24
17const IMAGE_NODE_WIDTH: number = 24
18const ITEM_WIDTH: number = 0
19const ITEM_HEIGHT: number = 48
20const ITEM_HEIGHT_INPUT: number = 32
21const BORDER_WIDTH_HAS: number = 2
22const BORDER_WIDTH_NONE: number = 0
23const NODE_HEIGHT: number = 48
24const LIST_ITEM_HEIGHT_NONE: number = 0
25const LIST_ITEM_HEIGHT: number = 48
26const SHADOW_OFFSETY: number = 10
27const FLAG_NUMBER: number = 2
28const DRAG_OPACITY: number = 0.4
29const DRAG_OPACITY_NONE: number = 1
30const FLAG_LINE_HEIGHT: string = '1.0vp'
31const X_OFF_SET: string = '0vp'
32const Y_OFF_SET: string = '2.75vp'
33const Y_BOTTOM_OFF_SET: string = '-1.25vp'
34const Y_BASE_PLATE_OFF_SET: string = '1.5vp'
35const COLOR_SELECT: string = '#1A0A59F7'
36const COLOR_IMAGE_ROW: string = '#00000000'
37const COLOR_IMAGE_EDIT: string = '#FFFFFF'
38const SHADOW_COLOR: string = '#00001E'
39const GRAG_POP_UP_HEIGHT: string = '48'
40const LEFT_PADDING: string = '8vp'
41const RIGHT_PADDING: string = '8vp'
42const FLOOR_MIN_WIDTH: string = '128vp'
43const FLOOR_MAX_WIDTH: string = '208vp'
44const TEXT_MIN_WIDTH: string = '80vp'
45const TEXT_MAX_WIDTH: string = '160vp'
46const MIN_WIDTH: string = '112vp'
47const MAX_WIDTH: string = '192vp'
48const TRANS_COLOR: string = '#00FFFFFF'
49
50const ARROW_DOWN = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAIGNIUk0AAHomAACAhAAA+' +
51  'gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzA' +
52  'AAOxAAADsQBlSsOGwAAAq9JREFUeNrt2j9rFEEYx/FJziIECwsLCwsJIVhYiISgaIhiioiIlpaCjZ1/qhNUIkHE0negpZggEnwHFgoWFiJic' +
53  'VyRQsQXIHKIvyGzIJKbmTW7M7P6/cCvyR3c7fPsPLO7F2MAAAAAAAAAAAAAAAAAAAAAAAAAAF01keAz9ihrymVln/JEua18L7w2V5TrykHlt' +
54  'XJTGTb9Ib0EB/JYueWKP6Ucd3mh/Ci0+Hfc9z6gTCuHlUvK06ZPnMkEB3Nth78tK89dQ0os/toOfz+kHG36w1I0YNwZs6JsFtaEccVvTYoGr' +
55  'HteW3ajaKoDxbfz/10X94BXyoIyO+b1Wbcn2JE0ylT8VeW+5/UtZUn52sUG/FQ23PycG/OeGWXevW+U4cwPFf+MMmjjw3uJDnLkRo2vCTlWQ' +
56  'l954Hn9i3Je+djWF+glPNNimlCthJcJLlH7buZPemb+ovKpzS/RS7zcqybMB/aEhZZXwmqg+NXMH7ZdkNQNqJrwLLAxz7Q4jkIzf5iq+LkaU' +
57  'GdjbroJoUvNb674g1SFyNWAOntCU48tYq7zzyqfUxYhZwPqNOHYLi9RQ8XfylH8EhpQZ2P+23EUU/zFlGOntAbU2Zjr3qyF7nCrsTPIdeClN' +
58  'CB2Y66zEvoRG26WsVNqA+rerPlWwg3lYeAmayl38UtsQBOPLezMfxRxkzUo4WBLbEDsxmxXwinlrRsn+5W7yr1SbrJiTJiy2d+T7Y82K4H32' +
59  'QbsNf7fFeyDtRMlFb/kFVBnY7amXbN8l5oXTYtPNf/VBsTuCSZQfPs8/0OJB9eFBuymCXbsnCxlw+1yA2I35j+Lf860/Dz/f2rA73fMdhUc8' +
60  'bzvjXKhxJnftasgH3sJelU5bbb/Z8ee8e/N9j9PrZt8P/ADAAAAAAAAAAAAAAAAAAAAAAAgpV9KuZwVm6MIDQAAAABJRU5ErkJggg=='
61
62const ARROW_DOWN_WITHE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIGNIUk0AAHomAAC' +
63  'AhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAl' +
64  'wSFlzAAAOxAAADsQBlSsOGwAAAKVJREFUeNpjYBgFo2AU0Bww4pL4////diC1hZGRcSo+A4DqWIDUZCB+AVTbiC7PhEfvByCeAjQgn4Dhy4E' +
65  '4BYgvYFODz4JYIF4DxBOwWYJkeAAQRwBdvxGbIcy4TG9sbPzX0NCwHsjUAuIiIPsDUOwkDsPXkhwHWFwaAsQlQAwyrJsYw4myAIslIPCHGMP' +
66  'xBhGO4PoGxF+AOA9o+NbRTDgKRgFxAAAzj0Grm3RjyAAAAABJRU5ErkJggg=='
67
68const ARROW_RIGHT = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAIGNIUk0AAHomAACAhAAA' +
69  '+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz' +
70  'AAAOxAAADsQBlSsOGwAAAjVJREFUeNrtnCtvG1EQRq+9BlVUEBAYYFBgYGBYaBBoFUVVfkCtyqDAIL8jtI8EBruVIgU4UvMD0iKDPmirtlJg' +
71  'lJZUyqzsSFXlxwZsPPPtOdJHbDKac+/cXXvtlAAAAAAAAAAAAAAAAAAAAAAUyZzW1bMMLbuWv5YvqgJqTps/sjT+eW1geckOuB+OLdtzpHy3' +
72  'fFQTUHdY06MFr7+xPGMHlE93iYQnajvBo4APlj3LRhUkeBRwaRnProDkJXi9DP1ZFQmZ49oqISFzXl9RCb8sFwhYn4Sw9wlZkDplx1EWqFZJ' +
73  'CVmwHSsnIZoAuYM5ogCpgzmqAJlxFFmAxDiKLiD8OFIQEHocqQgIK0FJwF0kfLZMELA+CTuWI8s1AtYj4YHlt+UcAeVK+JqmX2/OY9Pyat1F' +
74  '1pM2J2n6Fec8mh4KVBdwO27m8RAB5dNb0uhPCCiXTpo+zLWItwgoj/bsKmhrwft/PBzAOQ3RlT9a0vycF5ZvHoqtCTZ/vKL5fcuhl4KVRlA7' +
75  'WvOVBOQP875f0fyBt+arnAH52DmNtvJVzoBwM19pBBVp/r7n5kceQa0CYyfE78pqoiu/733lRxUg1fxoZ0BbrfmRzoAiHy+Ea36UEdRRXPlR' +
76  'RlCR5g+iNt/7CGoqr3zvAuRXvucR1CrQ/GES+fMOjzvgQH3seN8B3ao036uASVWan+Pxybgflqf/LY78wH2dBPF6I/bY8txyZXlnOUsAAAAA' +
77  'AAAAAAAAAAAAAAAAEIgb8WKMjSFbuAQAAAAASUVORK5CYII='
78
79const ARROW_RIGHT_WITHE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIGNIUk0AAHomAA' +
80  'CAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAEZ0FNQQAAsY58+1GTAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAA' +
81  'lwSFlzAAAOxAAADsQBlSsOGwAAAKFJREFUeNpjYBgFowAE/v//bwHEPOToZSJS3XIg3k6OJcRaUALEFuRYwkyMosbGxusNDQ3XgMwCIHYAsl' +
82  'cDxX5RzQJKLGEmxbvkWMJEaqQxMjKuBVI5QGwDxOnUimR08AFK81DdAmAqArl8DhDfAOKpVLUAavh2IH4CxI7A4HpDMEgpMPwFUXFGS8NJCa' +
83  'L55BgOAixEqqsB4oOkGj4KRggAAN4STB9zyhGzAAAAAElFTkSuQmCC'
84
85enum Event {
86  TOUCH_DOWN = 0,
87  TOUCH_UP = 1,
88  HOVER = 3,
89  HOVER_OVER = 4,
90  FOCUS = 5,
91  BLUR = 6,
92  MOUSE_BUTTON_RIGHT = 7,
93  DRAG = 8
94}
95
96enum MenuOperation {
97  ADD_NODE = 0,
98  REMOVE_NODE = 1,
99  MODIFY_NODE = 2,
100  COMMIT_NODE = 3
101}
102
103enum PopUpType {
104  HINTS = 0,
105  WARNINGS = 1
106}
107
108enum InputError {
109  INVALID_ERROR = 0,
110  LENGTH_ERROR = 1,
111  NONE = 2
112}
113
114enum Flag {
115  DOWN_FLAG = 0,
116  UP_FLAG = 1,
117  NONE = 2
118}
119
120export enum NodeStatus {
121  Expand = 0,
122  Collapse
123}
124
125export enum InteractionStatus {
126  Normal = 0,
127  Selected,
128  Edit,
129  FinishEdit,
130  DragInsert,
131  FinishDragInsert
132}
133
134/* nodeId to find index */
135function findCurrentNodeIndex(this, currentNodeId: number): number {
136  let thisIndex: number = 0;
137  this.listNodeDataSource.ListNode.forEach(function (value, index) {
138    if (value.getNodeCurrentNodeId() == currentNodeId) {
139      thisIndex = index;
140    }
141  })
142  return thisIndex;
143}
144
145@Observed
146export class NodeInfo {
147  private childNodeInfo: { isHasChildNode: boolean, childNum: number, allChildNum: number };
148  imageSource: Resource | string;
149  private parentNodeId: number;
150  private currentNodeId: number;
151  fontColor: string | Resource;
152  private nodeHeight: Resource | number;
153  private nodeLevel: number;
154  private nodeItem: { imageNode?: TreeView.ImageNode,
155  inputText: TreeView.InputText,
156  mainTitleNode?: TreeView.MainTitleNode,
157  imageCollapse?: TreeView.ImageNode };
158  private nodeLeftPadding: number;
159  private nodeColor: Resource | string;
160  private nodeIsShow: boolean;
161  private status: { normal: Resource, hover: Resource, press: Resource, selected: string, highLight: Resource };
162  private nodeBorder: { borderWidth: Resource | number, borderColor: Resource, borderRadius: Resource };
163  private popUpInfo: { popUpIsShow: boolean,
164  popUpEnableArrow: boolean,
165  popUpColor: Resource,
166  popUpText: string | Resource,
167  popUpTextColor: Resource};
168  private listItemHeight: number;
169  private container: () => void;
170  private isShowTitle: boolean;
171  private isShowInputText: boolean;
172  private isSelected: boolean;
173  readonly borderWidth: {has: Resource | number, none: Resource | number } =
174    {has: BORDER_WIDTH_HAS/* 2vp */, none: BORDER_WIDTH_NONE/* 0vp */}
175
176  /* parameter of the drag event.*/
177  private nodeParam: {
178    isFolder?: boolean,
179    icon?: Resource,
180    selectedIcon?: Resource,
181    editIcon?: Resource,
182    primaryTitle?: string,
183    container?: () => void,
184    secondaryTitle?: number | string
185  };
186  private node: TreeView.NodeItem;
187  private canShowFlagLine: boolean = false;
188  private isOverBorder: boolean = false;
189  private canShowBottomFlagLine: boolean = false;
190  private isHighLight: boolean = false;
191  private flagLineLeftMargin: number;
192  private isModify: boolean = false;
193
194  constructor(node: TreeView.NodeItem) {
195    this.childNodeInfo = node.getChildNodeInfo();
196    this.nodeItem = { imageNode: null, inputText: null, mainTitleNode: null, imageCollapse: null };
197    this.popUpInfo = { popUpIsShow: false,
198      popUpEnableArrow: false,
199      popUpColor: null,
200      popUpText: '',
201      popUpTextColor: null };
202    this.nodeItem.imageNode = node.getNodeItem().imageNode;
203    this.nodeItem.inputText = new TreeView.InputText();
204    this.nodeItem.mainTitleNode = node.getNodeItem().mainTitleNode;
205    this.nodeItem.imageCollapse = node.getNodeItem().imageCollapse;
206    this.container = node.container;
207    this.parentNodeId = node.parentNodeId;
208    this.currentNodeId = node.currentNodeId;
209    this.nodeHeight = NODE_HEIGHT;
210    this.nodeLevel = node.nodeLevel;
211    this.nodeLeftPadding = node.nodeLevel * 12 + 8; // calculate left padding
212    this.nodeColor = $r('sys.color.ohos_id_color_background');
213    this.nodeIsShow = (this.nodeLevel > 0) ? false : true;
214    this.listItemHeight =  (this.nodeLevel > 0) ? LIST_ITEM_HEIGHT_NONE : LIST_ITEM_HEIGHT;
215    this.isShowTitle = true;
216    this.isShowInputText = false;
217    this.isSelected = false;
218    this.status = { normal: $r('sys.color.ohos_id_color_background_transparent'),
219      hover: $r('sys.color.ohos_id_color_hover'),
220      press: $r('sys.color.ohos_id_color_click_effect'),
221      selected: COLOR_SELECT,
222      highLight: $r('sys.color.ohos_id_color_activated')
223    };
224    this.nodeBorder = { borderWidth: BORDER_WIDTH_NONE,
225      borderColor: $r('sys.color.ohos_id_color_focused_outline'),
226      borderRadius: $r('sys.float.ohos_id_corner_radius_clicked')
227    };
228    this.flagLineLeftMargin = node.nodeLevel * 12 + 8;
229    this.node = node;
230    this.nodeParam = node.data;
231  }
232
233  setFontColor(color: string | Resource) {
234    this.fontColor = color
235  }
236
237  getFontColor() {
238    return this.fontColor;
239  }
240
241  getPopUpInfo() {
242    return this.popUpInfo;
243  }
244
245  setPopUpIsShow(isShow: boolean) {
246    this.popUpInfo.popUpIsShow = isShow;
247  }
248
249  setPopUpEnableArrow(popUpEnableArrow: boolean) {
250    this.popUpInfo.popUpEnableArrow = popUpEnableArrow;
251  }
252
253  setPopUpColor(color: Resource) {
254    this.popUpInfo.popUpColor = color;
255  }
256
257  setPopUpText(text: string | Resource) {
258    this.popUpInfo.popUpText = text;
259  }
260
261  setPopUpTextColor(popUpTextColor: Resource) {
262    this.popUpInfo.popUpTextColor = popUpTextColor;
263  }
264
265  getIsShowTitle() {
266    return this.isShowTitle;
267  }
268
269  getIsShowInputText() {
270    return this.isShowInputText;
271  }
272
273  setTitleAndInputTextStatus(isModify: boolean) {
274    if (isModify) {
275      this.isShowTitle = false;
276      this.isShowInputText = true;
277    } else {
278      this.isShowTitle = true;
279      this.isShowInputText = false;
280    }
281  }
282
283  handleImageCollapseAfterAddNode(isAddImageCollapse: boolean) {
284    // listTree this node already has ImageCollapse.
285    if (isAddImageCollapse) {
286      this.nodeItem.imageCollapse = new TreeView.ImageNode(ARROW_DOWN, null, null,
287        $r('sys.float.ohos_id_alpha_content_tertiary'),
288        IMAGE_NODE_HEIGHT,
289        IMAGE_NODE_WIDTH);
290      this.nodeItem.imageCollapse.itemRightMargin = ($r('sys.float.ohos_id_text_paragraph_margin_xs'));
291    } else {
292      this.nodeItem.imageCollapse = null;
293    }
294  }
295
296  setNodeColor(nodeColor: Resource | string): void {
297    this.nodeColor = nodeColor;
298  }
299
300  getNodeColor(): Resource | string {
301    return this.nodeColor;
302  }
303
304  setListItemHeight(listItemHeight: number): void {
305    this.listItemHeight = listItemHeight;
306  }
307
308  getListItemHeight(): number {
309    return this.listItemHeight;
310  }
311
312  getNodeCurrentNodeId(): number {
313    return this.currentNodeId;
314  }
315
316  getNodeParentNodeId(): number {
317    return this.parentNodeId;
318  }
319
320  getNodeLeftPadding(): number {
321    return this.nodeLeftPadding;
322  }
323
324  getNodeHeight(): Resource | number {
325    return this.nodeHeight;
326  }
327
328  setNodeIsShow(nodeIsShow: boolean): void {
329    this.nodeIsShow = nodeIsShow;
330  }
331
332  getNodeIsShow(): boolean {
333    return this.nodeIsShow;
334  }
335
336  getNodeItem()  {
337    return this.nodeItem;
338  }
339
340  getNodeStatus() {
341    return this.status;
342  }
343
344  getNodeBorder() {
345    return this.nodeBorder;
346  }
347
348  setNodeBorder(isClearFocusStatus: boolean): void {
349    this.nodeBorder.borderWidth = isClearFocusStatus ?  this.borderWidth.has : this.borderWidth.none;
350  }
351
352  getChildNodeInfo() {
353    return this.childNodeInfo;
354  }
355
356  getCurrentNodeId() {
357    return this.currentNodeId;
358  }
359
360  getMenu() {
361    return this.container;
362  }
363
364  setIsSelected(isSelected: boolean) {
365    this.isSelected = isSelected;
366  }
367
368  getIsSelected() {
369    return this.isSelected;
370  }
371
372  /* To gain the information while to alter node. */
373  getNodeInfoData() {
374    return this.nodeParam;
375  }
376
377  /* To gain the tree Node(NodeItem) while to alter node. */
378  public getNodeInfoNode() {
379    return this.node;
380  }
381
382  public getIsFolder() {
383    return this.nodeParam.isFolder;
384  }
385
386  public setCanShowFlagLine(canShowFlagLine: boolean) {
387    this.canShowFlagLine = canShowFlagLine;
388  }
389
390  public getCanShowFlagLine(): boolean {
391    return this.canShowFlagLine;
392  }
393
394  public setFlagLineLeftMargin(currentNodeLevel: number) {
395    this.flagLineLeftMargin = currentNodeLevel * 12 + 8;  // calculate
396  }
397
398  public getFlagLineLeftMargin(): number {
399    return this.flagLineLeftMargin;
400  }
401
402  public getNodeLevel(): number {
403    return this.nodeLevel;
404  }
405
406  public setIsOverBorder(isOverBorder: boolean) {
407    this.isOverBorder = isOverBorder;
408  }
409
410  public getIsOverBorder() {
411    return this.isOverBorder;
412  }
413
414  public setCanShowBottomFlagLine(canShowBottomFlagLine: boolean) {
415    this.canShowBottomFlagLine = canShowBottomFlagLine;
416  }
417
418  public getCanShowBottomFlagLine() {
419    return this.canShowBottomFlagLine;
420  }
421
422  public setIsHighLight(isHighLight: boolean) {
423    this.isHighLight = isHighLight;
424  }
425
426  public getIsHighLight(): boolean {
427    return this.isHighLight;
428  }
429
430  public setIsModify(isModify: boolean) {
431    this.isModify = isModify;
432  }
433
434  public getIsModify(): boolean {
435    return this.isModify;
436  }
437
438}
439
440export namespace TreeView {
441
442  /*
443 * TreeListenType listen type.
444 *
445 * @since 10
446 */
447  export enum TreeListenType {
448    NODE_ADD = "NodeAdd",
449    NODE_DELETE = "NodeDelete",
450    NODE_MODIFY = "NodeModify",
451    NODE_MOVE = "NodeMove",
452    NODE_CLICK = 'NodeClick',
453  }
454
455  export class TreeListener {
456    _events = []
457
458    constructor() {
459    }
460
461    /*
462     * Event registration and processing.
463     *
464     * The event will not be destroyed after being processed.
465     *
466     * @param type Registered Events.
467     * @param callback Event callback.
468     * @since 10
469     */
470    public on(type: TreeListenType, callback: (callbackParam: CallbackParam) => void) {
471      if (Array.isArray(type)) {
472        for (let i = 0, l = type.length; i < l; i++) {
473          this.on(type[i], callback)
474        }
475      } else {
476        (this._events[type] || (this._events[type] = [])).push(callback)
477      }
478    }
479
480    /*
481     * Event registration and processing.
482     *
483     * After the event is processed once, it will be destroyed.
484     *
485     * @param type Registered Events.
486     * @param callback Event callback.
487     * @since 10
488     */
489    public once(type: TreeListenType, callback: (callbackParam: CallbackParam) => void) {
490      let _self = this;
491      function handler() {
492        _self.off(type, handler);
493        callback.apply(null, [type, callback]);
494      }
495
496      handler.callback = callback;
497      this.on(type, handler);
498    }
499
500    /*
501     * Destroy event.
502     *
503     * @param type Registered Events.
504     * @param callback Event callback.
505     * @since 10
506     */
507    public off(type: TreeListenType, callback: (callbackParam: CallbackParam) => void) {
508      if (type == null) {
509        this._events = [];
510      }
511      if (Array.isArray(type)) {
512        for (let i = 0, l = type.length; i < l; i++) {
513          this.off(type[i], callback)
514        }
515      }
516      const cbs = this._events[type];
517      if (!cbs) {
518        return;
519      }
520      if (callback == null) {
521        this._events[type] = null
522      }
523      let cb, i = cbs.length
524      while (i--) {
525        cb = cbs[i]
526        if (cb === callback || cb.callback === callback) {
527          cbs.splice(i, 1)
528          break
529        }
530      }
531    }
532
533    /*
534     * Triggers all callbacks of an event with parameters.
535     *
536     * @param event Registered Events.
537     * @param argument Parameters returned by the callback event.
538     * @since 10
539     */
540    public emit(event, argument: any[]) {
541      let _self = this
542      if (!this._events[event]) {
543        return
544      }
545      let cbs = [...this._events[event]];
546      if (cbs) {
547        for (let i = 0, l = cbs.length; i < l; i++) {
548          try {
549            cbs[i].apply(_self,argument)
550          } catch (e) {
551            new Error(e)
552          }
553        }
554      }
555    }
556  }
557
558  /*
559   * TreeListenerManager.
560   *
561   * @since 10
562   */
563  export class TreeListenerManager {
564    static readonly APP_KEY_EVENT_BUS = "app_key_event_bus";
565    private appEventBus: TreeListener;
566    private constructor() {
567      this.appEventBus = new TreeListener();
568    }
569
570    /*
571     * Obtains the EventBusManager object.
572     *
573     * @since 10
574     */
575    public static getInstance(): TreeListenerManager {
576      if (AppStorage.Get(this.APP_KEY_EVENT_BUS) == null) {
577        AppStorage.SetOrCreate(this.APP_KEY_EVENT_BUS, new TreeListenerManager())
578      }
579      return AppStorage.Get(this.APP_KEY_EVENT_BUS);
580    }
581
582    /*
583     * Obtains the EventBus object.
584     *
585     * @since 10
586     */
587    public getTreeListener(): TreeListener {
588      return this.appEventBus;
589    }
590  }
591
592  class BasicDataSource implements IDataSource {
593    private listeners: DataChangeListener[] = []
594
595    public totalCount(): number {
596      return 0
597    }
598    public getData(index: number): any {
599      return undefined
600    }
601
602    registerDataChangeListener(listener: DataChangeListener): void {
603      if (this.listeners.indexOf(listener) < 0) {
604        this.listeners.push(listener)
605      }
606    }
607    unregisterDataChangeListener(listener: DataChangeListener): void {
608      const pos = this.listeners.indexOf(listener);
609      if (pos >= 0) {
610        this.listeners.splice(pos, 1)
611      }
612    }
613
614    notifyDataReload(): void {
615      this.listeners.forEach(listener => {
616        listener.onDataReloaded()
617      })
618    }
619    notifyDataAdd(index: number): void {
620      this.listeners.forEach(listener => {
621        listener.onDataAdd(index)
622      })
623    }
624    notifyDataChange(index: number): void {
625      this.listeners.forEach(listener => {
626        listener.onDataChange(index)
627      })
628    }
629    notifyDataDelete(index: number): void {
630      this.listeners.forEach(listener => {
631        listener.onDataDelete(index)
632      })
633    }
634    notifyDataMove(from: number, to: number): void {
635      this.listeners.forEach(listener => {
636        listener.onDataMove(from, to)
637      })
638    }
639  }
640
641  export class NodeItem {
642    private nodeItem: { imageNode?: ImageNode,
643    mainTitleNode?: MainTitleNode,
644    fontColor?: Resource,
645    imageCollapse?: ImageNode};
646    private childNodeInfo: { isHasChildNode: boolean, childNum: number, allChildNum: number };
647    container: () => void;
648    nodeLevel: number;
649    parentNodeId: number;
650    currentNodeId: number;
651    children: Array<NodeItem>;
652    data: { isFolder?: boolean,
653    icon?: Resource,
654    selectedIcon?: Resource,
655    editIcon?: Resource,
656    primaryTitle?: string,
657    container?: () => void,
658    objectCount?: number | string }
659
660    constructor(data: { isFolder?: boolean,
661    icon?: Resource,
662    selectedIcon?: Resource,
663    editIcon?: Resource,
664    primaryTitle?: string,
665    container?: () => void,
666    objectCount?: number | string }) {
667      this.data = data;
668      this.nodeLevel = -1;
669      this.parentNodeId = -1;
670      this.nodeItem = { imageNode: null, mainTitleNode: null, imageCollapse: null };
671      this.childNodeInfo = { isHasChildNode: false, childNum: 0, allChildNum: 0 };
672      this.container = data.container;
673      if (data.icon) {
674        this.nodeItem.imageNode = new ImageNode(data.icon, data.selectedIcon, data.editIcon,
675          $r('sys.float.ohos_id_alpha_content_fourth'),
676          IMAGE_NODE_HEIGHT,
677          IMAGE_NODE_WIDTH);
678      }
679      if (data.primaryTitle) {
680        this.nodeItem.mainTitleNode = new MainTitleNode(data.primaryTitle);
681      }
682      this.children = [];
683    }
684
685    addImageCollapse(isHasChildNode: boolean) {
686      if (isHasChildNode) {
687        this.nodeItem.imageCollapse = new ImageNode(ARROW_RIGHT, null, null,
688          $r('sys.float.ohos_id_alpha_content_tertiary'),
689          IMAGE_NODE_HEIGHT,
690          IMAGE_NODE_WIDTH);
691        this.nodeItem.imageCollapse.itemRightMargin = ($r('sys.float.ohos_id_text_paragraph_margin_xs'));
692      } else {
693        this.nodeItem.imageCollapse = null;
694      }
695    }
696
697    getNodeItem() {
698      return this.nodeItem;
699    }
700
701    getChildNodeInfo() {
702      return this.childNodeInfo;
703    }
704
705    getMenu(): () => void {
706      return this.container;
707    }
708
709    getCurrentNodeId() {
710      return this.currentNodeId;
711    }
712
713    getIsFolder() {
714      return this.data.isFolder;
715    }
716  }
717
718  class NodeBaseInfo {
719    public rightMargin: Resource | number;
720    private width: number;
721    private height: number;
722    constructor() {
723    }
724
725    set itemWidth(width: number) {
726      this.width = width;
727    }
728
729    get itemWidth(): number {
730      return this.width;
731    }
732
733    set itemHeight(height: number) {
734      this.height = height;
735    }
736
737    get itemHeight(): number {
738      return this.height;
739    }
740
741    set itemRightMargin(rightMargin: Resource | number) {
742      this.rightMargin = rightMargin;
743    }
744
745    get itemRightMargin() {
746      return this.rightMargin;
747    }
748  }
749
750  export class ImageNode extends NodeBaseInfo {
751    private imageSource: Resource | string;
752    private imageNormalSource: Resource | string;
753    private imageSelectedSource: Resource | string;
754    private imageEditSource: Resource | string;
755    private imageOpacity: Resource;
756    private currentInteractionStatus: InteractionStatus;
757    private imageCollapseSource: Resource | string;
758    private imageCollapseDownSource: Resource | string;
759    private isImageCollapse: boolean;
760    private imageCollapseRightSource: Resource | string;
761    constructor(imageSource: Resource | string, itemSelectedIcon: Resource, itemEditIcon: Resource,
762                imageOpacity: Resource, itemWidth: number, itemHeight: number) {
763      super();
764      this.rightMargin = $r('sys.float.ohos_id_elements_margin_horizontal_m');
765      this.imageSource = imageSource;
766      this.imageNormalSource = imageSource;
767      if (itemSelectedIcon != null) {
768        this.imageSelectedSource = itemSelectedIcon;
769      } else {
770        this.imageSelectedSource = this.imageNormalSource;
771      }
772      if (itemEditIcon != null) {
773        this.imageEditSource = itemEditIcon;
774      } else {
775        this.imageEditSource = this.imageNormalSource;
776      }
777      this.imageOpacity = imageOpacity;
778      this.itemWidth = itemWidth;
779      this.itemHeight = itemHeight;
780      this.imageCollapseSource = imageSource;
781      this.imageCollapseDownSource = ARROW_DOWN;
782      this.imageCollapseRightSource = ARROW_RIGHT;
783      this.isImageCollapse = true;
784    }
785
786    get source() {
787      return this.imageSource;
788    }
789
790    get normalSource() {
791      return this.imageNormalSource;
792    }
793
794    get selectedSource() {
795      return this.imageSelectedSource;
796    }
797
798    get editSource() {
799      return this.imageEditSource;
800    }
801
802    get opacity() {
803      return this.imageOpacity;
804    }
805
806    get noOpacity() {
807      return 1;
808    }
809
810    get collapseSource() {
811      return this.imageCollapseSource;
812    }
813
814    get isCollapse() {
815      return this.isImageCollapse;
816    }
817
818    changeImageCollapseSource(nodeStatus: NodeStatus) {
819      if (nodeStatus == NodeStatus.Expand) {
820        this.imageCollapseSource = this.imageCollapseDownSource;
821      } else if (nodeStatus == NodeStatus.Collapse) {
822        this.imageCollapseSource = this.imageCollapseRightSource;
823      }
824    }
825
826    setImageCollapseSource(interactionStatus: InteractionStatus, nodeStatus: NodeStatus) {
827      if (interactionStatus === InteractionStatus.Edit || interactionStatus === InteractionStatus.DragInsert) {
828        this.imageCollapseDownSource = ARROW_DOWN_WITHE;
829        this.imageCollapseRightSource = ARROW_RIGHT_WITHE;
830        this.isImageCollapse = false;
831      } else if (interactionStatus === InteractionStatus.FinishEdit ||
832        interactionStatus === InteractionStatus.FinishDragInsert) {
833        this.imageCollapseDownSource = ARROW_DOWN
834        this.imageCollapseRightSource = ARROW_RIGHT
835        this.isImageCollapse = true;
836      }
837      this.imageCollapseSource = (nodeStatus == NodeStatus.Collapse) ?
838      this.imageCollapseRightSource : this.imageCollapseDownSource;
839    }
840
841    setImageSource(interactionStatus: InteractionStatus) {
842      switch (interactionStatus) {
843        case InteractionStatus.Normal:
844          this.imageSource = this.imageNormalSource;
845          this.currentInteractionStatus = interactionStatus;
846          break;
847        case InteractionStatus.Selected:
848          if (this.currentInteractionStatus !== InteractionStatus.Edit) {
849            this.imageSource = this.imageSelectedSource;
850            this.currentInteractionStatus = interactionStatus;
851          }
852          break;
853        case InteractionStatus.Edit:
854          this.imageSource = this.imageEditSource;
855          this.currentInteractionStatus = interactionStatus;
856          break;
857        case InteractionStatus.FinishEdit:
858          this.imageSource = this.imageSelectedSource;
859          this.currentInteractionStatus = interactionStatus;
860          break;
861        case InteractionStatus.DragInsert:
862          this.imageSource = this.imageEditSource;
863          this.currentInteractionStatus = interactionStatus;
864          break;
865        case InteractionStatus.FinishDragInsert:
866          this.imageSource = this.imageNormalSource;
867          this.currentInteractionStatus = interactionStatus;
868          break;
869        default:
870          break;
871      }
872    }
873  }
874
875  export class MainTitleNode extends NodeBaseInfo {
876    private mainTitleName: string;
877    mainTitleSetting: { fontColor: Resource, fontSize: Resource, fontWeight: FontWeight }
878    private showPopUpTimeout: number;
879    constructor(mainTitleName: string) {
880      super();
881      this.mainTitleName = mainTitleName;
882      this.itemWidth = ITEM_WIDTH;
883      this.itemHeight = ITEM_HEIGHT;
884      this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs');
885      this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary'),
886        fontSize: $r('sys.float.ohos_id_text_size_body1'),
887        fontWeight: FontWeight.Normal };
888      this.showPopUpTimeout = 0;
889    }
890    setMainTitleSelected(isSelected: boolean): void {
891      if (isSelected) {
892        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_text_primary_activated'),
893          fontSize: $r('sys.float.ohos_id_text_size_body1'),
894          fontWeight: FontWeight.Regular };
895      } else {
896        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary'),
897          fontSize: $r('sys.float.ohos_id_text_size_body1'),
898          fontWeight: FontWeight.Normal };
899      }
900    }
901    set title(text: string) {
902      this.mainTitleName = text;
903    }
904    get title(): string {
905      return this.mainTitleName;
906    }
907
908    set popUpTimeout(showPopUpTimeout: number) {
909      this.showPopUpTimeout = showPopUpTimeout;
910    }
911
912    get popUpTimeout() {
913      return this.showPopUpTimeout;
914    }
915
916    get color(): Resource {
917      return this.mainTitleSetting.fontColor;
918    }
919
920    get size(): Resource {
921      return this.mainTitleSetting.fontSize;
922    }
923
924    get weight(): FontWeight {
925      return this.mainTitleSetting.fontWeight;
926    }
927
928    setMainTitleHighLight(isHighLight: boolean): void {
929      if (isHighLight) {
930        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary_contrary'),
931          fontSize: $r('sys.float.ohos_id_text_size_body1'),
932          fontWeight: FontWeight.Regular };
933      } else {
934        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary'),
935          fontSize: $r('sys.float.ohos_id_text_size_body1'),
936          fontWeight: FontWeight.Normal };
937      }
938    }
939  }
940
941  export class InputText extends NodeBaseInfo {
942    private inputTextSetting: { fontColor: Resource, fontSize: Resource, fontWeight: FontWeight }
943    private status: { normal: Resource, hover: Resource, press: Resource };
944    private statusColor: Resource = $r('sys.color.ohos_id_color_background');
945    private editItemColor: Resource = $r('sys.color.ohos_id_color_emphasize');
946    private radius: Resource = $r('sys.float.ohos_id_corner_radius_default_xs')
947    constructor() {
948      super();
949      this.itemWidth = ITEM_WIDTH;
950      this.itemHeight = ITEM_HEIGHT_INPUT;
951      this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs');
952      this.inputTextSetting = {
953        fontColor: $r('sys.color.ohos_id_color_text_primary'),
954        fontSize: $r('sys.float.ohos_id_text_size_body1'),
955        fontWeight: FontWeight.Normal };
956    }
957
958    get color(): Resource {
959      return this.inputTextSetting.fontColor;
960    }
961
962    get size(): Resource {
963      return this.inputTextSetting.fontSize;
964    }
965
966    get weight(): FontWeight {
967      return this.inputTextSetting.fontWeight;
968    }
969
970    get borderRadius(): Resource {
971      return this.radius;
972    }
973
974    get backgroundColor() {
975      return this.statusColor;
976    }
977
978    get editColor() {
979      return this.editItemColor;
980    }
981
982    get textInputStatusColor() {
983      return this.status;
984    }
985  }
986
987  export class ListNodeUtils {
988    private _root: NodeItem;
989    public addNewNodeId: number;
990    private readonly MaxNodeLevel = 50;
991    private readonly MAX_CN_LENGTH: number = 254;
992    private readonly MAX_EN_LENGTH: number = 255;
993    private readonly INITIAL_INVALID_VALUE = -1;
994    constructor() {
995      this._root = new NodeItem({});
996      this._root.nodeLevel = -1;
997      this._root.parentNodeId = -1;
998      this._root.currentNodeId = -1;
999    }
1000
1001    getNewNodeId() {
1002      return this.addNewNodeId;
1003    }
1004
1005    traverseNodeDF(callback, root: NodeItem = this._root) {
1006      let stack = [], found = false;
1007      stack.unshift(root);
1008      let currentNode = stack.shift();
1009      while(!found && currentNode) {
1010        found = callback(currentNode) === true;
1011        if (!found) {
1012          stack.unshift(...currentNode.children);
1013          currentNode = stack.shift();
1014        }
1015      }
1016    }
1017
1018    traverseNodeBF(callback) {
1019      let queue = [];
1020      let found: boolean = false;
1021      queue.push(this._root);
1022      let currentNode: NodeItem = queue.shift();
1023      while(!found && currentNode) {
1024        try {
1025          found = callback(currentNode);
1026        } catch(err) {
1027          var e = err.name + " == " + err.message;
1028        }
1029        if (!found) {
1030          queue.push(...currentNode.children)
1031          currentNode = queue.shift();
1032        }
1033      }
1034    }
1035
1036    private contains(callback, traversal) {
1037      traversal.call(this, callback, true);
1038    }
1039
1040    private updateParentChildNum(parentNode: NodeItem, isAdd: boolean, count: number) {
1041      let parentNodeId: number = parentNode.parentNodeId;
1042      while(parentNodeId >= 0) {
1043        this.traverseNodeDF((node: NodeItem): boolean => {
1044          if (node.currentNodeId == parentNodeId) {
1045            node.getChildNodeInfo().allChildNum =
1046              isAdd ? node.getChildNodeInfo().allChildNum + count : node.getChildNodeInfo().allChildNum - count;
1047            parentNodeId = node.parentNodeId;
1048            return false;
1049          }
1050          return false;
1051        })
1052      }
1053    }
1054
1055    findParentNodeId(currentNodeId: number): number {
1056      let current = null,
1057        callback = function(node): boolean {
1058          if (node.currentNodeId == currentNodeId ) {
1059            current = node;
1060            return true;
1061          }
1062          return false;
1063        };
1064      this.contains(callback, this.traverseNodeBF);
1065      return current.parentNodeId;
1066    }
1067
1068    addNode(parentNodeId: number,
1069            currentNodeId: number,
1070            data: { isFolder?: boolean,
1071            icon?: Resource,
1072            selectedIcon?: Resource,
1073            editIcon?: Resource,
1074            primaryTitle?: string,
1075            secondaryTitle?: number | string,
1076            container?: () => void,
1077            }): ListNodeUtils {
1078      if (this._root === null) {
1079        this._root = new NodeItem({});
1080        this._root.nodeLevel = -1;
1081        this._root.parentNodeId = -1;
1082        this._root.currentNodeId = -1;
1083      }
1084
1085      let parent = null,
1086        callback = function(node): boolean {
1087          if (node.currentNodeId == parentNodeId ) {
1088            parent = node;
1089            return true;
1090          }
1091          return false;
1092        };
1093      this.contains(callback, this.traverseNodeBF);
1094      if (parent) {
1095        let currentNode: NodeItem = new NodeItem(data);
1096        if (parent.nodeLevel > this.MaxNodeLevel) {
1097          throw new Error('ListNodeUtils[addNode]: The level of the tree view cannot exceed 50.');
1098        }
1099        currentNode.nodeLevel = parent.nodeLevel + 1; // nodeLevel
1100        currentNode.parentNodeId = parentNodeId;
1101        currentNode.currentNodeId = currentNodeId;
1102        parent.children.push(currentNode);
1103        parent.getChildNodeInfo().isHasChildNode = true;
1104        parent.getChildNodeInfo().childNum = parent.children.length;
1105        parent.getChildNodeInfo().allChildNum += 1; // childNum
1106        parent.addImageCollapse(parent.getChildNodeInfo().isHasChildNode);
1107        this.updateParentChildNum(parent, true, 1);
1108        return this;
1109      } else {
1110        throw new Error('ListNodeUtils[addNode]: Parent node not found.');
1111      }
1112    }
1113
1114    findNodeIndex(children, currentNodeId: number) {
1115      let index = this.INITIAL_INVALID_VALUE;
1116      for (let i = 0, len = children.length; i < len; i++) {
1117        if (children[i].currentNodeId === currentNodeId) {
1118          index = i;
1119          break;
1120        }
1121      }
1122      return index;
1123    }
1124
1125    private freeNodeMemory(rootNode: NodeItem, removeNodeIdList: number[]) {
1126      let deleteNode: NodeItem[] = [];
1127      let callback = function(node): boolean {
1128        deleteNode.push(node);
1129        return false;
1130      };
1131      this.traverseNodeDF(callback, rootNode);
1132      deleteNode.forEach((value)=>{
1133        removeNodeIdList.push(value.currentNodeId);
1134        value = null;
1135      })
1136    }
1137
1138    removeNode(currentNodeId: number, parentNodeId: number, traversal: any) {
1139      let parent = null,
1140        callback = function(node): boolean {
1141          if (node.currentNodeId == parentNodeId) {
1142            parent = node;
1143            return true;
1144          }
1145          return false;
1146        };
1147      this.contains(callback, traversal);
1148
1149      if (parent) {
1150        let removeNodeIdList: number[] = [];
1151        let index = this.findNodeIndex(parent.children, currentNodeId);
1152        if (index < 0) {
1153          throw new Error('Node does not exist.');
1154        } else {
1155          var deleteNodeAllChildNum = parent.children[index].getChildNodeInfo().allChildNum + 1;
1156          this.freeNodeMemory(parent.children[index], removeNodeIdList);
1157          let node = parent.children.splice(index, 1);
1158          node = null;
1159          if (parent.children.length == 0) {
1160            parent.addImageCollapse(false);
1161          }
1162        }
1163        parent.getChildNodeInfo().childNum = parent.children.length;
1164        parent.getChildNodeInfo().allChildNum -= (deleteNodeAllChildNum);
1165        this.updateParentChildNum(parent, false, deleteNodeAllChildNum);
1166        return removeNodeIdList;
1167      } else {
1168        throw new Error('Parent does not exist.');
1169      }
1170    }
1171
1172    getNewNodeInfo(nodeId: number) {
1173      let parent = null,
1174        callback = function(node): boolean {
1175          if (node.currentNodeId == nodeId) {
1176            parent = node;
1177            return true;
1178          }
1179          return false;
1180        };
1181      this.contains(callback, this.traverseNodeBF);
1182      let newNodeInfo: { isFolder: boolean, icon: Resource, selectedIcon: Resource, editIcon: Resource, container: () => any, secondaryTitle: number | string } =
1183        { isFolder: true, icon: null, selectedIcon: null, editIcon: null, container: null, secondaryTitle: '' };
1184      if (parent) {
1185        if (parent.children.length === 0) {
1186          if (parent.getNodeItem().imageNode != null) {
1187            newNodeInfo.icon = parent.getNodeItem().imageNode.normalSource;
1188            newNodeInfo.selectedIcon = parent.getNodeItem().imageNode.selectedSource;
1189            newNodeInfo.editIcon = parent.getNodeItem().imageNode.editSource;
1190            newNodeInfo.container = parent.getMenu();
1191          } else {
1192            newNodeInfo.icon = null;
1193            newNodeInfo.selectedIcon = null;
1194            newNodeInfo.editIcon = null;
1195            newNodeInfo.container = parent.getMenu();
1196          }
1197        } else if (parent.children.length > 0) {
1198          if (parent.getNodeItem().imageNode != null) {
1199            newNodeInfo.icon = (parent.children[0].getNodeItem().imageNode != null) ?
1200            parent.children[0].getNodeItem().imageNode.normalSource : null;
1201            newNodeInfo.selectedIcon = (parent.children[0].getNodeItem().imageNode != null) ?
1202            parent.children[0].getNodeItem().imageNode.selectedSource : null;
1203            newNodeInfo.editIcon = (parent.children[0].getNodeItem().imageNode != null) ?
1204            parent.children[0].getNodeItem().imageNode.editSource : null;
1205            newNodeInfo.container = parent.children[0].getMenu();
1206          } else {
1207            newNodeInfo.icon = null;
1208            newNodeInfo.selectedIcon = null;
1209            newNodeInfo.editIcon = null;
1210            newNodeInfo.container = parent.children[0].getMenu();
1211          }
1212        }
1213      }
1214      return newNodeInfo;
1215    }
1216
1217    getClickChildId(nodeId: number) {
1218      let parent = null,
1219        callback = function(node): boolean {
1220          if (node.currentNodeId == nodeId) {
1221            parent = node;
1222            return true;
1223          }
1224          return false;
1225        };
1226      this.contains(callback, this.traverseNodeBF);
1227      if (parent) {
1228        if (parent.children.length === 0) {
1229          return [];
1230        } else if (parent.children.length > 0) {
1231          var nodeInfo: { itemId: number, itemIcon: Resource, itemTitle: string } =
1232            { itemId: null, itemIcon: null, itemTitle: null }
1233          var childrenNodeInfo: Array<number> = new Array(parent.children.length);
1234          for (let i = 0; i < childrenNodeInfo.length; i++) {
1235            childrenNodeInfo[i] = 0;
1236          }
1237          for (let i = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) {
1238            childrenNodeInfo[i] = parent.children[i].currentNodeId;
1239          }
1240          return childrenNodeInfo;
1241        }
1242      }
1243      return [];
1244    }
1245
1246    getClickNodeChildrenInfo(nodeId: number) {
1247      let parent = null,
1248        callback = function(node): boolean {
1249          if (node.currentNodeId == nodeId) {
1250            parent = node;
1251            return true;
1252          }
1253          return false;
1254        };
1255      this.contains(callback, this.traverseNodeBF);
1256      if (parent) {
1257        if (parent.children.length === 0) {
1258          return [];
1259        } else if (parent.children.length > 0) {
1260          var nodeInfo: { itemId: number, itemIcon: Resource, itemTitle: string } =
1261            { itemId: null, itemIcon: null, itemTitle: null }
1262          var childrenNodeInfo: Array<{ itemId: number, itemIcon: Resource, itemTitle: string, isFolder: boolean }> = new Array(parent.children.length);
1263          for (let i = 0; i < childrenNodeInfo.length; i++) {
1264            childrenNodeInfo[i] = { itemId: null, itemIcon: null, itemTitle: null, isFolder: null };
1265          }
1266          for (let i = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) {
1267            childrenNodeInfo[i].itemId = parent.children[i].currentNodeId;
1268            if (parent.children[i].getNodeItem().imageNode) {
1269              childrenNodeInfo[i].itemIcon = parent.children[i].getNodeItem().imageNode.source;
1270            }
1271            if (parent.children[i].getNodeItem().mainTitleNode) {
1272              childrenNodeInfo[i].itemTitle = parent.children[i].getNodeItem().mainTitleNode.title;
1273            }
1274            childrenNodeInfo[i].isFolder =  parent.children[i].getIsFolder();
1275          }
1276          return childrenNodeInfo;
1277        }
1278      }
1279      return [];
1280    }
1281
1282    public checkMainTitleIsValid(title: string) : boolean {
1283      let invalid = /[\\\/:*?"<>|]/;
1284      let invalidLength = /^[\u4e00-\u9fa5]+$/;
1285      if (invalid.test(title)) {
1286        return false;
1287      }
1288      if ((invalidLength.test(title) && title.length > this.MAX_CN_LENGTH) ||
1289        (!invalidLength.test(title) && title.length > this.MAX_EN_LENGTH)) {
1290        return false;
1291      }
1292      return true;
1293    }
1294
1295    /*
1296     * DFS: Depth first traversal in drag event.
1297     * @param callback
1298     */
1299    dragTraverseNodeDF(callback, root: NodeItem = this._root, listNode) {
1300      let stack = [], found = false;
1301      stack.unshift(root);
1302      let currentNode = stack.shift();
1303      while(!found && currentNode) {
1304        found = callback(currentNode, listNode) === true;
1305        if (!found) {
1306          stack.unshift(...currentNode.children);
1307          currentNode = stack.shift();
1308        }
1309      }
1310    }
1311
1312    /*
1313     * Add the first dragging node in dragging nodes
1314     * 1.the first dragging node needs to distinguish the position to insert
1315     */
1316    addDragNode(parentNodeId: number,
1317                currentNodeId: number,
1318                insertCurrentNodeId: number,
1319                isAfter: boolean,
1320                data: { isFolder?: boolean,
1321                icon?: Resource,
1322                selectedIcon?: Resource,
1323                editIcon?: Resource,
1324                primaryTitle?: string,
1325                container?: () => any,
1326                objectCount?: number }): ListNodeUtils {
1327
1328      if (this._root === null) {
1329        this._root = new NodeItem({});
1330        this._root.nodeLevel = this.INITIAL_INVALID_VALUE;
1331        this._root.parentNodeId = this.INITIAL_INVALID_VALUE;
1332        this._root.currentNodeId = this.INITIAL_INVALID_VALUE;
1333      }
1334
1335      let parent = null,
1336        callback = function(node): boolean {
1337          if (node.currentNodeId == parentNodeId ) {
1338            parent = node;
1339            return true;
1340          }
1341          return false;
1342        };
1343      this.contains(callback, this.traverseNodeBF);
1344      if (parent) {
1345        let currentNode: NodeItem = new NodeItem(data);
1346        if (parent.nodeLevel > this.MaxNodeLevel) {
1347          throw new Error('ListNodeUtils[addNode]: The level of the tree view cannot exceed 50.');
1348        }
1349        currentNode.nodeLevel = parent.nodeLevel + 1;
1350        currentNode.parentNodeId = parentNodeId;
1351        currentNode.currentNodeId = currentNodeId;
1352        let insertIndex: number = this.INITIAL_INVALID_VALUE;
1353        if (parent.children.length) {
1354          for (let i = 0; i < parent.children.length; i++) {
1355            if ( parent.children[i].getCurrentNodeId() == insertCurrentNodeId) {
1356              insertIndex = i;
1357              break;
1358            }
1359          }
1360          if (isAfter) {
1361            parent.children.splice(insertIndex + 1, 0, currentNode);
1362          } else {
1363            parent.children.splice(insertIndex, 0, currentNode);
1364          }
1365        } else {
1366          parent.children.push(currentNode);
1367        }
1368        parent.getChildNodeInfo().isHasChildNode = true;
1369        parent.getChildNodeInfo().childNum = parent.children.length;
1370        parent.getChildNodeInfo().allChildNum += 1;
1371        parent.addImageCollapse(parent.getChildNodeInfo().isHasChildNode);
1372        this.updateParentChildNum(parent, true, 1);
1373        return this;
1374      } else {
1375        throw new Error('ListNodeUtils[addNode]: Parent node not found.');
1376      }
1377    }
1378  }
1379
1380  export class ListNodeDataSource extends BasicDataSource {
1381    readonly ROOT_NODE_ID = -1;
1382    public listNodeUtils: ListNodeUtils = new ListNodeUtils();
1383    listNode: NodeInfo[] = [];
1384    private readonly INITIAL_INVALID_VALUE = -1;
1385    public lastIndex: number = -1; // record the last focused node.
1386    thisIndex: number = -1; // records clicked nodes in the current period.
1387    private modifyNodeIndex: number = -1; // records the nodes edited in the current period.
1388    modifyNodeId: number = -1
1389    private currentOperation: MenuOperation;
1390    private expandAndCollapseInfo = new Map();
1391    loadedNodeIdAndIndexMap = new Map(); // [currentNodeId, index]
1392    private isTouchDown: boolean = false;
1393    private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener();
1394
1395    /* parameter of the drag event. */
1396    private isInnerDrag: boolean = false; // Judge whether it is an internal drag event.
1397    private isDrag: boolean = false; // It is used to handle events(For example, prevent press events) during global drag.
1398    private draggingCurrentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the current ID of the dragged node.
1399    private draggingParentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the parent ID of the dragged node.
1400    private currentNodeInfo: NodeInfo = null; // To solve the problem of currentIndex missed in onDrop event.
1401    private listItemOpacity : number = 1; // It is used to set the opacity of the node when dragged.
1402    private lastPassIndex: number = this.INITIAL_INVALID_VALUE; // record the last passing node index in drag.
1403    private lastPassId: number = this.INITIAL_INVALID_VALUE; // record the last passing node Id in drag.
1404    private thisPassIndex: number = this.INITIAL_INVALID_VALUE; // record the current passing node in drag.
1405    private lastDelayExpandIndex: number = this.INITIAL_INVALID_VALUE; // record last passing node in delay expand event.
1406    private timeoutExpandId: number = this.INITIAL_INVALID_VALUE;
1407    private lastTimeoutExpandId: number = this.INITIAL_INVALID_VALUE;
1408    private clearTimeoutExpandId: number = this.INITIAL_INVALID_VALUE;
1409    private timeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
1410    private lastTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
1411    private clearTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
1412    private lastDelayHighLightIndex: number = this.INITIAL_INVALID_VALUE; // record last passing node in HighLight event.
1413    private lastDelayHighLightId: number = this.INITIAL_INVALID_VALUE; //record last passing node Id in HighLight event.
1414    private nodeIdAndSubtitleMap = new Map(); // [currentNodeId, subtitle]
1415    private flag: Flag = Flag.NONE;
1416    private selectedParentNodeId: number = this.INITIAL_INVALID_VALUE;
1417    private selectedParentNodeSubtitle: any = '';
1418    private insertNodeSubtitle: any = '';
1419    private currentFocusNodeId: number = this.INITIAL_INVALID_VALUE;
1420    private lastFocusNodeId: number = this.INITIAL_INVALID_VALUE;
1421    private addFocusNodeId: number = this.INITIAL_INVALID_VALUE;
1422
1423    readonly FLAG_LINE: { flagLineHeight: string,
1424    flagLineColor: Resource,
1425    xOffset: string,
1426    yTopOffset: string,
1427    yBottomOffset: string,
1428    yBasePlateOffset: string } = {
1429      flagLineHeight: FLAG_LINE_HEIGHT,
1430      flagLineColor: $r('sys.color.ohos_id_color_emphasize'),
1431      xOffset: X_OFF_SET,
1432      yTopOffset: Y_OFF_SET,
1433      yBottomOffset: Y_BOTTOM_OFF_SET,
1434      yBasePlateOffset: Y_BASE_PLATE_OFF_SET
1435    }
1436
1437    private readonly DRAG_POPUP: { floorConstraintSize: { minWidth: string, maxWidth: string },
1438    textConstraintSize: { minWidth1: string, maxWidth1: string,
1439    minWidth2: string, maxWidth2: string },
1440    padding: { left: string, right: string },
1441    backgroundColor: ResourceColor,
1442    height: string,
1443    shadow: { radius: Resource, color: ResourceColor, offsetX?: number, offsetY?: number },
1444    borderRadius : Resource,
1445    fontColor: Resource,
1446    fontSize: Resource,
1447    fontWeight: FontWeight
1448    imageOpacity: Resource } = {
1449      floorConstraintSize: { minWidth: FLOOR_MIN_WIDTH, maxWidth: FLOOR_MAX_WIDTH },
1450      textConstraintSize: { minWidth1: TEXT_MIN_WIDTH, maxWidth1: TEXT_MAX_WIDTH, minWidth2: MIN_WIDTH, maxWidth2: MAX_WIDTH },
1451      padding: { left: LEFT_PADDING, right: RIGHT_PADDING },
1452      backgroundColor: COLOR_IMAGE_EDIT,
1453      height: GRAG_POP_UP_HEIGHT,
1454      shadow: { radius: $r('sys.float.ohos_id_corner_radius_default_m'), color: SHADOW_COLOR, offsetX: 0, offsetY: SHADOW_OFFSETY },
1455      borderRadius: $r('sys.float.ohos_id_corner_radius_clicked'),
1456      fontColor: $r('sys.color.ohos_id_color_primary'),
1457      fontSize: $r('sys.float.ohos_id_text_size_body1'),
1458      fontWeight: FontWeight.Regular,
1459      imageOpacity: $r('sys.float.ohos_id_alpha_content_fourth')
1460    }
1461
1462    private readonly subTitle: { normalFontColor: Resource,
1463    highLightFontColor: Resource,
1464    fontSize: Resource,
1465    fontWeight: FontWeight,
1466    margin: { left: string, right: string } } = {
1467      normalFontColor: $r('sys.color.ohos_id_color_secondary'),
1468      highLightFontColor: $r('sys.color.ohos_id_color_primary_contrary'),
1469      fontSize: $r('sys.float.ohos_id_text_size_body2'),
1470      fontWeight: FontWeight.Regular,
1471      margin: { left: '4vp', right: '24' }
1472    }
1473
1474    public changeNodeColor(index: number, color: Resource | string): void {
1475      this.listNode[index].setNodeColor(color);
1476      this.listNode[index].setNodeBorder(false)
1477    }
1478
1479    private getNodeColor(index) {
1480      return this.listNode[index].getNodeColor();
1481    }
1482
1483    private handleFocusEffect(index: number, isClearFocusStatus: boolean) {
1484      if (this.listNode[index].getNodeIsShow()) {
1485        this.listNode[index].setNodeBorder(isClearFocusStatus);
1486      }
1487    }
1488
1489    public setImageSource(index: number, interactionStatus: InteractionStatus) {
1490      let nodeInfo: NodeInfo = this.listNode[index];
1491      nodeInfo.setIsSelected(interactionStatus === InteractionStatus.Selected ||
1492        interactionStatus === InteractionStatus.Edit || interactionStatus === InteractionStatus.FinishEdit);
1493      if (nodeInfo.getNodeItem().mainTitleNode != null && interactionStatus != InteractionStatus.DragInsert &&
1494        interactionStatus != InteractionStatus.FinishDragInsert) {
1495        nodeInfo.getNodeItem().mainTitleNode.setMainTitleSelected(interactionStatus === InteractionStatus.Selected ||
1496          interactionStatus === InteractionStatus.FinishEdit);
1497      }
1498      if (nodeInfo.getNodeItem().imageNode != null) {
1499        nodeInfo.getNodeItem().imageNode.setImageSource(interactionStatus);
1500      }
1501    }
1502
1503    private setImageCollapseSource(index: number, interactionStatus: InteractionStatus) {
1504      let nodeInfo: NodeInfo = this.listNode[index];
1505      if (nodeInfo.getNodeItem().imageCollapse != null) {
1506        nodeInfo.getNodeItem().imageCollapse.setImageCollapseSource(interactionStatus,
1507          this.expandAndCollapseInfo.get(nodeInfo.getCurrentNodeId()));
1508      }
1509    }
1510
1511    public clearLastIndexStatus() {
1512      if (this.lastIndex == -1 || this.lastIndex >= this.listNode.length) {
1513        return;
1514      }
1515      this.setImageSource(this.lastIndex, InteractionStatus.Normal);
1516      this.changeNodeColor(this.lastIndex, this.listNode[this.lastIndex].getNodeStatus().normal);
1517      this.handleFocusEffect(this.lastIndex, false);
1518      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[this.lastIndex].getCurrentNodeId()));
1519    }
1520
1521    private changeNodeStatus(clickIndex: number): void {
1522      let thisIndex: number = clickIndex;
1523      let tmp: NodeInfo[] = this.ListNode;
1524      let nodeId =  tmp[clickIndex].getCurrentNodeId();
1525      if (this.expandAndCollapseInfo.get(nodeId) == NodeStatus.Expand) {
1526        this.expandAndCollapseInfo.set(nodeId, NodeStatus.Collapse);
1527        tmp[thisIndex].getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Collapse);
1528      } else if (this.expandAndCollapseInfo.get(nodeId) == NodeStatus.Collapse) {
1529        this.expandAndCollapseInfo.set(nodeId, NodeStatus.Expand);
1530        tmp[thisIndex].getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Expand);
1531      }
1532    }
1533
1534    private handleExpandAndCollapse(clickIndex: number) {
1535      let thisIndex: number = clickIndex;
1536      let tmp: NodeInfo[] = this.ListNode;
1537      let nodeId = tmp[thisIndex].getCurrentNodeId();
1538      if (!this.expandAndCollapseInfo.has(nodeId)) {
1539        return;
1540      }
1541
1542      let rootNodeStatus: NodeStatus = this.expandAndCollapseInfo.get(nodeId);
1543      if (tmp[thisIndex].getChildNodeInfo().isHasChildNode && rootNodeStatus == NodeStatus.Collapse) {
1544        for(var i = 0; i < tmp[thisIndex].getChildNodeInfo().allChildNum; i++) {
1545          tmp[thisIndex + 1 + i].setNodeIsShow(false);
1546          tmp[thisIndex + 1 + i].setListItemHeight(LIST_ITEM_HEIGHT_NONE);
1547        }
1548        this.notifyDataReload();
1549        return;
1550      }
1551
1552      let childNum: number[] = new Array(tmp[thisIndex].getChildNodeInfo().childNum);
1553      childNum[0] = thisIndex + 1;
1554      let index = 1;
1555      while(index < tmp[thisIndex].getChildNodeInfo().childNum) {
1556        childNum[index] = childNum[index -1] + tmp[childNum[index - 1]].getChildNodeInfo().allChildNum + 1;
1557        index++;
1558      }
1559      if (rootNodeStatus == NodeStatus.Expand) {
1560        for(var i = 0; i < childNum.length; i++) {
1561          tmp[childNum[i]].setNodeIsShow(true);
1562          tmp[childNum[i]].setListItemHeight(LIST_ITEM_HEIGHT);
1563          let nodeId =  tmp[childNum[i]].getCurrentNodeId();
1564          if(this.expandAndCollapseInfo.get(nodeId) == NodeStatus.Expand) {
1565            this.handleExpandAndCollapse(childNum[i]);
1566          }
1567        }
1568      }
1569      childNum = null;
1570      this.notifyDataReload();
1571    }
1572
1573    public init(listNodeUtils: ListNodeUtils) {
1574      let index = 0;
1575      this.listNode = [];
1576      this.listNodeUtils = listNodeUtils;
1577      this.loadedNodeIdAndIndexMap.clear();
1578      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
1579        if (node.currentNodeId >= 0) {
1580          var nodeInfo: NodeInfo = new NodeInfo(node);
1581          this.listNode.push(nodeInfo);
1582          if (nodeInfo.getChildNodeInfo().isHasChildNode) {
1583            this.expandAndCollapseInfo.set(nodeInfo.getCurrentNodeId(), NodeStatus.Collapse);
1584          }
1585          if (nodeInfo.getNodeIsShow()) {
1586            this.loadedNodeIdAndIndexMap.set(nodeInfo.getCurrentNodeId(), index++);
1587          }
1588          if (nodeInfo.getIsFolder()) {
1589            this.nodeIdAndSubtitleMap.set(nodeInfo.getCurrentNodeId(),
1590              nodeInfo.getNodeInfoData().secondaryTitle || nodeInfo.getNodeInfoData().secondaryTitle == 0 ?
1591              nodeInfo.getNodeInfoData().secondaryTitle : '');
1592          }
1593        }
1594        return false;
1595      });
1596    }
1597
1598    private refreshRemoveNodeData(removeNodeIdList: number[], parentNodeInfo: NodeInfo) {
1599      let deleteIndexList: number[] = [];
1600      for (let i = 0; i < removeNodeIdList.length; i++) {
1601        for (let j = 0; j < this.listNode.length; j++) {
1602          if (this.listNode[j].getNodeCurrentNodeId() == removeNodeIdList[i]) {
1603            let currentNodeId = this.listNode[j].getNodeCurrentNodeId();
1604            if (this.loadedNodeIdAndIndexMap.has(currentNodeId)) {
1605              // this.listNode index to lazyForEach index.
1606              deleteIndexList.push(this.loadedNodeIdAndIndexMap.get(currentNodeId));
1607            }
1608            let deleteNode = this.listNode.splice(j, 1);
1609            deleteNode = null; // free memory
1610            if (this.expandAndCollapseInfo.has(removeNodeIdList[i])) {
1611              this.expandAndCollapseInfo.delete(removeNodeIdList[i]); // delete deleteNode expandAndCollapseInfo.
1612            }
1613            break;
1614          }
1615        }
1616      }
1617      deleteIndexList.forEach((value)=>{
1618        this.notifyDataDelete(value); // notifyDataDelete do not  update data.
1619        this.notifyDataChange(value); // call notifyDataChange to update data.
1620      })
1621      let index: number = 0;
1622      for (let i = 0; i < this.listNode.length; i++) {
1623        if (this.listNode[i].getNodeCurrentNodeId() == parentNodeInfo.getNodeCurrentNodeId()) {
1624          if (parentNodeInfo.getNodeItem().imageCollapse == null) {
1625            this.listNode[i].handleImageCollapseAfterAddNode(false);
1626            // delete deleteNode parentNode expandAndCollapseInfo.
1627            this.expandAndCollapseInfo.delete(parentNodeInfo.getNodeCurrentNodeId());
1628            this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[i].getNodeCurrentNodeId()));
1629          }
1630          break;
1631        }
1632      }
1633      let callbackParam: CallbackParam = {currentNodeId: parentNodeInfo.getNodeCurrentNodeId(), parentNodeId: parentNodeInfo.getNodeParentNodeId()};
1634      this.appEventBus.emit(TreeListenType.NODE_DELETE, [callbackParam]);
1635    }
1636
1637    private refreshAddNodeData(addNodeIdList: number[]) {
1638      var addNodeInfo: NodeInfo;
1639      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
1640        if (node.currentNodeId === addNodeIdList[0]) {
1641          addNodeInfo = new NodeInfo(node);
1642          return true;
1643        }
1644        return false;
1645      });
1646      addNodeInfo.setIsModify(true);
1647
1648      let index: number = 0;
1649      for (let i = 0; i < this.listNode.length; i++) {
1650        if (this.listNode[i].getNodeCurrentNodeId() == addNodeInfo.getNodeParentNodeId()) {
1651          index = i;
1652          if (this.listNode[i].getNodeItem().imageCollapse == null) {
1653            this.listNode[i].handleImageCollapseAfterAddNode(true);
1654            this.notifyDataChange(index);
1655          } else if (this.expandAndCollapseInfo.get(this.listNode[i].getNodeCurrentNodeId()) == NodeStatus.Collapse) {
1656            this.changeNodeStatus(index);
1657          }
1658          this.listNode.splice(i + 1, 0, addNodeInfo);
1659          this.listNode[i + 1].setTitleAndInputTextStatus(true); // false->true: realize inner Interaction.
1660          this.listNode[i + 1].setNodeIsShow(true);
1661          this.listNode[i + 1].setListItemHeight(LIST_ITEM_HEIGHT);
1662          this.setImageSource(i + 1, InteractionStatus.Edit); // Normal->Edit : realize inner Interaction.
1663          this.currentOperation = MenuOperation.ADD_NODE;
1664          this.notifyDataAdd(i + 1);
1665          this.notificationNodeInfo(i+1, this.currentOperation);
1666          break;
1667        }
1668      }
1669      this.modifyNodeIndex = index + 1;
1670      this.expandAndCollapseInfo.set(addNodeInfo.getNodeParentNodeId(), NodeStatus.Expand);
1671      this.handleExpandAndCollapse(index);
1672    }
1673
1674    public refreshData(listNodeUtils: ListNodeUtils, operation: MenuOperation,
1675                       parentNodeId: number, changeNodeIdList: number[]) {
1676      let parentNodeInfo: NodeInfo;
1677      this.listNodeUtils = listNodeUtils;
1678      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
1679        if (node.currentNodeId == parentNodeId) {
1680          parentNodeInfo = new NodeInfo(node);
1681          return true;
1682        }
1683        return false;
1684      });
1685
1686      if (operation === MenuOperation.REMOVE_NODE) {
1687        this.nodeIdAndSubtitleMap.set(parentNodeId, this.selectedParentNodeSubtitle);
1688        this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(parentNodeId));
1689        this.refreshRemoveNodeData(changeNodeIdList, parentNodeInfo);
1690      }
1691
1692      if (operation === MenuOperation.ADD_NODE) {
1693        this.addFocusNodeId = changeNodeIdList[0];
1694        this.nodeIdAndSubtitleMap.set(this.getClickNodeId(), this.selectedParentNodeSubtitle);
1695        this.nodeIdAndSubtitleMap.set(changeNodeIdList[0], this.insertNodeSubtitle);
1696        this.refreshAddNodeData(changeNodeIdList);
1697      }
1698    }
1699
1700    public setClickIndex(index: number) {
1701      this.thisIndex = index;
1702    }
1703
1704    public getClickNodeId(): number {
1705      if (this.thisIndex < 0 || this.thisIndex >= this.ListNode.length) {
1706        return  -1;
1707      }
1708      return this.ListNode[this.thisIndex].getCurrentNodeId();
1709    }
1710
1711    public expandAndCollapseNode(clickIndex: number) {
1712      this.changeNodeStatus(clickIndex);
1713      this.handleExpandAndCollapse(clickIndex)
1714    }
1715
1716    public getIsTouchDown(): boolean {
1717      return this.isTouchDown;
1718    }
1719
1720    public getLastIndex(): number {
1721      return this.lastIndex;
1722    }
1723
1724    public handleEventDrag(index: number) {
1725      this.setImageSource(index, InteractionStatus.Normal);
1726      this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal);
1727      this.handleFocusEffect(index, false);
1728      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[index].getCurrentNodeId()));
1729    }
1730
1731    public handleEvent(event: Event, index: number) {
1732      /* Return while the event is dragging event. */
1733      if (this.isDrag) {
1734        return;
1735      }
1736
1737      if (event === Event.TOUCH_DOWN || event === Event.TOUCH_UP || event === Event.MOUSE_BUTTON_RIGHT) {
1738        if (index != this.lastIndex) {
1739          this.clearLastIndexStatus();
1740        }
1741      }
1742
1743      let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(this.listNode[index].getCurrentNodeId());
1744      switch(event) {
1745        case Event.TOUCH_DOWN:
1746          this.isTouchDown = true;
1747          this.changeNodeColor(index, this.listNode[index].getNodeStatus().press);
1748          this.notifyDataChange(lazyForEachIndex);
1749          break;
1750        case Event.TOUCH_UP: {
1751          if (this.isInnerDrag) {
1752            this.isInnerDrag = false;
1753          }
1754          this.isTouchDown = false;
1755          let nodeInfo: NodeInfo = this.listNode[index];
1756          this.setImageSource(index, InteractionStatus.Selected);
1757          nodeInfo.setFontColor($r('sys.color.ohos_id_color_primary'))
1758          this.lastIndex = index;
1759          this.changeNodeColor(index, nodeInfo.getNodeStatus().selected);
1760          this.notifyDataChange(lazyForEachIndex);
1761          break;
1762        }
1763        case Event.HOVER:
1764          if (this.getNodeColor(index) != this.listNode[index].getNodeStatus().selected) {
1765            this.changeNodeColor(index, this.listNode[index].getNodeStatus().hover);
1766            this.notifyDataChange(lazyForEachIndex);
1767          }
1768          break;
1769        case Event.HOVER_OVER:
1770          if (this.getNodeColor(index) != this.listNode[index].getNodeStatus().selected) {
1771            this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal);
1772            this.notifyDataChange(lazyForEachIndex);
1773          }
1774          break;
1775        case Event.FOCUS:
1776          this.handleFocusEffect(index, true);
1777          this.notifyDataChange(lazyForEachIndex);
1778          break;
1779        case Event.BLUR:
1780          this.handleFocusEffect(index, false);
1781          this.notifyDataChange(lazyForEachIndex);
1782          break;
1783        case Event.MOUSE_BUTTON_RIGHT:
1784          this.lastIndex = index;
1785          this.finishEditing();
1786          break;
1787        case Event.DRAG:
1788          this.isTouchDown = false;
1789          let nodeInfo: NodeInfo = this.listNode[index];
1790          this.setImageSource(index, InteractionStatus.Selected);
1791          this.lastIndex = index;
1792          this.changeNodeColor(index, nodeInfo.getNodeStatus().selected);
1793          this.notifyDataChange(lazyForEachIndex);
1794          break;
1795        default:
1796          break;
1797      }
1798    }
1799
1800    private notificationNodeInfo(addNodeId: number, operation: MenuOperation) {
1801      if (operation === MenuOperation.MODIFY_NODE) {
1802        let modifyNodeInfo: NodeInfo = this.listNode[this.modifyNodeIndex];
1803        let backParamModify: CallbackParam = { currentNodeId: modifyNodeInfo.getNodeCurrentNodeId(),
1804          parentNodeId: modifyNodeInfo.getNodeParentNodeId() }
1805        this.appEventBus.emit(TreeListenType.NODE_MODIFY,
1806          [backParamModify]);
1807      } else if (operation === MenuOperation.ADD_NODE)  {
1808        let addNodeInfo: NodeInfo = this.listNode[addNodeId];
1809        let icon: Resource | string = (addNodeInfo.getNodeItem().imageNode != null) ?
1810        addNodeInfo.getNodeItem().imageNode.source : null;
1811        let selectedIcon: Resource | string = (addNodeInfo.getNodeItem().imageNode != null) ?
1812        addNodeInfo.getNodeItem().imageNode.selectedSource : null;
1813        let editIcon: Resource | string = (addNodeInfo.getNodeItem().imageNode != null) ?
1814        addNodeInfo.getNodeItem().imageNode.editSource : null;
1815        let callbackParam: CallbackParam = { currentNodeId: addNodeInfo.getNodeCurrentNodeId(),
1816          parentNodeId: addNodeInfo.getNodeParentNodeId() }
1817        this.appEventBus.emit(TreeListenType.NODE_ADD,
1818          [callbackParam]);
1819      }
1820    }
1821
1822    public finishEditing() {
1823      if (this.modifyNodeIndex!= -1) {
1824        this.setImageSource(this.modifyNodeIndex, InteractionStatus.FinishEdit);
1825        this.setImageCollapseSource(this.modifyNodeIndex, InteractionStatus.FinishEdit);
1826        this.listNode[this.modifyNodeIndex].setIsModify(false);
1827        this.listNode[this.modifyNodeIndex].setTitleAndInputTextStatus(false);
1828        this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation);
1829        this.notifyDataChange(this.modifyNodeIndex);
1830      }
1831    }
1832
1833    public setItemVisibilityOnEdit(nodeId: number, operation: MenuOperation) {
1834      let index: number = -1;
1835      if (nodeId == -1) {
1836        return;
1837      }
1838      if (operation === MenuOperation.MODIFY_NODE) {
1839        for (let i = 0; i < this.listNode.length; i++) { // nodeId to find index
1840          if (this.listNode[i].getCurrentNodeId() == nodeId) {
1841            index = i;
1842            break;
1843          }
1844        }
1845        let nodeInfo: NodeInfo = this.listNode[index];
1846        nodeInfo.setIsModify(true);
1847        if (nodeInfo.getNodeItem().mainTitleNode === null) {
1848          return; // no title
1849        }
1850
1851        this.currentOperation = MenuOperation.MODIFY_NODE;
1852        nodeInfo.setTitleAndInputTextStatus(true);
1853        this.setImageSource(index, InteractionStatus.Edit);
1854        this.setImageCollapseSource(index, InteractionStatus.Edit);
1855        this.modifyNodeIndex = index;
1856        if (nodeInfo.getNodeItem().inputText) {
1857          if (nodeInfo.getNodeItem().imageCollapse != null) {
1858            nodeInfo.getNodeItem().inputText.rightMargin =
1859            $r('sys.float.ohos_id_text_paragraph_margin_xs')
1860          } else {
1861            nodeInfo.getNodeItem().inputText.rightMargin =
1862            $r('sys.float.ohos_id_elements_margin_horizontal_m')
1863          }
1864        }
1865        this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeId));
1866      }
1867      index = nodeId;
1868      if (operation === MenuOperation.COMMIT_NODE) {
1869        let nodeInfo: NodeInfo = this.listNode[index];
1870        nodeInfo.setTitleAndInputTextStatus(false);
1871        nodeInfo.setIsModify(false);
1872        this.setImageSource(index, InteractionStatus.FinishEdit);
1873        this.setImageCollapseSource(index, InteractionStatus.FinishEdit);
1874        this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation);
1875        this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeInfo.getCurrentNodeId()));
1876      }
1877    }
1878
1879    public setPopUpInfo(popUpType: PopUpType, inputError: InputError, isShow: boolean, index: number) {
1880      let nodeInfo: NodeInfo = this.listNode[index];
1881      nodeInfo.setPopUpIsShow(isShow);
1882      // this.listNode index to lazyForEach index.
1883      let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(nodeInfo.getCurrentNodeId());
1884      if (!isShow) {
1885        this.notifyDataChange(lazyForEachIndex);
1886        return;
1887      }
1888      if (popUpType === PopUpType.HINTS) {
1889        if (nodeInfo.getNodeItem().mainTitleNode != null) {
1890          nodeInfo.setPopUpText(nodeInfo.getNodeItem().mainTitleNode.title);
1891        } else {
1892          nodeInfo.setPopUpText('');
1893          nodeInfo.setPopUpIsShow(false);
1894        }
1895        nodeInfo.setPopUpEnableArrow(false);
1896        nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_background'));
1897        nodeInfo.setPopUpTextColor($r('sys.color.ohos_id_color_text_secondary'));
1898      } else if (popUpType === PopUpType.WARNINGS) {
1899        if (nodeInfo.getNodeItem().inputText != null) {
1900          if (inputError === InputError.INVALID_ERROR) {
1901            nodeInfo.setPopUpText("invalid error");
1902          } else if (inputError === InputError.LENGTH_ERROR) {
1903            nodeInfo.setPopUpText("length error");
1904          }
1905          nodeInfo.setPopUpEnableArrow(true);
1906          nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_help_tip_bg'));
1907          nodeInfo.setPopUpTextColor($r('sys.color.ohos_id_color_text_hint_contrary'));
1908        }
1909      }
1910      this.notifyDataChange(lazyForEachIndex);
1911    }
1912
1913    public setShowPopUpTimeout(timeout: number, index: number) {
1914      if (this.listNode[index].getNodeItem().mainTitleNode != null) {
1915        this.listNode[index].getNodeItem().mainTitleNode.popUpTimeout = timeout;
1916      }
1917      let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(this.listNode[index].getCurrentNodeId());
1918      this.notifyDataChange(lazyForEachIndex);
1919    }
1920
1921    public setMainTitleNameOnEdit(index: number, text: string) {
1922
1923      this.modifyNodeIndex = index;
1924      if (this.listNode[index].getNodeItem().mainTitleNode != null) {
1925        this.listNode[index].getNodeItem().mainTitleNode.title = text;
1926      }
1927    }
1928
1929    public get ListNode(): NodeInfo[] {
1930      return this.listNode;
1931    }
1932
1933    public totalCount(): number {
1934      let count: number = 0;
1935      let index: number = 0;
1936      this.loadedNodeIdAndIndexMap.clear();
1937      for (let i =0 ; i < this.listNode.length; i++) {
1938        if (this.listNode[i].getNodeIsShow()) {
1939          this.loadedNodeIdAndIndexMap.set(this.listNode[i].getCurrentNodeId(), index++);
1940          count++;
1941        }
1942      }
1943      return count;
1944    }
1945
1946    public getData(index: number): any {
1947      let count = 0;
1948      for (let i = 0; i < this.listNode.length; i++) {
1949        if (this.listNode[i].getNodeIsShow()) {
1950          if (index == count) {
1951            return this.listNode[i];
1952          }
1953          count++;
1954        }
1955      }
1956      return null;
1957    }
1958
1959    public addData(index: number, data: NodeInfo): void {
1960      this.listNode.splice(index, 0, data)
1961      this.notifyDataAdd(index)
1962    }
1963
1964    public pushData(data: NodeInfo): void {
1965      this.listNode.push(data)
1966      this.notifyDataAdd(this.listNode.length - 1)
1967    }
1968
1969    public setIsInnerDrag(isInnerDrag: boolean) {
1970      this.isInnerDrag = isInnerDrag;
1971    }
1972
1973    public getIsInnerDrag(): boolean {
1974      return this.isInnerDrag;
1975    }
1976
1977    public setIsDrag(isDrag: boolean) {
1978      this.isDrag = isDrag;
1979    }
1980
1981    public getIsDrag(): boolean {
1982      return this.isDrag;
1983    }
1984
1985    public setCurrentNodeInfo(currentNodeInfo: NodeInfo) {
1986      this.currentNodeInfo = currentNodeInfo;
1987    }
1988
1989    public getCurrentNodeInfo() {
1990      return this.currentNodeInfo;
1991    }
1992
1993    public setDraggingParentNodeId(draggingParentNodeId: number) {
1994      this.draggingParentNodeId = draggingParentNodeId;
1995    }
1996
1997    public getDraggingParentNodeId() {
1998      return this.draggingParentNodeId;
1999    }
2000
2001    public getDraggingCurrentNodeId() {
2002      return this.draggingCurrentNodeId;
2003    }
2004
2005    public setDraggingCurrentNodeId(draggingCurrentNodeId: number) {
2006      this.draggingCurrentNodeId = draggingCurrentNodeId;
2007    }
2008
2009    public setListItemOpacity(listItemOpacity: number) {
2010      this.listItemOpacity = listItemOpacity;
2011    }
2012
2013    public getListItemOpacity(item: NodeInfo) {
2014      return item.getCurrentNodeId() == this.getDraggingCurrentNodeId() ? this.listItemOpacity : 1;
2015    }
2016
2017    public getDragPopupPara() {
2018      return this.DRAG_POPUP;
2019    }
2020
2021    public setLastPassIndex(lastPassIndex: number) {
2022      this.lastPassIndex = lastPassIndex;
2023    }
2024
2025    public getLastPassIndex(): number {
2026      return this.lastPassIndex;
2027    }
2028
2029    public getIsParentOfInsertNode(insertNodeId: number): boolean {
2030      let selectedNodeItem: NodeItem = this.currentNodeInfo.getNodeInfoNode();
2031      let isParentNodeOfInsertNode = false,
2032        callback = function(node): boolean {
2033          if (node.currentNodeId == insertNodeId ) {
2034            isParentNodeOfInsertNode = true;
2035            return true;
2036          }
2037          return false;
2038        };
2039      this.listNodeUtils.traverseNodeDF(callback, selectedNodeItem);
2040      return isParentNodeOfInsertNode;
2041    }
2042
2043    public setPassIndex(thisPassIndex: number) {
2044      this.thisPassIndex = thisPassIndex;
2045    }
2046
2047    public getPassIndex(): number {
2048      return this.thisPassIndex;
2049    }
2050
2051    public clearTimeOutAboutDelayHighLightAndExpand(currentIndex: number) {
2052      if (this.lastPassId != this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId)) {
2053        let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId);
2054        let that =this;
2055        this.ListNode.forEach(function(value) {
2056          if (value.getNodeCurrentNodeId() == that.lastPassId) {
2057            value.setCanShowFlagLine(false);
2058          }
2059        })
2060        this.notifyDataChange(index);
2061      }
2062
2063      if ((this.lastTimeoutHighLightId != this.INITIAL_INVALID_VALUE &&
2064        this.clearTimeoutHighLightId != this.lastTimeoutHighLightId)) {
2065        clearTimeout(this.lastTimeoutHighLightId);
2066        if (this.lastDelayHighLightIndex != this.INITIAL_INVALID_VALUE) {
2067          this.clearHighLight(this.lastDelayHighLightIndex);
2068          let index: number = this.loadedNodeIdAndIndexMap
2069            .get(this.listNode[this.lastDelayHighLightIndex].getCurrentNodeId());
2070          this.notifyDataChange(index);
2071        }
2072        this.clearTimeoutHighLightId = this.lastTimeoutHighLightId;
2073      }
2074      this.lastTimeoutHighLightId = this.timeoutHighLightId;
2075      this.lastDelayHighLightIndex = currentIndex;
2076
2077      if ((this.lastTimeoutExpandId != this.INITIAL_INVALID_VALUE &&
2078        this.clearTimeoutExpandId != this.lastTimeoutExpandId)) {
2079        clearTimeout(this.lastTimeoutExpandId);
2080        this.clearTimeoutExpandId = this.lastTimeoutExpandId;
2081      }
2082      this.lastTimeoutExpandId = this.timeoutExpandId;
2083      this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
2084    }
2085
2086    public clearHighLight(currentIndex: number) {
2087      this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().normal);
2088      this.changeNodeHighLightColor(currentIndex, false);
2089      this.setImageSource(currentIndex, InteractionStatus.FinishDragInsert);
2090      this.setImageCollapseSource(currentIndex, InteractionStatus.FinishDragInsert);
2091      this.listNode[currentIndex].setIsHighLight(false);
2092    }
2093
2094    private changeNodeHighLightColor(index: number, isHighLight: boolean): void {
2095      if (this.listNode[index].getNodeItem().mainTitleNode && this.listNode[index].getIsShowTitle()) {
2096        this.listNode[index].getNodeItem().mainTitleNode.setMainTitleHighLight(isHighLight);
2097      }
2098    }
2099
2100    public setVisibility(flag: Flag, index: number, isOverBorder: boolean) {
2101      let isChanged: boolean = (this.thisPassIndex != index || this.flag != flag) ? true : false;
2102      this.thisPassIndex = index;
2103      if ((isChanged || isOverBorder) && this.isInnerDrag) {
2104        this.flag = flag;
2105        let currentNodeId: number = this.getData(index).getCurrentNodeId();
2106        let currentNodeLevel: number = this.expandAndCollapseInfo.get(currentNodeId) == NodeStatus.Expand &&
2107          this.flag == Flag.DOWN_FLAG ? this.getData(index).getNodeLevel() + 1 : this.getData(index).getNodeLevel();
2108        if (this.lastPassId != this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId)) {
2109          let lastIndex: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId);
2110          let that = this;
2111          this.ListNode.forEach(function (value) {
2112            if (value.getNodeCurrentNodeId() == that.lastPassId) {
2113              value.setCanShowFlagLine(false);
2114            }
2115          })
2116          this.notifyDataChange(lastIndex);
2117        }
2118        if (this.flag == Flag.DOWN_FLAG && index < this.totalCount() - 1) {
2119          this.getData(index).setCanShowFlagLine(false);
2120          this.getData(index+1).setCanShowFlagLine(true);
2121          this.getData(index).setCanShowBottomFlagLine(false);
2122          this.getData(index+1).setFlagLineLeftMargin(currentNodeLevel);
2123          this.notifyDataChange(index);
2124          this.notifyDataChange(index + 1);
2125          this.lastPassId = this.getData(index + 1).getNodeCurrentNodeId();
2126        } else if (this.flag == Flag.UP_FLAG && index < this.totalCount() - 1) {
2127          this.getData(index).setCanShowFlagLine(true);
2128          this.getData(index+1).setCanShowFlagLine(false);
2129          this.getData(index).setCanShowBottomFlagLine(false);
2130          this.getData(index).setFlagLineLeftMargin(currentNodeLevel);
2131          this.notifyDataChange(index);
2132          this.notifyDataChange(index + 1);
2133          this.lastPassId = this.getData(index).getNodeCurrentNodeId();
2134        } else if (index >= this.totalCount() - 1) {
2135          if (this.flag == Flag.DOWN_FLAG) {
2136            this.getData(index).setCanShowFlagLine(false);
2137            this.getData(index).setCanShowBottomFlagLine(true);
2138          } else {
2139            this.getData(index).setCanShowFlagLine(true);
2140            this.getData(index).setCanShowBottomFlagLine(false);
2141          }
2142          this.getData(index).setFlagLineLeftMargin(currentNodeLevel);
2143          this.notifyDataChange(index);
2144          this.lastPassId = this.getData(index).getNodeCurrentNodeId();
2145        }
2146      }
2147    }
2148
2149    public delayHighLightAndExpandNode(currentIndex: number, currentNodeId: number, showIndex: number) {
2150      let isChangIndex: boolean = currentIndex != this.lastDelayExpandIndex ? true : false;
2151      let isOverBorder: boolean = this.getData(showIndex).getIsOverBorder();
2152      if (isOverBorder) {
2153        this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
2154      } else {
2155        this.lastDelayExpandIndex = currentIndex;
2156      }
2157      if (isOverBorder || isChangIndex) {
2158        let that = this;
2159
2160        /* highLight node time-out. */
2161        let canDelayHighLight: boolean = !isOverBorder && (!this.isInnerDrag ||
2162          (this.expandAndCollapseInfo.get(currentNodeId) == NodeStatus.Collapse && this.isInnerDrag) ||
2163          (!this.expandAndCollapseInfo.has(currentNodeId) && this.listNode[currentIndex].getIsFolder()));
2164        if (canDelayHighLight) {
2165          /* set hoverState color before highLight. */
2166          this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().hover);
2167          this.notifyDataChange(showIndex);
2168
2169          let delayHighLightTime: number = this.isInnerDrag ? 1000 : 0; // ms
2170          this.timeoutHighLightId = setTimeout(function() {
2171            that.delayHighLight(currentIndex);
2172          }, delayHighLightTime)
2173        }
2174        if (isOverBorder || (this.lastTimeoutHighLightId != this.INITIAL_INVALID_VALUE &&
2175          this.clearTimeoutHighLightId != this.lastTimeoutHighLightId)) {
2176          clearTimeout(this.lastTimeoutHighLightId);
2177          if (this.lastDelayHighLightIndex != this.INITIAL_INVALID_VALUE) {
2178            this.clearHighLight(this.lastDelayHighLightIndex);
2179            this.notifyDataReload();
2180          }
2181          this.clearTimeoutHighLightId = this.lastTimeoutHighLightId;
2182        }
2183        this.lastTimeoutHighLightId = this.timeoutHighLightId;
2184        this.lastDelayHighLightIndex = currentIndex;
2185
2186        /* alter flagLine and expand node time-out. */
2187        if (!isOverBorder && this.expandAndCollapseInfo.get(currentNodeId) == NodeStatus.Collapse) {
2188          let firstChildNodeId: number = this.getData(showIndex).getNodeInfoNode().children[0].currentNodeId;
2189          let delayAlterFlagLineAndExpandNodeTime: number = 2000; // ms
2190          this.timeoutExpandId = setTimeout(function() {
2191            that.clearHighLight(that.lastDelayHighLightIndex);
2192            that.alterFlagLineAndExpandNode(currentIndex, firstChildNodeId);
2193          }, delayAlterFlagLineAndExpandNodeTime)
2194        }
2195        if (isOverBorder || (this.lastTimeoutExpandId != this.INITIAL_INVALID_VALUE &&
2196          this.clearTimeoutExpandId != this.lastTimeoutExpandId)) {
2197          clearTimeout(this.lastTimeoutExpandId);
2198          this.clearTimeoutExpandId = this.lastTimeoutExpandId;
2199        }
2200        this.lastTimeoutExpandId = this.timeoutExpandId;
2201      }
2202    }
2203
2204    public delayHighLight(currentIndex: number) {
2205      let that =this;
2206      this.ListNode.forEach(function (value) {
2207        if (value.getNodeCurrentNodeId() == that.lastPassId) {
2208          value.setCanShowFlagLine(false);
2209          value.setCanShowBottomFlagLine(false);
2210        }
2211      })
2212      this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().highLight);
2213      this.listNode[currentIndex].setIsHighLight(true);
2214      this.changeNodeHighLightColor(currentIndex, true);
2215      this.setImageSource(currentIndex, InteractionStatus.DragInsert);
2216      this.setImageCollapseSource(currentIndex, InteractionStatus.DragInsert);
2217      this.notifyDataReload();
2218    }
2219
2220    public alterFlagLineAndExpandNode(currentIndex: number, firstChildNodeId: number) {
2221      let that =this;
2222      this.ListNode.forEach(function (value) {
2223        if (value.getNodeCurrentNodeId() == that.lastPassId) {
2224          value.setCanShowFlagLine(false);
2225          value.setCanShowBottomFlagLine(false);
2226        }
2227      })
2228      this.ListNode.forEach(function (value) {
2229        if (that.isInnerDrag && value.getNodeCurrentNodeId() == firstChildNodeId) {
2230          value.setCanShowFlagLine(true);
2231        }
2232      })
2233      this.changeNodeStatus(currentIndex);
2234      this.handleExpandAndCollapse(currentIndex);
2235      this.lastPassId = firstChildNodeId;
2236    }
2237
2238    public hideLastLine() {
2239      if (this.lastPassId != this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId)) {
2240        let that = this;
2241        this.ListNode.forEach(function (value) {
2242          if (value.getNodeCurrentNodeId() == that.lastPassId) {
2243            value.setCanShowFlagLine(false);
2244            value.setCanShowBottomFlagLine(false);
2245          }
2246        })
2247        let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId);
2248        this.notifyDataChange(index);
2249      }
2250    }
2251
2252    public clearLastTimeoutHighLight() {
2253      if (this.lastTimeoutHighLightId != this.INITIAL_INVALID_VALUE &&
2254        this.clearTimeoutHighLightId != this.lastTimeoutHighLightId) {
2255        clearTimeout(this.lastTimeoutHighLightId);
2256        if (this.lastDelayHighLightIndex != this.INITIAL_INVALID_VALUE) {
2257          this.clearHighLight(this.lastDelayHighLightIndex);
2258        }
2259      }
2260    }
2261
2262    public clearLastTimeoutExpand() {
2263      if (this.lastTimeoutExpandId != this.INITIAL_INVALID_VALUE &&
2264        this.clearTimeoutExpandId != this.lastTimeoutExpandId) {
2265        clearTimeout(this.lastTimeoutExpandId);
2266      }
2267    }
2268
2269    public getSubtitle(currentNodeId: number): string {
2270      if (this.nodeIdAndSubtitleMap.has(currentNodeId)) {
2271        if (typeof this.nodeIdAndSubtitleMap.get(currentNodeId) == "number") {
2272          return this.nodeIdAndSubtitleMap.get(currentNodeId).toString();
2273        } else {
2274          return this.nodeIdAndSubtitleMap.get(currentNodeId)
2275        }
2276      } else {
2277        return '';
2278      }
2279    }
2280
2281    public hasSubtitle(currentNodeId: number) {
2282      return this.nodeIdAndSubtitleMap.has(currentNodeId);
2283    }
2284
2285    public initialParameterAboutDelayHighLightAndExpandIndex() {
2286      this.lastDelayHighLightIndex = this.INITIAL_INVALID_VALUE;
2287      this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
2288      this.lastPassIndex = this.INITIAL_INVALID_VALUE;
2289      this.draggingCurrentNodeId = this.INITIAL_INVALID_VALUE;
2290      this.flag = Flag.NONE;
2291    }
2292
2293    public refreshSubtitle(insertNodeCurrentNodeId: number) {
2294      this.nodeIdAndSubtitleMap.set(this.selectedParentNodeId, this.selectedParentNodeSubtitle);
2295      this.nodeIdAndSubtitleMap.set(insertNodeCurrentNodeId, this.insertNodeSubtitle);
2296      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.selectedParentNodeId));
2297      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(insertNodeCurrentNodeId));
2298    }
2299
2300    public setNodeSubtitlePara(
2301      selectedParentNodeId: number,
2302      selectedParentNodeSubtitle: ResourceStr,
2303      insertNodeSubtitle: ResourceStr
2304    ) {
2305      this.selectedParentNodeId = selectedParentNodeId;
2306      this.selectedParentNodeSubtitle = selectedParentNodeSubtitle;
2307      this.insertNodeSubtitle = insertNodeSubtitle;
2308    }
2309
2310    public getInsertNodeSubtitle() {
2311      return this.insertNodeSubtitle;
2312    }
2313
2314    public getExpandAndCollapseInfo(currentNodeId: number) {
2315      return this.expandAndCollapseInfo.get(currentNodeId);
2316    }
2317
2318    public getLastDelayHighLightId() {
2319      return this.lastDelayHighLightId;
2320    }
2321
2322    public setLastDelayHighLightId() {
2323      this.ListNode.forEach((value, index) => {
2324        if (index == this.lastDelayHighLightIndex) {
2325          this.lastDelayHighLightId = value.getCurrentNodeId();
2326        }
2327      })
2328    }
2329
2330    public setLastPassId(lastPassId: number) {
2331      this.lastPassId = lastPassId;
2332    }
2333
2334    public setLastDelayHighLightIndex(lastDelayHighLightIndex) {
2335      this.lastDelayHighLightIndex = lastDelayHighLightIndex;
2336    }
2337
2338    /*
2339     * Alter the current node location to a needful position.
2340     * 1.Create an array named 'dragNodeParam' to store dragging node information.
2341     * 2.Delete the dragging node from the tree.
2342     * 3.Add the dragging node to the tree.
2343     */
2344    public alterDragNode(rearParentNodeId: number, rearCurrentNodeId: number, insertNodeInfo: NodeInfo,
2345                         dragParentNodeId: number, dragCurrentNodeId: number, frontNodeInfoItem: NodeInfo) {
2346      let dragNodeParam: { parentId: number, currentId: number, data: any }[] = [];
2347      let parentNodeId: number = rearParentNodeId;
2348      let currentNodeId: number = dragCurrentNodeId;
2349      let nodeParam = frontNodeInfoItem.getNodeInfoData();
2350      let nodeInfo: NodeInfo = null;
2351      let nodeInfoNode: NodeItem = frontNodeInfoItem.getNodeInfoNode();
2352      let isHighLight : boolean = false;
2353      let insertChildIndex: number = this.INITIAL_INVALID_VALUE;
2354      let currentChildIndex: number = this.INITIAL_INVALID_VALUE;
2355      let isDownFlag: boolean = this.flag == Flag.DOWN_FLAG ? true : false;
2356
2357      currentChildIndex = this.getChildIndex(dragParentNodeId, dragCurrentNodeId);
2358
2359      insertChildIndex = this.getChildIndex(rearParentNodeId, rearCurrentNodeId) + 1;
2360
2361      if (rearParentNodeId != dragParentNodeId) {
2362        insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex;
2363      } else {
2364        if (insertChildIndex > currentChildIndex) {
2365          insertChildIndex = isDownFlag ? insertChildIndex : insertChildIndex - 1;
2366        } else {
2367          insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex;
2368        }
2369      }
2370
2371      for (let i = 0; i < this.listNode.length; i++) {
2372        if (this.listNode[i].getCurrentNodeId() == rearCurrentNodeId) {
2373          isHighLight = this.listNode[i].getIsHighLight();
2374          if (this.flag == Flag.DOWN_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) == NodeStatus.Expand) {
2375            parentNodeId = rearCurrentNodeId;
2376            insertChildIndex = 0;
2377          } else if (this.flag == Flag.UP_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) == NodeStatus.Expand
2378            && this.listNode[i].getCanShowFlagLine() == false) {
2379            parentNodeId = rearCurrentNodeId;
2380            insertChildIndex = 0;
2381          } else if (isHighLight) {
2382            parentNodeId = rearCurrentNodeId;
2383            insertChildIndex = 0;
2384          }
2385          break;
2386        }
2387      }
2388
2389      let callbackParam: CallbackParam = { currentNodeId: currentNodeId, parentNodeId: parentNodeId, childIndex: insertChildIndex }
2390
2391      /* export inner drag node Id. */
2392      this.appEventBus.emit(TreeListenType.NODE_MOVE, [callbackParam]);
2393
2394      /* To store dragging node information by the array named 'dragNodeParam'. */
2395      dragNodeParam.push({parentId: parentNodeId, currentId: currentNodeId, data: nodeParam});
2396
2397      let oneself = null,
2398        callback = function(node, listNode): boolean {
2399          if (node) {
2400            oneself = node;
2401            parentNodeId = oneself.parentNodeId;
2402            currentNodeId = oneself.currentNodeId;
2403            for (let i = 0; i < listNode.length; i++) {
2404              if (listNode[i].getNodeCurrentNodeId() == currentNodeId) {
2405                nodeInfo = listNode[i];
2406                break;
2407              }
2408            }
2409            nodeParam = nodeInfo.getNodeInfoData();
2410            if (parentNodeId != dragParentNodeId) {
2411              dragNodeParam.push({parentId: parentNodeId, currentId: currentNodeId, data: nodeParam});
2412            }
2413            return false;
2414          }
2415          return false;
2416        }
2417      this.listNodeUtils.dragTraverseNodeDF(callback, nodeInfoNode, this.listNode);
2418
2419      /* Delete the dragging node from the tree. */
2420      this.listNodeUtils.removeNode(dragCurrentNodeId, dragParentNodeId, this.listNodeUtils.traverseNodeBF);
2421
2422      /*
2423       * Add the dragging node to the tree
2424       * 1.The first dragging node is added singly, because it needs to distinguish the position to insert
2425       *
2426       * Add first node.
2427       */
2428      let insertCurrentNodeId: number = rearCurrentNodeId;
2429      let isAfter: boolean = isDownFlag;
2430      if (this.expandAndCollapseInfo.get(rearCurrentNodeId) == NodeStatus.Expand) {
2431        isAfter = false;
2432        this.listNode.forEach((value) => {
2433          if (value.getCurrentNodeId() == rearCurrentNodeId && value.getCanShowFlagLine() == false) {
2434            if (value.getNodeInfoNode().children.length) {
2435              insertCurrentNodeId = value.getNodeInfoNode().children[0].currentNodeId;
2436            } else {
2437              insertCurrentNodeId = this.INITIAL_INVALID_VALUE;
2438            }
2439          }
2440        })
2441      } else if (!this.expandAndCollapseInfo.get(rearCurrentNodeId) && isHighLight) {
2442        this.expandAndCollapseInfo.set(rearCurrentNodeId, NodeStatus.Expand);
2443      }
2444
2445      this.listNodeUtils.addDragNode(dragNodeParam[0].parentId, dragNodeParam[0].currentId, insertCurrentNodeId,
2446        isAfter, dragNodeParam[0].data);
2447
2448      /* Add remaining node. */
2449      for (let j = 1; j < dragNodeParam.length; j++) {
2450        this.listNodeUtils.addNode(dragNodeParam[j].parentId, dragNodeParam[j].currentId, dragNodeParam[j].data);
2451      }
2452
2453      /* Update node data and reload the array named 'listNode'. */
2454      for (let i = 0; i < this.listNode.length; i++) {
2455        if (this.listNode[i].getCurrentNodeId() == dragParentNodeId) {
2456          if (this.listNode[i].getNodeInfoNode().getNodeItem().imageCollapse == null) {
2457            this.listNode[i].handleImageCollapseAfterAddNode(false);
2458            this.expandAndCollapseInfo.delete(dragParentNodeId);
2459            break;
2460          }
2461        }
2462      }
2463      let tmp: NodeInfo[] = [...this.listNode];
2464      this.reloadListNode(this.listNodeUtils, tmp);
2465
2466    }
2467
2468    /*
2469     * Reload the array named 'listNode'
2470     * @param listNodeUtils
2471     * @param tmp
2472     */
2473    public reloadListNode(listNodeUtils: ListNodeUtils, tmp: NodeInfo[]) {
2474      let index = 0;
2475      this.listNode = [];
2476      this.listNodeUtils = listNodeUtils;
2477      this.loadedNodeIdAndIndexMap.clear();
2478      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
2479        if (node.currentNodeId >= 0) {
2480          var nodeInfo: NodeInfo = new NodeInfo(node);
2481          this.listNode.push(nodeInfo);
2482
2483          if (this.expandAndCollapseInfo.get(node.currentNodeId) == NodeStatus.Expand) {
2484            nodeInfo.getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Expand);
2485          } else if (this.expandAndCollapseInfo.get(node.currentNodeId) == NodeStatus.Collapse) {
2486            nodeInfo.getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Collapse);
2487          }
2488
2489          for (let i = 0; i < tmp.length; i++){
2490            if (tmp[i].getCurrentNodeId() == nodeInfo.getCurrentNodeId()) {
2491              nodeInfo.setNodeIsShow(tmp[i].getNodeIsShow());
2492              nodeInfo.setListItemHeight(tmp[i].getListItemHeight());
2493              if (nodeInfo.getNodeItem().mainTitleNode && nodeInfo.getIsShowTitle()) {
2494                nodeInfo.getNodeItem().mainTitleNode.title = tmp[i].getNodeItem().mainTitleNode.title;
2495              }
2496              break;
2497            }
2498          }
2499          if (nodeInfo.getNodeIsShow()) {
2500            this.loadedNodeIdAndIndexMap.set(nodeInfo.getCurrentNodeId(), index++);
2501          }
2502        }
2503        return false;
2504      });
2505    }
2506
2507    public getFlagLine() {
2508      return this.FLAG_LINE;
2509    }
2510
2511    public getVisibility(nodeInfo: NodeInfo): any {
2512      let lastShowIndex: number = this.loadedNodeIdAndIndexMap.get(nodeInfo.getCurrentNodeId()) - 1;
2513      if (lastShowIndex > this.INITIAL_INVALID_VALUE) {
2514        let lastNodeInfo: NodeInfo = this.getData(lastShowIndex);
2515        return (nodeInfo.getCanShowFlagLine() == true && !nodeInfo.getIsHighLight() && !lastNodeInfo.getIsHighLight()) ?
2516        Visibility.Visible : Visibility.Hidden;
2517      } else {
2518        return (nodeInfo.getCanShowFlagLine() == true && !nodeInfo.getIsHighLight()) ?
2519        Visibility.Visible : Visibility.Hidden;
2520      }
2521    }
2522
2523    public getSubTitlePara() {
2524      return this.subTitle;
2525    }
2526
2527    public getIsFolder(nodeId: number) {
2528      if (this.loadedNodeIdAndIndexMap.has(nodeId)) {
2529        return this.getData(this.loadedNodeIdAndIndexMap.get(nodeId)).getIsFolder();
2530      }
2531      return false;
2532    }
2533
2534    public getSubTitleFontColor(isHighLight: boolean) {
2535      return isHighLight ? this.subTitle.highLightFontColor : this.subTitle.normalFontColor;
2536    }
2537
2538    private getChildIndex(rearParentNodeId: number, rearCurrentNodeId: number) {
2539      let insertChildIndex: number = this.INITIAL_INVALID_VALUE;
2540      this.listNodeUtils.traverseNodeBF(function(node): boolean {
2541        if (node.getCurrentNodeId() == rearParentNodeId) {
2542          node.children.forEach((value, index) => {
2543            if (value.getCurrentNodeId() == rearCurrentNodeId) {
2544              insertChildIndex = index;
2545            }
2546          })
2547          return true;
2548        }
2549        return false;
2550      });
2551      return insertChildIndex;
2552    }
2553
2554    public setCurrentFocusNodeId(focusNodeId: number) {
2555      this.currentFocusNodeId = focusNodeId;
2556    }
2557
2558    public getCurrentFocusNodeId(): number {
2559      return this.currentFocusNodeId;
2560    }
2561
2562    public setLastFocusNodeId(focusNodeId: number) {
2563      this.lastFocusNodeId = focusNodeId;
2564    }
2565
2566    public getLastFocusNodeId(): number {
2567      return this.lastFocusNodeId;
2568    }
2569
2570    public getAddFocusNodeId(): number {
2571      return this.addFocusNodeId;
2572    }
2573
2574    public setFlag(flag: Flag) {
2575      this.flag = flag;
2576    }
2577  }
2578
2579  /**
2580   * Tree view control, which is created by using the TreeController class.
2581   *
2582   * When you create this component, you must initialize the listNodeDataSource.
2583   * You can run the listTreeViewWidth command to set the width of the component.
2584   * The default width is 200vp.
2585   *
2586   * @since 10
2587   */
2588  @Component
2589  export struct TreeView {
2590    @State nodeList: NodeInfo[] = [];
2591    listNodeDataSource: ListNodeDataSource;
2592    @State item: NodeInfo[] = null;
2593    treeController: TreeController;
2594    @State touchCount: number = 0;
2595    @State dropSelectedIndex: number = 0;
2596    @State viewLastIndex: number = -1;
2597    @State listItemBgColor: Resource = $r('sys.color.ohos_id_color_background_transparent')
2598    @BuilderParam private listTreeViewMenu: () => void  = null;
2599    private readonly MAX_CN_LENGTH: number = 254;
2600    private readonly MAX_EN_LENGTH: number = 255;
2601    private readonly INITIAL_INVALID_VALUE = -1;
2602    private readonly MAX_TOUCH_DOWN_COUNT = 0;
2603    private isMultiPress: boolean = false;
2604    private touchDownCount: number = this.INITIAL_INVALID_VALUE;
2605    private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener();
2606    private readonly itemPadding: { left: Resource, right: Resource, top: Resource, bottom: Resource } =
2607      { left: $r('sys.float.ohos_id_card_margin_start'),
2608        right: $r('sys.float.ohos_id_card_margin_end'),
2609        top: $r('sys.float.ohos_id_text_margin_vertical'),
2610        bottom: $r('sys.float.ohos_id_text_margin_vertical')};
2611    /* { left: '12vp', right: '12vp', top: '2vp', bottom: '2vp' } */
2612    private readonly textInputPadding:  { left : string, right: string, top: string, bottom: string } =
2613      { left : '0vp', right: '0vp', top: '0vp', bottom: '0vp'}
2614    aboutToAppear(): void  {
2615      this.listNodeDataSource = this.treeController.getListNodeDataSource();
2616      this.nodeList = this.treeController.getListNodeDataSource().listNode;
2617      this.item = this.treeController.getListNodeDataSource().listNode;
2618    }
2619
2620    private checkInvalidPattern(title: string): boolean {
2621      let pattern = /[\\\/:*?"<>|]/;
2622      return pattern.test(title)
2623    }
2624
2625    private checkIsAllCN(title: string): boolean {
2626      let pattern = /^[\u4e00-\u9fa5]+$/;
2627      return pattern.test(title)
2628    }
2629
2630    @Builder popupForShowTitle(text: string | Resource, backgroundColor: Resource, fontColor: Resource ) {
2631      Row() {
2632        Text(text).fontSize($r('sys.float.ohos_id_text_size_body2')).fontWeight('regular').fontColor(fontColor)
2633      }.backgroundColor(backgroundColor)
2634      .border({radius: $r('sys.float.ohos_id_elements_margin_horizontal_l')})
2635      .padding({left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
2636        right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
2637        top: $r('sys.float.ohos_id_card_margin_middle'),
2638        bottom: $r('sys.float.ohos_id_card_margin_middle')})
2639    }
2640
2641    @Builder builder() {
2642      this.listTreeViewMenu()
2643    }
2644
2645    /* Set the popup of dragging node. */
2646    @Builder draggingPopup(item: NodeInfo) {
2647      Row() {
2648        if (item.getNodeItem().imageNode) {
2649          Row() {
2650            Image(item.getNodeItem().imageNode.normalSource)
2651              .objectFit(ImageFit.Contain)
2652              .height(item.getNodeItem().imageNode.itemHeight)
2653              .width(item.getNodeItem().imageNode.itemWidth)
2654              .opacity(this.listNodeDataSource.getDragPopupPara().imageOpacity)
2655          }
2656          .backgroundColor(COLOR_IMAGE_ROW)
2657          .margin({ right: item.getNodeItem().imageNode.itemRightMargin })
2658          .height(item.getNodeItem().imageNode.itemHeight)
2659          .width(item.getNodeItem().imageNode.itemWidth)
2660        }
2661
2662        Row() {
2663          if (item.getNodeItem().mainTitleNode && item.getIsShowTitle()) {
2664            Text(item.getNodeItem().mainTitleNode.title)
2665              .maxLines(1)
2666              .fontSize(item.getNodeItem().mainTitleNode.size)
2667              .fontColor(this.listNodeDataSource.getDragPopupPara().fontColor)
2668              .fontWeight(this.listNodeDataSource.getDragPopupPara().fontWeight)
2669              .textOverflow({ overflow: TextOverflow.Ellipsis })
2670          }
2671        }
2672        .constraintSize({
2673          minWidth: item.getNodeItem().imageNode ?
2674          this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth1 :
2675          this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth2,
2676          maxWidth: item.getNodeItem().imageNode ?
2677          this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth1 :
2678          this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth2 })
2679      }
2680      .constraintSize({ minWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.minWidth,
2681        maxWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.maxWidth })
2682      .height(this.listNodeDataSource.getDragPopupPara().height)
2683      .backgroundColor(this.listNodeDataSource.getDragPopupPara().backgroundColor)
2684      .padding({ left: this.listNodeDataSource.getDragPopupPara().padding.left,
2685        right: this.listNodeDataSource.getDragPopupPara().padding.right })
2686      .shadow({ radius: $r('sys.float.ohos_id_corner_radius_default_m'),
2687        color: SHADOW_COLOR,
2688        offsetY: 0 })
2689      .borderRadius(this.listNodeDataSource.getDragPopupPara().borderRadius) // need to doubleCheck.
2690    }
2691
2692    clearLastIndexColor(){
2693      if (this.viewLastIndex == -1 || this.viewLastIndex >= this.nodeList.length) {
2694        return;
2695      }
2696      this.setImageSources(this.viewLastIndex, InteractionStatus.Normal);
2697
2698      this.nodeList[this.viewLastIndex].setNodeColor($r('sys.color.ohos_id_color_background_transparent'))
2699      this.nodeList[this.viewLastIndex].fontColor = $r('sys.color.ohos_id_color_primary');
2700      this.listNodeDataSource.listNode[this.viewLastIndex].setNodeColor($r('sys.color.ohos_id_color_background_transparent'));
2701      this.listNodeDataSource.listNode[this.viewLastIndex].fontColor = $r('sys.color.ohos_id_color_primary');
2702      this.listNodeDataSource.listNode[this.viewLastIndex].setIsSelected(false);
2703      this.listNodeDataSource.setImageSource(this.viewLastIndex, InteractionStatus.Normal);
2704    }
2705
2706    setImageSources(index: number, interactionStatus: InteractionStatus) {
2707      let nodeInfo: NodeInfo = this.nodeList[index];
2708      nodeInfo.setIsSelected(interactionStatus === InteractionStatus.Selected ||
2709        interactionStatus === InteractionStatus.Edit || interactionStatus === InteractionStatus.FinishEdit);
2710      if (nodeInfo.getNodeItem().mainTitleNode != null && interactionStatus != InteractionStatus.DragInsert &&
2711        interactionStatus != InteractionStatus.FinishDragInsert) {
2712        nodeInfo.getNodeItem().mainTitleNode.setMainTitleSelected(interactionStatus === InteractionStatus.Selected ||
2713          interactionStatus === InteractionStatus.FinishEdit);
2714      }
2715      if (nodeInfo.getNodeItem().imageNode != null) {
2716        nodeInfo.getNodeItem().imageNode.setImageSource(interactionStatus);
2717      }
2718    }
2719
2720    findIndex(currentNodeId: number): number {
2721      let thisIndex: number = 0;
2722      this.listNodeDataSource.ListNode.forEach(function (value, index) {
2723        if (value.getNodeCurrentNodeId() == currentNodeId) {
2724          thisIndex = index;
2725        }
2726      })
2727      return thisIndex;
2728    }
2729
2730    build() {
2731      List({ }) {
2732        LazyForEach(this.listNodeDataSource, (itemInner: NodeInfo) => {
2733          ListItem() {
2734            Row() {
2735              TreeViewInner({
2736                item: itemInner,
2737                listNodeDataSource: this.listNodeDataSource,
2738                index: this.findIndex(itemInner.getNodeCurrentNodeId()),
2739              })
2740            }
2741            .onTouch((event: TouchEvent) => {
2742              this.viewLastIndex = this.listNodeDataSource.getLastIndex();
2743              let index = this.findIndex(itemInner.getNodeCurrentNodeId())
2744
2745              if (event.type == TouchType.Down) {
2746                if (index != this.viewLastIndex) {
2747                  this.clearLastIndexColor()
2748                  this.listNodeDataSource.lastIndex = index;
2749                  this.listNodeDataSource.setClickIndex(index);
2750                }
2751              }
2752              if (event.type == TouchType.Up) {
2753                this.listNodeDataSource.listNode[index].setIsSelected(true);
2754                this.listNodeDataSource.setImageSource(index, InteractionStatus.Selected);
2755                if (this.listNodeDataSource.listNode[index].getNodeItem().imageNode != null) {
2756                  this.listNodeDataSource.listNode[index].imageSource = this.listNodeDataSource.listNode[index].getNodeItem().imageNode.source;
2757                }
2758
2759                if (index != this.viewLastIndex) {
2760                  this.clearLastIndexColor()
2761
2762                  this.listNodeDataSource.lastIndex = index;
2763                  this.listNodeDataSource.setClickIndex(index);
2764                }
2765                this.viewLastIndex = index;
2766              }
2767
2768              if (this.listNodeDataSource.getLastIndex() !== -1 && index !== this.listNodeDataSource.getLastIndex()) {
2769                this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, InputError.NONE, false, this.listNodeDataSource.getLastIndex());
2770                this.listNodeDataSource.setItemVisibilityOnEdit(this.listNodeDataSource.getLastIndex(), MenuOperation.COMMIT_NODE);
2771              }
2772            })
2773          }
2774          .width('100%').height(itemInner.getListItemHeight())
2775          .padding({ left: this.itemPadding.left, right: this.itemPadding.right})
2776          .align(Alignment.Start)
2777          .onDragStart((event: DragEvent, extraParams: string) => {
2778            if (this.listNodeDataSource.getIsDrag() || this.listNodeDataSource.getIsInnerDrag() || this.isMultiPress) {
2779              console.error('drag error, a item has been dragged');
2780              return;
2781            }
2782            this.dropSelectedIndex = JSON.parse(extraParams).selectedIndex;
2783            let currentNodeIndex = JSON.parse(extraParams).selectedIndex;
2784            let currentNodeInfo = this.listNodeDataSource.getData(currentNodeIndex);
2785            let currentItemNodeId = itemInner.getNodeCurrentNodeId();
2786            /* handle the situation of drag error, currentNodeIndex is not found in onDragStart. */
2787            if (currentNodeIndex >= this.listNodeDataSource.totalCount() || currentNodeIndex == undefined) {
2788              console.error('drag error, currentNodeIndex is not found in onDragStart');
2789              return;
2790            }
2791
2792            this.listNodeDataSource.setIsInnerDrag(true);
2793            this.listNodeDataSource.setIsDrag(true);
2794            this.listNodeDataSource.setCurrentNodeInfo(currentNodeInfo);
2795            this.listNodeDataSource.setDraggingCurrentNodeId(currentNodeInfo.getNodeCurrentNodeId());
2796            this.listNodeDataSource.setDraggingParentNodeId(currentNodeInfo.getNodeParentNodeId());
2797
2798            /* set the opacity of the dragging node. */
2799            let draggingNodeOpacity: number = DRAG_OPACITY;
2800            this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
2801            this.listNodeDataSource.notifyDataChange(currentNodeIndex);
2802
2803            /*
2804             * handle the situation of drag is too fast,it attribute a fault to OH.
2805             * OH has Solved on real machine.
2806             */
2807            if (currentItemNodeId != currentNodeInfo.getNodeCurrentNodeId()) {
2808              console.error('drag is too fast,it attribute a fault to OH');
2809              this.listNodeDataSource.setIsDrag(false);
2810              return;
2811            }
2812
2813            return this.draggingPopup(currentNodeInfo);
2814          })
2815        }, item => JSON.stringify(item))
2816      }
2817
2818      /* Move the dragged node. */
2819      .onDragMove((event: DragEvent) => {
2820        if (this.isMultiPress) {
2821          console.error('drag error, a item has been dragged');
2822          return;
2823        }
2824        let nodeHeight: number = LIST_ITEM_HEIGHT;
2825
2826        /* flag the position of the focus on the node. */
2827        let flag: Flag = Math.floor(event.getY() / (nodeHeight / FLAG_NUMBER)) % FLAG_NUMBER ? Flag.DOWN_FLAG : Flag.UP_FLAG;
2828
2829        /* Record the node position to which the dragged node moves. */
2830        let index = JSON.parse(extraParams).insertIndex;
2831
2832        /* Handle the situation where the focus(index) exceeds the list area. */
2833        let isOverBorder: boolean = false;
2834        if (index >= this.listNodeDataSource.totalCount()) {
2835          flag = Flag.DOWN_FLAG;
2836          index = this.listNodeDataSource.totalCount() - 1;
2837          this.listNodeDataSource.getData(index).setIsOverBorder(true);
2838          isOverBorder = true;
2839        } else {
2840          this.listNodeDataSource.getData(index).setIsOverBorder(false);
2841        }
2842
2843        let currentNodeInfo: NodeInfo = this.listNodeDataSource.getData(index);
2844        let currentNodeId: number = currentNodeInfo.getCurrentNodeId();
2845
2846        /*
2847         * handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId";
2848         * do not perform some functions.
2849         */
2850        if (index != this.listNodeDataSource.getLastPassIndex() && this.listNodeDataSource.getIsInnerDrag()) {
2851          let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(currentNodeId);
2852          if (isParentNodeOfInsertNode) {
2853            this.listNodeDataSource.setPassIndex(index);
2854            let that = this;
2855            this.listNodeDataSource.clearTimeOutAboutDelayHighLightAndExpand(findCurrentNodeIndex.call(that,
2856              currentNodeId));
2857            this.listNodeDataSource.setFlag(Flag.NONE);
2858            return;
2859          }
2860        }
2861        this.listNodeDataSource.setLastPassIndex(index);
2862
2863        /* Set the visibility of the flag line. */
2864        this.listNodeDataSource.setVisibility(flag, index - 1, isOverBorder);
2865
2866        /* Automatically HighLight one second delay and expand after two second delay. */
2867        if (currentNodeId != this.listNodeDataSource.getDraggingCurrentNodeId()) {
2868          let that = this;
2869          this.listNodeDataSource.delayHighLightAndExpandNode(findCurrentNodeIndex.call(that, currentNodeId),
2870            currentNodeId, index);
2871        }
2872      })
2873
2874      /* DragEvent Enter. */
2875      .onDragEnter((event: DragEvent, extraParams: string) => {
2876        if (this.listNodeDataSource.getIsInnerDrag()) {
2877          this.listNodeDataSource.setIsDrag(true);
2878
2879          /* set the opacity of the dragging node. */
2880          let draggingNodeOpacity: number = DRAG_OPACITY;
2881          this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
2882        }
2883      })
2884
2885      /* DragEvent Leave. */
2886      .onDragLeave((event: DragEvent, extraParams: string) => {
2887        this.listNodeDataSource.hideLastLine();
2888        this.listNodeDataSource.clearLastTimeoutHighLight();
2889        this.listNodeDataSource.clearLastTimeoutExpand();
2890        let draggingNodeOpacity: number = DRAG_OPACITY_NONE;
2891        this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
2892        this.listNodeDataSource.setIsDrag(false);
2893        this.listNodeDataSource.notifyDataReload();
2894      })
2895
2896      /* DragEvent Drop. */
2897      .onDrop((event: DragEvent, extraParams: string) => {
2898        this.listNodeDataSource.clearLastTimeoutExpand();
2899        let draggingNodeOpacity: number = DRAG_OPACITY_NONE;
2900        this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
2901        let insertNodeIndex: number = JSON.parse(extraParams).insertIndex
2902        let currentNodeIndex: number = this.dropSelectedIndex;
2903
2904        if (currentNodeIndex - 1 > this.listNodeDataSource.totalCount() || currentNodeIndex == undefined) {
2905          console.error('drag error, currentNodeIndex is not found');
2906          this.listNodeDataSource.setIsDrag(false);
2907          return;
2908        }
2909
2910        if (insertNodeIndex == this.listNodeDataSource.totalCount()) {
2911          console.log('need to insert into the position of the last line, now insertNodeIndex = insertNodeIndex - 1');
2912          insertNodeIndex -= 1;
2913        }
2914
2915        let insertNodeInfo: NodeInfo = this.listNodeDataSource.getData(insertNodeIndex);
2916        if (insertNodeInfo == null) {
2917          return;
2918        }
2919        let insertNodeCurrentNodeId: number = insertNodeInfo.getNodeCurrentNodeId();
2920
2921        /* outer node is move in. */
2922        if (!this.listNodeDataSource.getIsDrag() || !this.listNodeDataSource.getIsInnerDrag()) {
2923          this.listNodeDataSource.clearLastTimeoutHighLight();
2924          this.listNodeDataSource.setIsInnerDrag(false);
2925          this.listNodeDataSource.hideLastLine();
2926          this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
2927          this.listNodeDataSource.refreshSubtitle(insertNodeCurrentNodeId);
2928          this.listNodeDataSource.notifyDataReload();
2929          return;
2930        }
2931
2932        let currentNodeInfo: NodeInfo = this.listNodeDataSource.getCurrentNodeInfo();
2933        let insertNodeParentNodeId: number = insertNodeInfo.getNodeParentNodeId();
2934        let draggingCurrentNodeId: number = this.listNodeDataSource.getDraggingCurrentNodeId();
2935        let draggingParentNodeId: number = this.listNodeDataSource.getDraggingParentNodeId();
2936
2937        /*
2938         * handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId".
2939         * drag is fail.
2940         */
2941        let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(insertNodeCurrentNodeId);
2942        if (isParentNodeOfInsertNode) {
2943          this.listNodeDataSource.clearLastTimeoutHighLight();
2944          this.listNodeDataSource.setIsInnerDrag(false);
2945          this.listNodeDataSource.hideLastLine();
2946          this.listNodeDataSource.notifyDataChange(insertNodeIndex);
2947          this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
2948          this.listNodeDataSource.setIsDrag(false);
2949
2950          /* set the position of focus. */
2951          let that = this;
2952          let currentFocusIndex: number = findCurrentNodeIndex.call(that, draggingCurrentNodeId);
2953          this.listNodeDataSource.setClickIndex(currentFocusIndex);
2954          this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex);
2955          return;
2956        }
2957
2958        /* Collapse drag node. */
2959        if (this.listNodeDataSource.getExpandAndCollapseInfo(draggingCurrentNodeId) == NodeStatus.Expand) {
2960          let that = this;
2961          this.listNodeDataSource.expandAndCollapseNode(
2962            findCurrentNodeIndex.call(that, draggingCurrentNodeId));
2963        }
2964
2965        let flag: boolean = false;
2966
2967        /* Expand insert node. */
2968        if (this.listNodeDataSource.getExpandAndCollapseInfo(insertNodeCurrentNodeId) == NodeStatus.Collapse) {
2969          let that = this;
2970          let currentIndex: number = findCurrentNodeIndex.call(that, insertNodeCurrentNodeId);
2971          if (this.listNodeDataSource.ListNode[currentIndex].getIsHighLight()) {
2972            this.listNodeDataSource.expandAndCollapseNode(currentIndex);
2973          }
2974          flag = true;
2975        }
2976
2977        /* alter dragNode. */
2978        this.listNodeDataSource.setLastDelayHighLightId();
2979        if (draggingCurrentNodeId != insertNodeCurrentNodeId){
2980          this.listNodeDataSource.alterDragNode(insertNodeParentNodeId, insertNodeCurrentNodeId, insertNodeInfo,
2981            draggingParentNodeId, draggingCurrentNodeId, currentNodeInfo);
2982          this.listNodeDataSource.hideLastLine();
2983        } else {
2984          /*the position of dragNode is equal with the position of insertNode. */
2985          this.listNodeDataSource.hideLastLine();
2986          this.listNodeDataSource.setLastPassId(draggingCurrentNodeId);
2987          this.listNodeDataSource.hideLastLine();
2988        }
2989        let that = this;
2990        let lastDelayHighLightIndex: number = findCurrentNodeIndex.call(that,
2991          this.listNodeDataSource.getLastDelayHighLightId());
2992        this.listNodeDataSource.setLastDelayHighLightIndex(lastDelayHighLightIndex);
2993        this.listNodeDataSource.clearLastTimeoutHighLight();
2994        this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
2995        this.listNodeDataSource.setIsDrag(false);
2996
2997        /* set the position of focus. */
2998        let currentFocusIndex: number = findCurrentNodeIndex.call(that, draggingCurrentNodeId);
2999        this.listNodeDataSource.setClickIndex(currentFocusIndex);
3000        this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex);
3001
3002        /* innerDrag is over. */
3003        this.listNodeDataSource.setIsInnerDrag(false);
3004        this.listNodeDataSource.notifyDataReload();
3005
3006        this.listNodeDataSource.listNode[currentFocusIndex].fontColor = $r('sys.color.ohos_id_color_text_primary_activated');
3007        if (this.viewLastIndex !== -1 && currentNodeIndex != this.viewLastIndex) {
3008          this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem().mainTitleNode.setMainTitleSelected(false)
3009          this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem().mainTitleNode.setMainTitleHighLight(false)
3010        }
3011
3012        if (this.listNodeDataSource.listNode[this.viewLastIndex] != null) {
3013          this.listNodeDataSource.listNode[this.viewLastIndex].fontColor = $r('sys.color.ohos_id_color_text_primary');
3014        }
3015
3016        this.listNodeDataSource.lastIndex = this.viewLastIndex;
3017        if (this.listNodeDataSource.listNode[this.viewLastIndex]) {
3018          if (this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem().imageNode != null) {
3019
3020            this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem().imageNode.setImageSource(InteractionStatus.Normal);
3021            this.listNodeDataSource.listNode[this.viewLastIndex].imageSource = this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem().imageNode.source;
3022          }
3023        }
3024
3025        if(this.listNodeDataSource.listNode[this.viewLastIndex]) {
3026          this.listNodeDataSource.listNode[this.viewLastIndex].setNodeColor($r('sys.color.ohos_id_color_background_transparent'));
3027        }
3028
3029        this.viewLastIndex = currentFocusIndex;
3030      })
3031    }
3032  }
3033
3034  // Declare NodeParam
3035  export interface NodeParam {
3036    parentNodeId?: number,
3037    currentNodeId?: number,
3038    isFolder?: boolean,
3039    icon?: Resource,
3040    selectedIcon?: Resource,
3041    editIcon?: Resource,
3042    primaryTitle?: string,
3043    secondaryTitle?: number | string,
3044    container?: () => void,
3045  }
3046
3047  // Declare CallbackParam
3048  export interface CallbackParam {
3049    currentNodeId: number,
3050    parentNodeId?: number,
3051    childIndex?: number,
3052  }
3053
3054  /**
3055   * Create a tree view control proxy class to generate a tree view.
3056   *
3057   * @since 10
3058   */
3059  export class TreeController {
3060    readonly ROOT_NODE_ID: number = -1;
3061    private nodeIdList: number[] = [];
3062    private listNodeUtils : ListNodeUtils = new ListNodeUtils();
3063    private listNodeDataSource : ListNodeDataSource = new ListNodeDataSource();
3064
3065    /**
3066     * After the addNode interface is invoked,
3067     * this interface is used to obtain the initialization data of
3068     * the tree view component for creating a tree view component.
3069     *
3070     * @return ListNodeDataSource Obtains the initialization data of the tree view component.
3071     *
3072     * @since 10
3073     */
3074    public getListNodeDataSource(): ListNodeDataSource {
3075      return this.listNodeDataSource;
3076    }
3077
3078    /**
3079     * Obtains the subNode information of the currently clicked node.
3080     *
3081     * @return Array Returns an array that stores the configuration information of each node.
3082     * If there is no child node, an empty array is returned.
3083     *
3084     * @since 10
3085     */
3086    public getClickNodeChildrenInfo(): Array<{ itemId: number, itemIcon: Resource, itemTitle: string,
3087    isFolder: boolean }> {
3088      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3089      return this.listNodeUtils.getClickNodeChildrenInfo(clickNodeId);
3090    }
3091
3092    public getChildrenId(): Array<number> {
3093      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3094      return this.listNodeUtils.getClickChildId(clickNodeId);
3095    }
3096
3097    /**
3098     * Delete a node.
3099     *
3100     * Register an ON_ITEM_DELETE callback through the EventBus mechanism to obtain the IDs of all deleted nodes.
3101     *
3102     * @since 10
3103     */
3104    public removeNode(): void {
3105      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3106      if (clickNodeId < 0) {
3107        return;
3108      }
3109      let parentNodeId = this.listNodeUtils.findParentNodeId(clickNodeId);
3110      let removeNodeIdList: number[] = this.listNodeUtils.removeNode(clickNodeId,
3111        parentNodeId, this.listNodeUtils.traverseNodeBF);
3112      this.listNodeDataSource.refreshData(this.listNodeUtils, MenuOperation.REMOVE_NODE, parentNodeId, removeNodeIdList);
3113      this.nodeIdList.splice(this.nodeIdList.indexOf(clickNodeId), 1);
3114    }
3115
3116    /**
3117     * Modify the node name.
3118     *
3119     * Register an ON_ITEM_MODIFY callback to obtain the ID, parent node ID, and node name of the modified node.
3120     *
3121     * @since 10
3122     */
3123    public modifyNode(): void {
3124      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3125      this.listNodeDataSource.setItemVisibilityOnEdit(clickNodeId, MenuOperation.MODIFY_NODE);
3126    }
3127
3128    /**
3129     * Add a node.
3130     *
3131     * Icon of a new node, which is generated by the system by default.
3132     * If there is a same-level node, the icon of the first node of the same-level node is used.
3133     * If no icon is set for the first node of the same-level node, the new node does not have an icon.
3134     * If there is no peer node, the icon of the parent node is used.
3135     * If no icon is set for the parent node, the new node does not have an icon.
3136     * The system generates an ID for the new node and registers an ON_ITEM_ADD callback through
3137     * the EventBus mechanism to obtain the ID, parent node ID, node name, normal icon, selected icon,
3138     * edit icon of the new node.
3139     *
3140     * @since 10
3141     */
3142    public add(): void {
3143      let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
3144      if (clickNodeId == this.listNodeDataSource.ROOT_NODE_ID || !this.listNodeDataSource.getIsFolder(clickNodeId)) {
3145        return;
3146      }
3147      let newNodeInfo: { isFolder: boolean, icon: Resource, selectedIcon: Resource, editIcon: Resource,
3148      container: () =>any, secondaryTitle: number | string } =
3149        { isFolder: true, icon: null, selectedIcon: null, editIcon: null, container: null, secondaryTitle: '' };
3150      newNodeInfo = this.listNodeUtils.getNewNodeInfo(clickNodeId);
3151      this.nodeIdList.push(this.nodeIdList[this.nodeIdList.length - 1] + 1);
3152      let newNodeId: number = this.nodeIdList[this.nodeIdList.length - 1];
3153      this.listNodeUtils.addNewNodeId = newNodeId;
3154      this.listNodeUtils.addNode(clickNodeId, newNodeId,
3155        { isFolder: newNodeInfo.isFolder, icon: newNodeInfo.icon, selectedIcon: newNodeInfo.selectedIcon,
3156          editIcon: newNodeInfo.editIcon, primaryTitle: '新建文件夹', container: newNodeInfo.container,
3157          secondaryTitle: newNodeInfo.secondaryTitle });
3158      this.listNodeDataSource.refreshData(this.listNodeUtils, MenuOperation.ADD_NODE, clickNodeId, [newNodeId]);
3159
3160      this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, InputError.NONE, false, this.listNodeDataSource.getLastIndex());
3161      this.listNodeDataSource.setItemVisibilityOnEdit(this.listNodeDataSource.getLastIndex(), MenuOperation.COMMIT_NODE);
3162      this.listNodeDataSource.listNode[this.listNodeDataSource.getLastIndex()].setFontColor($r('sys.color.ohos_id_color_text_primary'));
3163
3164      let newNodeindex = findCurrentNodeIndex.call(this, newNodeId);
3165      this.listNodeDataSource.setClickIndex(newNodeindex);
3166      this.listNodeDataSource.handleEvent(Event.TOUCH_UP, newNodeindex);
3167    }
3168
3169    public addNodeParam(nodeParam: NodeParam): TreeController {
3170      if (nodeParam.primaryTitle != null &&
3171        !this.listNodeUtils.checkMainTitleIsValid(nodeParam.primaryTitle)) {
3172        throw new Error('ListTreeNode[addNode]: ' +
3173          'The directory name cannot contain the following characters\ /: *? "< > | or exceeds the maximum length.');
3174        return null;
3175      }
3176      if (nodeParam.primaryTitle == null && nodeParam.icon == null) {
3177        throw new Error('ListTreeNode[addNode]: ' +
3178          'The icon and directory name cannot be empty at the same time.');
3179        return null;
3180      }
3181      if (nodeParam.currentNodeId === this.ROOT_NODE_ID || nodeParam.currentNodeId === null) {
3182        throw new Error('ListTreeNode[addNode]: currentNodeId can not be -1 or null.');
3183        return null;
3184      }
3185      this.nodeIdList.push(nodeParam.currentNodeId);
3186      this.listNodeUtils.addNode(nodeParam.parentNodeId, nodeParam.currentNodeId, nodeParam);
3187      return this;
3188    }
3189
3190    /**
3191     * Initialize the interface of the tree view. This interface is used to generate ListNodeDataSource data.
3192     * addNode is only designed for initialization. It can only be invoked during initialization.
3193     *
3194     * A maximum of 50 directory levels can be added.
3195     *
3196     * @param parentNodeId ID of the parent node.
3197     * @param currentNodeId ID of the new node. The value cannot be -1 or null.
3198     * @param nodeParam Configuration information of the newly added node.
3199     *                  For details, see the comment description of NodeParam.
3200     * @return ListTreeNode Tree view component proxy class.
3201     *
3202     * @since 10
3203     */
3204    public addNode(nodeParam?: NodeParam): TreeController {
3205      if (nodeParam == null) {
3206        this.add();
3207      } else {
3208        if (nodeParam.primaryTitle != null &&
3209          !this.listNodeUtils.checkMainTitleIsValid(nodeParam.primaryTitle)) {
3210          throw new Error('ListTreeNode[addNode]: ' +
3211            'The directory name cannot contain the following characters\ /: *? "< > | or exceeds the maximum length.');
3212          return null;
3213        }
3214        if (nodeParam.primaryTitle == null && nodeParam.icon == null) {
3215          throw new Error('ListTreeNode[addNode]: ' +
3216            'The icon and directory name cannot be empty at the same time.');
3217          return null;
3218        }
3219        if (nodeParam.currentNodeId === this.ROOT_NODE_ID || nodeParam.currentNodeId === null) {
3220          throw new Error('ListTreeNode[addNode]: currentNodeId can not be -1 or null.');
3221          return null;
3222        }
3223        this.nodeIdList.push(nodeParam.currentNodeId);
3224        this.listNodeUtils.addNode(nodeParam.parentNodeId, nodeParam.currentNodeId, nodeParam);
3225        return this;
3226      }
3227    }
3228
3229    /**
3230     * After the initialization is complete by calling the addNode interface,
3231     * call this interface to complete initialization.
3232     *
3233     * This interface must be called when you finish initializing the ListTreeView by addNode.
3234     * @since 10
3235     */
3236    public buildDone() {
3237      this.listNodeDataSource.init(this.listNodeUtils);
3238      this.nodeIdList.sort((a, b) => a - b);
3239    }
3240
3241    public refreshNode(parentId: number, parentSubTitle: ResourceStr = '',
3242                       currentSubtitle: ResourceStr = '') {
3243      this.listNodeDataSource.setNodeSubtitlePara(parentId, parentSubTitle, currentSubtitle);
3244    }
3245  }
3246}
3247
3248@Component
3249export struct TreeViewInner {
3250  @ObjectLink item: NodeInfo;
3251  listNodeDataSource: TreeView.ListNodeDataSource;
3252  @State columnWidth: number = 0;
3253  @State isFocused: boolean = false;
3254  @State index: number = -1;
3255  @State lastIndex: number = -1;
3256  @State count: number = 0;
3257  @BuilderParam private listTreeViewMenu: () => void  = null;
3258  private readonly MAX_CN_LENGTH: number = 254;
3259  private readonly MAX_EN_LENGTH: number = 255;
3260  private readonly INITIAL_INVALID_VALUE = -1;
3261  private readonly MAX_TOUCH_DOWN_COUNT = 0;
3262  private isMultiPress: boolean = false;
3263  private touchDownCount: number = this.INITIAL_INVALID_VALUE;
3264  private appEventBus: TreeView.TreeListener = TreeView.TreeListenerManager.getInstance().getTreeListener();
3265  private readonly itemPadding: { left: Resource, right: Resource, top: Resource, bottom: string | Resource } =
3266    { left: $r('sys.float.ohos_id_card_margin_start'),
3267      right: $r('sys.float.ohos_id_card_margin_end'),
3268      top: $r('sys.float.ohos_id_text_margin_vertical'),
3269      bottom: '0vp' };
3270  /* { left: '12vp', right: '12vp', top: '2vp', bottom: '0vp' } */
3271  private readonly textInputPadding:  { left : string, right: string, top: string, bottom: string } =
3272    { left : '0vp', right: '0vp', top: '0vp', bottom: '0vp'}
3273
3274  aboutToAppear(): void  {
3275    if (this.item.getNodeItem().imageNode) {
3276      this.item.imageSource = this.item.getNodeItem().imageNode.source
3277    }
3278  }
3279
3280  private checkInvalidPattern(title: string): boolean {
3281    let pattern = /[\\\/:*?"<>|]/;
3282    return pattern.test(title)
3283  }
3284
3285  private checkIsAllCN(title: string): boolean {
3286    let pattern = /^[\u4e00-\u9fa5]+$/;
3287    return pattern.test(title)
3288  }
3289
3290  @Builder popupForShowTitle(text: string | Resource, backgroundColor: Resource, fontColor: Resource ) {
3291    Row() {
3292      Text(text).fontSize($r('sys.float.ohos_id_text_size_body2')).fontWeight('regular').fontColor(fontColor)
3293    }.backgroundColor(backgroundColor)
3294    .border({radius: $r('sys.float.ohos_id_elements_margin_horizontal_l')})
3295    .padding({left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
3296      right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
3297      top: $r('sys.float.ohos_id_card_margin_middle'),
3298      bottom: $r('sys.float.ohos_id_card_margin_middle')})
3299  }
3300
3301  @Builder builder() {
3302    if (this.listTreeViewMenu) {
3303      this.listTreeViewMenu()
3304    }
3305  }
3306
3307  build(){
3308    if (this.item.getNodeIsShow()) {
3309      Stack() {
3310        Column() {
3311          Stack({alignContent: Alignment.Bottom}) {
3312            Divider()
3313              .height(this.listNodeDataSource.getFlagLine().flagLineHeight)
3314              .color(this.listNodeDataSource.getFlagLine().flagLineColor)
3315              .visibility(this.listNodeDataSource.getVisibility(this.item))
3316              .lineCap(LineCapStyle.Round)
3317              .margin({ left: this.item.getFlagLineLeftMargin() })
3318              .focusable(true)
3319            Row({}) {
3320              Row({}) {
3321                if (this.item.getNodeItem().imageNode) {
3322                  Row() {
3323                    Image(this.item.imageSource)
3324                      .objectFit(ImageFit.Contain)
3325                      .height(this.item.getNodeItem().imageNode.itemHeight)
3326                      .width(this.item.getNodeItem().imageNode.itemWidth)
3327                      .opacity(!this.item.getIsSelected() && !this.item.getIsHighLight() ?
3328                      this.item.getNodeItem().imageNode.opacity : this.item.getNodeItem().imageNode.noOpacity)
3329                      .focusable(this.item.getNodeItem().mainTitleNode != null ? false : true)
3330                  }
3331                  .focusable(true)
3332                  .backgroundColor(COLOR_IMAGE_ROW)
3333                  .margin({ right: this.item.getNodeItem().imageNode.itemRightMargin })
3334                  .height(this.item.getNodeItem().imageNode.itemHeight)
3335                  .width(this.item.getNodeItem().imageNode.itemWidth)
3336                }
3337                Row() {
3338                  if (this.item.getNodeItem().mainTitleNode && this.item.getIsShowTitle()) {
3339                    Text(this.item.getNodeItem().mainTitleNode.title)
3340                      .maxLines(1) // max line
3341                      .fontSize(this.item.getNodeItem().mainTitleNode.size)
3342                      .fontColor(this.item.fontColor)
3343                      .margin({ right: this.item.getNodeItem().mainTitleNode.itemRightMargin })
3344                      .textOverflow({ overflow: TextOverflow.Ellipsis })
3345                      .fontWeight(this.item.getNodeItem().mainTitleNode.weight)
3346                      .focusable(true)
3347                  }
3348                  if (this.item.getNodeItem().mainTitleNode &&
3349                  this.item.getNodeItem().inputText &&
3350                  this.item.getIsShowInputText()) {
3351                    Row() {
3352                      TextInput({ text: this.item.getNodeItem().mainTitleNode.title })
3353                        .height(this.item.getNodeItem().inputText.itemHeight)
3354                        .fontSize(this.item.getNodeItem().inputText.size)
3355                        .fontColor(this.item.getNodeItem().inputText.color)
3356                        .borderRadius(this.item.getNodeItem().inputText.borderRadius)
3357                        .backgroundColor(this.item.getNodeItem().inputText.backgroundColor)
3358                        .enterKeyType(EnterKeyType.Done)
3359                        .focusable(true)
3360                        .padding({ left: this.textInputPadding.left, right: this.textInputPadding.right,
3361                          top: this.textInputPadding.top, bottom: this.textInputPadding.bottom })
3362                        .onChange((value: string) => {
3363                          let that = this;
3364                          var thisIndex = findCurrentNodeIndex.call(that, this.item.getNodeCurrentNodeId());
3365                          let res: string = '';
3366                          let isInvalidError: boolean = false;
3367                          let isLengthError: boolean = false;
3368                          if (that.checkInvalidPattern(value)) {
3369                            for (let i = 0; i < value.length; i++) {
3370                              if (!that.checkInvalidPattern(value[i])) {
3371                                res += value[i];
3372                              }
3373                            }
3374                            isInvalidError = true;
3375                            that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
3376                              InputError.INVALID_ERROR, true, thisIndex);
3377                          }
3378                          else {
3379                            res = value;
3380                            isInvalidError = false;
3381                          }
3382                          if ((that.checkIsAllCN(res) && res.length > this.MAX_CN_LENGTH) ||
3383                            (!that.checkIsAllCN(res) && res.length > this.MAX_EN_LENGTH)) {
3384                            res = that.checkIsAllCN(res) ?
3385                            res.substr(0, this.MAX_CN_LENGTH) : res.substr(0, this.MAX_EN_LENGTH);
3386                            isLengthError = true;
3387                            that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
3388                              InputError.LENGTH_ERROR, true, thisIndex);
3389                          }
3390                          else {
3391                            isLengthError = false;
3392                          }
3393                          if (!isLengthError && !isInvalidError) {
3394                            that.listNodeDataSource.setMainTitleNameOnEdit(thisIndex, res);
3395                          }
3396                        })
3397                        .onSubmit((enterKey: EnterKeyType) => {
3398                          let that = this;
3399                          var thisIndex = findCurrentNodeIndex.call(that, this.item.getNodeCurrentNodeId());
3400                          that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, InputError.NONE, false, thisIndex);
3401                          that.listNodeDataSource.setItemVisibilityOnEdit(thisIndex, MenuOperation.COMMIT_NODE);
3402                        })
3403                    }.backgroundColor(this.item.getNodeItem().inputText.backgroundColor)
3404                    .borderRadius(this.item.getNodeItem().inputText.borderRadius)
3405                    .margin({ right: this.item.getNodeItem().inputText.itemRightMargin })
3406                  }
3407                  Blank()
3408                }
3409                .layoutWeight(1)
3410                .focusable(true)
3411
3412                if (this.listNodeDataSource.hasSubtitle(this.item.getCurrentNodeId())) {
3413                  Row() {
3414                    Text(this.listNodeDataSource.getSubtitle(this.item.getCurrentNodeId()))
3415                      .fontSize(this.listNodeDataSource.getSubTitlePara().fontSize)
3416                      .fontColor(this.listNodeDataSource.getSubTitleFontColor(this.item.getIsHighLight() || this.item.getIsModify()))
3417                      .fontWeight(this.listNodeDataSource.getSubTitlePara().fontWeight)
3418                  }
3419                  .focusable(true)
3420                  .margin({ left: this.listNodeDataSource.getSubTitlePara().margin.left,
3421                    right: this.item.getNodeItem().imageCollapse ?
3422                      0 : this.listNodeDataSource.getSubTitlePara().margin.right })
3423                }
3424
3425                if (this.item.getNodeItem().imageCollapse) {
3426                  Row() {
3427                    Image(this.item.getNodeItem().imageCollapse.collapseSource)
3428                      .fillColor(this.item.getNodeItem().imageCollapse.isCollapse ? COLOR_IMAGE_ROW : COLOR_IMAGE_EDIT)
3429                      .align(Alignment.End)
3430                      .objectFit(ImageFit.Contain)
3431                      .height(this.item.getNodeItem().imageCollapse.itemHeight)
3432                      .width(this.item.getNodeItem().imageCollapse.itemWidth)
3433                      .opacity(!this.item.getIsHighLight() ?
3434                      this.item.getNodeItem().imageCollapse.opacity : this.item.getNodeItem().imageCollapse.noOpacity)
3435                      .onTouch((event: TouchEvent) => {
3436                        if (event.type === TouchType.Down) {
3437                          let that = this;
3438                          that.listNodeDataSource.expandAndCollapseNode(findCurrentNodeIndex.call(that, this.item.getNodeCurrentNodeId()));
3439                          this.listNodeDataSource.setCurrentFocusNodeId(this.item.getCurrentNodeId());
3440                        }
3441                        event.stopPropagation();
3442                      })
3443                      .focusable(true)
3444                  }
3445                  .focusable(true)
3446                  .height(this.item.getNodeItem().imageCollapse.itemHeight)
3447                  .width(this.item.getNodeItem().imageCollapse.itemWidth)
3448                }
3449              }
3450              .focusable(true)
3451              .width('100%')
3452              .gesture(
3453                TapGesture({ count: 2 }) // doubleClick
3454                  .onAction((event: GestureEvent) => {
3455                    let that = this;
3456                    that.listNodeDataSource.expandAndCollapseNode(
3457                      findCurrentNodeIndex.call(that, this.item.getNodeCurrentNodeId()));
3458                  })
3459              )
3460              .height(this.item.getNodeHeight())
3461              .padding({ left: this.item.getNodeLeftPadding() })
3462              .bindContextMenu(this.builder, ResponseType.RightClick)
3463            }.focusable(true)
3464          }.focusable(true)
3465        }
3466        .opacity(this.listNodeDataSource.getListItemOpacity(this.item))
3467        .onHover((isHover: boolean) => {
3468          if (isHover) {
3469            this.item.setNodeColor($r('sys.color.ohos_id_color_hover'))
3470          } else {
3471            this.item.setNodeColor($r('sys.color.ohos_id_color_background_transparent'))
3472          }
3473        })
3474        .onTouch((event) => {
3475          this.count++;
3476          if(this.count > 1) {
3477            this.count--;
3478            return;
3479          }
3480
3481          this.listNodeDataSource.setClickIndex(this.index);
3482          let currentId = this.item.getNodeCurrentNodeId();
3483
3484          if (event.type === TouchType.Down) {
3485            this.item.setNodeColor($r('sys.color.ohos_id_color_click_effect'));
3486          }
3487
3488          else if (event.type === TouchType.Up) {
3489            this.item.setNodeColor(COLOR_SELECT);
3490            this.item.fontColor = $r('sys.color.ohos_id_color_text_primary_activated');
3491            if (this.item.getNodeItem().imageNode != null) {
3492              this.item.getNodeItem().imageNode.setImageSource(InteractionStatus.Selected);
3493              this.listNodeDataSource.setImageSource(this.index, InteractionStatus.Selected);
3494              this.item.imageSource = this.item.getNodeItem().imageNode.source;
3495            }
3496
3497            this.item.getNodeItem().mainTitleNode.setMainTitleSelected(true)
3498            this.lastIndex = this.index;
3499
3500            let callbackParam = { currentNodeId: currentId }
3501            this.appEventBus.emit(TreeView.TreeListenType.NODE_CLICK, [callbackParam]);
3502          }
3503
3504          if (this.listNodeDataSource.getLastIndex() !== -1 && this.index !== this.listNodeDataSource.getLastIndex()) {
3505            this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, InputError.NONE, false, this.listNodeDataSource.getLastIndex());
3506            this.listNodeDataSource.setItemVisibilityOnEdit(this.listNodeDataSource.getLastIndex(), MenuOperation.COMMIT_NODE);
3507          }
3508          this.count--;
3509        })
3510        /* backgroundColor when editing and in other states. */
3511        .backgroundColor((this.item.getNodeItem().mainTitleNode && this.item.getNodeItem().inputText &&
3512        this.item.getIsShowInputText()) ? this.item.getNodeItem().inputText.editColor : this.item.getNodeColor())
3513        .border({
3514          width: this.item.getNodeBorder().borderWidth,
3515          color: this.item.getNodeBorder().borderColor,
3516          radius: this.item.getNodeBorder().borderRadius
3517        })
3518        .height(LIST_ITEM_HEIGHT)
3519        .focusable(true)
3520        .onMouse((event: MouseEvent) => {
3521          let that = this;
3522          let thisIndex = findCurrentNodeIndex.call(that, this.item.getNodeCurrentNodeId());
3523          if (event.button == MouseButton.Right) {
3524            that.listNodeDataSource.handleEvent(Event.MOUSE_BUTTON_RIGHT,
3525              findCurrentNodeIndex.call(that, this.item.getNodeCurrentNodeId()));
3526            this.listTreeViewMenu = this.item.getMenu();
3527            that.listNodeDataSource.setClickIndex(thisIndex);
3528            clearTimeout(this.item.getNodeItem().mainTitleNode.popUpTimeout);
3529          }
3530          event.stopPropagation();
3531        })
3532        .padding({ top: 0, bottom: 0 })
3533        .bindPopup(this.item.getPopUpInfo().popUpIsShow, {
3534          builder: this.popupForShowTitle(this.item.getPopUpInfo().popUpText, this.item.getPopUpInfo().popUpColor,
3535            this.item.getPopUpInfo().popUpTextColor),
3536          placement: Placement.BottomLeft,
3537          placementOnTop: false,
3538          popupColor: this.item.getPopUpInfo().popUpColor,
3539          autoCancel: true,
3540          enableArrow: this.item.getPopUpInfo().popUpEnableArrow
3541        })
3542        .onAreaChange((oldValue: Area, newValue: Area) => {
3543          let columnWidthNum = Number(parseInt(newValue.width.toString(), 0))
3544          this.columnWidth = columnWidthNum
3545        })
3546      }
3547      .stateStyles({
3548        focused: {
3549          .border({
3550            radius: $r('sys.float.ohos_id_corner_radius_clicked'),
3551            width: FLAG_NUMBER,
3552            color: $r('sys.color.ohos_id_color_focused_outline'),
3553            style: BorderStyle.Solid
3554          })
3555        },
3556        normal: {
3557          .border({
3558            radius: $r('sys.float.ohos_id_corner_radius_clicked'),
3559            width: 0 })
3560        }
3561      })
3562    }
3563  }
3564}