Allow left/right modifier keys to be used separately (no UI)
Should allow for any key to be used as a modifier (see #2981), simply by populating `Config.ModifierKeys`. Flip the assignment on `Input.cs:45` to try out this change specifically.
This commit is contained in:
parent
3f9fb0eaef
commit
accf0f038c
|
@ -5,6 +5,8 @@ using BizHawk.Common;
|
||||||
using BizHawk.Common.PathExtensions;
|
using BizHawk.Common.PathExtensions;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using BizHawk.Emulation.Cores;
|
using BizHawk.Emulation.Cores;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
namespace BizHawk.Client.Common
|
||||||
|
@ -332,5 +334,12 @@ namespace BizHawk.Client.Common
|
||||||
public EHostInputMethod HostInputMethod { get; set; } = HostCapabilityDetector.HasDirectX ? EHostInputMethod.DirectInput : EHostInputMethod.OpenTK;
|
public EHostInputMethod HostInputMethod { get; set; } = HostCapabilityDetector.HasDirectX ? EHostInputMethod.DirectInput : EHostInputMethod.OpenTK;
|
||||||
|
|
||||||
public bool UseStaticWindowTitles { get; set; }
|
public bool UseStaticWindowTitles { get; set; }
|
||||||
|
|
||||||
|
public List<string> ModifierKeys { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public IReadOnlyList<string> ModifierKeysEffective;
|
||||||
|
|
||||||
|
public bool MergeLAndRModifierKeys { get; set; } = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
|
@ -29,25 +31,31 @@ namespace BizHawk.Client.Common
|
||||||
Press, Release
|
Press, Release
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct LogicalButton
|
public struct LogicalButton
|
||||||
{
|
{
|
||||||
|
public const uint MASK_ALT = 1U << 2;
|
||||||
|
|
||||||
|
public const uint MASK_CTRL = 1U << 1;
|
||||||
|
|
||||||
|
public const uint MASK_SHIFT = 1U << 3;
|
||||||
|
|
||||||
|
public const uint MASK_WIN = 1U << 0;
|
||||||
|
|
||||||
public static bool operator ==(LogicalButton lhs, LogicalButton rhs)
|
public static bool operator ==(LogicalButton lhs, LogicalButton rhs)
|
||||||
=> lhs.Button == rhs.Button && lhs.Modifiers == rhs.Modifiers;
|
=> lhs.Button == rhs.Button && lhs.Modifiers == rhs.Modifiers;
|
||||||
|
|
||||||
public static bool operator !=(LogicalButton lhs, LogicalButton rhs) => !(lhs == rhs);
|
public static bool operator !=(LogicalButton lhs, LogicalButton rhs) => !(lhs == rhs);
|
||||||
|
|
||||||
public bool Alt => (Modifiers & ModifierKey.Alt) != 0;
|
/// <remarks>pretty sure these are always consumed during the same iteration of the main program loop, but ¯\_(ツ)_/¯ better safe than sorry --yoshi</remarks>
|
||||||
|
private readonly Func<IReadOnlyList<string>> _getEffectiveModListCallback;
|
||||||
|
|
||||||
public readonly string Button;
|
public readonly string Button;
|
||||||
|
|
||||||
public bool Control => (Modifiers & ModifierKey.Control) != 0;
|
public readonly uint Modifiers;
|
||||||
|
|
||||||
public readonly ModifierKey Modifiers;
|
public LogicalButton(string button, uint modifiers, Func<IReadOnlyList<string>> getEffectiveModListCallback)
|
||||||
|
|
||||||
public bool Shift => (Modifiers & ModifierKey.Shift) != 0;
|
|
||||||
|
|
||||||
public LogicalButton(string button, ModifierKey modifiers)
|
|
||||||
{
|
{
|
||||||
|
_getEffectiveModListCallback = getEffectiveModListCallback;
|
||||||
Button = button;
|
Button = button;
|
||||||
Modifiers = modifiers;
|
Modifiers = modifiers;
|
||||||
}
|
}
|
||||||
|
@ -58,12 +66,19 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
public override readonly string ToString()
|
public override readonly string ToString()
|
||||||
{
|
{
|
||||||
var ret = "";
|
if (Modifiers is 0U) return Button;
|
||||||
if (Control) ret += "Ctrl+";
|
var allMods = _getEffectiveModListCallback();
|
||||||
if (Alt) ret += "Alt+";
|
StringBuilder ret = new();
|
||||||
if (Shift) ret += "Shift+";
|
for (var i = 0; i < allMods.Count; i++)
|
||||||
ret += Button;
|
{
|
||||||
return ret;
|
var b = 1U << i;
|
||||||
|
if ((Modifiers & b) is not 0U)
|
||||||
|
{
|
||||||
|
ret.Append(allMods[i]);
|
||||||
|
ret.Append('+');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret + Button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
public enum AllowInput
|
public enum AllowInput
|
||||||
|
@ -8,19 +6,4 @@ namespace BizHawk.Client.Common
|
||||||
All = 1,
|
All = 1,
|
||||||
OnlyController = 2
|
OnlyController = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum ModifierKey
|
|
||||||
{
|
|
||||||
/// <summary>The bitmask to extract modifiers from a key value.</summary>
|
|
||||||
Modifiers = -65536,
|
|
||||||
/// <summary>No key pressed.</summary>
|
|
||||||
None = 0,
|
|
||||||
/// <summary>The SHIFT modifier key.</summary>
|
|
||||||
Shift = 65536,
|
|
||||||
/// <summary>The CTRL modifier key.</summary>
|
|
||||||
Control = 131072,
|
|
||||||
/// <summary>The ALT modifier key.</summary>
|
|
||||||
Alt = 262144,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using BizHawk.Bizware.DirectX;
|
||||||
using BizHawk.Bizware.OpenTK3;
|
using BizHawk.Bizware.OpenTK3;
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
using BizHawk.Client.Common;
|
using BizHawk.Client.Common;
|
||||||
|
using BizHawk.Common.CollectionExtensions;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
|
@ -33,15 +34,20 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
public readonly IHostInputAdapter Adapter;
|
public readonly IHostInputAdapter Adapter;
|
||||||
|
|
||||||
|
private Config _currentConfig;
|
||||||
|
|
||||||
private readonly Func<Config> _getConfigCallback;
|
private readonly Func<Config> _getConfigCallback;
|
||||||
|
|
||||||
internal Input(IntPtr mainFormHandle, Func<Config> getConfigCallback, Func<bool, AllowInput> mainFormInputAllowedCallback)
|
internal Input(IntPtr mainFormHandle, Func<Config> getConfigCallback, Func<bool, AllowInput> mainFormInputAllowedCallback)
|
||||||
{
|
{
|
||||||
_getConfigCallback = getConfigCallback;
|
_getConfigCallback = getConfigCallback;
|
||||||
|
_currentConfig = _getConfigCallback();
|
||||||
|
_currentConfig.MergeLAndRModifierKeys = true; // for debugging
|
||||||
|
UpdateModifierKeysEffective();
|
||||||
|
|
||||||
MainFormInputAllowedCallback = mainFormInputAllowedCallback;
|
MainFormInputAllowedCallback = mainFormInputAllowedCallback;
|
||||||
|
|
||||||
var config = _getConfigCallback();
|
Adapter = _currentConfig.HostInputMethod switch
|
||||||
Adapter = config.HostInputMethod switch
|
|
||||||
{
|
{
|
||||||
EHostInputMethod.OpenTK => new OpenTKInputAdapter(),
|
EHostInputMethod.OpenTK => new OpenTKInputAdapter(),
|
||||||
_ when OSTailoredCode.IsUnixHost => new OpenTKInputAdapter(),
|
_ when OSTailoredCode.IsUnixHost => new OpenTKInputAdapter(),
|
||||||
|
@ -49,7 +55,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_ => throw new Exception()
|
_ => throw new Exception()
|
||||||
};
|
};
|
||||||
Console.WriteLine($"Using {Adapter.Desc} for host input (keyboard + gamepads)");
|
Console.WriteLine($"Using {Adapter.Desc} for host input (keyboard + gamepads)");
|
||||||
Adapter.UpdateConfig(config);
|
Adapter.UpdateConfig(_currentConfig);
|
||||||
Adapter.FirstInitAll(mainFormHandle);
|
Adapter.FirstInitAll(mainFormHandle);
|
||||||
_updateThread = new Thread(UpdateThreadProc)
|
_updateThread = new Thread(UpdateThreadProc)
|
||||||
{
|
{
|
||||||
|
@ -66,26 +72,36 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private bool _trackDeltas;
|
private bool _trackDeltas;
|
||||||
private bool _ignoreEventsNextPoll;
|
private bool _ignoreEventsNextPoll;
|
||||||
|
|
||||||
|
private static readonly IReadOnlyList<string> ModifierKeysBase = new[] { "Win", "Ctrl", "Alt", "Shift" };
|
||||||
|
|
||||||
|
private static readonly IReadOnlyList<string> ModifierKeysBaseUnmerged = new[] { "Win", "Ctrl", "Alt", "Shift", "LeftWin", "RightWin", "LeftCtrl", "RightCtrl", "LeftAlt", "RightAlt", "LeftShift", "RightShift" };
|
||||||
|
|
||||||
|
public void UpdateModifierKeysEffective()
|
||||||
|
=> _currentConfig.ModifierKeysEffective = (_currentConfig.MergeLAndRModifierKeys ? ModifierKeysBase : ModifierKeysBaseUnmerged)
|
||||||
|
.Concat(_currentConfig.ModifierKeys)
|
||||||
|
.Take(32).ToArray();
|
||||||
|
|
||||||
|
private readonly IReadOnlyDictionary<string, string> _modifierKeyPreMap = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["LeftWin"] = "Win",
|
||||||
|
["RightWin"] = "Win",
|
||||||
|
["LeftCtrl"] = "Ctrl",
|
||||||
|
["RightCtrl"] = "Ctrl",
|
||||||
|
["LeftAlt"] = "Alt",
|
||||||
|
["RightAlt"] = "Alt",
|
||||||
|
["LeftShift"] = "Shift",
|
||||||
|
["RightShift"] = "Shift",
|
||||||
|
};
|
||||||
|
|
||||||
private void HandleButton(string button, bool newState, ClientInputFocus source)
|
private void HandleButton(string button, bool newState, ClientInputFocus source)
|
||||||
{
|
{
|
||||||
var currentModifier = button switch
|
if (!(_currentConfig.MergeLAndRModifierKeys &&_modifierKeyPreMap.TryGetValue(button, out var button1))) button1 = button;
|
||||||
{
|
var modIndex = _currentConfig.ModifierKeysEffective.IndexOf(button1);
|
||||||
// "LeftWin" => ModifierKey.Win,
|
var currentModifier = modIndex is -1 ? 0U : 1U << modIndex;
|
||||||
// "RightWin" => ModifierKey.Win,
|
if (EnableIgnoreModifiers && currentModifier is not 0U) return;
|
||||||
"LeftShift" => ModifierKey.Shift,
|
if (newState == _lastState[button1]) return;
|
||||||
"RightShift" => ModifierKey.Shift,
|
|
||||||
"LeftCtrl" => ModifierKey.Control,
|
|
||||||
"RightCtrl" => ModifierKey.Control,
|
|
||||||
"LeftAlt" => ModifierKey.Alt,
|
|
||||||
"RightAlt" => ModifierKey.Alt,
|
|
||||||
_ => ModifierKey.None
|
|
||||||
};
|
|
||||||
if (EnableIgnoreModifiers && currentModifier != ModifierKey.None) return;
|
|
||||||
if (_lastState[button] == newState) return;
|
|
||||||
|
|
||||||
// apply
|
if (currentModifier is not 0U)
|
||||||
// NOTE: this is not quite right. if someone held leftshift+rightshift it would be broken. seems unlikely, though.
|
|
||||||
if (currentModifier != ModifierKey.None)
|
|
||||||
{
|
{
|
||||||
if (newState)
|
if (newState)
|
||||||
_modifiers |= currentModifier;
|
_modifiers |= currentModifier;
|
||||||
|
@ -94,17 +110,17 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't generate events for things like Ctrl+LeftControl
|
// don't generate events for things like Ctrl+LeftControl
|
||||||
ModifierKey mods = _modifiers;
|
var mods = _modifiers;
|
||||||
if (currentModifier != ModifierKey.None)
|
if (currentModifier is not 0U)
|
||||||
mods &= ~currentModifier;
|
mods &= ~currentModifier;
|
||||||
|
|
||||||
var ie = new InputEvent
|
var ie = new InputEvent
|
||||||
{
|
{
|
||||||
EventType = newState ? InputEventType.Press : InputEventType.Release,
|
EventType = newState ? InputEventType.Press : InputEventType.Release,
|
||||||
LogicalButton = new LogicalButton(button, mods),
|
LogicalButton = new(button1, mods, () => _getConfigCallback().ModifierKeysEffective),
|
||||||
Source = source
|
Source = source
|
||||||
};
|
};
|
||||||
_lastState[button] = newState;
|
_lastState[button1] = newState;
|
||||||
|
|
||||||
// track the pressed events with modifiers that we send so that we can send corresponding unpresses with modifiers
|
// track the pressed events with modifiers that we send so that we can send corresponding unpresses with modifiers
|
||||||
// this is an interesting idea, which we may need later, but not yet.
|
// this is an interesting idea, which we may need later, but not yet.
|
||||||
|
@ -115,11 +131,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
// so, i am adding it as of 11-sep-2011
|
// so, i am adding it as of 11-sep-2011
|
||||||
if (newState)
|
if (newState)
|
||||||
{
|
{
|
||||||
_modifierState[button] = ie.LogicalButton;
|
_modifierState[button1] = ie.LogicalButton;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_modifierState.TryGetValue(button, out var buttonModifierState))
|
if (_modifierState.TryGetValue(button1, out var buttonModifierState))
|
||||||
{
|
{
|
||||||
if (buttonModifierState != ie.LogicalButton && !_ignoreEventsNextPoll)
|
if (buttonModifierState != ie.LogicalButton && !_ignoreEventsNextPoll)
|
||||||
{
|
{
|
||||||
|
@ -131,7 +147,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Source = source
|
Source = source
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_modifierState.Remove(button);
|
_modifierState.Remove(button1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +163,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_axisValues[axis] = newValue;
|
_axisValues[axis] = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModifierKey _modifiers;
|
private uint _modifiers;
|
||||||
private readonly List<InputEvent> _newEvents = new List<InputEvent>();
|
private readonly List<InputEvent> _newEvents = new List<InputEvent>();
|
||||||
|
|
||||||
public void ClearEvents()
|
public void ClearEvents()
|
||||||
|
@ -245,7 +261,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
};
|
};
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Adapter.UpdateConfig(_getConfigCallback());
|
_currentConfig = _getConfigCallback();
|
||||||
|
Adapter.UpdateConfig(_currentConfig);
|
||||||
|
|
||||||
var keyEvents = Adapter.ProcessHostKeyboards();
|
var keyEvents = Adapter.ProcessHostKeyboards();
|
||||||
Adapter.PreprocessHostGamepads();
|
Adapter.PreprocessHostGamepads();
|
||||||
|
@ -307,7 +324,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
foreach (var ie in _newEvents)
|
foreach (var ie in _newEvents)
|
||||||
{
|
{
|
||||||
//events are swallowed in some cases:
|
//events are swallowed in some cases:
|
||||||
if (ie.LogicalButton.Alt && ShouldSwallow(MainFormInputAllowedCallback(true), ie))
|
if ((ie.LogicalButton.Modifiers & LogicalButton.MASK_ALT) is not 0U && ShouldSwallow(MainFormInputAllowedCallback(true), ie))
|
||||||
continue;
|
continue;
|
||||||
if (ie.EventType == InputEventType.Press && ShouldSwallow(allowInput, ie))
|
if (ie.EventType == InputEventType.Press && ShouldSwallow(allowInput, ie))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -992,9 +992,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
if (triggers.Count == 0)
|
if (triggers.Count == 0)
|
||||||
{
|
{
|
||||||
// Maybe it is a system alt-key which hasn't been overridden
|
// Maybe it is a system alt-key which hasn't been overridden
|
||||||
if (ie.EventType is InputEventType.Press)
|
if (ie.EventType is InputEventType.Press && (ie.LogicalButton.Modifiers & LogicalButton.MASK_ALT) is not 0U)
|
||||||
{
|
{
|
||||||
if (ie.LogicalButton.Alt && ie.LogicalButton.Button.Length == 1)
|
if (ie.LogicalButton.Button.Length == 1)
|
||||||
{
|
{
|
||||||
var c = ie.LogicalButton.Button.ToLower()[0];
|
var c = ie.LogicalButton.Button.ToLower()[0];
|
||||||
if ((c >= 'a' && c <= 'z') || c == ' ')
|
if ((c >= 'a' && c <= 'z') || c == ' ')
|
||||||
|
@ -1002,8 +1002,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
SendAltKeyChar(c);
|
SendAltKeyChar(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (ie.LogicalButton.Button == "Space")
|
||||||
if (ie.LogicalButton.Alt && ie.LogicalButton.Button == "Space")
|
|
||||||
{
|
{
|
||||||
SendPlainAltKey(32);
|
SendPlainAltKey(32);
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
private Control CreateNormalPanel(Dictionary<string, string> settings, List<string> buttons, Size size)
|
private Control CreateNormalPanel(Dictionary<string, string> settings, List<string> buttons, Size size)
|
||||||
{
|
{
|
||||||
var cp = new ControllerConfigPanel { Dock = DockStyle.Fill, AutoScroll = true, Tooltip = toolTip1 };
|
ControllerConfigPanel cp = new(_config.ModifierKeysEffective) { Dock = DockStyle.Fill, AutoScroll = true, Tooltip = toolTip1 };
|
||||||
cp.LoadSettings(settings, checkBoxAutoTab.Checked, buttons, size.Width, size.Height);
|
cp.LoadSettings(settings, checkBoxAutoTab.Checked, buttons, size.Width, size.Height);
|
||||||
return cp;
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using BizHawk.Client.Common;
|
||||||
|
|
||||||
namespace BizHawk.Client.EmuHawk
|
namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
// this is a little messy right now because of remnants of the old config system
|
// this is a little messy right now because of remnants of the old config system
|
||||||
|
@ -33,8 +35,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
private bool _autoTab;
|
private bool _autoTab;
|
||||||
|
|
||||||
public ControllerConfigPanel()
|
private readonly IReadOnlyList<string> _effectiveModList;
|
||||||
|
|
||||||
|
public ControllerConfigPanel(IReadOnlyList<string> effectiveModList)
|
||||||
{
|
{
|
||||||
|
_effectiveModList = effectiveModList;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +126,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
x += columnWidth;
|
x += columnWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
var iw = new InputCompositeWidget
|
var iw = new InputCompositeWidget(_effectiveModList)
|
||||||
{
|
{
|
||||||
Location = new Point(x, y),
|
Location = new Point(x, y),
|
||||||
Size = new Size(_inputSize, UIHelper.ScaleY(23)),
|
Size = new Size(_inputSize, UIHelper.ScaleY(23)),
|
||||||
|
|
|
@ -110,7 +110,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Size = new Size(iwOffsetX - UIHelper.ScaleX(2), UIHelper.ScaleY(15))
|
Size = new Size(iwOffsetX - UIHelper.ScaleX(2), UIHelper.ScaleY(15))
|
||||||
};
|
};
|
||||||
|
|
||||||
var w = new InputCompositeWidget
|
var w = new InputCompositeWidget(_config.ModifierKeysEffective)
|
||||||
{
|
{
|
||||||
Location = new Point(x + iwOffsetX, y + iwOffsetY),
|
Location = new Point(x + iwOffsetX, y + iwOffsetY),
|
||||||
AutoTab = AutoTabCheckBox.Checked,
|
AutoTab = AutoTabCheckBox.Checked,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
using BizHawk.Client.Common;
|
using BizHawk.Client.Common;
|
||||||
|
@ -7,8 +8,12 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
public partial class InputCompositeWidget : UserControl
|
public partial class InputCompositeWidget : UserControl
|
||||||
{
|
{
|
||||||
public InputCompositeWidget()
|
private readonly IReadOnlyList<string> _effectiveModList;
|
||||||
|
|
||||||
|
public InputCompositeWidget(IReadOnlyList<string> effectiveModList)
|
||||||
{
|
{
|
||||||
|
_effectiveModList = effectiveModList;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
btnSpecial.Image = Properties.Resources.ArrowBlackDown;
|
btnSpecial.Image = Properties.Resources.ArrowBlackDown;
|
||||||
|
|
||||||
|
@ -82,24 +87,24 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
private void DropdownMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
|
private void DropdownMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
|
||||||
{
|
{
|
||||||
var mods = ModifierKey.None;
|
var mods = 0U;
|
||||||
|
|
||||||
if ((ModifierKeys & Keys.Shift) != 0)
|
if ((ModifierKeys & Keys.Shift) != 0)
|
||||||
{
|
{
|
||||||
mods |= ModifierKey.Shift;
|
mods |= LogicalButton.MASK_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ModifierKeys & Keys.Control) != 0)
|
if ((ModifierKeys & Keys.Control) != 0)
|
||||||
{
|
{
|
||||||
mods |= ModifierKey.Control;
|
mods |= LogicalButton.MASK_CTRL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ModifierKeys & Keys.Alt) != 0)
|
if ((ModifierKeys & Keys.Alt) != 0)
|
||||||
{
|
{
|
||||||
mods |= ModifierKey.Alt;
|
mods |= LogicalButton.MASK_ALT;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogicalButton lb = new(e.ClickedItem.Text, mods);
|
LogicalButton lb = new(e.ClickedItem.Text, mods, () => _effectiveModList);
|
||||||
|
|
||||||
widget.SetBinding(lb.ToString());
|
widget.SetBinding(lb.ToString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,19 @@ namespace BizHawk.Common.CollectionExtensions
|
||||||
foreach (var item in collection) list.Add(item);
|
foreach (var item in collection) list.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IList{T}.IndexOf"/>
|
||||||
|
/// <remarks>
|
||||||
|
/// (This is an extension method which reimplements <see cref="IList{T}.IndexOf"/> for other <see cref="IReadOnlyList{T}">collections</see>.
|
||||||
|
/// It defers to the existing <see cref="IList{T}.IndexOf">IndexOf</see> if the receiver's type is <see cref="IList{T}"/> or a subtype.)
|
||||||
|
/// </remarks>
|
||||||
|
public static int IndexOf<T>(this IReadOnlyList<T> list, T elem)
|
||||||
|
where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
if (list is IList<T> listImpl) return listImpl.IndexOf(elem);
|
||||||
|
for (int i = 0, l = list.Count; i < l; i++) if (elem.Equals(list[i])) return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public static T? FirstOrNull<T>(this IEnumerable<T> list, Func<T, bool> predicate)
|
public static T? FirstOrNull<T>(this IEnumerable<T> list, Func<T, bool> predicate)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,7 @@ namespace BizHawk.Tests.Client.Common.config
|
||||||
typeof(Dictionary<,>),
|
typeof(Dictionary<,>),
|
||||||
typeof(int),
|
typeof(int),
|
||||||
typeof(JToken),
|
typeof(JToken),
|
||||||
|
typeof(List<>),
|
||||||
typeof(Nullable<>),
|
typeof(Nullable<>),
|
||||||
typeof(object),
|
typeof(object),
|
||||||
typeof(float),
|
typeof(float),
|
||||||
|
@ -78,6 +79,7 @@ namespace BizHawk.Tests.Client.Common.config
|
||||||
groupDesc ??= t.Name;
|
groupDesc ??= t.Name;
|
||||||
foreach (var mi in t.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
foreach (var mi in t.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
{
|
{
|
||||||
|
if (mi.GetCustomAttribute<JsonIgnoreAttribute>() is not null) continue;
|
||||||
if (mi is PropertyInfo pi) CheckMemberAndTypeParams(pi.PropertyType, groupDesc);
|
if (mi is PropertyInfo pi) CheckMemberAndTypeParams(pi.PropertyType, groupDesc);
|
||||||
else if (mi is FieldInfo fi) CheckMemberAndTypeParams(fi.FieldType, groupDesc);
|
else if (mi is FieldInfo fi) CheckMemberAndTypeParams(fi.FieldType, groupDesc);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue