FguiMgr.ts 17 KB

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