diff --git a/Content.Client/Chemistry/UI/ButtonGrid.cs b/Content.Client/Chemistry/UI/ButtonGrid.cs new file mode 100644 index 0000000000..0abd9ef8a4 --- /dev/null +++ b/Content.Client/Chemistry/UI/ButtonGrid.cs @@ -0,0 +1,119 @@ +using System; +using Robust.Client.Graphics; +using Robust.Client.UserInterface.Controls; + +namespace Content.Client.Chemistry.UI; + +/// +/// Creates a grid of buttons given a comma-seperated list of Text +/// +public sealed class ButtonGrid : GridContainer +{ + private string _buttonList = ""; + + /// + /// A comma-seperated list of text to use for each button. These will be inserted sequentially. + /// + public string ButtonList + { + get => _buttonList; + set + { + _buttonList = value; + Update(); + } + } + + public bool RadioGroup { get; set; } = false; + + private string? _selected; + + /// + /// Which button is currently selected. Only matters when is true. + /// + public string? Selected + { + get => _selected; + set + { + _selected = value; + Update(); + } + } + + public Action? OnButtonPressed; + + /// + /// + /// + public new int Columns + { + get => base.Columns; + set + { + base.Columns = value; + Update(); + } + } + + /// + /// + /// + public new int Rows + { + get => base.Rows; + set + { + base.Rows = value; + Update(); + } + } + + private void Update() + { + if (ButtonList == "") + return; + + this.Children.Clear(); + var i = 0; + var list = ButtonList.Split(","); + + var group = new ButtonGroup(); + + foreach (var button in list) + { + var btn = new Button(); + btn.Text = button; + btn.OnPressed += _ => + { + if (RadioGroup) + btn.Pressed = true; + Selected = button; + OnButtonPressed?.Invoke(button); + }; + if (button == Selected) + btn.Pressed = true; + var sep = HSeparationOverride ?? 0; + // ReSharper disable once PossibleLossOfFraction + // btn.SetWidth = (this.PixelWidth - sep * (Columns - 1)) / 3; + btn.Group = group; + + var row = i / Columns; + var col = i % Columns; + var last = i == list.Length - 1; + var lastCol = i == Columns - 1; + var lastRow = row == list.Length / Columns - 1; + + if (row == 0 && (lastCol || last)) + btn.AddStyleClass("OpenLeft"); + else if (col == 0 && lastRow) + btn.AddStyleClass("OpenRight"); + else + btn.AddStyleClass("OpenBoth"); + + this.Children.Add(btn); + + i++; + } + } +} diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml new file mode 100644 index 0000000000..966c730140 --- /dev/null +++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml @@ -0,0 +1,37 @@ + + + + + + + diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs new file mode 100644 index 0000000000..60336143fc --- /dev/null +++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml.cs @@ -0,0 +1,30 @@ +using Content.Shared.Chemistry; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Chemistry.UI; + +[GenerateTypedNameReferences] +public sealed partial class ReagentCardControl : Control +{ + public string StorageSlotId { get; } + public Action? OnPressed; + public Action? OnEjectButtonPressed; + + public ReagentCardControl(ReagentInventoryItem item) + { + RobustXamlLoader.Load(this); + + StorageSlotId = item.StorageSlotId; + ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = item.ReagentColor }; + ReagentNameLabel.Text = item.ReagentLabel; + ReagentNameLabel.FontColorOverride = Color.White; + FillLabel.Text = item.StoredAmount; + EjectButtonIcon.Text = Loc.GetString("reagent-dispenser-window-eject-container-button"); + + MainButton.OnPressed += args => OnPressed?.Invoke(StorageSlotId); + EjectButton.OnPressed += args => OnEjectButtonPressed?.Invoke(StorageSlotId); + } +} diff --git a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs index 8244e3e6ed..fce57a6ec5 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs +++ b/Content.Client/Chemistry/UI/ReagentDispenserBoundUserInterface.cs @@ -42,38 +42,11 @@ namespace Content.Client.Chemistry.UI // Setup static button actions. _window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentDispenser.OutputSlotName)); _window.ClearButton.OnPressed += _ => SendMessage(new ReagentDispenserClearContainerSolutionMessage()); - _window.DispenseButton1.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U1)); - _window.DispenseButton5.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U5)); - _window.DispenseButton10.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U10)); - _window.DispenseButton15.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U15)); - _window.DispenseButton20.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U20)); - _window.DispenseButton25.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U25)); - _window.DispenseButton30.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U30)); - _window.DispenseButton50.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U50)); - _window.DispenseButton100.OnPressed += _ => SendMessage(new ReagentDispenserSetDispenseAmountMessage(ReagentDispenserDispenseAmount.U100)); - // Setup reagent button actions. - _window.OnDispenseReagentButtonPressed += (args, button) => SendMessage(new ReagentDispenserDispenseReagentMessage(button.ReagentId)); - _window.OnDispenseReagentButtonMouseEntered += (args, button) => - { - if (_lastState is not null) - _window.UpdateContainerInfo(_lastState); - }; - _window.OnDispenseReagentButtonMouseExited += (args, button) => - { - if (_lastState is not null) - _window.UpdateContainerInfo(_lastState); - }; + _window.AmountGrid.OnButtonPressed += s => SendMessage(new ReagentDispenserSetDispenseAmountMessage(s)); - _window.OnEjectJugButtonPressed += (args, button) => SendMessage(new ItemSlotButtonPressedEvent(button.ReagentId)); - _window.OnEjectJugButtonMouseEntered += (args, button) => { - if (_lastState is not null) - _window.UpdateContainerInfo(_lastState); - }; - _window.OnEjectJugButtonMouseExited += (args, button) => { - if (_lastState is not null) - _window.UpdateContainerInfo(_lastState); - }; + _window.OnDispenseReagentButtonPressed += (id) => SendMessage(new ReagentDispenserDispenseReagentMessage(id)); + _window.OnEjectJugButtonPressed += (id) => SendMessage(new ItemSlotButtonPressedEvent(id)); } /// diff --git a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml index 3b812ba56b..c900d7ecf2 100644 --- a/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml +++ b/Content.Client/Chemistry/UI/ReagentDispenserWindow.xaml @@ -1,53 +1,77 @@ - - - - [GenerateTypedNameReferences] - public sealed partial class ReagentDispenserWindow : DefaultWindow + public sealed partial class ReagentDispenserWindow : FancyWindow { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntityManager _entityManager = 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; + public event Action? OnDispenseReagentButtonPressed; + public event Action? OnEjectJugButtonPressed; /// /// Create and initialize the dispenser UI client-side. Creates the basic layout, @@ -35,44 +29,27 @@ namespace Content.Client.Chemistry.UI { 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) + public void UpdateReagentsList(List inventory) { - if (ChemicalList == null) + if (ReagentList == null) return; - ChemicalList.Children.Clear(); + ReagentList.Children.Clear(); //Sort inventory by reagentLabel - inventory.Sort((x, y) => x.Value.Key.CompareTo(y.Value.Key)); + inventory.Sort((x, y) => x.ReagentLabel.CompareTo(y.ReagentLabel)); - foreach (KeyValuePair> entry in inventory) + foreach (var item 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); + var card = new ReagentCardControl(item); + card.OnPressed += OnDispenseReagentButtonPressed; + card.OnEjectButtonPressed += OnEjectJugButtonPressed; + ReagentList.Children.Add(card); } } @@ -93,36 +70,7 @@ namespace Content.Client.Chemistry.UI 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; - } + AmountGrid.Selected = ((int)castState.SelectedDispenseAmount).ToString(); } /// @@ -137,23 +85,15 @@ namespace Content.Client.Chemistry.UI if (state.OutputContainer is null) { + ContainerInfoName.Text = ""; + ContainerInfoFill.Text = ""; 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} - } - } - }); + // Set Name of the container and its fill status (Ex: 44/100u) + ContainerInfoName.Text = state.OutputContainer.DisplayName; + ContainerInfoFill.Text = state.OutputContainer.CurrentVolume + "/" + state.OutputContainer.MaxVolume; foreach (var (reagent, quantity) in state.OutputContainer.Reagents!) { diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index 3bcdd4b964..c48bf086d3 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -81,9 +81,9 @@ namespace Content.Server.Chemistry.EntitySystems return null; } - private List>> GetInventory(Entity reagentDispenser) + private List GetInventory(Entity reagentDispenser) { - var inventory = new List>>(); + var inventory = new List(); for (var i = 0; i < reagentDispenser.Comp.NumSlots; i++) { @@ -99,15 +99,17 @@ namespace Content.Server.Chemistry.EntitySystems else continue; - // Add volume remaining label + // Get volume remaining and color of solution FixedPoint2 quantity = 0f; + var reagentColor = Color.White; if (storedContainer != null && _solutionContainerSystem.TryGetDrainableSolution(storedContainer.Value, out _, out var sol)) { quantity = sol.Volume; + reagentColor = sol.GetColor(_prototypeManager); } var storedAmount = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity)); - inventory.Add(new KeyValuePair>(storageSlotId, new KeyValuePair(reagentLabel, storedAmount))); + inventory.Add(new ReagentInventoryItem(storageSlotId, reagentLabel, storedAmount, reagentColor)); } return inventory; diff --git a/Content.Shared/Chemistry/SharedReagentDispenser.cs b/Content.Shared/Chemistry/SharedReagentDispenser.cs index 2b9c318c58..5de3f6cae3 100644 --- a/Content.Shared/Chemistry/SharedReagentDispenser.cs +++ b/Content.Shared/Chemistry/SharedReagentDispenser.cs @@ -20,6 +20,46 @@ namespace Content.Shared.Chemistry { ReagentDispenserDispenseAmount = amount; } + + /// + /// Create a new instance from interpreting a String as an integer, + /// throwing an exception if it is unable to parse. + /// + public ReagentDispenserSetDispenseAmountMessage(String s) + { + switch (s) + { + case "1": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U1; + break; + case "5": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U5; + break; + case "10": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U10; + break; + case "15": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U15; + break; + case "20": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U20; + break; + case "25": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U25; + break; + case "30": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U30; + break; + case "50": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U50; + break; + case "100": + ReagentDispenserDispenseAmount = ReagentDispenserDispenseAmount.U100; + break; + default: + throw new Exception($"Cannot convert the string `{s}` into a valid ReagentDispenser DispenseAmount"); + } + } } [Serializable, NetSerializable] @@ -52,20 +92,30 @@ namespace Content.Shared.Chemistry U100 = 100, } + [Serializable, NetSerializable] + public sealed class ReagentInventoryItem(string storageSlotId, string reagentLabel, string storedAmount, Color reagentColor) + { + public string StorageSlotId = storageSlotId; + public string ReagentLabel = reagentLabel; + public string StoredAmount = storedAmount; + public Color ReagentColor = reagentColor; + } + [Serializable, NetSerializable] public sealed class ReagentDispenserBoundUserInterfaceState : BoundUserInterfaceState { public readonly ContainerInfo? OutputContainer; public readonly NetEntity? OutputContainerEntity; + /// /// A list of the reagents which this dispenser can dispense. /// - public readonly List>> Inventory; + public readonly List Inventory; public readonly ReagentDispenserDispenseAmount SelectedDispenseAmount; - public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, NetEntity? outputContainerEntity, List>> inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) + public ReagentDispenserBoundUserInterfaceState(ContainerInfo? outputContainer, NetEntity? outputContainerEntity, List inventory, ReagentDispenserDispenseAmount selectedDispenseAmount) { OutputContainer = outputContainer; OutputContainerEntity = outputContainerEntity; diff --git a/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl b/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl index 48ec8f5213..37697c4517 100644 --- a/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/reagent-dispenser-component.ftl @@ -10,9 +10,9 @@ reagent-dispenser-bound-user-interface-title = Reagent dispenser ## UI reagent-dispenser-window-amount-to-dispense-label = Amount -reagent-dispenser-window-container-label = Container: reagent-dispenser-window-clear-button = Clear reagent-dispenser-window-eject-button = Eject +reagent-dispenser-window-eject-container-button = ⏏ reagent-dispenser-window-no-container-loaded-text = No container loaded. reagent-dispenser-window-reagent-name-not-found-text = Reagent name not found reagent-dispenser-window-unknown-reagent-text = Unknown reagent