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()
|
private void PopulateCrayons()
|
||||||
{
|
{
|
||||||
var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
||||||
_menu?.Populate(crayonDecals);
|
_menu?.Populate(crayonDecals.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
||||||
@@ -44,6 +44,16 @@ namespace Content.Client.Crayon.UI
|
|||||||
PopulateCrayons();
|
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)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
base.UpdateState(state);
|
base.UpdateState(state);
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
Title="{Loc 'crayon-window-title'}"
|
Title="{Loc 'crayon-window-title'}"
|
||||||
MinSize="250 300"
|
MinSize="450 500"
|
||||||
SetSize="250 300">
|
SetSize="450 500">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<ColorSelectorSliders Name="ColorSelector" Visible="False" />
|
<ColorSelectorSliders Name="ColorSelector" Visible="False" />
|
||||||
<LineEdit Name="Search" />
|
<LineEdit Name="Search" Margin="0 0 0 8" PlaceHolder="{Loc 'crayon-window-placeholder'}" />
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<GridContainer Name="Grid" Columns="6">
|
<BoxContainer Name="Grids" Orientation="Vertical">
|
||||||
<!-- Crayon decals get added here by code -->
|
</BoxContainer>
|
||||||
</GridContainer>
|
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Content.Shared.Crayon;
|
using Content.Shared.Crayon;
|
||||||
using Content.Shared.Decals;
|
using Content.Shared.Decals;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
@@ -18,7 +20,12 @@ namespace Content.Client.Crayon.UI
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class CrayonWindow : DefaultWindow
|
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 string? _selected;
|
||||||
private Color _color;
|
private Color _color;
|
||||||
|
|
||||||
@@ -28,8 +35,10 @@ namespace Content.Client.Crayon.UI
|
|||||||
public CrayonWindow()
|
public CrayonWindow()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||||
|
|
||||||
Search.OnTextChanged += _ => RefreshList();
|
Search.OnTextChanged += SearchChanged;
|
||||||
ColorSelector.OnColorChanged += SelectColor;
|
ColorSelector.OnColorChanged += SelectColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,51 +53,94 @@ namespace Content.Client.Crayon.UI
|
|||||||
private void RefreshList()
|
private void RefreshList()
|
||||||
{
|
{
|
||||||
// Clear
|
// Clear
|
||||||
Grid.DisposeAllChildren();
|
Grids.DisposeAllChildren();
|
||||||
if (_decals == null)
|
|
||||||
|
if (_decals == null || _allDecals == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var filter = Search.Text;
|
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;
|
continue;
|
||||||
|
|
||||||
var button = new TextureButton()
|
var label = new Label
|
||||||
{
|
{
|
||||||
TextureNormal = tex,
|
Text = locName
|
||||||
Name = decal,
|
|
||||||
ToolTip = decal,
|
|
||||||
Modulate = _color,
|
|
||||||
};
|
};
|
||||||
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()
|
TextureNormal = texture,
|
||||||
{
|
Name = name,
|
||||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
ToolTip = name,
|
||||||
},
|
Modulate = _color,
|
||||||
Children =
|
Scale = new System.Numerics.Vector2(2, 2)
|
||||||
{
|
|
||||||
button,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
Grid.AddChild(panelContainer);
|
button.OnPressed += ButtonOnPressed;
|
||||||
}
|
|
||||||
else
|
if (_selected == name)
|
||||||
{
|
{
|
||||||
Grid.AddChild(button);
|
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)
|
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||||
{
|
{
|
||||||
if (obj.Button.Name == null) return;
|
if (obj.Button.Name == null) return;
|
||||||
|
|
||||||
_selected = obj.Button.Name;
|
_selected = obj.Button.Name;
|
||||||
|
_autoSelected = null;
|
||||||
OnSelected?.Invoke(_selected);
|
OnSelected?.Invoke(_selected);
|
||||||
RefreshList();
|
RefreshList();
|
||||||
}
|
}
|
||||||
@@ -107,12 +159,38 @@ namespace Content.Client.Crayon.UI
|
|||||||
RefreshList();
|
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)
|
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();
|
RefreshList();
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||||||
|
|
||||||
if (component.DeleteEmpty && component.Charges <= 0)
|
if (component.DeleteEmpty && component.Charges <= 0)
|
||||||
UseUpCrayon(uid, args.User);
|
UseUpCrayon(uid, args.User);
|
||||||
|
else
|
||||||
|
_uiSystem.ServerSendUiMessage(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
|
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
|
||||||
|
|||||||
@@ -3,12 +3,23 @@ using Robust.Shared.Serialization;
|
|||||||
|
|
||||||
namespace Content.Shared.Crayon
|
namespace Content.Shared.Crayon
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component holding the state of a crayon-like component
|
||||||
|
/// </summary>
|
||||||
[NetworkedComponent, ComponentProtoName("Crayon"), Access(typeof(SharedCrayonSystem))]
|
[NetworkedComponent, ComponentProtoName("Crayon"), Access(typeof(SharedCrayonSystem))]
|
||||||
public abstract partial class SharedCrayonComponent : Component
|
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;
|
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]
|
[Serializable, NetSerializable]
|
||||||
public enum CrayonUiKey : byte
|
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]
|
[Serializable, NetSerializable]
|
||||||
public sealed class CrayonSelectMessage : BoundUserInterfaceMessage
|
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]
|
[Serializable, NetSerializable]
|
||||||
public sealed class CrayonColorMessage : BoundUserInterfaceMessage
|
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]
|
[Serializable, NetSerializable]
|
||||||
public enum CrayonVisuals
|
public sealed class CrayonUsedMessage : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
State,
|
public readonly string DrawnDecal;
|
||||||
Color
|
|
||||||
|
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]
|
[Serializable, NetSerializable]
|
||||||
public sealed class CrayonComponentState : ComponentState
|
public sealed class CrayonComponentState : ComponentState
|
||||||
{
|
{
|
||||||
@@ -60,10 +89,17 @@ namespace Content.Shared.Crayon
|
|||||||
Capacity = capacity;
|
Capacity = capacity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The state of the crayon UI as sent by the server
|
||||||
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
|
public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
public string Selected;
|
public string Selected;
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not the color can be selected
|
||||||
|
/// </summary>
|
||||||
public bool SelectableColor;
|
public bool SelectableColor;
|
||||||
public Color Color;
|
public Color Color;
|
||||||
|
|
||||||
|
|||||||
@@ -8,3 +8,10 @@ crayon-interact-invalid-location = Can't reach there!
|
|||||||
|
|
||||||
## UI
|
## UI
|
||||||
crayon-window-title = Crayon
|
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