diff --git a/Editor/Form.meta b/Editor/Form.meta
new file mode 100644
index 0000000..7366297
--- /dev/null
+++ b/Editor/Form.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2f18f75958efeb8428a4689e959e4350
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/Form/UIFormControllerEditor.cs b/Editor/Form/UIFormControllerEditor.cs
new file mode 100644
index 0000000..aa64846
--- /dev/null
+++ b/Editor/Form/UIFormControllerEditor.cs
@@ -0,0 +1,111 @@
+using UnityEditor;
+using UnityEngine;
+using XericUI.Form;
+
+namespace XericUIEditor.Form
+{
+ [CustomEditor(typeof(XericUI.Form.UIFormController))]
+ public class UIFormControllerEditor : Editor
+ {
+ private XericUI.Form.UIFormController _controller;
+
+ private void OnEnable()
+ {
+ _controller = (XericUI.Form.UIFormController)target;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ DrawBasicSection();
+ DrawEdgeSection();
+ DrawDebugSection();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ private void DrawBasicSection()
+ {
+ EditorGUILayout.LabelField("窗口基本设置", EditorStyles.boldLabel);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_WindowTitle"), new GUIContent("窗口标题 (Title)"));
+ EditorGUILayout.Space();
+ }
+
+ private void DrawEdgeSection()
+ {
+ EditorGUILayout.LabelField("边缘吸附", EditorStyles.boldLabel);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_EnableAutoSnap"), new GUIContent("启用边缘吸附 (Auto Snap)"));
+ if (_controller.AutoSnapEnabled)
+ {
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_SnapThreshold"), new GUIContent("吸附阈值 (Snap Threshold, px)"));
+ }
+
+ EditorGUILayout.Space();
+
+ EditorGUILayout.LabelField("自动隐藏", EditorStyles.boldLabel);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_EnableAutoHide"), new GUIContent("启用自动隐藏 (Auto Hide)"));
+ if (_controller.AutoHideEnabled)
+ {
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_AutoHideDirection"), new GUIContent("隐藏方向 (Direction)"));
+ }
+
+ EditorGUILayout.Space();
+ }
+
+ private void DrawDebugSection()
+ {
+ EditorGUILayout.LabelField("调试", EditorStyles.boldLabel);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_DebugMode"), new GUIContent("调试模式 (Debug Mode)"));
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_DebugLogEvents"), new GUIContent("事件日志 (Log Events)"));
+
+ EditorGUILayout.Space();
+
+ // 当前状态
+ EditorGUILayout.LabelField("当前状态", EditorStyles.boldLabel);
+ var state = _controller.CurrentState;
+ Color stateColor = state switch
+ {
+ 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($"{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("置顶 (Focus)"))
+ {
+ _controller.MoveToFront();
+ SceneView.RepaintAll();
+ }
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.Space();
+
+ if (!Application.isPlaying && _controller.DebugMode)
+ {
+ EditorGUILayout.HelpBox(
+ "调试模式已启用 — SceneView 中移动鼠标实时预览。\n" +
+ "蓝色边框=窗口范围 | 绿色=鼠标悬停中",
+ MessageType.Info);
+ }
+ }
+ }
+}
diff --git a/Editor/Form/UIFormControllerEditor.cs.meta b/Editor/Form/UIFormControllerEditor.cs.meta
new file mode 100644
index 0000000..9488ea3
--- /dev/null
+++ b/Editor/Form/UIFormControllerEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f105e0b75b54e142b3844d8f657e32a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/Form/UITaskBarEditor.cs b/Editor/Form/UITaskBarEditor.cs
new file mode 100644
index 0000000..472072e
--- /dev/null
+++ b/Editor/Form/UITaskBarEditor.cs
@@ -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();
+ }
+ }
+}
diff --git a/Editor/Form/UITaskBarEditor.cs.meta b/Editor/Form/UITaskBarEditor.cs.meta
new file mode 100644
index 0000000..d0f6a15
--- /dev/null
+++ b/Editor/Form/UITaskBarEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0dfbe31f222f10a4883ad6016cdad32b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/Form/UITitleBarEditor.cs b/Editor/Form/UITitleBarEditor.cs
new file mode 100644
index 0000000..3c07fd8
--- /dev/null
+++ b/Editor/Form/UITitleBarEditor.cs
@@ -0,0 +1,30 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace XericUIEditor.Form
+{
+ [CustomEditor(typeof(XericUI.Form.UITitleBar))]
+ public class UITitleBarEditor : Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ EditorGUILayout.LabelField("标题栏控件引用", EditorStyles.boldLabel);
+ EditorGUILayout.HelpBox(
+ "将标题栏下的子控件拖入下方槽位。\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_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)"));
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Editor/Form/UITitleBarEditor.cs.meta b/Editor/Form/UITitleBarEditor.cs.meta
new file mode 100644
index 0000000..abe633b
--- /dev/null
+++ b/Editor/Form/UITitleBarEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5c45b2d7be6212e479b5cf00a0369596
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Form/Controllers.meta b/Runtime/Form/Controllers.meta
new file mode 100644
index 0000000..5b0f65a
--- /dev/null
+++ b/Runtime/Form/Controllers.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d1e580c430b3308419647d63c73b21c2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Form/Controllers/AutoHideDirection.cs b/Runtime/Form/Controllers/AutoHideDirection.cs
new file mode 100644
index 0000000..c3dab5c
--- /dev/null
+++ b/Runtime/Form/Controllers/AutoHideDirection.cs
@@ -0,0 +1,14 @@
+namespace XericUI.Form
+{
+ ///
+ /// 自动隐藏滑动方向。
+ /// 窗口贴边后可沿此方向滑出屏幕,鼠标进入对应边缘区域后滑回。
+ ///
+ public enum AutoHideDirection
+ {
+ Left,
+ Right,
+ Up,
+ Down
+ }
+}
diff --git a/Runtime/Form/Controllers/AutoHideDirection.cs.meta b/Runtime/Form/Controllers/AutoHideDirection.cs.meta
new file mode 100644
index 0000000..a26cdbd
--- /dev/null
+++ b/Runtime/Form/Controllers/AutoHideDirection.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6441e2d1b03e7a942a1c958f979e9d3d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Form/Controllers/IUIFormTarget.cs b/Runtime/Form/Controllers/IUIFormTarget.cs
new file mode 100644
index 0000000..5d50570
--- /dev/null
+++ b/Runtime/Form/Controllers/IUIFormTarget.cs
@@ -0,0 +1,39 @@
+using UnityEngine;
+
+namespace XericUI.Form
+{
+ ///
+ /// 窗体悬停目标接口。
+ /// 实现此接口的组件可注册到 UIFormManager,由管理器统一检测鼠标悬停状态。
+ ///
+ public interface IUIFormTarget
+ {
+ ///
+ /// 进入范围(屏幕空间 Rect,原点为左下角,单位为像素)。
+ /// 鼠标进入此范围时触发 OnPointerEnter。
+ ///
+ Rect GetEnterRect();
+
+ ///
+ /// 退出范围(屏幕空间 Rect,原点为左下角,单位为像素)。
+ /// 应大于等于 EnterRect,鼠标离开此范围时触发 OnPointerExit,避免边界抖动。
+ ///
+ Rect GetExitRect();
+
+ ///
+ /// 鼠标进入范围内时由 UIFormManager 调用。
+ ///
+ void OnPointerEnter();
+
+ ///
+ /// 鼠标离开退出范围时由 UIFormManager 调用。
+ ///
+ void OnPointerExit();
+
+ ///
+ /// 此目标是否处于激活状态。
+ /// 返回 false 时管理器跳过此目标的检测。
+ ///
+ bool IsActive { get; }
+ }
+}
diff --git a/Runtime/Form/Controllers/IUIFormTarget.cs.meta b/Runtime/Form/Controllers/IUIFormTarget.cs.meta
new file mode 100644
index 0000000..94bc4cc
--- /dev/null
+++ b/Runtime/Form/Controllers/IUIFormTarget.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 756fa65ea23a49d4ea270f1697aaf89f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Form/Controllers/UIFormController.cs b/Runtime/Form/Controllers/UIFormController.cs
new file mode 100644
index 0000000..f5f00d2
--- /dev/null
+++ b/Runtime/Form/Controllers/UIFormController.cs
@@ -0,0 +1,480 @@
+using System.Collections;
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityEngine.UI;
+
+namespace XericUI.Form
+{
+ #region 枚举与结构体
+
+ public enum FormRangeSpace
+ {
+ RelativeOffset,
+ CanvasLocal,
+ AnchorToSelf,
+ AnchorToCanvas
+ }
+
+ [System.Serializable]
+ public struct RangeRect
+ {
+ [Tooltip("坐标空间")]
+ public FormRangeSpace space;
+
+ [Tooltip("范围定义")]
+ public Rect rect;
+ }
+
+ #endregion
+
+ ///
+ /// UI 窗体控制器 — 挂载到窗口根 GameObject 上,自身即为完整窗口。
+ /// 支持全屏/还原、边缘吸附、自动隐藏、拖拽移动、置顶。
+ /// 通过 WindowEntry 与 Manager 通信。
+ ///
+ [AddComponentMenu("Xeric UI Vessel/Form/UI Form Controller")]
+ [RequireComponent(typeof(RectTransform))]
+ public class UIFormController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler
+ {
+ #region 序列化字段
+
+ [Header("窗口基本设置")]
+ [SerializeField, Tooltip("窗口标题")]
+ private string m_WindowTitle = "Window";
+
+ [Header("边缘吸附")]
+ [SerializeField, Tooltip("启用边缘自动吸附")]
+ private bool m_EnableAutoSnap = true;
+
+ [SerializeField, Tooltip("吸附阈值(像素)")]
+ private float m_SnapThreshold = 8f;
+
+ [Header("自动隐藏")]
+ [SerializeField, Tooltip("启用自动隐藏")]
+ private bool m_EnableAutoHide;
+
+ [SerializeField, Tooltip("自动隐藏滑动方向")]
+ private AutoHideDirection m_AutoHideDirection = AutoHideDirection.Left;
+
+ [Header("调试")]
+ [SerializeField, Tooltip("编辑器调试模式")]
+ private bool m_DebugMode = true;
+
+ [SerializeField, Tooltip("输出事件日志")]
+ private bool m_DebugLogEvents;
+
+ #endregion
+
+ #region 公开属性
+
+ public string WindowTitle => m_WindowTitle;
+ public bool IsMaximized => _windowState == WindowState.Maximized;
+ public bool IsEdgeHidden => _windowState == WindowState.EdgeHidden;
+ public WindowState CurrentState => _windowState;
+ public bool AutoSnapEnabled => m_EnableAutoSnap;
+ public bool AutoHideEnabled => m_EnableAutoHide;
+ public AutoHideDirection HideDirection => m_AutoHideDirection;
+ public bool DebugMode => m_DebugMode;
+
+ /// 窗口数据 Entry(由 Manager 在 CreateWindow 时注入)
+ public WindowEntry Entry
+ {
+ get => _entry;
+ set
+ {
+ _entry = value;
+ if (_entry != null)
+ m_WindowTitle = _entry.Title;
+ }
+ }
+
+ /// 父级 Manager 引用
+ public UIFormManager Manager => _manager;
+
+ #endregion
+
+ #region 内部状态
+
+ 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;
+ private Vector2 _saveAnchorMax;
+
+ // 边缘隐藏
+ private Vector2 _edgeVisiblePos;
+ private Vector2 _edgeHiddenPos;
+ private Coroutine _autoHideCoroutine;
+
+ // 悬停
+ private bool _isHovering;
+
+ #endregion
+
+ #region 生命周期
+
+ protected virtual void Awake()
+ {
+ _rectTransform = (RectTransform)transform;
+ _manager = GetComponentInParent();
+ _canvas = GetComponentInParent