| #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 |
| { |
| |
| } |
|
|
| s_Cache[directory] = result; |
| s_CacheAt[directory] = EditorApplication.timeSinceStartup; |
| return result; |
| } |
| } |
| } |
| #endif |
|
|