com.sky.ondeviceagent / Runtime /AgentCore /Editor /VoiceStyleDropdownDrawer.cs
Sky-Kim's picture
Initial commit
2e7837a
Raw
History Blame Contribute Delete
5.11 kB
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using OnDeviceAgent.Inference;
namespace OnDeviceAgent.AgentCore.Editor
{
[CustomPropertyDrawer(typeof(VoiceStyleDropdownAttribute))]
public sealed class VoiceStyleDropdownDrawer : PropertyDrawer
{
static readonly string[] DefaultStyles = { "F1", "F2", "F3", "F4", "F5", "M1", "M2", "M3", "M4", "M5" };
static readonly Dictionary<string, string[]> s_Cache = new Dictionary<string, string[]>();
static readonly Dictionary<string, double> s_CacheAt = new Dictionary<string, double>();
const double CacheSeconds = 5.0;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType != SerializedPropertyType.String)
{
EditorGUI.PropertyField(position, property, label);
return;
}
var attr = (VoiceStyleDropdownAttribute)attribute;
var dir = ResolveDirectory(attr, property);
var styles = GetStyles(dir);
var current = property.stringValue ?? string.Empty;
var options = new List<string>(styles.Length + 1);
if (!string.IsNullOrEmpty(current) && Array.IndexOf(styles, current) < 0)
options.Add(current + " (custom)");
options.AddRange(styles);
if (options.Count == 0)
options.Add(string.IsNullOrEmpty(current) ? "(no styles)" : current);
var selectedIndex = 0;
for (var i = 0; i < options.Count; i++)
{
var name = options[i];
var marker = name.IndexOf(" (custom)", StringComparison.Ordinal);
var bare = marker >= 0 ? name.Substring(0, marker) : name;
if (bare == current) { selectedIndex = i; break; }
}
const float refreshWidth = 22f;
var popupRect = new Rect(position.x, position.y, position.width - refreshWidth - 2f, position.height);
var refreshRect = new Rect(position.xMax - refreshWidth, position.y, refreshWidth, position.height);
EditorGUI.BeginChangeCheck();
var newIndex = EditorGUI.Popup(popupRect, label, selectedIndex, ToGuiContents(options));
if (EditorGUI.EndChangeCheck())
{
var chosen = options[newIndex];
var marker = chosen.IndexOf(" (custom)", StringComparison.Ordinal);
property.stringValue = marker >= 0 ? chosen.Substring(0, marker) : chosen;
}
if (GUI.Button(refreshRect, new GUIContent("↻", "Rescan " + dir)))
{
s_CacheAt[dir] = 0;
GetStyles(dir);
EditorWindow.focusedWindow?.Repaint();
}
}
static GUIContent[] ToGuiContents(List<string> items)
{
var arr = new GUIContent[items.Count];
for (var i = 0; i < items.Count; i++) arr[i] = new GUIContent(items[i]);
return arr;
}
static string ResolveDirectory(VoiceStyleDropdownAttribute attr, SerializedProperty property)
{
if (!string.IsNullOrWhiteSpace(attr.DirectoryField))
{
var sibling = property.serializedObject.FindProperty(attr.DirectoryField);
if (sibling != null
&& sibling.propertyType == SerializedPropertyType.String
&& !string.IsNullOrWhiteSpace(sibling.stringValue))
return sibling.stringValue.Trim();
}
return string.IsNullOrWhiteSpace(attr.Directory) ? "Model/Supertonic/VoiceStyles" : attr.Directory.Trim();
}
static string[] GetStyles(string directory)
{
if (s_Cache.TryGetValue(directory, out var cached)
&& s_CacheAt.TryGetValue(directory, out var at)
&& EditorApplication.timeSinceStartup - at < CacheSeconds)
return cached;
var result = DefaultStyles;
try
{
var abs = ModelPathResolver.GetModelFilePath(directory);
if (Directory.Exists(abs))
{
var files = Directory.GetFiles(abs, "*.json");
if (files.Length > 0)
{
var names = new List<string>(files.Length);
for (var i = 0; i < files.Length; i++)
names.Add(Path.GetFileNameWithoutExtension(files[i]));
names.Sort(StringComparer.Ordinal);
result = names.ToArray();
}
}
}
catch
{
// Path resolution failed, fall through to defaults.
}
s_Cache[directory] = result;
s_CacheAt[directory] = EditorApplication.timeSinceStartup;
return result;
}
}
}
#endif