using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using XericLibrary.Runtime.CustomEditor; using Object = UnityEngine.Object; using Sirenix.OdinInspector; #if UNITY_EDITOR using Sirenix.Utilities.Editor; #endif namespace XericLibrary.Runtime.MacroLibrary { /// /// UI相关的扩展 /// public static class MacroUI { #region toggle 扩展 private static FieldInfo togglesFieldInfo = typeof(ToggleGroup).GetField("m_Toggles", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); public static List GetToggles(this ToggleGroup toggleGroup) { if (toggleGroup == null) { throw new ArgumentNullException(nameof(toggleGroup)); } if (togglesFieldInfo == null) { throw new InvalidOperationException("Unable to access the 'm_Toggles' field."); } var toggles = togglesFieldInfo.GetValue(toggleGroup) as List; if (toggles == null) { throw new InvalidCastException("The 'm_Toggles' field is not of type List."); } return toggles; // return (List)togglesFieldInfo.GetValue(toggleGroup); } /// /// 获取当前单选项组中激活的索引 /// /// /// [Obsolete("索引依赖单选项自身在大纲中的顺序,在运行时顺序可能与编辑时不一致,请使用ToggleMapping")] public static int GetActiveToggleIndex(this ToggleGroup toggleGroup) { var index = -1; foreach (var toggle in toggleGroup.GetToggles()) { index++; if (toggle.isOn) return index; } return -1; } /// /// 获取当前单选项组的数量 /// /// /// public static int GetToggleCount(this ToggleGroup toggleGroup) { return toggleGroup.GetToggles().Count; } /// /// 在单选项组上注册一个事件,当组中的任意成员变成激活状态时调用(其他的不会发生调用)。 /// /// /// public static void OnToggleGroupChangeEvent(this ToggleGroup toggleGroup, Action onToggleChange) { foreach (var toggle in toggleGroup.GetToggles()) { toggle.onValueChanged.AddListener(a => { if (!a) return; onToggleChange?.Invoke(toggle); }); } } /// /// 清空单选项组中的所有事件(与注册所有事件对应,但那个事件没法单独注销) /// /// public static void RemoveToggleGroupChangeEvent(this ToggleGroup toggleGroup) { foreach (var toggle in toggleGroup.GetToggles()) { toggle.onValueChanged.RemoveAllListeners(); } } #region toggle 索引 /// /// toggle映射集 /// /// toggle映射集必须使用unity序列化管理,否则和直接使用toggleGroup没区别,目的是解决toggleGroup在打包后大纲视图的索引可能发生错位的问题。 /// /// [Serializable] public class ToggleMapping : IEnumerable { #region 事件委托 /// /// 在单选项目切换时产生回调,返回选中的单选项目在组中的引用 /// public Action OnAnyToggleSwitchOn; /// /// 在单选项目切换时产生回调,返回选中的单选项目在组中的索引 /// public Action OnAnyToggleIndexSwitchOn; #endregion #region 字段属性 [LabelText("单选组")] public ToggleGroup ToggleGroup; [SerializeField, LabelText("编辑单选项目顺序")] [ListDrawerSettings(OnTitleBarGUI = "GetAndSortToggle")] private List toggleList = new List(); // 当前选中的项目 private int nowSelectToggleIndex = 0; private Toggle nowSelectToggle = null; public Transform TogglesContext => ToggleGroup.transform; /// /// 当前选中的toggle索引 /// public int NowSelectToggleIndex => nowSelectToggleIndex; /// /// 当前选中的toggle /// public Toggle NowSelectToggle => nowSelectToggle; /// /// 获取并给列表排序(顺序不一定与拼音有关) /// public void GetAndSortToggle() { #if UNITY_EDITOR // 自动获取并排序 if (SirenixEditorGUI.ToolbarButton(EditorIcons.Refresh)) { GetSortToggle(); } // 反转顺序 if (SirenixEditorGUI.ToolbarButton(EditorIcons.TriangleDown)) { toggleList.Reverse(); } #else GetSortToggle(); #endif void GetSortToggle() { var newToggleList = MacroSort.FullCharacterOrderSort(ToggleGroup.GetToggles(), a => a.name) .ToList(); if (newToggleList.Count <= 0 || newToggleList == null) Debug.LogError("如果无法更新获取自动排序toggle,可能是因为toggleGroup被隐藏了,手动将其激活后再获取即可。"); else toggleList = newToggleList; } } #endregion #region 接口 /// /// 获取索引下的单选项组件 /// /// public Toggle this[int index] => toggleList[index]; public int Count => toggleList.Count; public IEnumerator GetEnumerator() { return toggleList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region 方法 /// /// 初始化 /// /// 自动将所有的按键绑定回调事件;如果作为刷新函数调用,请确保已经完全组索引。 /// 或者可以直接清空旧项目(CleanToggle),然后逐个添加(AddToggle) /// /// public void Initialize() { // 未指定组时,说明压根没用这部分功能,用不着初始化。 if (ToggleGroup == null) return; // 防呆警告 if (toggleList.Count <= 0) { var toggles = ToggleGroup.GetToggles(); if (toggles.Count != toggleList.Count) { toggleList = toggles; Debug.LogWarning($"在初始化单选项组时,{ToggleGroup.name}并未预先指定索引顺序,将默认使用大纲顺序。"); } } // 事件初始化 for (int i = 0; i < toggleList.Count; i++) { var toggle = toggleList[i]; ToggleAddEvent(toggle); if (toggle.isOn) { nowSelectToggleIndex = i; nowSelectToggle = toggle; } } if (nowSelectToggle == null) SetToggleOn(0); } /// /// 添加一个toggle /// /// /// 返回toggle的索引 public int AddToggle(Toggle t) { ToggleAddEvent(t); var resultIndex = toggleList.Count; toggleList.Add(t); return resultIndex; } /// /// 移除一个toggle,这不会影响其他toggle的索引,但此处移除的位置会为空。 /// /// 注 :这不会销毁toggle。 /// /// /// /// 是否成功移除toggle public bool RemoveToggle(Toggle t) { var index = toggleList.IndexOf(t); if (index < 0) return false; t.onValueChanged.RemoveAllListeners(); toggleList[index] = null; return true; } /// /// 清除toggle /// /// 是否同时销毁所有toggle public void CleanToggle(bool allowDestroy) { foreach (var t in toggleList) { t.onValueChanged.RemoveAllListeners(); if (allowDestroy) Object.Destroy(t); } toggleList.Clear(); } /// /// toggle注册的事件,只有当按下时才需要调用此事件。 /// /// private void ToggleAddEvent(Toggle t) { t.onValueChanged.AddListener(a => { if (a) ToggleRegister(t); }); } /// /// toggle注册的事件,只有当按下时才需要调用此事件。 /// /// private void ToggleRegister(Toggle t) { nowSelectToggle = t; nowSelectToggleIndex = GetIndex(t); OnAnyToggleSwitchOn?.Invoke(t); OnAnyToggleIndexSwitchOn?.Invoke(nowSelectToggleIndex); } /// /// 获取toggle代表的索引 /// /// /// 如果这个toggle不存在于当前的单选项组中,返回-1 public int GetIndex(Toggle target) { return toggleList.IndexOf(target); } /// /// 获取toggle代表的索引, 如果toggle不存在于这个映射集中,将返回否 /// /// /// /// public bool TryGetIndex(Toggle target, out int index) { index = toggleList.IndexOf(target); return index >= 0; } /// /// 设置单选项激活 /// /// public void SetToggleOn(Toggle target) { target.isOn = true; } /// /// 设置单选项激活 /// /// public void SetToggleOn(int index) { if (0 < index && index < toggleList.Count) { SetToggleOn(toggleList[index]); } } /// /// 设置单选项激活 /// /// public void SetToggleOnWithoutNotify(Toggle target) { target.SetIsOnWithoutNotify(true); } /// /// 设置单选项激活 /// /// public void SetToggleOnWithoutNotify(int index) { if (0 < index && index < toggleList.Count) { SetToggleOnWithoutNotify(toggleList[index]); } } /// /// 清除映射结构(不会清除toggle实例) /// public void Clear() { ToggleGroup.RemoveToggleGroupChangeEvent(); toggleList.Clear(); } /// /// 清除映射结构,并销毁所有toggle组件 /// public void RemoveAllToggle() { for (int i = toggleList.Count - 1; i >= 0; i--) { Object.Destroy(toggleList[i]); } Clear(); } #endregion } #endregion #endregion #region 按钮扩展 /// /// 所有按钮注册一个事件 /// /// /// public static void RegisterOnClickEvent(this IEnumerable