/**
* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import hilog from '@ohos.hilog';
import { Theme } from '@ohos.arkui.theme';
import { LengthMetrics } from '@ohos.arkui.node';
import { common } from '@kit.AbilityKit';
import resourceManager from '@ohos.resourceManager';
import { accessibility } from '@kit.AccessibilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
const IMAGE_NODE_HEIGHT: number = 24;
const IMAGE_NODE_WIDTH: number = 24;
const ITEM_WIDTH: number = 0;
const ITEM_HEIGHT: number = 48;
const ITEM_HEIGHT_INPUT: number = 32;
const BORDER_WIDTH_HAS: number = 2;
const BORDER_WIDTH_NONE: number = 0;
const NODE_HEIGHT: number = 48;
const LIST_ITEM_HEIGHT_NONE: number = 0;
const LIST_ITEM_HEIGHT: number = 48;
const FLAG_NUMBER: number = 2;
const DRAG_OPACITY: number = 0.4;
const DRAG_OPACITY_NONE: number = 1;
const MIN_FONT_SCALE: number = 1;
const MAX_FONT_SCALE: number = 2;
const FLAG_LINE_HEIGHT: string = '1.0vp';
const X_OFF_SET: string = '0vp';
const Y_OFF_SET: string = '2.75vp';
const Y_BOTTOM_OFF_SET: string = '-1.25vp';
const Y_BASE_PLATE_OFF_SET: string = '1.5vp';
const COLOR_IMAGE_EDIT: string = '#FFFFFF';
const COLOR_IMAGE_ROW: string = '#00000000';
const COLOR_SELECT: string = '#1A0A59F7';
const GRAG_POP_UP_HEIGHT: string = '48';
const FLOOR_MIN_WIDTH: string = '128vp';
const FLOOR_MAX_WIDTH: string = '208vp';
const TEXT_MIN_WIDTH: string = '80vp';
const TEXT_MAX_WIDTH: string = '160vp';
const MIN_WIDTH: string = '112vp';
const MAX_WIDTH: string = '192vp';
const TRANS_COLOR: string = '#00FFFFFF';
const DELAY_TIME: number = 100;
const LEVEL_MARGIN: number = 12;
const MARGIN_OFFSET: number = 8;
const TAG: string = 'TreeViewV2';
const LOG_CODE: number = 0x3900;
const ENTER_EXIT_DURATION: number = 2000;
const ACCESSIBILITY_REFOCUS_DELAY_TIME: number = 300;
const RESOURCE_TYPE_SYMBOL: number = 40000;
const MAX_SYMBOL_FONT_SCALE: number = 1.3;
const MIN_SYMBOL_FONT_SCALE: number = 1;
const ARROW_DOWN: Resource = $r('sys.symbol.chevron_down');
const ARROW_DOWN_WITHE: Resource = $r('sys.symbol.chevron_down');
const ARROW_RIGHT: Resource = $r('sys.symbol.chevron_right');
const ARROW_RIGHT_WITHE: Resource = $r('sys.symbol.chevron_right');
enum Event {
TOUCH_DOWN = 0,
TOUCH_UP = 1,
HOVER = 3,
HOVER_OVER = 4,
FOCUS = 5,
BLUR = 6,
MOUSE_BUTTON_RIGHT = 7,
DRAG = 8,
}
enum MenuOperation {
ADD_NODE = 0,
REMOVE_NODE = 1,
MODIFY_NODE = 2,
COMMIT_NODE = 3,
}
enum PopUpType {
HINTS = 0,
WARNINGS = 1,
}
enum InputError {
INVALID_ERROR = 0,
LENGTH_ERROR = 1,
NONE = 2,
}
enum Flag {
DOWN_FLAG = 0,
UP_FLAG = 1,
NONE = 2,
}
export enum NodeStatus {
EXPAND = 0,
COLLAPSE,
}
export enum InteractionStatus {
NORMAL = 0,
SELECTED,
EDIT,
FINISH_EDIT,
DRAG_INSERT,
FINISH_DRAG_INSERT,
}
enum CollapseImageType {
ARROW_DOWN = 0,
ARROW_RIGHT,
ARROW_DOWN_WHITE,
ARROW_RIGHT_WHITE,
}
enum AccessibilityNodeType {
TEXT = 0,
PLACE = 1,
LIFT = 2,
}
interface ChildNodeInfo {
isHasChildNode: boolean;
childNum: number;
allChildNum: number;
}
interface NodeItemView {
imageNode?: ImageNode;
inputText: InputText;
mainTitleNode: MainTitleNode;
imageCollapse?: CollapseImageNode;
fontColor?: ResourceColor;
}
interface Status {
normal: ResourceColor;
hover: ResourceColor;
press: ResourceColor;
selected: ResourceColor;
highLight?: ResourceColor;
}
interface NodeBorder {
borderWidth: Resource | number;
borderColor: ResourceColor;
borderRadius: Resource;
}
interface PopUpInfo {
popUpIsShow: boolean;
popUpEnableArrow: boolean;
popUpColor?: ResourceColor;
popUpText?: string | Resource;
popUpTextColor?: ResourceColor;
}
interface BorderWidth {
has: Resource | number;
none: Resource | number;
}
interface TextSetting {
fontColor: ResourceColor;
fontSize: Resource;
fontWeight: FontWeight;
}
interface NodeInfoView {
itemId?: number;
itemIcon?: Resource | string;
itemTitle?: ResourceStr;
isFolder?: boolean;
}
interface FloorConstraintSize {
minWidth: string;
maxWidth: string;
}
interface TextConstraintSize {
minWidth1: string;
maxWidth1: string;
minWidth2: string;
maxWidth2: string;
}
interface Padding {
left: Resource;
right: Resource;
}
interface ItemPadding {
left: Resource;
right: Resource;
top: Resource;
bottom: Resource;
}
interface Shadow {
radius: Resource;
color: Resource;
offsetX?: number;
offsetY?: number;
}
interface DragPopup {
floorConstraintSize: FloorConstraintSize;
textConstraintSize: TextConstraintSize;
padding: Padding;
backgroundColor: ResourceColor;
height: string;
shadow: Shadow;
borderRadius: Resource;
fontColor: ResourceColor;
fontSize: Resource;
fontWeight: FontWeight;
imageOpacity: Resource;
}
interface DragNodeParam {
parentId: number,
currentId: number,
data: NodeParamV2,
}
interface FlagLine {
flagLineHeight: string;
flagLineColor: Resource;
xOffset: string;
yTopOffset: string;
yBottomOffset: string;
yBasePlateOffset: string;
}
interface SubTitleStyle {
normalFontColor: ResourceColor;
highLightFontColor: ResourceColor;
fontSize: Resource;
fontWeight: FontWeight;
margin: Padding;
}
interface NodeItemViewFactory {
createNode: () => NodeItemView;
createNodeByNodeParam: (nodeParam: NodeParamV2) => NodeItemView;
}
class Util {
public static isSymbolResource(resourceStr: ResourceStr | undefined | null): boolean {
if (!Util.isResourceType(resourceStr)) {
return false;
}
let resource: Resource = resourceStr as Resource;
return resource.type === RESOURCE_TYPE_SYMBOL;
}
public static isResourceType(resource: ResourceStr | Resource | undefined | null): boolean {
if (!resource) {
return false;
}
if (typeof resource === 'string' || typeof resource === 'undefined') {
return false;
}
return true;
}
}
class TreeViewNodeItemFactory implements NodeItemViewFactory {
private static instance: TreeViewNodeItemFactory;
private constructor() {
}
/**
* TreeViewNodeItemFactory singleton function
*
* @returns TreeViewNodeItemFactory
*/
public static getInstance(): TreeViewNodeItemFactory {
if (!TreeViewNodeItemFactory.instance) {
TreeViewNodeItemFactory.instance = new TreeViewNodeItemFactory();
}
return TreeViewNodeItemFactory.instance;
}
/**
* TreeViewNodeItemFactory create default node
*
* @returns NodeItemView
*/
public createNode(): NodeItemView {
return {
imageNode: undefined,
inputText: new InputText(),
mainTitleNode: new MainTitleNode(''),
imageCollapse: undefined,
fontColor: undefined,
};
}
public createNodeByNodeParam(nodeParam: NodeParamV2): NodeItemView {
let nodeItemView: NodeItemView = this.createNode();
if (nodeParam.icon) {
nodeItemView.imageNode = new ImageNode(
nodeParam.icon,
nodeParam.symbolIconStyle,
$r('sys.float.ohos_id_alpha_content_fourth'),
IMAGE_NODE_HEIGHT,
IMAGE_NODE_WIDTH,
nodeParam.selectedIcon,
nodeParam.symbolSelectedIconStyle,
nodeParam.editIcon,
nodeParam.symbolEditIconStyle,
);
}
if (nodeParam.primaryTitle) {
nodeItemView.mainTitleNode = new MainTitleNode(nodeParam.primaryTitle);
}
return nodeItemView;
}
}
let emptyNodeInfo: NodeParamV2 = {
isFolder: true,
icon: '',
symbolIconStyle: undefined,
selectedIcon: '',
symbolSelectedIconStyle: undefined,
editIcon: '',
symbolEditIconStyle: undefined,
container: () => {
},
secondaryTitle: '',
primaryTitle: '',
parentNodeId: -1,
currentNodeId: -1,
}
class TreeViewTheme {
private static instance: TreeViewTheme;
public itemSelectedBgColor: ResourceColor = '#1A0A59F7';
public primaryTitleFontColor: ResourceColor = $r('sys.color.ohos_id_color_primary');
public secondaryTitleFontColor: ResourceColor = $r('sys.color.ohos_id_color_secondary');
public primaryTitleActiveFontColor: ResourceColor = $r('sys.color.ohos_id_color_text_primary_activated');
public itemPressedBgColor: ResourceColor = $r('sys.color.ohos_id_color_click_effect');
public itemHoverBgColor: ResourceColor = $r('sys.color.ohos_id_color_hover');
public borderFocusedColor: ResourceColor = $r('sys.color.ohos_id_color_focused_outline');
public leftIconColor: ResourceColor = $r('sys.color.icon_secondary');
public leftIconActiveColor: ResourceColor = $r('sys.color.icon_secondary');
public arrowIconColor: ResourceColor = $r('sys.color.icon_tertiary');
private constructor() {
}
/**
* TreeViewTheme singleton function
*
* @returns TreeViewNodeItemFactory
*/
public static getInstance(): TreeViewTheme {
if (!TreeViewTheme.instance) {
TreeViewTheme.instance = new TreeViewTheme();
}
return TreeViewTheme.instance;
}
}
@ObservedV2
export class NodeInfo {
@Trace public imageSource: Resource | string | undefined = '';
@Trace public symbolSource: SymbolGlyphModifier | undefined = undefined;
@Trace private nodeHeight: Resource | number;
@Trace private nodeItemView: NodeItemView;
@Trace private nodeLeftPadding: number;
@Trace private nodeColor: ResourceColor;
@Trace private nodeIsShow: boolean;
@Trace private status: Status;
@Trace private nodeBorder: NodeBorder;
@Trace private popUpInfo: PopUpInfo;
@Trace private listItemHeight: number;
@Trace private isShowTitle: boolean;
@Trace private isShowInputText: boolean;
@Trace private isSelected: boolean;
public readonly borderWidth: BorderWidth =
{ has: BORDER_WIDTH_HAS, none: BORDER_WIDTH_NONE }
@Trace private nodeParam: NodeParamV2;
@Trace private node: NodeItem;
@Trace private canShowFlagLine: boolean = false;
@Trace private isOverBorder: boolean = false;
@Trace private canShowBottomFlagLine: boolean = false;
@Trace private isHighLight: boolean = false;
@Trace private flagLineLeftMargin: number;
@Trace private isModify: boolean = false;
private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance();
@Trace public fontColor: ResourceColor = '';
constructor(node: NodeItem, nodeParam: NodeParamV2) {
this.node = node;
this.nodeParam = nodeParam;
this.nodeItemView = TreeViewNodeItemFactory.getInstance().createNodeByNodeParam(nodeParam);
this.popUpInfo = {
popUpIsShow: false,
popUpEnableArrow: false,
popUpColor: undefined,
popUpText: '',
popUpTextColor: undefined,
};
this.nodeHeight = NODE_HEIGHT;
this.nodeLeftPadding = node.nodeLevel * LEVEL_MARGIN + MARGIN_OFFSET;
this.nodeColor = $r('sys.color.ohos_id_color_background');
this.nodeIsShow = (this.node.nodeLevel > 0) ? false : true;
this.listItemHeight = (this.node.nodeLevel > 0) ? LIST_ITEM_HEIGHT_NONE : LIST_ITEM_HEIGHT;
this.isShowTitle = true;
this.isShowInputText = false;
this.isSelected = false;
this.status = {
normal: $r('sys.color.ohos_id_color_background_transparent'),
hover: this.treeViewTheme.itemHoverBgColor,
press: this.treeViewTheme.itemPressedBgColor,
selected: this.treeViewTheme.itemSelectedBgColor,
highLight: $r('sys.color.ohos_id_color_activated')
};
this.nodeBorder = {
borderWidth: BORDER_WIDTH_NONE,
borderColor: this.treeViewTheme.borderFocusedColor,
borderRadius: $r('sys.float.ohos_id_corner_radius_clicked')
};
this.flagLineLeftMargin = node.nodeLevel * LEVEL_MARGIN + MARGIN_OFFSET;
}
/**
* NodeInfo add collapse image
*
* @param isHasChildNode whether node has child node
*/
addImageCollapse(isHasChildNode: boolean): void {
if (isHasChildNode) {
this.nodeItemView.imageCollapse =
CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(CollapseImageType.ARROW_RIGHT);
} else {
this.nodeItemView.imageCollapse = undefined;
}
}
/**
* NodeInfo add expand image
*
* @param isHasChildNode whether node has child node
*/
addImageExpand(isHasChildNode: boolean): void {
if (isHasChildNode) {
this.nodeItemView.imageCollapse =
CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(CollapseImageType.ARROW_DOWN);
} else {
this.nodeItemView.imageCollapse = undefined;
}
}
setFontColor(color: ResourceColor): void {
this.fontColor = color
}
getFontColor(): ResourceColor {
return this.fontColor;
}
getPopUpInfo(): PopUpInfo {
return this.popUpInfo;
}
setPopUpIsShow(isShow: boolean): void {
this.popUpInfo.popUpIsShow = isShow;
}
setPopUpEnableArrow(popUpEnableArrow: boolean): void {
this.popUpInfo.popUpEnableArrow = popUpEnableArrow;
}
setPopUpColor(color: ResourceColor): void {
this.popUpInfo.popUpColor = color;
}
setPopUpText(text: string | Resource | undefined): void {
this.popUpInfo.popUpText = text;
}
setPopUpTextColor(popUpTextColor: ResourceColor): void {
this.popUpInfo.popUpTextColor = popUpTextColor;
}
getIsShowTitle(): boolean {
return this.isShowTitle;
}
getIsShowInputText(): boolean {
return this.isShowInputText;
}
setTitleAndInputTextStatus(isModify: boolean): void {
if (isModify) {
this.isShowTitle = false;
this.isShowInputText = true;
} else {
this.isShowTitle = true;
this.isShowInputText = false;
}
}
handleImageCollapseAfterAddNode(isAddImageCollapse: boolean): void {
// listTree this node already has ImageCollapse.
if (isAddImageCollapse) {
this.nodeItemView.imageCollapse =
CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(CollapseImageType.ARROW_DOWN);
} else {
this.nodeItemView.imageCollapse = undefined;
}
}
setNodeColor(nodeColor: ResourceColor | undefined): void {
if (nodeColor === undefined) {
return;
}
this.nodeColor = nodeColor;
}
getNodeColor(): ResourceColor {
return this.nodeColor;
}
setListItemHeight(listItemHeight: number): void {
this.listItemHeight = listItemHeight;
}
getListItemHeight(): number {
return this.listItemHeight;
}
getNodeCurrentNodeId(): number {
return this.node.currentNodeId;
}
getNodeParentNodeId(): number {
return this.node.parentNodeId;
}
getNodeLeftPadding(): number {
return this.nodeLeftPadding;
}
getNodeHeight(): Resource | number {
return this.nodeHeight;
}
setNodeIsShow(nodeIsShow: boolean): void {
this.nodeIsShow = nodeIsShow;
}
getNodeIsShow(): boolean {
return this.nodeIsShow;
}
getNodeItem(): NodeItemView {
return this.nodeItemView;
}
getNodeStatus(): Status {
return this.status;
}
getNodeBorder(): NodeBorder {
return this.nodeBorder;
}
setNodeBorder(isClearFocusStatus: boolean): void {
this.nodeBorder.borderWidth = isClearFocusStatus ? this.borderWidth.has : this.borderWidth.none;
}
getChildNodeInfo(): ChildNodeInfo {
return this.node.childNodeInfo;
}
getMenu(): () => void {
return this.nodeParam.container as () => void;
}
setIsSelected(isSelected: boolean): void {
this.isSelected = isSelected;
}
getIsSelected(): boolean {
return this.isSelected;
}
getNodeInfoData(): NodeParamV2 {
return this.nodeParam;
}
/* To gain the tree Node(NodeItem) while to alter node. */
public getNodeInfoNode(): NodeItem {
return this.node;
}
public getIsFolder(): boolean | undefined {
return this.nodeParam.isFolder;
}
public setCanShowFlagLine(canShowFlagLine: boolean): void {
this.canShowFlagLine = canShowFlagLine;
}
public getCanShowFlagLine(): boolean {
return this.canShowFlagLine;
}
public setFlagLineLeftMargin(currentNodeLevel: number | undefined): void {
if (currentNodeLevel === undefined) {
return;
}
this.flagLineLeftMargin = currentNodeLevel * LEVEL_MARGIN + MARGIN_OFFSET; // calculate
}
public getFlagLineLeftMargin(): number {
return this.flagLineLeftMargin;
}
public getNodeLevel(): number {
return this.node.nodeLevel;
}
public setIsOverBorder(isOverBorder: boolean): void {
this.isOverBorder = isOverBorder;
}
public getIsOverBorder(): boolean {
return this.isOverBorder;
}
public setCanShowBottomFlagLine(canShowBottomFlagLine: boolean): void {
this.canShowBottomFlagLine = canShowBottomFlagLine;
}
public getCanShowBottomFlagLine(): boolean {
return this.canShowBottomFlagLine;
}
public setIsHighLight(isHighLight: boolean): void {
this.isHighLight = isHighLight;
}
public getIsHighLight(): boolean {
return this.isHighLight;
}
public setIsModify(isModify: boolean): void {
this.isModify = isModify;
}
public getIsModify(): boolean {
return this.isModify;
}
}
type OnChangedCallback = (callbackParam: CallbackParamV2) => void;
/**
* Declare class TreeListenerV2.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
export class TreeListenerV2 {
private nodeClickEvents: OnChangedCallback[] = [];
private nodeClickOnceEvents: OnChangedCallback[] = [];
private nodeAddEvents: OnChangedCallback[] = [];
private nodeAddOnceEvents: OnChangedCallback[] = [];
private nodeDeleteEvents: OnChangedCallback[] = [];
private nodeDeleteOnceEvents: OnChangedCallback[] = [];
private nodeModifyEvents: OnChangedCallback[] = [];
private nodeModifyOnceEvents: OnChangedCallback[] = [];
private nodeMoveEvents: OnChangedCallback[] = [];
private nodeMoveOnceEvents: OnChangedCallback[] = [];
constructor() {
}
/**
* Node click event registration and processing.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onNodeClick(callback: OnChangedCallback): void {
this.nodeClickEvents.push(callback);
}
/**
* Node click event registration and processing (once).
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onceNodeClick(callback: OnChangedCallback): void {
this.nodeClickOnceEvents.push(callback);
}
/**
* Destroy node click callback event.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public offNodeClick(callback?: OnChangedCallback): void {
if (callback === undefined) {
this.nodeClickEvents = [];
this.nodeClickOnceEvents = [];
} else {
let index = this.nodeClickEvents.indexOf(callback);
if (index >= 0) {
this.nodeClickEvents.splice(index, 1);
}
}
}
/**
* Node add event registration and processing.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onNodeAdd(callback: OnChangedCallback): void {
this.nodeAddEvents.push(callback);
}
/**
* Node add event registration and processing (once).
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onceNodeAdd(callback: OnChangedCallback): void {
this.nodeAddOnceEvents.push(callback);
}
/**
* Destroy node add callback event.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public offNodeAdd(callback?: OnChangedCallback): void {
if (callback === undefined) {
this.nodeAddEvents = [];
this.nodeAddOnceEvents = [];
} else {
let index = this.nodeAddEvents.indexOf(callback);
if (index >= 0) {
this.nodeAddEvents.splice(index, 1);
}
}
}
/**
* Node delete event registration and processing.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onNodeDelete(callback: OnChangedCallback): void {
this.nodeDeleteEvents.push(callback);
}
/**
* Node delete event registration and processing (once).
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onceNodeDelete(callback: OnChangedCallback): void {
this.nodeDeleteOnceEvents.push(callback);
}
/**
* Destroy node delete callback event.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public offNodeDelete(callback?: OnChangedCallback): void {
if (callback === undefined) {
this.nodeDeleteEvents = [];
this.nodeDeleteOnceEvents = [];
} else {
let index = this.nodeDeleteEvents.indexOf(callback);
if (index >= 0) {
this.nodeDeleteEvents.splice(index, 1);
}
}
}
/**
* Node modify event registration and processing.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onNodeModify(callback: OnChangedCallback): void {
this.nodeModifyEvents.push(callback);
}
/**
* Node modify event registration and processing (once).
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onceNodeModify(callback: OnChangedCallback): void {
this.nodeModifyOnceEvents.push(callback);
}
/**
* Destroy node modify callback event.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public offNodeModify(callback?: OnChangedCallback): void {
if (callback === undefined) {
this.nodeModifyEvents = [];
this.nodeModifyOnceEvents = [];
} else {
let index = this.nodeModifyEvents.indexOf(callback);
if (index >= 0) {
this.nodeModifyEvents.splice(index, 1);
}
}
}
/**
* Node move event registration and processing.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onNodeMove(callback: OnChangedCallback): void {
this.nodeMoveEvents.push(callback);
}
/**
* Node move event registration and processing (once).
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public onceNodeMove(callback: OnChangedCallback): void {
this.nodeMoveOnceEvents.push(callback);
}
/**
* Destroy node move callback event.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public offNodeMove(callback?: OnChangedCallback): void {
if (callback === undefined) {
this.nodeMoveEvents = [];
this.nodeMoveOnceEvents = [];
} else {
let index = this.nodeMoveEvents.indexOf(callback);
if (index >= 0) {
this.nodeMoveEvents.splice(index, 1);
}
}
}
/**
* Triggers all callbacks of node click event.
* @since 26
*/
public emitNodeClick(argument: CallbackParamV2): void {
this.triggerCallbacks(this.nodeClickEvents, argument);
this.triggerOnceCallbacks(this.nodeClickOnceEvents, argument);
}
/**
* Triggers all callbacks of node add event.
* @since 26
*/
public emitNodeAdd(argument: CallbackParamV2): void {
this.triggerCallbacks(this.nodeAddEvents, argument);
this.triggerOnceCallbacks(this.nodeAddOnceEvents, argument);
}
/**
* Triggers all callbacks of node delete event.
* @since 26
*/
public emitNodeDelete(argument: CallbackParamV2): void {
this.triggerCallbacks(this.nodeDeleteEvents, argument);
this.triggerOnceCallbacks(this.nodeDeleteOnceEvents, argument);
}
/**
* Triggers all callbacks of node modify event.
* @since 26
*/
public emitNodeModify(argument: CallbackParamV2): void {
this.triggerCallbacks(this.nodeModifyEvents, argument);
this.triggerOnceCallbacks(this.nodeModifyOnceEvents, argument);
}
/**
* Triggers all callbacks of node move event.
* @since 26
*/
public emitNodeMove(argument: CallbackParamV2): void {
this.triggerCallbacks(this.nodeMoveEvents, argument);
this.triggerOnceCallbacks(this.nodeMoveOnceEvents, argument);
}
private triggerCallbacks(callbacks: OnChangedCallback[], argument: CallbackParamV2): void {
for (let i = 0; i < callbacks.length; i++) {
try {
callbacks[i](argument);
} catch (e) {
hilog.error(LOG_CODE, TAG, 'triggerCallbacks error');
}
}
}
private triggerOnceCallbacks(callbacks: OnChangedCallback[], argument: CallbackParamV2): void {
for (let i = 0; i < callbacks.length; i++) {
try {
callbacks[i](argument);
} catch (e) {
hilog.error(LOG_CODE, TAG, 'triggerOnceCallbacks error');
}
}
callbacks.splice(0, callbacks.length);
}
}
/**
* Declare class TreeListenerManagerV2.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
export class TreeListenerManagerV2 {
public static readonly APP_KEY_EVENT_BUS = 'app_key_event_bus_v2';
private appEventBus: TreeListenerV2;
private constructor() {
this.appEventBus = new TreeListenerV2();
}
/**
* Get instance of treeListenerManagerV2.
* @return treeListenerManagerV2 instance.
* @static
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
static getInstance(): TreeListenerManagerV2 {
if (AppStorage.Get('app_key_event_bus_v2') === undefined) {
AppStorage.SetOrCreate('app_key_event_bus_v2', new TreeListenerManagerV2())
}
return AppStorage.Get('app_key_event_bus_v2') as TreeListenerManagerV2;
}
/**
* Get treeListenerV2.
* @return treeListenerV2 object
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public getTreeListener(): TreeListenerV2 {
return this.appEventBus;
}
}
/**
* Declare TreeViewV2 Component
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
@ComponentV2
export struct TreeViewV2 {
/**
* Node data source of TreeViewV2
* @type TreeControllerV2
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
@Param
treeControllerV2: TreeControllerV2 = new TreeControllerV2();
@Local nodeList: NodeInfo[] = [];
listNodeDataSource: ListNodeDataSource = new ListNodeDataSource();
@Local item: NodeInfo[] | null = null;
@Local touchCount: number = 0;
@Local dropSelectedIndex: number = 0;
@Local viewLastIndex: number = -1;
@Local followingSystemFontScale: boolean = false;
@Local maxAppFontScale: number = 1;
@Local listItemBgColor: ResourceColor = $r('sys.color.ohos_id_color_background_transparent');
@Local allParentNode: number[] = [];
@Provider('treeViewThemeV2') treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance();
@Provider('clickButtonFlagV2') clickButtonFlag: boolean = true;
@Provider('accessibilityNodeTypeV2') accessibilityNodeType: AccessibilityNodeType = AccessibilityNodeType.TEXT;
@Provider('isAccessibilityEnabledV2') isAccessibilityEnabled: boolean = accessibility.isScreenReaderOpenSync();
@Builder
NullBuilder() {
};
@BuilderParam private listTreeViewMenu: () => void = this.NullBuilder;
private readonly MAX_CN_LENGTH: number = 254;
private readonly MAX_EN_LENGTH: number = 255;
private readonly INITIAL_INVALID_VALUE = -1;
private readonly MAX_TOUCH_DOWN_COUNT = 0;
private isMultiPress: boolean = false;
private touchDownCount: number = this.INITIAL_INVALID_VALUE;
private appEventBus: TreeListenerV2 = TreeListenerManagerV2.getInstance().getTreeListener();
private readonly itemPadding: ItemPadding = {
left: $r('sys.float.ohos_id_card_margin_start'),
right: $r('sys.float.ohos_id_card_margin_end'),
top: $r('sys.float.ohos_id_text_margin_vertical'),
bottom: $r('sys.float.ohos_id_text_margin_vertical'),
};
private readonly textInputPadding: ItemPadding =
{
left: $r('sys.float.padding_level0'),
right: $r('sys.float.padding_level0'),
top: $r('sys.float.padding_level0'),
bottom: $r('sys.float.padding_level0'),
}
onWillApplyTheme(theme: Theme) {
this.treeViewTheme.itemSelectedBgColor = theme.colors.interactiveSelect;
this.treeViewTheme.itemPressedBgColor = theme.colors.interactivePressed;
this.treeViewTheme.itemHoverBgColor = theme.colors.interactiveHover;
this.treeViewTheme.primaryTitleFontColor = theme.colors.fontPrimary;
this.treeViewTheme.secondaryTitleFontColor = theme.colors.fontSecondary;
this.treeViewTheme.primaryTitleActiveFontColor = theme.colors.interactiveActive;
this.treeViewTheme.borderFocusedColor = theme.colors.interactiveFocus;
this.treeViewTheme.leftIconColor = theme.colors.iconSecondary;
this.treeViewTheme.leftIconActiveColor = theme.colors.interactiveActive;
this.treeViewTheme.arrowIconColor = theme.colors.iconPrimary;
this.treeControllerV2.treeViewTheme = this.treeViewTheme;
}
aboutToAppear(): void {
if (this.treeControllerV2 !== null) {
this.listNodeDataSource = this.treeControllerV2.getListNodeDataSource();
this.nodeList = this.treeControllerV2.getListNodeDataSource().listNode;
this.item = this.treeControllerV2.getListNodeDataSource().listNode;
}
let uiContent: UIContext = this.getUIContext();
this.followingSystemFontScale = uiContent.isFollowingSystemFontScale();
this.maxAppFontScale = uiContent.getMaxFontScale();
accessibility.on('screenReaderStateChange', (state: boolean) => {
this.isAccessibilityEnabled = state;
})
}
aboutToDisappear(): void {
accessibility.off('screenReaderStateChange');
}
decideFontScale(): number {
let uiContent: UIContext = this.getUIContext();
let systemFontScale: number = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
if (!this.followingSystemFontScale) {
return 1;
}
return Math.min(systemFontScale, this.maxAppFontScale, MAX_FONT_SCALE);
}
decideSymbolFontScale(isSymbol: boolean): number {
if (!isSymbol || !this.followingSystemFontScale) {
return 1;
}
let uiContent: UIContext = this.getUIContext();
let systemFontScale: number = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
let symbolFontSizeScale: number = Math.min(systemFontScale, this.maxAppFontScale, MAX_SYMBOL_FONT_SCALE);
return Math.max(symbolFontSizeScale, MIN_SYMBOL_FONT_SCALE);
}
@Builder
popupForShowTitle(text: string | Resource, backgroundColor: Resource, fontColor: Resource) {
Row() {
Text(text)
.fontSize($r('sys.float.ohos_id_text_size_body2'))
.fontWeight('regular')
.fontColor(fontColor)
.minFontScale(MIN_FONT_SCALE)
.maxFontScale(this.decideFontScale())
}.backgroundColor(backgroundColor)
.border({ radius: $r('sys.float.ohos_id_elements_margin_horizontal_l') })
.padding({
left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
top: $r('sys.float.ohos_id_card_margin_middle'),
bottom: $r('sys.float.ohos_id_card_margin_middle'),
})
}
@Builder
builder() {
this.listTreeViewMenu()
}
/* Set the popup of dragging node. */
@Builder
draggingPopup(item: NodeInfo) {
Row() {
if (item.getNodeItem().imageNode) {
Row() {
if (item.getNodeItem().imageNode?.symbolNormalSource) {
SymbolGlyph()
.attributeModifier(item.getNodeItem().imageNode?.symbolNormalSource)
.fontSize(`${item.getNodeItem().imageNode?.itemHeight as number * this.decideSymbolFontScale(true)}vp`)
.effectStrategy(SymbolEffectStrategy.NONE)
.symbolEffect(new SymbolEffect(), false)
.opacity(this.listNodeDataSource.getDragPopupPara().imageOpacity)
} else {
if (Util.isSymbolResource(item.getNodeItem().imageNode?.normalSource)) {
SymbolGlyph(item.getNodeItem().imageNode?.normalSource as Resource)
.fontSize(`${item.getNodeItem().imageNode?.itemHeight as number * this.decideSymbolFontScale(true)}vp`)
.opacity(this.listNodeDataSource.getDragPopupPara().imageOpacity)
} else {
Image(item.getNodeItem().imageNode?.normalSource)
.objectFit(ImageFit.Contain)
.height(item.getNodeItem().imageNode?.itemHeight)
.width(item.getNodeItem().imageNode?.itemWidth)
.opacity(this.listNodeDataSource.getDragPopupPara().imageOpacity)
.matchTextDirection((item.getNodeItem().imageCollapse?.collapseSource === ARROW_RIGHT ||
item.getNodeItem().imageCollapse?.collapseSource === ARROW_RIGHT_WITHE) ? true : false)
}
}
}
.backgroundColor(COLOR_IMAGE_ROW)
.margin({ end: getLengthMetricsByResourceOrNumber(item.getNodeItem().imageNode?.itemRightMargin) })
.height(item.getNodeItem().imageNode?.itemHeight as number * this.decideSymbolFontScale(
item.getNodeItem().imageNode?.symbolNormalSource !== undefined ||
Util.isSymbolResource(item.getNodeItem().imageNode?.normalSource)))
.width(item.getNodeItem().imageNode?.itemWidth as number * this.decideSymbolFontScale(
item.getNodeItem().imageNode?.symbolNormalSource !== undefined ||
Util.isSymbolResource(item.getNodeItem().imageNode?.normalSource)))
}
Row() {
if (item.getNodeItem().mainTitleNode && item.getIsShowTitle()) {
Text(item.getNodeItem().mainTitleNode?.title)
.maxLines(1)
.minFontScale(MIN_FONT_SCALE)
.maxFontScale(this.decideFontScale())
.fontSize(item.getNodeItem().mainTitleNode?.size)
.fontColor(this.listNodeDataSource.getDragPopupPara().fontColor)
.fontWeight(this.listNodeDataSource.getDragPopupPara().fontWeight)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
}
.constraintSize({
minWidth: item.getNodeItem().imageNode ?
this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth1 :
this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth2,
maxWidth: item.getNodeItem().imageNode ?
this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth1 :
this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth2,
})
}
.id(`treeView_node_lift${item.getNodeCurrentNodeId()}`)
.constraintSize({
minWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.minWidth,
maxWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.maxWidth,
})
.height(this.listNodeDataSource.getDragPopupPara().height)
.backgroundColor(this.listNodeDataSource.getDragPopupPara().backgroundColor)
.padding({
start: LengthMetrics.resource(this.listNodeDataSource.getDragPopupPara().padding.left),
end: LengthMetrics.resource(this.listNodeDataSource.getDragPopupPara().padding.right),
})
.shadow(this.listNodeDataSource.getDragPopupPara().shadow)
.borderRadius(this.listNodeDataSource.getDragPopupPara().borderRadius) // need to doubleCheck.
}
clearLastIndexColor(): void {
if (this.viewLastIndex === -1 || this.viewLastIndex >= this.nodeList.length) {
return;
}
this.setImageSources(this.viewLastIndex, InteractionStatus.NORMAL);
this.listNodeDataSource.setImageSource(this.viewLastIndex, InteractionStatus.NORMAL);
this.nodeList[this.viewLastIndex].setNodeColor($r('sys.color.ohos_id_color_background_transparent'))
this.nodeList[this.viewLastIndex].fontColor = this.treeViewTheme.primaryTitleFontColor;
this.listNodeDataSource.listNode[this.viewLastIndex]
.setNodeColor($r('sys.color.ohos_id_color_background_transparent'));
this.listNodeDataSource.listNode[this.viewLastIndex].fontColor = this.treeViewTheme.primaryTitleFontColor;
this.listNodeDataSource.listNode[this.viewLastIndex].setIsSelected(false);
this.listNodeDataSource.listNode[this.viewLastIndex].imageSource =
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.imageNode?.source;
this.listNodeDataSource.listNode[this.viewLastIndex].symbolSource =
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.imageNode?.symbolSource;
}
setImageSources(index: number, interactionStatus: InteractionStatus): void {
let nodeInfo: NodeInfo = this.nodeList[index];
nodeInfo.setIsSelected(interactionStatus === InteractionStatus.SELECTED ||
interactionStatus === InteractionStatus.EDIT || interactionStatus === InteractionStatus.FINISH_EDIT);
if (nodeInfo.getNodeItem().mainTitleNode !== null && interactionStatus !== InteractionStatus.DRAG_INSERT &&
interactionStatus !== InteractionStatus.FINISH_DRAG_INSERT) {
nodeInfo.getNodeItem().mainTitleNode?.setMainTitleSelected(interactionStatus === InteractionStatus.SELECTED ||
interactionStatus === InteractionStatus.FINISH_EDIT);
}
if (nodeInfo.getNodeItem().imageNode !== null) {
nodeInfo.getNodeItem().imageNode?.setImageSource(interactionStatus);
}
}
private touchInner(itemInner: NodeInfo, event: TouchEvent) {
this.viewLastIndex = this.listNodeDataSource.getLastIndex();
let index: number = this.listNodeDataSource.findIndex(itemInner.getNodeCurrentNodeId());
if (event.type === TouchType.Down) {
if (index !== this.viewLastIndex) {
this.clearLastIndexColor();
this.listNodeDataSource.lastIndex = index;
this.listNodeDataSource.setClickIndex(index);
}
}
if (event.type === TouchType.Up) {
this.listNodeDataSource.listNode[index].setIsSelected(true);
this.listNodeDataSource.setImageSource(index, InteractionStatus.SELECTED);
if (this.listNodeDataSource.listNode[index].getNodeItem().imageNode !== null) {
this.listNodeDataSource.listNode[index].imageSource = this.listNodeDataSource.listNode[index]
.getNodeItem().imageNode?.source;
this.listNodeDataSource.listNode[index].symbolSource = this.listNodeDataSource.listNode[index]
.getNodeItem().imageNode?.symbolSource;
}
if (index !== this.viewLastIndex) {
this.clearLastIndexColor();
this.listNodeDataSource.lastIndex = index;
this.listNodeDataSource.setClickIndex(index);
}
this.viewLastIndex = index;
}
if (this.listNodeDataSource.getLastIndex() !== -1 && index !== this.listNodeDataSource.getLastIndex()) {
this.listNodeDataSource.setPopUpInfo(
PopUpType.WARNINGS,
InputError.NONE,
false,
this.listNodeDataSource.getLastIndex()
);
this.listNodeDataSource.setItemVisibilityOnEdit(
this.listNodeDataSource.getLastIndex(),
MenuOperation.COMMIT_NODE
);
}
}
private clickInner(itemInner: NodeInfo) {
this.viewLastIndex = this.listNodeDataSource.getLastIndex();
let index: number = this.listNodeDataSource.findIndex(itemInner.getNodeCurrentNodeId());
if (index !== this.viewLastIndex) {
this.clearLastIndexColor();
this.listNodeDataSource.lastIndex = index;
this.listNodeDataSource.setClickIndex(index);
}
this.listNodeDataSource.listNode[index].setIsSelected(true);
this.listNodeDataSource.setImageSource(index, InteractionStatus.SELECTED);
if (this.listNodeDataSource.listNode[index].getNodeItem().imageNode !== null) {
this.listNodeDataSource.listNode[index].imageSource = this.listNodeDataSource.listNode[index]
.getNodeItem().imageNode?.source;
this.listNodeDataSource.listNode[index].symbolSource = this.listNodeDataSource.listNode[index]
.getNodeItem().imageNode?.symbolSource;
}
if (index !== this.viewLastIndex) {
this.clearLastIndexColor();
this.listNodeDataSource.lastIndex = index;
this.listNodeDataSource.setClickIndex(index);
}
this.viewLastIndex = index;
if (this.listNodeDataSource.getLastIndex() !== -1 && index !== this.listNodeDataSource.getLastIndex()) {
this.listNodeDataSource.setPopUpInfo(
PopUpType.WARNINGS,
InputError.NONE,
false,
this.listNodeDataSource.getLastIndex()
);
this.listNodeDataSource.setItemVisibilityOnEdit(
this.listNodeDataSource.getLastIndex(),
MenuOperation.COMMIT_NODE
);
}
}
build() {
List({}) {
LazyForEach(this.listNodeDataSource, (itemInner: NodeInfo) => {
ListItem() {
Row() {
TreeViewInner({
item: itemInner,
listNodeDataSource: this.listNodeDataSource,
initialIndex: this.listNodeDataSource.findIndex(itemInner.getNodeCurrentNodeId()),
listTreeViewMenu: this.listTreeViewMenu,
callBackClick: (): void => this.clickInner(itemInner),
})
}
.onTouch(this.isAccessibilityEnabled ? undefined : (event: TouchEvent) => {
this.touchInner(itemInner, event);
})
}
.width('100%')
.height(itemInner.getListItemHeight())
.padding({
start: LengthMetrics.resource(this.itemPadding.left),
end: LengthMetrics.resource(this.itemPadding.right)
})
.align(Alignment.Start)
.onDragStart((event: DragEvent, extraParams: string) => {
this.accessibilityNodeType = AccessibilityNodeType.LIFT;
if (this.listNodeDataSource.getIsDrag() || this.listNodeDataSource.getIsInnerDrag() || this.isMultiPress) {
hilog.error(LOG_CODE, TAG, 'drag error, a item has been dragged');
return;
}
this.dropSelectedIndex = JSON.parse(extraParams).selectedIndex;
let currentNodeIndex: number = JSON.parse(extraParams).selectedIndex;
let currentNodeInfo: NodeInfo = this.listNodeDataSource.getData(currentNodeIndex) as NodeInfo;
let currentItemNodeId: number = itemInner.getNodeCurrentNodeId();
/* handle the situation of drag error, currentNodeIndex is not found in onDragStart. */
if (currentNodeIndex >= this.listNodeDataSource.totalCount() || currentNodeIndex === undefined) {
hilog.error(LOG_CODE, TAG, 'drag error, currentNodeIndex is not found in onDragStart');
return;
}
this.listNodeDataSource.setIsInnerDrag(true);
this.listNodeDataSource.setIsDrag(true);
this.listNodeDataSource.setCurrentNodeInfo(currentNodeInfo);
this.listNodeDataSource.setDraggingCurrentNodeId(currentNodeInfo?.getNodeCurrentNodeId());
this.listNodeDataSource.setDraggingParentNodeId(currentNodeInfo?.getNodeParentNodeId());
/* set the opacity of the dragging node. */
let draggingNodeOpacity: number = DRAG_OPACITY;
this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
this.listNodeDataSource.notifyDataChange(currentNodeIndex);
/**
* handle the situation of drag is too fast,it attribute a fault to OH.
* OH has Solved on real machine.
*/
if (currentItemNodeId !== currentNodeInfo?.getNodeCurrentNodeId()) {
hilog.error(LOG_CODE, TAG, 'drag is too fast, it attribute a fault to OH');
this.listNodeDataSource.setIsDrag(false);
return;
}
let primaryTitle = currentNodeInfo.getNodeInfoData()?.primaryTitle === undefined ? '' :
currentNodeInfo.getNodeInfoData()?.primaryTitle;
let secondaryTitle = currentNodeInfo.getNodeInfoData()?.secondaryTitle === undefined ? '' :
currentNodeInfo.getNodeInfoData()?.secondaryTitle;
let primaryTitleText = this.listNodeDataSource.getAccessibleTitleText(primaryTitle);
let secondaryTitleText = this.listNodeDataSource.getAccessibleTitleText(secondaryTitle);
let title: string = `${primaryTitleText}, ${secondaryTitleText}`;
this.listNodeDataSource.sendAccessibility(
this.listNodeDataSource.getStringByName('treeview_accessibility_lift_node', title));
let allParentNode: number[] = [];
for (let i = 0; i < this.listNodeDataSource.listNode.length; i++) {
if (this.listNodeDataSource.listNode[i].getNodeParentNodeId() === -1) {
allParentNode.push(this.listNodeDataSource.listNode[i].getNodeCurrentNodeId());
}
}
this.allParentNode = allParentNode;
let eventInfo: accessibility.EventInfo = ({
type: 'requestFocusForAccessibility',
bundleName: (getContext() as common.UIAbilityContext)?.abilityInfo?.bundleName,
triggerAction: 'common',
customId: `treeView_node_lift${currentItemNodeId}`
});
accessibility.sendAccessibilityEvent(eventInfo).then(() => {
setTimeout(() => {
this.accessibilityNodeType = AccessibilityNodeType.TEXT;
}, ENTER_EXIT_DURATION)
});
return this.draggingPopup(currentNodeInfo);
})
}, (item: NodeInfo) => JSON.stringify(item))
}
/* Move the dragged node. */
.onDragMove((event: DragEvent, extraParams: string) => {
if (this.isMultiPress) {
hilog.error(LOG_CODE, TAG, 'drag error, a item has been dragged');
return;
}
let nodeHeight: number = LIST_ITEM_HEIGHT;
/* flag the position of the focus on the node. */
let flag: Flag = Math.floor(
event.getY() /
(nodeHeight / FLAG_NUMBER)) %
FLAG_NUMBER ? Flag.DOWN_FLAG : Flag.UP_FLAG;
/* Record the node position to which the dragged node moves. */
let index: number = JSON.parse(extraParams).insertIndex;
/* Handle the situation where the focus(index) exceeds the list area. */
let isOverBorder: boolean = false;
if (index >= this.listNodeDataSource.totalCount()) {
flag = Flag.DOWN_FLAG;
index = this.listNodeDataSource.totalCount() - 1;
this.listNodeDataSource.getData(index)?.setIsOverBorder(true);
isOverBorder = true;
} else {
this.listNodeDataSource.getData(index)?.setIsOverBorder(false);
}
let currentNodeInfo: NodeInfo | undefined = this.listNodeDataSource.getData(index);
let currentNodeId: number | undefined = currentNodeInfo?.getNodeCurrentNodeId();
/**
* handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId";
* do not perform some functions.
*/
if (index !== this.listNodeDataSource.getLastPassIndex() && this.listNodeDataSource.getIsInnerDrag()) {
let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(currentNodeId);
if (isParentNodeOfInsertNode) {
this.listNodeDataSource.setPassIndex(index);
if (currentNodeId !== undefined) {
this.listNodeDataSource.clearTimeOutAboutDelayHighLightAndExpand(
this.listNodeDataSource.findIndex(currentNodeId));
}
this.listNodeDataSource.setFlag(Flag.NONE);
return;
}
}
this.listNodeDataSource.setLastPassIndex(index);
/* Set the visibility of the flag line. */
this.listNodeDataSource.setVisibility(flag, index - 1, isOverBorder, this.allParentNode);
/* Automatically HighLight one second delay and expand after two second delay. */
if (currentNodeId !== undefined && currentNodeId !== this.listNodeDataSource.getDraggingCurrentNodeId()) {
this.listNodeDataSource.delayHighLightAndExpandNode(this.listNodeDataSource.findIndex(currentNodeId),
currentNodeId, index);
}
})
/* DragEvent Enter. */
.onDragEnter((event: DragEvent, extraParams: string) => {
if (this.listNodeDataSource.getIsInnerDrag()) {
this.listNodeDataSource.setIsDrag(true);
/* set the opacity of the dragging node. */
let draggingNodeOpacity: number = DRAG_OPACITY;
this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
}
})
/* DragEvent Leave. */
.onDragLeave((event: DragEvent, extraParams: string) => {
this.listNodeDataSource.hideLastLine();
this.listNodeDataSource.clearLastTimeoutHighLight();
this.listNodeDataSource.clearLastTimeoutExpand();
let draggingNodeOpacity: number = DRAG_OPACITY_NONE;
this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
this.listNodeDataSource.setIsDrag(false);
this.listNodeDataSource.notifyDataReload();
})
/* DragEvent Drop. */
.onDrop((event: DragEvent, extraParams: string) => {
this.accessibilityNodeType = AccessibilityNodeType.PLACE;
this.listNodeDataSource.clearLastTimeoutExpand();
let draggingNodeOpacity: number = DRAG_OPACITY_NONE;
this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
let insertNodeIndex: number = JSON.parse(extraParams).insertIndex;
let currentNodeIndex: number = this.dropSelectedIndex;
if (currentNodeIndex - 1 > this.listNodeDataSource.totalCount() || currentNodeIndex === undefined) {
hilog.error(LOG_CODE, TAG, 'drag error, currentNodeIndex is not found');
this.listNodeDataSource.setIsDrag(false);
return;
}
if (insertNodeIndex === this.listNodeDataSource.totalCount()) {
hilog.info(LOG_CODE, TAG, 'need to insert into the position of the last line');
insertNodeIndex -= 1;
}
let insertNodeInfo: NodeInfo | undefined = this.listNodeDataSource.getData(insertNodeIndex);
if (insertNodeInfo === undefined) {
return;
}
let insertNodeCurrentNodeId: number = insertNodeInfo.getNodeCurrentNodeId();
/* outer node is move in. */
if (!this.listNodeDataSource.getIsDrag() || !this.listNodeDataSource.getIsInnerDrag()) {
this.listNodeDataSource.clearLastTimeoutHighLight();
this.listNodeDataSource.setIsInnerDrag(false);
this.listNodeDataSource.hideLastLine();
this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
this.listNodeDataSource.refreshSubtitle(insertNodeCurrentNodeId);
this.listNodeDataSource.notifyDataReload();
return;
}
let currentNodeInfo: NodeInfo | null = this.listNodeDataSource.getCurrentNodeInfo();
let insertNodeParentNodeId: number = insertNodeInfo.getNodeParentNodeId();
let draggingCurrentNodeId: number = this.listNodeDataSource.getDraggingCurrentNodeId();
let draggingParentNodeId: number = this.listNodeDataSource.getDraggingParentNodeId();
/**
* handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId".
* drag is fail.
*/
let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(insertNodeCurrentNodeId);
if (isParentNodeOfInsertNode) {
this.listNodeDataSource.clearLastTimeoutHighLight();
this.listNodeDataSource.setIsInnerDrag(false);
this.listNodeDataSource.hideLastLine();
this.listNodeDataSource.notifyDataChange(insertNodeIndex);
this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
this.listNodeDataSource.setIsDrag(false);
/* set the position of focus. */
let currentFocusIndex: number = this.listNodeDataSource.findIndex(draggingCurrentNodeId);
this.listNodeDataSource.setClickIndex(currentFocusIndex);
this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex);
return;
}
/* Collapse drag node. */
if (this.listNodeDataSource.getExpandAndCollapseInfo(draggingCurrentNodeId) === NodeStatus.EXPAND) {
this.listNodeDataSource.expandAndCollapseNode(
this.listNodeDataSource.findIndex(draggingCurrentNodeId));
}
let flag: boolean = false;
/* Expand insert node. */
if (this.listNodeDataSource.getExpandAndCollapseInfo(insertNodeCurrentNodeId) === NodeStatus.COLLAPSE) {
let currentIndex: number = this.listNodeDataSource.findIndex(insertNodeCurrentNodeId);
if (this.listNodeDataSource.listNode[currentIndex].getIsHighLight()) {
this.listNodeDataSource.expandAndCollapseNode(currentIndex);
}
flag = true;
}
/* alter dragNode. */
this.listNodeDataSource.setLastDelayHighLightId();
if (currentNodeInfo !== null && draggingCurrentNodeId !== insertNodeCurrentNodeId) {
this.listNodeDataSource.alterDragNode(insertNodeParentNodeId, insertNodeCurrentNodeId,
draggingParentNodeId, draggingCurrentNodeId, currentNodeInfo);
this.listNodeDataSource.hideLastLine();
} else {
/*the position of dragNode is equal with the position of insertNode. */
this.listNodeDataSource.hideLastLine();
this.listNodeDataSource.setLastPassId(draggingCurrentNodeId);
this.listNodeDataSource.hideLastLine();
}
let lastDelayHighLightIndex: number =
this.listNodeDataSource.findIndex(this.listNodeDataSource.getLastDelayHighLightId());
this.listNodeDataSource.setLastDelayHighLightIndex(lastDelayHighLightIndex);
this.listNodeDataSource.clearLastTimeoutHighLight();
this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
this.listNodeDataSource.setIsDrag(false);
/* set the position of focus. */
let currentFocusIndex: number = this.listNodeDataSource.findIndex(draggingCurrentNodeId);
this.listNodeDataSource.setClickIndex(currentFocusIndex);
this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex);
/* innerDrag is over. */
this.listNodeDataSource.setIsInnerDrag(false);
this.listNodeDataSource.notifyDataReload();
this.listNodeDataSource.listNode[currentFocusIndex].fontColor = this.treeViewTheme.primaryTitleActiveFontColor;
if (this.viewLastIndex !== -1 && currentNodeIndex !== this.viewLastIndex) {
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.mainTitleNode?.setMainTitleSelected(false);
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.mainTitleNode?.setMainTitleHighLight(false);
}
this.listNodeDataSource.lastIndex = this.viewLastIndex;
if (this.listNodeDataSource.listNode[this.viewLastIndex]) {
if (this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.imageNode !== null) {
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.imageNode?.setImageSource(InteractionStatus.NORMAL);
this.listNodeDataSource.listNode[this.viewLastIndex].imageSource =
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.imageNode?.source;
this.listNodeDataSource.listNode[this.viewLastIndex].symbolSource =
this.listNodeDataSource.listNode[this.viewLastIndex].getNodeItem()
.imageNode?.symbolSource;
}
}
if (this.listNodeDataSource.listNode[this.viewLastIndex]) {
this.listNodeDataSource.listNode[this.viewLastIndex]
.setNodeColor($r('sys.color.ohos_id_color_background_transparent'));
}
this.listNodeDataSource.lastIndex = currentFocusIndex;
let parentNodeId: number | undefined = currentNodeInfo?.getNodeParentNodeId();
this.listNodeDataSource.judgeImageCollapse(parentNodeId);
/*accessibilityRead regain focus. */
let eventInfo: accessibility.EventInfo = ({
type: 'requestFocusForAccessibility',
bundleName: (getContext() as common.UIAbilityContext)?.abilityInfo?.bundleName,
triggerAction: 'common',
customId: `treeView_node${draggingCurrentNodeId}`
});
accessibility.sendAccessibilityEvent(eventInfo).then(() => {
setTimeout(() => {
this.accessibilityNodeType = AccessibilityNodeType.TEXT;
}, ENTER_EXIT_DURATION);
});
})
}
}
/**
* Declare CallbackParamV2
* @type CallbackParamV2
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
export interface CallbackParamV2 {
/**
* Get the currentNodeId.
* @type { number }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
currentNodeId: number,
/**
* Get the parentNodeId.
* @type { ?number }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
parentNodeId?: number,
/**
* Get the childIndex.
* @type { ?number }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
childIndex?: number,
}
type OnContainerCallback = () => void;
/**
* Declare NodeParamV2
* @typedef NodeParamV2
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
export interface NodeParamV2 {
/**
* Set the parentNodeId.
* @type { ?number }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
parentNodeId?: number,
/**
* Set currentNodeId.
* @type { ?number }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
currentNodeId?: number,
/**
* Set catalog whether is floder.
* @type { ?boolean }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
isFolder?: boolean,
/**
* Set the icon resource.
* @type { ?ResourceStr }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
icon?: ResourceStr,
/**
* Set the symbol resource.
* @type { ?SymbolGlyphModifier }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 16
*/
symbolIconStyle?: SymbolGlyphModifier;
/**
* Set selected icon resource.
* @type { ?ResourceStr }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
selectedIcon?: ResourceStr,
/**
* Set selected symbol resource.
* @type { ?SymbolGlyphModifier }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 16
*/
symbolSelectedIconStyle?: SymbolGlyphModifier;
/**
* Set edit icon resource.
* @type { ?ResourceStr }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
editIcon?: ResourceStr,
/**
* Set edit symbol resource.
* @type { ?SymbolGlyphModifier }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 16
*/
symbolEditIconStyle?: SymbolGlyphModifier;
/**
* Set primary title content.
* @type { ?ResourceStr }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
primaryTitle?: ResourceStr,
/**
* Set secondary title content.
* @type { ?ResourceStr }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
secondaryTitle?: ResourceStr,
/**
* Set subcomponent binded on tree item.
* @type { ?OnContainerCallback }
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
container?: OnContainerCallback,
}
/**
* Declare TreeControllerV2.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
export class TreeControllerV2 {
public readonly ROOT_NODE_ID: number = -1;
private nodeIdList: number[] = [];
private listNodeDataSource: ListNodeDataSource = new ListNodeDataSource();
private initBuild: boolean = true;
public treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance();
public getListNodeDataSource(): ListNodeDataSource {
return this.listNodeDataSource;
}
public getClickNodeChildrenInfo(): NodeInfoView[] {
let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
return this.listNodeDataSource.getClickNodeChildrenInfo(clickNodeId);
}
public getChildrenId(): number[] {
let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
return this.listNodeDataSource.getClickChildId(clickNodeId);
}
/**
* Delete a node.
* Register an ON_ITEM_DELETE callback through the EventBus mechanism to obtain the IDs of all deleted nodes.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public removeNode(): void {
let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
if (clickNodeId < 0) {
return;
}
let parentNodeId: number = this.listNodeDataSource.findParentNodeId(clickNodeId);
let removeNodeIdList: number[] = this.listNodeDataSource.removeNode(clickNodeId, parentNodeId);
this.listNodeDataSource.refreshData(
MenuOperation.REMOVE_NODE,
parentNodeId,
removeNodeIdList
);
this.nodeIdList.splice(this.nodeIdList.indexOf(clickNodeId), 1);
this.listNodeDataSource.lastIndex = -1;
}
/**
* Modify the node name.
* Register an ON_ITEM_MODIFY callback to obtain the ID, parent node ID, and node name of the modified node.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public modifyNode(): void {
let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
this.listNodeDataSource.setItemVisibilityOnEdit(clickNodeId, MenuOperation.MODIFY_NODE);
}
/**
* add new node
*
* @param initBuild whether is in initialization process
*/
public add(initBuild: boolean): void {
let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
if (clickNodeId === this.listNodeDataSource.ROOT_NODE_ID || !this.listNodeDataSource.getIsFolder(clickNodeId)) {
return;
}
let newNodeParam: NodeParamV2 = this.listNodeDataSource.getNewNodeParam(clickNodeId);
this.nodeIdList.push(this.nodeIdList[this.nodeIdList.length - 1] + 1);
let newNodeId: number = this.nodeIdList[this.nodeIdList.length - 1];
let addNodeResult: boolean = this.listNodeDataSource.addNode(clickNodeId, newNodeId,
{
isFolder: newNodeParam.isFolder,
icon: newNodeParam.icon,
symbolIconStyle: newNodeParam.symbolIconStyle,
selectedIcon: newNodeParam.selectedIcon,
symbolSelectedIconStyle: newNodeParam.symbolSelectedIconStyle,
editIcon: newNodeParam.editIcon,
symbolEditIconStyle: newNodeParam.symbolEditIconStyle,
primaryTitle: '新建文件夹',
container: newNodeParam.container,
secondaryTitle: newNodeParam.secondaryTitle as ResourceStr,
}, initBuild);
if (!addNodeResult) {
return;
}
this.listNodeDataSource.refreshData(
MenuOperation.ADD_NODE,
clickNodeId,
[newNodeId],
);
this.listNodeDataSource.setPopUpInfo(
PopUpType.WARNINGS,
InputError.NONE,
false,
this.listNodeDataSource.getLastIndex()
);
this.listNodeDataSource.setItemVisibilityOnEdit(
this.listNodeDataSource.getLastIndex(),
MenuOperation.COMMIT_NODE
);
this.listNodeDataSource.listNode[this.listNodeDataSource.getLastIndex()]
.setFontColor(this.treeViewTheme.primaryTitleFontColor);
let newNodeIndex: number = this.listNodeDataSource.findIndex(newNodeId);
this.listNodeDataSource.setClickIndex(newNodeIndex);
this.listNodeDataSource.handleEvent(Event.TOUCH_UP, newNodeIndex);
}
/**
* Initialize the interface of the tree view. This interface is used to generate ListNodeDataSource data.
* addNode is only designed for initialization. It can only be invoked during initialization.
*
* A maximum of 50 directory levels can be added.
*
* @param nodeParam Configuration information of the newly added node.
*
* For details, see the comment description of NodeParamV2.
* @return ListTreeNode Tree view component proxy class.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public addNode(nodeParam?: NodeParamV2): TreeControllerV2 {
if (nodeParam === undefined) {
this.add(this.initBuild);
return this;
} else {
for (let i: number = 0; i < this.nodeIdList.length; i++) {
if (nodeParam.currentNodeId === this.nodeIdList[i].valueOf()) {
throw new Error('ListTreeNode[addNode]: ' +
'The parameters of the new node cannot contain the same currentNodeId.');
return this;
}
}
let addNodeResult: boolean = false;
if (nodeParam.primaryTitle !== undefined &&
!this.listNodeDataSource.checkMainTitleIsValid(nodeParam.primaryTitle.toString())) {
throw new Error('ListTreeNode[addNode]: ' +
'The directory name cannot contain the following characters\ /: *? "< > | or exceeds the maximum length.');
return this;
}
if (nodeParam.primaryTitle === null && nodeParam.icon === null && nodeParam.symbolIconStyle === null) {
throw new Error('ListTreeNode[addNode]: ' +
'The icon|symbolIconStyle and directory name cannot be empty at the same time.');
return this;
}
if (nodeParam.currentNodeId === this.ROOT_NODE_ID || nodeParam.currentNodeId === null) {
throw new Error('ListTreeNode[addNode]: currentNodeId can not be -1 or null.');
return this;
}
if (nodeParam.currentNodeId !== undefined) {
this.nodeIdList.push(nodeParam.currentNodeId);
}
if (nodeParam.parentNodeId !== undefined) {
if (nodeParam.currentNodeId !== undefined) {
addNodeResult =
this.listNodeDataSource.addNode(nodeParam.parentNodeId, nodeParam.currentNodeId, nodeParam, this.initBuild);
}
}
if (!addNodeResult) {
return this;
}
if (!this.initBuild && nodeParam.parentNodeId !== undefined) {
let newNodeId: number = this.nodeIdList[this.nodeIdList.length - 1];
this.listNodeDataSource.refreshData(
MenuOperation.ADD_NODE,
nodeParam.parentNodeId,
[newNodeId]
);
}
return this;
}
}
/**
* this interface is called when a secondaryTitle needs to be updated.
*
* @Param parentId ID of the parent node.
* @Param parentSubTitle secondaryTitle of parent node.
* @Param currentSubTitle secondaryTitle of current node.
*
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public refreshNode(parentId: number, parentSubTitle: ResourceStr, CurrentSubtitle: ResourceStr): void {
this.listNodeDataSource.setNodeSubtitlePara(parentId, parentSubTitle, CurrentSubtitle);
}
/**
* After the initialization is complete by calling the addNode interface,
* call this interface to complete initialization.
*
* This interface must be called when you finish initializing the ListTreeViewV2 by addNode.
* @syscap SystemCapability.ArkUI.ArkUI.Full
* @since 26
*/
public buildDone(): void {
this.listNodeDataSource.initSection();
this.listNodeDataSource.delayInit();
this.listNodeDataSource.updateAllChildNum();
delaySortNodeIdList(this.nodeIdList);
this.initBuild = false;
}
}
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0;
}
public getData(index: number): NodeInfo | undefined {
return undefined;
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number | undefined): void {
if (index === undefined) {
return;
}
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
})
}
}
/**
* delay update all parentnodes childNum
*
* @param isAdd whether addNode or delete node
* @param count node count
* @param nodeIdNodeItemMap nodeId and nodeItem relation map
* @param updateNodeIdList nodeId list whose childNum need update
*/
function delayUpdateParentChildNum(isAdd: boolean, count: number,
nodeIdNodeItemMap: Map<number, NodeItem>, updateNodeIdList: Array<number>): void {
let taskId: number = setTimeout(() => {
updateNodeIdList.forEach((parentNodeId) => {
updateParentChildNumHandler(parentNodeId, nodeIdNodeItemMap, isAdd, count);
});
clearTimeout(taskId);
}, DELAY_TIME);
}
/**
* delay update all parentnodes child number handler
*
* @param parentNodeId parent node id
* @param nodeIdNodeItemMap nodeId and nodeItem relation map
* @param isAdd whether addNode or delete node
* @param count node count
*/
function updateParentChildNumHandler(parentNodeId: number, nodeIdNodeItemMap: Map<number, NodeItem>,
isAdd: boolean, count: number) {
let tmpParentNodeId: number = parentNodeId;
while (tmpParentNodeId >= 0) {
if (nodeIdNodeItemMap.has(tmpParentNodeId)) {
let parent: NodeItem = nodeIdNodeItemMap.get(tmpParentNodeId) as NodeItem;
parent.getChildNodeInfo().allChildNum =
isAdd ? parent.getChildNodeInfo().allChildNum + count : parent.getChildNodeInfo().allChildNum - count;
tmpParentNodeId = parent.parentNodeId;
} else {
hilog.error(LOG_CODE, TAG, 'updateParentChildNumHandler: parent node not found');
break;
}
}
}
/**
* delay sort nodeId list
*
* @param nodeIdList nodeId list
*/
function delaySortNodeIdList(nodeIdList: Array<number>): void {
let taskId: number = setTimeout(() => {
nodeIdList.sort((a, b) => a - b);
clearTimeout(taskId);
}, DELAY_TIME);
}
class ListNodeDataSource extends BasicDataSource {
public readonly ROOT_NODE_ID = -1;
public _root: NodeItem = new NodeItem(emptyNodeInfo);
private readonly maxNodeLevel: number = 50;
private readonly MAX_CN_LENGTH: number = 254;
private readonly MAX_EN_LENGTH: number = 255;
private readonly INITIAL_INVALID_VALUE = -1;
public listNode: NodeInfo[] = [];
public loadedListNode: NodeInfo[] = [];
public nodeIdNodeItemMap: Map<number, NodeItem> = new Map<number, NodeItem>();
public nodeIdNodeParamMap: Map<number, NodeParamV2> = new Map<number, NodeParamV2>();
public lastIndex: number = -1;
public thisIndex: number = -1;
private modifyNodeIndex: number = -1;
public modifyNodeId: number = -1;
private currentOperation?: MenuOperation;
private expandAndCollapseInfo: Map<number, NodeStatus> = new Map<number, NodeStatus>();
public loadedNodeIdAndIndexMap: Map<number, number> = new Map<number, number>();
public nodeIdAndNodeIndexMap: Map<number, number> = new Map<number, number>();
private isTouchDown: boolean = false;
private appEventBus: TreeListenerV2 = TreeListenerManagerV2.getInstance().getTreeListener();
/* parameter of the drag event. */
private isInnerDrag: boolean = false; // Judge whether it is an internal drag event.
// It is used to handle events(For example, prevent press events) during global drag.
private isDrag: boolean = false;
private draggingCurrentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the current ID of the dragged node.
private draggingParentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the parent ID of the dragged node.
private currentNodeInfo: NodeInfo | null = null; // To solve the problem of currentIndex missed in onDrop event.
private listItemOpacity: number = 1; // It is used to set the opacity of the node when dragged.
private lastPassIndex: number = this.INITIAL_INVALID_VALUE; // record the last passing node index in drag.
private lastPassId?: number = this.INITIAL_INVALID_VALUE; // record the last passing node Id in drag.
private thisPassIndex: number = this.INITIAL_INVALID_VALUE; // record the current passing node in drag.
// record last passing node in delay expand event.
private lastDelayExpandIndex: number = this.INITIAL_INVALID_VALUE;
private timeoutExpandId: number = this.INITIAL_INVALID_VALUE;
private lastTimeoutExpandId: number = this.INITIAL_INVALID_VALUE;
private clearTimeoutExpandId: number = this.INITIAL_INVALID_VALUE;
private timeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
private lastTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
private clearTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
// record last passing node in HighLight event.
private lastDelayHighLightIndex: number = this.INITIAL_INVALID_VALUE;
//record last passing node Id in HighLight event.
private lastDelayHighLightId: number = this.INITIAL_INVALID_VALUE;
private nodeIdAndSubtitleMap: Map<number, ResourceStr> = new Map<number, ResourceStr>();
private flag: Flag = Flag.NONE;
private selectedParentNodeId: number = this.INITIAL_INVALID_VALUE;
private selectedParentNodeSubtitle: ResourceStr = '';
private insertNodeSubtitle: ResourceStr = '';
private currentFocusNodeId: number = this.INITIAL_INVALID_VALUE;
private lastFocusNodeId: number = this.INITIAL_INVALID_VALUE;
private addFocusNodeId: number = this.INITIAL_INVALID_VALUE;
private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance();
public updateNodeIdList: number[] = [];
public readonly FLAG_LINE: FlagLine = {
flagLineHeight: FLAG_LINE_HEIGHT,
flagLineColor: $r('sys.color.ohos_id_color_emphasize'),
xOffset: X_OFF_SET,
yTopOffset: Y_OFF_SET,
yBottomOffset: Y_BOTTOM_OFF_SET,
yBasePlateOffset: Y_BASE_PLATE_OFF_SET,
}
private readonly DRAG_POPUP: DragPopup = {
floorConstraintSize: { minWidth: FLOOR_MIN_WIDTH, maxWidth: FLOOR_MAX_WIDTH },
textConstraintSize: {
minWidth1: TEXT_MIN_WIDTH,
maxWidth1: TEXT_MAX_WIDTH,
minWidth2: MIN_WIDTH,
maxWidth2: MAX_WIDTH,
},
padding: { left: $r('sys.float.padding_level4'), right: $r('sys.float.padding_level4') },
backgroundColor: $r('sys.color.comp_background_primary'),
height: GRAG_POP_UP_HEIGHT,
shadow: {
radius: $r('sys.float.ohos_id_default_shadow_m'),
color: $r('sys.color.ohos_id_color_dialog_bg'),
offsetX: 0,
offsetY: 0,
},
borderRadius: $r('sys.float.ohos_id_corner_radius_default_s'),
fontColor: this.treeViewTheme.primaryTitleFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Regular,
imageOpacity: $r('sys.float.ohos_id_alpha_content_fourth')
};
private readonly subTitle: SubTitleStyle = {
normalFontColor: this.treeViewTheme.secondaryTitleFontColor,
highLightFontColor: $r('sys.color.ohos_id_color_primary_contrary'),
fontSize: $r('sys.float.ohos_id_text_size_body2'),
fontWeight: FontWeight.Regular,
margin: { left: $r('sys.float.padding_level2'), right: $r('sys.float.padding_level12') }
}
constructor() {
super();
this._root.nodeLevel = -1;
this.nodeIdNodeItemMap.set(-1, this._root);
this.nodeIdNodeParamMap.set(-1, emptyNodeInfo);
}
private checkIndex(index: number): boolean {
if (index < 0 || index >= this.listNode.length) {
hilog.error(LOG_CODE, TAG, 'check index fail');
return false;
}
return true;
}
public changeNodeColor(index: number, color: ResourceColor | undefined): void {
if (!this.checkIndex(index)) {
return;
}
this.listNode[index].setNodeColor(color);
this.listNode[index].setNodeBorder(false);
}
private getNodeColor(index: number): ResourceColor {
return this.listNode[index].getNodeColor();
}
private handleFocusEffect(index: number, isClearFocusStatus: boolean): void {
if (this.listNode[index].getNodeIsShow()) {
this.listNode[index].setNodeBorder(isClearFocusStatus);
}
}
public setImageSource(index: number, interactionStatus: InteractionStatus): void {
if (!this.checkIndex(index)) {
return;
}
let nodeInfo: NodeInfo = this.listNode[index];
nodeInfo.setIsSelected(interactionStatus === InteractionStatus.SELECTED ||
interactionStatus === InteractionStatus.EDIT || interactionStatus === InteractionStatus.FINISH_EDIT);
if (nodeInfo.getNodeItem().mainTitleNode !== null && interactionStatus !== InteractionStatus.DRAG_INSERT &&
interactionStatus !== InteractionStatus.FINISH_DRAG_INSERT) {
nodeInfo.getNodeItem().mainTitleNode?.setMainTitleSelected(interactionStatus === InteractionStatus.SELECTED ||
interactionStatus === InteractionStatus.FINISH_EDIT);
}
if (nodeInfo.getNodeItem().imageNode !== null) {
nodeInfo.getNodeItem().imageNode?.setImageSource(interactionStatus);
}
}
private setImageCollapseSource(index: number, interactionStatus: InteractionStatus): void {
let nodeInfo: NodeInfo = this.listNode[index];
if (nodeInfo.getNodeItem().imageCollapse !== undefined) {
nodeInfo.getNodeItem().imageCollapse = CollapseImageNodeFlyweightFactory.getCollapseImageNode(interactionStatus,
this.expandAndCollapseInfo.get(nodeInfo.getNodeCurrentNodeId()), nodeInfo.getNodeItem().imageCollapse?.type);
}
}
public clearLastIndexStatus(): void {
if (!this.checkIndex(this.lastIndex)) {
return;
}
this.setImageSource(this.lastIndex, InteractionStatus.NORMAL);
this.changeNodeColor(this.lastIndex, this.listNode[this.lastIndex].getNodeStatus().normal);
this.handleFocusEffect(this.lastIndex, false);
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[this.lastIndex].getNodeCurrentNodeId()));
}
private loadedListNodeFunction(): void {
let index: number = 0;
this.loadedNodeIdAndIndexMap.clear();
this.nodeIdAndNodeIndexMap.clear();
this.loadedListNode.splice(0, this.loadedListNode.length);
for (let i: number = 0; i < this.listNode.length; i++) {
this.nodeIdAndNodeIndexMap.set(this.listNode[i].getNodeCurrentNodeId(), i);
if (this.listNode[i].getNodeIsShow()) {
this.loadedNodeIdAndIndexMap.set(this.listNode[i].getNodeCurrentNodeId(), index++);
this.loadedListNode.push(this.listNode[i]);
}
}
}
private changeNodeStatus(clickIndex: number): void {
if (clickIndex >= this.listNode.length) {
hilog.error(LOG_CODE, TAG, 'changeNodeStatus clickIndex error.');
return;
}
let thisIndex: number = clickIndex;
let nodeId: number = this.listNode[clickIndex].getNodeCurrentNodeId();
if (this.expandAndCollapseInfo.get(nodeId) === NodeStatus.EXPAND) {
this.expandAndCollapseInfo.set(nodeId, NodeStatus.COLLAPSE);
this.listNode[thisIndex].getNodeItem()
.imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.COLLAPSE,
this.listNode[thisIndex].getNodeItem().imageCollapse?.isCollapse);
} else if (this.expandAndCollapseInfo.get(nodeId) === NodeStatus.COLLAPSE) {
this.expandAndCollapseInfo.set(nodeId, NodeStatus.EXPAND);
this.listNode[thisIndex].getNodeItem()
.imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.EXPAND,
this.listNode[thisIndex].getNodeItem().imageCollapse?.isCollapse);
}
}
private handleExpandAndCollapse(clickIndex: number, isRefreshList: boolean): void {
if (clickIndex >= this.listNode.length) {
hilog.error(LOG_CODE, TAG, 'handleExpandAndCollapse clickIndex error.');
return;
}
let thisIndex: number = clickIndex;
let nodeId: number = this.listNode[thisIndex].getNodeCurrentNodeId();
if (!this.expandAndCollapseInfo.has(nodeId)) {
return;
}
let rootNodeStatus: NodeStatus | undefined = this.expandAndCollapseInfo.get(nodeId);
if (this.listNode[thisIndex].getChildNodeInfo().isHasChildNode && rootNodeStatus === NodeStatus.COLLAPSE) {
for (let i: number = 0; i < this.listNode[thisIndex].getChildNodeInfo().allChildNum; i++) {
if (this.listNode[thisIndex + 1 + i] === undefined) {
return;
}
this.listNode[thisIndex + 1 + i].setNodeIsShow(false);
this.listNode[thisIndex + 1 + i].setListItemHeight(LIST_ITEM_HEIGHT_NONE);
}
this.loadedListNodeFunction();
this.notifyDataReload();
return;
}
let childNum: number[] | null = new Array(this.listNode[thisIndex].getChildNodeInfo().childNum);
childNum[0] = thisIndex + 1;
let index: number = 1;
while (index < this.listNode[thisIndex].getChildNodeInfo().childNum) {
childNum[index] = childNum[index - 1] + this.listNode[childNum[index - 1]].getChildNodeInfo().allChildNum + 1;
index++;
}
if (rootNodeStatus === NodeStatus.EXPAND) {
for (let i: number = 0; i < childNum.length; i++) {
if (this.listNode[childNum[i]] === undefined) {
return;
}
this.listNode[childNum[i]].setNodeIsShow(true);
this.listNode[childNum[i]].setListItemHeight(LIST_ITEM_HEIGHT);
let nodeId: number = this.listNode[childNum[i]].getNodeCurrentNodeId();
if (this.expandAndCollapseInfo.get(nodeId) === NodeStatus.EXPAND) {
this.handleExpandAndCollapse(childNum[i], false);
}
}
}
childNum = null;
if (isRefreshList) {
this.loadedListNodeFunction();
this.notifyDataReload();
}
}
/**
* update all parentNodes childNum
*/
public updateAllChildNum(): void {
delayUpdateParentChildNum(true, 1, this.nodeIdNodeItemMap, this.updateNodeIdList);
}
private resetData(listNode: Array<NodeInfo>): void {
listNode.splice(0, listNode.length);
this.loadedNodeIdAndIndexMap.clear();
this.loadedListNode.splice(0, this.loadedListNode.length);
this.nodeIdAndNodeIndexMap.clear();
this.nodeIdAndSubtitleMap.clear();
}
private initHandler(listNode: Array<NodeInfo>, startLevel: number, endLevel?: number): void {
let index: number = 0;
let listIndex: number = 0;
this.resetData(listNode);
try {
this.traverseSectionNodeDF((node: NodeItem): boolean => {
if (node.getCurrentNodeId() >= 0 && this.nodeIdNodeParamMap.has(node.getCurrentNodeId())) {
let nodeInfo: NodeInfo =
new NodeInfo(node, this.nodeIdNodeParamMap.get(node.getCurrentNodeId()) as NodeParamV2);
nodeInfo.addImageCollapse(node.getChildNodeInfo().isHasChildNode);
listNode.push(nodeInfo);
this.nodeIdAndNodeIndexMap.set(nodeInfo.getNodeCurrentNodeId(), listIndex++);
index = this.nodeDFHandler(nodeInfo, index);
}
return false;
}, this._root, startLevel, endLevel);
} catch (err) {
hilog.error(LOG_CODE, TAG, 'traverseSectionNodeDF function callbacks error.');
this.resetData(listNode);
}
}
private nodeDFHandler(nodeInfo: NodeInfo, index: number): number {
if (nodeInfo.getChildNodeInfo().isHasChildNode) {
this.expandAndCollapseInfo.set(nodeInfo.getNodeCurrentNodeId(), NodeStatus.COLLAPSE);
}
if (nodeInfo.getNodeIsShow()) {
this.loadedNodeIdAndIndexMap.set(nodeInfo.getNodeCurrentNodeId(), index++);
this.loadedListNode.push(nodeInfo);
}
if (nodeInfo.getIsFolder()) {
if (nodeInfo.getNodeInfoData().secondaryTitle !== undefined) {
this.nodeIdAndSubtitleMap.set(
nodeInfo.getNodeCurrentNodeId(),
nodeInfo.getNodeInfoData().secondaryTitle as ResourceStr
);
} else {
this.nodeIdAndSubtitleMap.set(nodeInfo.getNodeCurrentNodeId(), '');
}
}
return index;
}
/**
* update delay init all nodes
*/
public delayInit(): void {
let timeId: number = setTimeout(() => {
let listNode: NodeInfo[] = [];
this.initHandler(listNode, 0);
this.listNode.splice(0, this.listNode.length);
this.listNode.push(...listNode);
this.listNode.forEach((value: NodeInfo, index: number) => {
this.notifyDataDelete(index);
this.notifyDataAdd(index);
});
clearTimeout(timeId);
}, DELAY_TIME);
}
/**
* update delay init some nodes
*/
public initSection(): void {
this.initHandler(this.listNode, 0, 1);
}
public setClickIndex(index: number): void {
this.thisIndex = index;
}
public getClickNodeId(): number {
if (!this.checkIndex(this.thisIndex)) {
return -1;
}
return this.listNode[this.thisIndex].getNodeCurrentNodeId();
}
public expandAndCollapseNode(clickIndex: number): void {
this.changeNodeStatus(clickIndex);
this.handleExpandAndCollapse(clickIndex, true);
}
public getIsTouchDown(): boolean {
return this.isTouchDown;
}
public getLastIndex(): number {
return this.lastIndex;
}
public findIndex(currentNodeId: number): number {
let thisIndex: number = -1;
if (this.nodeIdAndNodeIndexMap.has(currentNodeId)) {
thisIndex = this.nodeIdAndNodeIndexMap.get(currentNodeId) as number;
}
return thisIndex;
}
public handleEventDrag(index: number): void {
if (!this.checkIndex(index)) {
return;
}
this.setImageSource(index, InteractionStatus.NORMAL);
this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal);
this.handleFocusEffect(index, false);
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[index].getNodeCurrentNodeId()));
}
public handleEvent(event: Event, index: number): void {
/* Return while the event is dragging event. */
if (this.isDrag) {
return;
}
if (!this.checkIndex(index)) {
return;
}
if (event === Event.TOUCH_DOWN || event === Event.TOUCH_UP || event === Event.MOUSE_BUTTON_RIGHT) {
if (index !== this.lastIndex) {
this.clearLastIndexStatus();
}
}
this.eventHandler(index, event);
}
private eventHandler(index: number, event: Event): void {
let lazyForEachIndex: number =
this.loadedNodeIdAndIndexMap.get(this.listNode[index].getNodeCurrentNodeId()) as number;
switch (event) {
case Event.TOUCH_DOWN:
this.isTouchDown = true;
this.changeNodeColor(index, this.listNode[index].getNodeStatus().press);
this.notifyDataChange(lazyForEachIndex);
break;
case Event.TOUCH_UP: {
this.touchUpHandler(index, lazyForEachIndex);
break;
}
case Event.HOVER:
if (this.getNodeColor(index) !== this.listNode[index].getNodeStatus().selected) {
this.changeNodeColor(index, this.listNode[index].getNodeStatus().hover);
this.notifyDataChange(lazyForEachIndex);
}
break;
case Event.HOVER_OVER:
if (this.getNodeColor(index) !== this.listNode[index].getNodeStatus().selected) {
this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal);
this.notifyDataChange(lazyForEachIndex);
}
break;
case Event.FOCUS:
this.handleFocusEffect(index, true);
this.notifyDataChange(lazyForEachIndex);
break;
case Event.BLUR:
this.handleFocusEffect(index, false);
this.notifyDataChange(lazyForEachIndex);
break;
case Event.MOUSE_BUTTON_RIGHT:
this.lastIndex = index;
this.finishEditing();
break;
case Event.DRAG:
this.isTouchDown = false;
let nodeInfo: NodeInfo = this.listNode[index];
this.setImageSource(index, InteractionStatus.SELECTED);
this.lastIndex = index;
this.changeNodeColor(index, nodeInfo.getNodeStatus().selected);
this.notifyDataChange(lazyForEachIndex);
break;
default:
break;
}
}
private touchUpHandler(index: number, lazyForEachIndex: number): void {
if (this.isInnerDrag) {
this.isInnerDrag = false;
}
this.isTouchDown = false;
let nodeInfo: NodeInfo = this.listNode[index];
this.setImageSource(index, InteractionStatus.SELECTED);
nodeInfo.setFontColor(this.treeViewTheme.primaryTitleFontColor);
this.lastIndex = index;
this.changeNodeColor(index, nodeInfo.getNodeStatus().selected);
this.notifyDataChange(lazyForEachIndex);
}
private notificationNodeInfo(addNodeId: number, operation: MenuOperation | undefined): void {
if (operation === MenuOperation.MODIFY_NODE) {
let modifyNodeInfo: NodeInfo = this.listNode[this.modifyNodeIndex];
let backParamModify: CallbackParamV2 = {
currentNodeId: modifyNodeInfo?.getNodeCurrentNodeId(),
parentNodeId: modifyNodeInfo?.getNodeParentNodeId(),
};
this.appEventBus.emitNodeModify(backParamModify);
} else if (operation === MenuOperation.ADD_NODE) {
let addNodeInfo: NodeInfo = this.listNode[addNodeId];
if (addNodeInfo === undefined) {
return;
}
let icon: Resource | string | undefined = (addNodeInfo.getNodeItem().imageNode !== undefined) ?
addNodeInfo.getNodeItem().imageNode?.source : undefined;
let selectedIcon: Resource | string | undefined = (addNodeInfo.getNodeItem().imageNode !== undefined) ?
addNodeInfo.getNodeItem().imageNode?.selectedSource : undefined;
let editIcon: Resource | string | undefined = (addNodeInfo.getNodeItem().imageNode !== undefined) ?
addNodeInfo.getNodeItem().imageNode?.editSource : undefined;
let callbackParam: CallbackParamV2 = {
currentNodeId: addNodeInfo?.getNodeCurrentNodeId(),
parentNodeId: addNodeInfo?.getNodeParentNodeId(),
};
this.appEventBus.emitNodeAdd(callbackParam);
}
}
public finishEditing(): void {
if (this.modifyNodeIndex !== -1) {
this.setImageSource(this.modifyNodeIndex, InteractionStatus.FINISH_EDIT);
this.setImageCollapseSource(this.modifyNodeIndex, InteractionStatus.FINISH_EDIT);
this.listNode[this.modifyNodeIndex].setIsModify(false);
this.listNode[this.modifyNodeIndex].setTitleAndInputTextStatus(false);
this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation);
this.notifyDataChange(this.modifyNodeIndex);
}
}
public setItemVisibilityOnEdit(nodeId: number, operation: MenuOperation): void {
let index: number = -1;
if (nodeId === -1) {
return;
}
if (operation === MenuOperation.MODIFY_NODE) {
for (let i: number = 0; i < this.listNode.length; i++) { // nodeId to find index
if (this.listNode[i]?.getNodeCurrentNodeId() === nodeId) {
index = i;
break;
}
}
let nodeInfo: NodeInfo = this.listNode[index];
if (nodeInfo === undefined) {
return;
}
nodeInfo.setIsModify(true);
if (nodeInfo.getNodeItem().mainTitleNode === null) {
return; // no title
}
this.currentOperation = MenuOperation.MODIFY_NODE;
nodeInfo.setTitleAndInputTextStatus(true);
this.setImageSource(index, InteractionStatus.EDIT);
this.setImageCollapseSource(index, InteractionStatus.EDIT);
this.modifyNodeIndex = index;
if (nodeInfo.getNodeItem().inputText) {
if (nodeInfo.getNodeItem().imageCollapse !== null) {
nodeInfo.getNodeItem().inputText.rightMargin =
$r('sys.float.ohos_id_text_paragraph_margin_xs');
} else {
nodeInfo.getNodeItem().inputText.rightMargin =
$r('sys.float.ohos_id_elements_margin_horizontal_m');
}
}
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeId));
}
index = nodeId;
if (operation === MenuOperation.COMMIT_NODE) {
let nodeInfo: NodeInfo = this.listNode[index];
if (nodeInfo === undefined) {
return;
}
nodeInfo.setTitleAndInputTextStatus(false);
nodeInfo.setIsModify(false);
this.setImageSource(index, InteractionStatus.FINISH_EDIT);
this.setImageCollapseSource(index, InteractionStatus.FINISH_EDIT);
this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation);
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeInfo?.getNodeCurrentNodeId()));
}
}
public setPopUpInfo(popUpType: PopUpType, inputError: InputError, isShow: boolean, index: number): void {
if (!this.checkIndex(index)) {
return;
}
let nodeInfo: NodeInfo = this.listNode[index];
if (nodeInfo === undefined) {
return;
}
nodeInfo.setPopUpIsShow(isShow);
// this.listNode index to lazyForEach index.
let lazyForEachIndex: number = this.loadedNodeIdAndIndexMap.get(nodeInfo.getNodeCurrentNodeId()) as number;
if (!isShow) {
this.notifyDataChange(lazyForEachIndex);
return;
}
if (popUpType === PopUpType.HINTS) {
if (nodeInfo.getNodeItem().mainTitleNode !== null) {
nodeInfo.setPopUpText(nodeInfo.getNodeItem().mainTitleNode?.title);
} else {
nodeInfo.setPopUpText('');
nodeInfo.setPopUpIsShow(false);
}
nodeInfo.setPopUpEnableArrow(false);
nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_background'));
nodeInfo.setPopUpTextColor(this.treeViewTheme.secondaryTitleFontColor);
} else if (popUpType === PopUpType.WARNINGS) {
if (nodeInfo.getNodeItem().inputText !== null) {
if (inputError === InputError.INVALID_ERROR) {
nodeInfo.setPopUpText('invalid error');
} else if (inputError === InputError.LENGTH_ERROR) {
nodeInfo.setPopUpText('length error');
}
nodeInfo.setPopUpEnableArrow(true);
nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_help_tip_bg'));
nodeInfo.setPopUpTextColor($r('sys.color.ohos_id_color_text_hint_contrary'));
}
}
this.notifyDataChange(lazyForEachIndex);
}
public setShowPopUpTimeout(timeout: number, index: number): void {
if (!this.checkIndex(index)) {
return;
}
if (this.listNode[index].getNodeItem().mainTitleNode !== null) {
this.listNode[index].getNodeItem().mainTitleNode.popUpTimeout = timeout;
}
let lazyForEachIndex: number =
this.loadedNodeIdAndIndexMap.get(this.listNode[index].getNodeCurrentNodeId()) as number;
this.notifyDataChange(lazyForEachIndex);
}
public setMainTitleNameOnEdit(index: number, text: string): void {
this.modifyNodeIndex = index;
if (this.listNode[index].getNodeItem().mainTitleNode !== null) {
this.listNode[index].getNodeItem().mainTitleNode.title = text;
}
}
public totalCount(): number {
return this.loadedNodeIdAndIndexMap.size;
}
public getData(index: number): NodeInfo | undefined {
if (index < 0 || index >= this.loadedListNode.length) {
return undefined;
}
return this.loadedListNode[index];
}
public addData(index: number, data: NodeInfo): void {
if (!this.checkIndex(index)) {
return;
}
this.listNode.splice(index, 0, data);
this.nodeIdAndNodeIndexMap.set(data.getNodeCurrentNodeId(), index);
this.loadedListNodeFunction();
this.notifyDataAdd(index);
}
public pushData(data: NodeInfo): void {
this.listNode.push(data);
this.nodeIdAndNodeIndexMap.set(data.getNodeCurrentNodeId(), this.listNode.length);
this.loadedListNodeFunction();
this.notifyDataAdd(this.listNode.length - 1);
}
public setIsInnerDrag(isInnerDrag: boolean): void {
this.isInnerDrag = isInnerDrag;
}
public getIsInnerDrag(): boolean {
return this.isInnerDrag;
}
public setIsDrag(isDrag: boolean): void {
this.isDrag = isDrag;
}
public getIsDrag(): boolean {
return this.isDrag;
}
public setCurrentNodeInfo(currentNodeInfo: NodeInfo | undefined): void {
if (currentNodeInfo === undefined) {
return;
}
this.currentNodeInfo = currentNodeInfo;
}
public getCurrentNodeInfo(): NodeInfo | null {
return this.currentNodeInfo;
}
public setDraggingParentNodeId(draggingParentNodeId: number | undefined): void {
if (draggingParentNodeId === undefined) {
return;
}
this.draggingParentNodeId = draggingParentNodeId;
}
public getDraggingParentNodeId(): number {
return this.draggingParentNodeId;
}
public getDraggingCurrentNodeId(): number {
return this.draggingCurrentNodeId;
}
public setDraggingCurrentNodeId(draggingCurrentNodeId: number | undefined): void {
if (draggingCurrentNodeId === undefined) {
return;
}
this.draggingCurrentNodeId = draggingCurrentNodeId;
}
public setListItemOpacity(listItemOpacity: number): void {
this.listItemOpacity = listItemOpacity;
}
public getListItemOpacity(item: NodeInfo): number {
return item.getNodeCurrentNodeId() === this.getDraggingCurrentNodeId() ? this.listItemOpacity : 1;
}
public getDragPopupPara(): DragPopup {
return this.DRAG_POPUP;
}
public setLastPassIndex(lastPassIndex: number): void {
this.lastPassIndex = lastPassIndex;
}
public getLastPassIndex(): number {
return this.lastPassIndex;
}
public getIsParentOfInsertNode(insertNodeId: number | undefined): boolean {
if (this.currentNodeInfo === null || insertNodeId === undefined) {
return false;
}
let selectedNodeItem: NodeItem = this.currentNodeInfo.getNodeInfoNode();
let parentId: number = selectedNodeItem.currentNodeId;
let insertParentId: number | undefined = this.nodeIdNodeItemMap.get(insertNodeId as number)?.parentNodeId;
while (insertParentId !== undefined && insertParentId !== -1) {
if (parentId === insertParentId) {
return true;
} else {
insertParentId = this.nodeIdNodeItemMap.get(insertParentId as number)?.parentNodeId;
}
}
return false;
}
public setPassIndex(thisPassIndex: number): void {
this.thisPassIndex = thisPassIndex;
}
public getPassIndex(): number {
return this.thisPassIndex;
}
public clearTimeOutAboutDelayHighLightAndExpand(currentIndex: number): void {
if (this.lastPassId !== this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId as number)) {
let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId as number) as number;
this.listNode.forEach((value) => {
if (value.getNodeCurrentNodeId() === this.lastPassId) {
value.setCanShowFlagLine(false);
return;
}
})
this.notifyDataChange(index);
}
if ((this.lastTimeoutHighLightId !== this.INITIAL_INVALID_VALUE &&
this.clearTimeoutHighLightId !== this.lastTimeoutHighLightId)) {
clearTimeout(this.lastTimeoutHighLightId);
if (this.lastDelayHighLightIndex !== this.INITIAL_INVALID_VALUE) {
this.clearHighLight(this.lastDelayHighLightIndex);
let index: number = this.loadedNodeIdAndIndexMap
.get(this.listNode[this.lastDelayHighLightIndex].getNodeCurrentNodeId()) as number;
this.notifyDataChange(index);
}
this.clearTimeoutHighLightId = this.lastTimeoutHighLightId;
}
this.lastTimeoutHighLightId = this.timeoutHighLightId;
this.lastDelayHighLightIndex = currentIndex;
if ((this.lastTimeoutExpandId !== this.INITIAL_INVALID_VALUE &&
this.clearTimeoutExpandId !== this.lastTimeoutExpandId)) {
clearTimeout(this.lastTimeoutExpandId);
this.clearTimeoutExpandId = this.lastTimeoutExpandId;
}
this.lastTimeoutExpandId = this.timeoutExpandId;
this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
}
public clearHighLight(currentIndex: number): void {
if (!this.checkIndex(currentIndex)) {
return;
}
this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().normal);
this.changeNodeHighLightColor(currentIndex, false);
this.setImageSource(currentIndex, InteractionStatus.FINISH_DRAG_INSERT);
this.setImageCollapseSource(currentIndex, InteractionStatus.FINISH_DRAG_INSERT);
this.listNode[currentIndex].setIsHighLight(false);
}
private changeNodeHighLightColor(index: number, isHighLight: boolean): void {
if (this.listNode[index].getNodeItem().mainTitleNode && this.listNode[index].getIsShowTitle()) {
this.listNode[index].getNodeItem().mainTitleNode?.setMainTitleHighLight(isHighLight);
}
}
getAccessibleTitle(insertNodeCurrentNodeId: number | undefined): string {
let accessibleTitleList: string[] = [];
while (insertNodeCurrentNodeId !== -1) {
if (insertNodeCurrentNodeId === undefined) {
return '';
}
let insertNodeParentNodeId = this.findParentNodeId(insertNodeCurrentNodeId);
let nodeItem = this.nodeIdNodeItemMap.get(insertNodeParentNodeId as number);
if (nodeItem === undefined || insertNodeParentNodeId === undefined) {
return '';
}
let primaryTitle = this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData()?.primaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData().primaryTitle;
let secondaryTitle = this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData()?.secondaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData().secondaryTitle;
let primaryTitleText = this.getAccessibleTitleText(primaryTitle);
let secondaryTitleText = this.getAccessibleTitleText(secondaryTitle);
accessibleTitleList.unshift(`${primaryTitleText}, ${secondaryTitleText}`);
insertNodeCurrentNodeId = nodeItem.currentNodeId;
}
return accessibleTitleList.join(',');
}
getPlaceAccessibleTitle(insertNodeCurrentNodeId: number | undefined): string {
if (insertNodeCurrentNodeId === undefined) {
return '';
}
let insertNodeParentNodeId = this.findParentNodeId(insertNodeCurrentNodeId);
if (insertNodeParentNodeId === -1) {
let accessibleTitleList: string[] = [];
let nodeItem = this.nodeIdNodeItemMap.get(insertNodeCurrentNodeId as number);
if (nodeItem === undefined || insertNodeParentNodeId === undefined) {
return '';
}
let primaryTitle = this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData()?.primaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData().primaryTitle;
let secondaryTitle = this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData()?.secondaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData().secondaryTitle;
let primaryTitleText = this.getAccessibleTitleText(primaryTitle);
let secondaryTitleText = this.getAccessibleTitleText(secondaryTitle);
accessibleTitleList.unshift(`${primaryTitleText}, ${secondaryTitleText}`);
return accessibleTitleList.join(',');
} else {
let accessibleTitleList: string[] = [];
let currentNodeItem = this.nodeIdNodeItemMap.get(insertNodeCurrentNodeId as number);
if (currentNodeItem === undefined || insertNodeParentNodeId === undefined) {
return '';
}
let primaryTitle = this.getNodeInfoByNodeItem(currentNodeItem).getNodeInfoData()?.primaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(currentNodeItem).getNodeInfoData().primaryTitle;
let secondaryTitle = this.getNodeInfoByNodeItem(currentNodeItem).getNodeInfoData()?.secondaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(currentNodeItem).getNodeInfoData().secondaryTitle;
let primaryTitleText = this.getAccessibleTitleText(primaryTitle);
let secondaryTitleText = this.getAccessibleTitleText(secondaryTitle);
accessibleTitleList.unshift(`${primaryTitleText}, ${secondaryTitleText}`);
while (insertNodeCurrentNodeId !== -1) {
if (insertNodeCurrentNodeId === undefined) {
return '';
}
let insertNodeParentNodeId = this.findParentNodeId(insertNodeCurrentNodeId);
let nodeItem = this.nodeIdNodeItemMap.get(insertNodeParentNodeId as number);
if (nodeItem === undefined || insertNodeParentNodeId === undefined) {
return '';
}
let primaryTitle = this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData()?.primaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData().primaryTitle;
let secondaryTitle = this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData()?.secondaryTitle === undefined
? '' : this.getNodeInfoByNodeItem(nodeItem).getNodeInfoData().secondaryTitle;
let primaryTitleText = this.getAccessibleTitleText(primaryTitle);
let secondaryTitleText = this.getAccessibleTitleText(secondaryTitle);
accessibleTitleList.unshift(`${primaryTitleText}, ${secondaryTitleText}`);
insertNodeCurrentNodeId = nodeItem.currentNodeId;
}
return accessibleTitleList.join(',');
}
}
getDraggingAccessible(allParentNode: number[], insertNodeCurrentNodeId: number | undefined,
insertNodeId: number | undefined): void {
this.getAccessibleTitle(insertNodeId)
if (insertNodeCurrentNodeId === undefined || insertNodeId === undefined) {
return;
}
let parentId: number = this.findParentNodeId(insertNodeId);
let currentPlaceNode: number | undefined = allParentNode.indexOf(insertNodeId) + 2;
let childrenInfo: NodeInfoView[] = this.getClickNodeChildrenInfo(parentId);
let childrenItemId: (number | undefined)[] = childrenInfo.map(item => item.itemId)
let insertNodePosition = childrenItemId.indexOf(insertNodeId) + 2;
if (parentId === -1 && this.getExpandAndCollapseInfo(insertNodeCurrentNodeId) === NodeStatus.COLLAPSE ||
parentId === -1 && this.getExpandAndCollapseInfo(insertNodeCurrentNodeId) === undefined) {
this.sendAccessibility(this.getStringByName('treeview_accessibility_move_node_parent', currentPlaceNode));
} else if (this.getExpandAndCollapseInfo(insertNodeCurrentNodeId) === NodeStatus.EXPAND) {
this.sendAccessibility(this.getStringByName('treeview_accessibility_move_node_child',
this.getAccessibleTitle(insertNodeId), 1));
} else if (parentId !== -1) {
this.sendAccessibility(this.getStringByName('treeview_accessibility_move_node_child',
this.getAccessibleTitle(insertNodeId), insertNodePosition));
}
}
public getStringByName(resName: string, ...args: Array<string | number>): string {
if (resName) {
try {
return getContext()?.resourceManager.getStringByNameSync(resName, ...args);
} catch (error) {
console.error(`Ace SegmentButton getAccessibilityDescription, error: ${error.toString()}`);
}
}
return '';
}
public sendAccessibility(textAnnouncedForAccessibility: string): void {
let eventInfo: accessibility.EventInfo = ({
type: 'announceForAccessibility', // 表示主动播报的事件。
bundleName: (getContext() as common.UIAbilityContext)?.abilityInfo?.bundleName, // 目标应用名;不可缺省。
triggerAction: 'common', // 表示没有特定操作,用于主动聚焦、主动播报等场景。
textAnnouncedForAccessibility: textAnnouncedForAccessibility // 主动播报的内容。
});
accessibility.sendAccessibilityEvent(eventInfo);
}
public getAccessibleTitleText(resource: ResourceStr | string | undefined): string {
let resourceString: string = '';
try {
if (typeof resource === 'string') {
resourceString = resource;
} else {
resourceString = getContext()?.resourceManager?.getStringSync(resource?.id);
}
} catch (error) {
let code: number = (error as BusinessError).code;
let message: string = (error as BusinessError).message;
hilog.error(0x3900, 'Ace', `treeView getAccessibleTitleText error, code: ${code}, message: ${message}`);
}
return resourceString;
}
public setVisibility(flag: Flag, index: number, isOverBorder: boolean, allParentNode: number[]): void {
let isChanged: boolean = (this.thisPassIndex !== index || this.flag !== flag) ? true : false;
this.thisPassIndex = index;
if ((isChanged || isOverBorder) && this.isInnerDrag) {
this.flag = flag;
let currentNodeId: number | undefined = this.getData(index)?.getNodeCurrentNodeId();
let currentNodeLevel: number | undefined = this.getData(index)?.getNodeLevel();
if (currentNodeId !== undefined) {
currentNodeLevel = (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.EXPAND &&
this.flag === Flag.DOWN_FLAG) ? (currentNodeLevel ? currentNodeLevel + 1 : undefined) : currentNodeLevel;
if (this.lastPassId !== this.INITIAL_INVALID_VALUE &&
this.loadedNodeIdAndIndexMap.has(this.lastPassId as number)) {
let lastIndex: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId as number) as number;
this.listNode.forEach((value) => {
if (value.getNodeCurrentNodeId() === this.lastPassId) {
value.setCanShowFlagLine(false);
}
})
this.notifyDataChange(lastIndex);
}
let insertNodeUpNodeId: number | undefined = this.getData(index - 1)?.getNodeCurrentNodeId();
let insertNodeDownNodeId: number | undefined = this.getData(index + 2)?.getNodeCurrentNodeId();
let insertNodeCurrentNodeId: number | undefined = this.getData(index + 1)?.getNodeCurrentNodeId();
let nodeItem = this.nodeIdNodeItemMap.get(insertNodeCurrentNodeId as number);
if (this.flag === Flag.DOWN_FLAG && index < this.totalCount() - 1) {
this.getData(index)?.setCanShowFlagLine(false);
this.getData(index + 1)?.setCanShowFlagLine(true);
this.getData(index)?.setCanShowBottomFlagLine(false);
this.getData(index + 1)?.setFlagLineLeftMargin(currentNodeLevel);
this.notifyDataChange(index);
this.notifyDataChange(index + 1);
this.lastPassId = this.getData(index + 1)?.getNodeCurrentNodeId();
let nodeItem = this.nodeIdNodeItemMap.get(insertNodeCurrentNodeId as number);
if (!nodeItem?.childNodeInfo.isHasChildNode) {
this.getDraggingAccessible(allParentNode, insertNodeCurrentNodeId, insertNodeCurrentNodeId);
} else {
this.getDraggingAccessible(allParentNode, insertNodeCurrentNodeId, insertNodeDownNodeId);
}
} else if (this.flag === Flag.UP_FLAG && index < this.totalCount() - 1) {
this.getData(index)?.setCanShowFlagLine(true);
this.getData(index + 1)?.setCanShowFlagLine(false);
this.getData(index)?.setCanShowBottomFlagLine(false);
this.getData(index)?.setFlagLineLeftMargin(currentNodeLevel);
this.notifyDataChange(index);
this.notifyDataChange(index + 1);
this.lastPassId = this.getData(index)?.getNodeCurrentNodeId();
if (nodeItem?.childNodeInfo.isHasChildNode && nodeItem?.parentNodeId !== -1) {
this.getDraggingAccessible(allParentNode, insertNodeCurrentNodeId, insertNodeCurrentNodeId);
} else if (nodeItem?.childNodeInfo.isHasChildNode && nodeItem?.parentNodeId === -1) {
this.getDraggingAccessible(allParentNode, insertNodeUpNodeId, insertNodeCurrentNodeId);
}
} else if (index >= this.totalCount() - 1) {
if (this.flag === Flag.DOWN_FLAG) {
this.getData(index)?.setCanShowFlagLine(false);
this.getData(index)?.setCanShowBottomFlagLine(true);
} else {
this.getData(index)?.setCanShowFlagLine(true);
this.getData(index)?.setCanShowBottomFlagLine(false);
}
this.getData(index)?.setFlagLineLeftMargin(currentNodeLevel);
this.notifyDataChange(index);
this.lastPassId = this.getData(index)?.getNodeCurrentNodeId();
}
}
}
}
public delayHighLightAndExpandNode(currentIndex: number, currentNodeId: number, showIndex: number): void {
let isChangIndex: boolean = currentIndex !== this.lastDelayExpandIndex ? true : false;
let isOverBorder: boolean | undefined = this.getData(showIndex)?.getIsOverBorder();
let insertNodeId: number = this.listNode[currentIndex + 1]?.getNodeCurrentNodeId();
let insertNodeParentNodeId: number = this.findParentNodeId(currentNodeId);
if (isOverBorder) {
this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
} else {
this.lastDelayExpandIndex = currentIndex;
}
if (isOverBorder || isChangIndex) {
/* highLight node time-out. */
let canDelayHighLight: boolean | undefined = !isOverBorder && (!this.isInnerDrag ||
(this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.COLLAPSE && this.isInnerDrag) ||
(!this.expandAndCollapseInfo.has(currentNodeId) && this.listNode[currentIndex].getIsFolder()));
if (canDelayHighLight) {
/* set hoverState color before highLight. */
this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().hover);
this.notifyDataChange(showIndex);
let delayHighLightTime: number = this.isInnerDrag ? 1000 : 0; // ms
this.timeoutHighLightId = setTimeout(() => {
this.delayHighLight(currentIndex);
this.sendAccessibility(this.getStringByName('treeview_accessibility_move_node_child',
this.getPlaceAccessibleTitle(currentNodeId), 1))
}, delayHighLightTime);
}
if (isOverBorder || (this.lastTimeoutHighLightId !== this.INITIAL_INVALID_VALUE &&
this.clearTimeoutHighLightId !== this.lastTimeoutHighLightId)) {
clearTimeout(this.lastTimeoutHighLightId);
if (this.lastDelayHighLightIndex !== this.INITIAL_INVALID_VALUE) {
this.clearHighLight(this.lastDelayHighLightIndex);
this.notifyDataReload();
}
this.clearTimeoutHighLightId = this.lastTimeoutHighLightId;
}
this.lastTimeoutHighLightId = this.timeoutHighLightId;
this.lastDelayHighLightIndex = currentIndex;
/* alter flagLine and expand node time-out. */
if (!isOverBorder && this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.COLLAPSE) {
let firstChildNodeId: number | undefined =
this.getData(showIndex)?.getNodeInfoNode().children[0]?.currentNodeId;
let delayAlterFlagLineAndExpandNodeTime: number = 2000; // ms
this.timeoutExpandId = setTimeout(() => {
this.clearHighLight(this.lastDelayHighLightIndex);
if (firstChildNodeId !== undefined) {
this.alterFlagLineAndExpandNode(currentIndex, firstChildNodeId);
}
}, delayAlterFlagLineAndExpandNodeTime);
}
if (isOverBorder || (this.lastTimeoutExpandId !== this.INITIAL_INVALID_VALUE &&
this.clearTimeoutExpandId !== this.lastTimeoutExpandId)) {
clearTimeout(this.lastTimeoutExpandId);
this.clearTimeoutExpandId = this.lastTimeoutExpandId;
}
this.lastTimeoutExpandId = this.timeoutExpandId;
}
}
public delayHighLight(currentIndex: number): void {
this.listNode.forEach((value) => {
if (value.getNodeCurrentNodeId() === this.lastPassId) {
value.setCanShowFlagLine(false);
value.setCanShowBottomFlagLine(false);
return;
}
})
this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().highLight);
this.listNode[currentIndex].setIsHighLight(true);
this.changeNodeHighLightColor(currentIndex, true);
this.setImageSource(currentIndex, InteractionStatus.DRAG_INSERT);
this.setImageCollapseSource(currentIndex, InteractionStatus.DRAG_INSERT);
this.notifyDataReload();
}
public alterFlagLineAndExpandNode(currentIndex: number, firstChildNodeId: number): void {
this.listNode.forEach((value) => {
if (value.getNodeCurrentNodeId() === this.lastPassId) {
value.setCanShowFlagLine(false);
value.setCanShowBottomFlagLine(false);
}
})
this.listNode.forEach((value) => {
if (this.isInnerDrag && value.getNodeCurrentNodeId() === firstChildNodeId) {
value.setCanShowFlagLine(true);
}
})
this.changeNodeStatus(currentIndex);
this.handleExpandAndCollapse(currentIndex, true);
this.lastPassId = firstChildNodeId;
}
public hideLastLine(): void {
if (this.lastPassId !== this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId as number)) {
this.listNode.forEach((value) => {
if (value.getNodeCurrentNodeId() === this.lastPassId) {
value.setCanShowFlagLine(false);
value.setCanShowBottomFlagLine(false);
return;
}
})
let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId as number) as number;
this.notifyDataChange(index);
}
}
public clearLastTimeoutHighLight(): void {
if (this.lastTimeoutHighLightId !== this.INITIAL_INVALID_VALUE &&
this.clearTimeoutHighLightId !== this.lastTimeoutHighLightId) {
clearTimeout(this.lastTimeoutHighLightId);
if (this.lastDelayHighLightIndex !== this.INITIAL_INVALID_VALUE) {
this.clearHighLight(this.lastDelayHighLightIndex);
}
}
}
public clearLastTimeoutExpand(): void {
if (this.lastTimeoutExpandId !== this.INITIAL_INVALID_VALUE &&
this.clearTimeoutExpandId !== this.lastTimeoutExpandId) {
clearTimeout(this.lastTimeoutExpandId);
}
}
public getSubtitle(currentNodeId: number): string | undefined {
if (this.nodeIdAndSubtitleMap.has(currentNodeId)) {
if (typeof this.nodeIdAndSubtitleMap.get(currentNodeId) === 'number') {
return this.nodeIdAndSubtitleMap.get(currentNodeId)?.toString();
} else {
return this.nodeIdAndSubtitleMap.get(currentNodeId) as string;
}
} else {
return '';
}
}
public hasSubtitle(currentNodeId: number): boolean {
return this.nodeIdAndSubtitleMap.has(currentNodeId);
}
public initialParameterAboutDelayHighLightAndExpandIndex(): void {
this.lastDelayHighLightIndex = this.INITIAL_INVALID_VALUE;
this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
this.lastPassIndex = this.INITIAL_INVALID_VALUE;
this.draggingCurrentNodeId = this.INITIAL_INVALID_VALUE;
this.flag = Flag.NONE;
}
public refreshSubtitle(insertNodeCurrentNodeId: number): void {
this.nodeIdAndSubtitleMap.set(this.selectedParentNodeId, this.selectedParentNodeSubtitle);
this.nodeIdAndSubtitleMap.set(insertNodeCurrentNodeId, this.insertNodeSubtitle);
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.selectedParentNodeId));
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(insertNodeCurrentNodeId));
}
public setNodeSubtitlePara(
selectedParentNodeId: number,
selectedParentNodeSubtitle: ResourceStr,
insertNodeSubtitle: ResourceStr): void {
this.selectedParentNodeId = selectedParentNodeId;
this.selectedParentNodeSubtitle = selectedParentNodeSubtitle;
this.insertNodeSubtitle = insertNodeSubtitle;
}
public getInsertNodeSubtitle(): ResourceStr {
return this.insertNodeSubtitle;
}
public getExpandAndCollapseInfo(currentNodeId: number): NodeStatus | undefined {
return this.expandAndCollapseInfo.get(currentNodeId);
}
public getLastDelayHighLightId(): number {
return this.lastDelayHighLightId;
}
public setLastDelayHighLightId(): void {
this.listNode.forEach((value, index) => {
if (index === this.lastDelayHighLightIndex) {
this.lastDelayHighLightId = value.getNodeCurrentNodeId();
}
})
}
public setLastPassId(lastPassId: number): void {
this.lastPassId = lastPassId;
}
public setLastDelayHighLightIndex(lastDelayHighLightIndex: number): void {
this.lastDelayHighLightIndex = lastDelayHighLightIndex;
}
/**
* Alter the current node location to a needful position.
* 1.Create an array named 'dragNodeParam' to store dragging node information.
* 2.Delete the dragging node from the tree.
* 3.Add the dragging node to the tree.
*/
public alterDragNode(rearParentNodeId: number, rearCurrentNodeId: number,
dragParentNodeId: number, dragCurrentNodeId: number, frontNodeInfoItem: NodeInfo): void {
let dragNodeParam: DragNodeParam[] = [];
let parentNodeId: number = rearParentNodeId;
let currentNodeId: number = dragCurrentNodeId;
let nodeParam: NodeParamV2 = frontNodeInfoItem.getNodeInfoData();
let nodeInfo: NodeInfo | null = null;
let nodeInfoNode: NodeItem = frontNodeInfoItem.getNodeInfoNode();
let isHighLight: boolean = false;
let insertChildIndex: number = this.INITIAL_INVALID_VALUE;
let currentChildIndex: number = this.INITIAL_INVALID_VALUE;
let isDownFlag: boolean = this.flag === Flag.DOWN_FLAG ? true : false;
currentChildIndex = this.getChildIndex(dragParentNodeId, dragCurrentNodeId);
insertChildIndex = this.getChildIndex(rearParentNodeId, rearCurrentNodeId) + 1;
if (rearParentNodeId !== dragParentNodeId) {
insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex;
} else {
if (insertChildIndex > currentChildIndex) {
insertChildIndex = isDownFlag ? insertChildIndex : insertChildIndex - 1;
} else {
insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex;
}
}
for (let i: number = 0; i < this.listNode.length; i++) {
if (this.listNode[i].getNodeCurrentNodeId() === rearCurrentNodeId) {
isHighLight = this.listNode[i].getIsHighLight();
if (this.flag === Flag.DOWN_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) === NodeStatus.EXPAND) {
parentNodeId = rearCurrentNodeId;
insertChildIndex = 0;
} else if (this.flag === Flag.UP_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) ===
NodeStatus.EXPAND &&
this.listNode[i].getCanShowFlagLine() === false) {
parentNodeId = rearCurrentNodeId;
insertChildIndex = 0;
} else if (isHighLight) {
parentNodeId = rearCurrentNodeId;
insertChildIndex = 0;
}
break;
}
}
let callbackParam: CallbackParamV2 = {
currentNodeId: currentNodeId,
parentNodeId: parentNodeId,
childIndex: insertChildIndex,
}
/* export inner drag node Id. */
this.appEventBus.emitNodeMove(callbackParam);
/* To store dragging node information by the array named 'dragNodeParam'. */
dragNodeParam.push({ parentId: parentNodeId, currentId: currentNodeId, data: nodeParam });
let callback: (node: NodeItem, listNode: NodeInfo[]) => boolean =
(node: NodeItem, listNode: NodeInfo[]): boolean => {
if (node) {
parentNodeId = node.parentNodeId;
currentNodeId = node.currentNodeId;
for (let i: number = 0; i < listNode.length; i++) {
if (listNode[i].getNodeCurrentNodeId() === currentNodeId) {
nodeInfo = listNode[i];
break;
}
}
if (nodeInfo === null) {
return false;
}
let nodeParam: NodeParamV2 = nodeInfo.getNodeInfoData();
if (parentNodeId !== dragParentNodeId) {
dragNodeParam.push({ parentId: parentNodeId, currentId: currentNodeId, data: nodeParam });
}
return false;
}
return false;
}
this.dragTraverseNodeDF(callback, nodeInfoNode, this.listNode);
/* Delete the dragging node from the tree. */
let removeNodeIdList: number[] = this.removeNode(dragCurrentNodeId, dragParentNodeId);
if (removeNodeIdList.length === 0) {
return;
}
/**
* Add the dragging node to the tree
* 1.The first dragging node is added singly, because it needs to distinguish the position to insert
*
* Add first node.
*/
let insertCurrentNodeId: number = rearCurrentNodeId;
let isAfter: boolean = isDownFlag;
if (this.expandAndCollapseInfo.get(rearCurrentNodeId) === NodeStatus.EXPAND) {
isAfter = false;
this.listNode.forEach((value) => {
if (value.getNodeCurrentNodeId() === rearCurrentNodeId && value.getCanShowFlagLine() === false) {
if (value.getNodeInfoNode().children.length) {
insertCurrentNodeId = value.getNodeInfoNode().children[0].currentNodeId;
} else {
insertCurrentNodeId = this.INITIAL_INVALID_VALUE;
}
}
})
} else if (!this.expandAndCollapseInfo.get(rearCurrentNodeId) && isHighLight) {
this.expandAndCollapseInfo.set(rearCurrentNodeId, NodeStatus.EXPAND);
}
let addDragNodeResult: boolean =
this.addDragNode(dragNodeParam[0].parentId, dragNodeParam[0].currentId, insertCurrentNodeId,
isAfter, dragNodeParam[0].data);
if (!addDragNodeResult) {
return;
}
/* Add remaining node. */
for (let j: number = 1; j < dragNodeParam.length; j++) {
let addNodeResult: boolean =
this.addNode(dragNodeParam[j].parentId, dragNodeParam[j].currentId, dragNodeParam[j].data, false);
if (!addNodeResult) {
return;
}
}
/* Update node data and reload the array named 'listNode'. */
for (let i: number = 0; i < this.listNode.length; i++) {
if (this.listNode[i].getNodeCurrentNodeId() === dragParentNodeId) {
if (this.listNode[i].getNodeItem().imageCollapse === null) {
this.listNode[i].handleImageCollapseAfterAddNode(false);
this.expandAndCollapseInfo.delete(dragParentNodeId);
break;
}
}
}
let tmp: NodeInfo[] = [...this.listNode];
this.reloadListNode(tmp);
}
/**
* Reload the array named 'listNode'
* @param tmp
*/
public reloadListNode(tmp: NodeInfo[]): void {
let index: number = 0;
let listIndex: number = 0;
this.listNode.splice(0, this.listNode.length);
this.loadedNodeIdAndIndexMap.clear();
this.loadedListNode.splice(0, this.loadedListNode.length);
this.traverseNodeDF((node: NodeItem): boolean => {
let currentNodeId: number = node.currentNodeId;
if (currentNodeId >= 0) {
if (this.nodeIdNodeParamMap.has(currentNodeId)) {
let nodeInfo: NodeInfo = new NodeInfo(node, this.nodeIdNodeParamMap.get(currentNodeId) as NodeParamV2);
nodeInfo.addImageCollapse(node.getChildNodeInfo().isHasChildNode);
this.listNode.push(nodeInfo);
this.nodeIdAndNodeIndexMap.set(nodeInfo.getNodeCurrentNodeId(), listIndex++);
if (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.EXPAND) {
nodeInfo.getNodeItem()
.imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.EXPAND,
nodeInfo.getNodeItem().imageCollapse?.isCollapse);
} else if (this.expandAndCollapseInfo.get(currentNodeId) === NodeStatus.COLLAPSE) {
nodeInfo.getNodeItem()
.imageCollapse = CollapseImageNodeFlyweightFactory.changeImageCollapseSource(NodeStatus.COLLAPSE,
nodeInfo.getNodeItem().imageCollapse?.isCollapse);
}
for (let i: number = 0; i < tmp.length; i++) {
if (tmp[i].getNodeCurrentNodeId() === nodeInfo.getNodeCurrentNodeId()) {
nodeInfo.setNodeIsShow(tmp[i].getNodeIsShow());
nodeInfo.setListItemHeight(tmp[i].getListItemHeight());
if (nodeInfo.getNodeItem().mainTitleNode && nodeInfo.getIsShowTitle()) {
nodeInfo.getNodeItem().mainTitleNode.title = tmp[i].getNodeItem().mainTitleNode?.title as string;
}
break;
}
}
if (nodeInfo.getNodeIsShow()) {
this.loadedNodeIdAndIndexMap.set(nodeInfo.getNodeCurrentNodeId(), index++);
this.loadedListNode.push(nodeInfo);
}
}
}
return false;
});
}
public getFlagLine(): FlagLine {
return this.FLAG_LINE;
}
public getVisibility(nodeInfo: NodeInfo): Visibility {
let lastShowIndex: number = this.loadedNodeIdAndIndexMap.get(nodeInfo.getNodeCurrentNodeId()) as number - 1;
if (lastShowIndex > this.INITIAL_INVALID_VALUE) {
let lastNodeInfo: NodeInfo | undefined = this.getData(lastShowIndex);
return (nodeInfo.getCanShowFlagLine() === true && !nodeInfo.getIsHighLight() && !lastNodeInfo?.getIsHighLight()) ?
Visibility.Visible : Visibility.Hidden;
} else {
return (nodeInfo.getCanShowFlagLine() === true && !nodeInfo.getIsHighLight()) ?
Visibility.Visible : Visibility.Hidden;
}
}
public getSubTitlePara(): SubTitleStyle {
return this.subTitle;
}
public getIsFolder(nodeId: number): boolean | undefined {
if (this.loadedNodeIdAndIndexMap.has(nodeId)) {
return this.getData(this.loadedNodeIdAndIndexMap.get(nodeId) as number)?.getIsFolder();
}
return false;
}
public getSubTitleFontColor(isHighLight: boolean): ResourceColor {
return isHighLight ? this.subTitle.highLightFontColor : this.treeViewTheme.secondaryTitleFontColor;
}
private getChildIndex(rearParentNodeId: number, rearCurrentNodeId: number): number {
let insertChildIndex: number = this.INITIAL_INVALID_VALUE;
if (this.nodeIdNodeItemMap.has(rearParentNodeId)) {
let node: NodeItem = this.nodeIdNodeItemMap.get(rearParentNodeId) as NodeItem;
if (node.getCurrentNodeId() === rearParentNodeId) {
node.children.forEach((value, index) => {
if (value.getCurrentNodeId() === rearCurrentNodeId) {
insertChildIndex = index;
return;
}
})
}
}
return insertChildIndex;
}
public setCurrentFocusNodeId(focusNodeId: number): void {
this.currentFocusNodeId = focusNodeId;
}
public getCurrentFocusNodeId(): number {
return this.currentFocusNodeId;
}
public setLastFocusNodeId(focusNodeId: number): void {
this.lastFocusNodeId = focusNodeId;
}
public getLastFocusNodeId(): number {
return this.lastFocusNodeId;
}
public getAddFocusNodeId(): number {
return this.addFocusNodeId;
}
public setFlag(flag: Flag): void {
this.flag = flag;
}
private traverseNodeDF(callback: (currentNode: NodeItem) => boolean, root: NodeItem = this._root): void {
let stack: NodeItem[] = [];
let found: boolean = false;
stack.unshift(root);
let currentNode: NodeItem = stack.shift() as NodeItem;
while (!found && currentNode) {
found = callback(currentNode) === true;
if (!found) {
stack.unshift(...currentNode.children);
currentNode = stack.shift() as NodeItem;
}
}
}
private traverseSectionNodeDF(callback: (currentNode: NodeItem) => boolean, root: NodeItem = this._root,
startLevel?: number, endLevel?: number): void {
let stack: NodeItem[] = [];
let found: boolean = false;
let isPassNode: boolean = false;
stack.unshift(root);
let currentNode: NodeItem = stack.shift() as NodeItem;
while (!found && currentNode) {
try {
if (startLevel !== undefined && currentNode.nodeLevel < startLevel) {
isPassNode = true;
}
if (endLevel !== undefined && currentNode.nodeLevel > endLevel) {
isPassNode = true;
}
if (!isPassNode) {
found = callback(currentNode);
}
} catch (err) {
throw new Error('traverseSectionNodeDF function callbacks error');
}
if (!found) {
stack.unshift(...currentNode.children);
currentNode = stack.shift() as NodeItem;
isPassNode = false;
}
}
}
private updateParentChildNum(parentNode: NodeItem, isAdd: boolean, count: number): void {
let parentNodeId: number = parentNode.parentNodeId;
while (parentNodeId >= 0) {
if (this.nodeIdNodeItemMap.has(parentNodeId)) {
let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem;
parent.getChildNodeInfo().allChildNum =
isAdd ? parent.getChildNodeInfo().allChildNum + count : parent.getChildNodeInfo().allChildNum - count;
parentNodeId = parent.parentNodeId;
} else {
hilog.error(LOG_CODE, TAG, 'updateParentChildNum: parent node not found.');
break;
}
}
}
/**
* find parent node id
*
* @param currentNodeId current node id
* @returns parent node id
*/
public findParentNodeId(currentNodeId: number): number {
let current: NodeItem = new NodeItem(emptyNodeInfo);
if (this.nodeIdNodeItemMap.has(currentNodeId)) {
current = this.nodeIdNodeItemMap.get(currentNodeId) as NodeItem;
}
return current.parentNodeId;
}
private refreshRemoveNodeData(removeNodeIdList: number[], parentNodeInfo: NodeInfo): void {
let deleteIndexList: number[] = [];
if (removeNodeIdList.length === 0) {
return;
}
let startIndex: number | undefined = undefined;
for (let i: number = 0; i < removeNodeIdList.length; i++) {
if (this.loadedNodeIdAndIndexMap.has(removeNodeIdList[i])) {
let loadedIndex: number = this.loadedNodeIdAndIndexMap.get(removeNodeIdList[i]) as number;
deleteIndexList.push(loadedIndex);
}
if (startIndex === undefined && this.nodeIdAndNodeIndexMap.has(removeNodeIdList[i])) {
startIndex = this.nodeIdAndNodeIndexMap.get(removeNodeIdList[i]);
}
if (startIndex !== undefined) {
let deleteNode: NodeInfo[] | null = this.listNode.splice(startIndex, 1);
deleteNode = null;
}
if (this.expandAndCollapseInfo.has(removeNodeIdList[i])) {
this.expandAndCollapseInfo.delete(removeNodeIdList[i]);
}
}
deleteIndexList.forEach((value) => {
this.notifyDataDelete(value);
this.notifyDataChange(value);
})
if (parentNodeInfo.getNodeItem().imageCollapse === null) {
if (this.nodeIdAndNodeIndexMap.has(parentNodeInfo.getNodeCurrentNodeId())) {
let parentIndex: number = this.nodeIdAndNodeIndexMap.get(parentNodeInfo.getNodeCurrentNodeId()) as number;
this.listNode[parentIndex]?.handleImageCollapseAfterAddNode(false);
}
this.expandAndCollapseInfo.delete(parentNodeInfo.getNodeCurrentNodeId());
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(parentNodeInfo.getNodeCurrentNodeId()));
}
let callbackParam: CallbackParamV2 = {
currentNodeId: parentNodeInfo.getNodeCurrentNodeId(),
parentNodeId: parentNodeInfo.getNodeParentNodeId(),
};
this.loadedListNodeFunction();
this.appEventBus.emitNodeDelete(callbackParam);
}
private refreshAddNodeData(addNodeIdList: number[]): void {
let addNodeInfo: NodeInfo = new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo);
if (this.nodeIdNodeItemMap.has(addNodeIdList[0])) {
let node: NodeItem = this.nodeIdNodeItemMap.get(addNodeIdList[0]) as NodeItem;
addNodeInfo = new NodeInfo(node, this.nodeIdNodeParamMap.get(addNodeIdList[0]) as NodeParamV2);
addNodeInfo.addImageCollapse(node.getChildNodeInfo().isHasChildNode);
}
addNodeInfo.setIsModify(true);
let index: number = 0;
for (let i: number = 0; i < this.listNode.length; i++) {
if (this.listNode[i].getNodeCurrentNodeId() === addNodeInfo.getNodeParentNodeId()) {
index = i;
if (this.listNode[i].getNodeItem().imageCollapse === null) {
this.listNode[i].handleImageCollapseAfterAddNode(true);
this.notifyDataChange(index);
} else if (this.expandAndCollapseInfo.get(this.listNode[i].getNodeCurrentNodeId()) === NodeStatus.COLLAPSE) {
this.changeNodeStatus(index);
}
this.listNode.splice(i + 1, 0, addNodeInfo);
this.listNode[i + 1].setTitleAndInputTextStatus(true);
this.listNode[i + 1].setNodeIsShow(true);
this.listNode[i + 1].setListItemHeight(LIST_ITEM_HEIGHT);
this.nodeIdAndNodeIndexMap.set(addNodeIdList[0], i + 1);
this.setImageSource(i + 1, InteractionStatus.EDIT);
this.currentOperation = MenuOperation.ADD_NODE;
this.notifyDataAdd(i + 1);
this.notificationNodeInfo(i + 1, this.currentOperation);
break;
}
}
this.modifyNodeIndex = index + 1;
this.setClickIndex(index);
this.lastIndex = index;
this.expandAndCollapseInfo.set(addNodeInfo.getNodeParentNodeId(), NodeStatus.EXPAND);
this.handleExpandAndCollapse(index, true);
}
public refreshData(operation: MenuOperation, parentNodeId: number, changeNodeIdList: number[]): void {
let parentNodeInfo: NodeInfo = new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo);
if (this.nodeIdNodeItemMap.has(parentNodeId)) {
let parentNode: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem;
parentNodeInfo = new NodeInfo(parentNode, this.nodeIdNodeParamMap.get(parentNodeId) as NodeParamV2);
parentNodeInfo.addImageCollapse(parentNode.getChildNodeInfo().isHasChildNode);
}
if (operation === MenuOperation.REMOVE_NODE) {
this.nodeIdAndSubtitleMap.set(parentNodeId, this.selectedParentNodeSubtitle);
this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(parentNodeId));
this.refreshRemoveNodeData(changeNodeIdList, parentNodeInfo);
}
if (operation === MenuOperation.ADD_NODE) {
this.addFocusNodeId = changeNodeIdList[0];
this.nodeIdAndSubtitleMap.set(this.getClickNodeId(), this.selectedParentNodeSubtitle);
this.nodeIdAndSubtitleMap.set(changeNodeIdList[0], this.insertNodeSubtitle);
this.refreshAddNodeData(changeNodeIdList);
}
}
/**
* remove node
*
* @param currentNodeId current node id
* @param parentNodeId parent node id
* @returns node id list which is removed
*/
public removeNode(currentNodeId: number, parentNodeId: number): number[] {
if (this.nodeIdNodeItemMap.has(parentNodeId) && this.nodeIdNodeItemMap.has(currentNodeId)) {
let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem;
let current: NodeItem = this.nodeIdNodeItemMap.get(currentNodeId) as NodeItem;
let removeNodeIdList: number[] = [];
let index: number = current.indexOfParent;
let deleteNodeAllChildNum: number = 0;
if (index < 0) {
hilog.error(LOG_CODE, TAG, 'node does not exist.');
return [];
} else {
deleteNodeAllChildNum = parent.children[index].getChildNodeInfo().allChildNum + 1;
this.freeNodeMemory(parent.children[index], removeNodeIdList);
for (let i: number = index; i < parent.children.length; i++) {
parent.children[i].indexOfParent -= 1;
}
let node: NodeItem[] | null = parent.children.splice(index, 1);
node = null;
this.judgeImageCollapse(parentNodeId);
}
parent.getChildNodeInfo().childNum = parent.children.length;
parent.getChildNodeInfo().allChildNum -= (deleteNodeAllChildNum);
let updateNodeIdList: number[] = [];
updateNodeIdList.push(parent.parentNodeId);
delayUpdateParentChildNum(false, deleteNodeAllChildNum, this.nodeIdNodeItemMap, updateNodeIdList);
return removeNodeIdList;
} else {
hilog.error(LOG_CODE, TAG, 'parent does not exist.');
return [];
}
}
public addNode(parentNodeId: number,
currentNodeId: number,
data: NodeParamV2, initBuild: boolean): boolean {
if (this._root === null) {
this._root = new NodeItem(emptyNodeInfo);
this._root.nodeLevel = -1;
this.nodeIdNodeItemMap.set(-1, this._root);
this.nodeIdNodeParamMap.set(-1, emptyNodeInfo);
}
if (this.nodeIdNodeItemMap.has(parentNodeId)) {
let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem;
let currentNode: NodeItem = new NodeItem(data);
if (parent.nodeLevel > this.maxNodeLevel) {
hilog.error(LOG_CODE, TAG, 'ListDataSource[addNode]: The level of the tree view cannot exceed 50.');
return false;
}
currentNode.nodeLevel = parent.nodeLevel + 1;
currentNode.parentNodeId = parentNodeId;
currentNode.currentNodeId = currentNodeId;
currentNode.indexOfParent = parent.children.length;
data.parentNodeId = parentNodeId;
data.currentNodeId = currentNodeId;
if (data.symbolIconStyle && !data.icon) {
data.icon = 'symbolUsed';
}
parent.children.push(currentNode);
parent.getChildNodeInfo().isHasChildNode = true;
parent.getChildNodeInfo().childNum = parent.children.length;
parent.getChildNodeInfo().allChildNum += 1;
this.judgeImageCollapse(parentNodeId);
if (initBuild) {
this.updateNodeIdList.push(parent.parentNodeId);
} else {
let updateNodeIdList: number[] = [];
updateNodeIdList.push(parent.parentNodeId);
delayUpdateParentChildNum(true, 1, this.nodeIdNodeItemMap, updateNodeIdList);
}
this.nodeIdNodeParamMap.set(currentNodeId, data);
this.nodeIdNodeItemMap.set(currentNodeId, currentNode);
return true;
} else {
hilog.error(LOG_CODE, TAG, 'ListDataSource[addNode]: Parent node not found.');
return false;
}
}
public judgeImageCollapse(parentNodeId: number | undefined): void {
if (parentNodeId === undefined) {
return;
}
let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem;
let parentIndex: number = this.nodeIdAndNodeIndexMap.get(parentNodeId) as number;
if (parent.children.length > 0) {
if (this.nodeIdAndNodeIndexMap.has(parentNodeId)) {
this.listNode[parentIndex]?.addImageExpand(true);
}
} else {
this.listNode[parentIndex]?.addImageExpand(false);
}
}
private freeNodeMemory(rootNode: NodeItem, removeNodeIdList: number[]): void {
let deleteNode: NodeItem[] = [];
let callback = (node: NodeItem): boolean => {
deleteNode.push(node);
return false;
};
this.traverseNodeDF(callback, rootNode);
deleteNode.forEach((value: NodeItem) => {
removeNodeIdList.push(value.getCurrentNodeId());
this.nodeIdNodeItemMap.delete(value.getCurrentNodeId());
this.nodeIdNodeParamMap.delete(value.getCurrentNodeId());
value = new NodeItem(emptyNodeInfo);
})
}
public getNodeInfoByNodeItem(nodeItem: NodeItem): NodeInfo {
if (nodeItem?.currentNodeId === undefined) {
hilog.error(LOG_CODE, TAG, 'getNodeInfoByNodeItem: currentId is undefined');
return new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo);
}
if (!this.nodeIdAndNodeIndexMap.has(nodeItem.currentNodeId)) {
hilog.error(LOG_CODE, TAG, 'getNodeInfoByNodeItem: not has nodeItem.');
return new NodeInfo(new NodeItem(emptyNodeInfo), emptyNodeInfo);
}
let index: number = this.nodeIdAndNodeIndexMap.get(nodeItem.currentNodeId) as number;
return this.listNode[index];
}
public getNewNodeParam(nodeId: number): NodeParamV2 {
let parent: NodeItem = new NodeItem(emptyNodeInfo);
if (this.nodeIdNodeItemMap.has(nodeId)) {
parent = this.nodeIdNodeItemMap.get(nodeId) as NodeItem;
}
let newNodeParam: NodeParamV2 = emptyNodeInfo;
if (parent) {
let nodeInfo: NodeInfo = this.getNodeInfoByNodeItem(parent);
if (parent.children.length === 0) {
if (nodeInfo.getNodeItem().imageNode !== undefined) {
newNodeParam.icon = nodeInfo.getNodeItem().imageNode?.normalSource;
newNodeParam.symbolIconStyle = nodeInfo.getNodeItem().imageNode?.symbolNormalSource;
newNodeParam.selectedIcon = nodeInfo.getNodeItem().imageNode?.selectedSource;
newNodeParam.symbolSelectedIconStyle = nodeInfo.getNodeItem().imageNode?.symbolSelectedSource;
newNodeParam.editIcon = nodeInfo.getNodeItem().imageNode?.editSource;
newNodeParam.symbolEditIconStyle = nodeInfo.getNodeItem().imageNode?.symbolEditSource;
newNodeParam.container = nodeInfo.getMenu();
} else {
newNodeParam.icon = undefined;
newNodeParam.symbolIconStyle = undefined;
newNodeParam.selectedIcon = undefined;
newNodeParam.symbolSelectedIconStyle = undefined;
newNodeParam.editIcon = undefined;
newNodeParam.symbolEditIconStyle = undefined;
newNodeParam.container = nodeInfo.getMenu();
}
} else if (parent.children.length > 0) {
let childNodeInfo: NodeInfo = this.getNodeInfoByNodeItem(parent.children[0]);
if (nodeInfo.getNodeItem().imageNode !== null) {
newNodeParam.icon = (childNodeInfo.getNodeItem().imageNode !== undefined) ?
childNodeInfo.getNodeItem().imageNode?.normalSource : undefined;
newNodeParam.symbolIconStyle = (childNodeInfo.getNodeItem().imageNode !== undefined) ?
childNodeInfo.getNodeItem().imageNode?.symbolNormalSource : undefined;
newNodeParam.selectedIcon = (childNodeInfo.getNodeItem().imageNode !== undefined) ?
childNodeInfo.getNodeItem().imageNode?.selectedSource : undefined;
newNodeParam.symbolSelectedIconStyle = (childNodeInfo.getNodeItem().imageNode !== undefined) ?
childNodeInfo.getNodeItem().imageNode?.symbolSelectedSource : undefined;
newNodeParam.editIcon = (childNodeInfo.getNodeItem().imageNode !== undefined) ?
childNodeInfo.getNodeItem().imageNode?.editSource : undefined;
newNodeParam.symbolEditIconStyle = (childNodeInfo.getNodeItem().imageNode !== undefined) ?
childNodeInfo.getNodeItem().imageNode?.symbolEditSource : undefined;
newNodeParam.container = childNodeInfo.getMenu();
} else {
newNodeParam.icon = undefined;
newNodeParam.symbolIconStyle = undefined;
newNodeParam.selectedIcon = undefined;
newNodeParam.symbolSelectedIconStyle = undefined;
newNodeParam.editIcon = undefined;
newNodeParam.symbolEditIconStyle = undefined;
newNodeParam.container = childNodeInfo.getMenu();
}
}
}
return newNodeParam;
}
/**
* get child node ids by node id
*
* @param nodeId node id
* @returns child node ids
*/
public getClickChildId(nodeId: number): number[] {
let parent: NodeItem = new NodeItem(emptyNodeInfo);
if (this.nodeIdNodeItemMap.has(nodeId)) {
parent = this.nodeIdNodeItemMap.get(nodeId) as NodeItem;
}
if (parent) {
if (parent.children.length === 0) {
return [];
} else if (parent.children.length > 0) {
let childrenNodeInfo: number[] = new Array(parent.children.length);
for (let i: number = 0; i < childrenNodeInfo.length; i++) {
childrenNodeInfo[i] = 0;
}
for (let i: number = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) {
childrenNodeInfo[i] = parent.children[i].currentNodeId;
}
return childrenNodeInfo;
}
}
return [];
}
/**
* get child nodeInfo views by node id
*
* @param nodeId node id
* @returns child nodeInfo views
*/
public getClickNodeChildrenInfo(nodeId: number): NodeInfoView[] {
let parent: NodeItem = new NodeItem(emptyNodeInfo);
if (this.nodeIdNodeItemMap.has(nodeId)) {
parent = this.nodeIdNodeItemMap.get(nodeId) as NodeItem;
}
if (parent) {
if (parent.children.length === 0) {
return [];
} else if (parent.children.length > 0) {
let childrenNodeInfo: NodeInfoView[] = new Array(parent.children.length);
for (let i: number = 0; i < childrenNodeInfo.length; i++) {
childrenNodeInfo[i] = {};
}
for (let i: number = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) {
childrenNodeInfo[i].itemId = parent.children[i].currentNodeId;
let nodeInfo: NodeInfo = this.getNodeInfoByNodeItem(parent.children[i]);
if (nodeInfo.getNodeItem().imageNode) {
childrenNodeInfo[i].itemIcon = nodeInfo.getNodeItem().imageNode?.source;
}
if (nodeInfo.getNodeItem().mainTitleNode) {
childrenNodeInfo[i].itemTitle = nodeInfo.getNodeItem().mainTitleNode?.title;
}
childrenNodeInfo[i].isFolder = nodeInfo.getIsFolder();
}
return childrenNodeInfo;
}
}
return [];
}
/**
* check main title is valid
*
* @param title main title
* @returns check result
*/
public checkMainTitleIsValid(title: string): boolean {
if (new RegExp('/[\\\/:*?"<>|]/').test(title)) {
return false;
}
if ((new RegExp('/^[\u4e00-\u9fa5]+$/').test(title) && title.length > this.MAX_CN_LENGTH) ||
(!new RegExp('/^[\u4e00-\u9fa5]+$/').test(title) && title.length > this.MAX_EN_LENGTH)) {
return false;
}
return true;
}
/**
* DFS: Depth first traversal in drag event.
*
* @param callback dfs callback fuction
*/
dragTraverseNodeDF(callback: (node: NodeItem, listNode: NodeInfo[]) => boolean,
root: NodeItem = this._root, listNode: NodeInfo[]): void {
let stack: NodeItem[] = [];
let found: boolean = false;
stack.unshift(root);
let currentNode: NodeItem = stack.shift() as NodeItem;
while (!found && currentNode) {
found = callback(currentNode, listNode) === true;
if (!found) {
stack.unshift(...currentNode.children);
currentNode = stack.shift() as NodeItem;
}
}
}
private updateChildIndexOfParent(insertIndex: number, parent: NodeItem): void {
for (let i: number = insertIndex; i < parent.children.length; i++) {
parent.children[i].indexOfParent += 1;
}
}
/**
* Add the first dragging node in dragging nodes
* 1.the first dragging node needs to distinguish the position to insert
*/
private addDragNode(parentNodeId: number,
currentNodeId: number,
insertCurrentNodeId: number,
isAfter: boolean,
data: NodeParamV2): boolean {
if (this._root === null) {
this._root = new NodeItem(emptyNodeInfo);
this._root.nodeLevel = this.INITIAL_INVALID_VALUE;
}
if (this.nodeIdNodeItemMap.has(parentNodeId)) {
let parent: NodeItem = this.nodeIdNodeItemMap.get(parentNodeId) as NodeItem;
let currentNode: NodeItem = new NodeItem(data);
if (parent.nodeLevel > this.maxNodeLevel) {
hilog.error(LOG_CODE, TAG, 'addDragNode: The level of the tree view cannot exceed 50.');
return false;
}
currentNode.nodeLevel = parent.nodeLevel + 1;
currentNode.parentNodeId = parentNodeId;
currentNode.currentNodeId = currentNodeId;
data.parentNodeId = parentNodeId;
data.currentNodeId = currentNodeId;
let insertIndex: number = this.INITIAL_INVALID_VALUE;
if (parent.children.length) {
for (let i: number = 0; i < parent.children.length; i++) {
if (parent.children[i].getCurrentNodeId() === insertCurrentNodeId) {
insertIndex = i;
break;
}
}
if (isAfter) {
currentNode.indexOfParent = insertIndex + 1;
this.updateChildIndexOfParent(currentNode.indexOfParent, parent);
parent.children.splice(insertIndex + 1, 0, currentNode);
} else {
currentNode.indexOfParent = insertIndex < 0 ? parent.children.length + insertIndex : insertIndex;
this.updateChildIndexOfParent(currentNode.indexOfParent, parent);
parent.children.splice(insertIndex, 0, currentNode);
}
} else {
currentNode.indexOfParent = parent.children.length;
parent.children.push(currentNode);
}
parent.getChildNodeInfo().isHasChildNode = true;
parent.getChildNodeInfo().childNum = parent.children.length;
parent.getChildNodeInfo().allChildNum += 1;
this.updateParentChildNum(parent, true, 1);
this.nodeIdNodeItemMap.set(currentNodeId, currentNode);
this.nodeIdNodeParamMap.set(currentNodeId, data);
return true;
} else {
hilog.error(LOG_CODE, TAG, 'addDragNode: Parent node not found.');
return false;
}
}
}
class ButtonGestureModifier implements GestureModifier {
public static readonly longPressTime: number = 500;
public static readonly minFontSize: number = 1.75;
public fontSize: number = 1;
public controller: CustomDialogController | null = null;
constructor(controller: CustomDialogController | null) {
this.controller = controller;
}
applyGesture(event: UIGestureEvent): void {
if (this.fontSize >= ButtonGestureModifier.minFontSize) {
event.addGesture(
new LongPressGestureHandler({ repeat: false, duration: ButtonGestureModifier.longPressTime })
.onAction(() => {
if (event) {
this.controller?.open();
}
})
.onActionEnd(() => {
this.controller?.close();
})
)
} else {
event.clearGestures();
}
}
}
@ComponentV2
export struct TreeViewInner {
@Require @Param item: NodeInfo;
@Param listNodeDataSource: ListNodeDataSource = new ListNodeDataSource();
@Local columnWidth: number = 0;
@Local isFocused: boolean = false;
@Param initialIndex: number = -1;
@Local index: number = -1;
@Local lastIndex: number = -1;
@Local count: number = 0;
@Local followingSystemFontScale: boolean = false;
@Local maxAppFontScale: number = 1;
@Consumer('treeViewThemeV2') treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance()
@Consumer('clickButtonFlagV2') clickButtonFlag: boolean = true
@Consumer('accessibilityNodeTypeV2') accessibilityNodeType: AccessibilityNodeType = AccessibilityNodeType.TEXT
@Consumer('isAccessibilityEnabledV2') isAccessibilityEnabled: boolean = false
@BuilderParam private listTreeViewMenu: () => void;
@Param callBackClick: () => void = () => {
}
private readonly MAX_CN_LENGTH: number = 254;
private readonly MAX_EN_LENGTH: number = 255;
private readonly INITIAL_INVALID_VALUE = -1;
private readonly MAX_TOUCH_DOWN_COUNT = 0;
private isMultiPress: boolean = false;
private touchDownCount: number = this.INITIAL_INVALID_VALUE;
private appEventBus: TreeListenerV2 = TreeListenerManagerV2.getInstance().getTreeListener();
private readonly itemPadding: ItemPadding = {
left: $r('sys.float.ohos_id_card_margin_start'),
right: $r('sys.float.ohos_id_card_margin_end'),
top: $r('sys.float.ohos_id_text_margin_vertical'),
bottom: $r('sys.float.padding_level0'),
};
private readonly textInputPadding: ItemPadding = {
left: $r('sys.float.padding_level0'),
right: $r('sys.float.padding_level0'),
top: $r('sys.float.padding_level0'),
bottom: $r('sys.float.padding_level0'),
};
private inputFontSize: number = resourceManager.getSystemResourceManager().getNumberByName('ohos_id_text_size_body1');
aboutToAppear(): void {
this.index = this.initialIndex;
if (this.item.getNodeItem().imageNode) {
this.item.imageSource = this.item.getNodeItem().imageNode?.source;
this.item.symbolSource = this.item.getNodeItem().imageNode?.symbolSource;
}
let uiContent: UIContext = this.getUIContext();
this.followingSystemFontScale = uiContent.isFollowingSystemFontScale();
this.maxAppFontScale = uiContent.getMaxFontScale();
}
decideFontScale(): number {
let uiContent: UIContext = this.getUIContext();
let systemFontScale: number = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
if (!this.followingSystemFontScale) {
return 1;
}
return Math.min(systemFontScale, this.maxAppFontScale, MAX_FONT_SCALE);
}
decideSymbolFontScale(isSymbol: boolean): number {
if (!isSymbol || !this.followingSystemFontScale) {
return 1;
}
let uiContent: UIContext = this.getUIContext();
let systemFontScale: number = (uiContent.getHostContext() as common.UIAbilityContext)?.config?.fontSizeScale ?? 1;
let symbolFontSizeScale: number = Math.min(systemFontScale, this.maxAppFontScale, MAX_SYMBOL_FONT_SCALE);
return Math.max(symbolFontSizeScale, MIN_SYMBOL_FONT_SCALE);
}
getInputTextMaxFontSize() {
let inputTextMaxFontSize = this.decideFontScale() * this.inputFontSize + 'vp';
return inputTextMaxFontSize;
}
getLeftIconColor(): ResourceColor {
if (this.item.getIsModify() || this.item.getIsHighLight()) {
return $r('sys.color.icon_on_primary');
} else if (this.item.getIsSelected()) {
return this.treeViewTheme.leftIconActiveColor;
} else {
return this.treeViewTheme.leftIconColor;
}
}
getPrimaryTextColor(): ResourceColor {
if (this.item.getIsModify() || this.item.getIsHighLight()) {
return $r('sys.color.ohos_id_color_primary_contrary');
} else if (this.item.getIsSelected()) {
return this.treeViewTheme.primaryTitleActiveFontColor;
} else {
return this.treeViewTheme.primaryTitleFontColor;
}
}
private checkInvalidPattern(title: string): boolean {
return new RegExp('/[\\\/:*?"<>|]/').test(title);
}
private checkIsAllCN(title: string): boolean {
return new RegExp('/^[\u4e00-\u9fa5]+$/').test(title);
}
private getAccessibilityReadText(currentNodeId: number): string {
let nodeItem = this.listNodeDataSource.nodeIdNodeItemMap.get(currentNodeId);
if (nodeItem === undefined || currentNodeId === undefined) {
return '';
}
let nodeInfo = this.listNodeDataSource.getNodeInfoByNodeItem(nodeItem);
let primaryTitle = nodeInfo?.getNodeInfoData()?.primaryTitle === undefined
? '' : nodeInfo?.getNodeInfoData()?.primaryTitle;
let secondaryTitle = nodeInfo?.getNodeInfoData()?.secondaryTitle === undefined
? '' : nodeInfo?.getNodeInfoData()?.secondaryTitle;
let primaryTitleText = this.listNodeDataSource.getAccessibleTitleText(primaryTitle);
let secondaryTitleText = this.listNodeDataSource.getAccessibleTitleText(secondaryTitle);
let title: string | undefined = `${primaryTitleText}, ${secondaryTitleText}`;
let parentId: number = this.listNodeDataSource.findParentNodeId(currentNodeId);
let parentNode: number[] = [];
let insertRootNodePosition = 0;
let childrenInfo: NodeInfoView[] = this.listNodeDataSource.getClickNodeChildrenInfo(parentId);
let childrenItemId: (number | undefined)[] = childrenInfo.map(item => item.itemId);
let insertNodePosition: number = childrenItemId.indexOf(currentNodeId) + 1;
let accessibleTitle: string | undefined = this.listNodeDataSource.getAccessibleTitle(currentNodeId);
if (accessibleTitle === undefined) {
return ' ';
}
if (this.accessibilityNodeType === AccessibilityNodeType.PLACE) {
if (this.listNodeDataSource.findParentNodeId(currentNodeId) === -1) {
for (let i: number = 0; i < this.listNodeDataSource.listNode.length; i++) {
if (this.listNodeDataSource.listNode[i].getNodeParentNodeId() === -1) {
parentNode.push(this.listNodeDataSource.listNode[i].getNodeCurrentNodeId());
}
}
insertRootNodePosition = parentNode.indexOf(currentNodeId) + 1;
return this.listNodeDataSource.getStringByName('treeview_accessibility_place_node_parent',
insertRootNodePosition);
} else {
return this.listNodeDataSource.getStringByName('treeview_accessibility_place_node_child', accessibleTitle,
insertNodePosition);
}
} else if (this.accessibilityNodeType === AccessibilityNodeType.LIFT) {
return title;
} else {
return title;
}
}
private getAccessibilityDescription(): string {
if (this.accessibilityNodeType === AccessibilityNodeType.TEXT) {
return this.listNodeDataSource.getStringByName('treeview_accessibility_node_desc');
} else {
return ' ';
}
}
private getAccessibilityReadButtonText(isFolded: boolean): string {
if (this.clickButtonFlag === false) {
return this.item.getNodeItem().imageCollapse?.collapseSource === ARROW_RIGHT
? this.listNodeDataSource.getStringByName('treeview_accessibility_folded_node')
: this.listNodeDataSource.getStringByName('treeview_accessibility_expanded_node');
} else {
return isFolded
? this.listNodeDataSource.getStringByName('treeview_accessibility_expand_node')
: this.listNodeDataSource.getStringByName('treeview_accessibility_fold_node');
}
}
private getAccessibilityReadButtonDescription(): string {
if (this.clickButtonFlag === false) {
return ' ';
} else {
return this.listNodeDataSource.getStringByName('treeview_accessibility_implement_node');
}
}
private onTouchNode(event: TouchEvent) {
this.count++;
if (this.count > 1) {
this.count--;
return;
}
this.index = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId())
this.listNodeDataSource.setClickIndex(this.index);
let currentId: number = this.item.getNodeCurrentNodeId();
if (event.type === TouchType.Down) {
this.item.setNodeColor(this.treeViewTheme.itemPressedBgColor);
} else if (event.type === TouchType.Up) {
if (!(typeof this.treeViewTheme.itemSelectedBgColor === 'string')) {
this.item.setNodeColor(COLOR_SELECT);
} else {
this.item.setNodeColor(this.treeViewTheme.itemSelectedBgColor);
}
if (this.item.getNodeItem().imageNode !== null) {
this.item.getNodeItem().imageNode?.setImageSource(InteractionStatus.SELECTED);
this.listNodeDataSource.setImageSource(this.index, InteractionStatus.SELECTED);
this.item.imageSource = this.item.getNodeItem().imageNode?.source;
this.item.symbolSource = this.item.getNodeItem().imageNode?.symbolSource;
}
this.item.getNodeItem().mainTitleNode?.setMainTitleSelected(true);
let callParam: CallbackParamV2 = { currentNodeId: currentId };
this.appEventBus.emitNodeClick(callParam);
this.listNodeDataSource.sendAccessibility(this.item.getIsSelected()
? this.listNodeDataSource.getStringByName('treeview_accessibility_select_node',
`${this.getAccessibilityReadText(this.item.getNodeCurrentNodeId())}`) : '');
}
if (this.listNodeDataSource.getLastIndex() !== -1 && this.index !== this.listNodeDataSource.getLastIndex()) {
this.listNodeDataSource.setPopUpInfo(
PopUpType.WARNINGS,
InputError.NONE,
false,
this.listNodeDataSource.getLastIndex()
);
this.listNodeDataSource.setItemVisibilityOnEdit(
this.listNodeDataSource.getLastIndex(),
MenuOperation.COMMIT_NODE
);
}
this.lastIndex = this.index;
this.count--;
}
private onClickNode() {
this.count++;
if (this.count > 1) {
this.count--;
return;
}
this.index = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId())
this.listNodeDataSource.setClickIndex(this.index);
let currentId: number = this.item.getNodeCurrentNodeId();
if (!(typeof this.treeViewTheme.itemSelectedBgColor === 'string')) {
this.item.setNodeColor(COLOR_SELECT);
} else {
this.item.setNodeColor(this.treeViewTheme.itemSelectedBgColor);
}
if (this.item.getNodeItem().imageNode !== null) {
this.item.getNodeItem().imageNode?.setImageSource(InteractionStatus.SELECTED);
this.listNodeDataSource.setImageSource(this.index, InteractionStatus.SELECTED);
this.item.imageSource = this.item.getNodeItem().imageNode?.source;
this.item.symbolSource = this.item.getNodeItem().imageNode?.symbolSource;
}
this.item.getNodeItem().mainTitleNode?.setMainTitleSelected(true);
let callParam: CallbackParamV2 = { currentNodeId: currentId };
this.appEventBus.emitNodeClick(callParam);
this.listNodeDataSource.sendAccessibility(this.item.getIsSelected()
? this.listNodeDataSource.getStringByName('treeview_accessibility_select_node',
`${this.getAccessibilityReadText(this.item.getNodeCurrentNodeId())}`) : '');
if (this.listNodeDataSource.getLastIndex() !== -1 && this.index !== this.listNodeDataSource.getLastIndex()) {
this.listNodeDataSource.setPopUpInfo(
PopUpType.WARNINGS,
InputError.NONE,
false,
this.listNodeDataSource.getLastIndex()
);
this.listNodeDataSource.setItemVisibilityOnEdit(
this.listNodeDataSource.getLastIndex(),
MenuOperation.COMMIT_NODE
);
}
this.lastIndex = this.index;
this.count--;
}
private accessibilityRefocus() {
this.clickButtonFlag = false;
let eventInfo: accessibility.EventInfo = ({
type: 'requestFocusForAccessibility',
bundleName: (getContext() as common.UIAbilityContext)?.abilityInfo?.bundleName,
triggerAction: 'common',
customId: `treeView_button${this.item.getNodeCurrentNodeId()}`
})
accessibility.sendAccessibilityEvent(eventInfo).then(() => {
setTimeout(() => {
this.clickButtonFlag = true;
}, ENTER_EXIT_DURATION)
})
}
@Builder
popupForShowTitle(text: string | Resource | undefined, backgroundColor: ResourceColor, fontColor: ResourceColor) {
Row() {
Text(text)
.fontSize($r('sys.float.ohos_id_text_size_body2'))
.fontWeight('regular')
.fontColor(fontColor)
.minFontScale(MIN_FONT_SCALE)
.maxFontScale(this.decideFontScale())
}
.backgroundColor(backgroundColor)
.border({ radius: $r('sys.float.ohos_id_elements_margin_horizontal_l') })
.padding({
left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
top: $r('sys.float.ohos_id_card_margin_middle'),
bottom: $r('sys.float.ohos_id_card_margin_middle'),
})
}
@Builder
builder() {
if (this.listTreeViewMenu) {
this.listTreeViewMenu()
}
}
build() {
if (this.item.getNodeIsShow()) {
Stack() {
Column() {
Stack({ alignContent: Alignment.Bottom }) {
Divider()
.height(this.listNodeDataSource.getFlagLine().flagLineHeight)
.color(this.listNodeDataSource.getFlagLine().flagLineColor)
.visibility(this.listNodeDataSource.getVisibility(this.item))
.lineCap(LineCapStyle.Round)
.margin({ start: LengthMetrics.vp(this.item.getFlagLineLeftMargin()) })
.focusable(true)
Row({}) {
Row() {
if (this.item.getNodeItem().imageNode) {
Row() {
if (this.item.symbolSource) {
SymbolGlyph()
.fontColor([this.getLeftIconColor()])
.attributeModifier(this.item.symbolSource)
.fontSize(`${this.item.getNodeItem().imageNode?.itemHeight as number *
this.decideSymbolFontScale(true)}vp`)
.effectStrategy(SymbolEffectStrategy.NONE)
.symbolEffect(new SymbolEffect(), false)
.opacity(!this.item.getIsSelected() && !this.item.getIsHighLight() ?
this.item.getNodeItem().imageNode?.opacity : this.item.getNodeItem().imageNode?.noOpacity)
.focusable(this.item.getNodeItem().mainTitleNode !== null ? false : true)
} else {
if (Util.isSymbolResource(this.item.imageSource)) {
SymbolGlyph(this.item.imageSource as Resource)
.fontSize(`${this.item.getNodeItem().imageNode?.itemHeight as number *
this.decideSymbolFontScale(true)}vp`)
.fontColor([this.getLeftIconColor()])
.opacity(!this.item.getIsSelected() && !this.item.getIsHighLight() ?
this.item.getNodeItem().imageNode?.opacity : this.item.getNodeItem().imageNode?.noOpacity)
.focusable(this.item.getNodeItem().mainTitleNode !== null ? false : true)
} else {
Image(this.item.imageSource)
.objectFit(ImageFit.Contain)
.height(this.item.getNodeItem().imageNode?.itemHeight)
.width(this.item.getNodeItem().imageNode?.itemWidth)
.opacity(!this.item.getIsSelected() && !this.item.getIsHighLight() ?
this.item.getNodeItem().imageNode?.opacity : this.item.getNodeItem().imageNode?.noOpacity)
.focusable(this.item.getNodeItem().mainTitleNode !== null ? false : true)
.fillColor(this.getLeftIconColor())
.matchTextDirection((this.item.getNodeItem()
.imageCollapse?.collapseSource === ARROW_RIGHT || this.item.getNodeItem()
.imageCollapse?.collapseSource === ARROW_RIGHT_WITHE) ? true : false)
}
}
}
.focusable(true)
.backgroundColor(COLOR_IMAGE_ROW)
.margin({
end: getLengthMetricsByResourceOrNumber(this.item.getNodeItem().imageNode?.itemRightMargin)
})
.height(this.item.getNodeItem().imageNode?.itemHeight as number * this.decideSymbolFontScale(
this.item.symbolSource !== undefined || Util.isSymbolResource(this.item.imageSource)))
.width(this.item.getNodeItem().imageNode?.itemWidth as number * this.decideSymbolFontScale(
this.item.symbolSource !== undefined || Util.isSymbolResource(this.item.imageSource)))
}
Row() {
if (this.item.getNodeItem().mainTitleNode && this.item.getIsShowTitle()) {
Text(this.item.getNodeItem().mainTitleNode?.title)
.minFontScale(MIN_FONT_SCALE)
.maxFontScale(this.decideFontScale())
.maxLines(1)// max line
.fontSize(this.item.getNodeItem().mainTitleNode?.size)
.fontColor(this.getPrimaryTextColor())
.margin({
end: getLengthMetricsByResourceOrNumber(this.item.getNodeItem()
.mainTitleNode?.itemRightMargin)
})
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontWeight(this.item.getNodeItem().mainTitleNode?.weight)
.focusable(true)
}
if (this.item.getNodeItem().mainTitleNode && this.item.getNodeItem().inputText &&
this.item.getIsShowInputText()) {
Row() {
TextInput({ text: this.item.getNodeItem().mainTitleNode?.title })
.height(this.item.getNodeItem().inputText?.itemHeight)
.fontSize(this.getInputTextMaxFontSize())
.fontColor(this.item.getNodeItem().inputText?.color)
.borderRadius(this.item.getNodeItem().inputText?.borderRadius)
.backgroundColor(this.item.getNodeItem().inputText?.backgroundColor)
.enterKeyType(EnterKeyType.Done)
.focusable(true)
.padding({
start: LengthMetrics.resource(this.textInputPadding.left),
end: LengthMetrics.resource(this.textInputPadding.right),
top: LengthMetrics.resource(this.textInputPadding.top),
bottom: LengthMetrics.resource(this.textInputPadding.bottom),
})
.onChange((value: string) => {
let thisIndex: number = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId());
let res: string = '';
let isInvalidError: boolean = false;
let isLengthError: boolean = false;
if (this.checkInvalidPattern(value)) {
for (let i: number = 0; i < value.length; i++) {
if (!this.checkInvalidPattern(value[i])) {
res += value[i];
}
}
isInvalidError = true;
this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
InputError.INVALID_ERROR, true, thisIndex);
} else {
res = value;
isInvalidError = false;
}
if ((this.checkIsAllCN(res) && res.length > this.MAX_CN_LENGTH) ||
(!this.checkIsAllCN(res) && res.length > this.MAX_EN_LENGTH)) {
res = this.checkIsAllCN(res) ?
res.substr(0, this.MAX_CN_LENGTH) : res.substr(0, this.MAX_EN_LENGTH);
isLengthError = true;
this.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
InputError.LENGTH_ERROR, true, thisIndex);
} else {
isLengthError = false;
}
if (!isLengthError && !isInvalidError) {
this.listNodeDataSource.setMainTitleNameOnEdit(thisIndex, res);
}
})
.onSubmit((enterKey: EnterKeyType) => {
let thisIndex: number = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId());
this.listNodeDataSource.setPopUpInfo(
PopUpType.WARNINGS,
InputError.NONE,
false,
thisIndex
);
this.listNodeDataSource.setItemVisibilityOnEdit(thisIndex, MenuOperation.COMMIT_NODE);
})
}.backgroundColor(this.item.getNodeItem().inputText?.backgroundColor)
.borderRadius(this.item.getNodeItem().inputText?.borderRadius)
.margin({
end: getLengthMetricsByResourceOrNumber(this.item.getNodeItem()
.inputText?.itemRightMargin)
})
}
Blank()
}
.layoutWeight(1)
.focusable(true)
if (this.listNodeDataSource.hasSubtitle(this.item.getNodeCurrentNodeId())) {
Row() {
Text(this.listNodeDataSource.getSubtitle(this.item.getNodeCurrentNodeId()))
.minFontScale(MIN_FONT_SCALE)
.maxFontScale(this.decideFontScale())
.fontSize(this.listNodeDataSource.getSubTitlePara().fontSize)
.fontColor(this.item.getIsHighLight() || this.item.getIsModify() ?
$r('sys.color.ohos_id_color_primary_contrary') : this.treeViewTheme.secondaryTitleFontColor)
.fontWeight(this.listNodeDataSource.getSubTitlePara().fontWeight)
}
.focusable(true)
.margin({
start: LengthMetrics.resource(this.listNodeDataSource.getSubTitlePara().margin.left),
end: this.item.getNodeItem().imageCollapse ?
LengthMetrics.resource($r('sys.float.padding_level0')) :
LengthMetrics.resource(this.listNodeDataSource.getSubTitlePara().margin.right)
})
}
}
.height(LIST_ITEM_HEIGHT)
.layoutWeight(1)
.focusable(true)
.accessibilityGroup(true)
.id(`treeView_node${this.item.getNodeCurrentNodeId()}`)
.accessibilityText(this.getAccessibilityReadText(this.item.getNodeCurrentNodeId()))
.accessibilityDescription(this.getAccessibilityDescription())
.onClick(this.isAccessibilityEnabled ? () => {
this.onClickNode();
this.callBackClick();
} : undefined)
if (this.item.getNodeItem().imageCollapse) {
Row() {
SymbolGlyph(this.item.getNodeItem().imageCollapse?.collapseSource as Resource)
.fontSize(`${this.item.getNodeItem().imageCollapse?.itemHeight as number *
this.decideSymbolFontScale(true)}vp`)
.fontColor([this.item.getNodeItem().imageCollapse?.isCollapse ?
this.treeViewTheme.arrowIconColor : COLOR_IMAGE_EDIT])
.opacity(!this.item.getIsHighLight() ?
this.item.getNodeItem().imageCollapse?.opacity : this.item.getNodeItem().imageCollapse?.noOpacity)
.focusable(true)
}
.focusable(true)
.justifyContent(FlexAlign.Center)
.height(this.item.getNodeItem().imageCollapse?.itemHeight as number * this.decideSymbolFontScale(true))
.width(this.item.getNodeItem().imageCollapse?.itemWidth as number * this.decideSymbolFontScale(true))
.onClick(() => {
this.listNodeDataSource.expandAndCollapseNode(
this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId()));
this.listNodeDataSource.setCurrentFocusNodeId(this.item.getNodeCurrentNodeId());
setTimeout(() => {
this.accessibilityRefocus();
}, ACCESSIBILITY_REFOCUS_DELAY_TIME)
})
.id(`treeView_button${this.item.getNodeCurrentNodeId()}`)
.accessibilityText(this.getAccessibilityReadButtonText(this.item.getNodeItem()
.imageCollapse?.collapseSource === ARROW_RIGHT))
.accessibilityDescription(this.getAccessibilityReadButtonDescription())
}
}
.focusable(true)
.width('100%')
.height(this.item.getNodeHeight())
.padding({ start: LengthMetrics.vp(this.item.getNodeLeftPadding()) })
.bindContextMenu(this.builder, ResponseType.RightClick)
}.focusable(true)
}
.opacity(this.listNodeDataSource.getListItemOpacity(this.item))
.onHover((isHover: boolean) => {
if (isHover) {
this.item.setNodeColor(this.treeViewTheme.itemHoverBgColor)
} else {
this.item.setNodeColor($r('sys.color.ohos_id_color_background_transparent'))
}
})
.onTouch(this.isAccessibilityEnabled ? undefined : (event) => {
this.onTouchNode(event);
})
/* backgroundColor when editing and in other states. */
.backgroundColor((this.item.getNodeItem().mainTitleNode && this.item.getNodeItem().inputText &&
this.item.getIsShowInputText()) ? this.item.getNodeItem().inputText?.editColor : this.item.getNodeColor())
.border({
width: this.item.getNodeBorder().borderWidth,
color: this.item.getNodeBorder().borderColor,
radius: this.item.getNodeBorder().borderRadius,
})
.height(LIST_ITEM_HEIGHT)
.focusable(true)
.onMouse((event: MouseEvent) => {
let thisIndex: number = this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId());
if (event.button === MouseButton.Right) {
this.listNodeDataSource.handleEvent(Event.MOUSE_BUTTON_RIGHT,
this.listNodeDataSource.findIndex(this.item.getNodeCurrentNodeId()));
this.listTreeViewMenu = this.item.getMenu();
this.listNodeDataSource.setClickIndex(thisIndex);
clearTimeout(this.item.getNodeItem().mainTitleNode?.popUpTimeout);
}
event.stopPropagation();
})
.padding({ top: 0, bottom: 0 })
.bindPopup(this.item.getPopUpInfo().popUpIsShow, {
builder: this.popupForShowTitle(this.item.getPopUpInfo().popUpText, this.item.getPopUpInfo().popUpColor,
this.item.getPopUpInfo().popUpTextColor),
placement: Placement.BottomLeft,
placementOnTop: false,
popupColor: this.item.getPopUpInfo().popUpColor,
autoCancel: true,
enableArrow: this.item.getPopUpInfo().popUpEnableArrow,
})
.onAreaChange((oldValue: Area, newValue: Area) => {
let columnWidthNum: number = Number.parseInt(newValue.width.toString());
this.columnWidth = columnWidthNum;
})
}
.stateStyles({
focused: {
.border({
radius: $r('sys.float.ohos_id_corner_radius_clicked'),
width: FLAG_NUMBER,
color: this.treeViewTheme.borderFocusedColor,
style: BorderStyle.Solid,
})
},
normal: {
.border({
radius: $r('sys.float.ohos_id_corner_radius_clicked'),
width: 0,
})
}
})
}
}
}
export class NodeItem {
public childNodeInfo: ChildNodeInfo;
public nodeLevel: number;
public children: NodeItem[];
public indexOfParent: number;
public parentNodeId: number;
public currentNodeId: number;
public isFolder?: boolean;
constructor(nodeParam: NodeParamV2) {
this.currentNodeId = nodeParam.currentNodeId ?? -1;
this.parentNodeId = nodeParam.parentNodeId ?? -1;
this.isFolder = nodeParam.isFolder;
this.nodeLevel = -1;
this.indexOfParent = -1;
this.childNodeInfo = { isHasChildNode: false, childNum: 0, allChildNum: 0 };
this.children = [];
}
getChildNodeInfo(): ChildNodeInfo {
return this.childNodeInfo;
}
getCurrentNodeId(): number {
return this.currentNodeId;
}
getIsFolder(): boolean | undefined {
return this.isFolder;
}
}
class NodeBaseInfo {
public rightMargin: Resource | number = -1;
private width: number = -1;
private height: number = -1;
constructor() {
}
set itemWidth(width: number) {
this.width = width;
}
get itemWidth(): number {
return this.width;
}
set itemHeight(height: number) {
this.height = height;
}
get itemHeight(): number {
return this.height;
}
set itemRightMargin(rightMargin: Resource | number) {
this.rightMargin = rightMargin;
}
get itemRightMargin(): Resource | number {
return this.rightMargin;
}
}
export class CollapseImageNode extends NodeBaseInfo {
private imageSource: Resource | string;
private symbolIconSource: SymbolGlyphModifier | undefined;
private imageOpacity: Resource;
private imageCollapseSource: Resource | string;
private symbolIconCollapseSource: SymbolGlyphModifier | undefined;
private isImageCollapse: boolean;
private collapseImageType: CollapseImageType;
constructor(
imageSource: Resource | string,
symbolSource: SymbolGlyphModifier | undefined,
imageOpacity: Resource,
itemWidth: number,
itemHeight: number,
itemRightMargin: Resource | number,
isImageCollapse: boolean,
collapseImageType: CollapseImageType
) {
super();
this.rightMargin = $r('sys.float.ohos_id_elements_margin_horizontal_m');
this.imageSource = imageSource;
this.symbolIconSource = symbolSource;
this.rightMargin = itemRightMargin;
this.imageOpacity = imageOpacity;
this.itemWidth = itemWidth;
this.itemHeight = itemHeight;
this.imageCollapseSource = imageSource;
this.symbolIconCollapseSource = symbolSource;
this.isImageCollapse = isImageCollapse;
this.collapseImageType = collapseImageType;
}
get source(): Resource | string {
return this.imageSource;
}
get symbolSource(): SymbolGlyphModifier | undefined {
return this.symbolIconSource;
}
get opacity(): Resource {
return this.imageOpacity;
}
get noOpacity(): number {
return 1;
}
get collapseSource(): Resource | string {
return this.imageCollapseSource;
}
get symbolCollapseSource(): SymbolGlyphModifier | undefined {
return this.symbolIconCollapseSource;
}
get isCollapse(): boolean {
return this.isImageCollapse;
}
get type(): CollapseImageType {
return this.collapseImageType;
}
}
class CollapseImageNodeFactory {
private static instance: CollapseImageNodeFactory;
private constructor() {
}
/**
* CollapseImageNodeFactory singleton function
*
* @returns CollapseImageNodeFactory
*/
public static getInstance(): CollapseImageNodeFactory {
if (!CollapseImageNodeFactory.instance) {
CollapseImageNodeFactory.instance = new CollapseImageNodeFactory();
}
return CollapseImageNodeFactory.instance;
}
/**
* create collapse image node by type
*
* @param type collapse image type
* @returns collapse image node
*/
public createCollapseImageNodeByType(type: CollapseImageType): CollapseImageNode {
let imageSource: Resource;
switch (type) {
case CollapseImageType.ARROW_RIGHT_WHITE:
imageSource = ARROW_RIGHT_WITHE;
break;
case CollapseImageType.ARROW_RIGHT:
imageSource = ARROW_RIGHT;
break;
case CollapseImageType.ARROW_DOWN_WHITE:
imageSource = ARROW_DOWN_WITHE;
break;
default:
imageSource = ARROW_DOWN;
}
return new CollapseImageNode(
imageSource,
undefined,
$r('sys.float.ohos_id_alpha_content_tertiary'),
IMAGE_NODE_HEIGHT,
IMAGE_NODE_WIDTH,
$r('sys.float.ohos_id_text_paragraph_margin_xs'),
(type === CollapseImageType.ARROW_RIGHT_WHITE || type === CollapseImageType.ARROW_DOWN_WHITE) ? false : true,
type
);
}
}
class CollapseImageNodeFlyweightFactory {
private static nodeMap: Map<CollapseImageType, CollapseImageNode> = new Map<CollapseImageType, CollapseImageNode>();
/**
* get collapse image node by type
*
* @param type collapse image node type
* @returns collapse image node
*/
static getCollapseImageNodeByType(type: CollapseImageType): CollapseImageNode {
let node: CollapseImageNode | undefined = CollapseImageNodeFlyweightFactory.nodeMap.get(type);
if (node === undefined) {
node = CollapseImageNodeFactory.getInstance().createCollapseImageNodeByType(type);
CollapseImageNodeFlyweightFactory.nodeMap.set(type, node);
}
return node;
}
/**
* get collapse image node by interactionStatus and nodeStatus
*
* @param interactionStatus interaction status
* @param nodeStatus node status
* @param defaultType default collapse image type
* @returns collapse image node
*/
static getCollapseImageNode(interactionStatus: InteractionStatus, nodeStatus: NodeStatus | undefined,
defaultType?: CollapseImageType): CollapseImageNode | undefined {
if (defaultType === undefined) {
return undefined;
}
let type: CollapseImageType = defaultType;
if (interactionStatus == InteractionStatus.EDIT ||
interactionStatus === InteractionStatus.DRAG_INSERT) {
if (nodeStatus === NodeStatus.COLLAPSE) {
type = CollapseImageType.ARROW_RIGHT_WHITE;
} else {
type = CollapseImageType.ARROW_DOWN_WHITE;
}
} else if (interactionStatus === InteractionStatus.FINISH_EDIT ||
interactionStatus === InteractionStatus.FINISH_DRAG_INSERT) {
if (nodeStatus === NodeStatus.COLLAPSE) {
type = CollapseImageType.ARROW_RIGHT;
} else {
type = CollapseImageType.ARROW_DOWN;
}
}
return CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(type);
}
/**
* change collapse image node source
*
* @param nodeStatus node status
* @param isImageCollapse whether collapse image or white collapse image
* @returns collapse image node
*/
static changeImageCollapseSource(nodeStatus: NodeStatus, isImageCollapse?: boolean): CollapseImageNode | undefined {
if (isImageCollapse === undefined) {
return undefined;
}
let type: CollapseImageType;
if (!isImageCollapse) {
if (nodeStatus === NodeStatus.COLLAPSE) {
type = CollapseImageType.ARROW_RIGHT_WHITE;
} else {
type = CollapseImageType.ARROW_DOWN_WHITE;
}
} else {
if (nodeStatus === NodeStatus.COLLAPSE) {
type = CollapseImageType.ARROW_RIGHT;
} else {
type = CollapseImageType.ARROW_DOWN;
}
}
return CollapseImageNodeFlyweightFactory.getCollapseImageNodeByType(type);
}
}
export class ImageNode extends NodeBaseInfo {
private imageSource: Resource | string;
private symbolIconSource: SymbolGlyphModifier | undefined;
private imageNormalSource: Resource | string;
private symbolIconNormalSource: SymbolGlyphModifier | undefined;
private imageSelectedSource: Resource | string;
private symbolIconSelectedSource: SymbolGlyphModifier | undefined;
private imageEditSource: Resource | string;
private symbolIconEditSource: SymbolGlyphModifier | undefined;
private imageOpacity: Resource;
private currentInteractionStatus: InteractionStatus;
private imageCollapseSource: Resource | string;
private imageCollapseDownSource: Resource | string;
private isImageCollapse: boolean;
private imageCollapseRightSource: Resource | string;
constructor(
imageSource: Resource | string,
symbolSource: SymbolGlyphModifier | undefined,
imageOpacity: Resource,
itemWidth: number,
itemHeight: number,
itemSelectedIcon?: Resource | string,
symbolSelectedIcon?: SymbolGlyphModifier | undefined,
itemEditIcon?: Resource | string,
symbolEditIcon?: SymbolGlyphModifier | undefined,
) {
super();
this.rightMargin = $r('sys.float.ohos_id_elements_margin_horizontal_m');
this.imageSource = imageSource;
this.symbolIconSource = symbolSource;
this.imageNormalSource = imageSource;
this.symbolIconNormalSource = symbolSource;
if (itemSelectedIcon !== undefined) {
this.imageSelectedSource = itemSelectedIcon;
} else {
this.imageSelectedSource = this.imageNormalSource;
}
this.symbolIconSelectedSource = symbolSelectedIcon;
if (itemEditIcon !== undefined) {
this.imageEditSource = itemEditIcon;
} else {
this.imageEditSource = this.imageNormalSource;
}
this.symbolIconEditSource = symbolEditIcon;
this.imageOpacity = imageOpacity;
this.itemWidth = itemWidth;
this.itemHeight = itemHeight;
this.imageCollapseSource = imageSource;
this.imageCollapseDownSource = ARROW_DOWN;
this.imageCollapseRightSource = ARROW_RIGHT;
this.isImageCollapse = true;
this.currentInteractionStatus = InteractionStatus.NORMAL;
}
get source(): Resource | string {
return this.imageSource;
}
get symbolSource(): SymbolGlyphModifier | undefined {
return this.symbolIconSource;
}
get normalSource(): Resource | string {
return this.imageNormalSource;
}
get symbolNormalSource(): SymbolGlyphModifier | undefined {
return this.symbolIconNormalSource;
}
get selectedSource(): Resource | string {
return this.imageSelectedSource;
}
get symbolSelectedSource(): SymbolGlyphModifier | undefined {
return this.symbolIconSelectedSource;
}
get editSource(): Resource | string {
return this.imageEditSource;
}
get symbolEditSource(): SymbolGlyphModifier | undefined {
return this.symbolIconEditSource;
}
get opacity(): Resource {
return this.imageOpacity;
}
get noOpacity(): number {
return 1;
}
get collapseSource(): Resource | string {
return this.imageCollapseSource;
}
get isCollapse(): boolean {
return this.isImageCollapse;
}
changeImageCollapseSource(nodeStatus: NodeStatus): void {
if (nodeStatus === NodeStatus.EXPAND) {
this.imageCollapseSource = this.imageCollapseDownSource;
} else if (nodeStatus === NodeStatus.COLLAPSE) {
this.imageCollapseSource = this.imageCollapseRightSource;
}
}
setImageCollapseSource(interactionStatus: InteractionStatus, nodeStatus: NodeStatus | undefined): void {
if (interactionStatus === InteractionStatus.EDIT || interactionStatus === InteractionStatus.DRAG_INSERT) {
this.imageCollapseDownSource = ARROW_DOWN_WITHE;
this.imageCollapseRightSource = ARROW_RIGHT_WITHE;
this.isImageCollapse = false;
} else if (interactionStatus === InteractionStatus.FINISH_EDIT ||
interactionStatus === InteractionStatus.FINISH_DRAG_INSERT) {
this.imageCollapseDownSource = ARROW_DOWN
this.imageCollapseRightSource = ARROW_RIGHT
this.isImageCollapse = true;
}
this.imageCollapseSource = (nodeStatus === NodeStatus.COLLAPSE) ?
this.imageCollapseRightSource : this.imageCollapseDownSource;
}
setImageSource(interactionStatus: InteractionStatus): void {
switch (interactionStatus) {
case InteractionStatus.NORMAL:
this.imageSource = this.imageNormalSource;
this.symbolIconSource = this.symbolIconNormalSource;
this.currentInteractionStatus = interactionStatus;
break;
case InteractionStatus.SELECTED:
if (this.currentInteractionStatus !== InteractionStatus.EDIT) {
this.imageSource = this.imageSelectedSource;
this.symbolIconSource = this.symbolIconSelectedSource;
this.currentInteractionStatus = interactionStatus;
}
break;
case InteractionStatus.EDIT:
this.imageSource = this.imageEditSource;
this.symbolIconSource = this.symbolIconEditSource;
this.currentInteractionStatus = interactionStatus;
break;
case InteractionStatus.FINISH_EDIT:
this.imageSource = this.imageSelectedSource;
this.symbolIconSource = this.symbolIconSelectedSource;
this.currentInteractionStatus = interactionStatus;
break;
case InteractionStatus.DRAG_INSERT:
this.imageSource = this.imageEditSource;
this.symbolIconSource = this.symbolIconEditSource;
this.currentInteractionStatus = interactionStatus;
break;
case InteractionStatus.FINISH_DRAG_INSERT:
this.imageSource = this.imageNormalSource;
this.symbolIconSource = this.symbolIconNormalSource;
this.currentInteractionStatus = interactionStatus;
break;
default:
break;
}
}
}
class MainTitleNode extends NodeBaseInfo {
private mainTitleName: ResourceStr;
public mainTitleSetting: TextSetting;
private showPopUpTimeout: number;
private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance();
constructor(mainTitleName: ResourceStr) {
super();
this.mainTitleName = mainTitleName;
this.itemWidth = ITEM_WIDTH;
this.itemHeight = ITEM_HEIGHT;
this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs');
this.mainTitleSetting = {
fontColor: this.treeViewTheme.primaryTitleFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Normal,
};
this.showPopUpTimeout = 0;
}
setMainTitleSelected(isSelected: boolean): void {
if (isSelected) {
this.mainTitleSetting = {
fontColor: this.treeViewTheme.primaryTitleActiveFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Regular,
};
} else {
this.mainTitleSetting = {
fontColor: this.treeViewTheme.primaryTitleFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Normal,
};
}
}
set title(text: ResourceStr) {
this.mainTitleName = text;
}
get title(): ResourceStr {
return this.mainTitleName;
}
set popUpTimeout(showPopUpTimeout: number) {
this.showPopUpTimeout = showPopUpTimeout;
}
get popUpTimeout(): number {
return this.showPopUpTimeout;
}
get color(): ResourceColor {
return this.mainTitleSetting.fontColor;
}
get size(): Resource {
return this.mainTitleSetting.fontSize;
}
get weight(): FontWeight {
return this.mainTitleSetting.fontWeight;
}
setMainTitleHighLight(isHighLight: boolean): void {
if (isHighLight) {
this.mainTitleSetting = {
fontColor: this.treeViewTheme.primaryTitleActiveFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Regular,
};
} else {
this.mainTitleSetting = {
fontColor: this.treeViewTheme.primaryTitleFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Normal,
};
}
}
}
export class InputText extends NodeBaseInfo {
private inputTextSetting: TextSetting;
private status?: Status = undefined;
private statusColor: Resource = $r('sys.color.ohos_id_color_background');
private editItemColor: Resource = $r('sys.color.ohos_id_color_emphasize');
private radius: Resource = $r('sys.float.ohos_id_corner_radius_default_xs');
private treeViewTheme: TreeViewTheme = TreeViewTheme.getInstance();
constructor() {
super();
this.itemWidth = ITEM_WIDTH;
this.itemHeight = ITEM_HEIGHT_INPUT;
this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs');
this.inputTextSetting = {
fontColor: this.treeViewTheme.primaryTitleFontColor,
fontSize: $r('sys.float.ohos_id_text_size_body1'),
fontWeight: FontWeight.Normal,
};
}
get color(): ResourceColor {
return this.inputTextSetting.fontColor;
}
get size(): Resource {
return this.inputTextSetting.fontSize;
}
get weight(): FontWeight {
return this.inputTextSetting.fontWeight;
}
get borderRadius(): Resource {
return this.radius;
}
get backgroundColor(): Resource {
return this.statusColor;
}
get editColor(): Resource {
return this.editItemColor;
}
get textInputStatusColor(): Status | undefined {
return this.status;
}
}
/**
* get LengthMetrics
*
* @param Resource | number type
* @returns LengthMetrics
*/
function getLengthMetricsByResourceOrNumber(resourceOrNumber: Resource | number): LengthMetrics {
if (!resourceOrNumber) {
return LengthMetrics.vp(0);
} else if (typeof resourceOrNumber === 'number') {
return LengthMetrics.vp(resourceOrNumber);
} else {
return LengthMetrics.resource(resourceOrNumber);
}
}