1
0

AutodocWindow.xaml.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
  2. // SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
  3. // SPDX-FileCopyrightText: 2025 JohnOakman <sremy2012@hotmail.fr>
  4. // SPDX-FileCopyrightText: 2025 deltanedas <39013340+deltanedas@users.noreply.github.com>
  5. // SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org>
  6. //
  7. // SPDX-License-Identifier: AGPL-3.0-or-later
  8. using Content.Client.UserInterface.Controls;
  9. using Content.Shared.Administration;
  10. using Content.Shared._Shitmed.Autodoc;
  11. using Content.Shared._Shitmed.Autodoc.Components;
  12. using Content.Shared._Shitmed.Autodoc.Systems;
  13. using Robust.Client.AutoGenerated;
  14. using Robust.Client.Player;
  15. using Robust.Client.UserInterface.Controls;
  16. using Robust.Client.UserInterface.XAML;
  17. using Robust.Shared.Timing;
  18. using Robust.Client.UserInterface;
  19. using Robust.Shared.Serialization.Manager;
  20. using Robust.Shared.Utility;
  21. using System.IO;
  22. using YamlDotNet.RepresentationModel;
  23. using Robust.Shared.Serialization.Markdown;
  24. namespace Content.Client._Shitmed.Autodoc;
  25. [GenerateTypedNameReferences]
  26. public sealed partial class AutodocWindow : FancyWindow
  27. {
  28. [Dependency] private readonly IEntityManager _entMan = default!;
  29. [Dependency] private readonly IFileDialogManager _dialogMan = default!;
  30. [Dependency] private readonly IPlayerManager _player = default!;
  31. [Dependency] private readonly ISerializationManager _serMan = default!;
  32. [Dependency] private readonly ILogManager _logMan = default!;
  33. private SharedAutodocSystem _autodoc;
  34. private EntityUid _owner;
  35. private bool _active;
  36. private int _programCount = 0;
  37. private ISawmill _sawmill;
  38. public event Action<string>? OnCreateProgram;
  39. public event Action<int>? OnToggleProgramSafety;
  40. public event Action<int>? OnRemoveProgram;
  41. public event Action<int, IAutodocStep, int>? OnAddStep;
  42. public event Action<int, int>? OnRemoveStep;
  43. public event Action<int>? OnStart;
  44. public event Action? OnStop;
  45. public event Action<AutodocProgram>? OnImportProgram;
  46. private DialogWindow? _dialog;
  47. private AutodocProgramWindow? _currentProgram;
  48. public AutodocWindow(EntityUid owner, IEntityManager entMan, IPlayerManager player)
  49. {
  50. RobustXamlLoader.Load(this);
  51. IoCManager.InjectDependencies(this);
  52. _entMan = entMan;
  53. _player = player;
  54. _autodoc = entMan.System<SharedAutodocSystem>();
  55. _sawmill = _logMan.GetSawmill("autodoc-ui");
  56. _owner = owner;
  57. OnClose += () =>
  58. {
  59. _dialog?.Close();
  60. _currentProgram?.Close();
  61. };
  62. ImportProgramButton.OnPressed += _ =>
  63. {
  64. ImportProgram();
  65. };
  66. CreateProgramButton.OnPressed += _ =>
  67. {
  68. if (_dialog != null)
  69. {
  70. _dialog.MoveToFront();
  71. return;
  72. }
  73. if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
  74. return;
  75. var field = "title";
  76. var prompt = Loc.GetString("autodoc-program-title");
  77. var placeholder = Loc.GetString("autodoc-program-title-placeholder", ("number", comp.Programs.Count + 1));
  78. var entry = new QuickDialogEntry(field, QuickDialogEntryType.ShortText, prompt, placeholder);
  79. var entries = new List<QuickDialogEntry> { entry };
  80. _dialog = new DialogWindow(CreateProgramButton.Text!, entries);
  81. _dialog.OnConfirmed += responses =>
  82. {
  83. var title = responses[field].Trim();
  84. if (title.Length < 1 || title.Length > comp.MaxProgramTitleLength)
  85. return;
  86. OnCreateProgram?.Invoke(title);
  87. };
  88. // prevent MoveToFront being called on a closed window and double closing
  89. _dialog.OnClose += () => _dialog = null;
  90. };
  91. AbortButton.AddStyleClass("Caution");
  92. AbortButton.OnPressed += _ => OnStop?.Invoke();
  93. UpdateActive();
  94. UpdatePrograms();
  95. }
  96. public void UpdateActive()
  97. {
  98. if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
  99. return;
  100. // UI must be in the inactive state by default, since this wont run when inactive at startup
  101. var active = _entMan.HasComponent<ActiveAutodocComponent>(_owner);
  102. if (active == _active)
  103. return;
  104. _active = active;
  105. CreateProgramButton.Disabled = active || _programCount >= comp.MaxPrograms;
  106. AbortButton.Disabled = !active;
  107. foreach (var button in Programs.Children)
  108. {
  109. ((Button) button).Disabled = active;
  110. }
  111. if (!active)
  112. return;
  113. // close windows that can only be open when inactive
  114. _dialog?.Close();
  115. _currentProgram?.Close();
  116. }
  117. private void UpdatePrograms()
  118. {
  119. if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
  120. return;
  121. var count = comp.Programs.Count;
  122. if (count == _programCount)
  123. return;
  124. _programCount = count;
  125. CreateProgramButton.Disabled = _active || _programCount >= comp.MaxPrograms;
  126. Programs.RemoveAllChildren();
  127. for (int i = 0; i < comp.Programs.Count; i++)
  128. {
  129. var button = new Button()
  130. {
  131. Text = comp.Programs[i].Title
  132. };
  133. var index = i;
  134. button.OnPressed += _ => OpenProgram(index);
  135. button.Disabled = _active;
  136. Programs.AddChild(button);
  137. }
  138. }
  139. private async void ImportProgram()
  140. {
  141. if (await _dialogMan.OpenFile(new FileDialogFilters(new FileDialogFilters.Group("yml"))) is not {} file)
  142. return;
  143. try
  144. {
  145. using var reader = new StreamReader(file, EncodingHelpers.UTF8);
  146. var yamlStream = new YamlStream();
  147. yamlStream.Load(reader);
  148. var root = yamlStream.Documents[0].RootNode;
  149. var program = _serMan.Read<AutodocProgram>(root.ToDataNode(), notNullableOverride: true);
  150. OnImportProgram?.Invoke(program);
  151. }
  152. catch (Exception e)
  153. {
  154. _sawmill.Error($"Error when importing program: {e}");
  155. }
  156. }
  157. private void OpenProgram(int index)
  158. {
  159. if (!_entMan.TryGetComponent<AutodocComponent>(_owner, out var comp))
  160. return;
  161. // no editing multiple programs at once
  162. if (_currentProgram is {} existing)
  163. existing.Close();
  164. var window = new AutodocProgramWindow(_owner, comp.Programs[index]);
  165. window.OnToggleSafety += () => OnToggleProgramSafety?.Invoke(index);
  166. window.OnRemoveProgram += () =>
  167. {
  168. OnRemoveProgram?.Invoke(index);
  169. Programs.RemoveChild(index);
  170. };
  171. window.OnAddStep += (step, stepIndex) => OnAddStep?.Invoke(index, step, stepIndex);
  172. window.OnRemoveStep += step => OnRemoveStep?.Invoke(index, step);
  173. window.OnStart += () =>
  174. {
  175. if (_active)
  176. return;
  177. OnStart?.Invoke(index);
  178. // predict it starting the program
  179. _entMan.EnsureComponent<ActiveAutodocComponent>(_owner);
  180. };
  181. window.OnClose += () => _currentProgram = null;
  182. _currentProgram = window;
  183. window.OpenCentered();
  184. }
  185. protected override void FrameUpdate(FrameEventArgs args)
  186. {
  187. base.FrameUpdate(args);
  188. UpdateActive();
  189. UpdatePrograms();
  190. }
  191. }