feat(ui): complete retail quick-slot input

Port global toolbar use/select/create actions, migrate the collapsed Ctrl-number bindings, and route them through a focused retained-UI controller. Preserve shortcut aliases as aliases across inventory and paperdoll drops with retail's neutral/accept/reject drag states, preventing physical item moves such as unwielding an equipped helmet.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-11 08:59:49 +02:00
parent 6fcc510d5d
commit e65119f0c6
24 changed files with 650 additions and 69 deletions

View file

@ -191,6 +191,38 @@ public class KeyBindingsJsonTests
}
}
[Fact]
public void LoadOrDefault_migratesV1CtrlNumberQuickSlotFromUseToSelect()
{
var path = TempFile();
try
{
const string json = """
{
"version": 1,
"actions": {
"UseQuickSlot_5": [
{ "key": "Number5" },
{ "key": "Number5", "mod": "Ctrl" }
]
}
}
""";
File.WriteAllText(path, json);
var loaded = KeyBindings.LoadOrDefault(path);
Assert.Equal(InputAction.UseQuickSlot_5,
loaded.Find(new KeyChord(Key.Number5, ModifierMask.None), ActivationType.Press)?.Action);
Assert.Equal(InputAction.SelectQuickSlot_5,
loaded.Find(new KeyChord(Key.Number5, ModifierMask.Ctrl), ActivationType.Press)?.Action);
}
finally
{
if (File.Exists(path)) File.Delete(path);
}
}
[Fact]
public void LoadOrDefault_unknown_action_name_skipped_silently()
{

View file

@ -109,12 +109,14 @@ public class KeyBindingsRetailTests
}
[Fact]
public void UseQuickSlot_5_bound_to_Number5_with_and_without_Ctrl()
public void QuickSlot_5_bareUsesAndCtrlSelects()
{
var b = KeyBindings.RetailDefaults();
var binds = b.ForAction(InputAction.UseQuickSlot_5).ToList();
Assert.Contains(binds, x => x.Chord == new KeyChord(Key.Number5, ModifierMask.None));
Assert.Contains(binds, x => x.Chord == new KeyChord(Key.Number5, ModifierMask.Ctrl));
var bare = b.Find(new KeyChord(Key.Number5, ModifierMask.None), ActivationType.Press);
var ctrl = b.Find(new KeyChord(Key.Number5, ModifierMask.Ctrl), ActivationType.Press);
Assert.Equal(InputAction.UseQuickSlot_5, bare?.Action);
Assert.Equal(InputAction.SelectQuickSlot_5, ctrl?.Action);
}
[Fact]