UIController.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. const { ccclass, property } = cc._decorator;
  2. /***
  3. * @en internal class, used for handling node event.
  4. * @zh 内部类,用于节点事件监听
  5. *
  6. * */
  7. export class __NodeEventAgent__ extends cc.Component {
  8. /***
  9. * @en recieve button click event and deliver them to the real handlers.
  10. * @zh 接受按钮事件,并转发给真正的处理函数
  11. * */
  12. onButtonClicked(evt: cc.Event.EventTouch, customEventData) {
  13. let btn = (evt.target as cc.Node).getComponent(cc.Button);
  14. let clickEvents = btn.clickEvents;
  15. for (let i = 0; i < clickEvents.length; ++i) {
  16. let h = clickEvents[i];
  17. if (h.customEventData == customEventData) {
  18. let cb = h['$cb$'];
  19. let target = h['$target$']
  20. let args = h['$args$'];
  21. cb.apply(target, [btn, args]);
  22. }
  23. }
  24. }
  25. /***
  26. * @en recieve toggle event and deliver them to the real handlers.
  27. * @zh 接受Toggle事件,并转发给真正的处理函数
  28. * */
  29. onToggleEvent(toggle: cc.Toggle, customEventData) {
  30. let checkEvents = toggle.checkEvents;
  31. //if (toggle['_toggleContainer']) {
  32. // checkEvents = toggle['_toggleContainer'].checkEvents;
  33. //}
  34. for (let i = 0; i < checkEvents.length; ++i) {
  35. let h = checkEvents[i];
  36. if (h.customEventData == customEventData) {
  37. let cb = h['$cb$'];
  38. let target = h['$target$']
  39. let args = h['$args$'];
  40. cb.apply(target, [toggle, args]);
  41. }
  42. }
  43. }
  44. }
  45. /**
  46. * @en manage event-handlers automatically, will remove all handlers when the ui destroyed.
  47. * @zh 自动管理事件,将在UI销毁时自动清理
  48. * */
  49. export class AutoEventHandler {
  50. private _handlers = [];
  51. on(event: string, cb: () => void, target?: any, once?: boolean) {
  52. this._handlers.push({
  53. event: event,
  54. cb: cb,
  55. target: target,
  56. once: once
  57. });
  58. cc.game.on(event, cb, target, once);
  59. }
  60. off(event: string, cb: () => void, target?: any, once?: boolean) {
  61. cc.game.off(event, cb, target);
  62. for (let i = 0; i < this._handlers.length; ++i) {
  63. let h = this._handlers[i];
  64. if (h.event == event && h.cb == cb && h.target == target && h.once == once) {
  65. this._handlers.splice(i, 1);
  66. return;
  67. }
  68. }
  69. }
  70. dispose() {
  71. for (let i = 0; i < this._handlers.length; ++i) {
  72. let h = this._handlers[i];
  73. cc.game.off(h.event, h.cb, h.target);
  74. }
  75. }
  76. }
  77. /**
  78. * @en base class of UI Panel
  79. * @zh 各类UI面板基类
  80. * */
  81. export class UIController extends cc.EventTarget {
  82. private static _idBase = 1000;
  83. private static _controllers: UIController[] = [];
  84. private _instId: number = 0;
  85. private _prefab: string | cc.Prefab;
  86. private _layer: number;
  87. private _layout: any;
  88. protected node: cc.Node;
  89. private _destroyed:boolean = false;
  90. constructor(prefab: string | cc.Prefab, layer: number, layoutCls: any) {
  91. super();
  92. this._prefab = prefab;
  93. this._layer = layer;
  94. this._layout = layoutCls;
  95. this._instId = UIController._idBase++;
  96. UIController._controllers.push(this);
  97. }
  98. /***
  99. * @en the instance id to indicate an unique ui panel.
  100. * @zh 实例ID,用于标记一个唯一面板实例
  101. * */
  102. public get instId(): number {
  103. return this._instId;
  104. }
  105. /***
  106. * @en url of the prefab used by this ui panel.
  107. * @zh 本UI使用prefab路径
  108. * */
  109. public get prefab(): string | cc.Prefab {
  110. return this._prefab;
  111. }
  112. /***
  113. * @en layer of this ui panel.
  114. * @zh 本UI所在的UI层级
  115. * */
  116. public get layer(): number {
  117. return this._layer;
  118. }
  119. /***
  120. * @en layout of this ui panel.
  121. * @zh 本UI所在的UI层级
  122. * */
  123. public get layout(): any {
  124. return this._layout;
  125. }
  126. /***
  127. * @en hide and destroy all ui panel.
  128. * @zh 隐藏并销毁所有UI面板
  129. * */
  130. public static hideAll() {
  131. while (this._controllers.length) {
  132. this._controllers[0].hide();
  133. }
  134. }
  135. //update all ui, called by UIMgr.
  136. public static updateAll() {
  137. for (let i = 0; i < this._controllers.length; ++i) {
  138. let ctrl = this._controllers[i];
  139. if (ctrl.node && cc.isValid(ctrl.node)) {
  140. this._controllers[i].onUpdate();
  141. }
  142. }
  143. }
  144. //setup this ui,called by UIMgr.
  145. public setup(node: cc.Node) {
  146. this.node = node;
  147. if (this._layout) {
  148. this._layout = this.node.getComponent(this._layout);
  149. }
  150. //notify sub class to handle something.
  151. //节点创建完毕,调用子类的处理函数。
  152. this.onCreated();
  153. //check whether it has been destroyed, if has, hide it.
  154. //检查是否为已销毁,如果已销毁,则走销毁流程
  155. if(this._destroyed){
  156. this.hide();
  157. }
  158. }
  159. /**
  160. * @en hide and destroy this ui panel.
  161. * @zh 隐藏并销毁此UI面板cc.Component.EventHandler
  162. * */
  163. public hide() {
  164. this._destroyed = true;
  165. if(!this.node){
  166. return;
  167. }
  168. this.node.removeFromParent();
  169. for (let i = 0; i < UIController._controllers.length; ++i) {
  170. if (UIController._controllers[i] == this) {
  171. UIController._controllers.splice(i, 1);
  172. break;
  173. }
  174. }
  175. this.onDispose();
  176. this.node.destroy();
  177. this.node = null;
  178. }
  179. /**
  180. * @en add button event handler
  181. * @zh 添加按钮事件
  182. * @param relativeNodePath to indicate a button node, can pass `string`|`cc.Node`|`cc.Button` here.
  183. * @param cb will be called when event emits. method format:(btn:cc.Button,args:any)=>void
  184. * @param target the `this` argument of `cb`
  185. * */
  186. onButtonEvent(relativeNodePath: string | cc.Node | cc.Button, cb: Function, target?: any, args?: any) {
  187. let buttonNode: cc.Node = null;
  188. if (relativeNodePath instanceof cc.Node) {
  189. buttonNode = relativeNodePath;
  190. }
  191. else if (relativeNodePath instanceof cc.Button) {
  192. buttonNode = relativeNodePath.node;
  193. }
  194. else {
  195. buttonNode = cc.find(relativeNodePath, this.node);
  196. }
  197. if (!buttonNode) {
  198. return null;
  199. }
  200. //添加转发器
  201. let agent = this.node.getComponent(__NodeEventAgent__);
  202. if (!agent) {
  203. agent = this.node.addComponent(__NodeEventAgent__);
  204. }
  205. let btn = buttonNode.getComponent(cc.Button);
  206. let clickEvents = btn.clickEvents;
  207. let handler = new cc.Component.EventHandler();
  208. handler.target = this.node;
  209. handler.component = '__NodeEventAgent__';
  210. handler.handler = 'onButtonClicked';
  211. handler.customEventData = '' + UIController._idBase++;
  212. //附加额外信息 供事件转发使用
  213. handler['$cb$'] = cb;
  214. handler['$target$'] = target;
  215. handler['$args$'] = args;
  216. clickEvents.push(handler);
  217. btn.clickEvents = clickEvents;
  218. }
  219. /**
  220. * @en remove button event handler
  221. * @zh 移除按钮事件
  222. * @param relativeNodePath to indicate a button node, can pass `string`|`cc.Node`|`cc.Button` here.
  223. * @param cb will be called when event emits.
  224. * @param target the `this` argument of `cb`
  225. * */
  226. offButtonEvent(relativeNodePath: string | cc.Node | cc.Button, cb: Function, target: any) {
  227. let buttonNode: cc.Node = null;
  228. if (relativeNodePath instanceof cc.Node) {
  229. buttonNode = relativeNodePath;
  230. }
  231. else if (relativeNodePath instanceof cc.Button) {
  232. buttonNode = relativeNodePath.node;
  233. }
  234. else {
  235. buttonNode = cc.find(relativeNodePath, this.node);
  236. }
  237. if (!buttonNode) {
  238. return; ``
  239. }
  240. let agent = this.node.getComponent(__NodeEventAgent__);
  241. if (!agent) {
  242. return;
  243. }
  244. let btn = buttonNode.getComponent(cc.Button);
  245. if (!btn) {
  246. return;
  247. }
  248. let clickEvents = btn.clickEvents;
  249. for (let i = 0; i < clickEvents.length; ++i) {
  250. let h = clickEvents[i];
  251. if (h['$cb$'] == cb && h['$target$'] == target) {
  252. clickEvents.splice(i, 1);
  253. btn.clickEvents = clickEvents;
  254. break;
  255. }
  256. }
  257. }
  258. /**
  259. * @en add toggle event handler
  260. * @zh 添加Toggle事件
  261. * @param relativeNodePath to indicate a button node, can pass `string`|`cc.Node`|`cc.Button` here.
  262. * @param cb will be called when event emits. method format:(btn:cc.Button,args:any)=>void
  263. * @param target the `this` argument of `cb`
  264. * */
  265. onToggleEvent(relativeNodePath: string | cc.Node | cc.Button | cc.ToggleContainer, cb: Function, target?: any, args?: any) {
  266. let buttonNode: cc.Node = null;
  267. if (relativeNodePath instanceof cc.Node) {
  268. buttonNode = relativeNodePath;
  269. }
  270. else if (relativeNodePath instanceof cc.Button) {
  271. buttonNode = relativeNodePath.node;
  272. }
  273. else if (relativeNodePath instanceof cc.ToggleContainer) {
  274. buttonNode = relativeNodePath.node;
  275. }
  276. else {
  277. buttonNode = cc.find(relativeNodePath, this.node);
  278. }
  279. if (!buttonNode) {
  280. return null;
  281. }
  282. //添加转发器
  283. let agent = this.node.getComponent(__NodeEventAgent__);
  284. if (!agent) {
  285. agent = this.node.addComponent(__NodeEventAgent__);
  286. }
  287. let btn = buttonNode.getComponent(cc.Button) as any;
  288. if (!btn) {
  289. btn = buttonNode.getComponent(cc.ToggleContainer) as any;
  290. }
  291. let checkEvents = btn.checkEvents;
  292. let handler = new cc.Component.EventHandler();
  293. handler.target = this.node;
  294. handler.component = '__NodeEventAgent__';
  295. handler.handler = 'onToggleEvent';
  296. handler.customEventData = '' + UIController._idBase++;
  297. //附加额外信息 供事件转发使用
  298. handler['$cb$'] = cb;
  299. handler['$target$'] = target;
  300. handler['$args$'] = args;
  301. checkEvents.push(handler);
  302. btn.checkEvents = checkEvents;
  303. }
  304. /**
  305. * @en remove toggle event handler
  306. * @zh 移除Toggle事件
  307. * @param relativeNodePath to indicate a button node, can pass `string`|`cc.Node`|`cc.Button` here.
  308. * @param cb will be called when event emits. method format:(btn:cc.Button,args:any)=>void
  309. * @param target the `this` argument of `cb`
  310. * */
  311. offToggleEvent(relativeNodePath: string | cc.Node | cc.Button | cc.ToggleContainer, cb: Function, target: any) {
  312. let buttonNode: cc.Node = null;
  313. if (relativeNodePath instanceof cc.Node) {
  314. buttonNode = relativeNodePath;
  315. }
  316. else if (relativeNodePath instanceof cc.Button) {
  317. buttonNode = relativeNodePath.node;
  318. }
  319. else if (relativeNodePath instanceof cc.ToggleContainer) {
  320. buttonNode = relativeNodePath.node;
  321. }
  322. else {
  323. buttonNode = cc.find(relativeNodePath, this.node);
  324. }
  325. if (!buttonNode) {
  326. return null;
  327. }
  328. //添加转发器
  329. let agent = this.node.getComponent(__NodeEventAgent__);
  330. if (!agent) {
  331. return;
  332. }
  333. let btn = buttonNode.getComponent(cc.Button) as any;
  334. if (!btn) {
  335. btn = buttonNode.getComponent(cc.ToggleContainer) as any;
  336. }
  337. let checkEvents = btn.checkEvents;
  338. for (let i = 0; i < checkEvents.length; ++i) {
  339. let h = checkEvents[i];
  340. if (h['$cb$'] == cb && h['$target$'] == target) {
  341. checkEvents.splice(i, 1);
  342. btn.checkEvents = checkEvents;
  343. break;
  344. }
  345. }
  346. }
  347. /***
  348. * @en the extra resource needed by this ui panel.the ui will not be created until these res loaded.
  349. * @zh 本UI使用的依赖资源.UI会等这些资源加载完成后才创建。
  350. * */
  351. public getRes(): [] {
  352. return [];
  353. }
  354. //子类的所有操作,需要在这个函数之后。
  355. protected onCreated() { }
  356. //销毁
  357. protected onDispose() { }
  358. //
  359. protected onUpdate() { }
  360. }