]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix Admin Object tab sorting and search (#28609)
authorRepo <47093363+Titian3@users.noreply.github.com>
Thu, 6 Jun 2024 06:41:11 +0000 (18:41 +1200)
committerGitHub <noreply@github.com>
Thu, 6 Jun 2024 06:41:11 +0000 (02:41 -0400)
Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml
Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs
Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml
Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabEntry.xaml.cs
Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml [new file with mode: 0644]
Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs [new file with mode: 0644]
Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
Content.Client/UserInterface/Systems/Admin/AdminUIController.cs
Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl [new file with mode: 0644]

index fb68e6c79089bcf2d1a05349f5017a4df47aa986..ea89916ba8c20b7890b20f6c905a5781f000cdba 100644 (file)
@@ -1,15 +1,21 @@
 <Control xmlns="https://spacestation14.io"
-         xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
+         xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
+         xmlns:ot="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab"
+         xmlns:co="clr-namespace:Content.Client.UserInterface.Controls">
     <BoxContainer Orientation="Vertical">
         <BoxContainer Orientation="Horizontal">
             <Label HorizontalExpand="True" SizeFlagsStretchRatio="0.50"
                    Text="{Loc Object type:}" />
+            <LineEdit Name="SearchLineEdit" PlaceHolder="{Loc Search...}" HorizontalExpand="True" SizeFlagsStretchRatio="1"/>
             <OptionButton Name="ObjectTypeOptions" HorizontalExpand="True" SizeFlagsStretchRatio="0.25"/>
         </BoxContainer>
+        <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
+        </BoxContainer>
         <cc:HSeparator/>
-        <ScrollContainer HorizontalExpand="True" VerticalExpand="True">
-            <BoxContainer Orientation="Vertical" Name="ObjectList">
-            </BoxContainer>
-        </ScrollContainer>
+        <BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
+            <ot:ObjectsTabHeader Name="ListHeader"/>
+            <cc:HSeparator/>
+            <co:SearchListContainer Name="SearchList" Access="Public" VerticalExpand="True"/>
+        </BoxContainer>
     </BoxContainer>
 </Control>
index a5c300843658695b02e020555c190d9f8e10ca61..90559707f92c79e0b0823524cc7ea92ef605bb13 100644 (file)
@@ -1,5 +1,7 @@
 using Content.Client.Station;
+using Content.Client.UserInterface.Controls;
 using Robust.Client.AutoGenerated;
+using Robust.Client.Graphics;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Map.Components;
@@ -10,20 +12,20 @@ namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
 [GenerateTypedNameReferences]
 public sealed partial class ObjectsTab : Control
 {
-    [Dependency] private readonly EntityManager _entityManager = default!;
+    [Dependency] private readonly IEntityManager _entityManager = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
 
     private readonly List<ObjectsTabEntry> _objects = new();
-    private List<ObjectsTabSelection> _selections = new();
+    private readonly List<ObjectsTabSelection> _selections = new();
+    private bool _ascending = false;  // Set to false for descending order by default
+    private ObjectsTabHeader.Header _headerClicked = ObjectsTabHeader.Header.ObjectName;
+    private readonly Color _altColor = Color.FromHex("#292B38");
+    private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
 
-    public event Action<ObjectsTabEntry, GUIBoundKeyEventArgs>? OnEntryKeyBindDown;
+    public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
 
-    // Listen I could either have like 4 different event subscribers (for map / grid / station changes) and manage their lifetimes in AdminUIController
-    // OR
-    // I can do this.
-    private TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
-
-    private TimeSpan _nextUpdate = TimeSpan.FromSeconds(2);
+    private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(2);
+    private TimeSpan _nextUpdate;
 
     public ObjectsTab()
     {
@@ -42,6 +44,30 @@ public sealed partial class ObjectsTab : Control
             ObjectTypeOptions.AddItem(Enum.GetName((ObjectsTabSelection)type)!);
         }
 
+        ListHeader.OnHeaderClicked += HeaderClicked;
+        SearchList.SearchBar = SearchLineEdit;
+        SearchList.GenerateItem += GenerateButton;
+        SearchList.DataFilterCondition += DataFilterCondition;
+
+        RefreshObjectList();
+        // Set initial selection and refresh the list to apply the initial sort order
+        var defaultSelection = ObjectsTabSelection.Grids;
+        ObjectTypeOptions.SelectId((int)defaultSelection); // Set the default selection
+        RefreshObjectList(defaultSelection); // Refresh the list with the default selection
+
+        // Initialize the next update time
+        _nextUpdate = TimeSpan.Zero;
+    }
+
+    protected override void FrameUpdate(FrameEventArgs args)
+    {
+        base.FrameUpdate(args);
+
+        if (_timing.CurTime < _nextUpdate)
+            return;
+
+        _nextUpdate = _timing.CurTime + _updateFrequency;
+
         RefreshObjectList();
     }
 
@@ -81,32 +107,72 @@ public sealed partial class ObjectsTab : Control
                 throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
         }
 
-        foreach (var control in _objects)
+        entities.Sort((a, b) =>
         {
-            ObjectList.RemoveChild(control);
-        }
+            var valueA = GetComparableValue(a, _headerClicked);
+            var valueB = GetComparableValue(b, _headerClicked);
+            return _ascending ? Comparer<object>.Default.Compare(valueA, valueB) : Comparer<object>.Default.Compare(valueB, valueA);
+        });
 
-        _objects.Clear();
-
-        foreach (var (name, nent) in entities)
+        var listData = new List<ObjectsListData>();
+        for (int index = 0; index < entities.Count; index++)
         {
-            var ctrl = new ObjectsTabEntry(name, nent);
-            _objects.Add(ctrl);
-            ObjectList.AddChild(ctrl);
-            ctrl.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(ctrl, args);
+            var info = entities[index];
+            listData.Add(new ObjectsListData(info, $"{info.Name} {info.Entity}", index % 2 == 0 ? _altColor : _defaultColor));
         }
+
+        SearchList.PopulateList(listData);
     }
 
-    protected override void FrameUpdate(FrameEventArgs args)
+    private void GenerateButton(ListData data, ListContainerButton button)
     {
-        base.FrameUpdate(args);
-
-        if (_timing.CurTime < _nextUpdate)
+        if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
             return;
 
-        // I do not care for precision.
-        _nextUpdate = _timing.CurTime + _updateFrequency;
+        var entry = new ObjectsTabEntry(info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor });
+        button.ToolTip = $"{info.Name}, {info.Entity}";
+
+        // Add key binding event handler
+        entry.OnKeyBindDown += args => OnEntryKeyBindDown?.Invoke(args, data);
+
+        button.AddChild(entry);
+    }
+
+    private bool DataFilterCondition(string filter, ListData listData)
+    {
+        if (listData is not ObjectsListData { FilteringString: var filteringString })
+            return false;
+
+        // If the filter is empty, do not filter out any entries
+        if (string.IsNullOrEmpty(filter))
+            return true;
+
+        return filteringString.Contains(filter, StringComparison.CurrentCultureIgnoreCase);
+    }
+
+    private object GetComparableValue((string Name, NetEntity Entity) entity, ObjectsTabHeader.Header header)
+    {
+        return header switch
+        {
+            ObjectsTabHeader.Header.ObjectName => entity.Name,
+            ObjectsTabHeader.Header.EntityID => entity.Entity.ToString(),
+            _ => entity.Name
+        };
+    }
+
+    private void HeaderClicked(ObjectsTabHeader.Header header)
+    {
+        if (_headerClicked == header)
+        {
+            _ascending = !_ascending;
+        }
+        else
+        {
+            _headerClicked = header;
+            _ascending = true;
+        }
 
+        ListHeader.UpdateHeaderSymbols(_headerClicked, _ascending);
         RefreshObjectList();
     }
 
@@ -118,3 +184,4 @@ public sealed partial class ObjectsTab : Control
     }
 }
 
+public record ObjectsListData((string Name, NetEntity Entity) Info, string FilteringString, Color BackgroundColor) : ListData;
index 0f6975e3656d94176260abaef25526e4cdfa51e6..83c4cc5697f71fb45fb0eab108b09a2569d81afe 100644 (file)
@@ -1,6 +1,6 @@
-<ContainerButton xmlns="https://spacestation14.io"
-                 xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
-    <PanelContainer Name="BackgroundColorPanel"/>
+<PanelContainer xmlns="https://spacestation14.io"
+                xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
+                Name="BackgroundColorPanel">
     <BoxContainer Orientation="Horizontal"
                   HorizontalExpand="True"
                   SeparationOverride="4">
@@ -14,4 +14,4 @@
                HorizontalExpand="True"
                ClipText="True"/>
     </BoxContainer>
-</ContainerButton>
+</PanelContainer>
index c9b2cd8b572d131109bd6145ada066b0be8a2e7a..aab06c6ccd0f468e54c1c143afe56352bf437480 100644 (file)
@@ -1,19 +1,21 @@
 using Robust.Client.AutoGenerated;
+using Robust.Client.Graphics;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
 
 namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
 
 [GenerateTypedNameReferences]
-public sealed partial class ObjectsTabEntry : ContainerButton
+public sealed partial class ObjectsTabEntry : PanelContainer
 {
     public NetEntity AssocEntity;
 
-    public ObjectsTabEntry(string name, NetEntity nent)
+    public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
     {
         RobustXamlLoader.Load(this);
         AssocEntity = nent;
         EIDLabel.Text = nent.ToString();
         NameLabel.Text = name;
+        BackgroundColorPanel.PanelOverride = styleBox;
     }
 }
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml
new file mode 100644 (file)
index 0000000..71a1f5c
--- /dev/null
@@ -0,0 +1,21 @@
+<Control xmlns="https://spacestation14.io"
+         xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
+    <PanelContainer Name="BackgroundColorPanel" Access="Public"/>
+    <BoxContainer Orientation="Horizontal"
+                  HorizontalExpand="True"
+                  SeparationOverride="4">
+        <Label Name="ObjectNameLabel"
+               SizeFlagsStretchRatio="3"
+               HorizontalExpand="True"
+               ClipText="True"
+               Text="{Loc object-tab-object-name}"
+               MouseFilter="Pass"/>
+        <cc:VSeparator/>
+        <Label Name="EntityIDLabel"
+               SizeFlagsStretchRatio="3"
+               HorizontalExpand="True"
+               ClipText="True"
+               Text="{Loc object-tab-entity-id}"
+               MouseFilter="Pass"/>
+    </BoxContainer>
+</Control>
diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTabHeader.xaml.cs
new file mode 100644 (file)
index 0000000..3a91b5b
--- /dev/null
@@ -0,0 +1,86 @@
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Input;
+
+namespace Content.Client.Administration.UI.Tabs.ObjectsTab
+{
+    [GenerateTypedNameReferences]
+    public sealed partial class ObjectsTabHeader : Control
+    {
+        public event Action<Header>? OnHeaderClicked;
+
+        private const string ArrowUp = "↑";
+        private const string ArrowDown = "↓";
+
+        public ObjectsTabHeader()
+        {
+            RobustXamlLoader.Load(this);
+
+            ObjectNameLabel.OnKeyBindDown += ObjectNameClicked;
+            EntityIDLabel.OnKeyBindDown += EntityIDClicked;
+        }
+
+        public Label GetHeader(Header header)
+        {
+            return header switch
+            {
+                Header.ObjectName => ObjectNameLabel,
+                Header.EntityID => EntityIDLabel,
+                _ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
+            };
+        }
+
+        public void ResetHeaderText()
+        {
+            ObjectNameLabel.Text = Loc.GetString("object-tab-object-name");
+            EntityIDLabel.Text = Loc.GetString("object-tab-entity-id");
+        }
+
+        public void UpdateHeaderSymbols(Header headerClicked, bool ascending)
+        {
+            ResetHeaderText();
+            var arrow = ascending ? ArrowUp : ArrowDown;
+            GetHeader(headerClicked).Text += $" {arrow}";
+        }
+
+        private void HeaderClicked(GUIBoundKeyEventArgs args, Header header)
+        {
+            if (args.Function != EngineKeyFunctions.UIClick)
+            {
+                return;
+            }
+
+            OnHeaderClicked?.Invoke(header);
+            args.Handle();
+        }
+
+        private void ObjectNameClicked(GUIBoundKeyEventArgs args)
+        {
+            HeaderClicked(args, Header.ObjectName);
+        }
+
+        private void EntityIDClicked(GUIBoundKeyEventArgs args)
+        {
+            HeaderClicked(args, Header.EntityID);
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            base.Dispose(disposing);
+
+            if (disposing)
+            {
+                ObjectNameLabel.OnKeyBindDown -= ObjectNameClicked;
+                EntityIDLabel.OnKeyBindDown -= EntityIDClicked;
+            }
+        }
+
+        public enum Header
+        {
+            ObjectName,
+            EntityID
+        }
+    }
+}
index a8bfaddecf45e5638d1791cdde924efee8af3c95..826945e7cc043e5cdce76870737335d5f2bfc6fd 100644 (file)
@@ -53,6 +53,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
             SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
 
             RefreshPlayerList(_adminSystem.PlayerList);
+
         }
 
         #region Antag Overlay
@@ -110,7 +111,9 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
             _players = players;
             PlayerCount.Text = $"Players: {_playerMan.PlayerCount}";
 
-            var sortedPlayers = new List<PlayerInfo>(players);
+            var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
+
+            var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
             sortedPlayers.Sort(Compare);
 
             UpdateHeaderSymbols();
index a7397aff38d81a55c989f23fe65b3d405e7685a1..3d8235591ad4c52f0cc1856fe592843d3d7feace 100644 (file)
@@ -198,9 +198,12 @@ public sealed class AdminUIController : UIController,
         args.Handle();
     }
 
-    private void ObjectsTabEntryKeyBindDown(ObjectsTabEntry entry, GUIBoundKeyEventArgs args)
+    private void ObjectsTabEntryKeyBindDown(GUIBoundKeyEventArgs args, ListData? data)
     {
-        var uid = entry.AssocEntity;
+        if (data is not ObjectsListData { Info: var info })
+            return;
+
+        var uid = info.Entity;
         var function = args.Function;
 
         if (function == EngineKeyFunctions.UIClick)
diff --git a/Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl b/Resources/Locale/en-US/administration/ui/tabs/object-tab.ftl
new file mode 100644 (file)
index 0000000..0288efc
--- /dev/null
@@ -0,0 +1,2 @@
+object-tab-entity-id = Entity ID
+object-tab-object-name = Object name