Minor UI refactor (#11212)
This commit is contained in:
151
Content.Client/UserInterface/Controls/MainViewport.cs
Normal file
151
Content.Client/UserInterface/Controls/MainViewport.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using Content.Client.Viewport;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for <see cref="ScalingViewport"/> that listens to configuration variables.
|
||||
/// Also does NN-snapping within tolerances.
|
||||
/// </summary>
|
||||
public sealed class MainViewport : Control
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ViewportManager _vpManager = default!;
|
||||
|
||||
public ScalingViewport Viewport { get; }
|
||||
|
||||
public MainViewport()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Viewport = new ScalingViewport
|
||||
{
|
||||
AlwaysRender = true,
|
||||
RenderScaleMode = ScalingViewportRenderScaleMode.CeilInt,
|
||||
MouseFilter = MouseFilterMode.Stop
|
||||
};
|
||||
|
||||
AddChild(Viewport);
|
||||
}
|
||||
|
||||
protected override void EnteredTree()
|
||||
{
|
||||
base.EnteredTree();
|
||||
|
||||
_vpManager.AddViewport(this);
|
||||
}
|
||||
|
||||
protected override void ExitedTree()
|
||||
{
|
||||
base.ExitedTree();
|
||||
|
||||
_vpManager.RemoveViewport(this);
|
||||
}
|
||||
|
||||
public void UpdateCfg()
|
||||
{
|
||||
var stretch = _cfg.GetCVar(CCVars.ViewportStretch);
|
||||
var renderScaleUp = _cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||
var fixedFactor = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||
|
||||
if (stretch)
|
||||
{
|
||||
var snapFactor = CalcSnappingFactor();
|
||||
if (snapFactor == null)
|
||||
{
|
||||
// Did not find a snap, enable stretching.
|
||||
Viewport.FixedStretchSize = null;
|
||||
Viewport.StretchMode = ScalingViewportStretchMode.Bilinear;
|
||||
|
||||
if (renderScaleUp)
|
||||
{
|
||||
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.CeilInt;
|
||||
}
|
||||
else
|
||||
{
|
||||
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||
Viewport.FixedRenderScale = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Found snap, set fixed factor and run non-stretching code.
|
||||
fixedFactor = snapFactor.Value;
|
||||
}
|
||||
|
||||
Viewport.FixedStretchSize = Viewport.ViewportSize * fixedFactor;
|
||||
Viewport.StretchMode = ScalingViewportStretchMode.Nearest;
|
||||
|
||||
if (renderScaleUp)
|
||||
{
|
||||
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||
Viewport.FixedRenderScale = fixedFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Snapping but forced to render scale at scale 1 so...
|
||||
// At least we can NN.
|
||||
Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
|
||||
Viewport.FixedRenderScale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private int? CalcSnappingFactor()
|
||||
{
|
||||
// Margin tolerance is tolerance of "the window is too big"
|
||||
// where we add a margin to the viewport to make it fit.
|
||||
var cfgToleranceMargin = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin);
|
||||
// Clip tolerance is tolerance of "the window is too small"
|
||||
// where we are clipping the viewport to make it fit.
|
||||
var cfgToleranceClip = _cfg.GetCVar(CCVars.ViewportSnapToleranceClip);
|
||||
|
||||
// Calculate if the viewport, when rendered at an integer scale,
|
||||
// is close enough to the control size to enable "snapping" to NN,
|
||||
// potentially cutting a tiny bit off/leaving a margin.
|
||||
//
|
||||
// Idea here is that if you maximize the window at 1080p or 1440p
|
||||
// we are close enough to an integer scale (2x and 3x resp) that we should "snap" to it.
|
||||
|
||||
// Just do it iteratively.
|
||||
// I'm sure there's a smarter approach that needs one try with math but I'm dumb.
|
||||
for (var i = 1; i <= 10; i++)
|
||||
{
|
||||
var toleranceMargin = i * cfgToleranceMargin;
|
||||
var toleranceClip = i * cfgToleranceClip;
|
||||
var scaled = (Vector2) Viewport.ViewportSize * i;
|
||||
var (dx, dy) = PixelSize - scaled;
|
||||
|
||||
// The rule for which snap fits is that at LEAST one axis needs to be in the tolerance size wise.
|
||||
// One axis MAY be larger but not smaller than tolerance.
|
||||
// Obviously if it's too small it's bad, and if it's too big on both axis we should stretch up.
|
||||
if (Fits(dx) && Fits(dy) || Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy))
|
||||
{
|
||||
// Found snap that fits.
|
||||
return i;
|
||||
}
|
||||
|
||||
bool Larger(float a)
|
||||
{
|
||||
return a > toleranceMargin;
|
||||
}
|
||||
|
||||
bool Fits(float a)
|
||||
{
|
||||
return a <= toleranceMargin && a >= -toleranceClip;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void Resized()
|
||||
{
|
||||
base.Resized();
|
||||
|
||||
UpdateCfg();
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Content.Client/UserInterface/Controls/NanoHeading.cs
Normal file
31
Content.Client/UserInterface/Controls/NanoHeading.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
public sealed class NanoHeading : Container
|
||||
{
|
||||
private readonly Label _label;
|
||||
private readonly PanelContainer _panel;
|
||||
|
||||
public NanoHeading()
|
||||
{
|
||||
_panel = new PanelContainer
|
||||
{
|
||||
Children = {(_label = new Label
|
||||
{
|
||||
StyleClasses = {StyleNano.StyleClassLabelHeading}
|
||||
})}
|
||||
};
|
||||
AddChild(_panel);
|
||||
|
||||
HorizontalAlignment = HAlignment.Left;
|
||||
}
|
||||
|
||||
public string? Text
|
||||
{
|
||||
get => _label.Text;
|
||||
set => _label.Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Content.Client/UserInterface/Controls/ProgressTextureRect.cs
Normal file
20
Content.Client/UserInterface/Controls/ProgressTextureRect.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Content.Client.DoAfter;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
public sealed class ProgressTextureRect : TextureRect
|
||||
{
|
||||
public float Progress;
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
var dims = Texture != null ? GetDrawDimensions(Texture) : UIBox2.FromDimensions(Vector2.Zero, PixelSize);
|
||||
dims.Top = Math.Max(dims.Bottom - dims.Bottom * Progress,0);
|
||||
handle.DrawRect(dims, DoAfterOverlay.GetProgressColor(Progress));
|
||||
|
||||
base.Draw(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Content.Client/UserInterface/Controls/StripeBack.cs
Normal file
123
Content.Client/UserInterface/Controls/StripeBack.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
public sealed class StripeBack : Container
|
||||
{
|
||||
private const float PadSize = 4;
|
||||
private const float EdgeSize = 2;
|
||||
private static readonly Color EdgeColor = Color.FromHex("#525252ff");
|
||||
|
||||
private bool _hasTopEdge = true;
|
||||
private bool _hasBottomEdge = true;
|
||||
private bool _hasMargins = true;
|
||||
|
||||
public const string StylePropertyBackground = "background";
|
||||
|
||||
public bool HasTopEdge
|
||||
{
|
||||
get => _hasTopEdge;
|
||||
set
|
||||
{
|
||||
_hasTopEdge = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasBottomEdge
|
||||
{
|
||||
get => _hasBottomEdge;
|
||||
set
|
||||
{
|
||||
_hasBottomEdge = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasMargins
|
||||
{
|
||||
get => _hasMargins;
|
||||
set
|
||||
{
|
||||
_hasMargins = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
var padSize = HasMargins ? PadSize : 0;
|
||||
var padSizeTotal = 0f;
|
||||
|
||||
if (HasBottomEdge)
|
||||
padSizeTotal += padSize + EdgeSize;
|
||||
if (HasTopEdge)
|
||||
padSizeTotal += padSize + EdgeSize;
|
||||
|
||||
var size = Vector2.Zero;
|
||||
|
||||
availableSize.Y -= padSizeTotal;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.Measure(availableSize);
|
||||
size = Vector2.ComponentMax(size, child.DesiredSize);
|
||||
}
|
||||
|
||||
return size + (0, padSizeTotal);
|
||||
}
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
var box = new UIBox2(Vector2.Zero, finalSize);
|
||||
|
||||
var padSize = HasMargins ? PadSize : 0;
|
||||
|
||||
if (HasTopEdge)
|
||||
{
|
||||
box += (0, padSize + EdgeSize, 0, 0);
|
||||
}
|
||||
|
||||
if (HasBottomEdge)
|
||||
{
|
||||
box += (0, 0, 0, -(padSize + EdgeSize));
|
||||
}
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.Arrange(box);
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
UIBox2 centerBox = PixelSizeBox;
|
||||
|
||||
var padSize = HasMargins ? PadSize : 0;
|
||||
|
||||
if (HasTopEdge)
|
||||
{
|
||||
centerBox += (0, (padSize + EdgeSize) * UIScale, 0, 0);
|
||||
handle.DrawRect(new UIBox2(0, padSize * UIScale, PixelWidth, centerBox.Top), EdgeColor);
|
||||
}
|
||||
|
||||
if (HasBottomEdge)
|
||||
{
|
||||
centerBox += (0, 0, 0, -((padSize + EdgeSize) * UIScale));
|
||||
handle.DrawRect(new UIBox2(0, centerBox.Bottom, PixelWidth, PixelHeight - padSize * UIScale),
|
||||
EdgeColor);
|
||||
}
|
||||
|
||||
GetActualStyleBox()?.Draw(handle, centerBox);
|
||||
}
|
||||
|
||||
private StyleBox? GetActualStyleBox()
|
||||
{
|
||||
return TryGetStyleProperty(StylePropertyBackground, out StyleBox? box) ? box : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.UserInterface.Atmos.GasTank
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class GasTankBoundUserInterface
|
||||
@@ -7,12 +7,9 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.UserInterface.Atmos.GasTank
|
||||
namespace Content.Client.UserInterface.Systems.Atmos.GasTank
|
||||
{
|
||||
public sealed class GasTankWindow
|
||||
: BaseWindow
|
||||
@@ -0,0 +1,15 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'ghost-target-window-title'}"
|
||||
MinSize="450 450"
|
||||
SetSize="450 450">
|
||||
<ScrollContainer VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
HScrollEnabled="False">
|
||||
<BoxContainer Name="ButtonContainer"
|
||||
Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
SeparationOverride="5">
|
||||
<!-- Target buttons get added here by code -->
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GhostTargetWindow : DefaultWindow
|
||||
{
|
||||
private readonly IEntityNetworkManager _netManager;
|
||||
|
||||
private List<(string, EntityUid)> _warps = new();
|
||||
|
||||
public GhostTargetWindow(IEntityNetworkManager netManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_netManager = netManager;
|
||||
}
|
||||
|
||||
public void UpdateWarps(IEnumerable<GhostWarp> warps)
|
||||
{
|
||||
// Server COULD send these sorted but how about we just use the client to do it instead
|
||||
_warps = warps
|
||||
.OrderBy(w => w.IsWarpPoint)
|
||||
.ThenBy(w => w.DisplayName, Comparer<string>.Create(
|
||||
(x, y) => string.Compare(x, y, StringComparison.Ordinal)))
|
||||
.Select(w =>
|
||||
{
|
||||
var name = w.IsWarpPoint
|
||||
? Loc.GetString("ghost-target-window-current-button", ("name", w.DisplayName))
|
||||
: w.DisplayName;
|
||||
|
||||
return (name, w.Entity);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void Populate()
|
||||
{
|
||||
ButtonContainer.DisposeAllChildren();
|
||||
AddButtons();
|
||||
}
|
||||
|
||||
private void AddButtons()
|
||||
{
|
||||
foreach (var (name, warp) in _warps)
|
||||
{
|
||||
var currentButtonRef = new Button
|
||||
{
|
||||
Text = name,
|
||||
TextAlign = Label.AlignMode.Right,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
MinSize = (340, 20),
|
||||
ClipText = true,
|
||||
};
|
||||
|
||||
currentButtonRef.OnPressed += _ =>
|
||||
{
|
||||
var msg = new GhostWarpToTargetRequestEvent(warp);
|
||||
_netManager.SendSystemNetworkMessage(msg);
|
||||
};
|
||||
|
||||
ButtonContainer.AddChild(currentButtonRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
Orientation="Horizontal">
|
||||
<Button Name="RequestButton"
|
||||
Access="Public"
|
||||
Text="{Loc 'ghost-roles-window-request-role-button'}"
|
||||
StyleClasses="OpenRight"
|
||||
HorizontalAlignment="Left"/>
|
||||
<Button Name="FollowButton"
|
||||
Access="Public"
|
||||
Text="{Loc 'ghost-roles-window-follow-role-button'}"
|
||||
StyleClasses="OpenLeft"
|
||||
HorizontalAlignment="Right"/>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GhostRoleEntryButtons : BoxContainer
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'ghost-roles-window-title'}">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True">
|
||||
<RichTextLabel Name="TopBanner" VerticalExpand="True"/>
|
||||
<Button Name="RequestButton"
|
||||
Text="{Loc 'ghost-roles-window-request-role-button'}"
|
||||
Disabled="True"
|
||||
TextAlign="Center"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,52 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GhostRoleRulesWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = IoCManager.Resolve<IConfigurationManager>();
|
||||
private float _timer;
|
||||
|
||||
public GhostRoleRulesWindow(string rules, Action<BaseButton.ButtonEventArgs> requestAction)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
var ghostRoleTime = _cfg.GetCVar(CCVars.GhostRoleTime);
|
||||
_timer = ghostRoleTime;
|
||||
|
||||
if (ghostRoleTime > 0f)
|
||||
{
|
||||
RequestButton.Text = Loc.GetString("ghost-roles-window-request-role-button-timer", ("time", $"{_timer:0.0}"));
|
||||
TopBanner.SetMessage(FormattedMessage.FromMarkupPermissive(rules + "\n" + Loc.GetString("ghost-roles-window-rules-footer", ("time", ghostRoleTime))));
|
||||
RequestButton.Disabled = true;
|
||||
}
|
||||
|
||||
RequestButton.OnPressed += requestAction;
|
||||
}
|
||||
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
if (!RequestButton.Disabled) return;
|
||||
if (_timer > 0.0)
|
||||
{
|
||||
_timer -= args.DeltaSeconds;
|
||||
RequestButton.Text = Loc.GetString("ghost-roles-window-request-role-button-timer", ("time", $"{_timer:0.0}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
RequestButton.Disabled = false;
|
||||
RequestButton.Text = Loc.GetString("ghost-roles-window-request-role-button");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
Margin="0 0 8 8">
|
||||
<Label Name="Title"
|
||||
StyleClasses="LabelKeyText"/>
|
||||
<PanelContainer StyleClasses="HighDivider" />
|
||||
<RichTextLabel Name="Description"
|
||||
Margin="0 4"/>
|
||||
<BoxContainer Name="Buttons"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Vertical"
|
||||
SeparationOverride="5">
|
||||
<!-- Buttons are added here by code -->
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GhostRolesEntry : BoxContainer
|
||||
{
|
||||
public event Action<GhostRoleInfo>? OnRoleSelected;
|
||||
public event Action<GhostRoleInfo>? OnRoleFollow;
|
||||
|
||||
public GhostRolesEntry(string name, string description, IEnumerable<GhostRoleInfo> roles)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
Title.Text = name;
|
||||
Description.SetMessage(description);
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
var button = new GhostRoleEntryButtons();
|
||||
button.RequestButton.OnPressed += _ => OnRoleSelected?.Invoke(role);
|
||||
button.FollowButton.OnPressed += _ => OnRoleFollow?.Invoke(role);
|
||||
|
||||
Buttons.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class GhostRolesEui : BaseEui
|
||||
{
|
||||
private readonly GhostRolesWindow _window;
|
||||
private GhostRoleRulesWindow? _windowRules = null;
|
||||
private uint _windowRulesId = 0;
|
||||
|
||||
public GhostRolesEui()
|
||||
{
|
||||
_window = new GhostRolesWindow();
|
||||
|
||||
_window.OnRoleRequested += info =>
|
||||
{
|
||||
if (_windowRules != null)
|
||||
_windowRules.Close();
|
||||
_windowRules = new GhostRoleRulesWindow(info.Rules, _ =>
|
||||
{
|
||||
SendMessage(new GhostRoleTakeoverRequestMessage(info.Identifier));
|
||||
});
|
||||
_windowRulesId = info.Identifier;
|
||||
_windowRules.OnClose += () =>
|
||||
{
|
||||
_windowRules = null;
|
||||
};
|
||||
_windowRules.OpenCentered();
|
||||
};
|
||||
|
||||
_window.OnRoleFollow += info =>
|
||||
{
|
||||
SendMessage(new GhostRoleFollowRequestMessage(info.Identifier));
|
||||
};
|
||||
|
||||
_window.OnClose += () =>
|
||||
{
|
||||
SendMessage(new GhostRoleWindowCloseMessage());
|
||||
};
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
_window.Close();
|
||||
_windowRules?.Close();
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
base.HandleState(state);
|
||||
|
||||
if (state is not GhostRolesEuiState ghostState) return;
|
||||
_window.ClearEntries();
|
||||
|
||||
var groupedRoles = ghostState.GhostRoles.GroupBy(
|
||||
role => (role.Name, role.Description));
|
||||
foreach (var group in groupedRoles)
|
||||
{
|
||||
var name = group.Key.Name;
|
||||
var description = group.Key.Description;
|
||||
|
||||
_window.AddEntry(name, description, group);
|
||||
}
|
||||
|
||||
var closeRulesWindow = ghostState.GhostRoles.All(role => role.Identifier != _windowRulesId);
|
||||
if (closeRulesWindow)
|
||||
{
|
||||
_windowRules?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'ghost-roles-window-title'}"
|
||||
MinSize="375 275">
|
||||
<Label Name="NoRolesMessage"
|
||||
Text="{Loc 'ghost-roles-window-no-roles-available-label'}"
|
||||
VerticalAlignment="Top" />
|
||||
<ScrollContainer VerticalExpand="True"
|
||||
HScrollEnabled="False">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
Name="EntryContainer"
|
||||
VerticalExpand="True">
|
||||
<!-- Ghost role entries are added here by code -->
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GhostRolesWindow : DefaultWindow
|
||||
{
|
||||
public event Action<GhostRoleInfo>? OnRoleRequested;
|
||||
public event Action<GhostRoleInfo>? OnRoleFollow;
|
||||
|
||||
public void ClearEntries()
|
||||
{
|
||||
NoRolesMessage.Visible = true;
|
||||
EntryContainer.DisposeAllChildren();
|
||||
}
|
||||
|
||||
public void AddEntry(string name, string description, IEnumerable<GhostRoleInfo> roles)
|
||||
{
|
||||
NoRolesMessage.Visible = false;
|
||||
|
||||
var entry = new GhostRolesEntry(name, description, roles);
|
||||
entry.OnRoleSelected += OnRoleRequested;
|
||||
entry.OnRoleFollow += OnRoleFollow;
|
||||
EntryContainer.AddChild(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Content.Client.Eui;
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class MakeGhostRoleEui : BaseEui
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
|
||||
private readonly MakeGhostRoleWindow _window;
|
||||
|
||||
public MakeGhostRoleEui()
|
||||
{
|
||||
_window = new MakeGhostRoleWindow();
|
||||
|
||||
|
||||
_window.OnClose += OnClose;
|
||||
_window.OnMake += OnMake;
|
||||
}
|
||||
|
||||
public override void HandleState(EuiStateBase state)
|
||||
{
|
||||
if (state is not MakeGhostRoleEuiState uiState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window.SetEntity(uiState.EntityUid);
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
base.Opened();
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
private void OnMake(EntityUid uid, string name, string description, string rules, bool makeSentient)
|
||||
{
|
||||
var player = _playerManager.LocalPlayer;
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var makeGhostRoleCommand =
|
||||
$"makeghostrole " +
|
||||
$"\"{CommandParsing.Escape(uid.ToString())}\" " +
|
||||
$"\"{CommandParsing.Escape(name)}\" " +
|
||||
$"\"{CommandParsing.Escape(description)}\" " +
|
||||
$"\"{CommandParsing.Escape(rules)}\"";
|
||||
|
||||
_consoleHost.ExecuteCommand(player.Session, makeGhostRoleCommand);
|
||||
|
||||
if (makeSentient)
|
||||
{
|
||||
var makeSentientCommand = $"makesentient \"{CommandParsing.Escape(uid.ToString())}\"";
|
||||
_consoleHost.ExecuteCommand(player.Session, makeSentientCommand);
|
||||
}
|
||||
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
private void OnClose()
|
||||
{
|
||||
base.Closed();
|
||||
SendMessage(new MakeGhostRoleWindowClosedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<DefaultWindow Title="{Loc Make Ghost Role}"
|
||||
xmlns="https://spacestation14.io">
|
||||
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="RoleEntityLabel" Text="Entity" />
|
||||
<Label Name="RoleEntity" Text="" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="RoleNameLabel" Text="Role Name" />
|
||||
<LineEdit Name="RoleName" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="RoleDescriptionLabel" Text="Role Description" />
|
||||
<LineEdit Name="RoleDescription" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="RoleRulesLabel" Text="Role Rules" />
|
||||
<LineEdit Name="RoleRules" HorizontalExpand="True" Text="{Loc ghost-role-component-default-rules}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Name="MakeSentientLabel" Text="Make Sentient" />
|
||||
<CheckBox Name="MakeSentientCheckbox" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="MakeButton" Text="Make" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
</DefaultWindow>
|
||||
@@ -0,0 +1,52 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class MakeGhostRoleWindow : DefaultWindow
|
||||
{
|
||||
public delegate void MakeRole(EntityUid uid, string name, string description, string rules, bool makeSentient);
|
||||
|
||||
public MakeGhostRoleWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
MakeSentientLabel.MinSize = (150, 0);
|
||||
RoleEntityLabel.MinSize = (150, 0);
|
||||
RoleNameLabel.MinSize = (150, 0);
|
||||
RoleName.MinSize = (300, 0);
|
||||
RoleDescriptionLabel.MinSize = (150, 0);
|
||||
RoleDescription.MinSize = (300, 0);
|
||||
RoleRulesLabel.MinSize = (150, 0);
|
||||
RoleRules.MinSize = (300, 0);
|
||||
|
||||
MakeButton.OnPressed += OnPressed;
|
||||
}
|
||||
|
||||
private EntityUid? EntityUid { get; set; }
|
||||
|
||||
public event MakeRole? OnMake;
|
||||
|
||||
public void SetEntity(EntityUid uid)
|
||||
{
|
||||
EntityUid = uid;
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
RoleName.Text = entManager.GetComponent<MetaDataComponent>(uid).EntityName;
|
||||
RoleEntity.Text = $"{uid}";
|
||||
}
|
||||
|
||||
private void OnPressed(ButtonEventArgs args)
|
||||
{
|
||||
if (EntityUid == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnMake?.Invoke(EntityUid.Value, RoleName.Text, RoleDescription.Text, RoleRules.Text, MakeSentientCheckbox.Pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Content.Client/UserInterface/Systems/Ghost/GhostGui.cs
Normal file
84
Content.Client/UserInterface/Systems/Ghost/GhostGui.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using Content.Client.Ghost;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.Ghost.Controls;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Ghost
|
||||
{
|
||||
public sealed class GhostGui : Control
|
||||
{
|
||||
private readonly Button _returnToBody = new() {Text = Loc.GetString("ghost-gui-return-to-body-button") };
|
||||
private readonly Button _ghostWarp = new() {Text = Loc.GetString("ghost-gui-ghost-warp-button") };
|
||||
private readonly Button _ghostRoles = new();
|
||||
private readonly GhostComponent _owner;
|
||||
private readonly GhostSystem _system;
|
||||
|
||||
public GhostTargetWindow? TargetWindow { get; }
|
||||
|
||||
public GhostGui(GhostComponent owner, GhostSystem system, IEntityNetworkManager eventBus)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_owner = owner;
|
||||
_system = system;
|
||||
|
||||
TargetWindow = new GhostTargetWindow(eventBus);
|
||||
|
||||
MouseFilter = MouseFilterMode.Ignore;
|
||||
|
||||
_ghostWarp.OnPressed += _ =>
|
||||
{
|
||||
eventBus.SendSystemNetworkMessage(new GhostWarpsRequestEvent());
|
||||
TargetWindow.Populate();
|
||||
TargetWindow.OpenCentered();
|
||||
};
|
||||
_returnToBody.OnPressed += _ =>
|
||||
{
|
||||
var msg = new GhostReturnToBodyRequest();
|
||||
eventBus.SendSystemNetworkMessage(msg);
|
||||
};
|
||||
_ghostRoles.OnPressed += _ => IoCManager.Resolve<IClientConsoleHost>()
|
||||
.RemoteExecuteCommand(null, "ghostroles");
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
_returnToBody,
|
||||
_ghostWarp,
|
||||
_ghostRoles,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_returnToBody.Disabled = !_owner.CanReturnToBody;
|
||||
_ghostRoles.Text = Loc.GetString("ghost-gui-ghost-roles-button", ("count", _system.AvailableGhostRoleCount));
|
||||
if (_system.AvailableGhostRoleCount != 0)
|
||||
{
|
||||
_ghostRoles.StyleClasses.Add(StyleBase.ButtonCaution);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ghostRoles.StyleClasses.Remove(StyleBase.ButtonCaution);
|
||||
}
|
||||
TargetWindow?.Populate();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
TargetWindow?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user