FguiMgr.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. import { StringMap } from "../../Collections/StringMap";
  2. import Config from "../../Config";
  3. import { gameMethod } from "../../common/gameMethod";
  4. import GameDataCenter from "../../data/GameDataCenter";
  5. import { GUIEvent } from "../../data/const/EventConst";
  6. import { PlatFormDevType } from "../../data/const/TypeConst";
  7. import { ViewZOrder } from "../../data/const/ViewZOrder";
  8. import EventMng from "../../manager/EventMng";
  9. import { Singleton } from "../../manager/Singleton";
  10. import { PromiseDefault } from "../Promise/SpecialPromise";
  11. import { ResCollector } from "../compment/ResCollector";
  12. import { ResKeeper } from "../compment/ResKeeper";
  13. import FguiLoadMgr from "./FguiLoadMgr";
  14. import { IDisposable } from "./Interface/IDisposable";
  15. import { ILateUpdate } from "./Interface/ILateUpdate";
  16. import { IUpdate } from "./Interface/IUpdate";
  17. import { FguiClass, FguiView, ViewType } from "./mvc/FguiView";
  18. export default class FguiMgr extends Singleton<FguiMgr>() implements IUpdate, ILateUpdate, IDisposable {
  19. /** 记录图层节点的Map */
  20. private _layerNodeMap: Map<ViewZOrder, fgui.GComponent> = new Map();
  21. /** 已开启过的ui */
  22. private _opened: StringMap<FguiView> = new StringMap<FguiView>();
  23. /** 缓存被关闭的ui */
  24. private _closed: StringMap<FguiView> = new StringMap<FguiView>();
  25. /** 记录所加载的包 */
  26. private _addPkged: StringMap<number> = new StringMap<number>();
  27. Init(): void {
  28. fgui.GRoot.create();
  29. fgui.GRoot.inst.width = Config.safeAreaRect.width
  30. fgui.GRoot.inst.height = Config.realHeight
  31. fgui.GRoot.inst.node.y = Config.realHeight + Config.safeAreaRect.y / 2
  32. this._layerNodeMap = new Map();
  33. // 创建所有UI层级
  34. Object.values(ViewZOrder).forEach((layer) => {
  35. if (typeof layer === 'number') {
  36. const layerName = ViewZOrder[layer];
  37. this.createLayer(layerName + 'Layer', layer);
  38. }
  39. });
  40. }
  41. private createLayer(name: string, zOrder: number): fgui.GComponent {
  42. const layer = new fgui.GComponent();
  43. layer.name = name;
  44. layer.node.name = name;
  45. layer.sortingOrder = zOrder;
  46. fgui.GRoot.inst.addChild(layer);
  47. layer.makeFullScreen();
  48. this._layerNodeMap.set(zOrder, layer);
  49. return layer;
  50. }
  51. /**
  52. * @param callback 打开完毕回调函数
  53. * @param intent 传入到UI的参数
  54. */
  55. public openUI<T extends FguiView>(uiClass: FguiClass<T>, zOrder: number = ViewZOrder.UI, callback?: Function, intent?: any): Promise<void> {
  56. if (CC_PREVIEW) console.log(`打开页面${uiClass.getViewName()}`);
  57. const viewKey = uiClass.getViewName();
  58. if (this._opened.ContainsKey(viewKey)) {
  59. console.log(`FguiMgr::Open ${viewKey} has already opened.`);
  60. return;
  61. }
  62. if (this.getCacheView(uiClass, zOrder, callback)) {
  63. return;
  64. }
  65. let pkgName = uiClass.getPkgName();
  66. EventMng.emit(GUIEvent.SHOW_MASK, pkgName, true);
  67. if (fgui.UIPackage.getByName(pkgName) == null) {
  68. // 加载uipackage
  69. FguiLoadMgr.loadPackage(pkgName, pkgName, async (error, pkg) => {
  70. if (error) {
  71. EventMng.emit(GUIEvent.SHOW_MASK, pkgName, false)
  72. console.error(`FguiMgr LoadPackage[${pkgName}] error: ${error}`);
  73. return;
  74. }
  75. this.CreateView(uiClass, zOrder, intent, callback);
  76. });
  77. }
  78. else {
  79. this.CreateView(uiClass, zOrder, intent, callback);
  80. }
  81. }
  82. /** 从缓存中加载页面 */
  83. private getCacheView<T extends FguiView>(uiClass: FguiClass<T>, zOrder: number = ViewZOrder.UI, callback?: Function, intent?: any): boolean {
  84. const viewKey = uiClass.getViewName();
  85. if (this._closed.ContainsKey(viewKey)) {
  86. const uiview = this._closed.Value(viewKey);
  87. if (uiview.viewType == ViewType.Full) {
  88. this._opened.Foreach((key: string, value: FguiView) => {
  89. if (value.viewType != ViewType.Part && value.zOrder <= uiview.zOrder) {
  90. value.addFullHideCount(1);
  91. }
  92. })
  93. }
  94. this._closed.Remove(viewKey);
  95. this._opened.Add(viewKey, uiview);
  96. this.AddPanel(uiview.Panel, zOrder);
  97. uiview.show(intent);
  98. uiview.zOrder = zOrder;
  99. uiview.uiPkgName = uiClass.getPkgName();
  100. //回调
  101. callback && callback(uiview);
  102. return true;
  103. }
  104. return false;
  105. }
  106. private CreateView<T extends FguiView>(uiClass: FguiClass<T>, zOrder: number = ViewZOrder.UI, intent?: any, complete?: Function) {
  107. let handler = new fgui.AsyncOperation();
  108. handler.callback = async (gObject) => {
  109. if (this._opened.ContainsKey(uiClass.getViewName())) {
  110. EventMng.emit(GUIEvent.SHOW_MASK, uiClass.getPkgName(), false)
  111. console.error(`FguiMgr::Open ${uiClass.getViewName()} has already opened.`);
  112. return;
  113. }
  114. let panel = gObject as fgui.GComponent;
  115. if (panel == null) {
  116. EventMng.emit(GUIEvent.SHOW_MASK, uiClass.getPkgName(), false)
  117. console.error(`FguiMgr createObject[${uiClass.getViewName}] error: GObject为空.`);
  118. return;
  119. }
  120. panel.node.name = uiClass.getPrefabName();
  121. let uiview = new uiClass();
  122. uiview.zOrder = zOrder;
  123. uiview.uiPkgName = uiClass.getPkgName();
  124. uiview.initForward(panel, null);
  125. const resKeeper = ResKeeper.Get(panel.node, true);
  126. let resCollector = new ResCollector();
  127. uiview.Controller.onCollectRes(resCollector, intent);
  128. if (resCollector.GetResList().length > 0) {
  129. await resKeeper.LoadResCollector(resCollector, PromiseDefault);
  130. }
  131. if (uiview.viewType == ViewType.Full) {
  132. this._opened.Foreach((key: string, value: FguiView) => {
  133. if (value.viewType != ViewType.Part && value.zOrder <= uiview.zOrder) {
  134. value.addFullHideCount(1);
  135. }
  136. })
  137. }
  138. this._opened.Add(uiClass.getViewName(), uiview);
  139. uiview.init();
  140. this.AddPanel(panel, zOrder);
  141. uiview.show(intent);
  142. this.AddPkgCount(uiClass.getPkgName(), uiClass.getViewName());
  143. EventMng.emit(GUIEvent.SHOW_MASK, uiClass.getPkgName(), false)
  144. complete && complete(uiview);
  145. };
  146. handler.createObject(uiClass.getPkgName(), uiClass.getPrefabName());
  147. }
  148. /**
  149. * @param isDispose 是否销毁
  150. */
  151. public closeUI<T extends FguiView>(uiClass: FguiClass<T>, isDispose: boolean = false) {
  152. const viewKey = uiClass.getViewName();
  153. if (this._opened.ContainsKey(viewKey)) {
  154. const view = this._opened.Value(viewKey);
  155. this._opened.Remove(viewKey);
  156. view.hide(isDispose);
  157. this.RemovePanel(view.Panel);
  158. if (isDispose) {
  159. this.DestroyView(view, uiClass);
  160. }
  161. else {
  162. this._closed.Add(viewKey, view);
  163. }
  164. if (view.viewType == ViewType.Full) {
  165. this._opened.Foreach((key: string, value: FguiView) => {
  166. if (value.viewType != ViewType.Part && value.zOrder <= view.zOrder) {
  167. value.addFullHideCount(-1);
  168. }
  169. })
  170. }
  171. }
  172. else if (isDispose && this._closed.ContainsKey(viewKey)) {
  173. const view = this._closed.Value(viewKey);
  174. this._closed.Remove(viewKey);
  175. this.DestroyView(view, uiClass);
  176. }
  177. }
  178. // 判断界面是否打开
  179. public isShowing<T extends FguiView>(uiClass: FguiClass<T>) {
  180. const viewKey = uiClass?.getViewName();
  181. if (this._opened.ContainsKey(viewKey)) {
  182. return true
  183. }
  184. return false
  185. }
  186. // 获取打开的界面
  187. public getShowingView<T extends FguiView>(uiClass: FguiClass<T>) {
  188. const viewKey = uiClass?.getViewName();
  189. if (this._opened.ContainsKey(viewKey)) {
  190. const view = this._opened.Value(viewKey);
  191. return view
  192. }
  193. return null
  194. }
  195. /**
  196. * 当前是否有除了指定页面以外的其他页面被打开
  197. */
  198. public checkOtherUIOpen(uilist: FguiClass<any>[]) {
  199. let list = []
  200. for (let i of uilist) {
  201. list.push(i.getViewName())
  202. }
  203. let haveother: boolean = false
  204. this._opened.Foreach((key: string, value: FguiView) => {
  205. if (list.indexOf(key) < 0) {
  206. haveother = true
  207. }
  208. })
  209. return haveother
  210. }
  211. public isShowingByName(uiViewName: string) {
  212. return this._opened.ContainsKey(uiViewName);
  213. }
  214. // 获取打开的界面
  215. public getShowingViewByName(uiViewName: string) {
  216. if (this._opened.ContainsKey(uiViewName)) {
  217. const view = this._opened.Value(uiViewName);
  218. return view
  219. }
  220. return null
  221. }
  222. public findNodeByName(uiViewName: string, goName: string): cc.Node {
  223. if (this._opened.ContainsKey(uiViewName)) {
  224. let view = this._opened.Value(uiViewName);
  225. let goNames = goName.split("/")
  226. let go = null
  227. let goTemp = view["_viewModel"][goNames[0]];
  228. if (gameMethod.isEmpty(goTemp)) {
  229. goTemp = view.Panel.getChild(goNames[0]);
  230. }
  231. for (let i = 1; i < goNames.length; i++) {
  232. if (goTemp?.Panel) {
  233. goTemp = goTemp.Panel?.getChild(goNames[i])
  234. } else {
  235. goTemp = goTemp?.getChild(goNames[i]);
  236. }
  237. }
  238. go = goTemp
  239. if (!gameMethod.isEmpty(go)) {
  240. go.visible = true
  241. } else {
  242. console.error(`界面:${uiViewName} 找不到目标节点: ${goNames}`)
  243. }
  244. return go?.node ?? null;
  245. }
  246. return null;
  247. }
  248. public removePkg<T extends FguiView>(uiClass: FguiClass<T>) {
  249. if (!this._addPkged.ContainsKey(uiClass.getPkgName()) || this._addPkged.Value(uiClass.getPkgName()) == 0) {
  250. fgui.UIPackage.removePackage(uiClass.getPkgName())
  251. } else {
  252. console.warn("removePkg fail")
  253. }
  254. }
  255. public clearOpenUI() {
  256. this._opened.Foreach((viewKey: string, value: FguiView) => {
  257. value.hide();
  258. this.RemovePanel(value.Panel);
  259. this._closed.Add(viewKey, value);
  260. })
  261. this._opened.Clear()
  262. }
  263. public clearAllUI() {
  264. this._opened.Foreach((viewKey: string, value: FguiView) => {
  265. value.hide();
  266. this.RemovePanel(value.Panel);
  267. })
  268. this.clearCacheMap()
  269. // this._addPkged.Foreach((pkgName: string, value: number) => {
  270. // fgui.UIPackage.removePackage(pkgName)
  271. // })
  272. this._addPkged.Clear();
  273. }
  274. public clearCacheMap() {
  275. this._opened.Clear()
  276. this._closed.Clear()
  277. }
  278. OnUpdate(elapseTime: number): void {
  279. this._opened.Foreach((k, v) => {
  280. v.OnUpdate(elapseTime);
  281. }, this);
  282. }
  283. OnLateUpdate(elapseTime: number): void {
  284. this._opened.Foreach((k, v) => {
  285. v.OnLateUpdate(elapseTime);
  286. }, this);
  287. }
  288. Dispose(): void {
  289. //TODO 有需要再补充
  290. }
  291. /** 增加fgui pkg计数 */
  292. public AddPkgCount(pkgName: string, from?: string) {
  293. if (this._addPkged.ContainsKey(pkgName)) {
  294. //已存在++
  295. let pkgCount = this._addPkged.Value(pkgName);
  296. pkgCount++;
  297. this._addPkged.Replace(pkgName, pkgCount)
  298. } else {
  299. this._addPkged.Add(pkgName, 1)
  300. }
  301. if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
  302. console.log(`${from} ${pkgName} count: ${this._addPkged.Value(pkgName)}`)
  303. }
  304. }
  305. /** 减少fgui pkg计数 */
  306. public DelPkgCount(pkgName: string, from?: string) {
  307. if (this._addPkged.ContainsKey(pkgName)) {
  308. //已存在++
  309. let pkgCount = this._addPkged.Value(pkgName);
  310. pkgCount--;
  311. if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
  312. console.log(`${pkgName} count: ${pkgCount}`, from)
  313. }
  314. this._addPkged.Replace(pkgName, pkgCount);
  315. if (pkgCount <= 0 && pkgName != "Common") {
  316. if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
  317. console.log(`removePackage:${pkgName}`, from)
  318. }
  319. fgui.UIPackage.removePackage(pkgName)
  320. }
  321. }
  322. }
  323. AddPkgByPkgName(pkgName: string, cb?: Function) {
  324. if (fgui.UIPackage.getByName(pkgName) == null) {
  325. // 加载uipackage
  326. FguiLoadMgr.loadPackage(pkgName, pkgName, (error, pkg) => {
  327. if (error) {
  328. EventMng.emit(GUIEvent.SHOW_MASK, pkgName, false)
  329. console.error(`FguiMgr LoadPackage[${pkgName}] error: ${error}`);
  330. return;
  331. }
  332. this.AddPkgCount(pkgName);
  333. if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
  334. console.log(`${pkgName} count: ${this._addPkged.Value(pkgName)}`)
  335. }
  336. cb && cb();
  337. });
  338. } else {
  339. this.AddPkgCount(pkgName);
  340. if (!gameMethod.isEmpty(GameDataCenter?.login.playerInfo?.switch?.clientLog)) {
  341. console.log(`${pkgName} count: ${this._addPkged.Value(pkgName)}`)
  342. }
  343. cb && cb();
  344. }
  345. }
  346. RemovePkgByPkgName(pkgName: string) {
  347. //ios平台才释放,安卓平台不释放
  348. if (GameDataCenter.plat.instance.deviceOS != PlatFormDevType.iosH5 && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.ios && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.local) {
  349. return;
  350. }
  351. this.DelPkgCount(pkgName);
  352. }
  353. private AddPanel(panel: fgui.GComponent, zOrder: ViewZOrder) {
  354. const layerNode = this._layerNodeMap.get(zOrder);
  355. if (layerNode) {
  356. layerNode.addChild(panel);
  357. } else {
  358. fgui.GRoot.inst.addChild(panel);
  359. }
  360. // UI全屏自适应
  361. panel.makeFullScreen();
  362. }
  363. private RemovePanel(panel: fgui.GComponent) {
  364. fgui.GRoot.inst.removeChild(panel);
  365. }
  366. private DestroyView<T extends FguiView>(uiview: FguiView, uiClass: FguiClass<T>) {
  367. //ios平台才释放,安卓平台不释放
  368. if (GameDataCenter.plat.instance.deviceOS != PlatFormDevType.iosH5 && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.ios && GameDataCenter.plat.instance.deviceOS != PlatFormDevType.local) {
  369. return;
  370. }
  371. var panel = uiview.Panel;
  372. panel.dispose();
  373. this.DelPkgCount(uiClass.getPkgName());
  374. }
  375. public CloseUIByLayer(zOrder: ViewZOrder) {
  376. this._opened.Foreach((viewKey: string, value: FguiView) => {
  377. if (value.zOrder == zOrder) {
  378. const view = this._opened.Value(viewKey);
  379. this._opened.Remove(viewKey);
  380. view.hide();
  381. this.RemovePanel(view.Panel);
  382. this._closed.Add(viewKey, view);
  383. if (view.viewType == ViewType.Full) {
  384. this._opened.Foreach((key: string, value: FguiView) => {
  385. if (value.viewType != ViewType.Part && value.zOrder <= view.zOrder) {
  386. value.addFullHideCount(-1);
  387. }
  388. })
  389. }
  390. }
  391. })
  392. }
  393. /** 关闭所有的UI elseView 排除无需关闭的界面 */
  394. public CloseAllUI(elseView: string[] = []): void {
  395. this._opened.Foreach((viewKey: string, value: FguiView) => {
  396. if (elseView.indexOf(viewKey) < 0) {
  397. const view = this._opened.Value(viewKey);
  398. this._opened.Remove(viewKey);
  399. view.hide();
  400. this.RemovePanel(view.Panel);
  401. this._closed.Add(viewKey, view);
  402. if (view.viewType == ViewType.Full) {
  403. this._opened.Foreach((key: string, value: FguiView) => {
  404. if (value.viewType != ViewType.Part && value.zOrder <= view.zOrder) {
  405. value.addFullHideCount(-1);
  406. }
  407. })
  408. }
  409. }
  410. })
  411. }
  412. //#endregion
  413. }