mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Various modifications to the input manager to allow key rebinding.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -18,7 +19,10 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using static Robust.Client.Input.Keyboard;
|
||||
|
||||
@@ -26,24 +30,37 @@ namespace Robust.Client.Input
|
||||
{
|
||||
internal class InputManager : IInputManager
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
// This is for both userdata and resources.
|
||||
private const string KeybindsPath = "/keybinds.yml";
|
||||
|
||||
public virtual Vector2 MouseScreenPosition => Vector2.Zero;
|
||||
[ViewVariables] public bool Enabled { get; set; } = true;
|
||||
|
||||
[ViewVariables] public virtual Vector2 MouseScreenPosition => Vector2.Zero;
|
||||
|
||||
[Dependency] private readonly IResourceManager _resourceMan = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManagerInternal = default!;
|
||||
|
||||
private readonly List<KeyBindingRegistration> _defaultRegistrations = new List<KeyBindingRegistration>();
|
||||
|
||||
private readonly Dictionary<BoundKeyFunction, InputCmdHandler> _commands =
|
||||
new Dictionary<BoundKeyFunction, InputCmdHandler>();
|
||||
|
||||
private readonly List<KeyBinding> _bindings = new List<KeyBinding>();
|
||||
private readonly Dictionary<BoundKeyFunction, List<KeyBinding>> _bindingsByFunction
|
||||
= new Dictionary<BoundKeyFunction, List<KeyBinding>>();
|
||||
|
||||
// For knowing what to write to config.
|
||||
private readonly HashSet<BoundKeyFunction> _modifiedKeyFunctions = new HashSet<BoundKeyFunction>();
|
||||
|
||||
[ViewVariables] private readonly List<KeyBinding> _bindings = new List<KeyBinding>();
|
||||
private readonly bool[] _keysPressed = new bool[256];
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public BoundKeyMap NetworkBindMap { get; private set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public IInputContextContainer Contexts { get; } = new InputContextContainer();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -64,12 +81,7 @@ namespace Robust.Client.Input
|
||||
|
||||
public string GetKeyFunctionButtonString(BoundKeyFunction function)
|
||||
{
|
||||
IKeyBinding bind;
|
||||
try
|
||||
{
|
||||
bind = GetKeyBinding(function);
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
if (!TryGetKeyBinding(function, out var bind))
|
||||
{
|
||||
return Loc.GetString("<not bound>");
|
||||
}
|
||||
@@ -77,6 +89,11 @@ namespace Robust.Client.Input
|
||||
return bind.GetKeyString();
|
||||
}
|
||||
|
||||
public IEnumerable<IKeyBinding> AllBindings => _bindings;
|
||||
public event KeyEventAction? FirstChanceOnKeyEvent;
|
||||
public event Action<IKeyBinding>? OnKeyBindingAdded;
|
||||
public event Action<IKeyBinding>? OnKeyBindingRemoved;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -87,13 +104,55 @@ namespace Robust.Client.Input
|
||||
|
||||
Contexts.ContextChanged += OnContextChanged;
|
||||
|
||||
var path = new ResourcePath("/keybinds.yml");
|
||||
var path = new ResourcePath(KeybindsPath);
|
||||
if (_resourceMan.UserData.Exists(path))
|
||||
{
|
||||
LoadKeyFile(path, true);
|
||||
}
|
||||
|
||||
if (_resourceMan.ContentFileExists(path))
|
||||
{
|
||||
LoadKeyFile(path);
|
||||
LoadKeyFile(path, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveToUserData()
|
||||
{
|
||||
var mapping = new YamlMappingNode();
|
||||
var ser = YamlObjectSerializer.NewWriter(mapping);
|
||||
|
||||
var modifiedBindings = _modifiedKeyFunctions
|
||||
.Select(p => _bindingsByFunction[p])
|
||||
.SelectMany(p => p)
|
||||
.Select(p => new KeyBindingRegistration
|
||||
{
|
||||
Function = p.Function,
|
||||
BaseKey = p.BaseKey,
|
||||
Mod1 = p.Mod1,
|
||||
Mod2 = p.Mod2,
|
||||
Mod3 = p.Mod3,
|
||||
Priority = p.Priority,
|
||||
Type = p.BindingType,
|
||||
CanFocus = p.CanFocus,
|
||||
CanRepeat = p.CanRepeat
|
||||
}).ToArray();
|
||||
|
||||
var leaveEmpty = _modifiedKeyFunctions
|
||||
.Where(p => _bindingsByFunction[p].Count == 0)
|
||||
.ToArray();
|
||||
|
||||
var version = 1;
|
||||
|
||||
ser.DataField(ref version, "version", 1);
|
||||
ser.DataField(ref modifiedBindings, "binds", null);
|
||||
ser.DataField(ref leaveEmpty, "leaveEmpty", null);
|
||||
|
||||
var path = new ResourcePath(KeybindsPath);
|
||||
using var writer = new StreamWriter(_resourceMan.UserData.Create(path));
|
||||
var stream = new YamlStream {new YamlDocument(mapping)};
|
||||
stream.Save(new YamlMappingFix(new Emitter(writer)), false);
|
||||
}
|
||||
|
||||
private void OnContextChanged(object? sender, ContextChangedEventArgs args)
|
||||
{
|
||||
// keyup any commands that are not in the new contexts, because it will not exist in the new context and get filtered. Luckily
|
||||
@@ -120,6 +179,13 @@ namespace Robust.Client.Input
|
||||
return;
|
||||
}
|
||||
|
||||
FirstChanceOnKeyEvent?.Invoke(args, args.IsRepeat ? KeyEventType.Repeat : KeyEventType.Down);
|
||||
|
||||
if (args.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_keysPressed[(int) args.Key] = true;
|
||||
|
||||
PackedKeyCombo matchedCombo = default;
|
||||
@@ -177,6 +243,8 @@ namespace Robust.Client.Input
|
||||
return;
|
||||
}
|
||||
|
||||
FirstChanceOnKeyEvent?.Invoke(args, KeyEventType.Up);
|
||||
|
||||
var hasCanFocus = false;
|
||||
foreach (var binding in _bindings)
|
||||
{
|
||||
@@ -237,7 +305,7 @@ namespace Robust.Client.Input
|
||||
SetBindState(binding, BoundKeyState.Up);
|
||||
}
|
||||
|
||||
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly=false)
|
||||
private bool SetBindState(KeyBinding binding, BoundKeyState state, bool uiOnly = false)
|
||||
{
|
||||
binding.State = state;
|
||||
|
||||
@@ -307,85 +375,101 @@ namespace Robust.Client.Input
|
||||
return true;
|
||||
}
|
||||
|
||||
private void LoadKeyFile(ResourcePath yamlFile)
|
||||
private void LoadKeyFile(ResourcePath file, bool userData)
|
||||
{
|
||||
YamlDocument document;
|
||||
|
||||
using (var stream = _resourceMan.ContentFileRead(yamlFile))
|
||||
using (var reader = new StreamReader(stream, EncodingHelpers.UTF8))
|
||||
TextReader reader;
|
||||
if (userData)
|
||||
{
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(reader);
|
||||
document = yamlStream.Documents[0];
|
||||
reader = _resourceMan.UserData.OpenText(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader = _resourceMan.ContentFileReadText(file);
|
||||
}
|
||||
|
||||
var mapping = (YamlMappingNode) document.RootNode;
|
||||
foreach (var keyMapping in mapping.GetNode<YamlSequenceNode>("binds").Cast<YamlMappingNode>())
|
||||
var yamlStream = new YamlStream();
|
||||
yamlStream.Load(reader);
|
||||
|
||||
var mapping = (YamlMappingNode) yamlStream.Documents[0].RootNode;
|
||||
|
||||
var baseSerializer = YamlObjectSerializer.NewReader(mapping);
|
||||
var baseKeyRegs = baseSerializer.ReadDataField<KeyBindingRegistration[]>("binds");
|
||||
|
||||
foreach (var reg in baseKeyRegs)
|
||||
{
|
||||
var function = keyMapping.GetNode("function").AsString();
|
||||
if (!NetworkBindMap.FunctionExists(function))
|
||||
if (!NetworkBindMap.FunctionExists(reg.Function.FunctionName))
|
||||
{
|
||||
Logger.ErrorS("input", "Key function in {0} does not exist: '{1}'", yamlFile, function);
|
||||
Logger.ErrorS("input", "Key function in {0} does not exist: '{1}'", file,
|
||||
reg.Function.FunctionName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = keyMapping.GetNode("key").AsEnum<Key>();
|
||||
|
||||
var canFocus = false;
|
||||
if (keyMapping.TryGetNode("canFocus", out var canFocusName))
|
||||
if (!userData)
|
||||
{
|
||||
canFocus = canFocusName.AsBool();
|
||||
_defaultRegistrations.Add(reg);
|
||||
|
||||
if (_modifiedKeyFunctions.Contains(reg.Function))
|
||||
{
|
||||
// Don't read key functions from preset files that have been modified.
|
||||
// So that we don't bulldoze a user's saved preferences.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var canRepeat = false;
|
||||
if (keyMapping.TryGetNode("canRepeat", out var canRepeatName))
|
||||
{
|
||||
canRepeat = canRepeatName.AsBool();
|
||||
}
|
||||
RegisterBinding(reg, markModified: userData);
|
||||
}
|
||||
|
||||
var mod1 = Key.Unknown;
|
||||
if (keyMapping.TryGetNode("mod1", out var mod1Name))
|
||||
{
|
||||
mod1 = mod1Name.AsEnum<Key>();
|
||||
}
|
||||
|
||||
var mod2 = Key.Unknown;
|
||||
if (keyMapping.TryGetNode("mod2", out var mod2Name))
|
||||
{
|
||||
mod2 = mod2Name.AsEnum<Key>();
|
||||
}
|
||||
|
||||
var mod3 = Key.Unknown;
|
||||
if (keyMapping.TryGetNode("mod3", out var mod3Name))
|
||||
{
|
||||
mod3 = mod3Name.AsEnum<Key>();
|
||||
}
|
||||
|
||||
var priority = 0;
|
||||
if (keyMapping.TryGetNode("priority", out var priorityValue))
|
||||
{
|
||||
priority = priorityValue.AsInt();
|
||||
}
|
||||
|
||||
var type = keyMapping.GetNode("type").AsEnum<KeyBindingType>();
|
||||
|
||||
var binding = new KeyBinding(this, function, type, key, canFocus, canRepeat, priority, mod1, mod2,
|
||||
mod3);
|
||||
RegisterBinding(binding);
|
||||
if (userData)
|
||||
{
|
||||
// Adding to _modifiedKeyFunctions means that these keybinds won't be loaded from the base file.
|
||||
// Because they've been explicitly cleared.
|
||||
var leaveEmpty = baseSerializer.ReadDataField<BoundKeyFunction[]>("leaveEmpty");
|
||||
_modifiedKeyFunctions.UnionWith(leaveEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterBinding(BoundKeyFunction function, KeyBindingType bindingType,
|
||||
public IKeyBinding RegisterBinding(BoundKeyFunction function, KeyBindingType bindingType,
|
||||
Key baseKey, Key? mod1, Key? mod2, Key? mod3)
|
||||
{
|
||||
var binding = new KeyBinding(this, function, bindingType, baseKey, false, false,
|
||||
0, mod1 ?? Key.Unknown, mod2 ?? Key.Unknown, mod3 ?? Key.Unknown);
|
||||
|
||||
RegisterBinding(binding);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
private void RegisterBinding(KeyBinding binding)
|
||||
public IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified = true)
|
||||
{
|
||||
var binding = new KeyBinding(this, reg.Function, reg.Type, reg.BaseKey, reg.CanFocus, reg.CanRepeat,
|
||||
reg.Priority, reg.Mod1, reg.Mod2, reg.Mod3);
|
||||
|
||||
RegisterBinding(binding, markModified);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
public void RemoveBinding(IKeyBinding binding, bool markModified = true)
|
||||
{
|
||||
var bindings = _bindingsByFunction[binding.Function];
|
||||
var cast = (KeyBinding) binding;
|
||||
if (!bindings.Remove(cast))
|
||||
{
|
||||
// Keybind does not exist.
|
||||
return;
|
||||
}
|
||||
|
||||
if (markModified)
|
||||
{
|
||||
_modifiedKeyFunctions.Add(binding.Function);
|
||||
}
|
||||
|
||||
_bindings.Remove(cast);
|
||||
OnKeyBindingRemoved?.Invoke(binding);
|
||||
}
|
||||
|
||||
private void RegisterBinding(KeyBinding binding, bool markModified = true)
|
||||
{
|
||||
// we sort larger combos first so they take priority over smaller (single key) combos,
|
||||
// so they get processed first in KeyDown and such.
|
||||
@@ -395,7 +479,14 @@ namespace Robust.Client.Input
|
||||
pos = ~pos;
|
||||
}
|
||||
|
||||
if (markModified)
|
||||
{
|
||||
_modifiedKeyFunctions.Add(binding.Function);
|
||||
}
|
||||
|
||||
_bindings.Insert(pos, binding);
|
||||
_bindingsByFunction.GetOrNew(binding.Function).Add(binding);
|
||||
OnKeyBindingAdded?.Invoke(binding);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -409,10 +500,50 @@ namespace Robust.Client.Input
|
||||
throw new KeyNotFoundException($"No keys are bound for function '{function}'");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetKeyBinding(BoundKeyFunction function, out IKeyBinding binding)
|
||||
public IReadOnlyList<IKeyBinding> GetKeyBindings(BoundKeyFunction function)
|
||||
{
|
||||
binding = _bindings.FirstOrDefault(k => k.Function == function);
|
||||
return _bindingsByFunction.GetOrNew(function);
|
||||
}
|
||||
|
||||
public void ResetBindingsFor(BoundKeyFunction function)
|
||||
{
|
||||
foreach (var binding in GetKeyBindings(function).ToArray())
|
||||
{
|
||||
RemoveBinding(binding);
|
||||
}
|
||||
|
||||
// Mark as unmodified.
|
||||
_modifiedKeyFunctions.Remove(function);
|
||||
|
||||
foreach (var defaultBinding in _defaultRegistrations.Where(p => p.Function == function))
|
||||
{
|
||||
RegisterBinding(defaultBinding, markModified: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetAllBindings()
|
||||
{
|
||||
foreach (var modified in _modifiedKeyFunctions.ToArray())
|
||||
{
|
||||
ResetBindingsFor(modified);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsKeyFunctionModified(BoundKeyFunction function)
|
||||
{
|
||||
return _modifiedKeyFunctions.Contains(function);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetKeyBinding(BoundKeyFunction function, [NotNullWhen(true)] out IKeyBinding? binding)
|
||||
{
|
||||
if (!_bindingsByFunction.TryGetValue(function, out var bindings))
|
||||
{
|
||||
binding = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
binding = bindings.FirstOrDefault();
|
||||
return binding != null;
|
||||
}
|
||||
|
||||
@@ -445,22 +576,29 @@ namespace Robust.Client.Input
|
||||
{
|
||||
private readonly InputManager _inputManager;
|
||||
|
||||
public BoundKeyState State { get; set; }
|
||||
[ViewVariables] public BoundKeyState State { get; set; }
|
||||
public PackedKeyCombo PackedKeyCombo { get; }
|
||||
public BoundKeyFunction Function { get; }
|
||||
public KeyBindingType BindingType { get; }
|
||||
[ViewVariables] public BoundKeyFunction Function { get; }
|
||||
[ViewVariables] public KeyBindingType BindingType { get; }
|
||||
|
||||
[ViewVariables] public Key BaseKey => PackedKeyCombo.BaseKey;
|
||||
[ViewVariables] public Key Mod1 => PackedKeyCombo.Mod1;
|
||||
[ViewVariables] public Key Mod2 => PackedKeyCombo.Mod2;
|
||||
[ViewVariables] public Key Mod3 => PackedKeyCombo.Mod3;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the BoundKey can change the focused control.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool CanFocus { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the BoundKey still triggers while held down.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool CanRepeat { get; internal set; }
|
||||
|
||||
public int Priority { get; internal set; }
|
||||
[ViewVariables] public int Priority { get; internal set; }
|
||||
|
||||
public KeyBinding(InputManager inputManager, BoundKeyFunction function,
|
||||
KeyBindingType bindingType,
|
||||
@@ -518,7 +656,28 @@ namespace Robust.Client.Input
|
||||
}
|
||||
}
|
||||
|
||||
public static IComparer<KeyBinding> ProcessPriorityComparer { get; } = new ProcessPriorityRelationalComparer();
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0}: {1}", Function.FunctionName, BaseKey);
|
||||
if (Mod1 != Key.Unknown)
|
||||
{
|
||||
sb.AppendFormat("+{0}", Mod1);
|
||||
if (Mod2 != Key.Unknown)
|
||||
{
|
||||
sb.AppendFormat("+{0}", Mod2);
|
||||
if (Mod3 != Key.Unknown)
|
||||
{
|
||||
sb.AppendFormat("+{0}", Mod3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static IComparer<KeyBinding> ProcessPriorityComparer { get; } =
|
||||
new ProcessPriorityRelationalComparer();
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
@@ -612,6 +771,7 @@ namespace Robust.Client.Input
|
||||
public string Command => "bind";
|
||||
public string Description => "Binds an input key to an input command.";
|
||||
public string Help => "bind <KeyName> <BindMode> <InputCommand>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
if (args.Length < 3)
|
||||
@@ -628,26 +788,50 @@ namespace Robust.Client.Input
|
||||
|
||||
var keyName = args[0];
|
||||
|
||||
if (!Enum.TryParse(typeof(Keyboard.Key), keyName, true, out var keyIdObj))
|
||||
if (!Enum.TryParse(typeof(Key), keyName, true, out var keyIdObj))
|
||||
{
|
||||
console.AddLine($"Key '{keyName}' is unrecognized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyId = (Keyboard.Key) keyIdObj!;
|
||||
var keyId = (Key) keyIdObj!;
|
||||
|
||||
if (!Enum.TryParse(typeof(KeyBindingType), args[1], true, out var keyModeObj))
|
||||
{
|
||||
console.AddLine($"BindMode '{args[1]}' is unrecognized.");
|
||||
return false;
|
||||
}
|
||||
var keyMode = (KeyBindingType)keyModeObj!;
|
||||
|
||||
var keyMode = (KeyBindingType) keyModeObj!;
|
||||
|
||||
var inputCommand = args[2];
|
||||
|
||||
var inputMan = IoCManager.Resolve<IInputManager>();
|
||||
|
||||
inputMan.RegisterBinding(new BoundKeyFunction(inputCommand), keyMode, keyId, null, null, null);
|
||||
var registration = new KeyBindingRegistration
|
||||
{
|
||||
Function = new BoundKeyFunction(inputCommand),
|
||||
BaseKey = keyId,
|
||||
Type = keyMode
|
||||
};
|
||||
|
||||
inputMan.RegisterBinding(registration);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
internal class SaveBindCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "svbind";
|
||||
public string Description => "";
|
||||
public string Help => "";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IInputManager>()
|
||||
.SaveToUserData();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
20
Robust.Client/Input/KeyEventType.cs
Normal file
20
Robust.Client/Input/KeyEventType.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace Robust.Client.Input
|
||||
{
|
||||
public enum KeyEventType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// This key is pressed down.
|
||||
/// </summary>
|
||||
Down,
|
||||
|
||||
/// <summary>
|
||||
/// This key was repeated by the operating system while already being pressed down.
|
||||
/// </summary>
|
||||
Repeat,
|
||||
|
||||
/// <summary>
|
||||
/// This key has been released.
|
||||
/// </summary>
|
||||
Up
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.IoC;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Input;
|
||||
@@ -9,6 +8,8 @@ using Robust.Shared.Input.Binding;
|
||||
|
||||
namespace Robust.Client.Interfaces.Input
|
||||
{
|
||||
public delegate void KeyEventAction(KeyEventArgs keyEvent, KeyEventType type);
|
||||
|
||||
/// <summary>
|
||||
/// Manages key bindings, input commands and other misc. input systems.
|
||||
/// </summary>
|
||||
@@ -23,30 +24,32 @@ namespace Robust.Client.Interfaces.Input
|
||||
IInputContextContainer Contexts { get; }
|
||||
|
||||
void Initialize();
|
||||
void SaveToUserData();
|
||||
|
||||
void KeyDown(KeyEventArgs e);
|
||||
void KeyUp(KeyEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new key binding in the input manager.
|
||||
/// </summary>
|
||||
/// <param name="function">The function the key binding is bound to.</param>
|
||||
/// <param name="bindingType"></param>
|
||||
/// <param name="baseKey"></param>
|
||||
/// <param name="mod1"></param>
|
||||
/// <param name="mod2"></param>
|
||||
/// <param name="mod3"></param>
|
||||
void RegisterBinding(BoundKeyFunction function, KeyBindingType bindingType,
|
||||
Keyboard.Key baseKey, Keyboard.Key? mod1, Keyboard.Key? mod2, Keyboard.Key? mod3);
|
||||
IKeyBinding RegisterBinding(in KeyBindingRegistration reg, bool markModified=true);
|
||||
|
||||
void RemoveBinding(IKeyBinding binding, bool markModified=true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a key binding according to the function it is bound to.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Which keybind is returned if there are multiple for a key function is unspecified.
|
||||
/// </remarks>
|
||||
/// <param name="function">The function the key binding is bound to.</param>
|
||||
/// <returns>The key binding.</returns>
|
||||
/// <exception cref="KeyNotFoundException">
|
||||
/// Thrown if no there is no keybind for the specified function.
|
||||
/// </exception>
|
||||
IKeyBinding GetKeyBinding(BoundKeyFunction function);
|
||||
|
||||
bool TryGetKeyBinding(BoundKeyFunction function, out IKeyBinding binding);
|
||||
/// <remarks>
|
||||
/// Which keybind is returned if there are multiple for a key function is unspecified.
|
||||
/// </remarks>
|
||||
bool TryGetKeyBinding(BoundKeyFunction function, [NotNullWhen(true)] out IKeyBinding? binding);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the input command bound to a key function.
|
||||
@@ -79,5 +82,41 @@ namespace Robust.Client.Interfaces.Input
|
||||
/// of buttons is bound to the specified key function.
|
||||
/// </summary>
|
||||
string GetKeyFunctionButtonString(BoundKeyFunction function);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the key bindings currently registered into the manager.
|
||||
/// </summary>
|
||||
IEnumerable<IKeyBinding> AllBindings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An event that gets fired before everything else when a key event comes in.
|
||||
/// For key down events, the event can be handled to block it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Do not use this for regular input handling.
|
||||
/// This is a low-level API intended solely for stuff like the key rebinding menu.
|
||||
/// </remarks>
|
||||
event KeyEventAction FirstChanceOnKeyEvent;
|
||||
|
||||
event Action<IKeyBinding> OnKeyBindingAdded;
|
||||
event Action<IKeyBinding> OnKeyBindingRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the keybinds bound to a specific function.
|
||||
/// </summary>
|
||||
IReadOnlyList<IKeyBinding> GetKeyBindings(BoundKeyFunction function);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the bindings for a specific BoundKeyFunction to the defaults from the resource files.
|
||||
/// </summary>
|
||||
/// <param name="function">The key function to reset the bindings for.</param>
|
||||
void ResetBindingsFor(BoundKeyFunction function);
|
||||
|
||||
/// <summary>
|
||||
/// Resets ALL the keybinds to the defaults from the resource files.
|
||||
/// </summary>
|
||||
void ResetAllBindings();
|
||||
|
||||
bool IsKeyFunctionModified(BoundKeyFunction function);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,15 @@ namespace Robust.Client.Interfaces.Input
|
||||
BoundKeyFunction Function { get; }
|
||||
KeyBindingType BindingType { get; }
|
||||
|
||||
Keyboard.Key BaseKey { get; }
|
||||
Keyboard.Key Mod1 { get; }
|
||||
Keyboard.Key Mod2 { get; }
|
||||
Keyboard.Key Mod3 { get; }
|
||||
|
||||
bool CanFocus { get; }
|
||||
bool CanRepeat { get; }
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user-presentable, localized & keyboard-adjusted string for which buttons the user has to press.
|
||||
/// </summary>
|
||||
|
||||
33
Robust.Client/Interfaces/Input/KeyBindingRegistration.cs
Normal file
33
Robust.Client/Interfaces/Input/KeyBindingRegistration.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Robust.Client.Input;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Client.Interfaces.Input
|
||||
{
|
||||
public struct KeyBindingRegistration : IExposeData
|
||||
{
|
||||
public BoundKeyFunction Function;
|
||||
public KeyBindingType Type;
|
||||
public Keyboard.Key BaseKey;
|
||||
public Keyboard.Key Mod1;
|
||||
public Keyboard.Key Mod2;
|
||||
public Keyboard.Key Mod3;
|
||||
public int Priority;
|
||||
public bool CanFocus;
|
||||
public bool CanRepeat;
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref Function, "function", default);
|
||||
serializer.DataField(ref Type, "type", KeyBindingType.State);
|
||||
serializer.DataField(ref BaseKey, "key", default);
|
||||
serializer.DataField(ref Mod1, "mod1", default);
|
||||
serializer.DataField(ref Mod2, "mod2", default);
|
||||
serializer.DataField(ref Mod3, "mod3", default);
|
||||
serializer.DataField(ref Priority, "priority", 0);
|
||||
serializer.DataField(ref CanFocus, "canFocus", false);
|
||||
serializer.DataField(ref CanRepeat, "canRepeat", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Input
|
||||
@@ -72,7 +73,7 @@ namespace Robust.Shared.Input
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public struct BoundKeyFunction : IComparable, IComparable<BoundKeyFunction>, IEquatable<BoundKeyFunction>
|
||||
public struct BoundKeyFunction : IComparable, IComparable<BoundKeyFunction>, IEquatable<BoundKeyFunction>, ISelfSerialize
|
||||
{
|
||||
public readonly string FunctionName;
|
||||
|
||||
@@ -86,14 +87,14 @@ namespace Robust.Shared.Input
|
||||
return new BoundKeyFunction(name);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"KeyFunction({FunctionName})";
|
||||
}
|
||||
|
||||
#region Code for easy equality and sorting.
|
||||
|
||||
public int CompareTo(object? obj)
|
||||
public readonly int CompareTo(object? obj)
|
||||
{
|
||||
if (!(obj is BoundKeyFunction func))
|
||||
{
|
||||
@@ -102,23 +103,23 @@ namespace Robust.Shared.Input
|
||||
return CompareTo(func);
|
||||
}
|
||||
|
||||
public int CompareTo(BoundKeyFunction other)
|
||||
public readonly int CompareTo(BoundKeyFunction other)
|
||||
{
|
||||
return string.Compare(FunctionName, other.FunctionName, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
// Could maybe go dirty and optimize these on the assumption that they're singletons.
|
||||
public override bool Equals(object? obj)
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is BoundKeyFunction func && Equals(func);
|
||||
}
|
||||
|
||||
public bool Equals(BoundKeyFunction other)
|
||||
public readonly bool Equals(BoundKeyFunction other)
|
||||
{
|
||||
return other.FunctionName == FunctionName;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return FunctionName.GetHashCode();
|
||||
}
|
||||
@@ -134,6 +135,16 @@ namespace Robust.Shared.Input
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Deserialize(string value)
|
||||
{
|
||||
this = new BoundKeyFunction(value);
|
||||
}
|
||||
|
||||
public readonly string Serialize()
|
||||
{
|
||||
return FunctionName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user