UniWindow.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.UI;
  6. using YooAsset;
  7. namespace UniFramework.Window
  8. {
  9. public static class UniWindow
  10. {
  11. public struct WindowInfo
  12. {
  13. public string WindowName;
  14. public int WindowLayer;
  15. public bool IsLoadDone;
  16. }
  17. private static bool _isInitialize = false;
  18. private static GameObject _driver = null;
  19. private static readonly List<UIWindow> _stack = new List<UIWindow>(100);
  20. internal static GameObject Desktop { private set; get; }
  21. /// <summary>
  22. /// 初始化界面系统
  23. /// </summary>
  24. public static void Initalize(GameObject desktop)
  25. {
  26. if (_isInitialize)
  27. throw new Exception($"{nameof(UniWindow)} is initialized !");
  28. if (desktop == null)
  29. throw new ArgumentNullException();
  30. if (_isInitialize == false)
  31. {
  32. // 创建驱动器
  33. _isInitialize = true;
  34. _driver = new UnityEngine.GameObject($"[{nameof(UniWindow)}]");
  35. _driver.AddComponent<UniWindowDriver>();
  36. UnityEngine.Object.DontDestroyOnLoad(_driver);
  37. UniLogger.Log($"{nameof(UniWindow)} initalize !");
  38. Desktop = desktop;
  39. }
  40. }
  41. /// <summary>
  42. /// 销毁界面系统
  43. /// </summary>
  44. public static void Destroy()
  45. {
  46. if (_isInitialize)
  47. {
  48. CloseAll();
  49. _isInitialize = false;
  50. if (_driver != null)
  51. GameObject.Destroy(_driver);
  52. UniLogger.Log($"{nameof(UniWindow)} destroy all !");
  53. }
  54. }
  55. /// <summary>
  56. /// 更新界面系统
  57. /// </summary>
  58. internal static void Update()
  59. {
  60. if (_isInitialize)
  61. {
  62. int count = _stack.Count;
  63. for (int i = 0; i < _stack.Count; i++)
  64. {
  65. if (_stack.Count != count)
  66. break;
  67. var window = _stack[i];
  68. window.InternalUpdate();
  69. }
  70. }
  71. }
  72. /// <summary>
  73. /// 设置屏幕安全区域(异形屏支持)
  74. /// </summary>
  75. /// <param name="safeRect">安全区域</param>
  76. public static void ApplyScreenSafeRect(Rect safeRect)
  77. {
  78. CanvasScaler scaler = Desktop.GetComponentInParent<CanvasScaler>();
  79. if (scaler == null)
  80. {
  81. UniLogger.Error($"Not found {nameof(CanvasScaler)} !");
  82. return;
  83. }
  84. // Convert safe area rectangle from absolute pixels to UGUI coordinates
  85. float rateX = scaler.referenceResolution.x / Screen.width;
  86. float rateY = scaler.referenceResolution.y / Screen.height;
  87. float posX = (int)(safeRect.position.x * rateX);
  88. float posY = (int)(safeRect.position.y * rateY);
  89. float width = (int)(safeRect.size.x * rateX);
  90. float height = (int)(safeRect.size.y * rateY);
  91. float offsetMaxX = scaler.referenceResolution.x - width - posX;
  92. float offsetMaxY = scaler.referenceResolution.y - height - posY;
  93. // 注意:安全区坐标系的原点为左下角
  94. var rectTrans = Desktop.transform as RectTransform;
  95. rectTrans.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量
  96. rectTrans.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量
  97. }
  98. /// <summary>
  99. /// 模拟IPhoneX异形屏
  100. /// </summary>
  101. public static void SimulateIPhoneXNotchScreen()
  102. {
  103. Rect rect;
  104. if (Screen.height > Screen.width)
  105. {
  106. // 竖屏Portrait
  107. float deviceWidth = 1125;
  108. float deviceHeight = 2436;
  109. rect = new Rect(0f / deviceWidth, 102f / deviceHeight, 1125f / deviceWidth, 2202f / deviceHeight);
  110. }
  111. else
  112. {
  113. // 横屏Landscape
  114. float deviceWidth = 2436;
  115. float deviceHeight = 1125;
  116. rect = new Rect(132f / deviceWidth, 63f / deviceHeight, 2172f / deviceWidth, 1062f / deviceHeight);
  117. }
  118. Rect safeArea = new Rect(Screen.width * rect.x, Screen.height * rect.y, Screen.width * rect.width, Screen.height * rect.height);
  119. ApplyScreenSafeRect(safeArea);
  120. }
  121. /// <summary>
  122. /// 获取窗口堆栈信息
  123. /// </summary>
  124. public static void GetWindowInfos(List<WindowInfo> output)
  125. {
  126. if (output == null)
  127. output = new List<WindowInfo>();
  128. else
  129. output.Clear();
  130. for (int i = 0; i < _stack.Count; i++)
  131. {
  132. var window = _stack[i];
  133. WindowInfo info = new WindowInfo();
  134. info.WindowName = window.WindowName;
  135. info.WindowLayer = window.WindowLayer;
  136. info.IsLoadDone = window.IsLoadDone;
  137. output.Add(info);
  138. }
  139. }
  140. /// <summary>
  141. /// 获取所有层级下顶部的窗口名称
  142. /// </summary>
  143. public static string GetTopWindow()
  144. {
  145. if (_stack.Count == 0)
  146. return string.Empty;
  147. UIWindow topWindow = _stack[_stack.Count - 1];
  148. return topWindow.WindowName;
  149. }
  150. /// <summary>
  151. /// 获取指定层级下顶部的窗口名称
  152. /// </summary>
  153. public static string GetTopWindow(int layer)
  154. {
  155. UIWindow lastOne = null;
  156. for (int i = 0; i < _stack.Count; i++)
  157. {
  158. if (_stack[i].WindowLayer == layer)
  159. lastOne = _stack[i];
  160. }
  161. if (lastOne == null)
  162. return string.Empty;
  163. return lastOne.WindowName;
  164. }
  165. /// <summary>
  166. /// 是否有任意窗口正在加载
  167. /// </summary>
  168. public static bool IsAnyLoading()
  169. {
  170. for (int i = 0; i < _stack.Count; i++)
  171. {
  172. var window = _stack[i];
  173. if (window.IsLoadDone == false)
  174. return true;
  175. }
  176. return false;
  177. }
  178. /// <summary>
  179. /// 查询窗口是否存在
  180. /// </summary>
  181. public static bool HasWindow<T>()
  182. {
  183. return HasWindow(typeof(T));
  184. }
  185. public static bool HasWindow(Type type)
  186. {
  187. return IsContains(type.FullName);
  188. }
  189. /// <summary>
  190. /// 异步打开窗口
  191. /// </summary>
  192. /// <param name="location">资源定位地址</param>
  193. /// <param name="userDatas">用户自定义数据</param>
  194. public static OpenWindowOperation OpenWindowAsync<T>(string location, params System.Object[] userDatas) where T : UIWindow
  195. {
  196. return OpenWindowAsync(typeof(T), location, userDatas);
  197. }
  198. public static OpenWindowOperation OpenWindowAsync(Type type, string location, params System.Object[] userDatas)
  199. {
  200. string windowName = type.FullName;
  201. // 如果窗口已经存在
  202. if (IsContains(windowName))
  203. {
  204. UIWindow window = GetWindow(windowName);
  205. Pop(window); //弹出窗口
  206. Push(window); //重新压入
  207. window.TryInvoke(OnWindowPrepare, userDatas);
  208. var operation = new OpenWindowOperation(window.Handle);
  209. YooAssets.StartOperation(operation);
  210. return operation;
  211. }
  212. else
  213. {
  214. UIWindow window = CreateInstance(type);
  215. Push(window); //首次压入
  216. window.InternalLoad(location, OnWindowPrepare, userDatas);
  217. var operation = new OpenWindowOperation(window.Handle);
  218. YooAssets.StartOperation(operation);
  219. return operation;
  220. }
  221. }
  222. /// <summary>
  223. /// 同步打开窗口
  224. /// </summary>
  225. /// <typeparam name="T">窗口类</typeparam>
  226. /// <param name="location">资源定位地址</param>
  227. /// <param name="userDatas">用户自定义数据</param>
  228. public static OpenWindowOperation OpenWindowSync<T>(string location, params System.Object[] userDatas) where T : UIWindow
  229. {
  230. var operation = OpenWindowAsync(typeof(T), location, userDatas);
  231. operation.WaitForAsyncComplete();
  232. return operation;
  233. }
  234. public static OpenWindowOperation OpenWindowSync(Type type, string location, params System.Object[] userDatas)
  235. {
  236. var operation = OpenWindowAsync(type, location, userDatas);
  237. operation.WaitForAsyncComplete();
  238. return operation;
  239. }
  240. /// <summary>
  241. /// 关闭窗口
  242. /// </summary>
  243. public static void CloseWindow<T>() where T : UIWindow
  244. {
  245. CloseWindow(typeof(T));
  246. }
  247. public static void CloseWindow(Type type)
  248. {
  249. string windowName = type.FullName;
  250. UIWindow window = GetWindow(windowName);
  251. if (window == null)
  252. return;
  253. window.InternalDestroy();
  254. Pop(window);
  255. OnSortWindowDepth(window.WindowLayer);
  256. OnSetWindowVisible();
  257. }
  258. /// <summary>
  259. /// 关闭所有窗口
  260. /// </summary>
  261. public static void CloseAll()
  262. {
  263. for (int i = 0; i < _stack.Count; i++)
  264. {
  265. UIWindow window = _stack[i];
  266. window.InternalDestroy();
  267. }
  268. _stack.Clear();
  269. }
  270. private static void OnWindowPrepare(UIWindow window)
  271. {
  272. OnSortWindowDepth(window.WindowLayer);
  273. window.InternalCreate();
  274. window.InternalRefresh();
  275. OnSetWindowVisible();
  276. }
  277. private static void OnSortWindowDepth(int layer)
  278. {
  279. int depth = layer;
  280. for (int i = 0; i < _stack.Count; i++)
  281. {
  282. if (_stack[i].WindowLayer == layer)
  283. {
  284. _stack[i].Depth = depth;
  285. depth += 100; //注意:每次递增100深度
  286. }
  287. }
  288. }
  289. private static void OnSetWindowVisible()
  290. {
  291. bool isHideNext = false;
  292. for (int i = _stack.Count - 1; i >= 0; i--)
  293. {
  294. UIWindow window = _stack[i];
  295. if (isHideNext == false)
  296. {
  297. window.Visible = true;
  298. if (window.IsPrepare && window.FullScreen)
  299. isHideNext = true;
  300. }
  301. else
  302. {
  303. window.Visible = false;
  304. }
  305. }
  306. }
  307. private static UIWindow CreateInstance(Type type)
  308. {
  309. UIWindow window = Activator.CreateInstance(type) as UIWindow;
  310. WindowAttribute attribute = Attribute.GetCustomAttribute(type, typeof(WindowAttribute)) as WindowAttribute;
  311. if (window == null)
  312. throw new Exception($"Window {type.FullName} create instance failed.");
  313. if (attribute == null)
  314. throw new Exception($"Window {type.FullName} not found {nameof(WindowAttribute)} attribute.");
  315. window.Init(type.FullName, attribute.WindowLayer, attribute.FullScreen);
  316. return window;
  317. }
  318. private static UIWindow GetWindow(string name)
  319. {
  320. for (int i = 0; i < _stack.Count; i++)
  321. {
  322. UIWindow window = _stack[i];
  323. if (window.WindowName == name)
  324. return window;
  325. }
  326. return null;
  327. }
  328. private static bool IsContains(string name)
  329. {
  330. for (int i = 0; i < _stack.Count; i++)
  331. {
  332. UIWindow window = _stack[i];
  333. if (window.WindowName == name)
  334. return true;
  335. }
  336. return false;
  337. }
  338. private static void Push(UIWindow window)
  339. {
  340. // 如果已经存在
  341. if (IsContains(window.WindowName))
  342. throw new System.Exception($"Window {window.WindowName} is exist.");
  343. // 获取插入到所属层级的位置
  344. int insertIndex = -1;
  345. for (int i = 0; i < _stack.Count; i++)
  346. {
  347. if (window.WindowLayer == _stack[i].WindowLayer)
  348. insertIndex = i + 1;
  349. }
  350. // 如果没有所属层级,找到相邻层级
  351. if (insertIndex == -1)
  352. {
  353. for (int i = 0; i < _stack.Count; i++)
  354. {
  355. if (window.WindowLayer > _stack[i].WindowLayer)
  356. insertIndex = i + 1;
  357. }
  358. }
  359. // 如果是空栈或没有找到插入位置
  360. if (insertIndex == -1)
  361. {
  362. insertIndex = 0;
  363. }
  364. // 最后插入到堆栈
  365. _stack.Insert(insertIndex, window);
  366. }
  367. private static void Pop(UIWindow window)
  368. {
  369. // 从堆栈里移除
  370. _stack.Remove(window);
  371. }
  372. }
  373. }