添加窗口任务栏功能,添加窗口的管理功能
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XericUI.Form;
|
||||
|
||||
namespace XericUIEditor.Form
|
||||
{
|
||||
@@ -24,8 +25,6 @@ namespace XericUIEditor.Form
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
#region 基本设置
|
||||
|
||||
private void DrawBasicSection()
|
||||
{
|
||||
EditorGUILayout.LabelField("窗口基本设置", EditorStyles.boldLabel);
|
||||
@@ -33,10 +32,6 @@ namespace XericUIEditor.Form
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 边缘行为
|
||||
|
||||
private void DrawEdgeSection()
|
||||
{
|
||||
EditorGUILayout.LabelField("边缘吸附", EditorStyles.boldLabel);
|
||||
@@ -58,10 +53,6 @@ namespace XericUIEditor.Form
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 调试与测试
|
||||
|
||||
private void DrawDebugSection()
|
||||
{
|
||||
EditorGUILayout.LabelField("调试", EditorStyles.boldLabel);
|
||||
@@ -72,74 +63,49 @@ namespace XericUIEditor.Form
|
||||
|
||||
// 当前状态
|
||||
EditorGUILayout.LabelField("当前状态", EditorStyles.boldLabel);
|
||||
|
||||
var state = _controller.CurrentState;
|
||||
Color stateColor;
|
||||
string stateText;
|
||||
|
||||
switch (state)
|
||||
Color stateColor = state switch
|
||||
{
|
||||
case XericUI.Form.WindowState.Normal:
|
||||
stateColor = Color.white;
|
||||
stateText = "● 窗口化 (Normal)";
|
||||
break;
|
||||
case XericUI.Form.WindowState.Maximized:
|
||||
stateColor = Color.green;
|
||||
stateText = "■ 全屏 (Maximized)";
|
||||
break;
|
||||
case XericUI.Form.WindowState.Minimized:
|
||||
stateColor = Color.gray;
|
||||
stateText = "○ 最小化 (Minimized)";
|
||||
break;
|
||||
case XericUI.Form.WindowState.EdgeHidden:
|
||||
stateColor = new Color(1f, 0.6f, 0f);
|
||||
stateText = "◄ 边缘隐藏 (Edge Hidden)";
|
||||
break;
|
||||
default:
|
||||
stateColor = Color.white;
|
||||
stateText = state.ToString();
|
||||
break;
|
||||
}
|
||||
XericUI.Form.WindowState.Normal => Color.white,
|
||||
XericUI.Form.WindowState.Maximized => Color.green,
|
||||
XericUI.Form.WindowState.Minimized => Color.gray,
|
||||
XericUI.Form.WindowState.EdgeHidden => new Color(1f, 0.6f, 0f),
|
||||
_ => Color.white
|
||||
};
|
||||
var entries = _controller.Manager?.GetAllEntries();
|
||||
WindowEntry entry = _controller.Entry;
|
||||
string extra = entry != null ? $" | ID={entry.Id}" : "";
|
||||
|
||||
GUI.color = stateColor;
|
||||
EditorGUILayout.LabelField(stateText, EditorStyles.miniBoldLabel);
|
||||
EditorGUILayout.LabelField($"{state}{extra}", EditorStyles.miniBoldLabel);
|
||||
GUI.color = Color.white;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// 手动测试按钮
|
||||
// 手动测试
|
||||
EditorGUILayout.LabelField("手动测试", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("全屏/还原"))
|
||||
{
|
||||
_controller.ToggleMaximize();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
if (GUILayout.Button("模拟进入"))
|
||||
if (GUILayout.Button("置顶 (Focus)"))
|
||||
{
|
||||
_controller.SimulateEnter();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
if (GUILayout.Button("模拟退出"))
|
||||
{
|
||||
_controller.SimulateExit();
|
||||
_controller.MoveToFront();
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// 调试提示
|
||||
if (!Application.isPlaying && _controller.DebugMode)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
"调试模式已启用 — 在 SceneView 中移动鼠标即可预览。\n" +
|
||||
"蓝色边框 = 窗口范围 | 绿色 = 鼠标悬停中 | 橙色 = 吸附阈值范围",
|
||||
"调试模式已启用 — SceneView 中移动鼠标实时预览。\n" +
|
||||
"蓝色边框=窗口范围 | 绿色=鼠标悬停中",
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUIEditor.Form
|
||||
{
|
||||
[CustomEditor(typeof(XericUI.Form.UITaskBar))]
|
||||
public class UITaskBarEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("任务栏绑定", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Manager"), new GUIContent("窗口管理器 (Manager)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_ItemPrefab"), new GUIContent("图标预制体 (Item Prefab)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_ItemsContainer"), new GUIContent("图标容器 (Container)"));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(XericUI.Form.UITaskBarItem))]
|
||||
public class UITaskBarItemEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("任务栏图标项控件", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Icon"), new GUIContent("图标 (Icon)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_TitleText"), new GUIContent("标题文本 (Title)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_ProgressBar"), new GUIContent("进度条 (Progress Bar)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Highlight"), new GUIContent("聚焦高亮 (Highlight)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Button"), new GUIContent("按钮 (Button)"));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dfbe31f222f10a4883ad6016cdad32b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -6,26 +6,20 @@ namespace XericUIEditor.Form
|
||||
[CustomEditor(typeof(XericUI.Form.UITitleBar))]
|
||||
public class UITitleBarEditor : Editor
|
||||
{
|
||||
private XericUI.Form.UITitleBar _titleBar;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_titleBar = (XericUI.Form.UITitleBar)target;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("标题栏控件引用", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox(
|
||||
"将标题栏下的子控件拖入下方槽位即可。\n" +
|
||||
"UITitleBar 会在 Start 时自动向上查找父级的 UIFormController 并绑定事件。",
|
||||
"将标题栏下的子控件拖入下方槽位。\n" +
|
||||
"UITitleBar 在 Start 时向上查找父级 UIFormController 并绑定事件。",
|
||||
MessageType.Info);
|
||||
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_IconImage"), new GUIContent("窗口图标 (Icon Image)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_TitleText"), new GUIContent("标题文本 (Title Text)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CloseButton"), new GUIContent("关闭按钮 (Close)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MaximizeButton"), new GUIContent("最大化按钮 (Maximize)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MaximizeToggle"), new GUIContent("最大化 Toggle (Maximize)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_MinimizeButton"), new GUIContent("最小化按钮 (Minimize)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoSnapToggle"), new GUIContent("吸附开关 (Auto Snap)"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoHideToggle"), new GUIContent("隐藏开关 (Auto Hide)"));
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace XericUI.Form
|
||||
{
|
||||
#region 枚举与结构体
|
||||
|
||||
/// <summary>范围坐标空间枚举</summary>
|
||||
public enum FormRangeSpace
|
||||
{
|
||||
RelativeOffset,
|
||||
@@ -22,7 +21,7 @@ namespace XericUI.Form
|
||||
[Tooltip("坐标空间")]
|
||||
public FormRangeSpace space;
|
||||
|
||||
[Tooltip("范围定义。\nRelativeOffset: x,y=像素偏移; w,h=像素宽高(0=用参考尺寸)\nCanvasLocal: Canvas局部坐标\nAnchorToSelf: x,y=锚点0-1; w,h=尺寸比例0-1\nAnchorToCanvas: x,y=锚点0-1; w,h=尺寸比例0-1")]
|
||||
[Tooltip("范围定义")]
|
||||
public Rect rect;
|
||||
}
|
||||
|
||||
@@ -30,31 +29,31 @@ namespace XericUI.Form
|
||||
|
||||
/// <summary>
|
||||
/// UI 窗体控制器 — 挂载到窗口根 GameObject 上,自身即为完整窗口。
|
||||
/// 支持全屏/还原、边缘吸附、自动隐藏、拖拽移动。
|
||||
/// 标题栏功能由独立的 UITitleBar 子组件提供,Controller 只提供公开 API。
|
||||
/// 支持全屏/还原、边缘吸附、自动隐藏、拖拽移动、置顶。
|
||||
/// 通过 WindowEntry 与 Manager 通信。
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/Form/UI Form Controller")]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class UIFormController : MonoBehaviour, IUIFormTarget, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
public class UIFormController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler
|
||||
{
|
||||
#region 序列化字段
|
||||
|
||||
[Header("窗口基本设置")]
|
||||
[SerializeField, Tooltip("窗口标题,供外部读取")]
|
||||
[SerializeField, Tooltip("窗口标题")]
|
||||
private string m_WindowTitle = "Window";
|
||||
|
||||
[Header("边缘吸附")]
|
||||
[SerializeField, Tooltip("是否启用边缘自动吸附")]
|
||||
[SerializeField, Tooltip("启用边缘自动吸附")]
|
||||
private bool m_EnableAutoSnap = true;
|
||||
|
||||
[SerializeField, Tooltip("吸附阈值(像素),窗口距边缘小于此值时自动贴边")]
|
||||
[SerializeField, Tooltip("吸附阈值(像素)")]
|
||||
private float m_SnapThreshold = 8f;
|
||||
|
||||
[Header("自动隐藏")]
|
||||
[SerializeField, Tooltip("是否启用自动隐藏(贴边后滑出屏幕)")]
|
||||
[SerializeField, Tooltip("启用自动隐藏")]
|
||||
private bool m_EnableAutoHide;
|
||||
|
||||
[SerializeField, Tooltip("自动隐藏的滑动方向")]
|
||||
[SerializeField, Tooltip("自动隐藏滑动方向")]
|
||||
private AutoHideDirection m_AutoHideDirection = AutoHideDirection.Left;
|
||||
|
||||
[Header("调试")]
|
||||
@@ -69,28 +68,29 @@ namespace XericUI.Form
|
||||
#region 公开属性
|
||||
|
||||
public string WindowTitle => m_WindowTitle;
|
||||
|
||||
/// <summary>是否处于全屏状态</summary>
|
||||
public bool IsMaximized => _windowState == WindowState.Maximized;
|
||||
|
||||
/// <summary>是否处于边缘隐藏状态</summary>
|
||||
public bool IsEdgeHidden => _windowState == WindowState.EdgeHidden;
|
||||
|
||||
/// <summary>当前窗口状态</summary>
|
||||
public WindowState CurrentState => _windowState;
|
||||
|
||||
/// <summary>自动吸附是否开启</summary>
|
||||
public bool AutoSnapEnabled => m_EnableAutoSnap;
|
||||
|
||||
/// <summary>自动隐藏是否开启</summary>
|
||||
public bool AutoHideEnabled => m_EnableAutoHide;
|
||||
|
||||
/// <summary>自动隐藏方向</summary>
|
||||
public AutoHideDirection HideDirection => m_AutoHideDirection;
|
||||
|
||||
/// <summary>编辑器调试模式</summary>
|
||||
public bool DebugMode => m_DebugMode;
|
||||
|
||||
/// <summary>窗口数据 Entry(由 Manager 在 CreateWindow 时注入)</summary>
|
||||
public WindowEntry Entry
|
||||
{
|
||||
get => _entry;
|
||||
set
|
||||
{
|
||||
_entry = value;
|
||||
if (_entry != null)
|
||||
m_WindowTitle = _entry.Title;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>父级 Manager 引用</summary>
|
||||
public UIFormManager Manager => _manager;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部状态
|
||||
@@ -98,13 +98,15 @@ namespace XericUI.Form
|
||||
private RectTransform _rectTransform;
|
||||
private Canvas _canvas;
|
||||
private RectTransform _canvasRt;
|
||||
private UIFormManager _manager;
|
||||
private WindowEntry _entry;
|
||||
|
||||
private WindowState _windowState = WindowState.Normal;
|
||||
|
||||
// 拖拽
|
||||
private Vector2 _dragOffset;
|
||||
|
||||
// 全屏还原数据
|
||||
// 全屏还原
|
||||
private Vector2 _savePos;
|
||||
private Vector2 _saveSize;
|
||||
private Vector2 _saveAnchorMin;
|
||||
@@ -125,110 +127,72 @@ namespace XericUI.Form
|
||||
protected virtual void Awake()
|
||||
{
|
||||
_rectTransform = (RectTransform)transform;
|
||||
_manager = GetComponentInParent<UIFormManager>();
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
_canvasRt = _canvas != null ? (RectTransform)_canvas.transform : null;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (UIFormManager.Main != null)
|
||||
UIFormManager.Main.RegisterWindow(this);
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
StopAutoHideMonitor();
|
||||
|
||||
if (_windowState == WindowState.EdgeHidden && UIFormManager.Main != null)
|
||||
UIFormManager.Main.UnregisterEdgeHidden(this);
|
||||
|
||||
if (UIFormManager.Main != null)
|
||||
{
|
||||
UIFormManager.Main.UnregisterWindow(this);
|
||||
UIFormManager.Main.UnregisterEdgeHidden(this);
|
||||
}
|
||||
if (_windowState == WindowState.EdgeHidden && _manager != null)
|
||||
_manager.UnregisterEdgeHidden(_entry);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
UnsubscribeEditorUpdate();
|
||||
#endif
|
||||
_isHovering = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUIFormTarget 实现
|
||||
#region === 公开 API ===
|
||||
|
||||
public bool IsActive => isActiveAndEnabled;
|
||||
|
||||
public Rect GetEnterRect() => GetWindowScreenRect();
|
||||
public Rect GetExitRect() => GetWindowScreenRect();
|
||||
|
||||
public void OnPointerEnter()
|
||||
{
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 鼠标进入窗口范围: {name}", this);
|
||||
}
|
||||
|
||||
public void OnPointerExit()
|
||||
{
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 鼠标离开窗口范围: {name}", this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 公开 API(供 UITitleBar 及外部调用) ===
|
||||
|
||||
/// <summary>全屏/还原切换</summary>
|
||||
public void ToggleMaximize()
|
||||
{
|
||||
if (IsMaximized)
|
||||
RestoreFromFullscreen();
|
||||
else
|
||||
MaximizeToFullscreen();
|
||||
|
||||
if (_entry != null)
|
||||
_entry.State = _windowState;
|
||||
}
|
||||
|
||||
/// <summary>最小化窗口</summary>
|
||||
public void Minimize()
|
||||
{
|
||||
if (_windowState == WindowState.Minimized) return;
|
||||
if (UIFormManager.Main == null) return;
|
||||
|
||||
UIFormManager.Main.MinimizeWindow(this);
|
||||
if (_manager == null || _entry == null) return;
|
||||
_manager.MinimizeWindow(_entry.Id);
|
||||
}
|
||||
|
||||
/// <summary>关闭窗口(Destroy GameObject)</summary>
|
||||
public void Close()
|
||||
{
|
||||
if (UIFormManager.Main != null)
|
||||
UIFormManager.Main.CloseWindow(this);
|
||||
|
||||
if (Application.isPlaying)
|
||||
if (_manager != null && _entry != null)
|
||||
_manager.DestroyWindow(_entry.Id);
|
||||
else if (Application.isPlaying)
|
||||
Destroy(gameObject);
|
||||
else
|
||||
DestroyImmediate(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>设置边缘吸附开关</summary>
|
||||
public void SetAutoSnapEnabled(bool enabled)
|
||||
{
|
||||
m_EnableAutoSnap = enabled;
|
||||
}
|
||||
|
||||
/// <summary>设置自动隐藏开关</summary>
|
||||
public void SetAutoSnapEnabled(bool enabled) => m_EnableAutoSnap = enabled;
|
||||
public void SetAutoHideEnabled(bool enabled)
|
||||
{
|
||||
m_EnableAutoHide = enabled;
|
||||
|
||||
if (!enabled && _windowState == WindowState.EdgeHidden)
|
||||
{
|
||||
// 关闭自动隐藏 → 还原窗口
|
||||
SlideInFromEdge();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>设置自动隐藏方向</summary>
|
||||
public void SetAutoHideDirection(AutoHideDirection direction)
|
||||
public void SetAutoHideDirection(AutoHideDirection direction) => m_AutoHideDirection = direction;
|
||||
|
||||
/// <summary>置顶窗口</summary>
|
||||
public void MoveToFront()
|
||||
{
|
||||
m_AutoHideDirection = direction;
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -250,9 +214,6 @@ namespace XericUI.Form
|
||||
_rectTransform.offsetMax = Vector2.zero;
|
||||
|
||||
_windowState = WindowState.Maximized;
|
||||
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 窗口全屏: {name}", this);
|
||||
}
|
||||
|
||||
private void RestoreFromFullscreen()
|
||||
@@ -265,25 +226,27 @@ namespace XericUI.Form
|
||||
_rectTransform.sizeDelta = _saveSize;
|
||||
|
||||
_windowState = WindowState.Normal;
|
||||
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 窗口还原: {name}", this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 拖拽 ===
|
||||
#region === 拖拽 & 点击聚焦 ===
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
// 点击窗口任意位置 → 置顶
|
||||
transform.SetAsLastSibling();
|
||||
if (_manager != null && _entry != null)
|
||||
_manager.MoveToFront(_entry.Id);
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized)
|
||||
return;
|
||||
if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) return;
|
||||
|
||||
// 全屏拖拽 → 先还原
|
||||
if (IsMaximized)
|
||||
{
|
||||
RestoreFromFullscreen();
|
||||
// 将窗口移到鼠标附近
|
||||
_rectTransform.anchoredPosition = eventData.position;
|
||||
}
|
||||
|
||||
@@ -292,8 +255,7 @@ namespace XericUI.Form
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized)
|
||||
return;
|
||||
if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) return;
|
||||
|
||||
_rectTransform.anchoredPosition = eventData.position + _dragOffset;
|
||||
|
||||
@@ -303,14 +265,10 @@ namespace XericUI.Form
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized)
|
||||
return;
|
||||
if (_windowState == WindowState.EdgeHidden || _windowState == WindowState.Minimized) return;
|
||||
|
||||
// 拖拽结束 → 如果贴边且启用自动隐藏,则触发
|
||||
if (m_EnableAutoHide && IsSnappedToEdge())
|
||||
{
|
||||
AutoHideToEdge();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -320,54 +278,35 @@ namespace XericUI.Form
|
||||
private void CheckEdgeSnap()
|
||||
{
|
||||
if (_canvasRt == null) return;
|
||||
|
||||
Vector2 canvasSize = _canvasRt.rect.size;
|
||||
Vector2 pos = _rectTransform.anchoredPosition;
|
||||
Vector2 size = _rectTransform.rect.size;
|
||||
|
||||
bool snapped = false;
|
||||
|
||||
// 左
|
||||
if (Mathf.Abs(pos.x) < m_SnapThreshold)
|
||||
{
|
||||
pos.x = 0;
|
||||
snapped = true;
|
||||
}
|
||||
// 右
|
||||
else if (Mathf.Abs(pos.x + size.x - canvasSize.x) < m_SnapThreshold)
|
||||
{
|
||||
pos.x = canvasSize.x - size.x;
|
||||
snapped = true;
|
||||
}
|
||||
|
||||
_rectTransform.anchoredPosition = pos;
|
||||
}
|
||||
|
||||
/// <summary>检测窗口是否贴边</summary>
|
||||
private bool IsSnappedToEdge()
|
||||
{
|
||||
if (_canvasRt == null) return false;
|
||||
|
||||
Vector2 canvasSize = _canvasRt.rect.size;
|
||||
Vector2 pos = _rectTransform.anchoredPosition;
|
||||
Vector2 size = _rectTransform.rect.size;
|
||||
|
||||
return Mathf.Abs(pos.x) < 1f
|
||||
|| Mathf.Abs(pos.x + size.x - canvasSize.x) < 1f;
|
||||
return Mathf.Abs(pos.x) < 1f || Mathf.Abs(pos.x + size.x - canvasSize.x) < 1f;
|
||||
}
|
||||
|
||||
/// <summary>获取最近的边缘方向</summary>
|
||||
public AutoHideDirection GetClosestEdge()
|
||||
{
|
||||
if (_canvasRt == null) return AutoHideDirection.Left;
|
||||
|
||||
Vector2 canvasSize = _canvasRt.rect.size;
|
||||
Vector2 pos = _rectTransform.anchoredPosition;
|
||||
Vector2 size = _rectTransform.rect.size;
|
||||
|
||||
float distLeft = Mathf.Abs(pos.x);
|
||||
float distRight = Mathf.Abs(pos.x + size.x - canvasSize.x);
|
||||
|
||||
return distLeft < distRight ? AutoHideDirection.Left : AutoHideDirection.Right;
|
||||
}
|
||||
|
||||
@@ -377,8 +316,7 @@ namespace XericUI.Form
|
||||
|
||||
private void AutoHideToEdge()
|
||||
{
|
||||
if (_windowState == WindowState.EdgeHidden) return;
|
||||
if (_canvasRt == null) return;
|
||||
if (_windowState == WindowState.EdgeHidden || _canvasRt == null) return;
|
||||
|
||||
Vector2 canvasSize = _canvasRt.rect.size;
|
||||
Vector2 pos = _rectTransform.anchoredPosition;
|
||||
@@ -390,58 +328,40 @@ namespace XericUI.Form
|
||||
if (dir == AutoHideDirection.Left || dir == AutoHideDirection.Right)
|
||||
dir = GetClosestEdge();
|
||||
|
||||
switch (dir)
|
||||
_edgeHiddenPos = dir switch
|
||||
{
|
||||
case AutoHideDirection.Left:
|
||||
_edgeHiddenPos = new Vector2(-size.x, pos.y);
|
||||
break;
|
||||
case AutoHideDirection.Right:
|
||||
_edgeHiddenPos = new Vector2(canvasSize.x, pos.y);
|
||||
break;
|
||||
case AutoHideDirection.Down:
|
||||
_edgeHiddenPos = new Vector2(pos.x, -size.y);
|
||||
break;
|
||||
case AutoHideDirection.Up:
|
||||
_edgeHiddenPos = new Vector2(pos.x, canvasSize.y);
|
||||
break;
|
||||
}
|
||||
AutoHideDirection.Left => new Vector2(-size.x, pos.y),
|
||||
AutoHideDirection.Right => new Vector2(canvasSize.x, pos.y),
|
||||
AutoHideDirection.Down => new Vector2(pos.x, -size.y),
|
||||
AutoHideDirection.Up => new Vector2(pos.x, canvasSize.y),
|
||||
_ => pos
|
||||
};
|
||||
|
||||
_rectTransform.anchoredPosition = _edgeHiddenPos;
|
||||
_windowState = WindowState.EdgeHidden;
|
||||
|
||||
if (UIFormManager.Main != null)
|
||||
UIFormManager.Main.RegisterEdgeHidden(this, dir);
|
||||
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 自动隐藏 (方向={dir}): {name}", this);
|
||||
if (_manager != null && _entry != null)
|
||||
_manager.RegisterEdgeHidden(_entry, dir);
|
||||
}
|
||||
|
||||
/// <summary>由 Manager 调用 — 从边缘滑出</summary>
|
||||
public void SlideInFromEdge()
|
||||
{
|
||||
if (_windowState != WindowState.EdgeHidden) return;
|
||||
|
||||
StopAutoHideMonitor();
|
||||
_rectTransform.anchoredPosition = _edgeVisiblePos;
|
||||
_windowState = WindowState.Normal;
|
||||
|
||||
if (UIFormManager.Main != null)
|
||||
UIFormManager.Main.UnregisterEdgeHidden(this);
|
||||
if (_manager != null)
|
||||
_manager.UnregisterEdgeHidden(_entry);
|
||||
|
||||
// 开始监控鼠标是否离开窗口
|
||||
_autoHideCoroutine = StartCoroutine(MonitorMouseExit());
|
||||
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 从边缘滑出: {name}", this);
|
||||
}
|
||||
|
||||
/// <summary>监控鼠标是否离开窗口范围,离开则自动隐藏</summary>
|
||||
private IEnumerator MonitorMouseExit()
|
||||
{
|
||||
while (_windowState == WindowState.Normal && m_EnableAutoHide)
|
||||
{
|
||||
yield return null;
|
||||
|
||||
Rect screenRect = GetWindowScreenRect();
|
||||
if (!screenRect.Contains(Input.mousePosition))
|
||||
{
|
||||
@@ -464,82 +384,29 @@ namespace XericUI.Form
|
||||
|
||||
#region === 窗口屏幕范围 ===
|
||||
|
||||
/// <summary>获取窗口自身在屏幕空间中的 Rect</summary>
|
||||
public Rect GetWindowScreenRect()
|
||||
{
|
||||
if (_rectTransform == null || _canvas == null) return Rect.zero;
|
||||
|
||||
Vector3[] corners = new Vector3[4];
|
||||
_rectTransform.GetWorldCorners(corners);
|
||||
|
||||
Camera cam = _canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : _canvas.worldCamera;
|
||||
|
||||
Vector2 min = Vector2.positiveInfinity;
|
||||
Vector2 max = Vector2.negativeInfinity;
|
||||
|
||||
Vector2 min = Vector2.positiveInfinity, max = Vector2.negativeInfinity;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Vector2 sp = cam != null
|
||||
? RectTransformUtility.WorldToScreenPoint(cam, corners[i])
|
||||
: (Vector2)corners[i];
|
||||
|
||||
min = Vector2.Min(min, sp);
|
||||
max = Vector2.Max(max, sp);
|
||||
}
|
||||
|
||||
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 悬停检测(供 Manager 调用) ===
|
||||
|
||||
public bool CheckHover(Vector2 screenPoint)
|
||||
{
|
||||
Rect windowRect = GetWindowScreenRect();
|
||||
|
||||
if (!_isHovering)
|
||||
{
|
||||
if (windowRect.Contains(screenPoint))
|
||||
{
|
||||
_isHovering = true;
|
||||
OnPointerEnter();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!windowRect.Contains(screenPoint))
|
||||
{
|
||||
_isHovering = false;
|
||||
OnPointerExit();
|
||||
}
|
||||
}
|
||||
|
||||
return _isHovering;
|
||||
}
|
||||
|
||||
public void SimulateEnter()
|
||||
{
|
||||
_isHovering = true;
|
||||
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 模拟进入: {name}", this);
|
||||
}
|
||||
|
||||
public void SimulateExit()
|
||||
{
|
||||
_isHovering = false;
|
||||
|
||||
if (m_DebugLogEvents)
|
||||
Debug.Log($"[UIFormController] 模拟退出: {name}", this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 编辑器调试
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
[System.NonSerialized] private bool _editorDebugSubscribed;
|
||||
[System.NonSerialized] private Vector2 _editorMouseScreenPos;
|
||||
|
||||
@@ -559,87 +426,53 @@ namespace XericUI.Form
|
||||
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
if (m_DebugMode)
|
||||
SubscribeEditorUpdate();
|
||||
else
|
||||
UnsubscribeEditorUpdate();
|
||||
if (m_DebugMode) SubscribeEditorUpdate();
|
||||
else UnsubscribeEditorUpdate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SceneView 重绘回调 — 在 GUI 上下文中获取鼠标屏幕坐标并执行悬停检测。
|
||||
/// 使用 GUIUtility.GUIToScreenPoint 将 SceneView 内的 GUI 坐标转为 Unity 屏幕坐标。
|
||||
/// </summary>
|
||||
private void OnSceneGUI(UnityEditor.SceneView sceneView)
|
||||
{
|
||||
if (this == null)
|
||||
{
|
||||
UnsubscribeEditorUpdate();
|
||||
return;
|
||||
}
|
||||
if (this == null) { UnsubscribeEditorUpdate(); return; }
|
||||
if (Application.isPlaying || !m_DebugMode) return;
|
||||
|
||||
if (Application.isPlaying) return;
|
||||
if (!m_DebugMode) return;
|
||||
|
||||
// Event.current.mousePosition 是相对于 SceneView 窗口的 GUI 坐标
|
||||
// GUIUtility.GUIToScreenPoint 将其转换为屏幕坐标(左下角为原点)
|
||||
Vector2 guiPos = Event.current.mousePosition;
|
||||
_editorMouseScreenPos = GUIUtility.GUIToScreenPoint(guiPos);
|
||||
|
||||
CheckHover(_editorMouseScreenPos);
|
||||
Rect rect = GetWindowScreenRect();
|
||||
_isHovering = rect.Contains(_editorMouseScreenPos);
|
||||
}
|
||||
|
||||
protected virtual void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!m_DebugMode) return;
|
||||
if (_canvas == null) return;
|
||||
if (_canvasRt == null) return;
|
||||
|
||||
if (!m_DebugMode || _canvas == null || _canvasRt == null) return;
|
||||
Rect screenRect = GetWindowScreenRect();
|
||||
if (screenRect.width <= 0 || screenRect.height <= 0) return;
|
||||
|
||||
// 悬停时高亮边框
|
||||
Color borderColor = _isHovering
|
||||
? new Color(0f, 1f, 0f, 0.6f)
|
||||
: new Color(0f, 0.7f, 1f, 0.3f);
|
||||
Gizmos.color = borderColor;
|
||||
|
||||
DrawScreenRectOnCanvas(screenRect, borderColor, _canvas, _canvasRt);
|
||||
|
||||
// 边缘吸附阈值可视化
|
||||
if (m_EnableAutoSnap)
|
||||
{
|
||||
Gizmos.color = new Color(1f, 0.6f, 0f, 0.15f);
|
||||
DrawScreenRectOnCanvas(
|
||||
new Rect(screenRect.x + m_SnapThreshold, screenRect.y,
|
||||
screenRect.width - m_SnapThreshold * 2, screenRect.height),
|
||||
Gizmos.color, _canvas, _canvasRt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawScreenRectOnCanvas(Rect screenRect, Color color, Canvas canvas, RectTransform canvasRt)
|
||||
{
|
||||
if (screenRect.width <= 0 || screenRect.height <= 0) return;
|
||||
|
||||
Gizmos.color = color;
|
||||
Camera uiCam = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera;
|
||||
|
||||
Vector3 p0 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMin, screenRect.yMin), uiCam, canvasRt);
|
||||
Vector3 p1 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMax, screenRect.yMin), uiCam, canvasRt);
|
||||
Vector3 p2 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMax, screenRect.yMax), uiCam, canvasRt);
|
||||
Vector3 p3 = ScreenToWorldOnCanvas(new Vector3(screenRect.xMin, screenRect.yMax), uiCam, canvasRt);
|
||||
|
||||
Gizmos.DrawLine(p0, p1);
|
||||
Gizmos.DrawLine(p1, p2);
|
||||
Gizmos.DrawLine(p2, p3);
|
||||
Gizmos.DrawLine(p3, p0);
|
||||
Vector3 p0 = Stw(new Vector3(screenRect.xMin, screenRect.yMin), uiCam, canvasRt);
|
||||
Vector3 p1 = Stw(new Vector3(screenRect.xMax, screenRect.yMin), uiCam, canvasRt);
|
||||
Vector3 p2 = Stw(new Vector3(screenRect.xMax, screenRect.yMax), uiCam, canvasRt);
|
||||
Vector3 p3 = Stw(new Vector3(screenRect.xMin, screenRect.yMax), uiCam, canvasRt);
|
||||
Gizmos.DrawLine(p0, p1); Gizmos.DrawLine(p1, p2);
|
||||
Gizmos.DrawLine(p2, p3); Gizmos.DrawLine(p3, p0);
|
||||
}
|
||||
|
||||
private static Vector3 ScreenToWorldOnCanvas(Vector3 screenPoint, Camera uiCamera, RectTransform canvasRt)
|
||||
private static Vector3 Stw(Vector3 sp, Camera cam, RectTransform rt)
|
||||
{
|
||||
RectTransformUtility.ScreenPointToWorldPointInRectangle(canvasRt, (Vector2)screenPoint, uiCamera, out Vector3 wp);
|
||||
RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, (Vector2)sp, cam, out Vector3 wp);
|
||||
return wp;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务栏控制器 — 监听 UIFormManager 的窗口创建/销毁/状态变更事件,
|
||||
/// 动态创建/销毁 UITaskBarItem 图标。
|
||||
/// </summary>
|
||||
public class UITaskBar : MonoBehaviour
|
||||
{
|
||||
[Header("绑定")]
|
||||
[SerializeField, Tooltip("窗口管理器引用")]
|
||||
private UIFormManager m_Manager;
|
||||
|
||||
[SerializeField, Tooltip("任务栏图标项预制体")]
|
||||
private UITaskBarItem m_ItemPrefab;
|
||||
|
||||
[SerializeField, Tooltip("图标项的父容器")]
|
||||
private Transform m_ItemsContainer;
|
||||
|
||||
private readonly Dictionary<string, UITaskBarItem> _items = new Dictionary<string, UITaskBarItem>();
|
||||
|
||||
#region 生命周期
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (m_Manager == null)
|
||||
{
|
||||
Debug.LogError("[UITaskBar] 未绑定 UIFormManager!", this);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Manager.OnWindowCreated += OnWindowCreated;
|
||||
m_Manager.OnWindowDestroyed += OnWindowDestroyed;
|
||||
m_Manager.OnWindowStateChanged += OnWindowStateChanged;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (m_Manager != null)
|
||||
{
|
||||
m_Manager.OnWindowCreated -= OnWindowCreated;
|
||||
m_Manager.OnWindowDestroyed -= OnWindowDestroyed;
|
||||
m_Manager.OnWindowStateChanged -= OnWindowStateChanged;
|
||||
}
|
||||
|
||||
foreach (var item in _items.Values)
|
||||
{
|
||||
if (item != null)
|
||||
Destroy(item.gameObject);
|
||||
}
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件回调
|
||||
|
||||
private void OnWindowCreated(WindowEntry entry)
|
||||
{
|
||||
if (entry == null || !entry.ShowInTaskBar) return;
|
||||
if (m_ItemPrefab == null || m_ItemsContainer == null) return;
|
||||
|
||||
var item = Instantiate(m_ItemPrefab, m_ItemsContainer);
|
||||
item.Bind(entry, m_Manager);
|
||||
_items[entry.Id] = item;
|
||||
}
|
||||
|
||||
private void OnWindowDestroyed(WindowEntry entry)
|
||||
{
|
||||
if (entry == null) return;
|
||||
|
||||
if (_items.TryGetValue(entry.Id, out var item))
|
||||
{
|
||||
if (item != null)
|
||||
Destroy(item.gameObject);
|
||||
_items.Remove(entry.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWindowStateChanged(WindowEntry entry)
|
||||
{
|
||||
if (entry == null) return;
|
||||
|
||||
// 状态变更时触发 Repaint(进度、高亮等由 Item 自己的 Update 处理)
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08764fa4decbd8b4b9f16f7236d36baf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,108 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XericUI.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务栏图标项 — 显示单个窗口的图标、标题、进度条和聚焦高亮。
|
||||
/// 点击时通过 UIFormManager 切换窗口显示/隐藏并置顶。
|
||||
/// </summary>
|
||||
public class UITaskBarItem : MonoBehaviour
|
||||
{
|
||||
[Header("控件引用")]
|
||||
[SerializeField, Tooltip("窗口图标")]
|
||||
private Image m_Icon;
|
||||
|
||||
[SerializeField, Tooltip("窗口标题文本")]
|
||||
private Text m_TitleText;
|
||||
|
||||
[SerializeField, Tooltip("进度条 Image(fillAmount)")]
|
||||
private Image m_ProgressBar;
|
||||
|
||||
[SerializeField, Tooltip("聚焦高亮 Image")]
|
||||
private Image m_Highlight;
|
||||
|
||||
[SerializeField, Tooltip("按钮组件")]
|
||||
private Button m_Button;
|
||||
|
||||
private WindowEntry _entry;
|
||||
private UIFormManager _manager;
|
||||
|
||||
public WindowEntry Entry => _entry;
|
||||
|
||||
#region 初始化
|
||||
|
||||
public void Bind(WindowEntry entry, UIFormManager manager)
|
||||
{
|
||||
_entry = entry;
|
||||
_manager = manager;
|
||||
|
||||
if (m_Icon != null && entry.Icon != null)
|
||||
m_Icon.sprite = entry.Icon;
|
||||
|
||||
if (m_Icon != null)
|
||||
m_Icon.color = entry.TintColor;
|
||||
|
||||
if (m_TitleText != null)
|
||||
m_TitleText.text = entry.Title;
|
||||
|
||||
if (m_Button != null)
|
||||
m_Button.onClick.AddListener(OnClick);
|
||||
|
||||
RefreshHighlight();
|
||||
RefreshProgress();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (m_Button != null)
|
||||
m_Button.onClick.RemoveListener(OnClick);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 交互
|
||||
|
||||
private void OnClick()
|
||||
{
|
||||
if (_entry == null || _manager == null) return;
|
||||
|
||||
_manager.ToggleWindow(_entry.Id);
|
||||
_manager.MoveToFront(_entry.Id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 每帧同步
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_entry == null) return;
|
||||
|
||||
if (m_TitleText != null && m_TitleText.text != _entry.Title)
|
||||
m_TitleText.text = _entry.Title;
|
||||
|
||||
if (m_Icon != null && m_Icon.color != _entry.TintColor)
|
||||
m_Icon.color = _entry.TintColor;
|
||||
|
||||
RefreshProgress();
|
||||
RefreshHighlight();
|
||||
}
|
||||
|
||||
private void RefreshProgress()
|
||||
{
|
||||
if (m_ProgressBar == null) return;
|
||||
m_ProgressBar.fillAmount = _entry.Progress;
|
||||
m_ProgressBar.gameObject.SetActive(_entry.ShowProgress && _entry.Progress > 0f);
|
||||
}
|
||||
|
||||
private void RefreshHighlight()
|
||||
{
|
||||
if (m_Highlight == null || _manager == null) return;
|
||||
var focused = _manager.GetFocusedEntry();
|
||||
m_Highlight.gameObject.SetActive(focused == _entry);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf298c8b67bb4d345a69728112b72229
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,21 +12,23 @@ namespace XericUI.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 窗口标题栏 — 独立子组件,挂载在 UIFormController 的子对象上。
|
||||
/// Start 时向上查找父级 UIFormController,绑定按钮事件到其公开 API。
|
||||
/// 与窗口完全解耦:Controller 不关心标题栏是否存在。
|
||||
/// Start 时向上查找父级 UIFormController,绑定按钮/Toggle 事件到其公开 API。
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/Form/UI Title Bar")]
|
||||
public class UITitleBar : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
[Header("控件引用")]
|
||||
[SerializeField, Tooltip("窗口图标")]
|
||||
private Image m_IconImage;
|
||||
|
||||
[SerializeField, Tooltip("标题文本")]
|
||||
private UI_Text m_TitleText;
|
||||
|
||||
[SerializeField, Tooltip("关闭按钮")]
|
||||
private Button m_CloseButton;
|
||||
|
||||
[SerializeField, Tooltip("最大化按钮")]
|
||||
private Button m_MaximizeButton;
|
||||
[SerializeField, Tooltip("最大化 Toggle(isOn=全屏)")]
|
||||
private Toggle m_MaximizeToggle;
|
||||
|
||||
[SerializeField, Tooltip("最小化按钮")]
|
||||
private Button m_MinimizeButton;
|
||||
@@ -57,9 +59,25 @@ namespace XericUI.Form
|
||||
{
|
||||
if (_controller == null) return;
|
||||
|
||||
// 实时同步标题(可能由外部修改)
|
||||
if (m_TitleText != null && m_TitleText.text != _controller.WindowTitle)
|
||||
m_TitleText.text = _controller.WindowTitle;
|
||||
|
||||
// 同步最大化 Toggle 状态
|
||||
if (m_MaximizeToggle != null)
|
||||
{
|
||||
bool maxed = _controller.IsMaximized;
|
||||
if (m_MaximizeToggle.isOn != maxed)
|
||||
m_MaximizeToggle.SetIsOnWithoutNotify(maxed);
|
||||
}
|
||||
|
||||
// 同步图标
|
||||
if (m_IconImage != null && _controller.Entry != null)
|
||||
{
|
||||
if (_controller.Entry.Icon != null && m_IconImage.sprite != _controller.Entry.Icon)
|
||||
m_IconImage.sprite = _controller.Entry.Icon;
|
||||
if (m_IconImage.color != _controller.Entry.TintColor)
|
||||
m_IconImage.color = _controller.Entry.TintColor;
|
||||
}
|
||||
}
|
||||
|
||||
#region 事件绑定
|
||||
@@ -69,8 +87,8 @@ namespace XericUI.Form
|
||||
if (m_CloseButton != null)
|
||||
m_CloseButton.onClick.AddListener(() => _controller.Close());
|
||||
|
||||
if (m_MaximizeButton != null)
|
||||
m_MaximizeButton.onClick.AddListener(() => _controller.ToggleMaximize());
|
||||
if (m_MaximizeToggle != null)
|
||||
m_MaximizeToggle.onValueChanged.AddListener(_ => _controller.ToggleMaximize());
|
||||
|
||||
if (m_MinimizeButton != null)
|
||||
m_MinimizeButton.onClick.AddListener(() => _controller.Minimize());
|
||||
@@ -119,11 +137,21 @@ namespace XericUI.Form
|
||||
if (m_TitleText != null)
|
||||
m_TitleText.text = _controller.WindowTitle;
|
||||
|
||||
if (m_MaximizeToggle != null)
|
||||
m_MaximizeToggle.isOn = _controller.IsMaximized;
|
||||
|
||||
if (m_AutoSnapToggle != null)
|
||||
m_AutoSnapToggle.isOn = _controller.AutoSnapEnabled;
|
||||
|
||||
if (m_AutoHideToggle != null)
|
||||
m_AutoHideToggle.isOn = _controller.AutoHideEnabled;
|
||||
|
||||
// 图标
|
||||
if (m_IconImage != null && _controller.Entry != null && _controller.Entry.Icon != null)
|
||||
{
|
||||
m_IconImage.sprite = _controller.Entry.Icon;
|
||||
m_IconImage.color = _controller.Entry.TintColor;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
+237
-177
@@ -4,91 +4,57 @@ using UnityEngine;
|
||||
namespace XericUI.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// UI 窗体管理器 — 窗口管理中心。
|
||||
/// 挂载在 Canvas 所在 GameObject 上(或通过代码自动创建),
|
||||
/// 管理所有窗口的注册、最小化恢复、边缘触发隐藏窗口的显隐调度。
|
||||
/// UI 窗体管理器 — 窗口管理中心。挂载在 Canvas 下作为所有窗口的父容器。
|
||||
/// 通过 Entry 驱动管理窗口生命周期、层级排序、委托事件发布。
|
||||
/// 支持任务栏和边缘触发隐藏窗口。
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric UI Vessel/Form/UI Form Manager")]
|
||||
[DefaultExecutionOrder(32000)]
|
||||
public class UIFormManager : MonoBehaviour
|
||||
{
|
||||
[Header("边缘触发")]
|
||||
[SerializeField, Tooltip("边缘触发区域大小(x=宽度方向,y=高度方向),鼠标进入此区域时触发对应方向隐藏窗口的显示")]
|
||||
[SerializeField, Tooltip("边缘触发区域大小")]
|
||||
private Vector2 m_EdgeTriggerSize = new Vector2(80, 60);
|
||||
|
||||
[Header("调试")]
|
||||
[SerializeField, Tooltip("输出事件日志")]
|
||||
private bool m_EnableDebugLog;
|
||||
|
||||
#region 静态单例
|
||||
#region === 委托事件 ===
|
||||
|
||||
private static UIFormManager s_Main;
|
||||
/// <summary>窗口创建</summary>
|
||||
public event WindowEntryEventHandler OnWindowCreated;
|
||||
|
||||
/// <summary>全局单例引用</summary>
|
||||
public static UIFormManager Main => s_Main;
|
||||
/// <summary>窗口销毁</summary>
|
||||
public event WindowEntryEventHandler OnWindowDestroyed;
|
||||
|
||||
/// <summary>窗口状态变更</summary>
|
||||
public event WindowEntryEventHandler OnWindowStateChanged;
|
||||
|
||||
/// <summary>窗口获得焦点(置顶)</summary>
|
||||
public event WindowEntryEventHandler OnWindowFocused;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部状态
|
||||
#region === 内部状态 ===
|
||||
|
||||
// 所有窗口
|
||||
private readonly List<UIFormController> _windows = new List<UIFormController>();
|
||||
private readonly Dictionary<string, WindowEntry> _entries = new Dictionary<string, WindowEntry>();
|
||||
private readonly List<WindowEntry> _orderedEntries = new List<WindowEntry>();
|
||||
private string _focusedEntryId;
|
||||
|
||||
// 最小化窗口数据
|
||||
private struct MinimizedData
|
||||
// 边缘隐藏(按方向分组)
|
||||
private readonly Dictionary<AutoHideDirection, List<WindowEntry>> _edgeHidden = new()
|
||||
{
|
||||
public Vector2 anchoredPosition;
|
||||
public Vector2 sizeDelta;
|
||||
public Vector2 anchorMin;
|
||||
public Vector2 anchorMax;
|
||||
}
|
||||
private readonly Dictionary<UIFormController, MinimizedData> _minimized = new();
|
||||
|
||||
// 边缘隐藏窗口(按方向分组)
|
||||
private readonly Dictionary<AutoHideDirection, List<UIFormController>> _edgeHidden = new()
|
||||
{
|
||||
{ AutoHideDirection.Left, new List<UIFormController>() },
|
||||
{ AutoHideDirection.Right, new List<UIFormController>() },
|
||||
{ AutoHideDirection.Up, new List<UIFormController>() },
|
||||
{ AutoHideDirection.Down, new List<UIFormController>() },
|
||||
{ AutoHideDirection.Left, new List<WindowEntry>() },
|
||||
{ AutoHideDirection.Right, new List<WindowEntry>() },
|
||||
{ AutoHideDirection.Up, new List<WindowEntry>() },
|
||||
{ AutoHideDirection.Down, new List<WindowEntry>() },
|
||||
};
|
||||
|
||||
// 边缘触发状态跟踪(避免重复触发)
|
||||
private AutoHideDirection? _lastEdgeTrigger;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (s_Main != null && s_Main != this)
|
||||
{
|
||||
Debug.LogWarning($"[UIFormManager] 已存在单例,销毁重复实例: {name}", this);
|
||||
Destroy(this);
|
||||
return;
|
||||
}
|
||||
|
||||
s_Main = this;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (s_Main == null) s_Main = this;
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (s_Main == this) s_Main = null;
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
if (s_Main == this) s_Main = null;
|
||||
_windows.Clear();
|
||||
_minimized.Clear();
|
||||
foreach (var list in _edgeHidden.Values) list.Clear();
|
||||
}
|
||||
#region === 生命周期 ===
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
@@ -97,64 +63,222 @@ namespace XericUI.Form
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 窗口注册 / 注销 ===
|
||||
#region === 程序化窗口创建 / 销毁 ===
|
||||
|
||||
public void RegisterWindow(UIFormController window)
|
||||
/// <summary>程序化创建一个窗口实例并返回其 Entry</summary>
|
||||
public WindowEntry CreateWindow(RectTransform prefab, string title = null, Sprite icon = null)
|
||||
{
|
||||
if (window == null || _windows.Contains(window)) return;
|
||||
_windows.Add(window);
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError("[UIFormManager] CreateWindow: prefab 为空", this);
|
||||
return null;
|
||||
}
|
||||
|
||||
var instance = Instantiate(prefab, transform);
|
||||
instance.name = title ?? prefab.name;
|
||||
|
||||
var controller = instance.GetComponent<UIFormController>();
|
||||
var entry = new WindowEntry
|
||||
{
|
||||
Title = title ?? prefab.name,
|
||||
Icon = icon,
|
||||
Prefab = prefab,
|
||||
Controller = controller,
|
||||
State = WindowState.Normal,
|
||||
};
|
||||
|
||||
if (controller != null)
|
||||
controller.Entry = entry;
|
||||
|
||||
_entries[entry.Id] = entry;
|
||||
_orderedEntries.Add(entry);
|
||||
|
||||
MoveToFront(entry.Id);
|
||||
|
||||
NotifyCreated(entry);
|
||||
|
||||
if (m_EnableDebugLog)
|
||||
Debug.Log($"[UIFormManager] 注册窗口: {window.name}");
|
||||
Debug.Log($"[UIFormManager] 创建窗口: {entry.Title} (ID={entry.Id})");
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void UnregisterWindow(UIFormController window)
|
||||
/// <summary>销毁窗口及其所有子窗口</summary>
|
||||
public void DestroyWindow(string entryId)
|
||||
{
|
||||
if (window == null) return;
|
||||
_windows.Remove(window);
|
||||
UnregisterEdgeHidden(window);
|
||||
_minimized.Remove(window);
|
||||
if (!_entries.TryGetValue(entryId, out var entry)) return;
|
||||
|
||||
if (m_EnableDebugLog)
|
||||
Debug.Log($"[UIFormManager] 注销窗口: {window.name}");
|
||||
// 级联关闭子窗口
|
||||
CascadeCloseChildren(entry);
|
||||
|
||||
UnregisterEdgeHiddenForEntry(entry);
|
||||
|
||||
_orderedEntries.Remove(entry);
|
||||
_entries.Remove(entryId);
|
||||
|
||||
if (_focusedEntryId == entryId)
|
||||
_focusedEntryId = null;
|
||||
|
||||
var controller = entry.Controller;
|
||||
entry.Controller = null;
|
||||
|
||||
NotifyDestroyed(entry);
|
||||
|
||||
if (controller != null && Application.isPlaying)
|
||||
Destroy(controller.gameObject);
|
||||
}
|
||||
|
||||
private void CascadeCloseChildren(WindowEntry entry)
|
||||
{
|
||||
for (int i = entry.ChildEntries.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var child = entry.ChildEntries[i];
|
||||
DestroyWindow(child.Id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 边缘隐藏窗口管理 ===
|
||||
#region === 显示控制 ===
|
||||
|
||||
/// <summary>注册边缘隐藏窗口</summary>
|
||||
public void RegisterEdgeHidden(UIFormController window, AutoHideDirection direction)
|
||||
/// <summary>显示窗口并置顶</summary>
|
||||
public void ShowWindow(string entryId)
|
||||
{
|
||||
if (window == null) return;
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
|
||||
entry.Controller.gameObject.SetActive(true);
|
||||
MoveToFront(entryId);
|
||||
}
|
||||
|
||||
/// <summary>隐藏窗口</summary>
|
||||
public void HideWindow(string entryId)
|
||||
{
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
|
||||
entry.Controller.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>切换窗口显示/隐藏</summary>
|
||||
public void ToggleWindow(string entryId)
|
||||
{
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
|
||||
if (entry.State == WindowState.Minimized)
|
||||
{
|
||||
RestoreWindow(entryId);
|
||||
}
|
||||
else if (entry.Controller.gameObject.activeSelf)
|
||||
{
|
||||
MinimizeWindow(entryId);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowWindow(entryId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>将窗口置顶</summary>
|
||||
public void MoveToFront(string entryId)
|
||||
{
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
|
||||
entry.Controller.transform.SetAsLastSibling();
|
||||
|
||||
_orderedEntries.Remove(entry);
|
||||
_orderedEntries.Add(entry);
|
||||
|
||||
_focusedEntryId = entryId;
|
||||
|
||||
NotifyFocused(entry);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 最小化 / 恢复 ===
|
||||
|
||||
/// <summary>最小化窗口</summary>
|
||||
public void MinimizeWindow(string entryId)
|
||||
{
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
if (entry.State == WindowState.Minimized) return;
|
||||
|
||||
entry.Controller.gameObject.SetActive(false);
|
||||
entry.State = WindowState.Minimized;
|
||||
|
||||
NotifyStateChanged(entry);
|
||||
}
|
||||
|
||||
/// <summary>恢复已最小化的窗口</summary>
|
||||
public void RestoreWindow(string entryId)
|
||||
{
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
if (entry.State != WindowState.Minimized) return;
|
||||
|
||||
ShowWindow(entryId);
|
||||
entry.State = WindowState.Normal;
|
||||
|
||||
NotifyStateChanged(entry);
|
||||
}
|
||||
|
||||
public void ToggleMaximize(string entryId)
|
||||
{
|
||||
if (!TryGetEntry(entryId, out var entry)) return;
|
||||
if (entry.Controller == null) return;
|
||||
|
||||
entry.Controller.ToggleMaximize();
|
||||
NotifyStateChanged(entry);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 查询 ===
|
||||
|
||||
public WindowEntry GetEntry(string id)
|
||||
{
|
||||
_entries.TryGetValue(id, out var entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public IReadOnlyList<WindowEntry> GetAllEntries() => _orderedEntries;
|
||||
|
||||
public WindowEntry GetFocusedEntry()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_focusedEntryId)) return null;
|
||||
return GetEntry(_focusedEntryId);
|
||||
}
|
||||
|
||||
public int EntryCount => _entries.Count;
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 边缘隐藏 ===
|
||||
|
||||
public void RegisterEdgeHidden(WindowEntry entry, AutoHideDirection direction)
|
||||
{
|
||||
if (entry == null) return;
|
||||
var list = _edgeHidden[direction];
|
||||
if (!list.Contains(window))
|
||||
list.Add(window);
|
||||
|
||||
if (m_EnableDebugLog)
|
||||
Debug.Log($"[UIFormManager] 边缘隐藏注册: {window.name} (方向={direction})");
|
||||
if (!list.Contains(entry))
|
||||
list.Add(entry);
|
||||
}
|
||||
|
||||
/// <summary>注销边缘隐藏窗口</summary>
|
||||
public void UnregisterEdgeHidden(UIFormController window)
|
||||
public void UnregisterEdgeHidden(WindowEntry entry)
|
||||
{
|
||||
if (window == null) return;
|
||||
|
||||
if (entry == null) return;
|
||||
foreach (var list in _edgeHidden.Values)
|
||||
list.Remove(window);
|
||||
list.Remove(entry);
|
||||
}
|
||||
|
||||
/// <summary>获取指定方向的隐藏窗口列表</summary>
|
||||
public IReadOnlyList<UIFormController> GetEdgeWindows(AutoHideDirection direction)
|
||||
private void UnregisterEdgeHiddenForEntry(WindowEntry entry)
|
||||
{
|
||||
return _edgeHidden[direction];
|
||||
UnregisterEdgeHidden(entry);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 边缘触发检测 ===
|
||||
|
||||
private void CheckEdgeTriggers()
|
||||
{
|
||||
Vector2 mousePos = Input.mousePosition;
|
||||
@@ -167,124 +291,60 @@ namespace XericUI.Form
|
||||
bool topTrigger = mousePos.y > (sh - m_EdgeTriggerSize.y);
|
||||
|
||||
AutoHideDirection? activeTrigger = null;
|
||||
|
||||
if (leftTrigger) activeTrigger = AutoHideDirection.Left;
|
||||
else if (rightTrigger) activeTrigger = AutoHideDirection.Right;
|
||||
else if (bottomTrigger) activeTrigger = AutoHideDirection.Down;
|
||||
else if (topTrigger) activeTrigger = AutoHideDirection.Up;
|
||||
|
||||
// 触发进入 → 显示该方向窗口
|
||||
if (activeTrigger.HasValue && activeTrigger != _lastEdgeTrigger)
|
||||
{
|
||||
ShowEdgeWindows(activeTrigger.Value);
|
||||
}
|
||||
|
||||
// 鼠标离开所有边缘触发区
|
||||
if (!activeTrigger.HasValue)
|
||||
{
|
||||
_lastEdgeTrigger = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>显示指定方向的所有边缘隐藏窗口</summary>
|
||||
public void ShowEdgeWindows(AutoHideDirection direction)
|
||||
{
|
||||
_lastEdgeTrigger = direction;
|
||||
|
||||
var list = _edgeHidden[direction];
|
||||
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var window = list[i];
|
||||
if (window == null)
|
||||
var entry = list[i];
|
||||
if (entry?.Controller == null)
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
window.SlideInFromEdge();
|
||||
entry.Controller.SlideInFromEdge();
|
||||
}
|
||||
|
||||
if (m_EnableDebugLog && list.Count > 0)
|
||||
Debug.Log($"[UIFormManager] 边缘触发: 方向={direction}, 显示 {list.Count} 个窗口");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 最小化 / 恢复 ===
|
||||
#region === 内部通知 ===
|
||||
|
||||
internal void MinimizeWindow(UIFormController window)
|
||||
private void NotifyCreated(WindowEntry entry) => OnWindowCreated?.Invoke(entry);
|
||||
private void NotifyDestroyed(WindowEntry entry) => OnWindowDestroyed?.Invoke(entry);
|
||||
private void NotifyStateChanged(WindowEntry entry) => OnWindowStateChanged?.Invoke(entry);
|
||||
private void NotifyFocused(WindowEntry entry) => OnWindowFocused?.Invoke(entry);
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 工具 ===
|
||||
|
||||
private bool TryGetEntry(string id, out WindowEntry entry)
|
||||
{
|
||||
if (window == null || _minimized.ContainsKey(window)) return;
|
||||
|
||||
var rt = window.transform as RectTransform;
|
||||
if (rt == null) return;
|
||||
|
||||
_minimized[window] = new MinimizedData
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
anchoredPosition = rt.anchoredPosition,
|
||||
sizeDelta = rt.sizeDelta,
|
||||
anchorMin = rt.anchorMin,
|
||||
anchorMax = rt.anchorMax,
|
||||
};
|
||||
|
||||
window.gameObject.SetActive(false);
|
||||
|
||||
if (m_EnableDebugLog)
|
||||
Debug.Log($"[UIFormManager] 最小化窗口: {window.name}");
|
||||
entry = null;
|
||||
return false;
|
||||
}
|
||||
return _entries.TryGetValue(id, out entry);
|
||||
}
|
||||
|
||||
/// <summary>恢复已最小化的窗口</summary>
|
||||
public void RestoreWindow(UIFormController window)
|
||||
{
|
||||
if (window == null || !_minimized.TryGetValue(window, out var data)) return;
|
||||
|
||||
var rt = window.transform as RectTransform;
|
||||
if (rt == null) return;
|
||||
|
||||
rt.anchorMin = data.anchorMin;
|
||||
rt.anchorMax = data.anchorMax;
|
||||
rt.anchoredPosition = data.anchoredPosition;
|
||||
rt.sizeDelta = data.sizeDelta;
|
||||
|
||||
window.gameObject.SetActive(true);
|
||||
_minimized.Remove(window);
|
||||
|
||||
if (m_EnableDebugLog)
|
||||
Debug.Log($"[UIFormManager] 恢复窗口: {window.name}");
|
||||
}
|
||||
|
||||
/// <summary>获取所有已最小化的窗口列表</summary>
|
||||
public IReadOnlyList<UIFormController> GetMinimizedWindows()
|
||||
{
|
||||
return new List<UIFormController>(_minimized.Keys);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 关闭 ===
|
||||
|
||||
internal void CloseWindow(UIFormController window)
|
||||
{
|
||||
if (window == null) return;
|
||||
|
||||
_windows.Remove(window);
|
||||
UnregisterEdgeHidden(window);
|
||||
_minimized.Remove(window);
|
||||
|
||||
if (m_EnableDebugLog)
|
||||
Debug.Log($"[UIFormManager] 关闭窗口: {window.name}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region === 查询 ===
|
||||
|
||||
/// <summary>已注册窗口数量</summary>
|
||||
public int WindowCount => _windows.Count;
|
||||
|
||||
/// <summary>获取所有已注册窗口</summary>
|
||||
public IReadOnlyList<UIFormController> GetAllWindows() => _windows;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericUI.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 窗口数据描述对象 — 由 UIFormManager 创建和持有。
|
||||
/// 描述一个窗口实例的标识信息、运行时引用、显示状态和层级关系。
|
||||
/// </summary>
|
||||
public class WindowEntry
|
||||
{
|
||||
#region 基本标识
|
||||
|
||||
/// <summary>唯一 ID(GUID)</summary>
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>窗口标题</summary>
|
||||
public string Title;
|
||||
|
||||
/// <summary>窗口图标(任务栏和标题栏使用)</summary>
|
||||
public Sprite Icon;
|
||||
|
||||
/// <summary>任务栏图标着色</summary>
|
||||
public Color TintColor = Color.white;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 实例引用
|
||||
|
||||
/// <summary>运行时窗口实例</summary>
|
||||
public UIFormController Controller;
|
||||
|
||||
/// <summary>窗口预制体引用</summary>
|
||||
public RectTransform Prefab;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 显示状态
|
||||
|
||||
/// <summary>当前窗口状态</summary>
|
||||
public WindowState State;
|
||||
|
||||
/// <summary>是否在任务栏上显示图标</summary>
|
||||
public bool ShowInTaskBar = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 层级关系
|
||||
|
||||
/// <summary>父窗口 Entry(关闭父窗口时级联关闭子窗口)</summary>
|
||||
public WindowEntry ParentEntry { get; private set; }
|
||||
|
||||
/// <summary>子窗口 Entry 列表</summary>
|
||||
public readonly List<WindowEntry> ChildEntries = new List<WindowEntry>();
|
||||
|
||||
/// <summary>添加子窗口依赖</summary>
|
||||
public void AddChild(WindowEntry child)
|
||||
{
|
||||
if (child == null || ChildEntries.Contains(child)) return;
|
||||
child.ParentEntry = this;
|
||||
ChildEntries.Add(child);
|
||||
}
|
||||
|
||||
/// <summary>移除子窗口依赖</summary>
|
||||
public void RemoveChild(WindowEntry child)
|
||||
{
|
||||
if (child == null) return;
|
||||
child.ParentEntry = null;
|
||||
ChildEntries.Remove(child);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 进度
|
||||
|
||||
/// <summary>任务栏进度 (0-1)</summary>
|
||||
public float Progress;
|
||||
|
||||
/// <summary>是否在任务栏显示进度条</summary>
|
||||
public bool ShowProgress;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造
|
||||
|
||||
public WindowEntry(string id)
|
||||
{
|
||||
Id = id ?? System.Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public WindowEntry() : this(null) { }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 委托
|
||||
|
||||
/// <summary>窗口 Entry 事件委托</summary>
|
||||
public delegate void WindowEntryEventHandler(WindowEntry entry);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 174c44278748c9a48b2d6600a450f65a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 785b681a65a87f54e9ade90261da6d87
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7f23b52396c0c840a4f927a3468a0c9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1ad7693cfe6cf3448535dce51381df1
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user