From 6f8242c1fc922ea1d6dc5b5423f89ab48e2da24b Mon Sep 17 00:00:00 2001 From: pathetic meowmeow Date: Thu, 22 Jan 2026 16:31:45 -0500 Subject: [PATCH] Simplify hands UI code (#42534) * Simplify hands UI code * i remembered about SortedHands in the component * minor cleanup --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Systems/Hands/Controls/HandsContainer.cs | 65 ++---- .../Systems/Hands/HandsUIController.cs | 219 +++--------------- .../Systems/Hotbar/HotbarUIController.cs | 1 - .../Systems/Hotbar/Widgets/HotbarGui.xaml.cs | 10 +- .../Inventory/Controls/ItemSlotUIContainer.cs | 103 +++----- .../Controls/ItemStatusPanel.xaml.cs | 8 +- .../Inventory/InventoryUIController.cs | 6 +- .../Hands/Components/HandsComponent.cs | 38 +-- .../Hands/EntitySystems/SharedHandsSystem.cs | 2 + 9 files changed, 108 insertions(+), 344 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs b/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs index d2f24abd6c..e6c83a1262 100644 --- a/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs +++ b/Content.Client/UserInterface/Systems/Hands/Controls/HandsContainer.cs @@ -1,5 +1,7 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Client.UserInterface.Systems.Inventory.Controls; +using Content.Shared.Hands.Components; using Robust.Client.UserInterface.Controls; namespace Content.Client.UserInterface.Systems.Hands.Controls; @@ -7,10 +9,10 @@ namespace Content.Client.UserInterface.Systems.Hands.Controls; public sealed class HandsContainer : ItemSlotUIContainer { private readonly GridContainer _grid; - public int ColumnLimit { get => _grid.Columns; set => _grid.Columns = value; } - public int MaxButtonCount { get; set; } = 0; + private readonly List _orderedButtons = new(); + public HandsComponent? PlayerHandsComponent; - public int MaxButtonsPerRow { get; set; }= 6; + public int ColumnLimit { get; set; } = 6; /// /// Indexer. This is used to reference a HandsContainer from the @@ -24,57 +26,32 @@ public sealed class HandsContainer : ItemSlotUIContainer _grid.ExpandBackwards = true; } - public override HandButton? AddButton(HandButton newButton) + protected override void AddButton(HandButton newButton) { - if (MaxButtonCount > 0) - { - if (ButtonCount >= MaxButtonCount) - return null; + _orderedButtons.Add(newButton); - _grid.AddChild(newButton); - } - else + _grid.RemoveAllChildren(); + var enumerable = PlayerHandsComponent?.SortedHands is { } sortedHands + ? _orderedButtons.OrderBy(it => sortedHands.IndexOf(it.SlotName)) + : _orderedButtons.OrderBy(it => it.HandLocation); + foreach (var button in enumerable) { - _grid.AddChild(newButton); + _grid.AddChild(button); } - _grid.Columns = Math.Min(_grid.ChildCount, MaxButtonsPerRow); - return base.AddButton(newButton); + _grid.Columns = Math.Min(_grid.ChildCount, ColumnLimit); } - public override void RemoveButton(string handName) + protected override void RemoveButton(HandButton button) { - var button = GetButton(handName); - if (button == null) - return; - base.RemoveButton(button); + _orderedButtons.Remove(button); _grid.RemoveChild(button); } - public bool TryGetLastButton(out HandButton? control) + public override void ClearButtons() { - if (Buttons.Count == 0) - { - control = null; - return false; - } - - control = Buttons.Values.Last(); - return true; - } - - public bool TryRemoveLastHand(out HandButton? control) - { - var success = TryGetLastButton(out control); - if (control != null) - RemoveButton(control); - return success; - } - - public void Clear() - { - ClearButtons(); - _grid.RemoveAllChildren(); + base.ClearButtons(); + _orderedButtons.Clear(); } public IEnumerable GetButtons() @@ -85,8 +62,4 @@ public sealed class HandsContainer : ItemSlotUIContainer yield return hand; } } - - public bool IsFull => (MaxButtonCount != 0 && ButtonCount >= MaxButtonCount); - - public int ButtonCount => _grid.ChildCount; } diff --git a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs index a20f4b35f8..122e0b2f08 100644 --- a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs +++ b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs @@ -7,7 +7,6 @@ using Content.Shared.Hands.Components; using Content.Shared.Input; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Timing; -using JetBrains.Annotations; using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; @@ -26,9 +25,6 @@ public sealed class HandsUIController : UIController, IOnStateEntered _handsContainers = new(); - private readonly Dictionary _handContainerIndices = new(); - private readonly Dictionary _handLookup = new(); private HandsComponent? _playerHandsComponent; private HandButton? _activeHand; @@ -40,8 +36,6 @@ public sealed class HandsUIController : UIController, IOnStateEntered UIManager.GetActiveUIWidgetOrNull(); public void OnSystemLoaded(HandsSystem system) @@ -119,25 +113,16 @@ public sealed class HandsUIController : UIController, IOnStateEntered handsComp) { DebugTools.Assert(_playerHandsComponent == null); - if (HandsGui != null) - HandsGui.Visible = true; - + HandsGui?.Visible = true; + HandsGui?.HandContainer.PlayerHandsComponent = handsComp; _playerHandsComponent = handsComp; foreach (var (name, hand) in handsComp.Comp.Hands) { @@ -182,29 +167,18 @@ public sealed class HandsUIController : UIController, IOnStateEntered - /// Swap hands from one container to the other. - /// - /// - /// - public void SwapHands(HandsContainer other, HandsContainer? source = null) - { - if (HandsGui == null && source == null) - { - throw new ArgumentException("Cannot swap hands if no source hand container exists!"); - } - - source ??= HandsGui!.HandContainer; - - var transfer = new List(); - foreach (var child in source.Children) - { - if (child is not HandButton) - { - continue; - } - - transfer.Add(child); - } - - foreach (var control in transfer) - { - source.RemoveChild(control); - other.AddChild(control); - } - } - private void RemoveHand(string handName) { - RemoveHand(handName, out _); - } - - [PublicAPI] - private bool RemoveHand(string handName, out HandButton? handButton) - { - if (!_handLookup.TryGetValue(handName, out handButton)) - return false; - if (handButton.Parent is HandsContainer handContainer) - { - handContainer.RemoveButton(handButton); - } + if (HandsGui?.HandContainer.TryRemoveButton(handName, out var handButton) != true) + return; if (_statusHandLeft == handButton) _statusHandLeft = null; if (_statusHandRight == handButton) _statusHandRight = null; - _handLookup.Remove(handName); - handButton.Orphan(); UpdateVisibleStatusPanels(); - return true; } private void UpdateVisibleStatusPanels() @@ -441,9 +334,12 @@ public sealed class HandsUIController : UIController, IOnStateEntered(); _hands = UIManager.GetUIController(); _storage = UIManager.GetUIController(); - _hands.RegisterHandContainer(handsContainer); } public void ReloadHotbar() diff --git a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml.cs b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml.cs index f6802de28f..81dfffb062 100644 --- a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml.cs +++ b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml.cs @@ -11,8 +11,8 @@ public sealed partial class HotbarGui : UIWidget public HotbarGui() { RobustXamlLoader.Load(this); - StatusPanelRight.SetSide(HandUILocation.Right); - StatusPanelLeft.SetSide(HandUILocation.Left); + StatusPanelRight.SetSide(HandLocation.Right); + StatusPanelLeft.SetSide(HandLocation.Left); var hotbarController = UserInterfaceManager.GetUIController(); hotbarController.Setup(HandContainer); @@ -29,10 +29,10 @@ public sealed partial class HotbarGui : UIWidget StatusPanelRight.Update(entity, hand); } - public void SetHighlightHand(HandUILocation? hand) + public void SetHighlightHand(HandLocation? hand) { - StatusPanelLeft.UpdateHighlight(hand is HandUILocation.Left); - StatusPanelRight.UpdateHighlight(hand is HandUILocation.Right); + StatusPanelLeft.UpdateHighlight(hand is HandLocation.Left); + StatusPanelRight.UpdateHighlight(hand is HandLocation.Right); } public void UpdateStatusVisibility(bool left, bool right) diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs index df45ceabf6..8094a77b6b 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs @@ -14,54 +14,36 @@ public interface IItemslotUIContainer [Virtual] public abstract class ItemSlotUIContainer : GridContainer, IItemslotUIContainer where T : SlotControl { - protected readonly Dictionary Buttons = new(); + private readonly Dictionary _buttons = new(); - private int? _maxColumns; + public int? MaxColumns { get; set; } - public int? MaxColumns + public virtual void ClearButtons() { - get => _maxColumns; - set => _maxColumns = value; - } - - public virtual bool TryAddButton(T newButton, out T button) - { - var tempButton = AddButton(newButton); - if (tempButton == null) - { - button = newButton; - return false; - } - - button = newButton; - return true; - } - - public void ClearButtons() - { - foreach (var button in Buttons.Values) + foreach (var button in _buttons.Values) { - button.Dispose(); + button.Orphan(); } - Buttons.Clear(); + _buttons.Clear(); } public bool TryRegisterButton(SlotControl control, string newSlotName) { if (newSlotName == "") return false; - if (!(control is T slotButton)) + if (control is not T slotButton) return false; - if (Buttons.TryGetValue(newSlotName, out var foundButton)) + + if (_buttons.TryGetValue(newSlotName, out var foundButton)) { if (control == foundButton) return true; //if the slotName is already set do nothing throw new Exception("Could not update button to slot:" + newSlotName + " slot already assigned!"); } - Buttons.Remove(slotButton.SlotName); - AddButton(slotButton); + _buttons.Remove(slotButton.SlotName); + TryAddButton(slotButton); return true; } @@ -69,69 +51,54 @@ public abstract class ItemSlotUIContainer : GridContainer, IItemslotUIContain { if (control is not T newButton) return false; - return AddButton(newButton) != null; - } - - public virtual T? AddButton(T newButton) - { - if (!Children.Contains(newButton) && newButton.Parent == null && newButton.SlotName != "") - AddChild(newButton); - Columns = _maxColumns ?? ChildCount; - return AddButtonToDict(newButton); + return TryAddButton(newButton) != null; } - protected virtual T? AddButtonToDict(T newButton) + public T? TryAddButton(T newButton) { if (newButton.SlotName == "") { - Logger.Warning("Could not add button " + newButton.Name + "No slotname"); + Log.Warning($"{newButton.Name} because it has no slot name"); + return null; } - return !Buttons.TryAdd(newButton.SlotName, newButton) ? null : newButton; - } + if (Children.Contains(newButton) || newButton.Parent != null) + return null; - public virtual void RemoveButton(string slotName) - { - if (!Buttons.TryGetValue(slotName, out var button)) - return; - RemoveButton(button); - } + if (!_buttons.TryAdd(newButton.SlotName, newButton)) + return null; - public virtual void RemoveButtons(params string[] slotNames) - { - foreach (var slotName in slotNames) - { - RemoveButton(slotName); - } + AddButton(newButton); + return newButton; } - public virtual void RemoveButtons(params T?[] buttons) + protected virtual void AddButton(T newButton) { - foreach (var button in buttons) - { - if (button != null) - RemoveButton(button); - } + AddChild(newButton); + Columns = MaxColumns ?? ChildCount; } - protected virtual void RemoveButtonFromDict(T button) + public bool TryRemoveButton(string slotName, [NotNullWhen(true)] out T? button) { - Buttons.Remove(button.SlotName); + if (!_buttons.TryGetValue(slotName, out button)) + return false; + + _buttons.Remove(button.SlotName); + RemoveButton(button); + return true; } - public virtual void RemoveButton(T button) + protected virtual void RemoveButton(T button) { - RemoveButtonFromDict(button); Children.Remove(button); - button.Dispose(); } - public virtual T? GetButton(string slotName) + public T? GetButton(string slotName) { - return !Buttons.TryGetValue(slotName, out var button) ? null : button; + return _buttons.GetValueOrDefault(slotName); } - public virtual bool TryGetButton(string slotName, [NotNullWhen(true)] out T? button) + public bool TryGetButton(string slotName, [NotNullWhen(true)] out T? button) { return (button = GetButton(slotName)) != null; } diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs index dcc6221e46..d5502028c6 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemStatusPanel.xaml.cs @@ -20,7 +20,7 @@ public sealed partial class ItemStatusPanel : Control [ViewVariables] private Hand? _hand; // Tracked so we can re-run SetSide() if the theme changes. - private HandUILocation _side; + private HandLocation _side; public ItemStatusPanel() { @@ -28,7 +28,7 @@ public sealed partial class ItemStatusPanel : Control IoCManager.InjectDependencies(this); } - public void SetSide(HandUILocation location) + public void SetSide(HandLocation location) { // AN IMPORTANT REMINDER ABOUT THIS CODE: // In the UI, the RIGHT hand is on the LEFT on the screen. @@ -43,14 +43,14 @@ public sealed partial class ItemStatusPanel : Control switch (location) { - case HandUILocation.Right: + case HandLocation.Right or HandLocation.Middle: texture = Theme.ResolveTexture("item_status_right"); textureHighlight = Theme.ResolveTexture("item_status_right_highlight"); cutOut = StyleBox.Margin.Left; flat = StyleBox.Margin.Right; contentMargin = MarginFromThemeColor("_itemstatus_content_margin_right"); break; - case HandUILocation.Left: + case HandLocation.Left: texture = Theme.ResolveTexture("item_status_left"); textureHighlight = Theme.ResolveTexture("item_status_left_highlight"); cutOut = StyleBox.Margin.Right; diff --git a/Content.Client/UserInterface/Systems/Inventory/InventoryUIController.cs b/Content.Client/UserInterface/Systems/Inventory/InventoryUIController.cs index ec6293811f..1fa379e525 100644 --- a/Content.Client/UserInterface/Systems/Inventory/InventoryUIController.cs +++ b/Content.Client/UserInterface/Systems/Inventory/InventoryUIController.cs @@ -147,7 +147,7 @@ public sealed class InventoryUIController : UIController, IOnStateEntered(data.HeldEntity); @@ -373,7 +373,7 @@ public sealed class InventoryUIController : UIController, IOnStateEntered /// What side of the body this hand is on. /// -/// -/// public enum HandLocation : byte { - Left, + Right, Middle, - Right -} - -/// -/// What side of the UI a hand is on. -/// -/// -/// -public enum HandUILocation : byte -{ - Left, - Right -} - -/// -/// Helper functions for working with . -/// -public static class HandLocationExt -{ - /// - /// Convert a into the appropriate . - /// This maps "middle" hands to . - /// - public static HandUILocation GetUILocation(this HandLocation location) - { - return location switch - { - HandLocation.Left => HandUILocation.Left, - HandLocation.Middle => HandUILocation.Right, - HandLocation.Right => HandUILocation.Right, - _ => throw new ArgumentOutOfRangeException(nameof(location), location, null) - }; - } + Left } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index 2060ee892f..75d2b3ba01 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -90,6 +90,8 @@ public abstract partial class SharedHandsSystem ent.Comp.Hands.Add(handName, hand); ent.Comp.SortedHands.Add(handName); + // we use LINQ + ToList instead of the list sort because it's a stable sort vs the list sort + ent.Comp.SortedHands = ent.Comp.SortedHands.OrderBy(handId => ent.Comp.Hands[handId].Location).ToList(); Dirty(ent); OnPlayerAddHand?.Invoke((ent, ent.Comp), handName, hand.Location); -- 2.52.0