From cd7e2503f2b0abbb88c32dd4a967d231492cbeee Mon Sep 17 00:00:00 2001 From: fox Date: Fri, 23 Feb 2024 22:07:06 +0300 Subject: [PATCH] Added a language selector to the chat ui (kmp) --- .../Systems/Chat/Controls/ChatInputBox.cs | 25 +++- .../_NF/Language/LanguageMenuWindow.xaml.cs | 7 +- .../Chat/Controls/LanguageSelectorButton.cs | 95 +++++++++++++++ .../Controls/LanguageSelectorItemButton.cs | 28 +++++ .../Chat/Controls/LanguageSelectorPopup.cs | 109 ++++++++++++++++++ .../Language/LanguageMenuUIController.cs | 22 +++- 6 files changed, 280 insertions(+), 6 deletions(-) create mode 100644 Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorButton.cs create mode 100644 Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorItemButton.cs create mode 100644 Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorPopup.cs diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs index 843fd46c1a0..e5f35ee5d82 100644 --- a/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs +++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChatInputBox.cs @@ -1,5 +1,9 @@ -using Content.Shared.Chat; +using Content.Client._NF.Language.Systems.Chat.Controls; +using Content.Client.UserInterface.Systems.Language; +using Content.Shared.Chat; using Content.Shared.Input; +using Content.Shared.Language; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; namespace Content.Client.UserInterface.Systems.Chat.Controls; @@ -8,6 +12,7 @@ namespace Content.Client.UserInterface.Systems.Chat.Controls; public class ChatInputBox : PanelContainer { public readonly ChannelSelectorButton ChannelSelector; + public readonly LanguageSelectorButton LanguageSelector; // Frontier public readonly HistoryLineEdit Input; public readonly ChannelFilterButton FilterButton; protected readonly BoxContainer Container; @@ -30,6 +35,17 @@ public ChatInputBox() MinWidth = 75 }; Container.AddChild(ChannelSelector); + // frontier block - begin + LanguageSelector = new LanguageSelectorButton + { + Name = "LanguageSelector", + ToggleMode = true, + StyleClasses = { "chatSelectorOptionButton" }, + MinWidth = 75 + }; + Container.AddChild(LanguageSelector); + LanguageSelector.OnLanguageSelect += SelectLanguage; + // frontier block - end Input = new HistoryLineEdit { Name = "Input", @@ -52,6 +68,13 @@ private void UpdateActiveChannel(ChatSelectChannel selectedChannel) ActiveChannel = (ChatChannel) selectedChannel; } + // Frontier + private void SelectLanguage(LanguagePrototype language) + { + // This sucks a lot + IoCManager.Resolve().GetUIController().SetLanguage(language.ID); + } + private static string GetChatboxInfoPlaceholder() { return (BoundKeyHelper.IsBound(ContentKeyFunctions.FocusChat), BoundKeyHelper.IsBound(ContentKeyFunctions.CycleChatChannelForward)) switch diff --git a/Content.Client/_NF/Language/LanguageMenuWindow.xaml.cs b/Content.Client/_NF/Language/LanguageMenuWindow.xaml.cs index 88edd8147a3..37704570edb 100644 --- a/Content.Client/_NF/Language/LanguageMenuWindow.xaml.cs +++ b/Content.Client/_NF/Language/LanguageMenuWindow.xaml.cs @@ -14,15 +14,14 @@ namespace Content.Client._NF.Language; // This EXACT class must have the _NF par [GenerateTypedNameReferences] public sealed partial class LanguageMenuWindow : DefaultWindow { - [Dependency] private readonly IConsoleHost _consoleHost = default!; private readonly LanguageSystem _language; - private readonly List _entries = new(); + public Action? OnLanguageSelected; + public LanguageMenuWindow() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); _language = IoCManager.Resolve().GetEntitySystem(); Title = Loc.GetString("language-menu-window-title"); @@ -113,7 +112,7 @@ private void AddLanguageEntry(string language) private void OnLanguageChosen(string id) { - _consoleHost.ExecuteCommand("lsselectlang " + id); + OnLanguageSelected?.Invoke(id); } private struct EntryState diff --git a/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorButton.cs b/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorButton.cs new file mode 100644 index 00000000000..a26f1dd9776 --- /dev/null +++ b/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorButton.cs @@ -0,0 +1,95 @@ +using System.Linq; +using System.Numerics; +using Content.Client.UserInterface.Systems.Chat.Controls; +using Content.Shared.Language; +using Robust.Shared.Utility; + +namespace Content.Client._NF.Language.Systems.Chat.Controls; + +// Mostly copied from ChannelSelectorButton +public sealed class LanguageSelectorButton : ChatPopupButton +{ + public event Action? OnLanguageSelect; + + public LanguagePrototype? SelectedLanguage { get; private set; } + + private const int SelectorDropdownOffset = 38; + + public LanguageSelectorButton() + { + Name = "LanguageSelector"; + + Popup.Selected += OnLanguageSelected; + + if (Popup.FirstLanguage is { } firstSelector) + { + Select(firstSelector); + } + } + + protected override UIBox2 GetPopupPosition() + { + var globalLeft = GlobalPosition.X; + var globalBot = GlobalPosition.Y + Height; + return UIBox2.FromDimensions( + new Vector2(globalLeft, globalBot), + new Vector2(SizeBox.Width, SelectorDropdownOffset)); + } + + private void OnLanguageSelected(LanguagePrototype channel) + { + Select(channel); + } + + public void Select(LanguagePrototype language) + { + if (Popup.Visible) + { + Popup.Close(); + } + + if (SelectedLanguage == language) + return; + SelectedLanguage = language; + OnLanguageSelect?.Invoke(language); + + Text = LanguageSelectorName(language); + } + + public static string LanguageSelectorName(LanguagePrototype language, bool full = false) + { + var name = language.LocalizedName; + + // if the language name is short enough, just return it + if (full || name.Length < 5) + return name; + + // If the language name is multi-word, collect first letters and capitalize them + if (name.Contains(' ')) + { + var result = name + .Split(" ") + .Select(it => it.FirstOrNull()) + .Where(it => it != null) + .Select(it => char.ToUpper(it!.Value)); + + return new string(result.ToArray()); + } + + // Alternatively, take the first 5 letters + return name[..5]; + } + + // public Color ChannelSelectColor(ChatSelectChannel channel) + // { + // return channel switch + // { + // ChatSelectChannel.Radio => Color.LimeGreen, + // ChatSelectChannel.LOOC => Color.MediumTurquoise, + // ChatSelectChannel.OOC => Color.LightSkyBlue, + // ChatSelectChannel.Dead => Color.MediumPurple, + // ChatSelectChannel.Admin => Color.HotPink, + // _ => Color.DarkGray + // }; + // } +} diff --git a/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorItemButton.cs b/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorItemButton.cs new file mode 100644 index 00000000000..caa5eb31219 --- /dev/null +++ b/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorItemButton.cs @@ -0,0 +1,28 @@ +using Content.Client.Stylesheets; +using Content.Client.UserInterface.Systems.Chat; +using Content.Client.UserInterface.Systems.Chat.Controls; +using Content.Shared.Chat; +using Content.Shared.Language; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client._NF.Language.Systems.Chat.Controls; + +// Mostly copied from ChannelSelectorItemButton +public sealed class LanguageSelectorItemButton : Button +{ + public readonly LanguagePrototype Language; + + public bool IsHidden => Parent == null; + + public LanguageSelectorItemButton(LanguagePrototype language) + { + Language = language; + AddStyleClass(StyleNano.StyleClassChatChannelSelectorButton); + + Text = LanguageSelectorButton.LanguageSelectorName(language, full: true); + + // var prefix = ChatUIController.ChannelPrefixes[selector]; + // if (prefix != default) + // Text = Loc.GetString("hud-chatbox-select-name-prefixed", ("name", Text), ("prefix", prefix)); + } +} diff --git a/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorPopup.cs b/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorPopup.cs new file mode 100644 index 00000000000..38eddd4daac --- /dev/null +++ b/Content.Client/_NF/Language/Systems/Chat/Controls/LanguageSelectorPopup.cs @@ -0,0 +1,109 @@ +using System.Reflection.Metadata.Ecma335; +using Content.Client.Language.Systems; +using Content.Client.UserInterface.Systems.Chat.Controls; +using Content.Client.UserInterface.Systems.Language; +using Content.Shared.Chat; +using Content.Shared.Language; +using Content.Shared.Language.Systems; +using Robust.Client.UserInterface.Controls; +using static Robust.Client.UserInterface.Controls.BaseButton; + +namespace Content.Client._NF.Language.Systems.Chat.Controls; + +// Mostly copied from LanguageSelectorPopup +public sealed class LanguageSelectorPopup : Popup +{ + private readonly BoxContainer _channelSelectorHBox; + private readonly Dictionary _selectorStates = new(); + private readonly LanguageMenuUIController _languageMenuController; + + public event Action? Selected; + + public LanguageSelectorPopup() + { + _channelSelectorHBox = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Horizontal, + SeparationOverride = 1 + }; + + _languageMenuController = UserInterfaceManager.GetUIController(); + _languageMenuController.LanguagesChanged += SetLanguages; + + AddChild(_channelSelectorHBox); + } + + public LanguagePrototype? FirstLanguage + { + get + { + foreach (var selector in _selectorStates.Values) + { + if (!selector.IsHidden) + return selector.Language; + } + + return null; + } + } + + private bool IsPreferredAvailable() + { + var preferred = _languageMenuController.LastPreferredLanguage; + return preferred != null && _selectorStates.TryGetValue(preferred, out var selector) && !selector.IsHidden; + } + + public void SetLanguages(List languages) + { + var languageSystem = IoCManager.Resolve().GetEntitySystem(); + _channelSelectorHBox.RemoveAllChildren(); + + foreach (var language in languages) + { + if (!_selectorStates.TryGetValue(language, out var selector)) + { + var proto = languageSystem.GetLanguage(language); + if (proto == null) + continue; + + selector = new LanguageSelectorItemButton(proto); + _selectorStates[language] = selector; + selector.OnPressed += OnSelectorPressed; + } + + if (selector.IsHidden) + { + _channelSelectorHBox.AddChild(selector); + } + } + + var isPreferredAvailable = IsPreferredAvailable(); + if (!isPreferredAvailable) + { + var first = FirstLanguage; + if (first != null) + Select(first); + } + } + + private void OnSelectorPressed(ButtonEventArgs args) + { + var button = (LanguageSelectorItemButton) args.Button; + Select(button.Language); + } + + private void Select(LanguagePrototype language) + { + Selected?.Invoke(language); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (!disposing) + return; + + _languageMenuController.LanguagesChanged -= SetLanguages; + } +} diff --git a/Content.Client/_NF/UserInterface/Systems/Language/LanguageMenuUIController.cs b/Content.Client/_NF/UserInterface/Systems/Language/LanguageMenuUIController.cs index 6141f11ac25..ef764320dd6 100644 --- a/Content.Client/_NF/UserInterface/Systems/Language/LanguageMenuUIController.cs +++ b/Content.Client/_NF/UserInterface/Systems/Language/LanguageMenuUIController.cs @@ -1,8 +1,10 @@ using Content.Client._NF.Language; using Content.Client.Gameplay; +using Content.Client.UserInterface.Systems.Chat; using Content.Shared.Language; using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controls; +using Robust.Shared.Console; using Robust.Shared.Timing; using static Content.Shared.Language.Systems.SharedLanguageSystem; @@ -13,7 +15,11 @@ public sealed class LanguageMenuUIController : UIController, IOnStateEntered>? LanguagesChanged; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IConsoleHost _consoleHost = default!; public override void Initialize() { @@ -28,6 +34,7 @@ private void OnStateUpdate(LanguageMenuStateMessage ev) return; _languageWindow.UpdateState(ev); + LanguagesChanged?.Invoke(ev.Options); } private void OnActionMenu(EntityUid uid, LanguageSpeakerComponent component, LanguageMenuActionEvent args) @@ -43,16 +50,29 @@ private void OnActionMenu(EntityUid uid, LanguageSpeakerComponent component, Lan else { _languageWindow!.Open(); - EntityManager.EntityNetManager?.SendSystemNetworkMessage(new RequestLanguageMenuStateMessage()); + RequestUpdate(); } args.Handled = true; } + public void RequestUpdate() + { + EntityManager.EntityNetManager?.SendSystemNetworkMessage(new RequestLanguageMenuStateMessage()); + } + + public void SetLanguage(string id) + { + _consoleHost.ExecuteCommand("lsselectlang " + id); + LastPreferredLanguage = id; + } + public void OnStateEntered(GameplayState state) { _languageWindow = UIManager.CreateWindow(); + _languageWindow.OnLanguageSelected += SetLanguage; LayoutContainer.SetAnchorPreset(_languageWindow, LayoutContainer.LayoutPreset.Center); + RequestUpdate(); } public void OnStateExited(GameplayState state)