import { _decorator, assetManager, Button, Component, EventHandler, EventTouch, find, isValid, Node, Prefab, Toggle, ToggleContainer } from 'cc'; const { ccclass, property } = _decorator; /*** * @en internal class, used for handling node event. * @zh 内部类,用于节点事件监听 * * */ @ccclass('tgxNodeEventAgent') class __NodeEventAgent__ extends Component { /*** * @en recieve button click event and deliver them to the real handlers. * @zh 接受按钮事件,并转发给真正的处理函数 * */ onButtonClicked(evt: EventTouch, customEventData) { let btn = (evt.target as Node).getComponent(Button); let clickEvents = btn.clickEvents; for (let i = 0; i < clickEvents.length; ++i) { let h = clickEvents[i]; if (h.customEventData == customEventData) { let cb = h['$cb$']; let target = h['$target$'] let args = h['$args$']; cb.apply(target, [btn, args]); } } } /*** * @en recieve toggle event and deliver them to the real handlers. * @zh 接受Toggle事件,并转发给真正的处理函数 * */ onToggleEvent(toggle: Toggle, customEventData) { let checkEvents = toggle.checkEvents; //if (toggle['_toggleContainer']) { // checkEvents = toggle['_toggleContainer'].checkEvents; //} for (let i = 0; i < checkEvents.length; ++i) { let h = checkEvents[i]; if (h.customEventData == customEventData) { let cb = h['$cb$']; let target = h['$target$'] let args = h['$args$']; cb.apply(target, [toggle, args]); } } } } let _id:number=0; export default class ui_base { private static _clss:Set=new Set(); private static _uis: ui_base[] = []; /*** * @en hide and destroy all ui panel. * @zh 隐藏并销毁所有UI面板 * */ public static closeAll() { while (this._uis.length) { this._uis[0].close(); } this._clss.clear(); } // public static closeAndReleaseAll(){ while (this._uis.length) { this._uis[0].closeAndRelease(); } this._clss.clear(); } //update all ui, called by UI. public static updateAll(dt:number) { for (let i = 0; i < this._uis.length; ++i) { let ctrl = this._uis[i]; if (ctrl.node && isValid(ctrl.node)) { this._uis[i].onUpdate(dt); } } } private static _addCls(cls:any):void{ if(!cls) return;this._clss.add(cls); } private static _removeCls(cls:any):void{ if(!cls) return;this._clss.delete(cls); } private static _hasCls(cls:any):boolean{ return this._clss.has(cls); } private _id: number = 0; private _bundle:string; private _prefab: string; private _layer: number; private _layout: any; private _cls:any; protected node: Node; private _destroyed:boolean = false; /*** * @en the instance id to indicate an unique ui panel. * @zh 实例ID,用于标记一个唯一面板实例 * */ public get id(): number {return this._id;} /*** * @en url of the prefab used by this ui panel. * @zh 本UI使用prefab路径 * */ public get prefab(): string {return this._prefab;} public get bundle():string{return this._bundle;} /*** * @en layer of this ui panel. * @zh 本UI所在的UI层级 * */ public get layer(): number {return this._layer;} /*** * @en layout of this ui panel. * @zh 本UI组件 * */ public get layout(): Component { return this._layout;} public getLayout():T{ return this._layout as T;} constructor(bundle:string,prefab: string, layer: number, layoutCls: any) { this._cls = null; this._bundle=bundle; this._prefab = prefab; this._layer = layer; this._layout = layoutCls; this._id = _id++; } //setup this ui,called by UIMgr. private _setup(cls:any,node: Node,...data: any[]) { ui_base._uis.push(this); this._cls=cls; (ui_base as any)._addCls(this._cls); this.node = node; if (this._layout) this._layout = this.node.getComponent(this._layout); //notify sub class to handle something. //节点创建完毕,调用子类的处理函数。 this.onCreated(...data); //check whether it has been destroyed, if has, hide it. //检查是否为已销毁,如果已销毁,则走销毁流程 if(this._destroyed) this.close(); } /** * @en hide and destroy this ui panel. * @zh 隐藏并销毁此UI面板 * */ public close(){ this._resolve_close?.(); this._resolve_close=null; this._destroyed = true; if(!this.node) return; this.node.removeFromParent(); for (let i = 0; i < ui_base._uis.length; ++i) { if (ui_base._uis[i] == this) { ui_base._uis.splice(i, 1); break; } } this.onDispose(); this.node.destroy(); this.node = null; (ui_base as any)._removeCls(this._cls); this._cls=null; } public closeAndRelease(){ this.close(); assetManager.getBundle(this._bundle)?.release(this._prefab); } private _resolve_close: (() => void) | null = null; /**等待此ui关闭*/ public wait_close():Promise{ if(this._resolve_close) return; return new Promise((resolve) => {this._resolve_close = resolve;}); } /** * @en add button event handler * @zh 添加按钮事件 * @param relativeNodePath to indicate a button node, can pass `string`|`Node`|`Button` here. * @param cb will be called when event emits. method format:(btn:Button,args:any)=>void * @param target the `this` argument of `cb` * */ onButtonEvent(relativeNodePath: string | Node | Button, cb: Function, target?: any, args?: any) { let buttonNode: Node = null; if (relativeNodePath instanceof Node) { buttonNode = relativeNodePath; } else if (relativeNodePath instanceof Button) { buttonNode = relativeNodePath.node; } else { buttonNode = find(relativeNodePath, this.node); } if (!buttonNode) { return null; } //添加转发器 let agent = this.node.getComponent(__NodeEventAgent__); if (!agent) { agent = this.node.addComponent(__NodeEventAgent__); } let btn = buttonNode.getComponent(Button); let clickEvents = btn.clickEvents; let handler = new EventHandler(); handler.target = this.node; handler.component = 'tgxNodeEventAgent'; handler.handler = 'onButtonClicked'; handler.customEventData = '' + _id++; //附加额外信息 供事件转发使用 handler['$cb$'] = cb; handler['$target$'] = target; handler['$args$'] = args; clickEvents.push(handler); btn.clickEvents = clickEvents; } /** * @en remove button event handler * @zh 移除按钮事件 * @param relativeNodePath to indicate a button node, can pass `string`|`Node`|`Button` here. * @param cb will be called when event emits. * @param target the `this` argument of `cb` * */ offButtonEvent(relativeNodePath: string | Node | Button, cb: Function, target: any) { let buttonNode: Node = null; if (relativeNodePath instanceof Node) { buttonNode = relativeNodePath; } else if (relativeNodePath instanceof Button) { buttonNode = relativeNodePath.node; } else { buttonNode = find(relativeNodePath, this.node); } if (!buttonNode) { return; `` } let agent = this.node.getComponent(__NodeEventAgent__); if (!agent) { return; } let btn = buttonNode.getComponent(Button); if (!btn) { return; } let clickEvents = btn.clickEvents; for (let i = 0; i < clickEvents.length; ++i) { let h = clickEvents[i]; if (h['$cb$'] == cb && h['$target$'] == target) { clickEvents.splice(i, 1); btn.clickEvents = clickEvents; break; } } } /** * @en add toggle event handler * @zh 添加Toggle事件 * @param relativeNodePath to indicate a button node, can pass `string`|`Node`|`Button` here. * @param cb will be called when event emits. method format:(btn:Toggle,args:any)=>void * @param target the `this` argument of `cb` */ onToggleEvent(relativeNodePath: string | Node | Toggle | ToggleContainer, cb: Function, target?: any, args?: any) { let buttonNode: Node = null; if (relativeNodePath instanceof Node) { buttonNode = relativeNodePath; } else if (relativeNodePath instanceof Toggle) { buttonNode = relativeNodePath.node; } else if (relativeNodePath instanceof ToggleContainer) { buttonNode = relativeNodePath.node; } else { buttonNode = find(relativeNodePath, this.node); } if (!buttonNode) { return null; } //添加转发器 let agent = this.node.getComponent(__NodeEventAgent__); if (!agent) agent = this.node.addComponent(__NodeEventAgent__); let btn = buttonNode.getComponent(Toggle) as any; if (!btn) btn = buttonNode.getComponent(ToggleContainer) as any; let checkEvents = btn.checkEvents; let handler = new EventHandler(); handler.target = this.node; handler.component = 'tgxNodeEventAgent'; handler.handler = 'onToggleEvent'; handler.customEventData = '' + _id++; //附加额外信息 供事件转发使用 handler['$cb$'] = cb; handler['$target$'] = target; handler['$args$'] = args; checkEvents.push(handler); btn.checkEvents = checkEvents; } /** * @en remove toggle event handler * @zh 移除Toggle事件 * @param relativeNodePath to indicate a button node, can pass `string`|`Node`|`Button` here. * @param cb will be called when event emits. method format:(btn:Toggle,args:any)=>void * @param target the `this` argument of `cb` * */ offToggleEvent(relativeNodePath: string | Node | Toggle | ToggleContainer, cb: Function, target: any) { let buttonNode: Node = null; if (relativeNodePath instanceof Node) { buttonNode = relativeNodePath; } else if (relativeNodePath instanceof Toggle) { buttonNode = relativeNodePath.node; } else if (relativeNodePath instanceof ToggleContainer) { buttonNode = relativeNodePath.node; } else { buttonNode = find(relativeNodePath, this.node); } if (!buttonNode) { return null; } //添加转发器 let agent = this.node.getComponent(__NodeEventAgent__); if (!agent) { return; } let btn = buttonNode.getComponent(Toggle) as any; if (!btn) { btn = buttonNode.getComponent(ToggleContainer) as any; } let checkEvents = btn.checkEvents; for (let i = 0; i < checkEvents.length; ++i) { let h = checkEvents[i]; if (h['$cb$'] == cb && h['$target$'] == target) { checkEvents.splice(i, 1); btn.checkEvents = checkEvents; break; } } } //子类的所有操作,需要在这个函数之后。 protected onCreated(...data: any[]){} //当界面销毁时调用 protected onDispose(){} // protected onUpdate(dt?:number) { } }