From 5ac7223980caaf577b3fa441eaba43b22d2f570b Mon Sep 17 00:00:00 2001 From: Zekins <136648667+Zekins3366@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:50:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA?= =?UTF-8?q?=D0=B8=20TTS=20(#3381)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Corvax/TTS/HumanoidProfileEditor.TTS.cs | 78 ------- Content.Client/Corvax/TTS/TTSTab.xaml | 24 +++ Content.Client/Corvax/TTS/TTSTab.xaml.cs | 194 ++++++++++++++++++ .../Lobby/UI/HumanoidProfileEditor.xaml | 8 - .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 69 +++++-- Resources/Locale/ru-RU/corvax/tts/tts-ui.ftl | 9 +- .../Locale/ru-RU/corvax/tts/tts-voices.ftl | 16 +- 7 files changed, 290 insertions(+), 108 deletions(-) delete mode 100644 Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs create mode 100644 Content.Client/Corvax/TTS/TTSTab.xaml create mode 100644 Content.Client/Corvax/TTS/TTSTab.xaml.cs diff --git a/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs b/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs deleted file mode 100644 index bb61102c41..0000000000 --- a/Content.Client/Corvax/TTS/HumanoidProfileEditor.TTS.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Linq; -using Content.Client.Corvax.TTS; -using Content.Client.Lobby; -using Content.Corvax.Interfaces.Shared; -using Content.Shared.Corvax.TTS; -using Content.Shared.Preferences; - -namespace Content.Client.Lobby.UI; - -public sealed partial class HumanoidProfileEditor -{ - private ISharedSponsorsManager? _sponsorsMgr; - private List _voiceList = new(); - - private void InitializeVoice() - { - _voiceList = _prototypeManager - .EnumeratePrototypes() - .Where(o => o.RoundStart) - .OrderBy(o => Loc.GetString(o.Name)) - .ToList(); - - VoiceButton.OnItemSelected += args => - { - VoiceButton.SelectId(args.Id); - SetVoice(_voiceList[args.Id].ID); - }; - - VoicePlayButton.OnPressed += _ => PlayPreviewTTS(); - - IoCManager.Instance!.TryResolveType(out _sponsorsMgr); - } - - private void UpdateTTSVoicesControls() - { - if (Profile is null) - return; - - VoiceButton.Clear(); - - var firstVoiceChoiceId = 1; - for (var i = 0; i < _voiceList.Count; i++) - { - var voice = _voiceList[i]; - if (!HumanoidCharacterProfile.CanHaveVoice(voice, Profile.Sex)) - continue; - - var name = Loc.GetString(voice.Name); - VoiceButton.AddItem(name, i); - - if (firstVoiceChoiceId == 1) - firstVoiceChoiceId = i; - - if (_sponsorsMgr is null) - continue; - if (voice.SponsorOnly && _sponsorsMgr != null && - !_sponsorsMgr.GetClientPrototypes().Contains(voice.ID)) - { - VoiceButton.SetItemDisabled(VoiceButton.GetIdx(i), true); - } - } - - var voiceChoiceId = _voiceList.FindIndex(x => x.ID == Profile.Voice); - if (!VoiceButton.TrySelectId(voiceChoiceId) && - VoiceButton.TrySelectId(firstVoiceChoiceId)) - { - SetVoice(_voiceList[firstVoiceChoiceId].ID); - } - } - - private void PlayPreviewTTS() - { - if (Profile is null) - return; - - _entManager.System().RequestPreviewTTS(Profile.Voice); - } -} diff --git a/Content.Client/Corvax/TTS/TTSTab.xaml b/Content.Client/Corvax/TTS/TTSTab.xaml new file mode 100644 index 0000000000..2a308ef253 --- /dev/null +++ b/Content.Client/Corvax/TTS/TTSTab.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/Corvax/TTS/TTSTab.xaml.cs b/Content.Client/Corvax/TTS/TTSTab.xaml.cs new file mode 100644 index 0000000000..21b4d81de8 --- /dev/null +++ b/Content.Client/Corvax/TTS/TTSTab.xaml.cs @@ -0,0 +1,194 @@ +using System.Linq; +using Content.Corvax.Interfaces.Shared; +using Content.Shared.Corvax.TTS; +using Content.Shared.Preferences; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; +using Content.Client.Stylesheets; +using Content.Shared.Humanoid; +using System.Text.RegularExpressions; + +namespace Content.Client.Corvax.TTS; + +[GenerateTypedNameReferences] +public sealed partial class TTSTab : Control +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public event Action? OnVoiceSelected; + public event Action? OnPreviewRequested; + + private List _allVoices = new(); + private List _filteredVoices = new(); + private Dictionary> _categorizedVoices = new(); + private string? _selectedVoiceId; + + private static readonly Regex CategoryRegex = new Regex(@"^(.*?)\s*\(([^)]+)\)\s*$", RegexOptions.Compiled); + + public TTSTab() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + LoadVoices(); + SearchEdit.OnTextChanged += OnSearchChanged; + } + + private void LoadVoices() + { + foreach (var voice in _allVoices) + { + var name = Loc.GetString(voice.Name); + var category = Loc.GetString("humanoid-profile-editor-voice-other"); + + var match = CategoryRegex.Match(name); + if (match.Success) + { + category = match.Groups[2].Value.Trim(); + } + + if (!_categorizedVoices.ContainsKey(category)) + _categorizedVoices[category] = new List(); + + _categorizedVoices[category].Add(voice); + } + + CategoriesContainer.RemoveAllChildren(); + + foreach (var category in _categorizedVoices.Keys.OrderBy(k => k)) + { + var button = new Button + { + Text = category, + ToolTip = Loc.GetString("humanoid-profile-editor-voice-category-tooltip", ("category", category)), + HorizontalExpand = true, + }; + + button.OnPressed += _ => + { + SearchEdit.Text = category; + UpdateResults(); + }; + + CategoriesContainer.AddChild(button); + } + + UpdateResults(); + } + + private void OnSearchChanged(LineEdit.LineEditEventArgs args) + { + UpdateResults(); + } + + private void UpdateResults() + { + VoicesGrid.RemoveAllChildren(); + _filteredVoices.Clear(); + + var searchText = SearchEdit.Text.ToLowerInvariant(); + + foreach (var voice in _allVoices) + { + var name = Loc.GetString(voice.Name).ToLowerInvariant(); + + if (string.IsNullOrEmpty(searchText) || + name.Contains(searchText) || + voice.ID.ToLowerInvariant().Contains(searchText)) + { + _filteredVoices.Add(voice); + } + } + + foreach (var voice in _filteredVoices) + { + var displayName = Loc.GetString(voice.Name); + var canSelectVoice = CanUseVoice(voice); + + var voiceContainer = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Horizontal, + HorizontalExpand = true, + VerticalAlignment = VAlignment.Center + }; + + var selectButton = new Button + { + Text = displayName, + ToolTip = canSelectVoice ? voice.ID : Loc.GetString("humanoid-profile-editor-voice-tooltip-sponsoronly"), + HorizontalExpand = true, + Disabled = !canSelectVoice, + StyleClasses = { StyleNano.ButtonOpenRight } + }; + + if (voice.ID == _selectedVoiceId) + { + selectButton.AddStyleClass(StyleBase.ButtonCaution); + } + + selectButton.OnPressed += _ => + { + if (canSelectVoice) + { + OnVoiceSelected?.Invoke(voice.ID); + } + }; + + var previewButton = new Button + { + Text = Loc.GetString("humanoid-profile-editor-voice-play"), + MinWidth = 30, + ToolTip = Loc.GetString("humanoid-profile-editor-voice-tooltip-play"), + StyleClasses = { StyleNano.ButtonOpenLeft } + }; + + previewButton.OnPressed += _ => + { + OnPreviewRequested?.Invoke(voice.ID); + }; + + voiceContainer.AddChild(selectButton); + voiceContainer.AddChild(previewButton); + + VoicesGrid.AddChild(voiceContainer); + } + + ResultsLabel.Text = Loc.GetString("humanoid-profile-editor-voice-match", + ("filtered", _filteredVoices.Count), ("all", _allVoices.Count)); + } + + private bool CanUseVoice(TTSVoicePrototype voice) + { + if (!voice.SponsorOnly) + return true; + + var sponsorsManager = IoCManager.Resolve(); + return sponsorsManager?.GetClientPrototypes().Contains(voice.ID) == true; + } + + public void UpdateControls(HumanoidCharacterProfile? profile, Sex sex) + { + if (profile == null) + return; + + _selectedVoiceId = profile.Voice; + + _allVoices = _prototypeManager + .EnumeratePrototypes() + .Where(o => o.RoundStart && HumanoidCharacterProfile.CanHaveVoice(o, sex)) + .OrderBy(o => Loc.GetString(o.Name)) + .ToList(); + + _categorizedVoices.Clear(); + LoadVoices(); + } + + public void SetSelectedVoice(string voiceId) + { + _selectedVoiceId = voiceId; + UpdateResults(); + } +} diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 09c8357649..ab150e01da 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -91,14 +91,6 @@ - - -