Improve paper stamping experience (#17135)

This commit is contained in:
eoineoineoin
2023-08-13 19:28:10 +01:00
committed by GitHub
parent ae2dcc8aba
commit 4ccc8a04be
28 changed files with 629 additions and 202 deletions

View File

@@ -1,4 +1,4 @@
using Content.Client.Eui;
using Content.Client.Eui;
using Content.Shared.Eui;
using Content.Shared.Fax;
using JetBrains.Annotations;
@@ -15,7 +15,8 @@ public sealed class AdminFaxEui : BaseEui
_window = new AdminFaxWindow();
_window.OnClose += () => SendMessage(new AdminFaxEuiMsg.Close());
_window.OnFollowFax += uid => SendMessage(new AdminFaxEuiMsg.Follow(uid));
_window.OnMessageSend += args => SendMessage(new AdminFaxEuiMsg.Send(args.uid, args.title, args.from, args.message, args.stamp));
_window.OnMessageSend += args => SendMessage(new AdminFaxEuiMsg.Send(args.uid, args.title,
args.stampedBy, args.message, args.stampSprite, args.stampColor));
}
public override void Opened()

View File

@@ -1,4 +1,4 @@
<DefaultWindow xmlns="https://spacestation14.io"
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc admin-fax-title}"
MinWidth="400">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
@@ -21,6 +21,8 @@
<Control MinWidth="5" />
<OptionButton Name="StampSelector" HorizontalExpand="True" />
</BoxContainer>
<Label Text="{Loc admin-fax-stamp-color}" />
<ColorSelectorSliders Margin="12 0 0 0" Name="StampColorSelector" Color="#BB3232"/>
<Control MinHeight="10" />
<Button Name="SendButton" Text="{Loc admin-fax-send}"></Button>
</BoxContainer>

View File

@@ -1,4 +1,4 @@
using Content.Shared.Fax;
using Content.Shared.Fax;
using Robust.Client.AutoGenerated;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
@@ -13,7 +13,7 @@ public sealed partial class AdminFaxWindow : DefaultWindow
{
private const string StampsRsiPath = "/Textures/Objects/Misc/bureaucracy.rsi";
public Action<(EntityUid uid, string title, string from, string message, string stamp)>? OnMessageSend;
public Action<(EntityUid uid, string title, string stampedBy, string message, string stampSprite, Color stampColor)>? OnMessageSend;
public Action<EntityUid>? OnFollowFax;
public AdminFaxWindow()
@@ -28,6 +28,9 @@ public sealed partial class AdminFaxWindow : DefaultWindow
FollowButton.OnPressed += FollowFax;
SendButton.OnPressed += SendMessage;
// Don't use this, but ColorSelectorSliders requires it:
StampColorSelector.OnColorChanged += (Color) => {};
var loc = IoCManager.Resolve<ILocalizationManager>();
MessageEdit.Placeholder = new Rope.Leaf(loc.GetString("admin-fax-message-placeholder")); // TextEdit work only with Nodes
}
@@ -90,6 +93,7 @@ public sealed partial class AdminFaxWindow : DefaultWindow
return;
var from = FromEdit.Text;
OnMessageSend?.Invoke((faxUid.Value, title, from, message, stamp));
var stampColor = StampColorSelector.Color;
OnMessageSend?.Invoke((faxUid.Value, title, from, message, stamp, stampColor));
}
}

View File

@@ -5,67 +5,66 @@ using Robust.Shared.Input;
using Robust.Shared.Utility;
using static Content.Shared.Paper.SharedPaperComponent;
namespace Content.Client.Paper.UI
namespace Content.Client.Paper.UI;
[UsedImplicitly]
public sealed class PaperBoundUserInterface : BoundUserInterface
{
[UsedImplicitly]
public sealed class PaperBoundUserInterface : BoundUserInterface
[ViewVariables]
private PaperWindow? _window;
public PaperBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
[ViewVariables]
private PaperWindow? _window;
}
public PaperBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
protected override void Open()
{
base.Open();
_window = new PaperWindow();
_window.OnClose += Close;
_window.Input.OnKeyBindDown += args => // Solution while TextEdit don't have events
{
}
protected override void Open()
{
base.Open();
_window = new PaperWindow();
_window.OnClose += Close;
_window.Input.OnKeyBindDown += args => // Solution while TextEdit don't have events
if (args.Function == EngineKeyFunctions.TextSubmit)
{
if (args.Function == EngineKeyFunctions.TextSubmit)
{
var text = Rope.Collapse(_window.Input.TextRope);
Input_OnTextEntered(text);
args.Handle();
}
};
if (EntMan.TryGetComponent<PaperVisualsComponent>(Owner, out var visuals))
{
_window.InitVisuals(visuals);
var text = Rope.Collapse(_window.Input.TextRope);
Input_OnTextEntered(text);
args.Handle();
}
};
_window.OpenCentered();
if (EntMan.TryGetComponent<PaperVisualsComponent>(Owner, out var visuals))
{
_window.InitVisuals(Owner, visuals);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_window?.Populate((PaperBoundUserInterfaceState) state);
}
_window.OpenCentered();
}
private void Input_OnTextEntered(string text)
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_window?.Populate((PaperBoundUserInterfaceState) state);
}
private void Input_OnTextEntered(string text)
{
if (!string.IsNullOrEmpty(text))
{
if (!string.IsNullOrEmpty(text))
SendMessage(new PaperInputTextMessage(text));
if (_window != null)
{
SendMessage(new PaperInputTextMessage(text));
if (_window != null)
{
_window.Input.TextRope = Rope.Leaf.Empty;
_window.Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top);
}
_window.Input.TextRope = Rope.Leaf.Empty;
_window.Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top);
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
}

View File

@@ -20,7 +20,8 @@
<TextEdit Name="Input" StyleClasses="PaperLineEdit" Access="Public" />
</PanelContainer>
</BoxContainer>
<BoxContainer Name="StampDisplay" Orientation="Vertical" VerticalAlignment="Bottom" Margin="6"/>
<paper:StampCollection Name="StampDisplay" VerticalAlignment="Bottom" Margin="6"/>
</PanelContainer>
</ScrollContainer>
</PanelContainer>

View File

@@ -44,8 +44,11 @@ namespace Content.Client.Paper.UI
/// Initialize this UI according to <code>visuals</code> Initializes
/// textures, recalculates sizes, and applies some layout rules.
/// </summary>
public void InitVisuals(PaperVisualsComponent visuals)
public void InitVisuals(EntityUid entity, PaperVisualsComponent visuals)
{
// Randomize the placement of any stamps based on the entity UID
// so that there's some variety in different papers.
StampDisplay.PlacementSeed = (int)entity;
var resCache = IoCManager.Resolve<IResourceCache>();
// Initialize the background:
@@ -206,9 +209,10 @@ namespace Content.Client.Paper.UI
BlankPaperIndicator.Visible = !isEditing && state.Text.Length == 0;
StampDisplay.RemoveAllChildren();
StampDisplay.RemoveStamps();
foreach(var stamper in state.StampedBy)
{
StampDisplay.AddChild(new StampWidget{ Stamper = stamper });
StampDisplay.AddStamp(new StampWidget{ StampInfo = stamper });
}
}
@@ -220,7 +224,7 @@ namespace Content.Client.Paper.UI
/// </summary>
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
{
var mode = DragMode.Move;
var mode = DragMode.None;
// Be quite generous with resize margins:
if (relativeMousePos.Y < DRAG_MARGIN_SIZE)
@@ -241,6 +245,10 @@ namespace Content.Client.Paper.UI
mode |= DragMode.Right;
}
if((mode & _allowedResizeModes) == DragMode.None)
{
return DragMode.Move;
}
return mode & _allowedResizeModes;
}
}

View File

@@ -0,0 +1,5 @@
<paper:StampCollection xmlns="https://spacestation14.io"
xmlns:paper="clr-namespace:Content.Client.Paper.UI"
MinSize="150 150">
</paper:StampCollection>

View File

@@ -0,0 +1,98 @@
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Random;
namespace Content.Client.Paper.UI;
[GenerateTypedNameReferences]
public sealed partial class StampCollection : Container
{
private List<StampWidget> _stamps = new();
/// Seed for random number generator to place stamps deterministically
public int PlacementSeed;
public StampCollection()
{
RobustXamlLoader.Load(this);
}
/// <summary>
/// Remove any stamps from the page
/// </summary>
public void RemoveStamps()
{
_stamps.Clear();
InvalidateArrange();
}
/// <summary>
/// Adds a stamp to the display; will perform
/// automatic layout.
/// </summary>
public void AddStamp(StampWidget s)
{
_stamps.Add(s);
AddChild(s);
}
protected override Vector2 ArrangeOverride(Vector2 finalSize)
{
var random = new Random(PlacementSeed);
var r = (finalSize * 0.5f).Length();
var dtheta = -MathHelper.DegreesToRadians(90);
var theta0 = random.Next(0, 3) * dtheta;
var thisCenter = PixelSizeBox.TopLeft + finalSize * UIScale * 0.5f;
// Here's where we lay out the stamps. The first stamp goes in the
// center of this container; subsequent stamps will chose an angle
// (theta) to place the center of the stamp. The stamp is moved out
// as far as it can in that direction, taking the size and
// orientation of the stamp into account.
for (var i = 0; i < _stamps.Count; i++)
{
var stampOrientation = MathHelper.DegreesToRadians((random.NextFloat() - 0.5f) * 10.0f) ;
_stamps[i].Orientation = stampOrientation;
var theta = theta0 + dtheta * 0.5f + dtheta * i + (i > 4 ? MathF.Log(1 + i / 4) * dtheta : 0); // There is probably a better way to lay these out, to minimize overlaps
var childCenterOnCircle = thisCenter;
if (i > 0)
{
// First stamp can go in the center. Subsequent stamps have to find space.
childCenterOnCircle += new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * r * UIScale;
}
var childHeLocal = _stamps[i].DesiredPixelSize * 0.5f;
var c = childHeLocal * MathF.Abs(MathF.Cos(stampOrientation));
var s = childHeLocal * MathF.Abs(MathF.Sin(stampOrientation));
var childHePage = new Vector2(c.X + s.Y, s.X + c.Y);
var controlBox = new UIBox2(PixelSizeBox.TopLeft, PixelSizeBox.TopLeft + finalSize * UIScale);
var clampedCenter = Clamp(Shrink(controlBox, childHePage), childCenterOnCircle);
var finalPosition = clampedCenter - childHePage;
var finalPositionAsInt = new Vector2i((int)finalPosition.X, (int)finalPosition.Y);
_stamps[i].ArrangePixel(new UIBox2i(finalPositionAsInt, finalPositionAsInt + _stamps[i].DesiredPixelSize));
}
return finalSize;
}
/// <summary>
/// Shrink a UIBox2 by a half extents, moving both the top-left and
/// bottom-right closer together.
/// </summary>
private UIBox2 Shrink(UIBox2 box, Vector2 shrinkHe)
{
return new UIBox2(box.TopLeft + shrinkHe, box.BottomRight - shrinkHe);
}
/// <summary>
/// Returns the input vector clamped to be within the UIBox
/// </summary>
private Vector2 Clamp(UIBox2 box, Vector2 point)
{
return Vector2.Min(box.BottomRight, Vector2.Max(box.TopLeft, point));
}
}

View File

@@ -0,0 +1,2 @@
<paper:StampLabel xmlns="https://spacestation14.io"
xmlns:paper="clr-namespace:Content.Client.Paper.UI" />

View File

@@ -0,0 +1,56 @@
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Paper.UI;
[GenerateTypedNameReferences]
public sealed partial class StampLabel : Label
{
/// A scale that's applied to the text to ensure it
/// fits in the allowed space.
private Vector2 _textScaling = Vector2.One;
/// Shader used to draw the stamps
private ShaderInstance? _stampShader;
/// Allows an additional orientation to be applied to
/// this control.
public float Orientation = 0.0f;
public StampLabel()
{
RobustXamlLoader.Load(this);
var prototypes = IoCManager.Resolve<IPrototypeManager>();
_stampShader = prototypes.Index<ShaderPrototype>("PaperStamp").InstanceUnique();
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
var desiredTextSize = base.MeasureOverride(availableSize);
var clampedScale = Vector2.Min(availableSize / desiredTextSize, Vector2.One);
var keepAspectRatio = MathF.Min(clampedScale.X, clampedScale.Y);
const float shimmerReduction = 0.1f;
_textScaling = Vector2.One * MathF.Round(keepAspectRatio / shimmerReduction) * shimmerReduction;
return desiredTextSize;
}
protected override void Draw(DrawingHandleScreen handle)
{
var offset = new Vector2(PixelPosition.X * MathF.Cos(Orientation) - PixelPosition.Y * MathF.Sin(Orientation),
PixelPosition.Y * MathF.Cos(Orientation) + PixelPosition.X * MathF.Sin(Orientation));
_stampShader?.SetParameter("objCoord", GlobalPosition * UIScale * new Vector2(1, -1));
handle.UseShader(_stampShader);
handle.SetTransform(GlobalPixelPosition - PixelPosition + offset, Orientation, _textScaling);
base.Draw(handle);
// Restore a sane transform+shader
handle.SetTransform(Matrix3.Identity);
handle.UseShader(null);
}
}

View File

@@ -4,22 +4,7 @@
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:paper="clr-namespace:Content.Client.Paper.UI" HorizontalAlignment="Center" Margin="6">
<BoxContainer Orientation="Vertical">
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="{x:Static style:StyleNano.DangerousRedFore}" />
</PanelContainer.PanelOverride>
<Control MinSize="3 3" />
</PanelContainer>
<Label Name="StampedByLabel" StyleClasses="LabelHeadingBigger" FontColorOverride="{x:Static style:StyleNano.DangerousRedFore}" Margin="12 6 12 6"/>
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="{x:Static style:StyleNano.DangerousRedFore}" />
</PanelContainer.PanelOverride>
<Control MinSize="3 3" />
</PanelContainer>
</BoxContainer>
<paper:StampLabel Name="StampedByLabel" StyleClasses="LabelHeadingBigger"
FontColorOverride="{x:Static style:StyleNano.DangerousRedFore}"
Margin="12 6 12 6"/>
</paper:StampWidget>

View File

@@ -1,23 +1,59 @@
using System.Numerics;
using Content.Shared.Paper;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
using Robust.Shared.Prototypes;
namespace Content.Client.Paper.UI
namespace Content.Client.Paper.UI;
[GenerateTypedNameReferences]
public sealed partial class StampWidget : PanelContainer
{
[GenerateTypedNameReferences]
public sealed partial class StampWidget : Container
{
public string? Stamper {
get => StampedByLabel.Text;
set => StampedByLabel.Text = value;
}
private StyleBoxTexture _borderTexture;
private ShaderInstance? _stampShader;
public StampWidget()
{
RobustXamlLoader.Load(this);
public float Orientation
{
get => StampedByLabel.Orientation;
set => StampedByLabel.Orientation = value;
}
public StampDisplayInfo StampInfo {
set {
StampedByLabel.Text = Loc.GetString(value.StampedName);
StampedByLabel.FontColorOverride = value.StampedColor;
ModulateSelfOverride = value.StampedColor;
}
}
public StampWidget()
{
RobustXamlLoader.Load(this);
var resCache = IoCManager.Resolve<IResourceCache>();
var borderImage = resCache.GetResource<TextureResource>(
"/Textures/Interface/Paper/paper_stamp_border.svg.96dpi.png");
_borderTexture = new StyleBoxTexture {
Texture = borderImage,
};
_borderTexture.SetPatchMargin(StyleBoxTexture.Margin.All, 7.0f);
PanelOverride = _borderTexture;
var prototypes = IoCManager.Resolve<IPrototypeManager>();
_stampShader = prototypes.Index<ShaderPrototype>("PaperStamp").InstanceUnique();
}
protected override void Draw(DrawingHandleScreen handle)
{
_stampShader?.SetParameter("objCoord", GlobalPosition * UIScale * new Vector2(1, -1));
handle.UseShader(_stampShader);
handle.SetTransform(GlobalPosition * UIScale, Orientation, Vector2.One);
base.Draw(handle);
// Restore a sane transform+shader
handle.SetTransform(Matrix3.Identity);
handle.UseShader(null);
}
}