diff --git a/src/LitMotion/Assets/LitMotion.Animation/Editor/AnimationComponentView.cs b/src/LitMotion/Assets/LitMotion.Animation/Editor/AnimationComponentView.cs index be38e526..49fd1eca 100644 --- a/src/LitMotion/Assets/LitMotion.Animation/Editor/AnimationComponentView.cs +++ b/src/LitMotion/Assets/LitMotion.Animation/Editor/AnimationComponentView.cs @@ -9,11 +9,13 @@ public class AnimationComponentView : VisualElement VisualElement container; readonly VisualElement contextMenuButton; readonly Foldout foldout; + readonly Toggle foldoutToggle; readonly VisualElement icon; readonly Toggle enabledToggle; readonly ProgressBar progressBar; public Foldout Foldout => foldout; + public Toggle FoldoutToggle => foldoutToggle; public Toggle EnabledToggle => enabledToggle; public VisualElement ContextMenuButton => contextMenuButton; @@ -61,6 +63,7 @@ public AnimationComponentView() }; root.Add(foldout); foldout.Add(new VisualElement() { style = { height = 5f } }); + foldoutToggle = foldout.Q(); var foldoutCheck = foldout.Q(className: Foldout.checkmarkUssClassName); icon = new VisualElement { @@ -84,7 +87,7 @@ public AnimationComponentView() { enabledToggle.pickingMode = PickingMode.Ignore; enabledToggle.Q(className: Toggle.inputUssClassName).pickingMode = PickingMode.Ignore; - enabledToggle.Q(className: Toggle.textUssClassName).pickingMode = PickingMode.Ignore; + // enabledToggle.Q(className: Toggle.textUssClassName).pickingMode = PickingMode.Ignore; # エラーが出るのでコメント enabledToggle.Q(className: Toggle.checkmarkUssClassName).pickingMode = PickingMode.Position; }); foldoutCheck.parent.Add(enabledToggle); @@ -127,7 +130,9 @@ public AnimationComponentView() }; root.Add(contextMenuButton); - container = foldout.contentContainer; + var dynamicContainer = new VisualElement { name = "dynamic-container" }; + container = dynamicContainer; + foldout.contentContainer.Add(container); } public new void SetEnabled(bool enabled) diff --git a/src/LitMotion/Assets/LitMotion.Animation/Editor/LitMotionAnimationEditor.cs b/src/LitMotion/Assets/LitMotion.Animation/Editor/LitMotionAnimationEditor.cs index e9bcd1cd..dea60d2e 100644 --- a/src/LitMotion/Assets/LitMotion.Animation/Editor/LitMotionAnimationEditor.cs +++ b/src/LitMotion/Assets/LitMotion.Animation/Editor/LitMotionAnimationEditor.cs @@ -103,22 +103,93 @@ VisualElement CreateComponentsPanel() var box = CreateBox("Components"); var views = new List(); - for (int i = 0; i < componentsProperty.arraySize; i++) + var properties = new List(); + for (int i = 0; i < componentsProperty.arraySize; ++i) { - var property = componentsProperty.GetArrayElementAtIndex(i); - var view = CreateComponentGUI(property.Copy()); - CreateContextMenuManipulator(componentsProperty, i, false).target = view.Foldout.Q(); + properties.Add(componentsProperty.GetArrayElementAtIndex(i)); + } + + ListView listView = null; + VisualElement MakeItem() + { + var view = new AnimationComponentView(); + var dynamicContainer = view.Q("dynamic-container"); + dynamicContainer.RegisterCallback>(_ => + { + componentsProperty.serializedObject.ApplyModifiedProperties(); + }); + return view; + } + void BindItem(VisualElement element, int index) + { + var view = (AnimationComponentView)element; + var dynamicContainer = view.Q("dynamic-container"); + dynamicContainer.Clear(); - var enabledProperty = property.FindPropertyRelative("enabled"); - if (enabledProperty != null) + var propertyOriginal = componentsProperty.GetArrayElementAtIndex(index); + var property = propertyOriginal.Copy(); + if (string.IsNullOrEmpty(property.managedReferenceFullTypename)) { - view.EnabledToggle.BindProperty(enabledProperty); + view.Text = "(Missing)"; + view.Icon = (Texture2D)EditorGUIUtility.IconContent("Error").image; + view.EnabledToggle.value = true; + view.SetEnabled(true); + view.EnabledToggle.Q("unity-checkmark").style.visibility = Visibility.Hidden; + view.Add(new HelpBox("The type referenced in SerializeReference is missing. You may have renamed the type or moved it to a different namespace or assembly.", HelpBoxMessageType.Error)); } + else + { + view.Text = property.FindPropertyRelative("displayName").stringValue; + + var targetProperty = property.FindPropertyRelative("target"); + if (targetProperty != null) + { + view.Icon = GUIHelper.GetComponentIcon(targetProperty.GetPropertyType()); + } + + view.TrackPropertyValue(property.FindPropertyRelative("displayName"), x => + { + view.Text = x.stringValue; + }); - box.Add(view); - views.Add(view); - CreateContextMenuManipulator(componentsProperty, i, true).target = view.ContextMenuButton; + view.Foldout.BindProperty(property); + view.FoldoutToggle.BindProperty(property.FindPropertyRelative("foldoutOn")); + + var endProperty = property.GetEndProperty(); + var isFirst = true; + while (property.NextVisible(isFirst)) + { + if (SerializedProperty.EqualContents(property, endProperty)) break; + if (property.name == "enabled") continue; + isFirst = false; + + var field = new PropertyField(property); + field.BindProperty(property); + dynamicContainer.Add(field); + } + var enabledProperty = propertyOriginal.FindPropertyRelative("enabled"); + if (enabledProperty != null) + view.EnabledToggle.BindProperty(enabledProperty); + CreateContextMenuManipulator(componentsProperty, index, false).target = view.Foldout.Q(); + CreateContextMenuManipulator(componentsProperty, index, true).target = view.ContextMenuButton; + + } } + listView = new ListView(properties, -1, MakeItem, BindItem) + { + reorderable = true, + selectionType = SelectionType.Single, + showBorder = true, + virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight, + reorderMode = ListViewReorderMode.Animated, + }; + listView.itemIndexChanged += (startIndex, endIndex) => + { + componentsProperty.MoveArrayElement(startIndex, endIndex); + serializedObject.ApplyModifiedProperties(); + listView.Rebuild(); + }; + box.Add(listView); var addButton = new Button() { @@ -145,6 +216,24 @@ VisualElement CreateComponentsPanel() { if (componentsProperty.arraySize != prevArraySize) { + if (prevArraySize < componentsProperty.arraySize) + { + var seen = new HashSet(); + bool dirty = false; + for (int i = 0; i < componentsProperty.arraySize; ++i) + { + var element = componentsProperty.GetArrayElementAtIndex(i); + var value = element.managedReferenceValue; + if (value != null && !seen.Add(value)) + { + var cloned = JsonUtility.FromJson(JsonUtility.ToJson(value), value.GetType()); + element.managedReferenceValue = cloned; + dirty = true; + } + } + if (dirty) + serializedObject.ApplyModifiedProperties(); + } RefleshComponentsView(true); prevArraySize = componentsProperty.arraySize; } @@ -251,51 +340,6 @@ void RefleshComponentsView(bool applyModifiedProperties) componentRoot.Add(CreateComponentsPanel()); } - AnimationComponentView CreateComponentGUI(SerializedProperty property) - { - var view = new AnimationComponentView(); - - if (string.IsNullOrEmpty(property.managedReferenceFullTypename)) - { - view.Text = "(Missing)"; - view.Icon = (Texture2D)EditorGUIUtility.IconContent("Error").image; - view.EnabledToggle.value = true; - view.SetEnabled(true); - view.EnabledToggle.Q("unity-checkmark").style.visibility = Visibility.Hidden; - view.Add(new HelpBox("The type referenced in SerializeReference is missing. You may have renamed the type or moved it to a different namespace or assembly.", HelpBoxMessageType.Error)); - } - else - { - view.Text = property.FindPropertyRelative("displayName").stringValue; - - var targetProperty = property.FindPropertyRelative("target"); - if (targetProperty != null) - { - view.Icon = GUIHelper.GetComponentIcon(targetProperty.GetPropertyType()); - } - - view.TrackPropertyValue(property.FindPropertyRelative("displayName"), x => - { - view.Text = x.stringValue; - }); - - view.Foldout.BindProperty(property); - - var endProperty = property.GetEndProperty(); - var isFirst = true; - while (property.NextVisible(isFirst)) - { - if (SerializedProperty.EqualContents(property, endProperty)) break; - if (property.name == "enabled") continue; - isFirst = false; - - view.Add(new PropertyField(property)); - } - } - - return view; - } - ContextualMenuManipulator CreateContextMenuManipulator(SerializedProperty property, int arrayIndex, bool activeLeftClick) { var manipulator = new ContextualMenuManipulator(evt => @@ -310,6 +354,12 @@ ContextualMenuManipulator CreateContextMenuManipulator(SerializedProperty proper evt.menu.AppendSeparator(); + evt.menu.AppendAction("Duplicate", x => + { + property.InsertArrayElementAtIndex(arrayIndex); + RefleshComponentsView(true); + }); + evt.menu.AppendAction("Remove Component", x => { property.DeleteArrayElementAtIndex(arrayIndex); diff --git a/src/LitMotion/Assets/LitMotion.Animation/Runtime/LitMotionAnimationComponent.cs b/src/LitMotion/Assets/LitMotion.Animation/Runtime/LitMotionAnimationComponent.cs index 482e7e68..ac6b285b 100644 --- a/src/LitMotion/Assets/LitMotion.Animation/Runtime/LitMotionAnimationComponent.cs +++ b/src/LitMotion/Assets/LitMotion.Animation/Runtime/LitMotionAnimationComponent.cs @@ -32,5 +32,8 @@ public virtual void OnPause() { } public virtual void OnStop() { } public MotionHandle TrackedHandle { get; set; } +#if UNITY_EDITOR + [SerializeField, HideInInspector] bool foldoutOn = true; +#endif } } \ No newline at end of file diff --git a/src/LitMotion/ProjectSettings/URPProjectSettings.asset b/src/LitMotion/ProjectSettings/URPProjectSettings.asset index 08faf033..64a8674a 100644 --- a/src/LitMotion/ProjectSettings/URPProjectSettings.asset +++ b/src/LitMotion/ProjectSettings/URPProjectSettings.asset @@ -12,4 +12,4 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 247994e1f5a72c2419c26a37e9334c01, type: 3} m_Name: m_EditorClassIdentifier: - m_LastMaterialVersion: 9 + m_LastMaterialVersion: 10