Ability to assign hotkeys to cycle controllers for players (WIP)
This commit is contained in:
parent
0137c9e635
commit
6a555ee9f7
9 changed files with 313 additions and 166 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
|
@ -11,5 +13,6 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||||
public Key ResScaleDown { get; set; }
|
public Key ResScaleDown { get; set; }
|
||||||
public Key VolumeUp { get; set; }
|
public Key VolumeUp { get; set; }
|
||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
|
public List<Key> CycleControllers { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
|
@ -46,6 +47,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -1200,6 +1202,24 @@ namespace Ryujinx.Ava
|
||||||
|
|
||||||
_viewModel.Volume = Device.GetVolume();
|
_viewModel.Volume = Device.GetVolume();
|
||||||
break;
|
break;
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer1:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer2:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer3:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer4:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer5:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer6:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer7:
|
||||||
|
case KeyboardHotkeyState.CycleControllersPlayer8:
|
||||||
|
Dispatcher.UIThread.Invoke(() => {
|
||||||
|
var player = currentHotkeyState - KeyboardHotkeyState.CycleControllersPlayer1;
|
||||||
|
var ivm = new UI.ViewModels.Input.InputViewModel();
|
||||||
|
ivm.LoadDevices();
|
||||||
|
ivm.PlayerId = (Ryujinx.Common.Configuration.Hid.PlayerIndex) player;
|
||||||
|
ivm.Device = (ivm.Device + 1) % ivm.Devices.Count;
|
||||||
|
ivm.Save();
|
||||||
|
Console.WriteLine($"Cycling controller for player {ivm.PlayerId} to {ivm.Devices[ivm.Device].Name}");
|
||||||
|
});
|
||||||
|
break;
|
||||||
case KeyboardHotkeyState.None:
|
case KeyboardHotkeyState.None:
|
||||||
(_keyboardInterface as AvaloniaKeyboard).Clear();
|
(_keyboardInterface as AvaloniaKeyboard).Clear();
|
||||||
break;
|
break;
|
||||||
|
@ -1274,6 +1294,15 @@ namespace Ryujinx.Ava
|
||||||
state = KeyboardHotkeyState.VolumeDown;
|
state = KeyboardHotkeyState.VolumeDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var cycle in ConfigurationState.Instance.Hid.Hotkeys.Value.CycleControllers.Select((value, index) => (value, index)))
|
||||||
|
{
|
||||||
|
if (_keyboardInterface.IsPressed((Key)cycle.value))
|
||||||
|
{
|
||||||
|
state = KeyboardHotkeyState.CycleControllersPlayer1 + cycle.index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -738,6 +738,7 @@
|
||||||
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
|
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
|
||||||
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
|
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
|
||||||
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
|
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
|
||||||
|
"SettingsTabHotkeysCycleControllers": "Cycle Controllers",
|
||||||
"SettingsEnableMacroHLE": "Enable Macro HLE",
|
"SettingsEnableMacroHLE": "Enable Macro HLE",
|
||||||
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
|
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
|
||||||
"SettingsEnableColorSpacePassthrough": "Color Space Passthrough",
|
"SettingsEnableColorSpacePassthrough": "Color Space Passthrough",
|
||||||
|
|
|
@ -12,5 +12,13 @@ namespace Ryujinx.Ava.Common
|
||||||
ResScaleDown,
|
ResScaleDown,
|
||||||
VolumeUp,
|
VolumeUp,
|
||||||
VolumeDown,
|
VolumeDown,
|
||||||
|
CycleControllersPlayer1,
|
||||||
|
CycleControllersPlayer2,
|
||||||
|
CycleControllersPlayer3,
|
||||||
|
CycleControllersPlayer4,
|
||||||
|
CycleControllersPlayer5,
|
||||||
|
CycleControllersPlayer6,
|
||||||
|
CycleControllersPlayer7,
|
||||||
|
CycleControllersPlayer8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
using DynamicData;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models.Input
|
namespace Ryujinx.Ava.UI.Models.Input
|
||||||
{
|
{
|
||||||
|
@ -104,8 +109,14 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<CycleController> CycleControllers { get; set; } = new ObservableCollection<CycleController>();
|
||||||
|
public ICommand AddCycleController { get; set; }
|
||||||
|
public ICommand RemoveCycleController { get; set; }
|
||||||
|
public bool CanRemoveCycleController => CycleControllers.Count > 0 && CycleControllers.Count < 8;
|
||||||
public HotkeyConfig(KeyboardHotkeys config)
|
public HotkeyConfig(KeyboardHotkeys config)
|
||||||
{
|
{
|
||||||
|
AddCycleController = MiniCommand.Create(() => CycleControllers.Add(new CycleController(CycleControllers.Count + 1, Key.Unbound)));
|
||||||
|
RemoveCycleController = MiniCommand.Create(() => CycleControllers.Remove(CycleControllers.Last()));
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
ToggleVsync = config.ToggleVsync;
|
ToggleVsync = config.ToggleVsync;
|
||||||
|
@ -117,7 +128,9 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||||
ResScaleDown = config.ResScaleDown;
|
ResScaleDown = config.ResScaleDown;
|
||||||
VolumeUp = config.VolumeUp;
|
VolumeUp = config.VolumeUp;
|
||||||
VolumeDown = config.VolumeDown;
|
VolumeDown = config.VolumeDown;
|
||||||
|
CycleControllers.AddRange((config.CycleControllers ?? []).Select((x, i) => new CycleController(i + 1, x)));
|
||||||
}
|
}
|
||||||
|
CycleControllers.CollectionChanged += (sender, e) => OnPropertyChanged(nameof(CanRemoveCycleController));
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyboardHotkeys GetConfig()
|
public KeyboardHotkeys GetConfig()
|
||||||
|
@ -133,6 +146,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
||||||
ResScaleDown = ResScaleDown,
|
ResScaleDown = ResScaleDown,
|
||||||
VolumeUp = VolumeUp,
|
VolumeUp = VolumeUp,
|
||||||
VolumeDown = VolumeDown,
|
VolumeDown = VolumeDown,
|
||||||
|
CycleControllers = CycleControllers.Select(x => x.Hotkey).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
36
src/Ryujinx/UI/ViewModels/CycleController.cs
Normal file
36
src/Ryujinx/UI/ViewModels/CycleController.cs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class CycleController : BaseModel
|
||||||
|
{
|
||||||
|
private string _player;
|
||||||
|
private Key _hotkey;
|
||||||
|
|
||||||
|
public string Player
|
||||||
|
{
|
||||||
|
get => _player;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_player = value;
|
||||||
|
OnPropertyChanged(nameof(Player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key Hotkey
|
||||||
|
{
|
||||||
|
get => _hotkey;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_hotkey = value;
|
||||||
|
OnPropertyChanged(nameof(Hotkey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CycleController(int v, Key x)
|
||||||
|
{
|
||||||
|
Player = $"Player {v}";
|
||||||
|
Hotkey = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -256,6 +256,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
public InputViewModel()
|
public InputViewModel()
|
||||||
{
|
{
|
||||||
|
_mainWindow =
|
||||||
|
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current
|
||||||
|
.ApplicationLifetime).MainWindow;
|
||||||
|
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(_mainWindow);
|
||||||
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
||||||
Controllers = new ObservableCollection<ControllerModel>();
|
Controllers = new ObservableCollection<ControllerModel>();
|
||||||
Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
|
Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
|
||||||
|
@ -740,38 +744,34 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
|
||||||
|
|
||||||
|
if (!validFileName)
|
||||||
{
|
{
|
||||||
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
|
||||||
|
return;
|
||||||
if (validFileName)
|
|
||||||
{
|
|
||||||
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
|
|
||||||
|
|
||||||
InputConfig config = null;
|
|
||||||
|
|
||||||
if (IsKeyboard)
|
|
||||||
{
|
|
||||||
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
|
|
||||||
}
|
|
||||||
else if (IsController)
|
|
||||||
{
|
|
||||||
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
|
||||||
|
|
||||||
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
|
|
||||||
|
|
||||||
await File.WriteAllTextAsync(path, jsonString);
|
|
||||||
|
|
||||||
LoadProfiles();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
|
||||||
|
|
||||||
|
InputConfig config = null;
|
||||||
|
|
||||||
|
if (IsKeyboard)
|
||||||
|
{
|
||||||
|
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
|
||||||
|
}
|
||||||
|
else if (IsController)
|
||||||
|
{
|
||||||
|
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
|
|
||||||
|
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
|
||||||
|
|
||||||
|
await File.WriteAllTextAsync(path, jsonString);
|
||||||
|
|
||||||
|
LoadProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void RemoveProfile()
|
public async void RemoveProfile()
|
||||||
|
|
|
@ -18,15 +18,15 @@
|
||||||
<helpers:KeyValueConverter x:Key="Key" />
|
<helpers:KeyValueConverter x:Key="Key" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="StackPanel > StackPanel">
|
<Style Selector="StackPanel StackPanel">
|
||||||
<Setter Property="Margin" Value="10, 0, 0, 0" />
|
<Setter Property="Margin" Value="10, 0, 0, 0" />
|
||||||
<Setter Property="Orientation" Value="Horizontal" />
|
<Setter Property="Orientation" Value="Horizontal" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="StackPanel > StackPanel > TextBlock">
|
<Style Selector="StackPanel StackPanel > TextBlock">
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
<Setter Property="Width" Value="230" />
|
<Setter Property="Width" Value="230" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ToggleButton">
|
<Style Selector="ToggleButton, Button">
|
||||||
<Setter Property="Width" Value="90" />
|
<Setter Property="Width" Value="90" />
|
||||||
<Setter Property="Height" Value="27" />
|
<Setter Property="Height" Value="27" />
|
||||||
</Style>
|
</Style>
|
||||||
|
@ -42,67 +42,110 @@
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Auto">
|
||||||
<Border Classes="settings">
|
<Border Classes="settings">
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Name="SettingButtons"
|
|
||||||
Margin="10"
|
Margin="10"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
Orientation="Vertical"
|
Orientation="Vertical"
|
||||||
Spacing="10">
|
Spacing="10"
|
||||||
|
Name="SettingButtons">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Classes="h1"
|
Classes="h1"
|
||||||
Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
|
||||||
<StackPanel>
|
<StackPanel
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" />
|
Margin="10,0,0,0"
|
||||||
<ToggleButton Name="ToggleVsync">
|
Spacing="10"
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleVsync, Converter={StaticResource Key}}" />
|
Orientation="Vertical">
|
||||||
</ToggleButton>
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" />
|
||||||
|
<ToggleButton Name="ToggleVsync">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.ToggleVsync, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" />
|
||||||
|
<ToggleButton Name="Screenshot">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" />
|
||||||
|
<ToggleButton Name="ShowUI">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" />
|
||||||
|
<ToggleButton Name="Pause">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" />
|
||||||
|
<ToggleButton Name="ToggleMute">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" />
|
||||||
|
<ToggleButton Name="ResScaleUp">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" />
|
||||||
|
<ToggleButton Name="ResScaleDown">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" />
|
||||||
|
<ToggleButton Name="VolumeUp">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" />
|
||||||
|
<ToggleButton Name="VolumeDown">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<Separator Height="1" />
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" />
|
<StackPanel Margin="0">
|
||||||
<ToggleButton Name="Screenshot">
|
<TextBlock
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={StaticResource Key}}" />
|
Classes="h1"
|
||||||
</ToggleButton>
|
Text="{locale:Locale SettingsTabHotkeysCycleControllers}" />
|
||||||
</StackPanel>
|
<Button
|
||||||
<StackPanel>
|
Margin="10,0,0,0"
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" />
|
Content="+"
|
||||||
<ToggleButton Name="ShowUI">
|
Command="{Binding KeyboardHotkey.AddCycleController}" />
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={StaticResource Key}}" />
|
<Button
|
||||||
</ToggleButton>
|
Margin="10,0,0,0"
|
||||||
</StackPanel>
|
Content="-"
|
||||||
<StackPanel>
|
IsEnabled="{Binding KeyboardHotkey.CanRemoveCycleController}"
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" />
|
Command="{Binding KeyboardHotkey.RemoveCycleController}" />
|
||||||
<ToggleButton Name="Pause">
|
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={StaticResource Key}}" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" />
|
|
||||||
<ToggleButton Name="ToggleMute">
|
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={StaticResource Key}}" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" />
|
|
||||||
<ToggleButton Name="ResScaleUp">
|
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={StaticResource Key}}" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" />
|
|
||||||
<ToggleButton Name="ResScaleDown">
|
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={StaticResource Key}}" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" />
|
|
||||||
<ToggleButton Name="VolumeUp">
|
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={StaticResource Key}}" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" />
|
|
||||||
<ToggleButton Name="VolumeDown">
|
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
|
|
||||||
</ToggleButton>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<ItemsControl ItemsSource="{Binding KeyboardHotkey.CycleControllers}"
|
||||||
|
Name="CycleControllers">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel
|
||||||
|
Margin="0"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="10" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{Binding Player}" />
|
||||||
|
<ToggleButton>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Hotkey, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|
|
@ -3,11 +3,17 @@ using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
using DynamicData.Kernel;
|
||||||
|
using FluentAvalonia.Core;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.Assigner;
|
using Ryujinx.Input.Assigner;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Views.Settings
|
namespace Ryujinx.Ava.UI.Views.Settings
|
||||||
|
@ -20,16 +26,21 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||||
public SettingsHotkeysView()
|
public SettingsHotkeysView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
RegisterEvents();
|
||||||
|
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
|
||||||
|
CycleControllers.LayoutUpdated += (_, _1) => RegisterEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterEvents()
|
||||||
|
{
|
||||||
foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
|
foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
|
||||||
{
|
{
|
||||||
if (visual is ToggleButton button and not CheckBox)
|
if (visual is ToggleButton button and not CheckBox)
|
||||||
{
|
{
|
||||||
|
button.IsCheckedChanged -= Button_IsCheckedChanged;
|
||||||
button.IsCheckedChanged += Button_IsCheckedChanged;
|
button.IsCheckedChanged += Button_IsCheckedChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||||
|
@ -50,87 +61,89 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||||
|
|
||||||
PointerPressed -= MouseClick;
|
PointerPressed -= MouseClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
|
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is ToggleButton button)
|
if (sender is not ToggleButton button)
|
||||||
{
|
{
|
||||||
if ((bool)button.IsChecked)
|
return;
|
||||||
{
|
|
||||||
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentAssigner == null)
|
|
||||||
{
|
|
||||||
_currentAssigner = new ButtonKeyAssigner(button);
|
|
||||||
|
|
||||||
this.Focus(NavigationMethod.Pointer);
|
|
||||||
|
|
||||||
PointerPressed += MouseClick;
|
|
||||||
|
|
||||||
var keyboard = (IKeyboard)_avaloniaKeyboardDriver.GetGamepad("0");
|
|
||||||
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
|
|
||||||
|
|
||||||
_currentAssigner.ButtonAssigned += (sender, e) =>
|
|
||||||
{
|
|
||||||
if (e.ButtonValue.HasValue)
|
|
||||||
{
|
|
||||||
var viewModel = (DataContext) as SettingsViewModel;
|
|
||||||
var buttonValue = e.ButtonValue.Value;
|
|
||||||
|
|
||||||
switch (button.Name)
|
|
||||||
{
|
|
||||||
case "ToggleVsync":
|
|
||||||
viewModel.KeyboardHotkey.ToggleVsync = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "Screenshot":
|
|
||||||
viewModel.KeyboardHotkey.Screenshot = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "ShowUI":
|
|
||||||
viewModel.KeyboardHotkey.ShowUI = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "Pause":
|
|
||||||
viewModel.KeyboardHotkey.Pause = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "ToggleMute":
|
|
||||||
viewModel.KeyboardHotkey.ToggleMute = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "ResScaleUp":
|
|
||||||
viewModel.KeyboardHotkey.ResScaleUp = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "ResScaleDown":
|
|
||||||
viewModel.KeyboardHotkey.ResScaleDown = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "VolumeUp":
|
|
||||||
viewModel.KeyboardHotkey.VolumeUp = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
case "VolumeDown":
|
|
||||||
viewModel.KeyboardHotkey.VolumeDown = buttonValue.AsHidType<Key>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_currentAssigner.GetInputAndAssign(assigner, keyboard);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_currentAssigner != null)
|
|
||||||
{
|
|
||||||
_currentAssigner.Cancel();
|
|
||||||
_currentAssigner = null;
|
|
||||||
button.IsChecked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_currentAssigner?.Cancel();
|
|
||||||
_currentAssigner = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!button.IsChecked.ValueOr(default))
|
||||||
|
{
|
||||||
|
_currentAssigner?.Cancel();
|
||||||
|
_currentAssigner = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentAssigner != null)
|
||||||
|
{
|
||||||
|
_currentAssigner.Cancel();
|
||||||
|
_currentAssigner = null;
|
||||||
|
button.IsChecked = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentAssigner = new ButtonKeyAssigner(button);
|
||||||
|
|
||||||
|
this.Focus(NavigationMethod.Pointer);
|
||||||
|
|
||||||
|
PointerPressed += MouseClick;
|
||||||
|
|
||||||
|
var keyboard = (IKeyboard)_avaloniaKeyboardDriver.GetGamepad("0");
|
||||||
|
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
|
||||||
|
|
||||||
|
_currentAssigner.ButtonAssigned += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (!e.ButtonValue.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewModel = (DataContext) as SettingsViewModel;
|
||||||
|
var buttonValue = e.ButtonValue.Value;
|
||||||
|
|
||||||
|
switch (button.Name)
|
||||||
|
{
|
||||||
|
case "ToggleVsync":
|
||||||
|
viewModel.KeyboardHotkey.ToggleVsync = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "Screenshot":
|
||||||
|
viewModel.KeyboardHotkey.Screenshot = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "ShowUI":
|
||||||
|
viewModel.KeyboardHotkey.ShowUI = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "Pause":
|
||||||
|
viewModel.KeyboardHotkey.Pause = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "ToggleMute":
|
||||||
|
viewModel.KeyboardHotkey.ToggleMute = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "ResScaleUp":
|
||||||
|
viewModel.KeyboardHotkey.ResScaleUp = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "ResScaleDown":
|
||||||
|
viewModel.KeyboardHotkey.ResScaleDown = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "VolumeUp":
|
||||||
|
viewModel.KeyboardHotkey.VolumeUp = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "VolumeDown":
|
||||||
|
viewModel.KeyboardHotkey.VolumeDown = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var index = button.FindAncestorOfType<ItemsControl>().GetLogicalDescendants().OfType<ToggleButton>().IndexOf(button);
|
||||||
|
viewModel.KeyboardHotkey.CycleControllers[index].Hotkey = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_currentAssigner.GetInputAndAssign(assigner, keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
Loading…
Reference in a new issue