// Copyright (c) 2025 EFramework Innovation. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. using System; using System.Collections.Generic; using System.Reflection; using EFramework.Unity.Utility; using UnityEngine; using UnityEngine.SceneManagement; namespace EFramework.Unity.MVVM { /// /// XView 提供了业务开发的基础视图,通过适配器模式实现了视图的管理功能。 /// /// /// /// 功能特性 /// - 业务适配器:通过适配器实例控制视图,实现了加载、绑定、显示、排序等功能 /// - 高可用拓展:提供了视图元素特性标记,支持 Unity UI、FairyGUI 等 UI 插件 /// /// 使用手册 /// 1. 定义视图 /// /// 1.1 基础视图 /// /// // 基础视图 /// public class MyView : XView.Base { } /// /// // 模块视图 /// public class MyModularView : XView.Base<MyModularView> { } /// /// 1.2 视图描述 /// /// // 创建描述 /// var meta = new XView.Meta( /// path: "Prefabs/MyView", // 预制体路径 /// fixedRQ: 0, // 固定渲染顺序 /// focus: XView.EventType.Dynamic, // 焦点类型 /// cache: XView.CacheType.Scene, // 缓存类型 /// multiple: false // 是否支持多实例 /// ); /// /// 1.3 事件系统 /// /// 基础示例: /// /// // 注册事件 /// Event.Register(id, callback); /// Event.Register<T1>(id, callback); /// Event.Register<T1, T2>(id, callback); /// Event.Register<T1, T2, T3>(id, callback); /// /// // 注销事件 /// Event.Unregister(id, callback); /// Event.Unregister<T1>(id, callback); /// Event.Unregister<T1, T2>(id, callback); /// Event.Unregister<T1, T2, T3>(id, callback); /// /// // 通知事件 /// Event.Notify(id, manager, args); /// /// 标记示例: /// /// - 事件标记会在视图 `OnEnable` 时自动注册,在视图 `OnDisable` 时自动注销 /// - 若未指定模块类型,则使用当前视图关联的模块 /// /// // 定义模块 /// public class MyModule : XModule.Base<MyModule> { } /// /// // 定义事件 /// public enum MyEvent /// { /// Event1, /// Event2, /// Event3, /// } /// /// // 事件标记 /// public class MyView : XView.Base<MyModule> { /// // 模块事件注册(指定模块类型) /// [XModule.Event(MyEvent.Event1, typeof(MyModule))] /// private void OnEvent1() { } /// /// // 模块事件注册(单次触发) /// [XModule.Event(MyEvent.Event2, typeof(MyModule), true)] /// private void OnEvent2(params object[] args) { } /// /// // 模块事件注册(当前视图关联的模块) /// [XModule.Event(MyEvent.Event3)] /// private void OnEvent3(int paramA, int paramB) { } /// } /// /// 1.4 视图特性 /// /// - 可用于标记视图元素的元数据,实现视图绑定和事件绑定 /// - 子类会继承父类的所有特性标记,可应用于类、字段和方法 /// - 视图特性会在视图 `Awake` 时回调 `Handler.SetBinding` 方法 /// - 特性标记只维护视图的元数据,业务层需要自行处理视图的绑定行为 /// /// 使用示例: /// /// // 类特性标记 /// [XView.Element("类特性名称")] /// [XView.Element("类特性名称带参数", "参数值")] /// public class MyView : XView.Base /// { /// // 字段特性标记 /// [XView.Element("字段特性名称")] /// private Button button; /// /// // 字段特性标记带参数 /// [XView.Element("字段特性名称带参数", "参数值")] /// private Text text; /// /// // 方法特性标记 /// [XView.Element("方法特性名称")] /// private void OnButtonClick() { } /// /// // 方法特性标记带参数 /// [XView.Element("方法特性名称带参数", "参数值")] /// private void OnTextChanged() { } /// } /// /// 2. 视图管理 /// /// 2.1 初始化 /// /// 使用示例: /// /// // 创建自定义Handler /// public class MyHandler : XView.IHandler /// { /// public void Load(XView.IMeta meta, Transform parent, out XView.IBase view, out GameObject panel) /// { /// // 实现视图加载逻辑 /// } /// /// public void LoadAsync(XView.IMeta meta, Transform parent, Action<XView.IBase, GameObject> callback) /// { /// // 实现异步加载逻辑 /// } /// /// public bool Loading(XView.IMeta meta) { /// // 是否正在加载视图 /// } /// /// public void SetBinding(GameObject go, object target, XView.Element[] elements) /// { /// // 实现视图绑定逻辑 /// } /// /// public void SetOrder(XView.IBase view, int order) /// { /// // 实现视图排序逻辑 /// } /// /// public void SetFocus(XView.IBase view, bool focus) /// { /// // 实现焦点设置逻辑 /// } /// } /// /// // 初始化视图系统 /// var handler = new MyHandler(); /// XView.Initialize(handler); /// /// 2.2 打开视图 /// /// 使用示例: /// /// // 同步打开视图 /// var view = XView.Open(meta, args); /// /// // 异步打开视图 /// XView.OpenAsync(meta, callback, args); /// /// 2.3 关闭视图 /// /// 使用示例: /// /// // 关闭指定视图 /// XView.Close(meta, resume, destroy); /// /// // 关闭所有视图 /// XView.CloseAll(exclude); /// /// // 关闭并销毁所有视图 /// XView.DestroyAll(exclude); /// /// 2.4 视图排序 /// /// 使用示例: /// /// // 设置视图顺序 /// XView.Sort(view, below, above); /// /// // 恢复默认顺序 /// XView.Resume(); /// /// 常见问题 /// 1. 视图的 EventType(焦点类型)有什么区别? /// /// 1. Dynamic(动态):当新视图打开时,之前的视图会自动失去焦点,适合需要独占用户输入的视图,如对话框。 /// 2. Static(静态):打开新视图不会影响此视图的焦点状态,适合需要保持交互的持久性视图,如操作面板。 /// 3. Silent(静默):视图不参与焦点系统,适合纯显示性质的视图,如提示信息或背景元素。 /// /// 2. 视图的 CacheType(缓存类型)有什么区别? /// /// 1. Scene(场景级):视图实例在场景切换时销毁,适合与当前场景关联的UI。 /// 2. Shared(共享级):视图实例在应用程序生命周期内保持,适合全局共享的UI,如主菜单。 /// 3. None(无缓存):视图在关闭时立即销毁,适合临时性强、内存占用大的UI。 /// /// 更多信息请参考视图文档。 /// #region 基础视图 public partial class XView { /// /// EventType 定义了视图的事件类型。 /// public enum EventType { /// /// Dynamic 表示动态事件,会触发视图的焦点变化。 /// Dynamic, /// /// Static 表示静态事件,不会触发视图的焦点变化。 /// Static, /// /// Slience 表示静默事件,不会触发视图的焦点变化和事件通知。 /// Slience, } /// /// CacheType 定义了视图的缓存类型。 /// public enum CacheType { /// /// Scene 表示场景级别的缓存,场景切换时会被清理。 /// Scene, /// /// Shared 表示全局共享的缓存,场景切换时不会被清理。 /// Shared, /// /// None 表示不启用缓存,关闭时会被销毁。 /// None, } /// /// IMeta 定义了视图的描述接口,包含视图的基本配置信息。 /// public interface IMeta { /// /// Path 获取视图的预制体路径。 /// string Path { get; } /// /// FixedRQ 获取视图的固定渲染顺序。 /// int FixedRQ { get; } /// /// Focus 获取视图的事件类型。 /// EventType Focus { get; } /// /// Cache 获取视图的缓存类型。 /// CacheType Cache { get; } /// /// Multiple 获取是否允许多个实例。 /// bool Multiple { get; } } /// /// Meta 提供了视图的描述实现。 /// public class Meta : IMeta { /// /// Path 获取或设置视图的预制体路径。 /// public string Path { get; set; } /// /// FixedRQ 获取或设置视图的固定渲染顺序。 /// public int FixedRQ { get; set; } /// /// Focus 获取或设置视图的事件类型。 /// public EventType Focus { get; set; } /// /// Cache 获取或设置视图的缓存类型。 /// public CacheType Cache { get; set; } /// /// Multiple 获取或设置是否允许多个实例。 /// public bool Multiple { get; set; } /// /// Meta 初始化描述。 /// public Meta() { } /// /// Meta 使用指定参数初始化描述。 /// /// 预制体路径 /// 固定渲染顺序 /// 事件类型 /// 缓存类型 /// 是否允许多个实例 public Meta(string path, int fixedRQ = 0, EventType focus = EventType.Dynamic, CacheType cache = CacheType.Scene, bool multiple = false) { Path = path; FixedRQ = fixedRQ; Cache = cache; Focus = focus; Multiple = multiple; } } /// /// IHandler 定义了视图的加载处理器接口。 /// public interface IHandler { /// /// Load 同步加载视图。 /// /// 视图描述 /// 父级变换 /// 视图实例 /// 视图面板 void Load(IMeta meta, Transform parent, out IBase view, out GameObject panel); /// /// LoadAsync 异步加载视图。 /// /// 视图描述 /// 父级变换 /// 加载完成回调 void LoadAsync(IMeta meta, Transform parent, Action callback); /// /// IsLoading 检查视图是否正在加载。 /// /// 视图描述 /// 是否正在加载 bool IsLoading(IMeta meta); /// /// SetBinding 设置视图的元素绑定。 /// /// 游戏对象 /// 组件实例 /// 元素列表 void SetBinding(GameObject go, object target, Element[] elements); /// /// SetOrder 设置视图的渲染顺序。 /// /// 视图实例 /// 渲染顺序 void SetOrder(IBase view, int order); /// /// SetFocus 设置视图的焦点状态。 /// /// 视图实例 /// 是否获得焦点 void SetFocus(IBase view, bool focus); } /// /// Event 提供了视图的事件系统实现,支持事件注册、注销和通知。 /// public class Event { /// /// Proxy 是事件代理类,用于管理事件回调的上下文。 /// internal class Proxy { /// /// ID 是事件的标识。 /// public int ID; /// /// Context 是事件的上下文实例。 /// public XEvent.Manager Context; /// /// Callback 是事件的回调函数。 /// public XEvent.Callback Callback; } /// /// context 是事件的上下文实例。 /// internal readonly XEvent.Manager context; /// /// proxies 是事件的代理容器。 /// internal readonly Dictionary> proxies = new(); public Event(XEvent.Manager context = null) { this.context = context ?? new XEvent.Manager(); } /// /// Register 注册事件。 /// /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(int id, XEvent.Callback callback, XEvent.Manager manager = null, bool once = false) { if (callback == null) return false; manager ??= context; var ret = manager.Register(id, callback, once); if (ret) { if (!proxies.TryGetValue(id, out var list)) { list = new List(); proxies.Add(id, list); } var proxy = new Proxy { ID = callback.GetHashCode(), Context = manager, Callback = callback }; list.Add(proxy); } return ret; } /// /// Register 注册事件。 /// /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(int id, XEvent.Callback callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, XEvent.Callback callback, XEvent.Manager manager = null, bool once = false) { return Register(id.GetHashCode(), callback, manager, once); } /// /// Register 注册事件。 /// /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, XEvent.Callback callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 参数类型 /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(int id, Action callback, XEvent.Manager manager = null, bool once = false) { if (callback == null) return false; manager ??= context; var ncallback = new XEvent.Callback(args => { var arg1 = args != null && args.Length > 0 ? (T1)args[0] : default; callback?.Invoke(arg1); }); var ret = manager.Register(id, ncallback, once); if (ret) { if (!proxies.TryGetValue(id, out var list)) { list = new List(); proxies.Add(id, list); } var proxy = new Proxy { ID = callback.GetHashCode(), Context = manager, Callback = ncallback }; list.Add(proxy); } return ret; } /// /// Register 注册事件。 /// /// 参数类型 /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(int id, Action callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 参数类型 /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, Action callback, XEvent.Manager manager = null, bool once = false) { return Register(id.GetHashCode(), callback, manager, once); } /// /// Register 注册事件。 /// /// 参数类型 /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, Action callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(int id, Action callback, XEvent.Manager manager = null, bool once = false) { if (callback == null) return false; manager ??= context; var ncallback = new XEvent.Callback(args => { var arg1 = args != null && args.Length > 0 ? (T1)args[0] : default; var arg2 = args != null && args.Length > 1 ? (T2)args[1] : default; callback?.Invoke(arg1, arg2); }); var ret = manager.Register(id, ncallback, once); if (ret) { if (!proxies.TryGetValue(id, out var list)) { list = new List(); proxies.Add(id, list); } var proxy = new Proxy { ID = callback.GetHashCode(), Context = manager, Callback = ncallback }; list.Add(proxy); } return ret; } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(int id, Action callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, Action callback, XEvent.Manager manager = null, bool once = false) { return Register(id.GetHashCode(), callback, manager, once); } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 事件标识 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, Action callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 第三个参数类型 /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(int id, Action callback, XEvent.Manager manager = null, bool once = false) { if (callback == null) return false; manager ??= context; var ncallback = new XEvent.Callback(args => { var arg1 = args != null && args.Length > 0 ? (T1)args[0] : default; var arg2 = args != null && args.Length > 1 ? (T2)args[1] : default; var arg3 = args != null && args.Length > 2 ? (T3)args[2] : default; callback?.Invoke(arg1, arg2, arg3); }); var ret = manager.Register(id, ncallback, once); if (ret) { if (!proxies.TryGetValue(id, out var list)) { list = new List(); proxies.Add(id, list); } var proxy = new Proxy { ID = callback.GetHashCode(), Context = manager, Callback = ncallback }; list.Add(proxy); } return ret; } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 第三个参数类型 /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(int id, Action callback, bool once = false) { return Register(id, callback, null, once); } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 第三个参数类型 /// 事件标识 /// 事件回调 /// 事件管理器 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, Action callback, XEvent.Manager manager = null, bool once = false) { return Register(id.GetHashCode(), callback, manager, once); } /// /// Register 注册事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 第三个参数类型 /// 事件标识 /// 事件回调 /// 回调一次 /// 是否成功 public virtual bool Register(Enum id, Action callback, bool once = false) { return Register(id, callback, null, once); } /// /// Unregister 注销事件。 /// /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(int id, XEvent.Callback callback = null) { var ret = false; if (proxies.TryGetValue(id, out var list)) { if (callback != null) { var hashCode = callback.GetHashCode(); for (int i = list.Count - 1; i >= 0; i--) { var proxy = list[i]; if (proxy.ID == hashCode) { ret |= proxy.Context.Unregister(id, proxy.Callback); list.RemoveAt(i); } } } else { foreach (var proxy in list) { ret |= proxy.Context.Unregister(id, proxy.Callback); } proxies.Remove(id); } } return ret; } /// /// Unregister 注销事件。 /// /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(Enum id, XEvent.Callback callback = null) { return Unregister(id.GetHashCode(), callback); } /// /// Unregister 注销事件。 /// /// 参数类型 /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(int id, Action callback) { if (callback == null) return false; var ret = false; if (proxies.TryGetValue(id, out var list)) { var hashCode = callback.GetHashCode(); for (int i = list.Count - 1; i >= 0; i--) { var proxy = list[i]; if (proxy.ID == hashCode) { ret |= proxy.Context.Unregister(id, proxy.Callback); list.RemoveAt(i); } } } return ret; } /// /// Unregister 注销事件。 /// /// 参数类型 /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(Enum id, Action callback) { return Unregister(id.GetHashCode(), callback); } /// /// Unregister 注销事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(int id, Action callback) { if (callback == null) return false; var ret = false; if (proxies.TryGetValue(id, out var list)) { var hashCode = callback.GetHashCode(); for (int i = list.Count - 1; i >= 0; i--) { var proxy = list[i]; if (proxy.ID == hashCode) { ret |= proxy.Context.Unregister(id, proxy.Callback); list.RemoveAt(i); } } } return ret; } /// /// Unregister 注销事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(Enum id, Action callback) { return Unregister(id.GetHashCode(), callback); } /// /// Unregister 注销事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 第三个参数类型 /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(int id, Action callback) { if (callback == null) return false; var ret = false; if (proxies.TryGetValue(id, out var list)) { var hashCode = callback.GetHashCode(); for (int i = list.Count - 1; i >= 0; i--) { var proxy = list[i]; if (proxy.ID == hashCode) { ret |= proxy.Context.Unregister(id, proxy.Callback); list.RemoveAt(i); } } } return ret; } /// /// Unregister 注销事件。 /// /// 第一个参数类型 /// 第二个参数类型 /// 第三个参数类型 /// 事件标识 /// 事件回调 /// 是否成功 public virtual bool Unregister(Enum id, Action callback) { return Unregister(id.GetHashCode(), callback); } /// /// UnregisterAll 清除事件注册。 /// public virtual void UnregisterAll() { foreach (var kvp in proxies) { foreach (var proxy in kvp.Value) { proxy.Context.Unregister(kvp.Key, proxy.Callback); } } proxies.Clear(); } /// /// Notify 通知事件。 /// /// 事件标识 /// 事件参数 public virtual void Notify(int id, params object[] args) { context.Notify(id, args); } /// /// Notify 通知事件。 /// /// 事件标识 /// 事件参数 public virtual void Notify(Enum id, XEvent.Manager manager = null, params object[] args) { Notify(id.GetHashCode(), args); } } /// /// IBase 定义了视图的基础接口。 /// public interface IBase { /// /// Meta 获取或设置视图的描述。 /// IMeta Meta { get; set; } /// /// Panel 获取或设置视图的面板对象。 /// GameObject Panel { get; set; } /// /// OnOpen 在视图打开时调用。 /// /// 打开参数 void OnOpen(params object[] args); /// /// OnFocus 在视图获得焦点时调用。 /// void OnFocus(); /// /// OnBlur 在视图失去焦点时调用。 /// void OnBlur(); /// /// OnClose 在视图关闭时调用。 /// /// 关闭完成回调 void OnClose(Action done); } /// /// Element 是视图元素特性,支持自定义描述。 /// [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public sealed class Element : Attribute { /// /// Name 是元素的名称。 /// public string Name { get; set; } /// /// Extras 是自定义的描述。 /// public object[] Extras { get; set; } /// /// Reflect 表示反射的信息。 /// public object Reflect { get; set; } public Element(string name = null, params object[] extras) { Name = name; Extras = extras; } /// /// cached 是视图元素特性的全局缓存。 /// internal static readonly Dictionary cached = new(); /// /// Get 根据类型获取视图元素标记的特性。 /// /// 目标类型 /// 标记的特性列表 public static Element[] Get(Type type) { if (type == null) return null; if (!cached.TryGetValue(type, out var elements)) { var telements = new List(); var classAttrs = type.GetCustomAttributes(); foreach (var attr in classAttrs) { attr.Reflect = type; telements.Add(attr); } var members = type.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var member in members) { var memberAttrs = member.GetCustomAttributes(); foreach (var attr in memberAttrs) { attr.Reflect = member; telements.Add(attr); } } foreach (var element in telements) { if (string.IsNullOrEmpty(element.Name) && element.Reflect != null) { element.Name = (element.Reflect as MemberInfo).Name; } } elements = telements.ToArray(); cached.Add(type, elements); } return elements; } } } public partial class XView { /// /// Base 提供了视图的基础实现。 /// public class Base : MonoBehaviour, IBase { /// /// Meta 获取或设置视图的描述。 /// public virtual IMeta Meta { get; set; } /// /// Panel 获取或设置视图的面板对象。 /// public virtual GameObject Panel { get; set; } internal Event @event; /// /// Event 获取视图的事件管理器。 /// public virtual Event Event { get { @event ??= new Event(); return @event; } } internal XLog.LogTag tags; /// /// Tags 获取或设置视图的日志标签。 /// public virtual XLog.LogTag Tags { get { if (tags == null) { tags = XLog.GetTag(); tags.Set("Name", this ? "null" : name); tags.Set("Comp", GetType().FullName); tags.Set("Hash", GetHashCode().ToString()); } return tags; } set => tags = value; } /// /// Base 初始化视图。 /// public Base() { } /// /// Base 使用代理实例初始化视图。 /// /// 代理对象 public Base(object proxy) { } // PuerTS 类型导出 /// /// Awake 在视图初始化时调用。 /// public virtual void Awake() { var elements = Element.Get(GetType()); Handler.SetBinding(gameObject, this, elements); } /// /// OnEnable 在视图启用时调用。 /// public virtual void OnEnable() { var events = XModule.Event.Get(GetType()); if (events != null && events.Count > 0) { foreach (var @event in events) { if (@event.Target != null) { Event.Register(@event.ID, args => { if (@event.Callback.IsStatic) @event.Callback.Invoke(null, args); else @event.Callback.Invoke(this, args); }, @event.Target.Event, @event.Once); } else { Event.Register(@event.ID, args => { if (@event.Callback.IsStatic) @event.Callback.Invoke(null, args); else @event.Callback.Invoke(this, args); }, @event.Once); } } } } /// /// OnOpen 在视图打开时调用。 /// /// 打开参数 public virtual void OnOpen(params object[] args) { } /// /// OnFocus 在视图获得焦点时调用。 /// public virtual void OnFocus() { } /// /// OnBlur 在视图失去焦点时调用。 /// public virtual void OnBlur() { } /// /// OnDisable 在视图禁用时调用。 /// public virtual void OnDisable() { Event?.UnregisterAll(); } /// /// OnClose 在视图关闭时调用。 /// /// 关闭完成回调 public virtual void OnClose(Action done) { done(); } /// /// Focus 设置视图焦点。 /// public virtual void Focus() { XView.Focus(this); } /// /// Close 关闭视图。 /// /// 是否恢复焦点 public virtual void Close(bool resume = true) { XView.Close(this, resume); } } /// /// Base 提供了带模块的视图基础实现。 /// /// 模块类型 public class Base : Base where TModule : XModule.IBase, new() { private TModule module; /// /// Module 获取视图的模块实例。 /// public TModule Module { get { if (module == null) { var property = typeof(TModule).GetProperty("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (property != null) module = (TModule)property.GetValue(null); else module = new TModule(); } return module; } } /// /// Event 获取视图的事件管理器。 /// public override Event Event { get { @event ??= new Event(Module.Event); return @event; } } /// /// Tags 获取或设置视图的日志标签。 /// public override XLog.LogTag Tags { get { if (tags == null) { tags = XLog.GetTag(); tags.Set("Name", this ? "null" : name); tags.Set("Comp", GetType().FullName); tags.Set("Hash", GetHashCode().ToString()); tags.Set("Module", Module == null ? "null" : Module.Name); } return tags; } set => tags = value; } } } #endregion #region 视图管理 public partial class XView { /// /// Handler 是共享的视图处理器。 /// public static IHandler Handler { get; internal set; } /// /// cachedView 是缓存的视图列表。 /// internal static List cachedView = new(); /// /// openedView 是打开的视图列表。 /// internal static List openedView = new(); /// /// focusedView 是获得焦点的视图字典。 /// internal static readonly Dictionary focusedView = new(); /// /// Initialize 初始化视图系统。 /// /// 视图加载处理器 public static void Initialize(IHandler handler) { Handler = handler ?? throw new ArgumentNullException("handler"); SceneManager.sceneUnloaded += scene => { if (!scene.isSubScene) { for (var i = 0; i < cachedView.Count;) { var view = cachedView[i]; if (view.Meta.Cache == CacheType.Scene) { view.Panel.DestroyGameObject(true); cachedView.RemoveAt(i); XLog.Info("XView.Initialize: removed scene-cached view: {0}.", view.Meta.Path); } else { i++; } } } }; } /// /// Load 加载视图。 /// /// 视图描述 /// 父级变换 /// 如果已打开则关闭 /// 视图实例 public static IBase Load(IMeta meta, Transform parent, bool closeIfOpened) { IBase view = null; if (!meta.Multiple) { view = Find(meta); if (closeIfOpened && view != null) { Close(meta, false); view = null; } } if (view == null) { for (var i = 0; i < cachedView.Count; i++) { var temp = cachedView[i]; if (temp.Meta.Path == meta.Path) { view = temp; if (view.Panel.activeSelf) view.Panel.SetGameObjectActive(false); cachedView.RemoveAt(i); break; } } } if (view == null) { Handler.Load(meta, parent, out view, out var panel); if (view != null) { view.Meta = meta; view.Panel = panel; Handler.SetFocus(view, true); } } return view; } /// /// 查找视图。 /// /// 视图描述 /// 视图实例 public static IBase Find(IMeta meta) { if (meta != null) { for (var i = 0; i < openedView.Count; i++) { var view = openedView[i]; if (view.Meta.Path == meta.Path) { return view; } } } return null; } /// /// Open 打开视图。 /// /// 目标视图 /// 打开参数 /// 视图实例 public static IBase Open(IMeta target, params object[] args) { // [TONOTE]: 兼容js拦截函数参数类型匹配错误问题 if (args != null) { if (args.Length > 0 && args[0] is Transform transform) { var nargs = new object[args.Length - 1]; Array.Copy(args, 1, nargs, 0, args.Length - 1); return Open(target, transform, nargs); } else if (args.Length > 2 && args[0] is IMeta meta && args[1] is IMeta meta1 && args[2] is Transform transform1) { var nargs = new object[args.Length - 3]; Array.Copy(args, 3, nargs, 0, args.Length - 3); return Open(target, meta, meta1, transform1, nargs); } } return Open(target, null, null, null, args); } /// /// Open 打开视图。 /// /// 目标视图 /// 父级变换 /// 打开参数 /// 视图实例 public static IBase Open(IMeta target, Transform parent, params object[] args) { return Open(target, null, null, parent, args); } /// /// Open 打开视图。 /// /// 目标视图 /// 下方视图 /// 上方视图 /// 父级变换 /// 打开参数 /// 视图实例 public static IBase Open(IMeta target, IMeta below, IMeta above, Transform parent, params object[] args) { var view = Load(target, parent, true); if (view == null) XLog.Error("XView.Open: error caused by nil IView obj, please check it {0}.", target.Path); else view.Panel.SetGameObjectActive(true); var belowWin = Find(below); var aboveWin = Find(above); Sort(view, belowWin, aboveWin); view.OnOpen(args); return view; } /// /// OpenAsync 异步打开视图。 /// /// 目标视图 /// 打开完成回调 /// 打开参数 /// public static bool OpenAsync(IMeta target, Action callback = null, params object[] args) { return OpenAsync(target, null, null, null, callback, args); } /// /// OpenAsync 异步打开视图。 /// /// 目标视图 /// 父级变换 /// 打开完成回调 /// 打开参数 /// public static bool OpenAsync(IMeta target, Transform parent, Action callback = null, params object[] args) { return OpenAsync(target, null, null, parent, callback, args); } /// /// OpenAsync 异步打开视图。 /// /// 目标视图 /// 下方视图 /// 上方视图 /// 父级变换 /// 打开完成回调 /// 打开参数 /// 是否开始打开 public static bool OpenAsync(IMeta target, IMeta below, IMeta above, Transform parent, Action callback = null, params object[] args) { IBase view = null; if (!target.Multiple) { view = Find(target); if (view != null) { Close(target, false); view = null; } } if (view == null) { for (var i = 0; i < cachedView.Count; i++) { var temp = cachedView[i]; if (temp.Meta.Path == target.Path) { view = temp; if (view.Panel.activeSelf) view.Panel.SetGameObjectActive(false); cachedView.RemoveAt(i); break; } } } if (view == null) { if (!target.Multiple && Handler.IsLoading(target)) { try { callback?.Invoke(null); } catch (Exception e) { XLog.Panic(e); } return false; } else { Handler.LoadAsync(target, parent, (view, panel) => { if (view != null) { view.Meta = target; view.Panel = panel; Handler.SetFocus(view, true); panel.SetGameObjectActive(true); var belowWin = Find(below); var aboveWin = Find(above); Sort(view, belowWin, aboveWin); view.OnOpen(args); } try { callback?.Invoke(view); } catch (Exception e) { XLog.Panic(e); } }); } } else { view.Panel.SetGameObjectActive(true); var belowWin = Find(below); var aboveWin = Find(above); Sort(view, belowWin, aboveWin); view.OnOpen(args); try { callback?.Invoke(view); } catch (Exception e) { XLog.Panic(e); } } return true; } /// /// Sort 排序视图。 /// /// 目标视图 /// 下方视图 /// 上方视图 public static void Sort(IBase view, IBase below, IBase above) { if (view != null) { var inserted = false; if (below != null) { for (var i = 0; i < openedView.Count; i++) { var temp = openedView[i]; if (temp == below) { openedView.Insert(i, view); inserted = true; break; } } } else if (above != null) { for (var i = 0; i < openedView.Count; i++) { var temp = openedView[i]; if (temp == above) { openedView.Insert(i + 1, view); inserted = true; break; } } } if (!inserted) { openedView.Add(view); } } var index = openedView.Count - 1; var rqIndex = index; var lastFocused = false; while (index >= 0) { var temp = openedView[index]; if (temp.Panel == null) { XLog.Error("XView.Sort: view {0} has already been destroyed.", temp.Meta.Path); openedView.RemoveAt(index); focusedView.Remove(temp); } else { focusedView.TryGetValue(temp, out var focused); if (temp.Meta.FixedRQ > 0) { Handler.SetOrder(temp, temp.Meta.FixedRQ); } else { Handler.SetOrder(temp, 1000 + (rqIndex - 1) * 500); rqIndex -= 1; } if (temp.Meta.Focus == EventType.Slience) { if (focused) { focusedView[temp] = false; Handler.SetFocus(temp, false); temp.OnBlur(); } } else if (lastFocused == false || temp.Meta.Focus == EventType.Static) { if (!focused) { focusedView[temp] = true; Handler.SetFocus(temp, true); temp.OnFocus(); } lastFocused = true; } else { if (focused) { focusedView[temp] = false; Handler.SetFocus(temp, false); temp.OnBlur(); } } } index--; } } /// /// Focus 设置视图焦点。 /// /// 视图描述 public static void Focus(IMeta meta) { if (meta != null) { for (var i = 0; i < openedView.Count; i++) { var temp = openedView[i]; if (temp.Meta.Path == meta.Path) { Handler.SetFocus(temp, true); temp.OnFocus(); break; } } } } /// /// Focus 设置视图焦点。 /// /// 视图实例 public static void Focus(IBase view) { if (view != null) { for (var i = 0; i < openedView.Count; i++) { var temp = openedView[i]; if (temp == view) { Handler.SetFocus(temp, true); temp.OnFocus(); break; } } } } /// /// Resume 恢复视图焦点。 /// public static void Resume() { Sort(null, null, null); } /// /// Close 关闭视图。 /// /// 视图描述 /// 是否恢复焦点 /// 是否强制删除 public static void Close(IMeta meta, bool resume = true, bool destroy = false) { if (meta != null) { for (var i = openedView.Count - 1; i >= 0; i--) { var temp = openedView[i]; if (temp.Meta.Path == meta.Path) { openedView.RemoveAt(i); if ((temp.Meta.Cache == CacheType.Shared || temp.Meta.Cache == CacheType.Scene) && !destroy) { cachedView.Add(temp); if (temp.Meta.Cache == CacheType.Shared) UnityEngine.Object.DontDestroyOnLoad(temp.Panel); } var post = new Action(() => { focusedView.Remove(temp); temp.Panel.SetGameObjectActive(false); if (temp.Meta.Cache == CacheType.None || destroy) { temp.Panel.DestroyGameObject(true); } }); temp.OnClose(post); break; } } if (resume) Resume(); } } /// /// Close 关闭视图。 /// /// 视图实例 /// 是否恢复焦点 /// 是否强制删除 public static void Close(IBase view, bool resume = true, bool destroy = false) { if (view != null) { for (var i = openedView.Count - 1; i >= 0; i--) { var temp = openedView[i]; if (temp == view) { openedView.RemoveAt(i); if ((temp.Meta.Cache == CacheType.Shared || temp.Meta.Cache == CacheType.Scene) && !destroy) { cachedView.Add(temp); if (temp.Meta.Cache == CacheType.Shared) UnityEngine.Object.DontDestroyOnLoad(temp.Panel); } var post = new Action(() => { focusedView.Remove(temp); temp.Panel.SetGameObjectActive(false); if (temp.Meta.Cache == CacheType.None || destroy) { temp.Panel.DestroyGameObject(true); } }); temp.OnClose(post); break; } } if (resume) Resume(); } } /// /// CloseAll 关闭所有视图。 /// /// 排除的视图 public static void CloseAll(params IMeta[] exclude) { var index = 0; while (index < openedView.Count) { var view = openedView[index]; var close = true; for (var i = 0; i < exclude.Length; i++) { if (view.Meta.Path == exclude[i].Path) { close = false; break; } } if (close) Close(view.Meta, false); else index++; } Resume(); } /// /// DestroyAll 关闭并销毁所有视图。 /// /// 排除的视图 public static void DestroyAll(params IMeta[] exclude) { var index = 0; while (index < openedView.Count) { var view = openedView[index]; var close = true; for (var i = 0; i < exclude.Length; i++) { if (view.Meta.Path == exclude[i].Path) { close = false; break; } } if (close) Close(view.Meta, false, true); else index++; } index = 0; while (index < cachedView.Count) { var view = cachedView[index]; var destroy = true; for (var i = 0; i < exclude.Length; i++) { if (view.Meta.Path == exclude[i].Path) { destroy = false; break; } } if (destroy) { cachedView.RemoveAt(index); if (view.Panel) view.Panel.DestroyGameObject(true); } else index++; } Resume(); } } #endregion }