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