Delta-v/Content.Client/_DV/AACTablet/UI/AACWindow.xaml.cs

244 lines
7.6 KiB
C#

using System.Linq;
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared._DV.QuickPhrase;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._DV.AACTablet.UI;
[GenerateTypedNameReferences]
public sealed partial class AACWindow : FancyWindow
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly List<QuickPhrasePrototype> _phrases;
private readonly Dictionary<string, List<QuickPhrasePrototype>> _filteredPhrases = new();
public event Action<List<ProtoId<QuickPhrasePrototype>>>? PhraseButtonPressed;
public event Action? Typing;
public event Action? SubmitPressed;
private const float SpaceWidth = 10f;
private const float ParentWidth = 540f;
private const int ColumnCount = 4;
private const int ButtonWidth =
(int)((ParentWidth - SpaceWidth * 2) / ColumnCount - SpaceWidth * ((ColumnCount - 1f) / ColumnCount));
public const int MaxPhrases = 10; // no writing novels
private readonly List<ProtoId<QuickPhrasePrototype>> _phraseBuffer = [];
private readonly List<ProtoId<QuickPhrasePrototype>> _phraseSingle = [];
public AACWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_phrases = _prototype.EnumeratePrototypes<QuickPhrasePrototype>().ToList();
_phrases.Sort((a, b) => string.CompareOrdinal(a.Group, b.Group));
SearchBar.OnTextChanged += FilterSearch;
SendButton.OnPressed += SendBuffer;
ClearButton.OnPressed += BackspaceBuffer;
PopulateGui();
FilterSearch(null);
}
private void BackspaceBuffer(BaseButton.ButtonEventArgs obj)
{
if (_phraseBuffer.Count == 0)
return;
_phraseBuffer.RemoveAt(_phraseBuffer.Count - 1);
UpdateBufferText();
Typing?.Invoke();
}
private void UpdateBufferText()
{
BufferedString.Text = string.Empty;
foreach (var phraseId in _phraseBuffer)
{
var phrase = _prototype.Index(phraseId);
BufferedString.Text += Loc.GetString(phrase.Text) + " ";
}
}
private void SendBuffer(BaseButton.ButtonEventArgs obj)
{
PhraseButtonPressed?.Invoke(_phraseBuffer);
_phraseBuffer.Clear();
BufferedString.Text = string.Empty;
SubmitPressed?.Invoke();
}
private void FilterSearch(LineEdit.LineEditEventArgs? obj)
{
SearchResults.RemoveAllChildren();
_filteredPhrases.Clear();
var emptySearch = string.IsNullOrEmpty(SearchBar.Text);
foreach (var phrase in _phrases)
{
if (!emptySearch && !Loc.GetString(phrase.Text).Contains(SearchBar.Text, StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
if (_filteredPhrases.TryGetValue(phrase.Group, out var group))
{
group.Add(phrase);
}
else
{
_filteredPhrases.Add(phrase.Group, new List<QuickPhrasePrototype> { phrase });
}
}
foreach (var phraseList in _filteredPhrases.Values)
{
phraseList.Sort((a, b) =>
string.Compare(Loc.GetString(a.Text),
Loc.GetString(b.Text),
StringComparison.CurrentCultureIgnoreCase));
}
var boxContainer = CreateBoxContainerForTab(_filteredPhrases);
SearchResults.AddChild(boxContainer);
}
private void PopulateGui()
{
// take ALL phrases and turn them into tabs and groups, so the buttons are sorted and tabbed
var sortedTabs = _phrases
.GroupBy(p => p.Tab)
.OrderBy(g => g.Key)
.ToDictionary(
g => g.Key,
g => g.GroupBy(p => p.Group)
.OrderBy(gg => gg.Key)
.ToDictionary(
gg => gg.Key,
gg => gg.OrderBy(p => Loc.GetString(p.Text)).ToList()
)
);
CreateTabContainer(sortedTabs);
}
private void CreateTabContainer(Dictionary<string, Dictionary<string, List<QuickPhrasePrototype>>> sortedTabs)
{
foreach (var tab in sortedTabs)
{
var tabName = Loc.GetString(tab.Key);
var boxContainer = CreateBoxContainerForTab(tab.Value);
var scroll = new ScrollContainer();
scroll.HScrollEnabled = false;
scroll.AddChild(boxContainer);
WindowBody.AddChild(scroll);
WindowBody.SetTabTitle(WindowBody.ChildCount - 1, tabName);
}
}
private BoxContainer CreateBoxContainerForTab(Dictionary<string, List<QuickPhrasePrototype>> groups)
{
var boxContainer = new BoxContainer
{
HorizontalExpand = true,
Orientation = BoxContainer.LayoutOrientation.Vertical
};
foreach (var group in groups)
{
var header = CreateHeaderForGroup(group.Key);
var buttonContainer = CreateButtonContainerForGroup(group.Value);
boxContainer.AddChild(header);
boxContainer.AddChild(buttonContainer);
}
return boxContainer;
}
private static Label CreateHeaderForGroup(string groupName)
{
var header = new Label
{
HorizontalExpand = true,
Text = groupName,
Margin = new Thickness(10, 10, 10, 0),
StyleClasses = { "LabelBig" }
};
return header;
}
private GridContainer CreateButtonContainerForGroup(List<QuickPhrasePrototype> phrases)
{
var buttonContainer = CreateButtonContainer();
foreach (var phrase in phrases)
{
var text = Loc.GetString(phrase.Text);
var button = CreatePhraseButton(text, phrase.StyleClass);
button.OnPressed += _ => OnPhraseButtonPressed(phrase);
buttonContainer.AddChild(button);
}
return buttonContainer;
}
private static GridContainer CreateButtonContainer()
{
var buttonContainer = new GridContainer
{
Margin = new Thickness(10),
Columns = ColumnCount
};
return buttonContainer;
}
private static Button CreatePhraseButton(string text, string styleClass)
{
var phraseButton = new Button
{
Access = AccessLevel.Public,
MaxSize = new Vector2(ButtonWidth, ButtonWidth),
ClipText = false,
HorizontalExpand = true,
StyleClasses = { styleClass }
};
var buttonLabel = new RichTextLabel
{
Margin = new Thickness(0, 5),
StyleClasses = { "WhiteText" }
};
buttonLabel.SetMessage(text);
phraseButton.AddChild(buttonLabel);
return phraseButton;
}
private void OnPhraseButtonPressed(ProtoId<QuickPhrasePrototype> phraseId)
{
if (ShouldBuffer.Pressed)
{
// there's no user feedback but you shouldn't be writing novels anyway
if (_phraseBuffer.Count >= MaxPhrases)
return;
_phraseBuffer.Add(phraseId);
UpdateBufferText();
Typing?.Invoke();
}
else
{
_phraseSingle.Clear();
_phraseSingle.Add(phraseId);
PhraseButtonPressed?.Invoke(_phraseSingle);
SubmitPressed?.Invoke();
}
}
}