mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 18:31:17 +03:00
Переработка вкладки TTS (#3381)
This commit is contained in:
parent
7fd7fb4f0d
commit
5ac7223980
7 changed files with 290 additions and 108 deletions
|
|
@ -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<TTSVoicePrototype> _voiceList = new();
|
||||
|
||||
private void InitializeVoice()
|
||||
{
|
||||
_voiceList = _prototypeManager
|
||||
.EnumeratePrototypes<TTSVoicePrototype>()
|
||||
.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<TTSSystem>().RequestPreviewTTS(Profile.Voice);
|
||||
}
|
||||
}
|
||||
24
Content.Client/Corvax/TTS/TTSTab.xaml
Normal file
24
Content.Client/Corvax/TTS/TTSTab.xaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<Control xmlns="https://spacestation14.io" xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="5">
|
||||
<LineEdit Name="SearchEdit" MinWidth="600" PlaceHolder="{Loc 'humanoid-profile-editor-voice-placeholder'}"/>
|
||||
<Label Name="ResultsLabel" MinWidth="100" HorizontalExpand="True" HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
|
||||
<PanelContainer HorizontalExpand="True" VerticalExpand="False" MinSize="0 200" Margin="0 5 0 20">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#1A1A1A" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="5">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-voice-categories'}" StyleClasses="LabelHeading" HorizontalAlignment="Center" Margin="0 0 0 5"/>
|
||||
<ScrollContainer VerticalExpand="True" HorizontalExpand="True">
|
||||
<GridContainer Name="CategoriesContainer" Columns="4" HorizontalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
<ScrollContainer VerticalExpand="True" Margin="5">
|
||||
<GridContainer Name="VoicesGrid" Columns="3" HorizontalExpand="True"/>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
194
Content.Client/Corvax/TTS/TTSTab.xaml.cs
Normal file
194
Content.Client/Corvax/TTS/TTSTab.xaml.cs
Normal file
|
|
@ -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<string>? OnVoiceSelected;
|
||||
public event Action<string>? OnPreviewRequested;
|
||||
|
||||
private List<TTSVoicePrototype> _allVoices = new();
|
||||
private List<TTSVoicePrototype> _filteredVoices = new();
|
||||
private Dictionary<string, List<TTSVoicePrototype>> _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<TTSVoicePrototype>();
|
||||
|
||||
_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<ISharedSponsorsManager>();
|
||||
return sponsorsManager?.GetClientPrototypes().Contains(voice.ID) == true;
|
||||
}
|
||||
|
||||
public void UpdateControls(HumanoidCharacterProfile? profile, Sex sex)
|
||||
{
|
||||
if (profile == null)
|
||||
return;
|
||||
|
||||
_selectedVoiceId = profile.Voice;
|
||||
|
||||
_allVoices = _prototypeManager
|
||||
.EnumeratePrototypes<TTSVoicePrototype>()
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
|
@ -91,14 +91,6 @@
|
|||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="SpawnPriorityButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Corvax-TTS-Start -->
|
||||
<BoxContainer HorizontalExpand="True" Visible="False" Name="TTSContainer">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-voice-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="VoiceButton" HorizontalAlignment="Right" />
|
||||
<Button Name="VoicePlayButton" Text="{Loc 'humanoid-profile-editor-voice-play'}" MaxWidth="80" />
|
||||
</BoxContainer>
|
||||
<!-- Corvax-TTS-End -->
|
||||
</BoxContainer>
|
||||
<!-- Skin -->
|
||||
<BoxContainer Margin="10" HorizontalExpand="True" Orientation="Vertical">
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ using Robust.Shared.Prototypes;
|
|||
using Robust.Shared.Utility;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using static Content.Client.Corvax.SponsorOnlyHelpers; // Corvax-Sponsors
|
||||
using Content.Client.Corvax.TTS; // Corvax-TTS
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
|
|
@ -65,6 +66,8 @@ namespace Content.Client.Lobby.UI
|
|||
// One at a time.
|
||||
private LoadoutWindow? _loadoutWindow;
|
||||
|
||||
private TTSTab? _ttsTab; // Corvax-TTS
|
||||
|
||||
private bool _exporting;
|
||||
private bool _imaging;
|
||||
|
||||
|
|
@ -223,18 +226,6 @@ namespace Content.Client.Lobby.UI
|
|||
|
||||
#endregion Gender
|
||||
|
||||
// Corvax-TTS-Start
|
||||
#region Voice
|
||||
|
||||
if (configurationManager.GetCVar(CCCVars.TTSEnabled))
|
||||
{
|
||||
TTSContainer.Visible = true;
|
||||
InitializeVoice();
|
||||
}
|
||||
|
||||
#endregion
|
||||
// Corvax-TTS-End
|
||||
|
||||
RefreshSpecies();
|
||||
|
||||
SpeciesButton.OnItemSelected += args =>
|
||||
|
|
@ -441,6 +432,8 @@ namespace Content.Client.Lobby.UI
|
|||
|
||||
RefreshFlavorText();
|
||||
|
||||
RefreshVoiceTab(); // Corvax-TTS
|
||||
|
||||
#region Dummy
|
||||
|
||||
SpriteRotateLeft.OnPressed += _ =>
|
||||
|
|
@ -500,6 +493,56 @@ namespace Content.Client.Lobby.UI
|
|||
}
|
||||
}
|
||||
|
||||
// Corvax-TTS-Start
|
||||
#region Voice
|
||||
|
||||
private void RefreshVoiceTab()
|
||||
{
|
||||
if (!_cfgManager.GetCVar(CCCVars.TTSEnabled))
|
||||
return;
|
||||
|
||||
_ttsTab = new TTSTab();
|
||||
var children = new List<Control>();
|
||||
foreach (var child in TabContainer.Children)
|
||||
children.Add(child);
|
||||
|
||||
TabContainer.RemoveAllChildren();
|
||||
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
if (i == 1) // Set the tab to the 2nd place.
|
||||
{
|
||||
TabContainer.AddChild(_ttsTab);
|
||||
}
|
||||
TabContainer.AddChild(children[i]);
|
||||
}
|
||||
|
||||
TabContainer.SetTabTitle(1, Loc.GetString("humanoid-profile-editor-voice-tab"));
|
||||
|
||||
_ttsTab.OnVoiceSelected += voiceId =>
|
||||
{
|
||||
SetVoice(voiceId);
|
||||
_ttsTab.SetSelectedVoice(voiceId);
|
||||
};
|
||||
|
||||
_ttsTab.OnPreviewRequested += voiceId =>
|
||||
{
|
||||
_entManager.System<TTSSystem>().RequestPreviewTTS(voiceId);
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateTTSVoicesControls()
|
||||
{
|
||||
if (Profile is null || _ttsTab is null)
|
||||
return;
|
||||
|
||||
_ttsTab.UpdateControls(Profile, Profile.Sex);
|
||||
_ttsTab.SetSelectedVoice(Profile.Voice);
|
||||
}
|
||||
|
||||
#endregion
|
||||
// Corvax-TTS-End
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes traits selector
|
||||
/// </summary>
|
||||
|
|
@ -591,7 +634,7 @@ namespace Content.Client.Lobby.UI
|
|||
{
|
||||
TraitsList.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
|
||||
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount), ("max", category.MaxTraitPoints)),
|
||||
FontColorOverride = Color.Gray
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
ui-options-tts-volume = Громкость TTS:
|
||||
credits-window-tts-title = Функция TTS (Text-To-Speech)
|
||||
humanoid-profile-editor-voice-label = Голос:
|
||||
humanoid-profile-editor-voice-tab = Голос
|
||||
humanoid-profile-editor-voice-placeholder = Поиск по голосам...
|
||||
humanoid-profile-editor-voice-categories = Категории
|
||||
humanoid-profile-editor-voice-other = Прочие
|
||||
humanoid-profile-editor-voice-play = ▶
|
||||
humanoid-profile-editor-voice-tooltip-play = Прослушать
|
||||
humanoid-profile-editor-voice-tooltip-sponsoronly = Доступно только спонсорам
|
||||
humanoid-profile-editor-voice-category-tooltip = Показать голоса из {$category}
|
||||
humanoid-profile-editor-voice-match = Найдено: {$filtered}/{$all}
|
||||
tts-rate-limited = Вы генерируете TTS слишком быстро!
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ tts-voice-name-barbas = Барбас (Skyrim)
|
|||
tts-voice-name-hermaeus = Хермеус (Skyrim)
|
||||
tts-voice-name-hakon = Хакон (Skyrim)
|
||||
tts-voice-name-rita = Рита (Рита)
|
||||
tts-voice-name-barman = Бармен (н\д)
|
||||
tts-voice-name-barman = Бармен (Прочие)
|
||||
tts-voice-name-bridger2 = Мостовой 2 (Metro)
|
||||
tts-voice-name-bridger3 = Мостовой 3 (Metro)
|
||||
tts-voice-name-cannibal3 = Людоед 3 (Metro)
|
||||
|
|
@ -562,7 +562,7 @@ tts-voice-name-vesemir = Весемир (Ведьмак)
|
|||
tts-voice-name-kate = Катя (Зайчик)
|
||||
tts-voice-name-polina = Полина (Зайчик)
|
||||
tts-voice-name-crach = Крах (Ведьмак)
|
||||
tts-voice-name-gryphon = Грифон (WarCraft 3)
|
||||
tts-voice-name-gryphon = Грифон (Warcraft 3)
|
||||
tts-voice-name-zeus = Zeus (Dota 2)
|
||||
tts-voice-name-iz = Из (Проклятые земли)
|
||||
tts-voice-name-geralt = Геральт (Ведьмак)
|
||||
|
|
@ -721,7 +721,7 @@ tts-voice-name-stalker_metro_saharov = Сахаров (Metro)
|
|||
tts-voice-name-stalker_metro_soldier = Солдат (Metro)
|
||||
tts-voice-name-stalker_metro_stalker01 = Сталкер01 (Metro)
|
||||
tts-voice-name-stalker_metro_newbie01 = Новичок01 (Metro)
|
||||
tts-voice-name-warcraft_garrosh = Гаррош (WarCraft 3)
|
||||
tts-voice-name-warcraft_garrosh = Гаррош (Warcraft 3)
|
||||
tts-voice-name-male_v = Мужской V
|
||||
tts-voice-name-johny = Джонни
|
||||
tts-voice-name-takemura = Такемура
|
||||
|
|
@ -742,10 +742,10 @@ tts-voice-name-joel = Джоэл
|
|||
tts-voice-name-tess = Тесс
|
||||
tts-voice-name-mission_control = Миссия Контроль
|
||||
tts-voice-name-dwarves = Гномы
|
||||
tts-voice-name-sunboy_inner = Санбой (внутренний)
|
||||
tts-voice-name-sunboy_sad = Санбой (грустный)
|
||||
tts-voice-name-sunboy_mannered = Санбой (вежливый)
|
||||
tts-voice-name-sunboy_kalm = Санбой (спокойный)
|
||||
tts-voice-name-sunboy_inner = Санбой (внутренний) (Мемы)
|
||||
tts-voice-name-sunboy_sad = Санбой (грустный) (Мемы)
|
||||
tts-voice-name-sunboy_mannered = Санбой (вежливый) (Мемы)
|
||||
tts-voice-name-sunboy_kalm = Санбой (спокойный) (Мемы)
|
||||
tts-voice-name-last_years_snow_narrator = Рассказчик прошлогоднего снега
|
||||
tts-voice-name-last_years_snow_man = Мужчина прошлогоднего снега
|
||||
tts-voice-name-titus = Титус
|
||||
|
|
@ -811,7 +811,7 @@ tts-voice-name-azmodan = Азмодан (Diablo)
|
|||
tts-voice-name-wizard = Волшебница (Прочие)
|
||||
tts-voice-name-guldan = Гул'дан (Warcraft)
|
||||
tts-voice-name-malganis = Мал'Ганис (Warcraft)
|
||||
tts-voice-name-zagara = Zagara (Starcraft)
|
||||
tts-voice-name-zagara = Zagara (StarCraft)
|
||||
tts-voice-name-cho = Чо (Warcraft)
|
||||
tts-voice-name-meiow = Мяу (Прочие)
|
||||
tts-voice-name-stitches = Мясник (Warcraft)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue