// 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
}