From 2ccd4713885b0cb04d24ba84ce9c88bf3d67bdb6 Mon Sep 17 00:00:00 2001 From: Intoxicating-Innocence <188202277+Intoxicating-Innocence@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:17:06 +1100 Subject: [PATCH] Chem master UI (#33328) * chemmaster buffer has colors now * I have saved chemists everywhere * implimented panelcontainers instead of labels, slight visual rework * added UI changes to input buffer * fixed some unsightly indentation on brackets and removed redundant minheight specification from verticalstretch elements * Formatting and code cleanup * pills still not rendering correctly * more tinkering, entities finally display correctly * entities display correctly, pill fields default to max now * fixed stripes * fixed excess pillCount bug * removed cache, fixed tab swapping --------- Co-authored-by: Saphire --- .../Chemistry/UI/ChemMasterWindow.xaml.cs | 285 ++++++++++-------- 1 file changed, 161 insertions(+), 124 deletions(-) diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs index 5eace08a7fdb28..20c61f10cb821a 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Numerics; using Content.Shared.FixedPoint; +using Robust.Client.Graphics; using static Robust.Client.UserInterface.Controls.BoxContainer; namespace Content.Client.Chemistry.UI @@ -90,10 +91,40 @@ public ChemMasterWindow() private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass) { - var button = new ReagentButton(text, amount, id, isBuffer, styleClass); - button.OnPressed += args - => OnReagentButtonPressed?.Invoke(args, button); - return button; + var reagentTransferButton = new ReagentButton(text, amount, id, isBuffer, styleClass); + reagentTransferButton.OnPressed += args + => OnReagentButtonPressed?.Invoke(args, reagentTransferButton); + return reagentTransferButton; + } + /// + /// Conditionally generates a set of reagent buttons based on the supplied boolean argument. + /// This was moved outside of BuildReagentRow to facilitate conditional logic, stops indentation depth getting out of hand as well. + /// + private List CreateReagentTransferButtons(ReagentId reagent, bool isBuffer, bool addReagentButtons) + { + if (!addReagentButtons) + return new List(); // Return an empty list if reagentTransferButton creation is disabled. + + var buttonConfigs = new (string text, ChemMasterReagentAmount amount, string styleClass)[] + { + ("1", ChemMasterReagentAmount.U1, StyleBase.ButtonOpenBoth), + ("5", ChemMasterReagentAmount.U5, StyleBase.ButtonOpenBoth), + ("10", ChemMasterReagentAmount.U10, StyleBase.ButtonOpenBoth), + ("25", ChemMasterReagentAmount.U25, StyleBase.ButtonOpenBoth), + ("50", ChemMasterReagentAmount.U50, StyleBase.ButtonOpenBoth), + ("100", ChemMasterReagentAmount.U100, StyleBase.ButtonOpenBoth), + (Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, StyleBase.ButtonOpenLeft), + }; + + var buttons = new List(); + + foreach (var (text, amount, styleClass) in buttonConfigs) + { + var reagentTransferButton = MakeReagentButton(text, amount, reagent, isBuffer, styleClass); + buttons.Add(reagentTransferButton); + } + + return buttons; } /// @@ -102,25 +133,36 @@ private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amo /// State data sent by the server. public void UpdateState(BoundUserInterfaceState state) { - var castState = (ChemMasterBoundUserInterfaceState) state; + var castState = (ChemMasterBoundUserInterfaceState)state; + if (castState.UpdateLabel) LabelLine = GenerateLabel(castState); - UpdatePanelInfo(castState); - - var output = castState.OutputContainerInfo; + // Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on + UpdatePanelInfo(castState); + BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u"; - + InputEjectButton.Disabled = castState.InputContainerInfo is null; - OutputEjectButton.Disabled = output is null; - CreateBottleButton.Disabled = output?.Reagents == null; - CreatePillButton.Disabled = output?.Entities == null; - + OutputEjectButton.Disabled = castState.OutputContainerInfo is null; + CreateBottleButton.Disabled = castState.OutputContainerInfo?.Reagents == null; + CreatePillButton.Disabled = castState.OutputContainerInfo?.Entities == null; + + UpdateDosageFields(castState); + } + + //assign default values for pill and bottle fields. + private void UpdateDosageFields(ChemMasterBoundUserInterfaceState castState) + { + var output = castState.OutputContainerInfo; var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int(); var holdsReagents = output?.Reagents != null; var pillNumberMax = holdsReagents ? 0 : remainingCapacity; var bottleAmountMax = holdsReagents ? remainingCapacity : 0; + var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0; + PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit); + PillTypeButtons[castState.SelectedPillType].Pressed = true; PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax; PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit; @@ -130,8 +172,19 @@ public void UpdateState(BoundUserInterfaceState state) PillNumber.Value = pillNumberMax; if (BottleDosage.Value > bottleAmountMax) BottleDosage.Value = bottleAmountMax; - } + // Avoid division by zero + if (PillDosage.Value > 0) + { + PillNumber.Value = Math.Min(bufferVolume / PillDosage.Value, pillNumberMax); + } + else + { + PillNumber.Value = 0; + } + + BottleDosage.Value = Math.Min(bottleAmountMax, bufferVolume); + } /// /// Generate a product label based on reagents in the buffer. /// @@ -178,46 +231,23 @@ private void UpdatePanelInfo(ChemMasterBoundUserInterfaceState state) var bufferVol = new Label { Text = $"{state.BufferCurrentVolume}u", - StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} + StyleClasses = { StyleNano.StyleClassLabelSecondaryColor } }; bufferHBox.AddChild(bufferVol); + // initialises rowCount to allow for striped rows + + var rowCount = 0; foreach (var (reagent, quantity) in state.BufferReagents) { - // Try to get the prototype for the given reagent. This gives us its name. - _prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto); + var reagentId = reagent; + _prototypeManager.TryIndex(reagentId.Prototype, out ReagentPrototype? proto); var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); - - if (proto != null) - { - BufferInfo.Children.Add(new BoxContainer - { - Orientation = LayoutOrientation.Horizontal, - Children = - { - new Label {Text = $"{name}: "}, - new Label - { - Text = $"{quantity}u", - StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} - }, - - // Padding - new Control {HorizontalExpand = true}, - - MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent, true, StyleBase.ButtonOpenRight), - MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent, true, StyleBase.ButtonOpenBoth), - MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent, true, StyleBase.ButtonOpenBoth), - MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent, true, StyleBase.ButtonOpenLeft), - } - }); - } + var reagentColor = proto?.SubstanceColor ?? default(Color); + BufferInfo.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagentId, quantity, true, true)); } } - + private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons) { control.Children.Clear(); @@ -228,104 +258,111 @@ private void BuildContainerUI(Control control, ContainerInfo? info, bool addReag { Text = Loc.GetString("chem-master-window-no-container-loaded-text") }); + return; } - else + + // Name of the container and its fill status (Ex: 44/100u) + control.Children.Add(new BoxContainer { - // Name of the container and its fill status (Ex: 44/100u) - control.Children.Add(new BoxContainer + Orientation = LayoutOrientation.Horizontal, + Children = { - Orientation = LayoutOrientation.Horizontal, - Children = + new Label { Text = $"{info.DisplayName}: " }, + new Label { - new Label {Text = $"{info.DisplayName}: "}, - new Label - { - Text = $"{info.CurrentVolume}/{info.MaxVolume}", - StyleClasses = {StyleNano.StyleClassLabelSecondaryColor} - } + Text = $"{info.CurrentVolume}/{info.MaxVolume}", + StyleClasses = { StyleNano.StyleClassLabelSecondaryColor } } - }); - - IEnumerable<(string Name, ReagentId Id, FixedPoint2 Quantity)> contents; + } + }); + // Initialises rowCount to allow for striped rows + var rowCount = 0; - if (info.Entities != null) + // Handle entities if they are not null + if (info.Entities != null) + { + foreach (var (id, quantity) in info.Entities.Select(x => (x.Id, x.Quantity))) { - contents = info.Entities.Select(x => (x.Id, default(ReagentId), x.Quantity)); + control.Children.Add(BuildReagentRow(default(Color), rowCount++, id, default(ReagentId), quantity, false, addReagentButtons)); } - else if (info.Reagents != null) - { - contents = info.Reagents.Select(x => - { - _prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto); - var name = proto?.LocalizedName - ?? Loc.GetString("chem-master-window-unknown-reagent-text"); + } - return (name, Id: x.Reagent, x.Quantity); - }) - .OrderBy(r => r.Item1); - } - else + // Handle reagents if they are not null + if (info.Reagents != null) + { + foreach (var reagent in info.Reagents) { - return; + _prototypeManager.TryIndex(reagent.Reagent.Prototype, out ReagentPrototype? proto); + var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); + var reagentColor = proto?.SubstanceColor ?? default(Color); + + control.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagent.Reagent, reagent.Quantity, false, addReagentButtons)); } - - - foreach (var (name, id, quantity) in contents) + } + } + /// + /// Take reagent/entity data and present rows, labels, and buttons appropriately. todo sprites? + /// + private Control BuildReagentRow(Color reagentColor, int rowCount, string name, ReagentId reagent, FixedPoint2 quantity, bool isBuffer, bool addReagentButtons) + { + //Colors rows and sets fallback for reagentcolor to the same as background, this will hide colorPanel for entities hopefully + var rowColor1 = Color.FromHex("#1B1B1E"); + var rowColor2 = Color.FromHex("#202025"); + var currentRowColor = (rowCount % 2 == 1) ? rowColor1 : rowColor2; + if ((reagentColor == default(Color))|(!addReagentButtons)) + { + reagentColor = currentRowColor; + } + //this calls the separated button builder, and stores the return to render after labels + var reagentButtonConstructors = CreateReagentTransferButtons(reagent, isBuffer, addReagentButtons); + + // Create the row layout with the color panel + var rowContainer = new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + Children = { - var inner = new BoxContainer + new Label { Text = $"{name}: " }, + new Label { - Orientation = LayoutOrientation.Horizontal, - Children = - { - new Label { Text = $"{name}: " }, - new Label - { - Text = $"{quantity}u", - StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }, - } - } - }; - - if (addReagentButtons) + Text = $"{quantity}u", + StyleClasses = { StyleNano.StyleClassLabelSecondaryColor } + }, + + // Padding + new Control { HorizontalExpand = true }, + // Colored panels for reagents + new PanelContainer { - var cs = inner.Children; - - // Padding - cs.Add(new Control { HorizontalExpand = true }); - - cs.Add(MakeReagentButton( - "1", ChemMasterReagentAmount.U1, id, false, StyleBase.ButtonOpenRight)); - cs.Add(MakeReagentButton( - "5", ChemMasterReagentAmount.U5, id, false, StyleBase.ButtonOpenBoth)); - cs.Add(MakeReagentButton( - "10", ChemMasterReagentAmount.U10, id, false, StyleBase.ButtonOpenBoth)); - cs.Add(MakeReagentButton( - "25", ChemMasterReagentAmount.U25, id, false, StyleBase.ButtonOpenBoth)); - cs.Add(MakeReagentButton( - "50", ChemMasterReagentAmount.U50, id, false, StyleBase.ButtonOpenBoth)); - cs.Add(MakeReagentButton( - "100", ChemMasterReagentAmount.U100, id, false, StyleBase.ButtonOpenBoth)); - cs.Add(MakeReagentButton( - Loc.GetString("chem-master-window-buffer-all-amount"), - ChemMasterReagentAmount.All, id, false, StyleBase.ButtonOpenLeft)); + Name = "colorPanel", + VerticalExpand = true, + MinWidth = 4, + PanelOverride = new StyleBoxFlat + { + BackgroundColor = reagentColor + }, + Margin = new Thickness(0, 1) } - - control.Children.Add(inner); } + }; - } - } - - public String LabelLine - { - get + // Add the reagent buttons after the color panel + foreach (var reagentTransferButton in reagentButtonConstructors) { - return LabelLineEdit.Text; + rowContainer.AddChild(reagentTransferButton); } - set + //Apply panencontainer to allow for striped rows + return new PanelContainer { - LabelLineEdit.Text = value; - } + PanelOverride = new StyleBoxFlat(currentRowColor), + Children = { rowContainer } + }; + } + + public string LabelLine + { + get => LabelLineEdit.Text; + set => LabelLineEdit.Text = value; } }