using System.Linq; using Content.Client.Stylesheets; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Reagent; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Chemistry.UI { /// /// Client-side UI used to control a . /// [GenerateTypedNameReferences] public sealed partial class ReagentDispenserWindow : DefaultWindow { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public event Action? OnDispenseReagentButtonPressed; public event Action? OnDispenseReagentButtonMouseEntered; public event Action? OnDispenseReagentButtonMouseExited; public event Action? OnEjectJugButtonPressed; public event Action? OnEjectJugButtonMouseEntered; public event Action? OnEjectJugButtonMouseExited; /// /// Create and initialize the dispenser UI client-side. Creates the basic layout, /// actual data isn't filled in until the server sends data about the dispenser. /// public ReagentDispenserWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); var dispenseAmountGroup = new ButtonGroup(); DispenseButton1.Group = dispenseAmountGroup; DispenseButton5.Group = dispenseAmountGroup; DispenseButton10.Group = dispenseAmountGroup; DispenseButton15.Group = dispenseAmountGroup; DispenseButton20.Group = dispenseAmountGroup; DispenseButton25.Group = dispenseAmountGroup; DispenseButton30.Group = dispenseAmountGroup; DispenseButton50.Group = dispenseAmountGroup; DispenseButton100.Group = dispenseAmountGroup; } /// /// Update the button grid of reagents which can be dispensed. /// /// Reagents which can be dispensed by this dispenser public void UpdateReagentsList(List>> inventory) { if (ChemicalList == null) return; ChemicalList.Children.Clear(); foreach (KeyValuePair> entry in inventory) { var button = new DispenseReagentButton(entry.Key, entry.Value.Key, entry.Value.Value); button.OnPressed += args => OnDispenseReagentButtonPressed?.Invoke(args, button); button.OnMouseEntered += args => OnDispenseReagentButtonMouseEntered?.Invoke(args, button); button.OnMouseExited += args => OnDispenseReagentButtonMouseExited?.Invoke(args, button); ChemicalList.AddChild(button); var ejectButton = new EjectJugButton(entry.Key); ejectButton.OnPressed += args => OnEjectJugButtonPressed?.Invoke(args, ejectButton); ejectButton.OnMouseEntered += args => OnEjectJugButtonMouseEntered?.Invoke(args, ejectButton); ejectButton.OnMouseExited += args => OnEjectJugButtonMouseExited?.Invoke(args, ejectButton); ChemicalList.AddChild(ejectButton); } } /// /// Update the UI state when new state data is received from the server. /// /// State data sent by the server. public void UpdateState(BoundUserInterfaceState state) { var castState = (ReagentDispenserBoundUserInterfaceState) state; UpdateContainerInfo(castState); UpdateReagentsList(castState.Inventory); // Disable the Clear & Eject button if no beaker ClearButton.Disabled = castState.OutputContainer is null; EjectButton.Disabled = castState.OutputContainer is null; switch (castState.SelectedDispenseAmount) { case ReagentDispenserDispenseAmount.U1: DispenseButton1.Pressed = true; break; case ReagentDispenserDispenseAmount.U5: DispenseButton5.Pressed = true; break; case ReagentDispenserDispenseAmount.U10: DispenseButton10.Pressed = true; break; case ReagentDispenserDispenseAmount.U15: DispenseButton15.Pressed = true; break; case ReagentDispenserDispenseAmount.U20: DispenseButton20.Pressed = true; break; case ReagentDispenserDispenseAmount.U25: DispenseButton25.Pressed = true; break; case ReagentDispenserDispenseAmount.U30: DispenseButton30.Pressed = true; break; case ReagentDispenserDispenseAmount.U50: DispenseButton50.Pressed = true; break; case ReagentDispenserDispenseAmount.U100: DispenseButton100.Pressed = true; break; } } /// /// Update the fill state and list of reagents held by the current reagent container, if applicable. /// Also highlights a reagent if it's dispense button is being mouse hovered. /// /// State data for the dispenser. /// or null if no button is being hovered. public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state) { ContainerInfo.Children.Clear(); if (state.OutputContainer is null) { ContainerInfo.Children.Add(new Label {Text = Loc.GetString("reagent-dispenser-window-no-container-loaded-text") }); return; } ContainerInfo.Children.Add(new BoxContainer // Name of the container and its fill status (Ex: 44/100u) { Orientation = LayoutOrientation.Horizontal, Children = { new Label {Text = $"{state.OutputContainer.DisplayName}: "}, new Label { Text = $"{state.OutputContainer.CurrentVolume}/{state.OutputContainer.MaxVolume}", StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} } } }); foreach (var (reagent, quantity) in state.OutputContainer.Reagents!) { // Try get to the prototype for the given reagent. This gives us its name. var localizedName = _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? p) ? p.LocalizedName : Loc.GetString("reagent-dispenser-window-reagent-name-not-found-text"); var nameLabel = new Label {Text = $"{localizedName}: "}; var quantityLabel = new Label { Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity)), StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}, }; ContainerInfo.Children.Add(new BoxContainer { Orientation = LayoutOrientation.Horizontal, Children = { nameLabel, quantityLabel, } }); } } } public sealed class DispenseReagentButton : Button { public string ReagentId { get; } public DispenseReagentButton(string reagentId, string text, string amount) { AddStyleClass("OpenRight"); ReagentId = reagentId; Text = text + " " + amount; } } public sealed class EjectJugButton : Button { public string ReagentId { get; } public EjectJugButton(string reagentId) { AddStyleClass("OpenLeft"); ReagentId = reagentId; Text = "⏏"; } } }