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