using System; using RosettaUI.UndoSystem; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UIElements; namespace RosettaUI.UIToolkit { /// /// ColorPicker,GradientEditor, AnimationCurveEditorなど他の操作をさせないパラメータエディター /// public abstract class ModalEditor : VisualElement { protected readonly ModalWindow window; public event Action onEditorValueChanged; /// /// persistentSizeKeyでウィンドウサイズを保存・復元するコンストラクタ /// protected ModalEditor(string persistentSizeKey, Vector2 defaultSize, string visualTreeAssetPath = "") : this(visualTreeAssetPath, true) { Assert.IsFalse(string.IsNullOrEmpty(persistentSizeKey), $"{nameof(persistentSizeKey)} is null or empty"); if (!PersistentData.TryGet(persistentSizeKey, out var size)) { size = defaultSize; } window.SetSize(size); window.onHide += (_) => PersistentData.Set(persistentSizeKey, window.GetSize()); } protected ModalEditor(string visualTreeAssetPath = "", bool resizable = false) { if (!string.IsNullOrEmpty(visualTreeAssetPath)) { var visualTree = Resources.Load(visualTreeAssetPath); if (visualTree != null) { visualTree.CloneTree(this); } else { Debug.LogError($"Visual tree asset not found: {visualTreeAssetPath}"); } } window = new ModalWindow(resizable); window.Add(this); } protected void Show(Vector2 position, VisualElement target, Action onValueChanged, Action onHide = null) { onEditorValueChanged += onValueChanged; UndoHistory.PushHistoryStack(nameof(ModalEditor)); window.onHide += OnHide; window.RegisterCallbackOnce(_ => { window.onHide -= OnHide; onEditorValueChanged -= onValueChanged; target?.Focus(); }); window.Show(position, target); // 画面からはみ出さないように補正する window.RegisterCallbackOnce(_ => { VisualElementExtensions.ClampPositionToScreen(position, window); }); return; void OnHide(bool isCancelled) { // onHideでModalEditorの結果をUndoに残すケースを考慮してonHideの前にPopする UndoHistory.PopHistoryStack(); onHide?.Invoke(isCancelled); } } protected virtual void NotifyEditorValueChanged() { onEditorValueChanged?.Invoke(CopiedValue); } // TValueがクラスの場合、同じTValueインスタンスを外部で変更されるとまずい場合がある // SetInitialValue()とGetValueForNotification()の実装を強制してTValueをCloneすべきかどうか protected abstract TValue CopiedValue { get; set; } } }