// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR && UNITY_IMGUI using System.Collections.Generic; using UnityEditor; using UnityEngine; using static Animancer.Editor.AnimancerGUI; namespace Animancer.Editor.Previews { /// [Editor-Only] GUI utilities for . /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerPreviewObjectGUI public static class AnimancerPreviewObjectGUI { /************************************************************************************************************************/ /// Calculates the pixel height required to draw the `preview`. public static float CalculateHeight(AnimancerPreviewObject preview) { var lines = 1; var instanceAnimators = preview.InstanceAnimators; if (instanceAnimators != null && instanceAnimators.Length > 1) lines++; return AnimancerGUI.CalculateHeight(lines); } /************************************************************************************************************************/ /// Draws the model selection GUI. public static void DoModelGUI(AnimancerPreviewObject preview) { var area = LayoutRect(CalculateHeight(preview)); DoModelGUI(area, preview); } /************************************************************************************************************************/ /// Draws the model selection GUI. public static void DoModelGUI(Rect area, AnimancerPreviewObject preview) { var root = preview.OriginalObject; var model = root != null ? root.gameObject : null; EditorGUI.BeginChangeCheck(); var warning = GetModelWarning(model, preview); var color = GUI.color; if (warning != null) GUI.color = WarningFieldColor; using (var label = PooledGUIContent.Acquire("Model")) { var objectFieldArea = StealLineFromTop(ref area); if (DoDropdownObjectFieldGUI(objectFieldArea, label, true, ref model)) { var menu = new GenericMenu(); menu.AddItem( new("Default Humanoid"), model != null && model == TransitionPreviewSettings.GetOrCreateDefaultHumanoid(null), () => preview.OriginalObject = TransitionPreviewSettings.GetOrCreateDefaultHumanoid(preview.InstanceRoot).transform); menu.AddItem( new("Default Sprite"), model != null && model == TransitionPreviewSettings.GetDefaultSpriteIfAlreadyCreated(), () => preview.OriginalObject = TransitionPreviewSettings.GetOrCreateDefaultSprite(preview.InstanceRoot).transform); var persistentModels = TransitionPreviewSettings.Models; var temporaryModels = TemporarySettings.PreviewModels; if (persistentModels.Count == 0 && temporaryModels.Count == 0) { menu.AddDisabledItem(new("No model prefabs have been used yet")); } else { AddModelSelectionFunctions(menu, preview, persistentModels, model); AddModelSelectionFunctions(menu, preview, temporaryModels, model); } menu.ShowAsContext(); } } GUI.color = color; if (EditorGUI.EndChangeCheck()) preview.OriginalObject = model != null ? model.transform : null; if (warning != null) EditorGUILayout.HelpBox(warning, MessageType.Warning, true); DoAnimatorSelectorGUI(preview); } /************************************************************************************************************************/ /// Adds menu functions for selecting each of the `models`. private static void AddModelSelectionFunctions( GenericMenu menu, AnimancerPreviewObject preview, List models, GameObject selected) { for (int i = models.Count - 1; i >= 0; i--) { var model = models[i]; var path = AssetDatabase.GetAssetPath(model); if (!string.IsNullOrEmpty(path)) path = path.Replace('/', '\\'); else path = model.name; menu.AddItem(new(path), model == selected, () => preview.OriginalObject = model.transform); } } /************************************************************************************************************************/ /// Returns a warning about the selected model or null. private static string GetModelWarning( GameObject model, AnimancerPreviewObject preview) { if (model == null) return "No Model is selected so nothing can be previewed."; if (preview.SelectedInstanceAnimator == null) return "The selected Model has no Animator component."; return null; } /************************************************************************************************************************/ /// /// Draws a button for selecting which to control if there's more than one. /// private static void DoAnimatorSelectorGUI(AnimancerPreviewObject preview) { var instanceAnimators = preview.InstanceAnimators; if (instanceAnimators == null || instanceAnimators.Length <= 1) return; var area = LayoutSingleLineRect(SpacingMode.After); var labelArea = StealFromLeft(ref area, EditorGUIUtility.labelWidth, StandardSpacing); GUI.Label(labelArea, nameof(Animator)); var selectedAnimator = preview.SelectedInstanceAnimator; using (var label = PooledGUIContent.Acquire( selectedAnimator != null ? selectedAnimator.name : "None")) { var clicked = EditorGUI.DropdownButton(area, label, FocusType.Passive); if (!clicked) return; var menu = new GenericMenu(); for (int i = 0; i < instanceAnimators.Length; i++) { var animator = instanceAnimators[i]; var index = i; menu.AddItem(new(animator.name), animator == selectedAnimator, () => { preview.SetSelectedAnimator(index); }); } menu.ShowAsContext(); } } /************************************************************************************************************************/ private static DragAndDropHandler _ModelDropHandler; private static AnimancerPreviewObject _ModelDropPreview; /// Handles drag and drop events for preview models. public static void HandleDragAndDrop(Rect area, AnimancerPreviewObject preview) { _ModelDropPreview = preview; _ModelDropHandler ??= (gameObject, isDrop) => { if (!gameObject.TryGetComponent(out _)) return false; if (isDrop) _ModelDropPreview.OriginalObject = gameObject.transform; return true; }; _ModelDropPreview = null; } /************************************************************************************************************************/ } } #endif