Improve crayon UI to not be stuck in 1996 (#33101)
* Improve crayon UI to not be stuck in 1996 * Make a horrifying crayon spaghetti * Crayon * Undeprecate the crayon, describe the crayon
This commit is contained in:
@@ -31,7 +31,7 @@ namespace Content.Client.Crayon.UI
|
||||
private void PopulateCrayons()
|
||||
{
|
||||
var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
||||
_menu?.Populate(crayonDecals);
|
||||
_menu?.Populate(crayonDecals.ToList());
|
||||
}
|
||||
|
||||
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
||||
@@ -44,6 +44,16 @@ namespace Content.Client.Crayon.UI
|
||||
PopulateCrayons();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (_menu is null || message is not CrayonUsedMessage crayonMessage)
|
||||
return;
|
||||
|
||||
_menu.AdvanceState(crayonMessage.DrawnDecal);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'crayon-window-title'}"
|
||||
MinSize="250 300"
|
||||
SetSize="250 300">
|
||||
MinSize="450 500"
|
||||
SetSize="450 500">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<ColorSelectorSliders Name="ColorSelector" Visible="False" />
|
||||
<LineEdit Name="Search" />
|
||||
<LineEdit Name="Search" Margin="0 0 0 8" PlaceHolder="{Loc 'crayon-window-placeholder'}" />
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<GridContainer Name="Grid" Columns="6">
|
||||
<!-- Crayon decals get added here by code -->
|
||||
</GridContainer>
|
||||
<BoxContainer Name="Grids" Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
@@ -18,7 +20,12 @@ namespace Content.Client.Crayon.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CrayonWindow : DefaultWindow
|
||||
{
|
||||
private Dictionary<string, Texture>? _decals;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly SpriteSystem _spriteSystem = default!;
|
||||
|
||||
private Dictionary<string, List<(string Name, Texture Texture)>>? _decals;
|
||||
private List<string>? _allDecals;
|
||||
private string? _autoSelected;
|
||||
private string? _selected;
|
||||
private Color _color;
|
||||
|
||||
@@ -28,8 +35,10 @@ namespace Content.Client.Crayon.UI
|
||||
public CrayonWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
Search.OnTextChanged += _ => RefreshList();
|
||||
Search.OnTextChanged += SearchChanged;
|
||||
ColorSelector.OnColorChanged += SelectColor;
|
||||
}
|
||||
|
||||
@@ -44,51 +53,94 @@ namespace Content.Client.Crayon.UI
|
||||
private void RefreshList()
|
||||
{
|
||||
// Clear
|
||||
Grid.DisposeAllChildren();
|
||||
if (_decals == null)
|
||||
Grids.DisposeAllChildren();
|
||||
|
||||
if (_decals == null || _allDecals == null)
|
||||
return;
|
||||
|
||||
var filter = Search.Text;
|
||||
foreach (var (decal, tex) in _decals)
|
||||
var comma = filter.IndexOf(',');
|
||||
var first = (comma == -1 ? filter : filter[..comma]).Trim();
|
||||
|
||||
var names = _decals.Keys.ToList();
|
||||
names.Sort((a, b) => a == "random" ? 1 : b == "random" ? -1 : a.CompareTo(b));
|
||||
|
||||
if (_autoSelected != null && first != _autoSelected && _allDecals.Contains(first))
|
||||
{
|
||||
if (!decal.Contains(filter))
|
||||
_selected = first;
|
||||
_autoSelected = _selected;
|
||||
OnSelected?.Invoke(_selected);
|
||||
}
|
||||
|
||||
foreach (var categoryName in names)
|
||||
{
|
||||
var locName = Loc.GetString("crayon-category-" + categoryName);
|
||||
var category = _decals[categoryName].Where(d => locName.Contains(first) || d.Name.Contains(first)).ToList();
|
||||
|
||||
if (category.Count == 0)
|
||||
continue;
|
||||
|
||||
var button = new TextureButton()
|
||||
var label = new Label
|
||||
{
|
||||
TextureNormal = tex,
|
||||
Name = decal,
|
||||
ToolTip = decal,
|
||||
Modulate = _color,
|
||||
Text = locName
|
||||
};
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
if (_selected == decal)
|
||||
|
||||
var grid = new GridContainer
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
Columns = 6,
|
||||
Margin = new Thickness(0, 0, 0, 16)
|
||||
};
|
||||
|
||||
Grids.AddChild(label);
|
||||
Grids.AddChild(grid);
|
||||
|
||||
foreach (var (name, texture) in category)
|
||||
{
|
||||
var button = new TextureButton()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
TextureNormal = texture,
|
||||
Name = name,
|
||||
ToolTip = name,
|
||||
Modulate = _color,
|
||||
Scale = new System.Numerics.Vector2(2, 2)
|
||||
};
|
||||
Grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grid.AddChild(button);
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
|
||||
if (_selected == name)
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
};
|
||||
grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchChanged(LineEdit.LineEditEventArgs obj)
|
||||
{
|
||||
_autoSelected = ""; // Placeholder to kick off the auto-select in refreshlist()
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||
{
|
||||
if (obj.Button.Name == null) return;
|
||||
|
||||
_selected = obj.Button.Name;
|
||||
_autoSelected = null;
|
||||
OnSelected?.Invoke(_selected);
|
||||
RefreshList();
|
||||
}
|
||||
@@ -107,12 +159,38 @@ namespace Content.Client.Crayon.UI
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(IEnumerable<DecalPrototype> prototypes)
|
||||
public void AdvanceState(string drawnDecal)
|
||||
{
|
||||
_decals = new Dictionary<string, Texture>();
|
||||
var filter = Search.Text;
|
||||
if (!filter.Contains(',') || !filter.Contains(drawnDecal))
|
||||
return;
|
||||
|
||||
var first = filter[..filter.IndexOf(',')].Trim();
|
||||
|
||||
if (first.Equals(drawnDecal, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
Search.Text = filter[(filter.IndexOf(',') + 1)..].Trim();
|
||||
_autoSelected = first;
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(List<DecalPrototype> prototypes)
|
||||
{
|
||||
_decals = [];
|
||||
_allDecals = [];
|
||||
|
||||
prototypes.Sort((a, b) => a.ID.CompareTo(b.ID));
|
||||
|
||||
foreach (var decalPrototype in prototypes)
|
||||
{
|
||||
_decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
|
||||
var category = "random";
|
||||
if (decalPrototype.Tags.Count > 1 && decalPrototype.Tags[1].StartsWith("crayon-"))
|
||||
category = decalPrototype.Tags[1].Replace("crayon-", "");
|
||||
var list = _decals.GetOrNew(category);
|
||||
list.Add((decalPrototype.ID, _spriteSystem.Frame0(decalPrototype.Sprite)));
|
||||
_allDecals.Add(decalPrototype.ID);
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
|
||||
@@ -82,6 +82,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
||||
|
||||
if (component.DeleteEmpty && component.Charges <= 0)
|
||||
UseUpCrayon(uid, args.User);
|
||||
else
|
||||
_uiSystem.ServerSendUiMessage(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
|
||||
}
|
||||
|
||||
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
|
||||
|
||||
@@ -3,12 +3,23 @@ using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Crayon
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Component holding the state of a crayon-like component
|
||||
/// </summary>
|
||||
[NetworkedComponent, ComponentProtoName("Crayon"), Access(typeof(SharedCrayonSystem))]
|
||||
public abstract partial class SharedCrayonComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of currently selected decal prototype that will be placed when the crayon is used
|
||||
/// </summary>
|
||||
public string SelectedState { get; set; } = string.Empty;
|
||||
|
||||
[DataField("color")] public Color Color;
|
||||
/// <summary>
|
||||
/// Color with which the crayon will draw
|
||||
/// </summary>
|
||||
[DataField("color")]
|
||||
public Color Color;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum CrayonUiKey : byte
|
||||
@@ -17,6 +28,9 @@ namespace Content.Shared.Crayon
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by the client to notify the server about the selected decal ID
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CrayonSelectMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
@@ -27,6 +41,9 @@ namespace Content.Shared.Crayon
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the color of the crayon, used by Rainbow Crayon
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CrayonColorMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
@@ -37,13 +54,25 @@ namespace Content.Shared.Crayon
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server to CLIENT. Notifies the BUI that a decal with given ID has been drawn.
|
||||
/// Allows the client UI to advance forward in the client-only ephemeral queue,
|
||||
/// preventing the crayon from becoming a magic text storage device.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public enum CrayonVisuals
|
||||
public sealed class CrayonUsedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
State,
|
||||
Color
|
||||
public readonly string DrawnDecal;
|
||||
|
||||
public CrayonUsedMessage(string drawn)
|
||||
{
|
||||
DrawnDecal = drawn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Component state, describes how many charges are left in the crayon in the near-hand UI
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CrayonComponentState : ComponentState
|
||||
{
|
||||
@@ -60,10 +89,17 @@ namespace Content.Shared.Crayon
|
||||
Capacity = capacity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The state of the crayon UI as sent by the server
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public string Selected;
|
||||
/// <summary>
|
||||
/// Whether or not the color can be selected
|
||||
/// </summary>
|
||||
public bool SelectableColor;
|
||||
public Color Color;
|
||||
|
||||
|
||||
@@ -8,3 +8,10 @@ crayon-interact-invalid-location = Can't reach there!
|
||||
|
||||
## UI
|
||||
crayon-window-title = Crayon
|
||||
crayon-window-placeholder = Search, or queue a comma-separated list of names
|
||||
crayon-category-1-brushes = Brushes
|
||||
crayon-category-2-alphanum = Numbers and letters
|
||||
crayon-category-3-symbols = Symbols
|
||||
crayon-category-4-info = Signs
|
||||
crayon-category-5-graffiti = Graffiti
|
||||
crayon-category-random = Random
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user