// 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