Rework the ChemMaster's output handling (#11207)

* Fix doc comment on FitsInDispenserComponent

It's clearly intended to be a doc comment, but wasn't.

* Allow the ChemMaster to accept canisters and bottles

* Give the ChemMaster an output container slot

* Tweak ChemMaster UI layout

* Make more ChemMaster UI tweaks

* Update ChemMaster SpinBox max handling

* Rework the ChemMaster

* Apply suggestions from code review

Co-authored-by: Flipp Syder <76629141+vulppine@users.noreply.github.com>

* Implement PR feedback

* Switch ChemMaster to a tabbed UI layout

* Rename Amount to Dosage for clarity

* Replace Amount with Dosage in messages

* Clarify dose in UI

Co-authored-by: Flipp Syder <76629141+vulppine@users.noreply.github.com>
This commit is contained in:
Illiux
2022-09-14 17:10:12 -07:00
committed by GitHub
parent f54c1cb2b3
commit 71e46de0fc
9 changed files with 472 additions and 247 deletions

View File

@@ -37,11 +37,19 @@ namespace Content.Client.Chemistry.UI
_window.OnClose += Close;
// Setup static button actions.
_window.EjectButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(SharedChemMaster.ContainerSlotName));
_window.BufferTransferButton.OnPressed += _ => SendMessage(new ChemMasterSetModeMessage(ChemMasterMode.Transfer));
_window.BufferDiscardButton.OnPressed += _ => SendMessage(new ChemMasterSetModeMessage(ChemMasterMode.Discard));
_window.CreatePillButton.OnPressed += _ => SendMessage(new ChemMasterCreatePillsMessage(((uint)_window.PillAmount.Value), _window.LabelLine));
_window.CreateBottleButton.OnPressed += _ => SendMessage(new ChemMasterCreateBottlesMessage(((uint)_window.BottleAmount.Value), _window.LabelLine));
_window.InputEjectButton.OnPressed += _ => SendMessage(
new ItemSlotButtonPressedEvent(SharedChemMaster.InputSlotName));
_window.OutputEjectButton.OnPressed += _ => SendMessage(
new ItemSlotButtonPressedEvent(SharedChemMaster.OutputSlotName));
_window.BufferTransferButton.OnPressed += _ => SendMessage(
new ChemMasterSetModeMessage(ChemMasterMode.Transfer));
_window.BufferDiscardButton.OnPressed += _ => SendMessage(
new ChemMasterSetModeMessage(ChemMasterMode.Discard));
_window.CreatePillButton.OnPressed += _ => SendMessage(
new ChemMasterCreatePillsMessage(
(uint)_window.PillDosage.Value, (uint)_window.PillNumber.Value, _window.LabelLine));
_window.CreateBottleButton.OnPressed += _ => SendMessage(
new ChemMasterOutputToBottleMessage((uint)_window.BottleDosage.Value, _window.LabelLine));
for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
{

View File

@@ -1,76 +1,106 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
MinSize="400 525"
MinSize="620 670"
Title="{Loc 'chem-master-bound-user-interface-title'}">
<BoxContainer Orientation="Vertical"
Margin="5 5 5 5"
SeparationOverride="10">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-container-label'}" />
<Control HorizontalExpand="True" />
<Button Name="EjectButton"
Access="Public"
Text="{Loc 'chem-master-window-eject-button'}" />
</BoxContainer>
<PanelContainer VerticalExpand="True"
SizeFlagsStretchRatio="6"
MinSize="0 200">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<!-- Initially empty, when server sends state data this will have container contents and fill volume.-->
<BoxContainer Name="ContainerInfo"
Orientation="Vertical"
HorizontalExpand="True">
<Label Text="{Loc 'chem-master-window-no-container-loaded-text'}" />
<TabContainer Name="Tabs" Margin="0 0 2 0">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
Margin="5 5 5 5"
SeparationOverride="10">
<!-- Input container info -->
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-container-label'}" />
<Control HorizontalExpand="True" />
<Button Name="InputEjectButton"
Access="Public"
Text="{Loc 'chem-master-window-eject-button'}" />
</BoxContainer>
</PanelContainer>
<Control MinSize="0 10" />
<!-- Buffer -->
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-buffer-text'}" />
<Control HorizontalExpand="True" />
<Button Name="BufferTransferButton"
Access="Public"
Text="{Loc 'chem-master-window-transfer-button'}" ToggleMode="True"
StyleClasses="OpenRight" />
<Button Name="BufferDiscardButton"
Access="Public"
Text="{Loc 'chem-master-window-discard-button'}" ToggleMode="True"
StyleClasses="OpenLeft" />
</BoxContainer>
<!-- Buffer info -->
<PanelContainer VerticalExpand="True" SizeFlagsStretchRatio="1" MinSize="0 150">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<!-- Buffer reagent list -->
<BoxContainer Name="BufferInfo"
Orientation="Vertical"
HorizontalExpand="True">
<Label Text="{Loc 'chem-master-window-buffer-empty-text'}" />
<PanelContainer VerticalExpand="True" MinSize="0 200">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<!-- Initially empty, when server sends state data this will have container contents and fill volume.-->
<BoxContainer Name="InputContainerInfo"
Orientation="Vertical"
Margin="4 4 4 4"
HorizontalExpand="True">
<Label Text="{Loc 'chem-master-window-no-container-loaded-text'}" />
</BoxContainer>
</PanelContainer>
<!-- Padding -->
<Control MinSize="0 10" />
<!-- Buffer -->
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-buffer-text'}" />
<Control HorizontalExpand="True" />
<Button Name="BufferTransferButton"
Access="Public"
Text="{Loc 'chem-master-window-transfer-button'}" ToggleMode="True"
StyleClasses="OpenRight" />
<Button Name="BufferDiscardButton"
Access="Public"
Text="{Loc 'chem-master-window-discard-button'}" ToggleMode="True"
StyleClasses="OpenLeft" />
</BoxContainer>
</PanelContainer>
<!-- Padding -->
<Control MinSize="0 10" />
<PanelContainer VerticalExpand="True" MinSize="100 100">
<!-- Buffer info -->
<PanelContainer VerticalExpand="True" MinSize="0 200">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<!-- Buffer reagent list -->
<BoxContainer Name="BufferInfo"
Orientation="Vertical"
Margin="4 4 4 4"
HorizontalExpand="True">
<Label Text="{Loc 'chem-master-window-buffer-empty-text'}" />
</BoxContainer>
</PanelContainer>
</BoxContainer>
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
Margin="5 5 5 5"
SeparationOverride="10">
<!-- Output container info -->
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-container-label'}" />
<Control HorizontalExpand="True" />
<Button Name="OutputEjectButton"
Access="Public"
Text="{Loc 'chem-master-window-eject-button'}" />
</BoxContainer>
<PanelContainer VerticalExpand="True" MinSize="0 200">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<!-- Initially empty, when server sends state data this will have container contents and fill volume.-->
<BoxContainer Name="OutputContainerInfo"
Orientation="Vertical"
Margin="4 4 4 4"
HorizontalExpand="True">
<Label Text="{Loc 'chem-master-window-no-container-loaded-text'}" />
</BoxContainer>
</PanelContainer>
<!-- Padding -->
<Control MinSize="0 10" />
<!-- Packaging -->
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-packaging-text'}" />
<Control HorizontalExpand="True"/>
<Label Text="{Loc 'chem-master-window-buffer-label'}" />
<Label Name="BufferCurrentVolume" StyleClasses="LabelSecondaryColor" />
</BoxContainer>
<!-- Wrap the packaging info-->
<PanelContainer VerticalExpand="True"
SizeFlagsStretchRatio="6"
MinSize="0 100">
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<!-- Packaging Info -->
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
SeparationOverride="5">
Margin="4 4 4 4"
HorizontalExpand="True"
VerticalExpand="True"
SeparationOverride="5">
<!-- Label for output -->
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-current-text-label'}" />
@@ -89,30 +119,38 @@
<Label Text="{Loc 'chem-master-window-pills-label'}" />
<Control HorizontalExpand="True"
MinSize="50 0" />
<SpinBox Name="PillAmount"
Access="Public"
Value="1" />
<Label Text="{Loc 'chem-master-window-pills-number-label'}"
Margin="5 0 0 0"
StyleClasses="LabelSecondaryColor" />
<SpinBox Name="PillNumber"
Access="Public"
Value="0" />
<Label Text="{Loc 'chem-master-window-dose-label'}"
Margin="5 0 0 0"
StyleClasses="LabelSecondaryColor" />
<SpinBox Name="PillDosage"
Access="Public"
Value="1" />
<Button Name="CreatePillButton"
Access="Public"
Text="{Loc 'chem-master-window-create-pill-button'}" />
<Label Text="{Loc 'chem-master-window-max-pills-volume-text'}"
StyleClasses="LabelSecondaryColor" />
Text="{Loc 'chem-master-window-create-button'}" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-bottles-label'}" />
<Control HorizontalExpand="True"
MinSize="50 0" />
<SpinBox Name="BottleAmount"
<Label Text="{Loc 'chem-master-window-dose-label'}"
Margin="5 0 0 0"
StyleClasses="LabelSecondaryColor" />
<SpinBox Name="BottleDosage"
Access="Public"
Value="1" />
Value="0" />
<Button Name="CreateBottleButton"
Access="Public"
Text="{Loc 'chem-master-window-create-bottle-button'}" />
<Label Text="{Loc 'chem-master-window-max-bottles-volume-text'}"
StyleClasses="LabelSecondaryColor" />
Text="{Loc 'chem-master-window-create-button'}" />
</BoxContainer>
</BoxContainer>
</PanelContainer>
</PanelContainer>
</BoxContainer>
</BoxContainer>
</TabContainer>
</DefaultWindow>

View File

@@ -75,8 +75,12 @@ namespace Content.Client.Chemistry.UI
Grid.AddChild(PillTypeButtons[i]);
}
PillAmount.InitDefaultButtons();
BottleAmount.InitDefaultButtons();
PillDosage.InitDefaultButtons();
PillNumber.InitDefaultButtons();
BottleDosage.InitDefaultButtons();
Tabs.SetTabTitle(0, Loc.GetString("chem-master-window-input-tab"));
Tabs.SetTabTitle(1, Loc.GetString("chem-master-window-output-tab"));
}
private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, string id, bool isBuffer, string styleClass)
@@ -98,14 +102,30 @@ namespace Content.Client.Chemistry.UI
if (castState.UpdateLabel)
LabelLine = GenerateLabel(castState);
UpdatePanelInfo(castState);
if (Contents.Children != null)
{
EjectButton.Disabled = !castState.HasContainer();
}
var output = castState.OutputContainerInfo;
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
InputEjectButton.Disabled = castState.InputContainerInfo is null;
OutputEjectButton.Disabled = output is null;
CreateBottleButton.Disabled = output is null || !output.HoldsReagents;
CreatePillButton.Disabled = output is null || output.HoldsReagents;
var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int();
var holdsReagents = output?.HoldsReagents ?? false;
var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
PillTypeButtons[castState.SelectedPillType].Pressed = true;
PillAmount.IsValid = x => x > 0 && x <= castState.PillProductionLimit;
BottleAmount.IsValid = x => x > 0 && x <= castState.BottleProductionLimit;
PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax;
PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit;
BottleDosage.IsValid = x => x >= 0 && x <= bottleAmountMax;
if (PillNumber.Value > pillNumberMax)
PillNumber.Value = pillNumberMax;
if (BottleDosage.Value > bottleAmountMax)
BottleDosage.Value = bottleAmountMax;
}
/// <summary>
@@ -134,64 +154,8 @@ namespace Content.Client.Chemistry.UI
BufferTransferButton.Pressed = state.Mode == Shared.Chemistry.ChemMasterMode.Transfer;
BufferDiscardButton.Pressed = state.Mode == Shared.Chemistry.ChemMasterMode.Discard;
ContainerInfo.Children.Clear();
if (!state.HasContainer())
{
ContainerInfo.Children.Add(new Label {Text = Loc.GetString("chem-master-window-no-container-loaded-text") });
}
else
{
ContainerInfo.Children.Add(new BoxContainer // Name of the container and its fill status (Ex: 44/100u)
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label {Text = $"{state.ContainerName}: "},
new Label
{
Text = $"{state.ContainerCurrentVolume}/{state.ContainerMaxVolume}",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
}
}
});
foreach (var reagent in state.ContainerReagents!.OrderBy(
r => {_prototypeManager.TryIndex(r.ReagentId, out ReagentPrototype? p); return p?.LocalizedName;}))
{
// Try to get the prototype for the given reagent. This gives us its name.
_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? proto);
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
if (proto != null)
{
ContainerInfo.Children.Add(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label {Text = $"{name}: "},
new Label
{
Text = $"{reagent.Quantity}u",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
},
// Padding
new Control {HorizontalExpand = true},
MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent.ReagentId, false, StyleBase.ButtonOpenRight),
MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent.ReagentId, false, StyleBase.ButtonOpenBoth),
MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent.ReagentId, false, StyleBase.ButtonOpenBoth),
MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent.ReagentId, false, StyleBase.ButtonOpenBoth),
MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent.ReagentId, false, StyleBase.ButtonOpenLeft),
}
});
}
}
}
BuildContainerUI(InputContainerInfo, state.InputContainerInfo, true);
BuildContainerUI(OutputContainerInfo, state.OutputContainerInfo, false);
BufferInfo.Children.Clear();
@@ -212,7 +176,7 @@ namespace Content.Client.Chemistry.UI
bufferHBox.AddChild(bufferLabel);
var bufferVol = new Label
{
Text = $"{state.BufferCurrentVolume}",
Text = $"{state.BufferCurrentVolume}u",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
};
bufferHBox.AddChild(bufferVol);
@@ -251,6 +215,92 @@ namespace Content.Client.Chemistry.UI
}
}
private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons)
{
control.Children.Clear();
if (info is null)
{
control.Children.Add(new Label
{
Text = Loc.GetString("chem-master-window-no-container-loaded-text")
});
}
else
{
// Name of the container and its fill status (Ex: 44/100u)
control.Children.Add(new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label {Text = $"{info.DisplayName}: "},
new Label
{
Text = $"{info.CurrentVolume}/{info.MaxVolume}",
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
}
}
});
var contents = info.Contents
.Select(lineItem =>
{
if (!info.HoldsReagents)
return (lineItem.Id, lineItem.Id, lineItem.Quantity);
// Try to get the prototype for the given reagent. This gives us its name.
_prototypeManager.TryIndex(lineItem.Id, out ReagentPrototype? proto);
var name = proto?.LocalizedName
?? Loc.GetString("chem-master-window-unknown-reagent-text");
return (name, lineItem.Id, lineItem.Quantity);
})
.OrderBy(r => r.Item1);
foreach (var (name, id, quantity) in contents)
{
var inner = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
Children =
{
new Label { Text = $"{name}: " },
new Label
{
Text = $"{quantity}u",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor },
}
}
};
if (addReagentButtons)
{
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(
Loc.GetString("chem-master-window-buffer-all-amount"),
ChemMasterReagentAmount.All, id, false, StyleBase.ButtonOpenLeft));
}
control.Children.Add(inner);
}
}
}
public String LabelLine
{
get