perf(D.5.4): toolbar re-binds only on shortcut-guid object changes; clear on remove

Now that the object table holds ALL entities (creatures, NPCs, world objects),
filtering ObjectAdded/Updated/Removed to the 18 shortcut guids prevents the bar
from thrashing on every creature spawn in a busy zone.

Also subscribes to ObjectRemoved so a despawned/traded-away item clears its slot
(matching retail gmToolbarUI::SetDelayedShortcutNum's deferred-bind contract).

Four new unit tests (iconIds spy pattern) verify: non-shortcut ObjectAdded/Removed
do NOT invoke Populate; shortcut ObjectAdded deferred-binds; shortcut ObjectRemoved
clears the slot. 2671 tests, 4 skipped, 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-18 16:57:04 +02:00
parent a9d40addac
commit a33e897400
2 changed files with 124 additions and 3 deletions

View file

@ -109,9 +109,23 @@ public sealed class ToolbarController
if (combatState is not null)
combatState.CombatModeChanged += SetCombatMode;
// Re-bind any deferred slot whenever the repo learns about a new/updated item.
repo.ObjectAdded += _ => Populate();
repo.ObjectUpdated += _ => Populate();
// D.5.4: the table now holds ALL objects (creatures, NPCs, etc.), so filter
// to our 18 shortcut guids — else every creature spawn in a busy zone
// needlessly re-populates the bar (gmToolbarUI::SetDelayedShortcutNum pattern).
repo.ObjectAdded += o => { if (IsShortcutGuid(o.ObjectId)) Populate(); };
repo.ObjectUpdated += o => { if (IsShortcutGuid(o.ObjectId)) Populate(); };
repo.ObjectRemoved += o => { if (IsShortcutGuid(o.ObjectId)) Populate(); };
}
/// <summary>
/// Returns true if <paramref name="guid"/> is one of the currently-active shortcut guids.
/// Used to gate repo-event subscriptions so we don't re-populate on every creature spawn.
/// </summary>
private bool IsShortcutGuid(uint guid)
{
foreach (var sc in _shortcuts())
if (sc.ObjectGuid == guid) return true;
return false;
}
/// <summary>