From 3f9de93163a938ccd27d315674ebe437cf2a84f4 Mon Sep 17 00:00:00 2001 From: Eoin Mcloughlin Date: Mon, 22 Jul 2024 23:09:29 +0100 Subject: [PATCH 1/3] Avoid destroying and recreating record list on every update message --- .../CriminalRecordsConsoleWindow.xaml.cs | 77 +++++++++++++------ 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs index b259e08e723c..e86440dd1613 100644 --- a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs +++ b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs @@ -7,10 +7,12 @@ using Content.Shared.StationRecords; using Robust.Client.AutoGenerated; using Robust.Client.Player; +using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; +using System.Linq; namespace Content.Client.CriminalRecords; @@ -36,7 +38,6 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow public Action? OnDialogConfirmed; private uint _maxLength; - private bool _isPopulating; private bool _access; private uint? _selectedKey; private CriminalRecord? _selectedRecord; @@ -74,7 +75,7 @@ public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerMa RecordListing.OnItemSelected += args => { - if (_isPopulating || RecordListing[args.ItemIndex].Metadata is not uint cast) + if (RecordListing[args.ItemIndex].Metadata is not uint cast) return; OnKeySelected?.Invoke(cast); @@ -82,8 +83,7 @@ public CriminalRecordsConsoleWindow(EntityUid console, uint maxLength, IPlayerMa RecordListing.OnItemDeselected += _ => { - if (!_isPopulating) - OnKeySelected?.Invoke(null); + OnKeySelected?.Invoke(null); }; FilterType.OnItemSelected += eventArgs => @@ -133,13 +133,8 @@ public void UpdateState(CriminalRecordsConsoleState state) FilterType.SelectId((int)_currentFilterType); - // set up the records listing panel - RecordListing.Clear(); - - var hasRecords = state.RecordListing != null && state.RecordListing.Count > 0; - NoRecords.Visible = !hasRecords; - if (hasRecords) - PopulateRecordListing(state.RecordListing!); + NoRecords.Visible = state.RecordListing == null || state.RecordListing.Count == 0; + PopulateRecordListing(state.RecordListing); // set up the selected person's record var selected = _selectedKey != null; @@ -167,19 +162,58 @@ public void UpdateState(CriminalRecordsConsoleState state) } } - private void PopulateRecordListing(Dictionary listing) + private void PopulateRecordListing(Dictionary? listing) { - _isPopulating = true; + if (listing == null) + { + RecordListing.Clear(); + } - foreach (var (key, name) in listing) + var entries = listing!.ToList(); + entries.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal)); + // `entries` now contains the definitive list of items which should be in + // our list of records and is in the order we want to present those items. + + // Walk through the existing items in RecordListing and in the updated listing + // in parallel to synchronize the items in RecordListing with `entries`. + int i = RecordListing.Count - 1; + int j = entries.Count - 1; + while(i >= 0 && j >= 0) { - var item = RecordListing.AddItem(name); - item.Metadata = key; - item.Selected = key == _selectedKey; + var strcmp = string.Compare(RecordListing[i].Text, entries[j].Value, StringComparison.Ordinal); + if (strcmp == 0) + { + // This item exists in both RecordListing and `entries`. Nothing to do. + i--; + j--; + } + else if (strcmp > 0) + { + // Item exists in RecordListing, but not in `entries`. Remove it. + RecordListing.RemoveAt(i); + i--; + } + else if (strcmp < 0) + { + // A new entry which doesn't exist in RecordListing. Create it. + RecordListing.Insert(i + 1, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key}); + j--; + } + } + + // Any remaining items in RecordListing don't exist in `entries`, so remove them + while (i >= 0) + { + RecordListing.RemoveAt(i); + i--; } - _isPopulating = false; - RecordListing.SortItemsByText(); + // And finally, any remaining items in `entries`, don't exist in RecordListing. Create them. + while (j >= 0) + { + RecordListing.Insert(0, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key}); + j--; + } } private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord) @@ -211,10 +245,7 @@ private void AddStatusSelect(SecurityStatus status) private void FilterListingOfRecords(string text = "") { - if (!_isPopulating) - { - OnFiltersChanged?.Invoke(_currentFilterType, text); - } + OnFiltersChanged?.Invoke(_currentFilterType, text); } private void SetStatus(SecurityStatus status) From 2b0a982de3941c3ca9af293fcfc89ee636b24df4 Mon Sep 17 00:00:00 2001 From: Eoin Mcloughlin Date: Fri, 9 Aug 2024 08:29:49 +0100 Subject: [PATCH 2/3] Add early-out on nullptr input --- .../CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs index e86440dd1613..e23ed709628e 100644 --- a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs +++ b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs @@ -167,6 +167,7 @@ private void PopulateRecordListing(Dictionary? listing) if (listing == null) { RecordListing.Clear(); + return; } var entries = listing!.ToList(); From ca9b9e17ae96adb7ba42658f53c6914672dcd41a Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Fri, 9 Aug 2024 17:40:17 +1000 Subject: [PATCH 3/3] Remove sussy null suppress --- .../CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs index e23ed709628e..21aa54c9622f 100644 --- a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs +++ b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs @@ -170,7 +170,7 @@ private void PopulateRecordListing(Dictionary? listing) return; } - var entries = listing!.ToList(); + var entries = listing.ToList(); entries.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal)); // `entries` now contains the definitive list of items which should be in // our list of records and is in the order we want to present those items.