Files
AppleHillsProduction/Assets/Editor/Dialogue/DialogueContentDrawer.cs

218 lines
9.8 KiB
C#
Raw Normal View History

using UnityEditor;
using UnityEngine;
namespace Dialogue.Editor
{
/// <summary>
/// Custom property drawer for DialogueContent that displays either text or image fields based on content type
/// </summary>
[CustomPropertyDrawer(typeof(DialogueContent))]
public class DialogueContentDrawer : PropertyDrawer
{
// Height constants
private const float TypeSelectorHeight = 20f;
2025-10-17 15:20:29 +02:00
private const float PropertySpacing = 2f; // Reduced spacing for tighter layout
private const float TextFieldHeight = 40f; // Taller for multi-line text
private const float ImageFieldHeight = 18f;
2025-10-17 15:20:29 +02:00
private const float AudioFieldHeight = 18f;
private const float PreviewHeight = 64f;
2025-10-17 15:20:29 +02:00
// Track the last assigned sprite to detect changes
private static Sprite lastAssignedSprite;
private static string lastAssignedPropertyPath;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var contentTypeProperty = property.FindPropertyRelative("_contentType");
2025-10-17 15:20:29 +02:00
var imageProperty = property.FindPropertyRelative("_image");
// Start with base height for type selector
var height = TypeSelectorHeight + PropertySpacing;
// Add height based on content type
if (contentTypeProperty.enumValueIndex == (int)DialogueContentType.Text)
{
2025-10-17 15:20:29 +02:00
height += TextFieldHeight + PropertySpacing;
}
else // Image
{
2025-10-17 15:20:29 +02:00
height += ImageFieldHeight + PropertySpacing;
2025-10-17 15:20:29 +02:00
// Add preview height if an image is assigned - make sure it's AFTER the image field
// but BEFORE the audio field
if (imageProperty.objectReferenceValue != null)
{
2025-10-17 15:20:29 +02:00
height += PreviewHeight + PropertySpacing;
}
}
2025-10-17 15:20:29 +02:00
// Add height for audio field (always displayed)
height += AudioFieldHeight;
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
// Create a property field and indent it
var contentRect = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Get properties
var contentTypeProperty = property.FindPropertyRelative("_contentType");
var textProperty = property.FindPropertyRelative("_text");
var imageProperty = property.FindPropertyRelative("_image");
2025-10-17 15:20:29 +02:00
var audioProperty = property.FindPropertyRelative("_audio");
// Check for sprite changes and force repaint if needed
var currentSprite = imageProperty.objectReferenceValue as Sprite;
if (currentSprite != lastAssignedSprite && property.propertyPath == lastAssignedPropertyPath)
{
// Sprite changed, force a layout recalculation
EditorUtility.SetDirty(property.serializedObject.targetObject);
GUI.changed = true;
}
// Track current y position as we add controls
float currentY = contentRect.y;
2025-10-17 15:20:29 +02:00
// Calculate type rect
var typeRect = new Rect(contentRect.x, currentY, contentRect.width, TypeSelectorHeight);
currentY += TypeSelectorHeight + PropertySpacing;
// Calculate content field rect based on the content type
var contentHeight = contentTypeProperty.enumValueIndex == (int)DialogueContentType.Text ?
TextFieldHeight : ImageFieldHeight;
var contentFieldRect = new Rect(
contentRect.x,
2025-10-17 15:20:29 +02:00
currentY,
contentRect.width,
2025-10-17 15:20:29 +02:00
contentHeight);
currentY += contentHeight + PropertySpacing;
// If we have an image and it's selected, calculate preview rect
Rect previewRect = Rect.zero;
if (contentTypeProperty.enumValueIndex == (int)DialogueContentType.Image &&
imageProperty.objectReferenceValue != null)
{
previewRect = new Rect(
contentRect.x,
currentY,
contentRect.width,
PreviewHeight);
currentY += PreviewHeight + PropertySpacing;
}
2025-10-17 15:20:29 +02:00
// Calculate audio field rect
var audioFieldRect = new Rect(
contentRect.x,
currentY,
contentRect.width,
AudioFieldHeight);
// Now draw all the controls
// Draw the content type dropdown
EditorGUI.PropertyField(typeRect, contentTypeProperty, GUIContent.none);
// Draw the appropriate field based on content type
if (contentTypeProperty.enumValueIndex == (int)DialogueContentType.Text)
{
// Create a custom style with word wrap enabled
GUIStyle wordWrapStyle = new GUIStyle(EditorStyles.textArea);
wordWrapStyle.wordWrap = true;
// Text field with word wrap for multi-line input
textProperty.stringValue = EditorGUI.TextArea(contentFieldRect, textProperty.stringValue, wordWrapStyle);
}
else // Image
{
2025-10-17 15:20:29 +02:00
// Store the sprite before drawing the field to detect changes
var previousSprite = imageProperty.objectReferenceValue as Sprite;
// Draw the image field
2025-10-17 15:20:29 +02:00
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(contentFieldRect, imageProperty, GUIContent.none);
2025-10-17 15:20:29 +02:00
if (EditorGUI.EndChangeCheck())
{
// Image changed, store the property path so we can detect which property changed
lastAssignedPropertyPath = property.propertyPath;
lastAssignedSprite = imageProperty.objectReferenceValue as Sprite;
// Force an inspector update to recalculate layout
EditorUtility.SetDirty(property.serializedObject.targetObject);
// Mark the scene as dirty to ensure serialization and proper layout refresh
if (!EditorApplication.isPlaying)
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
}
// Draw a preview if an image is assigned
if (imageProperty.objectReferenceValue != null)
{
var sprite = imageProperty.objectReferenceValue as Sprite;
if (sprite != null)
{
// Draw the preview with preserved aspect ratio
DrawSpritePreview(previewRect, sprite);
}
}
}
2025-10-17 15:20:29 +02:00
// Draw the audio field (always displayed regardless of content type)
EditorGUI.PropertyField(audioFieldRect, audioProperty, new GUIContent("Audio"));
// Restore indent
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
2025-10-17 15:20:29 +02:00
// Helper method to draw a sprite preview with preserved aspect ratio
private void DrawSpritePreview(Rect rect, Sprite sprite)
{
2025-10-17 15:20:29 +02:00
if (sprite == null || sprite.texture == null)
return;
var texture = sprite.texture;
var spriteRect = sprite.rect;
var aspectRatio = spriteRect.width / spriteRect.height;
// Calculate preview rect while preserving aspect ratio within our fixed area
Rect previewRect = rect;
// Limit the display size to the allocated space while maintaining aspect ratio
if (aspectRatio > 1f) // Wider than tall
{
previewRect.height = Mathf.Min(rect.width / aspectRatio, rect.height);
previewRect.y += (rect.height - previewRect.height) * 0.5f;
}
else // Taller than wide or square
{
previewRect.width = Mathf.Min(rect.height * aspectRatio, rect.width);
previewRect.x += (rect.width - previewRect.width) * 0.5f;
}
// Constrain the preview to the allocated space
previewRect.height = Mathf.Min(previewRect.height, rect.height);
previewRect.width = Mathf.Min(previewRect.width, rect.width);
// Draw preview with a dark background for better visibility
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 1f));
GUI.DrawTexture(previewRect, texture, ScaleMode.ScaleToFit);
// Draw sprite bounds
EditorGUI.DrawRect(new Rect(previewRect.x, previewRect.y, previewRect.width, 1), Color.gray);
EditorGUI.DrawRect(new Rect(previewRect.x, previewRect.y + previewRect.height - 1, previewRect.width, 1), Color.gray);
EditorGUI.DrawRect(new Rect(previewRect.x, previewRect.y, 1, previewRect.height), Color.gray);
EditorGUI.DrawRect(new Rect(previewRect.x + previewRect.width - 1, previewRect.y, 1, previewRect.height), Color.gray);
}
}
}