diff --git a/Editor/Inspector/DragAndDropManipulator.cs b/Editor/Inspector/DragAndDropManipulator.cs new file mode 100644 index 00000000..0d3de862 --- /dev/null +++ b/Editor/Inspector/DragAndDropManipulator.cs @@ -0,0 +1,106 @@ +using System; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace nadena.dev.modular_avatar.core.editor +{ + internal abstract class DragAndDropManipulator : PointerManipulator where T : Component, IHaveObjReferences + { + private const string DragActiveClassName = "drop-area--drag-active"; + + public T TargetComponent { get; set; } + + protected virtual bool AllowKnownObjects => true; + + private Transform _avatarRoot; + private GameObject[] _draggingObjects = Array.Empty(); + + public DragAndDropManipulator(VisualElement targetElement, T targetComponent) + { + target = targetElement; + TargetComponent = targetComponent; + } + + protected sealed override void RegisterCallbacksOnTarget() + { + target.RegisterCallback(OnDragEnter); + target.RegisterCallback(OnDragLeave); + target.RegisterCallback(OnDragExited); + target.RegisterCallback(OnDragUpdated); + target.RegisterCallback(OnDragPerform); + } + + protected sealed override void UnregisterCallbacksFromTarget() + { + target.UnregisterCallback(OnDragEnter); + target.UnregisterCallback(OnDragLeave); + target.UnregisterCallback(OnDragExited); + target.UnregisterCallback(OnDragUpdated); + target.UnregisterCallback(OnDragPerform); + } + + private void OnDragEnter(DragEnterEvent _) + { + if (TargetComponent == null) return; + + _avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform); + if (_avatarRoot == null) return; + + var knownObjects = TargetComponent.GetObjectReferences().Select(x => x.Get(TargetComponent)).ToHashSet(); + _draggingObjects = DragAndDrop.objectReferences.OfType() + .Where(x => AllowKnownObjects || !knownObjects.Contains(x)) + .Where(x => RuntimeUtil.FindAvatarTransformInParents(x.transform) == _avatarRoot) + .Where(FilterGameObject) + .ToArray(); + if (_draggingObjects.Length == 0) return; + + target.AddToClassList(DragActiveClassName); + } + + private void OnDragLeave(DragLeaveEvent _) + { + _draggingObjects = Array.Empty(); + target.RemoveFromClassList(DragActiveClassName); + } + + private void OnDragExited(DragExitedEvent _) + { + _draggingObjects = Array.Empty(); + target.RemoveFromClassList(DragActiveClassName); + } + + private void OnDragUpdated(DragUpdatedEvent _) + { + if (TargetComponent == null) return; + if (_avatarRoot == null) return; + if (_draggingObjects.Length == 0) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + } + + private void OnDragPerform(DragPerformEvent _) + { + if (TargetComponent == null) return; + if (_avatarRoot == null) return; + if (_draggingObjects.Length == 0) return; + + AddObjectReferences(_draggingObjects + .Select(x => + { + var reference = new AvatarObjectReference(); + reference.Set(x); + return reference; + }) + .ToArray()); + } + + protected virtual bool FilterGameObject(GameObject obj) + { + return true; + } + + protected abstract void AddObjectReferences(AvatarObjectReference[] references); + } +} diff --git a/Editor/Inspector/DragAndDropManipulator.cs.meta b/Editor/Inspector/DragAndDropManipulator.cs.meta new file mode 100644 index 00000000..b78bdf93 --- /dev/null +++ b/Editor/Inspector/DragAndDropManipulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 528c660b56905844ea2f88bc73837e9f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs b/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs index c7410eb8..a2b055b3 100644 --- a/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs +++ b/Editor/Inspector/MaterialSetter/MaterialSetterEditor.cs @@ -16,6 +16,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger [SerializeField] private StyleSheet uss; [SerializeField] private VisualTreeAsset uxml; + private DragAndDropManipulator _dragAndDropManipulator; protected override void OnInnerInspectorGUI() { @@ -37,7 +38,44 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger listView.showBoundCollectionSize = false; listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; + _dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarMaterialSetter); + return root; } + + private void OnEnable() + { + if (_dragAndDropManipulator != null) + _dragAndDropManipulator.TargetComponent = target as ModularAvatarMaterialSetter; + } + + private class DragAndDropManipulator : DragAndDropManipulator + { + public DragAndDropManipulator(VisualElement targetElement, ModularAvatarMaterialSetter targetComponent) + : base(targetElement, targetComponent) { } + + protected override bool FilterGameObject(GameObject obj) + { + if (obj.TryGetComponent(out var renderer)) + { + return renderer.sharedMaterials.Length > 0; + } + return false; + } + + protected override void AddObjectReferences(AvatarObjectReference[] references) + { + Undo.RecordObject(TargetComponent, "Add Material Switch Objects"); + + foreach (var reference in references) + { + var materialSwitchObject = new MaterialSwitchObject { Object = reference, MaterialIndex = 0 }; + TargetComponent.Objects.Add(materialSwitchObject); + } + + EditorUtility.SetDirty(TargetComponent); + PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent); + } + } } } \ No newline at end of file diff --git a/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss b/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss index 84204231..8e422d2d 100644 --- a/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss +++ b/Editor/Inspector/MaterialSetter/MaterialSetterStyles.uss @@ -62,3 +62,13 @@ #f-material { flex-grow: 1; } + +.drop-area--drag-active { + background-color: rgba(0, 127, 255, 0.2); +} + +.drop-area--drag-active .unity-scroll-view, +.drop-area--drag-active .unity-list-view__footer, +.drop-area--drag-active .unity-list-view__reorderable-item { + background-color: rgba(0, 0, 0, 0.0); +} diff --git a/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs b/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs index 627121b7..999f596c 100644 --- a/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs +++ b/Editor/Inspector/ObjectToggle/ObjectSwitcherEditor.cs @@ -35,14 +35,12 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger ROSimulatorButton.BindRefObject(root, target); var listView = root.Q("Shapes"); - _dragAndDropManipulator = new DragAndDropManipulator(listView) - { - TargetComponent = target as ModularAvatarObjectToggle - }; listView.showBoundCollectionSize = false; listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; + _dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarObjectToggle); + return root; } @@ -52,91 +50,25 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger _dragAndDropManipulator.TargetComponent = target as ModularAvatarObjectToggle; } - private class DragAndDropManipulator : PointerManipulator + private class DragAndDropManipulator : DragAndDropManipulator { - public ModularAvatarObjectToggle TargetComponent; - private GameObject[] _nowDragging = Array.Empty(); - private Transform _avatarRoot; + public DragAndDropManipulator(VisualElement targetElement, ModularAvatarObjectToggle targetComponent) + : base(targetElement, targetComponent) { } - private readonly VisualElement _parentElem; + protected override bool AllowKnownObjects => false; - public DragAndDropManipulator(VisualElement target) + protected override void AddObjectReferences(AvatarObjectReference[] references) { - this.target = target; - _parentElem = target.parent; - } + Undo.RecordObject(TargetComponent, "Add Toggled Objects"); - protected override void RegisterCallbacksOnTarget() - { - target.RegisterCallback(OnDragEnter); - target.RegisterCallback(OnDragLeave); - target.RegisterCallback(OnDragPerform); - target.RegisterCallback(OnDragUpdate); - } - - protected override void UnregisterCallbacksFromTarget() - { - target.UnregisterCallback(OnDragEnter); - target.UnregisterCallback(OnDragLeave); - target.UnregisterCallback(OnDragPerform); - target.RegisterCallback(OnDragUpdate); - } - - - private void OnDragEnter(DragEnterEvent evt) - { - if (TargetComponent == null) return; - - _avatarRoot = RuntimeUtil.FindAvatarTransformInParents(TargetComponent.transform); - if (_avatarRoot == null) return; - - _nowDragging = DragAndDrop.objectReferences.OfType() - .Where(o => RuntimeUtil.FindAvatarTransformInParents(o.transform) == _avatarRoot) - .ToArray(); - - if (_nowDragging.Length > 0) + foreach (var reference in references) { - DragAndDrop.visualMode = DragAndDropVisualMode.Link; - - _parentElem.AddToClassList("drop-area--drag-active"); - } - } - - private void OnDragUpdate(DragUpdatedEvent _) - { - if (_nowDragging.Length > 0) DragAndDrop.visualMode = DragAndDropVisualMode.Link; - } - - private void OnDragLeave(DragLeaveEvent evt) - { - _nowDragging = Array.Empty(); - _parentElem.RemoveFromClassList("drop-area--drag-active"); - } - - private void OnDragPerform(DragPerformEvent evt) - { - if (_nowDragging.Length > 0 && TargetComponent != null && _avatarRoot != null) - { - var knownObjs = TargetComponent.Objects.Select(o => o.Object.Get(TargetComponent)).ToHashSet(); - - Undo.RecordObject(TargetComponent, "Add Toggled Objects"); - foreach (var obj in _nowDragging) - { - if (knownObjs.Contains(obj)) continue; - - var aor = new AvatarObjectReference(); - aor.Set(obj); - - var toggledObject = new ToggledObject { Object = aor, Active = !obj.activeSelf }; - TargetComponent.Objects.Add(toggledObject); - } - - EditorUtility.SetDirty(TargetComponent); - PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent); + var toggledObject = new ToggledObject { Object = reference, Active = !reference.Get(TargetComponent).activeSelf }; + TargetComponent.Objects.Add(toggledObject); } - _nowDragging = Array.Empty(); - _parentElem.RemoveFromClassList("drop-area--drag-active"); + EditorUtility.SetDirty(TargetComponent); + PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent); } } } diff --git a/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss b/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss index d4bdedc5..12402b5d 100644 --- a/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss +++ b/Editor/Inspector/ObjectToggle/ObjectSwitcherStyles.uss @@ -51,6 +51,12 @@ width: 60px; } -.drop-area--drag-active > ListView ScrollView { - background-color: rgba(0, 255, 255, 0.1); +.drop-area--drag-active { + background-color: rgba(0, 127, 255, 0.2); +} + +.drop-area--drag-active .unity-scroll-view, +.drop-area--drag-active .unity-list-view__footer, +.drop-area--drag-active .unity-list-view__reorderable-item { + background-color: rgba(0, 0, 0, 0.0); } diff --git a/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs b/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs index 9a278278..9bec32a3 100644 --- a/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs +++ b/Editor/Inspector/ShapeChanger/ShapeChangerEditor.cs @@ -19,6 +19,7 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger [SerializeField] private StyleSheet uss; [SerializeField] private VisualTreeAsset uxml; + private DragAndDropManipulator _dragAndDropManipulator; private BlendshapeSelectWindow _window; protected override void OnInnerInspectorGUI() @@ -41,6 +42,8 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger listView.showBoundCollectionSize = false; listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; + _dragAndDropManipulator = new DragAndDropManipulator(root.Q("group-box"), target as ModularAvatarShapeChanger); + // The Add button callback isn't exposed publicly for some reason... var field_addButton = typeof(BaseListView).GetField("m_AddButton", NonPublic | Instance); var addButton = (Button)field_addButton.GetValue(listView); @@ -50,6 +53,41 @@ namespace nadena.dev.modular_avatar.core.editor.ShapeChanger return root; } + private void OnEnable() + { + if (_dragAndDropManipulator != null) + _dragAndDropManipulator.TargetComponent = target as ModularAvatarShapeChanger; + } + + private class DragAndDropManipulator : DragAndDropManipulator + { + public DragAndDropManipulator(VisualElement targetElement, ModularAvatarShapeChanger targetComponent) + : base(targetElement, targetComponent) { } + + protected override bool FilterGameObject(GameObject obj) + { + if (obj.TryGetComponent(out var smr)) + { + return smr.sharedMesh != null && smr.sharedMesh.blendShapeCount > 0; + } + return false; + } + + protected override void AddObjectReferences(AvatarObjectReference[] references) + { + Undo.RecordObject(TargetComponent, "Add Changed Shapes"); + + foreach (var reference in references) + { + var changedShape = new ChangedShape { Object = reference, ShapeName = string.Empty }; + TargetComponent.Shapes.Add(changedShape); + } + + EditorUtility.SetDirty(TargetComponent); + PrefabUtility.RecordPrefabInstancePropertyModifications(TargetComponent); + } + } + private void OnDisable() { if (_window != null) DestroyImmediate(_window); diff --git a/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss b/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss index adff445e..e74734ff 100644 --- a/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss +++ b/Editor/Inspector/ShapeChanger/ShapeChangerStyles.uss @@ -68,3 +68,13 @@ .change-type-delete #f-value-delete { display: flex; } + +.drop-area--drag-active { + background-color: rgba(0, 127, 255, 0.2); +} + +.drop-area--drag-active .unity-scroll-view, +.drop-area--drag-active .unity-list-view__footer, +.drop-area--drag-active .unity-list-view__reorderable-item { + background-color: rgba(0, 0, 0, 0.0); +}