diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 41db0ad7ce..83d927c94b 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -29,7 +29,6 @@ namespace Content.Client.Actions public event Action? OnActionAdded; public event Action? OnActionRemoved; - public event OnActionReplaced? ActionReplaced; public event Action? ActionsUpdated; public event Action? LinkActions; public event Action? UnlinkActions; diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs index fcacc1b052..bb83e370fe 100644 --- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs +++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs @@ -42,6 +42,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged actions) { - if (_window == null) + if (_window is not { Disposed: false, IsOpen: true }) return; - ClearList(); + if (_actionsSystem == null) + return; + _window.UpdateNeeded = false; + + List existing = new(_window.ResultsGrid.ChildCount); + foreach (var child in _window.ResultsGrid.Children) + { + if (child is ActionButton button) + existing.Add(button); + } + + int i = 0; foreach (var action in actions) { - var button = new ActionButton {Locked = true}; + if (i < existing.Count) + { + existing[i++].UpdateData(action.Id, _actionsSystem); + continue; + } - button.UpdateData(action.Id); + var button = new ActionButton(_entMan, _spriteSystem, this) {Locked = true}; button.ActionPressed += OnWindowActionPressed; button.ActionUnpressed += OnWindowActionUnPressed; button.ActionFocusExited += OnWindowActionFocusExisted; - + button.UpdateData(action.Id, _actionsSystem); _window.ResultsGrid.AddChild(button); } + + for (; i < existing.Count; i++) + { + existing[i].Dispose(); + } + } + + public void QueueWindowUpdate() + { + if (_window != null) + _window.UpdateNeeded = true; } private void SearchAndDisplay() { - if (_window is not { Disposed: false } || _actionsSystem == null) + if (_window is not { Disposed: false, IsOpen: true }) + return; + + if (_actionsSystem == null) return; if (_playerManager.LocalPlayer?.ControlledEntity is not { } player) @@ -598,6 +617,9 @@ public sealed class ActionUIController : UIController, IOnStateChanged assignments) { + if (_actionsSystem == null) + return; + foreach (ref var assignment in CollectionsMarshal.AsSpan(assignments)) { _pages[assignment.Hotbar][assignment.Slot] = assignment.ActionId; } - _container?.SetActionData(_pages[_currentPageIndex]); + _container?.SetActionData(_actionsSystem, _pages[_currentPageIndex]); } public void RemoveActionContainer() @@ -881,19 +906,24 @@ public sealed class ActionUIController : UIController, IOnStateChanged UserInterfaceManager.GetUIController(); - private IEntityManager Entities => _entities ??= IoCManager.Resolve(); - private ActionsSystem Actions => Entities.System(); + private IEntityManager _entities; + private SpriteSystem? _spriteSys; + private ActionUIController? _controller; private bool _beingHovered; private bool _depressed; private bool _toggled; @@ -54,14 +50,21 @@ public sealed class ActionButton : Control, IEntityControl private readonly SpriteView _bigItemSpriteView; public EntityUid? ActionId { get; private set; } + private BaseActionComponent? _action; public bool Locked { get; set; } public event Action? ActionPressed; public event Action? ActionUnpressed; public event Action? ActionFocusExited; - public ActionButton() + public ActionButton(IEntityManager entities, SpriteSystem? spriteSys = null, ActionUIController? controller = null) { + // TODO why is this constructor so slooooow. The rest of the code is fine + + _entities = entities; + _spriteSys = spriteSys; + _controller = controller; + MouseFilter = MouseFilterMode.Pass; Button = new TextureRect { @@ -180,7 +183,7 @@ public sealed class ActionButton : Control, IEntityControl private Control? SupplyTooltip(Control sender) { - if (!Entities.TryGetComponent(ActionId, out MetaDataComponent? metadata)) + if (!_entities.TryGetComponent(ActionId, out MetaDataComponent? metadata)) return null; var name = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityName)); @@ -196,9 +199,8 @@ public sealed class ActionButton : Control, IEntityControl private void UpdateItemIcon() { - if (!Actions.TryGetActionData(ActionId, out var action) || - action is not {EntityIcon: { } entity} || - !Entities.HasComponent(entity)) + if (_action is not {EntityIcon: { } entity} || + !_entities.HasComponent(entity)) { _bigItemSpriteView.Visible = false; _bigItemSpriteView.SetEntity(null); @@ -207,7 +209,7 @@ public sealed class ActionButton : Control, IEntityControl } else { - switch (action.ItemIconStyle) + switch (_action.ItemIconStyle) { case ItemActionIconStyle.BigItem: _bigItemSpriteView.Visible = true; @@ -233,17 +235,17 @@ public sealed class ActionButton : Control, IEntityControl private void SetActionIcon(Texture? texture) { - if (!Actions.TryGetActionData(ActionId, out var action) || texture == null) + if (_action == null || texture == null) { _bigActionIcon.Texture = null; _bigActionIcon.Visible = false; _smallActionIcon.Texture = null; _smallActionIcon.Visible = false; } - else if (action.EntityIcon != null && action.ItemIconStyle == ItemActionIconStyle.BigItem) + else if (_action.EntityIcon != null && _action.ItemIconStyle == ItemActionIconStyle.BigItem) { _smallActionIcon.Texture = texture; - _smallActionIcon.Modulate = action.IconColor; + _smallActionIcon.Modulate = _action.IconColor; _smallActionIcon.Visible = true; _bigActionIcon.Texture = null; _bigActionIcon.Visible = false; @@ -251,7 +253,7 @@ public sealed class ActionButton : Control, IEntityControl else { _bigActionIcon.Texture = texture; - _bigActionIcon.Modulate = action.IconColor; + _bigActionIcon.Modulate = _action.IconColor; _bigActionIcon.Visible = true; _smallActionIcon.Texture = null; _smallActionIcon.Visible = false; @@ -262,39 +264,43 @@ public sealed class ActionButton : Control, IEntityControl { UpdateItemIcon(); - if (!Actions.TryGetActionData(ActionId, out var action)) + if (_action == null) { SetActionIcon(null); return; } - if ((Controller.SelectingTargetFor == ActionId || action.Toggled) && action.IconOn != null) - SetActionIcon(action.IconOn.Frame0()); + _controller ??= UserInterfaceManager.GetUIController(); + _spriteSys ??= _entities.System(); + if ((_controller.SelectingTargetFor == ActionId || _action.Toggled) && _action.IconOn != null) + SetActionIcon(_spriteSys.Frame0(_action.IconOn)); else - SetActionIcon(action.Icon?.Frame0()); + SetActionIcon(_action.Icon != null ? _spriteSys.Frame0(_action.Icon) : null); } - public bool TryReplaceWith(EntityUid actionId) + public bool TryReplaceWith(EntityUid actionId, ActionsSystem system) { if (Locked) { return false; } - UpdateData(actionId); + UpdateData(actionId, system); return true; } - public void UpdateData(EntityUid actionId) + public void UpdateData(EntityUid? actionId, ActionsSystem system) { ActionId = actionId; - Label.Visible = true; + system.TryGetActionData(actionId, out _action); + Label.Visible = actionId != null; UpdateIcons(); } public void ClearData() { ActionId = null; + _action = null; Cooldown.Visible = false; Cooldown.Progress = 1; Label.Visible = false; @@ -305,19 +311,17 @@ public sealed class ActionButton : Control, IEntityControl { base.FrameUpdate(args); - if (!Actions.TryGetActionData(ActionId, out var action)) - { + if (_action == null) return; + + if (_action.Cooldown != null) + { + Cooldown.FromTime(_action.Cooldown.Value.Start, _action.Cooldown.Value.End); } - if (action.Cooldown != null) + if (ActionId != null && _toggled != _action.Toggled) { - Cooldown.FromTime(action.Cooldown.Value.Start, action.Cooldown.Value.End); - } - - if (ActionId != null && _toggled != action.Toggled) - { - _toggled = action.Toggled; + _toggled = _action.Toggled; } } @@ -344,7 +348,7 @@ public sealed class ActionButton : Control, IEntityControl public void Depress(GUIBoundKeyEventArgs args, bool depress) { // action can still be toggled if it's allowed to stay selected - if (!Actions.TryGetActionData(ActionId, out var action) || action is not {Enabled: true}) + if (_action is not {Enabled: true}) return; if (_depressed && !depress) @@ -362,14 +366,15 @@ public sealed class ActionButton : Control, IEntityControl HighlightRect.Visible = _beingHovered; // always show the normal empty button style if no action in this slot - if (!Actions.TryGetActionData(ActionId, out var action)) + if (_action == null) { SetOnlyStylePseudoClass(ContainerButton.StylePseudoClassNormal); return; } // show a hover only if the action is usable or another action is being dragged on top of this - if (_beingHovered && (Controller.IsDragging || action.Enabled)) + _controller ??= UserInterfaceManager.GetUIController(); + if (_beingHovered && (_controller.IsDragging || _action.Enabled)) { SetOnlyStylePseudoClass(ContainerButton.StylePseudoClassHover); } @@ -384,16 +389,16 @@ public sealed class ActionButton : Control, IEntityControl } // if it's toggled on, always show the toggled on style (currently same as depressed style) - if (action.Toggled || Controller.SelectingTargetFor == ActionId) + if (_action.Toggled || _controller.SelectingTargetFor == ActionId) { // when there's a toggle sprite, we're showing that sprite instead of highlighting this slot - SetOnlyStylePseudoClass(action.IconOn != null + SetOnlyStylePseudoClass(_action.IconOn != null ? ContainerButton.StylePseudoClassNormal : ContainerButton.StylePseudoClassPressed); return; } - if (!action.Enabled) + if (!_action.Enabled) { SetOnlyStylePseudoClass(ContainerButton.StylePseudoClassDisabled); return; diff --git a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs index 5d6ad5c8de..9986c61ad6 100644 --- a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs +++ b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButtonContainer.cs @@ -1,3 +1,4 @@ +using Content.Client.Actions; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -21,7 +22,7 @@ public class ActionButtonContainer : GridContainer get => (ActionButton) GetChild(index); } - public void SetActionData(params EntityUid?[] actionTypes) + public void SetActionData(ActionsSystem system, params EntityUid?[] actionTypes) { ClearActionData(); @@ -31,7 +32,7 @@ public class ActionButtonContainer : GridContainer if (action == null) continue; - ((ActionButton) GetChild(i)).UpdateData(action.Value); + ((ActionButton) GetChild(i)).UpdateData(action.Value, system); } } diff --git a/Content.Client/UserInterface/Systems/Actions/Widgets/ActionsBar.xaml.cs b/Content.Client/UserInterface/Systems/Actions/Widgets/ActionsBar.xaml.cs index c752a8b3ac..ff3c32cc0e 100644 --- a/Content.Client/UserInterface/Systems/Actions/Widgets/ActionsBar.xaml.cs +++ b/Content.Client/UserInterface/Systems/Actions/Widgets/ActionsBar.xaml.cs @@ -3,16 +3,18 @@ using Content.Shared.Input; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Input; namespace Content.Client.UserInterface.Systems.Actions.Widgets; [GenerateTypedNameReferences] public sealed partial class ActionsBar : UIWidget { + [Dependency] private readonly IEntityManager _entity = default!; + public ActionsBar() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); var keys = ContentKeyFunctions.GetHotbarBoundKeys(); for (var index = 1; index < keys.Length; index++) @@ -24,7 +26,7 @@ public sealed partial class ActionsBar : UIWidget ActionButton MakeButton(int index) { var boundKey = keys[index]; - var button = new ActionButton(); + var button = new ActionButton(_entity); button.KeyBind = boundKey; button.Label.Text = index.ToString(); return button; diff --git a/Content.Client/UserInterface/Systems/Actions/Windows/ActionsWindow.xaml.cs b/Content.Client/UserInterface/Systems/Actions/Windows/ActionsWindow.xaml.cs index 8c39883296..fbe1e71535 100644 --- a/Content.Client/UserInterface/Systems/Actions/Windows/ActionsWindow.xaml.cs +++ b/Content.Client/UserInterface/Systems/Actions/Windows/ActionsWindow.xaml.cs @@ -10,6 +10,11 @@ public sealed partial class ActionsWindow : DefaultWindow { public MultiselectOptionButton FilterButton { get; private set; } + /// + /// Whether the displayed actions or search filter needs updating. + /// + public bool UpdateNeeded; + public ActionsWindow() { RobustXamlLoader.Load(this);