2025-10-08 09:34:58 +00:00
|
|
|
|
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
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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;
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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;
|
|
|
|
|
|
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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;
|
2025-10-08 09:34:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
else // Image
|
|
|
|
|
|
{
|
2025-10-17 15:20:29 +02:00
|
|
|
|
height += ImageFieldHeight + PropertySpacing;
|
2025-10-08 09:34:58 +00:00
|
|
|
|
|
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
|
2025-10-08 09:34:58 +00:00
|
|
|
|
if (imageProperty.objectReferenceValue != null)
|
|
|
|
|
|
{
|
2025-10-17 15:20:29 +02:00
|
|
|
|
height += PreviewHeight + PropertySpacing;
|
2025-10-08 09:34:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-17 15:20:29 +02:00
|
|
|
|
// Add height for audio field (always displayed)
|
|
|
|
|
|
height += AudioFieldHeight;
|
|
|
|
|
|
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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-08 09:34:58 +00:00
|
|
|
|
|
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;
|
|
|
|
|
|
|
2025-10-08 09:34:58 +00:00
|
|
|
|
var contentFieldRect = new Rect(
|
|
|
|
|
|
contentRect.x,
|
2025-10-17 15:20:29 +02:00
|
|
|
|
currentY,
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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-08 09:34:58 +00:00
|
|
|
|
|
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
|
2025-10-08 09:34:58 +00:00
|
|
|
|
// 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;
|
|
|
|
|
|
|
2025-10-08 09:34:58 +00:00
|
|
|
|
// Draw the image field
|
2025-10-17 15:20:29 +02:00
|
|
|
|
EditorGUI.BeginChangeCheck();
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-08 09:34:58 +00:00
|
|
|
|
|
|
|
|
|
|
// 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
|
2025-10-08 09:34:58 +00:00
|
|
|
|
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-08 09:34:58 +00:00
|
|
|
|
{
|
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);
|
2025-10-08 09:34:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|