Device Linking and better linking ui (#13645)

Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
Co-authored-by: Visne <39844191+Visne@users.noreply.github.com>
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Julian Giebel
2023-05-07 08:07:24 +02:00
committed by GitHub
parent 2fe7055de6
commit 6ebd784cb6
100 changed files with 2096 additions and 342 deletions

View File

@@ -1,5 +1,5 @@
using Content.Shared.DeviceNetwork;
using JetBrains.Annotations;
using Content.Client.NetworkConfigurator.Systems;
using Content.Shared.DeviceNetwork;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
@@ -8,17 +8,18 @@ namespace Content.Client.NetworkConfigurator;
public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IEntityManager _entityManager = default!;
private NetworkConfiguratorListMenu? _listMenu;
private NetworkConfiguratorConfigurationMenu? _configurationMenu;
private NetworkConfiguratorLinkMenu? _linkMenu;
private NetworkConfiguratorSystem _netConfig;
private DeviceListSystem _deviceList;
public NetworkConfiguratorBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
_netConfig = _entityManager.System<NetworkConfiguratorSystem>();
_deviceList = _entityManager.System<DeviceListSystem>();
}
public void OnRemoveButtonPressed(string address)
@@ -36,7 +37,7 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
_listMenu = new NetworkConfiguratorListMenu(this);
_listMenu.OnClose += Close;
_listMenu.ClearButton.OnPressed += _ => OnClearButtonPressed();
_listMenu.OpenCentered();
_listMenu.OpenCenteredRight();
break;
case NetworkConfiguratorUiKey.Configure:
_configurationMenu = new NetworkConfiguratorConfigurationMenu();
@@ -50,6 +51,11 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
_configurationMenu.Show.Pressed = _netConfig.ConfiguredListIsTracked(Owner.Owner);
_configurationMenu.OpenCentered();
break;
case NetworkConfiguratorUiKey.Link:
_linkMenu = new NetworkConfiguratorLinkMenu(this);
_linkMenu.OnClose += Close;
_linkMenu.OpenCentered();
break;
}
}
@@ -70,6 +76,9 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
case DeviceListUserInterfaceState listState:
_configurationMenu?.UpdateState(listState);
break;
case DeviceLinkUserInterfaceState linkState:
_linkMenu?.UpdateState(linkState);
break;
}
}

View File

@@ -1,7 +1,7 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator"
Title="Network Configurator" MinSize="350 100">
Title="{Loc 'network-configurator-title-device-configuration'}" MinSize="350 100">
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" />
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1">
@@ -15,6 +15,6 @@
<Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
</BoxContainer>
<Label Name="Count" HorizontalAlignment="Right" />
<Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -0,0 +1,47 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator"
Title="Network Configurator" MinSize="620 400" RectClipContent="True">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Margin="12 0" Text="{Loc signal-port-selector-help}"/>
</BoxContainer>
<Control VerticalExpand="True" HorizontalExpand="True" Margin="12 6 12 0">
<PanelContainer Name="MainPanel" HorizontalExpand="False" VerticalExpand="True" />
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True" Margin="6">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.3">
<RichTextLabel Name="HeaderLeft"/>
<BoxContainer Name="ButtonContainerLeft" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"/>
</BoxContainer>
<BoxContainer Name="MiddleContainer" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SizeFlagsStretchRatio="0.2"/>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.3">
<RichTextLabel Name="HeaderRight"/>
<BoxContainer Name="ButtonContainerRight" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"/>
</BoxContainer>
</BoxContainer>
</ScrollContainer>
</Control>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="12 4">
<BoxContainer Orientation="Horizontal">
<Button Name="ButtonClear" SetHeight="32" StyleClasses="OpenRight" Text="{Loc signal-port-selector-menu-clear}"/>
<Button Name="ButtonLinkDefault" SetHeight="32" StyleClasses="OpenLeft" Text="{Loc signal-port-selector-menu-link-defaults}"/>
</BoxContainer>
<Control HorizontalExpand="True"/>
<Control>
<Button Name="ButtonOk" MinHeight="26" StyleClasses="ButtonColorGreen" Text="{Loc signal-port-selector-menu-done}"></Button>
</Control>
</BoxContainer>
<Control SetHeight="28" Margin="1 0 2 1">
<PanelContainer Name="FooterPanel"></PanelContainer>
<BoxContainer Name="ContentFooter" HorizontalExpand="True" SetHeight="28">
<Label Text="Net#Link ™" VerticalAlignment="Center" Margin="6 0" StyleClasses="PDAContentFooterText"/>
<Label Name="AddressLabel" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="6 0" StyleClasses="PDAContentFooterText"/>
<Control HorizontalExpand="True"/>
<Label Name="FromAddressLabel" Margin="6 0" StyleClasses="PDAContentFooterText"/>
<Label SetWidth="25" StyleClasses="PDAContentFooterText" Align="Center" Text="➝"/><!--Turn this into an arrow texture-->
<Label Name="ToAddressLabel" Margin="6 0" StyleClasses="PDAContentFooterText"/>
</BoxContainer>
</Control>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -0,0 +1,221 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Vector2 = Robust.Shared.Maths.Vector2;
namespace Content.Client.NetworkConfigurator;
[GenerateTypedNameReferences]
public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
{
private const string PanelBgColor = "#202023";
private readonly LinksRender _links;
private readonly List<SourcePortPrototype> _sources = new();
private readonly List<SinkPortPrototype> _sinks = new();
private readonly NetworkConfiguratorBoundUserInterface _userInterface;
private (ButtonPosition position, string id, int index)? _selectedButton;
private List<(string left, string right)>? _defaults;
public NetworkConfiguratorLinkMenu(NetworkConfiguratorBoundUserInterface userInterface)
{
_userInterface = userInterface;
RobustXamlLoader.Load(this);
var footerStyleBox = new StyleBoxFlat()
{
BorderThickness = new Thickness(0, 2, 0, 0),
BorderColor = Color.FromHex("#5A5A5A")
};
FooterPanel.PanelOverride = footerStyleBox;
MainPanel.PanelOverride = new StyleBoxFlat(Color.FromHex(PanelBgColor));
ButtonClear.AddStyleClass("ButtonColorRed");
ButtonLinkDefault.Disabled = true;
_links = new LinksRender(ButtonContainerLeft, ButtonContainerRight);
_links.VerticalExpand = true;
MiddleContainer.AddChild(_links);
ButtonOk.OnPressed += _ => Close();
ButtonLinkDefault.OnPressed += _ => LinkDefaults();
ButtonClear.OnPressed += _ => _userInterface.SendMessage(new NetworkConfiguratorClearLinksMessage());
}
public void UpdateState(DeviceLinkUserInterfaceState linkState)
{
ButtonContainerLeft.RemoveAllChildren();
ButtonContainerRight.RemoveAllChildren();
_sources.Clear();
_sources.AddRange(linkState.Sources);
_links.SourceButtons.Clear();
var i = 0;
foreach (var source in _sources)
{
var button = CreateButton(ButtonPosition.Left, source.Name, source.Description, source.ID, i);
ButtonContainerLeft.AddChild(button);
_links.SourceButtons.Add(source.ID, button);
i++;
}
_sinks.Clear();
_sinks.AddRange(linkState.Sinks);
_links.SinkButtons.Clear();
i = 0;
foreach (var sink in _sinks)
{
var button = CreateButton(ButtonPosition.Right, sink.Name, sink.Description, sink.ID, i);
ButtonContainerRight.AddChild(button);
_links.SinkButtons.Add(sink.ID, button);
i++;
}
_links.Links.Clear();
_links.Links.AddRange(linkState.Links);
_defaults = linkState.Defaults;
ButtonLinkDefault.Disabled = _defaults == default;
FromAddressLabel.Text = linkState.SourceAddress;
ToAddressLabel.Text = linkState.SinkAddress;
}
private void LinkDefaults()
{
if (_defaults == default)
return;
_userInterface.SendMessage(new NetworkConfiguratorLinksSaveMessage(_defaults));
}
private Button CreateButton(ButtonPosition position, string name, string description, string id, int index)
{
var button = new Button();
button.AddStyleClass("OpenBoth");
button.Text = Loc.GetString(name);
button.ToolTip = Loc.GetString(description);
button.ToggleMode = true;
button.OnPressed += args => OnButtonPressed(args, position, id, index);
return button;
}
private void OnButtonPressed(BaseButton.ButtonEventArgs args, ButtonPosition position, string id, int index)
{
var key = (position, id, index);
if (_selectedButton == key)
{
args.Button.Pressed = false;
_selectedButton = null;
return;
}
if (!_selectedButton.HasValue)
{
args.Button.Pressed = true;
_selectedButton = key;
return;
}
if (_selectedButton.Value.position == position)
{
args.Button.Pressed = false;
return;
}
var left = _selectedButton.Value.position == ButtonPosition.Left ? _selectedButton.Value.id : id;
var right = _selectedButton.Value.position == ButtonPosition.Left ? id : _selectedButton.Value.id;
_userInterface.SendMessage(new NetworkConfiguratorToggleLinkMessage(left, right));
args.Button.Pressed = false;
var container = _selectedButton.Value.position == ButtonPosition.Left ? ButtonContainerLeft : ButtonContainerRight;
if (container.GetChild(_selectedButton.Value.index) is Button button)
button.Pressed = false;
_selectedButton = null;
}
private enum ButtonPosition
{
Left,
Right
}
/// <summary>
/// Draws lines between linked ports using bezier curve calculated with polynomial coefficients
/// See: https://youtu.be/jvPPXbo87ds?t=351
/// </summary>
private sealed class LinksRender : Control
{
public readonly List<(string, string)> Links = new();
public readonly Dictionary<string, Button> SourceButtons = new();
public readonly Dictionary<string, Button> SinkButtons = new();
private readonly BoxContainer _leftButtonContainer;
private readonly BoxContainer _rightButtonContainer;
public LinksRender(BoxContainer leftButtonContainer, BoxContainer rightButtonContainer)
{
_leftButtonContainer = leftButtonContainer;
_rightButtonContainer = rightButtonContainer;
}
protected override void Draw(DrawingHandleScreen handle)
{
foreach (var (left, right) in Links)
{
if (!SourceButtons.TryGetValue(left, out var leftChild) || !SinkButtons.TryGetValue(right, out var rightChild))
continue;
var leftOffset = _leftButtonContainer.PixelPosition.Y;
var rightOffset = _rightButtonContainer.PixelPosition.Y;
var y1 = leftChild.PixelPosition.Y + leftChild.PixelHeight / 2 + leftOffset;
var y2 = rightChild.PixelPosition.Y + rightChild.PixelHeight / 2 + rightOffset;
if (left == right)
{
handle.DrawLine((0, y1), (PixelWidth, y2), Color.Cyan);
continue;
}
var controls = new List<Vector2>
{
new(0, y1),
new(30, y1),
new(PixelWidth - 30, y2),
new(PixelWidth, y2),
};
//Calculate coefficients
var c0 = controls[0];
var c1 = controls[0] * -3 + controls[1] * 3;
var c2 = controls[0] * 3 + controls[1] * -6 + controls[2] * 3;
var c3 = controls[0] * -1 + controls[1] * 3 + controls[2] * -3 + controls[3];
var points = new List<Vector2>();
//Calculate points using coefficients
for (float t = 0; t <= 1; t += 0.0001f)
{
var point = c0 + c1 * t + c2 * (t * t) + c3 * (t * t * t);
points.Add(point);
}
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, points.ToArray(), Color.Cyan);
}
}
}
}

View File

@@ -1,4 +1,6 @@
using Content.Client.NetworkConfigurator.Systems;
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Random;

View File

@@ -1,7 +1,7 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator"
Title="Network Configurator" MinSize="220 400">
Title="{Loc 'network-configurator-title-saved-devices'}" MinSize="220 400">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" />
<BoxContainer Orientation="Horizontal" Margin="8 8 8 8">

View File

@@ -1,8 +1,6 @@
using System.Linq;
using Content.Shared.DeviceNetwork;
using Robust.Client.Graphics;
namespace Content.Client.NetworkConfigurator;
namespace Content.Client.NetworkConfigurator.Systems;
public sealed class DeviceListSystem : SharedDeviceListSystem
{

View File

@@ -1,15 +1,22 @@
using System.Linq;
using Content.Client.Actions;
using Content.Shared.Actions;
using Content.Client.Items;
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Shared.Actions.ActionTypes;
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Input;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Robust.Shared.Timing;
namespace Content.Client.NetworkConfigurator;
namespace Content.Client.NetworkConfigurator.Systems;
public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
{
@@ -17,6 +24,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private const string Action = "ClearNetworkLinkOverlays";
@@ -25,6 +33,13 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
base.Initialize();
SubscribeLocalEvent<ClearAllOverlaysEvent>(_ => ClearAllOverlays());
SubscribeLocalEvent<NetworkConfiguratorComponent, ItemStatusCollectMessage>(OnCollectItemStatus);
}
private void OnCollectItemStatus(EntityUid uid, NetworkConfiguratorComponent configurator, ItemStatusCollectMessage args)
{
_inputManager.TryGetKeyBinding((ContentKeyFunctions.AltUseItemInHand), out var binding);
args.Controls.Add(new StatusControl(configurator, binding?.GetKeyString() ?? ""));
}
public bool ConfiguredListIsTracked(EntityUid uid, NetworkConfiguratorComponent? component = null)
@@ -102,6 +117,41 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
component.ActiveDeviceList = list;
}
private sealed class StatusControl : Control
{
private readonly RichTextLabel _label;
private readonly NetworkConfiguratorComponent _configurator;
private readonly string _keyBindingName;
private bool? _linkModeActive = null;
public StatusControl(NetworkConfiguratorComponent configurator, string keyBindingName)
{
_configurator = configurator;
_keyBindingName = keyBindingName;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
AddChild(_label);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_linkModeActive != null && _linkModeActive == _configurator.LinkModeActive)
return;
_linkModeActive = _configurator.LinkModeActive;
var modeLocString = _linkModeActive??false
? "network-configurator-examine-mode-link"
: "network-configurator-examine-mode-list";
_label.SetMarkup(Loc.GetString("network-configurator-item-status-label",
("mode", Loc.GetString(modeLocString)),
("keybinding", _keyBindingName)));
}
}
}
public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand