Compare commits

..

31 Commits

Author SHA1 Message Date
uncavo-hdmi aa9a37bfea merge with upstream 2025-03-01 09:11:06 +01:00
uncavo-hdmi f024c8a695 fix incorrect ReloadConfiguration placement 2025-02-28 17:16:55 +01:00
uncavo-hdmi 6ecdf690be removed unnecessary logging, formatting and imports. 2025-02-22 18:51:00 +01:00
uncavo-hdmi c6e82046dd minor fixes 2025-02-22 18:37:01 +01:00
uncavo-hdmi 538a9c83bd class to static; added a controller check on GetConfiguredControllers 2025-02-22 17:04:42 +01:00
uncavo-hdmi c3bfa67393 updated locales.json AutoAssignTooltip description 2025-02-22 10:28:14 +01:00
uncavo-hdmi 72c3ca7769 changed var name; fixed class name; removed hashset argument from GetConfiguredController function. 2025-02-22 10:15:41 +01:00
uncavo-hdmi da268ebbee merge with upstream 2025-02-19 20:03:35 +01:00
uncavo-hdmi 55f3a4b7ff simplified logic 2025-02-18 14:43:00 +01:00
uncavo-hdmi cc905280cd fixed some problems with rainbow led. also, rainbow won't be visible in settings (no easy way to constantly update it) 2025-02-18 14:38:30 +01:00
uncavo-hdmi 38ecb3d5bc added function to handle gamepad led while in settings; added rainbow color handling in the new function. 2025-02-18 13:40:34 +01:00
uncavo-hdmi fd9bce0f6b fixed led live update while in settings 2025-02-18 11:27:30 +01:00
uncavo-hdmi b8cb70ef32 possible refactor. to be tested 2025-02-17 22:17:54 +01:00
uncavo-hdmi e238ea85c9 fixed save config logic 2025-02-17 20:59:51 +01:00
uncavo-hdmi 8cc74dab08 Improved assignment logic in AutoAssignController.cs 2025-02-12 19:44:16 +01:00
uncavo-hdmi 62dfbb5dcb edited NpadManager to better support auto-assign; updated initialization methods; changed some settings for LED color 2025-02-09 18:10:32 +01:00
uncavo-hdmi 5b88a2dd89 enhance AutoAssignController to trigger configuration updates on gamepad connection changes; improve controller assignment logic and ensure proper LED color settings 2025-02-09 16:07:12 +01:00
uncavo-hdmi 5034ef18c9 minor fix: swapped LoadConfiguration() and LoadDevice(). The previous order caused the configuration to load incorrectly. 2025-02-07 15:23:07 +01:00
uncavo-hdmi 287d68c2cc namespace correction 2025-02-07 14:51:46 +01:00
uncavo-hdmi f07efb751e Relocate AutoAssignController.cs to a more appropriate directory 2025-02-07 14:45:29 +01:00
uncavo-hdmi 6c8a60db08 enhance AutoAssignController to set player colors and enable LED functionality; update gamepad connection handling to load configuration while in Input settings. 2025-02-04 17:02:25 +01:00
uncavo-hdmi 7999a973f3 update GetOrderedConfig to return if new controllers connected and conditionally update configuration 2025-02-04 14:51:48 +01:00
uncavo-hdmi 5513de93e5 code clean up; fix not loading EnableAutoAssign.Value from config file on startup 2025-02-04 11:50:08 +01:00
uncavo-hdmi 9cccaac9d3 minor fix to RefreshControllers logic 2025-02-02 22:56:34 +01:00
uncavo-hdmi 9b7dc6f4ee simplify RefreshControllers logic and introduce GetOrderedConfig for better controller management 2025-02-02 22:40:47 +01:00
uncavo-hdmi 3ff9d1e128 refactor: enhance AutoAssignController to utilize ViewModel and improve controller refresh logic 2025-02-02 16:35:30 +01:00
uncavo-hdmi ab4bb0a885 refactor: remove auto-assign option from NpadManager initialization and update related components 2025-02-01 19:36:55 +01:00
uncavo-hdmi 97be01d473 refactor: streamline configuration creation for controllers in NpadManager 2025-01-28 22:47:55 +01:00
uncavo-hdmi 24cef89b6c refactor: clean up logging and improve IgnoreApplet logic in settings 2025-01-26 23:52:44 +01:00
uncavo-hdmi 2fe157e2b2 Merge remote-tracking branch 'upstream/master' into auto-assign-controller 2025-01-26 21:23:37 +01:00
uncavo-hdmi 186ed4f984 feat: add option for automatic controller assignment in settings 2025-01-26 21:20:27 +01:00
69 changed files with 993 additions and 705 deletions
+1 -5
View File
@@ -631,7 +631,6 @@
010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27 010030D012FF6000,"Bus Driver Simulator",,playable,2022-10-17 13:55:27
0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41 0100A9101418C000,"BUSTAFELLOWS",nvdec,playable,2020-10-17 20:04:41
0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17 0100177005C8A000,"BUTCHER",,playable,2021-01-11 18:50:17
01008c2019598000,"Bluey: The Videogame",,playable,2025-02-11 04:38:00
01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40 01000B900D8B0000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda",slow;nvdec,playable,2024-04-01 22:43:40
010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15 010065700EE06000,"Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo",demo;gpu;nvdec,ingame,2021-02-14 21:48:15
01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25 01005C00117A8000,"Café Enchanté",,playable,2020-11-13 14:54:25
@@ -1383,9 +1382,6 @@
0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34 0100763015C2E000,"Gunvolt Chronicles: Luminous Avenger iX 2",crash;Needs Update,nothing,2022-04-29 15:34:34
01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31 01002C8018554000,"Gurimugurimoa OnceMore Demo",,playable,2022-07-29 22:07:31
0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51 0100AC601DCA8000,"GYLT",crash,ingame,2024-03-18 20:16:51
0100c3c012718000,"Grand Theft Auto: III The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100182014022000,"Grand Theft Auto: Vice City The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
010065a014024000,"Grand Theft Auto: San Andreas The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05 0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42 01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21 0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
@@ -2733,7 +2729,7 @@
0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11 0100C2500FC20000,"Splatoon™ 3",ldn-works;opengl-backend-bug;LAN;amd-vendor-bug,playable,2024-08-04 23:49:11
0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12 0100BA0018500000,"Splatoon™ 3: Splatfest World Premiere",gpu;online-broken;demo,ingame,2022-09-19 03:17:12
010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34 010062800D39C000,"SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated",online-broken;UE4;ldn-broken;vulkan-backend-bug,playable,2023-08-01 19:29:34
01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2024-03-04 16:35:00 01009FB0172F4000,"SpongeBob SquarePants: The Cosmic Shake",gpu;UE4,ingame,2023-08-01 19:29:53
010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44 010097C01336A000,"Spooky Chase",,playable,2022-11-04 12:17:44
0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11 0100C6100D75E000,"Spooky Ghosts Dot Com",,playable,2021-06-15 15:16:11
0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42 0100DE9005170000,"Sports Party",nvdec,playable,2021-03-05 13:40:42
1 title_id game_name labels status last_updated
631 010030D012FF6000 Bus Driver Simulator playable 2022-10-17 13:55:27
632 0100A9101418C000 BUSTAFELLOWS nvdec playable 2020-10-17 20:04:41
633 0100177005C8A000 BUTCHER playable 2021-01-11 18:50:17
01008c2019598000 Bluey: The Videogame playable 2025-02-11 04:38:00
634 01000B900D8B0000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda slow;nvdec playable 2024-04-01 22:43:40
635 010065700EE06000 Cadence of Hyrule: Crypt of the NecroDancer Featuring The Legend of Zelda Demo demo;gpu;nvdec ingame 2021-02-14 21:48:15
636 01005C00117A8000 Café Enchanté playable 2020-11-13 14:54:25
1382 0100763015C2E000 Gunvolt Chronicles: Luminous Avenger iX 2 crash;Needs Update nothing 2022-04-29 15:34:34
1383 01002C8018554000 Gurimugurimoa OnceMore Demo playable 2022-07-29 22:07:31
1384 0100AC601DCA8000 GYLT crash ingame 2024-03-18 20:16:51
0100c3c012718000 Grand Theft Auto: III – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
0100182014022000 Grand Theft Auto: Vice City – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
010065a014024000 Grand Theft Auto: San Andreas – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1385 0100822012D76000 HAAK gpu ingame 2023-02-19 14:31:05
1386 01007E100EFA8000 Habroxia playable 2020-06-16 23:04:42
1387 0100535012974000 Hades vulkan playable 2022-10-05 10:45:21
2729 0100C2500FC20000 Splatoon™ 3 ldn-works;opengl-backend-bug;LAN;amd-vendor-bug playable 2024-08-04 23:49:11
2730 0100BA0018500000 Splatoon™ 3: Splatfest World Premiere gpu;online-broken;demo ingame 2022-09-19 03:17:12
2731 010062800D39C000 SpongeBob SquarePants: Battle for Bikini Bottom - Rehydrated online-broken;UE4;ldn-broken;vulkan-backend-bug playable 2023-08-01 19:29:34
2732 01009FB0172F4000 SpongeBob SquarePants: The Cosmic Shake gpu;UE4 ingame 2024-03-04 16:35:00 2023-08-01 19:29:53
2733 010097C01336A000 Spooky Chase playable 2022-11-04 12:17:44
2734 0100C6100D75E000 Spooky Ghosts Dot Com playable 2021-06-15 15:16:11
2735 0100DE9005170000 Sports Party nvdec playable 2021-03-05 13:40:42
+1 -1
View File
@@ -95,7 +95,7 @@ namespace Ryujinx.Input.SDL2
} }
private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId) private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId)
{ {
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE) if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
{ {
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId)) if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
+9 -2
View File
@@ -14,6 +14,7 @@ using ControllerType = Ryujinx.Common.Configuration.Hid.ControllerType;
using PlayerIndex = Ryujinx.HLE.HOS.Services.Hid.PlayerIndex; using PlayerIndex = Ryujinx.HLE.HOS.Services.Hid.PlayerIndex;
using Switch = Ryujinx.HLE.Switch; using Switch = Ryujinx.HLE.Switch;
namespace Ryujinx.Input.HLE namespace Ryujinx.Input.HLE
{ {
public class NpadManager : IDisposable public class NpadManager : IDisposable
@@ -38,6 +39,8 @@ namespace Ryujinx.Input.HLE
private bool _enableMouse; private bool _enableMouse;
private Switch _device; private Switch _device;
public bool AutoAssignEnabled { get; set; } = false;
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver)
{ {
_controllers = new NpadController[MaxControllers]; _controllers = new NpadController[MaxControllers];
@@ -84,12 +87,14 @@ namespace Ryujinx.Input.HLE
} }
} }
if (AutoAssignEnabled) return;
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse);
} }
} }
private void HandleOnGamepadConnected(string id) private void HandleOnGamepadConnected(string id)
{ {
if (AutoAssignEnabled) return;
// Force input reload // Force input reload
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse);
} }
@@ -171,7 +176,7 @@ namespace Ryujinx.Input.HLE
_device.Hid.RefreshInputConfig(validInputs); _device.Hid.RefreshInputConfig(validInputs);
} }
} }
public void UnblockInputUpdates() public void UnblockInputUpdates()
{ {
lock (_lock) lock (_lock)
@@ -202,11 +207,13 @@ namespace Ryujinx.Input.HLE
} }
} }
public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse) public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign)
{ {
_device = device; _device = device;
_device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE; _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE;
AutoAssignEnabled = enableAutoAssign;
ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
} }
+1 -1
View File
@@ -463,7 +463,7 @@ namespace Ryujinx.Ava
DisplaySleep.Prevent(); DisplaySleep.Prevent();
NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse, ConfigurationState.Instance.Hid.EnableAutoAssign);
TouchScreenManager.Initialize(Device); TouchScreenManager.Initialize(Device);
_viewModel.IsGameRunning = true; _viewModel.IsGameRunning = true;
+51 -1
View File
@@ -147,6 +147,31 @@
"zh_TW": "滑鼠直接存取" "zh_TW": "滑鼠直接存取"
} }
}, },
{
"ID": "SettingsTabInputAutoAssign",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Auto-assign controllers",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "Assegnamento controller automatico",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "SettingsTabSystemMemoryManagerMode", "ID": "SettingsTabSystemMemoryManagerMode",
"Translations": { "Translations": {
@@ -16222,6 +16247,31 @@
"zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。" "zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。"
} }
}, },
{
"ID": "AutoAssignTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Automatic controllers assignment support.\n\nAutomatically assigns connected controllers to each player.\n\nManual configuration remains available, even when this option is activated.\n\nLeave OFF if you prefer to manually assign controllers.",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "Supporto per l'assegnazione automatica dei controller.\n\nAssegna automaticamente i controller connessi a ciascun giocatore.\n\nÈ possibile configurare manualmente i controller anche quando questa opzione è attivata.\n\nLascia disattivato se preferisci assegnare i controller manualmente.",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": "。"
}
},
{ {
"ID": "RegionTooltip", "ID": "RegionTooltip",
"Translations": { "Translations": {
@@ -24298,4 +24348,4 @@
} }
} }
] ]
} }
+3 -1
View File
@@ -46,6 +46,7 @@ namespace Ryujinx.Headless
private static List<InputConfig> _inputConfiguration = []; private static List<InputConfig> _inputConfiguration = [];
private static bool _enableKeyboard; private static bool _enableKeyboard;
private static bool _enableMouse; private static bool _enableMouse;
private static bool _enableAutoAssign;
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -230,6 +231,7 @@ namespace Ryujinx.Headless
_inputConfiguration ??= []; _inputConfiguration ??= [];
_enableKeyboard = option.EnableKeyboard; _enableKeyboard = option.EnableKeyboard;
_enableMouse = option.EnableMouse; _enableMouse = option.EnableMouse;
_enableAutoAssign = option.EnableAutoAssign;
LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1);
LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
@@ -371,7 +373,7 @@ namespace Ryujinx.Headless
DisplaySleep.Prevent(); DisplaySleep.Prevent();
_window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse); _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse, _enableAutoAssign);
_window.Execute(); _window.Execute();
+6
View File
@@ -26,6 +26,9 @@ namespace Ryujinx.Headless
if (NeedsOverride(nameof(EnableMouse))) if (NeedsOverride(nameof(EnableMouse)))
EnableMouse = configurationState.Hid.EnableMouse; EnableMouse = configurationState.Hid.EnableMouse;
if (NeedsOverride(nameof(EnableAutoAssign)))
EnableAutoAssign = configurationState.Hid.EnableAutoAssign;
if (NeedsOverride(nameof(HideCursorMode))) if (NeedsOverride(nameof(HideCursorMode)))
HideCursorMode = configurationState.HideCursor; HideCursorMode = configurationState.HideCursor;
@@ -272,6 +275,9 @@ namespace Ryujinx.Headless
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] [Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")]
public bool EnableMouse { get; set; } public bool EnableMouse { get; set; }
[Option("enable-auto-assign", Required = false, Default = false, HelpText = "Enable or disable auto-assigning controllers to players.")]
public bool EnableAutoAssign { get; set; }
[Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")] [Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")]
public HideCursorMode HideCursorMode { get; set; } public HideCursorMode HideCursorMode { get; set; }
+2 -2
View File
@@ -119,7 +119,7 @@ namespace Ryujinx.Headless
SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.Initialize();
} }
public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse) public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse, bool enableAutoAssign)
{ {
Device = device; Device = device;
@@ -132,7 +132,7 @@ namespace Ryujinx.Headless
Renderer = renderer; Renderer = renderer;
NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse); NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse, enableAutoAssign);
TouchScreenManager.Initialize(device); TouchScreenManager.Initialize(device);
} }
+75
View File
@@ -0,0 +1,75 @@
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Input
{
public class AutoAssignController
{
private readonly InputManager _inputManager;
private readonly MainWindowViewModel _viewModel;
private readonly ConfigurationState _configurationState;
public event Action ConfigurationUpdated;
public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel)
{
_inputManager = inputManager;
_viewModel = mainWindowViewModel;
_configurationState = ConfigurationState.Instance;
_inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
RefreshControllers();
}
private void HandleOnGamepadConnected(string id)
{
Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}");
RefreshControllers();
}
private void HandleOnGamepadDisconnected(string id)
{
Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}");
RefreshControllers();
}
public void RefreshControllers()
{
if (!_configurationState.Hid.EnableAutoAssign) return;
List<IGamepad> controllers = _inputManager.GamepadDriver.GetGamepads().ToList();
List<InputConfig> oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList();
List<InputConfig> newConfig = ControllerAssignmentManager.GetConfiguredControllers(
controllers, oldConfig, out bool hasNewControllersConnected);
_viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse);
if (!hasNewControllersConnected)
{
// there is no *new* controller, we must switch the order of the controllers in
// oldConfig to match the new order since probably a controller was disconnected
// or an old controller was reconnected
newConfig = ControllerAssignmentManager.ReorderControllers(newConfig, oldConfig);
}
_configurationState.Hid.InputConfig.Value = newConfig;
// we want to save the configuration only if a *new* controller was connected
if(hasNewControllersConnected)
{
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
ConfigurationUpdated?.Invoke();
}
}
}
@@ -0,0 +1,274 @@
using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
using Ryujinx.Input;
using System.Collections.Generic;
using System.Linq;
using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
namespace Ryujinx.Ava.Input
{
public static class ControllerAssignmentManager
{
private static readonly uint[] _playerColors =
[
0xFF0000FF, // Player 1 - Blue
0xFFFF0000, // Player 2 - Red
0xFF00FF00, // Player 3 - Green
0xFFFFFF00, // Player 4 - Yellow
0xFFFF00FF, // Player 5 - Magenta
0xFFFFA500, // Player 6 - Orange
0xFF00FFFF, // Player 7 - Cyan
0xFF800080 // Player 8 - Purple
];
private const int MaxControllers = 9;
public static List<InputConfig> ReorderControllers(List<InputConfig> newConfig, List<InputConfig> oldConfig)
{
if(newConfig == null || oldConfig == null || newConfig.Count == 0 || oldConfig.Count == 0) return [];
List<InputConfig> reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList();
foreach (var config in newConfig)
{
InputConfig substitute = reorderedConfig.FirstOrDefault(x => x.Id == config.Id);
InputConfig toBeReplaced = reorderedConfig.FirstOrDefault(x => x.PlayerIndex == config.PlayerIndex);
if (substitute == null || toBeReplaced == null || substitute.PlayerIndex == toBeReplaced.PlayerIndex) continue;
(substitute.PlayerIndex, toBeReplaced.PlayerIndex) = (toBeReplaced.PlayerIndex, substitute.PlayerIndex);
}
return reorderedConfig;
}
public static List<InputConfig> GetConfiguredControllers(
List<IGamepad> controllers,
List<InputConfig> oldConfig,
out bool hasNewControllersConnected)
{
if(controllers == null || controllers.Count == 0)
{
hasNewControllersConnected = false;
return [];
}
Dictionary<string, InputConfig> oldConfigMap = oldConfig
.Where(c => c?.Id != null)
.ToDictionary(x => x.Id);
Dictionary<int, InputConfig> playerIndexMap = new();
HashSet<int> usedIndices = [];
int recognizedControllersCount = 0;
List<IGamepad> remainingControllers = controllers.Where(c => c?.Id != null).ToList();
// Add controllers with existing configurations
AddExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount);
// Add new controllers
AddNewControllers(remainingControllers, playerIndexMap, usedIndices);
List<InputConfig> orderedConfigs = playerIndexMap
.OrderBy(x => x.Key)
.Select(x => x.Value)
.ToList();
// Update player indices and LED colors
UpdatePlayerIndicesAndLEDs(orderedConfigs);
hasNewControllersConnected = controllers.Count > recognizedControllersCount;
return orderedConfigs;
}
private static void AddExistingControllers(
List<IGamepad> controllers,
Dictionary<string, InputConfig> oldConfigMap,
Dictionary<int, InputConfig> playerIndexMap,
HashSet<int> usedIndices,
ref int recognizedControllersCount)
{
foreach (var controller in controllers.ToList())
{
if (!oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig))
{
continue;
}
int desiredIndex = (int)existingConfig.PlayerIndex;
// Ensure the index is valid and available
if (desiredIndex < 0 || desiredIndex >= MaxControllers || usedIndices.Contains(desiredIndex))
{
desiredIndex = GetFirstAvailableIndex(usedIndices);
}
if(desiredIndex == -1) continue;
InputConfig config = new GamepadInputConfig(existingConfig).GetConfig();
config.PlayerIndex = (PlayerIndex)desiredIndex;
usedIndices.Add(desiredIndex);
playerIndexMap[desiredIndex] = config;
recognizedControllersCount++;
controllers.Remove(controller);
}
}
private static void AddNewControllers(
List<IGamepad> controllers,
Dictionary<int, InputConfig> playerIndexMap,
HashSet<int> usedIndices)
{
foreach (var controller in controllers)
{
InputConfig config = CreateConfigFromController(controller);
int freeIndex = GetFirstAvailableIndex(usedIndices);
config.PlayerIndex = (PlayerIndex)freeIndex;
usedIndices.Add(freeIndex);
playerIndexMap[freeIndex] = config;
}
}
private static int GetFirstAvailableIndex(HashSet<int> usedIndices)
{
for (int i = 0; i < MaxControllers; i++)
{
if (!usedIndices.Contains(i)) return i;
}
return -1; // Should not happen unless MaxControllers is exceeded
}
private static void UpdatePlayerIndicesAndLEDs(List<InputConfig> orderedConfigs)
{
for (int index = 0; index < orderedConfigs.Count; index++)
{
orderedConfigs[index].PlayerIndex = (PlayerIndex)index;
if (orderedConfigs[index] is not StandardControllerInputConfig standardConfig ||
standardConfig.Led.UseRainbow)
{
continue;
}
Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}");
standardConfig.Led = new LedConfigController
{
EnableLed = true,
LedColor = _playerColors[index]
};
}
}
private static InputConfig CreateConfigFromController(IGamepad controller)
{
if (controller == null) return null;
Logger.Warning?.Print(LogClass.Application, $"Creating config for controller: {controller.Id}");
string id = controller.Id.Split(" ")[0];
bool isNintendoStyle = controller.Name.Contains("Nintendo");
ControllerType controllerType;
if (isNintendoStyle && !controller.Name.Contains("(L/R)"))
{
if (controller.Name.Contains("(L)"))
{
controllerType = ControllerType.JoyconLeft;
}
else if (controller.Name.Contains("(R)"))
{
controllerType = ControllerType.JoyconRight;
}
else
{
controllerType = ControllerType.ProController;
}
}
else
{
// if it's not a nintendo controller, we assume it's a pro controller or a joy-con pair
controllerType = ControllerType.ProController;
}
InputConfig config = new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = id,
ControllerType = controllerType,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
RangeLeft = 1.0f,
RangeRight = 1.0f,
TriggerThreshold = 0.5f,
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
{
DpadUp = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.Y : GamepadInputId.DpadUp,
DpadDown = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.A : GamepadInputId.DpadDown,
DpadLeft = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.B : GamepadInputId.DpadLeft,
DpadRight = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.X : GamepadInputId.DpadRight,
ButtonMinus = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.Plus : GamepadInputId.Minus,
ButtonL = GamepadInputId.LeftShoulder,
ButtonZl = GamepadInputId.LeftTrigger,
ButtonSl = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound,
ButtonSr = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound,
},
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{
Joystick = StickInputId.Left,
StickButton = GamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = (controllerType == ControllerType.JoyconLeft),
},
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>
{
ButtonA = GamepadInputId.B,
ButtonB = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.Y : GamepadInputId.A,
ButtonX = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.A : GamepadInputId.Y,
ButtonY = GamepadInputId.X,
ButtonPlus = GamepadInputId.Plus,
ButtonR = GamepadInputId.RightShoulder,
ButtonZr = GamepadInputId.RightTrigger,
ButtonSl = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound,
ButtonSr = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound,
},
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{
Joystick = (controllerType == ControllerType.JoyconRight) ? StickInputId.Left : StickInputId.Right,
StickButton = GamepadInputId.RightStick,
InvertStickX = (controllerType == ControllerType.JoyconRight),
InvertStickY = (controllerType == ControllerType.JoyconRight),
Rotate90CW = (controllerType == ControllerType.JoyconRight),
},
Motion = new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true,
Sensitivity = 100,
GyroDeadzone = 1,
},
Rumble = new RumbleConfigController
{
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
},
Led = new LedConfigController
{
EnableLed = true,
TurnOffLed = false,
UseRainbow = false,
LedColor = 0,
},
};
return config;
}
}
}
+1 -1
View File
@@ -134,7 +134,7 @@ namespace Ryujinx.Ava
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input); SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
ReloadConfig(); ReloadConfig();
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
// Logging system information. // Logging system information.
@@ -17,8 +17,12 @@
<viewModels:ProfileSelectorDialogViewModel /> <viewModels:ProfileSelectorDialogViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border <Border
CornerRadius="5" CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
@@ -9,14 +9,17 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile; using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile;
namespace Ryujinx.Ava.UI.Applet namespace Ryujinx.Ava.UI.Applet
{ {
public partial class ProfileSelectorDialog : RyujinxControl<ProfileSelectorDialogViewModel> public partial class ProfileSelectorDialog : UserControl
{ {
public ProfileSelectorDialogViewModel ViewModel { get; set; }
public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel) public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel)
{ {
DataContext = ViewModel = viewModel; DataContext = ViewModel = viewModel;
@@ -26,7 +26,6 @@ namespace Ryujinx.Ava.UI.Controls
{ {
public class ApplicationContextMenu : MenuFlyout public class ApplicationContextMenu : MenuFlyout
{ {
public ApplicationContextMenu() public ApplicationContextMenu()
{ {
InitializeComponent(); InitializeComponent();
@@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class ApplicationDataView : RyujinxControl<ApplicationDataViewModel> public partial class ApplicationDataView : UserControl
{ {
public static async Task Show(ApplicationData appData) public static async Task Show(ApplicationData appData)
{ {
@@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Controls
SecondaryButtonText = string.Empty, SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
MinWidth = 256, MinWidth = 256,
Content = new ApplicationDataView { ViewModel = new ApplicationDataViewModel(appData) } Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) }
}; };
Style closeButton = new(x => x.Name("CloseButton")); Style closeButton = new(x => x.Name("CloseButton"));
@@ -14,7 +14,10 @@
mc:Ignorable="d" mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<Grid RowDefinitions="*"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox <ListBox
Grid.Row="0" Grid.Row="0"
Padding="8" Padding="8"
@@ -54,7 +57,11 @@
Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}" Classes.small="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).IsGridSmall}"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="4"> CornerRadius="4">
<Grid RowDefinitions="Auto,Auto"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image <Image
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
@@ -2,13 +2,12 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using System; using System;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class ApplicationGridView : RyujinxControl<MainWindowViewModel> public partial class ApplicationGridView : UserControl
{ {
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<ApplicationGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
@@ -13,7 +13,10 @@
mc:Ignorable="d" mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<Grid RowDefinitions="*"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox <ListBox
Name="GameListBox" Name="GameListBox"
Grid.Row="0" Grid.Row="0"
@@ -11,7 +11,7 @@ using System.Linq;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class ApplicationListView : RyujinxControl<MainWindowViewModel> public partial class ApplicationListView : UserControl
{ {
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<ApplicationListView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
@@ -32,6 +32,9 @@ namespace Ryujinx.Ava.UI.Controls
private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e) private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
{ {
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock playabilityLabel }) if (sender is not Button { Content: TextBlock playabilityLabel })
return; return;
@@ -40,13 +43,16 @@ namespace Ryujinx.Ava.UI.Controls
private async void IdString_OnClick(object sender, RoutedEventArgs e) private async void IdString_OnClick(object sender, RoutedEventArgs e)
{ {
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock idText }) if (sender is not Button { Content: TextBlock idText })
return; return;
if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard)) if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard))
return; return;
ApplicationData appData = ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text); ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text);
if (appData is null) if (appData is null)
return; return;
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class DlcSelectView : RyujinxControl<DlcSelectViewModel> public partial class DlcSelectView : UserControl
{ {
public DlcSelectView() public DlcSelectView()
{ {
@@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Controls
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue], PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
SecondaryButtonText = string.Empty, SecondaryButtonText = string.Empty,
CloseButtonText = string.Empty, CloseButtonText = string.Empty,
Content = new DlcSelectView { ViewModel = viewModel } Content = new DlcSelectView { DataContext = viewModel }
}; };
Style closeButton = new(x => x.Name("CloseButton")); Style closeButton = new(x => x.Name("CloseButton"));
@@ -23,12 +23,13 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class NavigationDialogHost : RyujinxControl<UserProfileViewModel> public partial class NavigationDialogHost : UserControl
{ {
public AccountManager AccountManager { get; } public AccountManager AccountManager { get; }
public ContentManager ContentManager { get; } public ContentManager ContentManager { get; }
public VirtualFileSystem VirtualFileSystem { get; } public VirtualFileSystem VirtualFileSystem { get; }
public HorizonClient HorizonClient { get; } public HorizonClient HorizonClient { get; }
public UserProfileViewModel ViewModel { get; set; }
public NavigationDialogHost() public NavigationDialogHost()
{ {
-18
View File
@@ -1,18 +0,0 @@
using Avalonia.Controls;
using Gommon;
using Ryujinx.Ava.UI.ViewModels;
using System;
namespace Ryujinx.Ava.UI.Controls
{
public class RyujinxControl<TViewModel> : UserControl where TViewModel : BaseModel
{
public TViewModel ViewModel
{
get => (TViewModel)DataContext ?? throw new InvalidOperationException(
$"Underlying DataContext is not of type {typeof(TViewModel).AsPrettyString()}; " +
$"Actual type is {DataContext?.GetType().AsPrettyString()}");
set => DataContext = value;
}
}
}
@@ -13,7 +13,15 @@
<Grid <Grid
Margin="20" Margin="20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto"> VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image <Image
Grid.Row="1" Grid.Row="1"
Height="70" Height="70"
@@ -1,260 +0,0 @@
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Models.Input
{
public class StickVisualizer : BaseModel, IDisposable
{
public const int DrawStickPollRate = 50; // Milliseconds per poll.
public const int DrawStickCircumference = 5;
public const float DrawStickScaleFactor = DrawStickCanvasCenter;
public const int DrawStickCanvasSize = 100;
public const int DrawStickBorderSize = DrawStickCanvasSize + 5;
public const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2;
public const float MaxVectorLength = DrawStickCanvasSize / 2;
public CancellationTokenSource PollTokenSource;
public CancellationToken PollToken;
private static float _vectorLength;
private static float _vectorMultiplier;
private bool disposedValue;
private DeviceType _type;
public DeviceType Type
{
get => _type;
set
{
_type = value;
OnPropertyChanged();
}
}
private GamepadInputConfig _gamepadConfig;
public GamepadInputConfig GamepadConfig
{
get => _gamepadConfig;
set
{
_gamepadConfig = value;
OnPropertyChanged();
}
}
private KeyboardInputConfig _keyboardConfig;
public KeyboardInputConfig KeyboardConfig
{
get => _keyboardConfig;
set
{
_keyboardConfig = value;
OnPropertyChanged();
}
}
private (float, float) _uiStickLeft;
public (float, float) UiStickLeft
{
get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor);
set
{
_uiStickLeft = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UiStickRightX));
OnPropertyChanged(nameof(UiStickRightY));
OnPropertyChanged(nameof(UiDeadzoneRight));
}
}
private (float, float) _uiStickRight;
public (float, float) UiStickRight
{
get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor);
set
{
_uiStickRight = value;
OnPropertyChanged();
OnPropertyChanged(nameof(UiStickLeftX));
OnPropertyChanged(nameof(UiStickLeftY));
OnPropertyChanged(nameof(UiDeadzoneLeft));
}
}
public float UiStickLeftX => ClampVector(UiStickLeft).Item1;
public float UiStickLeftY => ClampVector(UiStickLeft).Item2;
public float UiStickRightX => ClampVector(UiStickRight).Item1;
public float UiStickRightY => ClampVector(UiStickRight).Item2;
public int UiStickCircumference => DrawStickCircumference;
public int UiCanvasSize => DrawStickCanvasSize;
public int UiStickBorderSize => DrawStickBorderSize;
public float? UiDeadzoneLeft => _gamepadConfig?.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference;
public float? UiDeadzoneRight => _gamepadConfig?.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference;
private InputViewModel Parent;
public StickVisualizer(InputViewModel parent)
{
Parent = parent;
PollTokenSource = new CancellationTokenSource();
PollToken = PollTokenSource.Token;
Task.Run(Initialize, PollToken);
}
public void UpdateConfig(object config)
{
if (config is ControllerInputViewModel padConfig)
{
GamepadConfig = padConfig.Config;
Type = DeviceType.Controller;
return;
}
else if (config is KeyboardInputViewModel keyConfig)
{
KeyboardConfig = keyConfig.Config;
Type = DeviceType.Keyboard;
return;
}
Type = DeviceType.None;
}
public async Task Initialize()
{
(float, float) leftBuffer;
(float, float) rightBuffer;
while (!PollToken.IsCancellationRequested)
{
leftBuffer = (0f, 0f);
rightBuffer = (0f, 0f);
switch (Type)
{
case DeviceType.Keyboard:
IKeyboard keyboard = (IKeyboard)Parent.AvaloniaKeyboardDriver.GetGamepad("0");
if (keyboard != null)
{
KeyboardStateSnapshot snapshot = keyboard.GetKeyboardStateSnapshot();
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickRight))
{
leftBuffer.Item1 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickLeft))
{
leftBuffer.Item1 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickUp))
{
leftBuffer.Item2 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.LeftStickDown))
{
leftBuffer.Item2 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickRight))
{
rightBuffer.Item1 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickLeft))
{
rightBuffer.Item1 -= 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickUp))
{
rightBuffer.Item2 += 1;
}
if (snapshot.IsPressed((Key)KeyboardConfig.RightStickDown))
{
rightBuffer.Item2 -= 1;
}
UiStickLeft = leftBuffer;
UiStickRight = rightBuffer;
}
break;
case DeviceType.Controller:
IGamepad controller = Parent.SelectedGamepad;
if (controller != null)
{
leftBuffer = controller.GetStick((StickInputId)GamepadConfig.LeftJoystick);
rightBuffer = controller.GetStick((StickInputId)GamepadConfig.RightJoystick);
}
break;
case DeviceType.None:
break;
default:
throw new ArgumentException($"Unable to poll device type \"{Type}\"");
}
UiStickLeft = leftBuffer;
UiStickRight = rightBuffer;
await Task.Delay(DrawStickPollRate, PollToken);
}
PollTokenSource.Dispose();
}
public static (float, float) ClampVector((float, float) vect)
{
_vectorMultiplier = 1;
_vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2));
if (_vectorLength > MaxVectorLength)
{
_vectorMultiplier = MaxVectorLength / _vectorLength;
}
vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter;
vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter;
return vect;
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
PollTokenSource.Cancel();
}
KeyboardConfig = null;
GamepadConfig = null;
Parent = null;
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
+28
View File
@@ -2,7 +2,9 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using System; using System;
namespace Ryujinx.Ava.UI.Renderer namespace Ryujinx.Ava.UI.Renderer
@@ -36,6 +38,32 @@ namespace Ryujinx.Ava.UI.Renderer
EmbeddedWindowOpenGL => GraphicsBackend.OpenGl, EmbeddedWindowOpenGL => GraphicsBackend.OpenGl,
_ => throw new NotImplementedException() _ => throw new NotImplementedException()
}; };
public RendererHost(string titleId)
{
Focusable = true;
FlowDirection = FlowDirection.LeftToRight;
EmbeddedWindow =
#pragma warning disable CS8524
ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch
#pragma warning restore CS8524
{
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(),
};
string backendText = EmbeddedWindow switch
{
EmbeddedWindowVulkan => "Vulkan",
EmbeddedWindowOpenGL => "OpenGL",
_ => throw new NotImplementedException()
};
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend ({ConfigurationState.Instance.Graphics.GraphicsBackend.Value}): {backendText}");
Initialize();
}
private void Initialize() private void Initialize()
@@ -1,9 +1,5 @@
using Avalonia.Svg.Skia; using Avalonia.Svg.Skia;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Ava.UI.Views.Input;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -14,30 +10,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
public partial class ControllerInputViewModel : BaseModel public partial class ControllerInputViewModel : BaseModel
{ {
private GamepadInputConfig _config; [ObservableProperty] private GamepadInputConfig _config;
public GamepadInputConfig Config
{
get => _config;
set
{
_config = value;
OnPropertyChanged();
}
}
private StickVisualizer _visualizer;
public StickVisualizer Visualizer
{
get => _visualizer;
set
{
_visualizer = value;
OnPropertyChanged();
}
}
private bool _isLeft; private bool _isLeft;
public bool IsLeft public bool IsLeft
{ {
@@ -63,15 +37,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
public bool HasSides => IsLeft ^ IsRight; public bool HasSides => IsLeft ^ IsRight;
[ObservableProperty] private SvgImage _image; [ObservableProperty] private SvgImage _image;
public InputViewModel ParentModel { get; } public InputViewModel ParentModel { get; }
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config, StickVisualizer visualizer) public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
{ {
ParentModel = model; ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged; model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged(); OnParentModelChanged();
config.PropertyChanged += (_, args) => config.PropertyChanged += (_, args) =>
@@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private int _controller; private int _controller;
private string _controllerImage; private string _controllerImage;
private int _device; private int _device;
private object _configViewModel; [ObservableProperty] private object _configViewModel;
[ObservableProperty] private string _profileName; [ObservableProperty] private string _profileName;
private bool _isLoaded; private bool _isLoaded;
@@ -74,7 +74,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed)); OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
} }
} }
public StickVisualizer VisualStick { get; private set; }
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; } public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; } public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
@@ -95,19 +94,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsModified { get; set; } public bool IsModified { get; set; }
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;
public object ConfigViewModel
{
get => _configViewModel;
set
{
_configViewModel = value;
VisualStick.UpdateConfig(value);
OnPropertyChanged();
}
}
public PlayerIndex PlayerIdChoose public PlayerIndex PlayerIdChoose
{ {
get => _playerIdChoose; get => _playerIdChoose;
@@ -266,6 +252,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
_mainWindow.AutoAssignController.ConfigurationUpdated += OnConfigurationUpdated;
_mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
_isLoaded = false; _isLoaded = false;
@@ -283,7 +271,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
Devices = []; Devices = [];
ProfilesList = []; ProfilesList = [];
DeviceList = []; DeviceList = [];
VisualStick = new StickVisualizer(this);
ControllerImage = ProControllerResource; ControllerImage = ProControllerResource;
@@ -304,12 +291,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (Config is StandardKeyboardInputConfig keyboardInputConfig) if (Config is StandardKeyboardInputConfig keyboardInputConfig)
{ {
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick); ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig));
} }
if (Config is StandardControllerInputConfig controllerInputConfig) if (Config is StandardControllerInputConfig controllerInputConfig)
{ {
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick); ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig));
} }
} }
@@ -380,14 +367,47 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private void HandleOnGamepadDisconnected(string id) private void HandleOnGamepadDisconnected(string id)
{ {
if(ConfigurationState.Instance.Hid.EnableAutoAssign) return;
Dispatcher.UIThread.Post(LoadDevices); Dispatcher.UIThread.Post(LoadDevices);
} }
private void HandleOnGamepadConnected(string id) private void HandleOnGamepadConnected(string id)
{ {
if(ConfigurationState.Instance.Hid.EnableAutoAssign) return;
Dispatcher.UIThread.Post(LoadDevices); Dispatcher.UIThread.Post(LoadDevices);
} }
private void OnConfigurationUpdated()
{
Dispatcher.UIThread.Post(() => {
LoadDevices();
_isLoaded = false;
LoadConfiguration();
LoadDevice();
_isLoaded = true;
UpdateGamepadLed();
});
}
private void UpdateGamepadLed()
{
if (ConfigViewModel is not ControllerInputViewModel controllerInputViewModel) return;
GamepadInputConfig inputConfig = controllerInputViewModel.Config;
if (inputConfig is not { EnableLedChanging: true }) return;
if (inputConfig.TurnOffLed)
{
SelectedGamepad.ClearLed();
}
if (!inputConfig.TurnOffLed && !inputConfig.UseRainbowLed)
{
SelectedGamepad.SetLed(inputConfig.LedColor.ToUInt32());
}
}
private string GetCurrentGamepadId() private string GetCurrentGamepadId()
{ {
if (_device < 0) if (_device < 0)
@@ -874,6 +894,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
if (_mainWindow.ViewModel.AppHost != null)
{
_mainWindow.ViewModel.AppHost.NpadManager.AutoAssignEnabled =
ConfigurationState.Instance.Hid.EnableAutoAssign;
}
_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
// Atomically replace and signal input change. // Atomically replace and signal input change.
@@ -905,11 +931,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
_mainWindow.AutoAssignController.ConfigurationUpdated -= OnConfigurationUpdated;
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
VisualStick.Dispose();
SelectedGamepad?.Dispose(); SelectedGamepad?.Dispose();
AvaloniaKeyboardDriver.Dispose(); AvaloniaKeyboardDriver.Dispose();
@@ -6,29 +6,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
public partial class KeyboardInputViewModel : BaseModel public partial class KeyboardInputViewModel : BaseModel
{ {
private KeyboardInputConfig _config; [ObservableProperty] private KeyboardInputConfig _config;
public KeyboardInputConfig Config
{
get => _config;
set
{
_config = value;
OnPropertyChanged();
}
}
private StickVisualizer _visualizer;
public StickVisualizer Visualizer
{
get => _visualizer;
set
{
_visualizer = value;
OnPropertyChanged();
}
}
private bool _isLeft; private bool _isLeft;
public bool IsLeft public bool IsLeft
@@ -60,10 +38,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public readonly InputViewModel ParentModel; public readonly InputViewModel ParentModel;
public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config, StickVisualizer visualizer) public KeyboardInputViewModel(InputViewModel model, KeyboardInputConfig config)
{ {
ParentModel = model; ParentModel = model;
Visualizer = visualizer;
model.NotifyChangesEvent += OnParentModelChanged; model.NotifyChangesEvent += OnParentModelChanged;
OnParentModelChanged(); OnParentModelChanged();
Config = config; Config = config;
+13 -2
View File
@@ -140,6 +140,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableDockedMode { get; set; } public bool EnableDockedMode { get; set; }
public bool EnableKeyboard { get; set; } public bool EnableKeyboard { get; set; }
public bool EnableMouse { get; set; } public bool EnableMouse { get; set; }
public bool EnableAutoAssign { get; set; }
public bool DisableInputWhenOutOfFocus { get; set; } public bool DisableInputWhenOutOfFocus { get; set; }
public int FocusLostActionType { get; set; } public int FocusLostActionType { get; set; }
@@ -563,6 +564,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableDockedMode = config.System.EnableDockedMode; EnableDockedMode = config.System.EnableDockedMode;
EnableKeyboard = config.Hid.EnableKeyboard; EnableKeyboard = config.Hid.EnableKeyboard;
EnableMouse = config.Hid.EnableMouse; EnableMouse = config.Hid.EnableMouse;
EnableAutoAssign = config.Hid.EnableAutoAssign;
DisableInputWhenOutOfFocus = config.Hid.DisableInputWhenOutOfFocus; DisableInputWhenOutOfFocus = config.Hid.DisableInputWhenOutOfFocus;
// Keyboard Hotkeys // Keyboard Hotkeys
@@ -668,6 +670,8 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableDockedMode.Value = EnableDockedMode; config.System.EnableDockedMode.Value = EnableDockedMode;
config.Hid.EnableKeyboard.Value = EnableKeyboard; config.Hid.EnableKeyboard.Value = EnableKeyboard;
config.Hid.EnableMouse.Value = EnableMouse; config.Hid.EnableMouse.Value = EnableMouse;
bool activatingAutoAssign = EnableAutoAssign && !config.Hid.EnableAutoAssign;
config.Hid.EnableAutoAssign.Value = EnableAutoAssign;
config.Hid.DisableInputWhenOutOfFocus.Value = DisableInputWhenOutOfFocus; config.Hid.DisableInputWhenOutOfFocus.Value = DisableInputWhenOutOfFocus;
// Keyboard Hotkeys // Keyboard Hotkeys
@@ -690,7 +694,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.DramSize.Value = DramSize; config.System.DramSize.Value = DramSize;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices; config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
config.System.IgnoreControllerApplet.Value = IgnoreApplet; config.System.IgnoreControllerApplet.Value = (EnableAutoAssign) || IgnoreApplet;
// CPU // CPU
config.System.EnablePtc.Value = EnablePptc; config.System.EnablePtc.Value = EnablePptc;
@@ -766,7 +770,14 @@ namespace Ryujinx.Ava.UI.ViewModels
MainWindow.UpdateGraphicsConfig(); MainWindow.UpdateGraphicsConfig();
RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged(); RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
SaveSettingsEvent?.Invoke(); if(activatingAutoAssign)
{
RyujinxApp.MainWindow.AutoAssignController.RefreshControllers();
}
else
{
SaveSettingsEvent?.Invoke();
}
GameListNeedsRefresh = false; GameListNeedsRefresh = false;
} }
@@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class UserFirmwareAvatarSelectorViewModel : BaseModel internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel
{ {
private static readonly Dictionary<string, byte[]> _avatarStore = new(); private static readonly Dictionary<string, byte[]> _avatarStore = new();
@@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class UserProfileImageSelectorViewModel : BaseModel internal partial class UserProfileImageSelectorViewModel : BaseModel
{ {
[ObservableProperty] private bool _firmwareFound; [ObservableProperty] private bool _firmwareFound;
} }
@@ -34,7 +34,12 @@
<!-- Button / JoyStick Settings --> <!-- Button / JoyStick Settings -->
<Grid <Grid
Name="SettingButtons" Name="SettingButtons"
MinHeight="450" ColumnDefinitions="Auto,*,Auto"> MinHeight="450">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Left Controls --> <!-- Left Controls -->
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -49,7 +54,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
@@ -303,99 +316,17 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<!-- Controller Picture --> <!-- Controller Picture -->
<Image
Margin="0,10"
MaxHeight="300"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="{Binding Image}" />
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
Margin="0,0, 0, 5"
MinHeight="90"> MinHeight="90">
<StackPanel Orientation="Vertical">
<Image
Margin="5,10"
MaxHeight="300"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="{Binding Image}" />
<StackPanel
Margin="10"
Orientation="Horizontal"
Spacing="20"
HorizontalAlignment="Center">
<Border
BorderBrush="Transparent"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsLeft}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="{DynamicResource ThemeControlBorderColor}"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}" />
<Ellipse
HorizontalAlignment="Center"
Fill="Black"
Opacity="100"
Height="{Binding Visualizer.UiDeadzoneLeft}"
Width="{Binding Visualizer.UiDeadzoneLeft}" />
</Grid>
<Ellipse
Fill="{DynamicResource Warning}"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickLeftY}"
Canvas.Left="{Binding Visualizer.UiStickLeftX}" />
</Canvas>
</Border>
<Border
BorderBrush="Transparent"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsRight}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="{DynamicResource ThemeControlBorderColor}"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}" />
<Ellipse
HorizontalAlignment="Center"
Fill="Black"
Opacity="100"
Height="{Binding Visualizer.UiDeadzoneRight}"
Width="{Binding Visualizer.UiDeadzoneRight}" />
</Grid>
<Ellipse
Fill="{DynamicResource Warning}"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickRightY}"
Canvas.Left="{Binding Visualizer.UiStickRightX}" />
</Canvas>
</Border>
</StackPanel>
</StackPanel>
</Border>
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5">
<StackPanel <StackPanel
Margin="8" Margin="8"
Orientation="Vertical"> Orientation="Vertical">
@@ -414,8 +345,8 @@
Minimum="0" Minimum="0"
Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" /> Value="{Binding Config.TriggerThreshold, Mode=TwoWay}" />
<TextBlock <TextBlock
Width="25" Width="25"
Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" /> Text="{Binding Config.TriggerThreshold, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -497,7 +428,7 @@
</Border> </Border>
<!-- Motion, Rumble, LED --> <!-- Motion, Rumble, LED -->
<StackPanel <StackPanel
Margin="0,5,0,0" Margin="0,10,0,0"
Spacing="5" Spacing="5"
Orientation="Vertical" Orientation="Vertical"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
@@ -507,7 +438,11 @@
CornerRadius="5" CornerRadius="5"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Grid ColumnDefinitions="*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<CheckBox <CheckBox
Margin="10" Margin="10"
MinWidth="0" MinWidth="0"
@@ -529,7 +464,11 @@
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,-1,0,0"> Margin="0,-1,0,0">
<Grid ColumnDefinitions="*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<CheckBox <CheckBox
Margin="10" Margin="10"
MinWidth="0" MinWidth="0"
@@ -551,7 +490,11 @@
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,-1,0,0"> Margin="0,-1,0,0">
<Grid IsVisible="{Binding ParentModel.HasLed}" ColumnDefinitions="*,Auto"> <Grid IsVisible="{Binding ParentModel.HasLed}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<CheckBox <CheckBox
Margin="10, 10, 5, 10" Margin="10, 10, 5, 10"
MinWidth="0" MinWidth="0"
@@ -583,7 +526,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
@@ -4,7 +4,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
@@ -15,7 +14,7 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class ControllerInputView : RyujinxControl<ControllerInputViewModel> public partial class ControllerInputView : UserControl
{ {
private ButtonKeyAssigner _currentAssigner; private ButtonKeyAssigner _currentAssigner;
@@ -218,12 +217,20 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed -= MouseClick; PointerPressed -= MouseClick;
} }
private IButtonAssigner CreateButtonAssigner(bool forStick) => private IButtonAssigner CreateButtonAssigner(bool forStick)
new GamepadButtonAssigner( {
ViewModel.ParentModel.SelectedGamepad, IButtonAssigner assigner;
(ViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel;
assigner = new GamepadButtonAssigner(
controllerInputViewModel.ParentModel.SelectedGamepad,
(controllerInputViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold,
forStick); forStick);
return assigner;
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);
+36 -6
View File
@@ -35,13 +35,22 @@
Margin="0 0 0 5" Margin="0 0 0 5"
Orientation="Vertical" Orientation="Vertical"
Spacing="5"> Spacing="5">
<Grid ColumnDefinitions="*,10,*"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Player Selection --> <!-- Player Selection -->
<Grid <Grid
Grid.Column="0" Grid.Column="0"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*"> VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90" Width="90"
@@ -68,7 +77,14 @@
Grid.Column="2" Grid.Column="2"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*,Auto,Auto,Auto"> VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90" Width="90"
@@ -123,12 +139,22 @@
</Grid> </Grid>
</Grid> </Grid>
<Separator /> <Separator />
<Grid ColumnDefinitions="*,10,*"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Input Device --> <!-- Input Device -->
<Grid <Grid
Grid.Column="0" Grid.Column="0"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*,Auto"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"
Margin="5,0,10,0" Margin="5,0,10,0"
@@ -160,7 +186,11 @@
Grid.Column="2" Grid.Column="2"
Margin="2" Margin="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" ColumnDefinitions="Auto,*"> VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90" Width="90"
@@ -1,19 +1,19 @@
using Avalonia.Controls; using Avalonia.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class InputView : RyujinxControl<InputViewModel> public partial class InputView : UserControl
{ {
private bool _dialogOpen; private bool _dialogOpen;
private InputViewModel ViewModel { get; set; }
public InputView() public InputView()
{ {
ViewModel = new InputViewModel(this); DataContext = ViewModel = new InputViewModel(this);
InitializeComponent(); InitializeComponent();
} }
@@ -32,7 +32,12 @@
<!-- Button / JoyStick Settings --> <!-- Button / JoyStick Settings -->
<Grid <Grid
Name="SettingButtons" Name="SettingButtons"
MinHeight="450" ColumnDefinitions="Auto,*,Auto"> MinHeight="450">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Left Controls --> <!-- Left Controls -->
<StackPanel <StackPanel
Orientation="Vertical" Orientation="Vertical"
@@ -47,7 +52,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
@@ -296,79 +309,12 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<!-- Controller Picture --> <!-- Controller Picture -->
<Border <Image
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Margin="0,10" Margin="0,10"
MinHeight="90"> MaxHeight="300"
<StackPanel HorizontalAlignment="Stretch"
Margin="10" VerticalAlignment="Stretch"
Orientation="Horizontal" Source="{Binding Image}" />
Spacing="20"
HorizontalAlignment="Center">
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsLeft}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="Black"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}"/>
</Grid>
<Ellipse
Fill="Red"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickLeftY}"
Canvas.Left="{Binding Visualizer.UiStickLeftX}" />
</Canvas>
</Border>
<Border
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1"
CornerRadius="5"
Height="{Binding Visualizer.UiStickBorderSize}"
Width="{Binding Visualizer.UiStickBorderSize}"
IsVisible="{Binding IsRight}">
<Canvas
Background="{DynamicResource ThemeBackgroundColor}"
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}">
<Grid
Height="{Binding Visualizer.UiCanvasSize}"
Width="{Binding Visualizer.UiCanvasSize}"
Background="{DynamicResource ThemeBackgroundColor}">
<Ellipse
HorizontalAlignment="Center"
Stroke="Black"
StrokeThickness="1"
Width="{Binding Visualizer.UiCanvasSize}"
Height="{Binding Visualizer.UiCanvasSize}"/>
</Grid>
<Ellipse
Fill="Red"
Width="{Binding Visualizer.UiStickCircumference}"
Height="{Binding Visualizer.UiStickCircumference}"
Canvas.Bottom="{Binding Visualizer.UiStickRightY}"
Canvas.Left="{Binding Visualizer.UiStickRightX}" />
</Canvas>
</Border>
</StackPanel>
</Border>
<Border <Border
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
@@ -467,7 +413,15 @@
CornerRadius="5"> CornerRadius="5">
<Grid <Grid
Margin="10" Margin="10"
HorizontalAlignment="Stretch" ColumnDefinitions="*,*" RowDefinitions="*,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
@@ -4,7 +4,6 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input; using Ryujinx.Input;
@@ -14,7 +13,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class KeyboardInputView : RyujinxControl<KeyboardInputViewModel> public partial class KeyboardInputView : UserControl
{ {
private ButtonKeyAssigner _currentAssigner; private ButtonKeyAssigner _currentAssigner;
@@ -61,103 +60,106 @@ namespace Ryujinx.Ava.UI.Views.Input
PointerPressed += MouseClick; PointerPressed += MouseClick;
IKeyboard keyboard = if (DataContext is not KeyboardInputViewModel viewModel)
(IKeyboard)ViewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. return;
IButtonAssigner assigner =
new KeyboardKeyAssigner((IKeyboard)ViewModel.ParentModel.SelectedGamepad);
_currentAssigner.ButtonAssigned += (_, be) => IKeyboard keyboard =
(IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
IButtonAssigner assigner =
new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad);
_currentAssigner.ButtonAssigned += (_, e) =>
{ {
if (be.ButtonValue.HasValue) if (e.ButtonValue.HasValue)
{ {
Button buttonValue = be.ButtonValue.Value; Button buttonValue = e.ButtonValue.Value;
ViewModel.ParentModel.IsModified = true; viewModel.ParentModel.IsModified = true;
switch (button.Name) switch (button.Name)
{ {
case "ButtonZl": case "ButtonZl":
ViewModel.Config.ButtonZl = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
break; break;
case "ButtonL": case "ButtonL":
ViewModel.Config.ButtonL = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
break; break;
case "ButtonMinus": case "ButtonMinus":
ViewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickButton": case "LeftStickButton":
ViewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickUp": case "LeftStickUp":
ViewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickDown": case "LeftStickDown":
ViewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickRight": case "LeftStickRight":
ViewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickLeft": case "LeftStickLeft":
ViewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
break; break;
case "DpadUp": case "DpadUp":
ViewModel.Config.DpadUp = buttonValue.AsHidType<Key>(); viewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
break; break;
case "DpadDown": case "DpadDown":
ViewModel.Config.DpadDown = buttonValue.AsHidType<Key>(); viewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
break; break;
case "DpadLeft": case "DpadLeft":
ViewModel.Config.DpadLeft = buttonValue.AsHidType<Key>(); viewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
break; break;
case "DpadRight": case "DpadRight":
ViewModel.Config.DpadRight = buttonValue.AsHidType<Key>(); viewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
break; break;
case "LeftButtonSr": case "LeftButtonSr":
ViewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>(); viewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
break; break;
case "LeftButtonSl": case "LeftButtonSl":
ViewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>(); viewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
break; break;
case "RightButtonSr": case "RightButtonSr":
ViewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>(); viewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
break; break;
case "RightButtonSl": case "RightButtonSl":
ViewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>(); viewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
break; break;
case "ButtonZr": case "ButtonZr":
ViewModel.Config.ButtonZr = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
break; break;
case "ButtonR": case "ButtonR":
ViewModel.Config.ButtonR = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
break; break;
case "ButtonPlus": case "ButtonPlus":
ViewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
break; break;
case "ButtonA": case "ButtonA":
ViewModel.Config.ButtonA = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
break; break;
case "ButtonB": case "ButtonB":
ViewModel.Config.ButtonB = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
break; break;
case "ButtonX": case "ButtonX":
ViewModel.Config.ButtonX = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
break; break;
case "ButtonY": case "ButtonY":
ViewModel.Config.ButtonY = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
break; break;
case "RightStickButton": case "RightStickButton":
ViewModel.Config.RightStickButton = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
break; break;
case "RightStickUp": case "RightStickUp":
ViewModel.Config.RightStickUp = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
break; break;
case "RightStickDown": case "RightStickDown":
ViewModel.Config.RightStickDown = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
break; break;
case "RightStickRight": case "RightStickRight":
ViewModel.Config.RightStickRight = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
break; break;
case "RightStickLeft": case "RightStickLeft":
ViewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
break; break;
} }
} }
@@ -2,18 +2,19 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.UI.Views.Input namespace Ryujinx.UI.Views.Input
{ {
public partial class LedInputView : RyujinxControl<LedInputViewModel> public partial class LedInputView : UserControl
{ {
private readonly LedInputViewModel _viewModel;
public LedInputView(ControllerInputViewModel viewModel) public LedInputView(ControllerInputViewModel viewModel)
{ {
ViewModel = new LedInputViewModel DataContext = _viewModel = new LedInputViewModel
{ {
ParentModel = viewModel.ParentModel, ParentModel = viewModel.ParentModel,
TurnOffLed = viewModel.Config.TurnOffLed, TurnOffLed = viewModel.Config.TurnOffLed,
@@ -28,18 +29,20 @@ namespace Ryujinx.UI.Views.Input
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args) private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
{ {
if (!args.NewColor.HasValue) return; if (!args.NewColor.HasValue) return;
if (!ViewModel.EnableLedChanging) return; if (DataContext is not LedInputViewModel lvm) return;
if (ViewModel.TurnOffLed) return; if (!lvm.EnableLedChanging) return;
if (lvm.TurnOffLed) return;
ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
} }
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{ {
if (!ViewModel.EnableLedChanging) return; if (DataContext is not LedInputViewModel lvm) return;
if (ViewModel.TurnOffLed) return; if (!lvm.EnableLedChanging) return;
if (lvm.TurnOffLed) return;
ViewModel.ParentModel.SelectedGamepad.SetLed(ViewModel.LedColor.ToUInt32()); lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32());
} }
public static async Task Show(ControllerInputViewModel viewModel) public static async Task Show(ControllerInputViewModel viewModel)
@@ -54,13 +57,13 @@ namespace Ryujinx.UI.Views.Input
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content, Content = content,
}; };
contentDialog.PrimaryButtonClick += (_, _) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.EnableLedChanging = content.ViewModel.EnableLedChanging; config.EnableLedChanging = content._viewModel.EnableLedChanging;
config.LedColor = content.ViewModel.LedColor; config.LedColor = content._viewModel.LedColor;
config.UseRainbowLed = content.ViewModel.UseRainbowLed; config.UseRainbowLed = content._viewModel.UseRainbowLed;
config.TurnOffLed = content.ViewModel.TurnOffLed; config.TurnOffLed = content._viewModel.TurnOffLed;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();
@@ -11,7 +11,11 @@
x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView" x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
x:DataType="viewModels:MotionInputViewModel" x:DataType="viewModels:MotionInputViewModel"
Focusable="True"> Focusable="True">
<Grid Margin="10" RowDefinitions="Auto,*"> <Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel <StackPanel
Orientation="Horizontal" Orientation="Horizontal"
@@ -76,7 +80,11 @@
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Grid VerticalAlignment="Top" RowDefinitions="Auto,*"> <Grid VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Center" HorizontalAlignment="Center"
@@ -110,7 +118,15 @@
Text="{Binding DsuServerPort, Mode=TwoWay}" /> Text="{Binding DsuServerPort, Mode=TwoWay}" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<Grid RowDefinitions="*,*" ColumnDefinitions="*,*"> <Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Margin="0,10,0,0" Margin="0,10,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -1,15 +1,16 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class MotionInputView : RyujinxControl<MotionInputViewModel> public partial class MotionInputView : UserControl
{ {
private readonly MotionInputViewModel _viewModel;
public MotionInputView() public MotionInputView()
{ {
InitializeComponent(); InitializeComponent();
@@ -19,7 +20,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
ViewModel = new MotionInputViewModel _viewModel = new MotionInputViewModel
{ {
Slot = config.Slot, Slot = config.Slot,
AltSlot = config.AltSlot, AltSlot = config.AltSlot,
@@ -32,6 +33,7 @@ namespace Ryujinx.Ava.UI.Views.Input
}; };
InitializeComponent(); InitializeComponent();
DataContext = _viewModel;
} }
public static async Task Show(ControllerInputViewModel viewModel) public static async Task Show(ControllerInputViewModel viewModel)
@@ -46,17 +48,17 @@ namespace Ryujinx.Ava.UI.Views.Input
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content, Content = content,
}; };
contentDialog.PrimaryButtonClick += (_, _) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.Slot = content.ViewModel.Slot; config.Slot = content._viewModel.Slot;
config.Sensitivity = content.ViewModel.Sensitivity; config.Sensitivity = content._viewModel.Sensitivity;
config.GyroDeadzone = content.ViewModel.GyroDeadzone; config.GyroDeadzone = content._viewModel.GyroDeadzone;
config.AltSlot = content.ViewModel.AltSlot; config.AltSlot = content._viewModel.AltSlot;
config.DsuServerHost = content.ViewModel.DsuServerHost; config.DsuServerHost = content._viewModel.DsuServerHost;
config.DsuServerPort = content.ViewModel.DsuServerPort; config.DsuServerPort = content._viewModel.DsuServerPort;
config.EnableCemuHookMotion = content.ViewModel.EnableCemuHookMotion; config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
config.MirrorInput = content.ViewModel.MirrorInput; config.MirrorInput = content._viewModel.MirrorInput;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();
@@ -10,7 +10,11 @@
x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView" x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView"
x:DataType="viewModels:RumbleInputViewModel" x:DataType="viewModels:RumbleInputViewModel"
Focusable="True"> Focusable="True">
<Grid Margin="10" RowDefinitions="Auto,*"> <Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock <TextBlock
@@ -1,15 +1,16 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Input namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class RumbleInputView : RyujinxControl<RumbleInputViewModel> public partial class RumbleInputView : UserControl
{ {
private readonly RumbleInputViewModel _viewModel;
public RumbleInputView() public RumbleInputView()
{ {
InitializeComponent(); InitializeComponent();
@@ -19,13 +20,15 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
ViewModel = new RumbleInputViewModel _viewModel = new RumbleInputViewModel
{ {
StrongRumble = config.StrongRumble, StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble, WeakRumble = config.WeakRumble,
}; };
InitializeComponent(); InitializeComponent();
DataContext = _viewModel;
} }
public static async Task Show(ControllerInputViewModel viewModel) public static async Task Show(ControllerInputViewModel viewModel)
@@ -41,11 +44,11 @@ namespace Ryujinx.Ava.UI.Views.Input
Content = content, Content = content,
}; };
contentDialog.PrimaryButtonClick += (_, _) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {
GamepadInputConfig config = viewModel.Config; GamepadInputConfig config = viewModel.Config;
config.StrongRumble = content.ViewModel.StrongRumble; config.StrongRumble = content._viewModel.StrongRumble;
config.WeakRumble = content.ViewModel.WeakRumble; config.WeakRumble = content._viewModel.WeakRumble;
}; };
await contentDialog.ShowAsync(); await contentDialog.ShowAsync();
@@ -6,7 +6,6 @@ using Gommon;
using LibHac.Common; using LibHac.Common;
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
@@ -26,9 +25,10 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
public partial class MainMenuBarView : RyujinxControl<MainWindowViewModel> public partial class MainMenuBarView : UserControl
{ {
public MainWindow Window { get; private set; } public MainWindow Window { get; private set; }
public MainWindowViewModel ViewModel { get; private set; }
public MainMenuBarView() public MainMenuBarView()
{ {
@@ -4,8 +4,6 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common; using Ryujinx.Common;
@@ -15,7 +13,7 @@ using System;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
public partial class MainStatusBarView : RyujinxControl<MainWindowViewModel> public partial class MainStatusBarView : UserControl
{ {
public MainWindow Window; public MainWindow Window;
@@ -31,7 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Main
if (VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
Window = window; Window = window;
ViewModel = window.ViewModel; DataContext = window.ViewModel;
LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() => LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() =>
{ {
if (Window.ViewModel.EnableNonGameRunningControls) if (Window.ViewModel.EnableNonGameRunningControls)
@@ -3,15 +3,16 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using System; using System;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main
{ {
public partial class MainViewControls : RyujinxControl<MainWindowViewModel> public partial class MainViewControls : UserControl
{ {
public MainWindowViewModel ViewModel;
public MainViewControls() public MainViewControls()
{ {
InitializeComponent(); InitializeComponent();
@@ -23,7 +24,7 @@ namespace Ryujinx.Ava.UI.Views.Main
if (VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
ViewModel = window.ViewModel; DataContext = ViewModel = window.ViewModel;
} }
} }
@@ -21,7 +21,12 @@
<Border Classes="settings"> <Border Classes="settings">
<Panel <Panel
Margin="10"> Margin="10">
<Grid RowDefinitions="Auto,*,Auto"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<views:InputView <views:InputView
Grid.Row="0" Grid.Row="0"
Name="InputView" /> Name="InputView" />
@@ -53,6 +58,12 @@
<TextBlock <TextBlock
Text="{ext:Locale SettingsTabInputDirectMouseAccess}" /> Text="{ext:Locale SettingsTabInputDirectMouseAccess}" />
</CheckBox> </CheckBox>
<CheckBox
ToolTip.Tip="{ext:Locale AutoAssignTooltip}"
IsChecked="{Binding EnableAutoAssign}">
<TextBlock
Text="{ext:Locale SettingsTabInputAutoAssign}" />
</CheckBox>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
@@ -319,6 +319,7 @@
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreMissingServices}" /> <TextBlock Text="{ext:Locale SettingsTabSystemIgnoreMissingServices}" />
</CheckBox> </CheckBox>
<CheckBox <CheckBox
IsEnabled="{Binding !EnableAutoAssign}"
IsChecked="{Binding IgnoreApplet}" IsChecked="{Binding IgnoreApplet}"
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}"> ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" /> <TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
@@ -177,7 +177,12 @@
</Style> </Style>
</ListBox.Styles> </ListBox.Styles>
</ListBox> </ListBox>
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto,Auto"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox <TextBox
Name="GameDirPathBox" Name="GameDirPathBox"
Margin="0" Margin="0"
@@ -230,7 +235,12 @@
</Style> </Style>
</ListBox.Styles> </ListBox.Styles>
</ListBox> </ListBox>
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto,Auto"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox <TextBox
Name="AutoloadDirPathBox" Name="AutoloadDirPathBox"
Margin="0" Margin="0"
@@ -14,7 +14,15 @@
mc:Ignorable="d" mc:Ignorable="d"
Focusable="True" Focusable="True"
x:DataType="models:TempProfile"> x:DataType="models:TempProfile">
<Grid Margin="0" ColumnDefinitions="Auto,*" RowDefinitions="*,Auto"> <Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
@@ -13,12 +13,13 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserEditorView : RyujinxControl<TempProfile> public partial class UserEditorView : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private UserProfile _profile; private UserProfile _profile;
private bool _isNewUser; private bool _isNewUser;
public TempProfile TempProfile { get; set; }
public static uint MaxProfileNameLength => 0x20; public static uint MaxProfileNameLength => 0x20;
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId; public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
@@ -41,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.User
(NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; (NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
_isNewUser = isNewUser; _isNewUser = isNewUser;
_profile = profile; _profile = profile;
ViewModel = new TempProfile(_profile); TempProfile = new TempProfile(_profile);
_parent = parent; _parent = parent;
break; break;
@@ -50,6 +51,8 @@ namespace Ryujinx.Ava.UI.Views.User
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " + ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
$"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; $"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
DataContext = TempProfile;
AddPictureButton.IsVisible = _isNewUser; AddPictureButton.IsVisible = _isNewUser;
ChangePictureButton.IsVisible = !_isNewUser; ChangePictureButton.IsVisible = !_isNewUser;
IdLabel.IsVisible = _profile != null; IdLabel.IsVisible = _profile != null;
@@ -69,7 +72,7 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
if (_isNewUser) if (_isNewUser)
{ {
if (ViewModel.Name != string.Empty || ViewModel.Image != null) if (TempProfile.Name != String.Empty || TempProfile.Image != null)
{ {
if (await ContentDialogHelper.CreateChoiceDialog( if (await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
@@ -86,7 +89,7 @@ namespace Ryujinx.Ava.UI.Views.User
} }
else else
{ {
if (_profile.Name != ViewModel.Name || _profile.Image != ViewModel.Image) if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image)
{ {
if (await ContentDialogHelper.CreateChoiceDialog( if (await ContentDialogHelper.CreateChoiceDialog(
LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle],
@@ -112,31 +115,31 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
DataValidationErrors.ClearErrors(NameBox); DataValidationErrors.ClearErrors(NameBox);
if (string.IsNullOrWhiteSpace(ViewModel.Name)) if (string.IsNullOrWhiteSpace(TempProfile.Name))
{ {
DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError])); DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError]));
return; return;
} }
if (ViewModel.Image == null) if (TempProfile.Image == null)
{ {
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
return; return;
} }
if (_profile != null && !_isNewUser) if (_profile != null && !_isNewUser)
{ {
_profile.Name = ViewModel.Name; _profile.Name = TempProfile.Name;
_profile.Image = ViewModel.Image; _profile.Image = TempProfile.Image;
_profile.UpdateState(); _profile.UpdateState();
_parent.AccountManager.SetUserName(_profile.UserId, _profile.Name); _parent.AccountManager.SetUserName(_profile.UserId, _profile.Name);
_parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image); _parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image);
} }
else if (_isNewUser) else if (_isNewUser)
{ {
_parent.AccountManager.AddUser(ViewModel.Name, ViewModel.Image, ViewModel.UserId); _parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId);
} }
else else
{ {
@@ -148,7 +151,7 @@ namespace Ryujinx.Ava.UI.Views.User
public void SelectProfileImage() public void SelectProfileImage()
{ {
_parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile));
} }
private void ChangePictureButton_Click(object sender, RoutedEventArgs e) private void ChangePictureButton_Click(object sender, RoutedEventArgs e)
@@ -20,7 +20,13 @@
<Grid <Grid
Margin="0" Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" RowDefinitions="Auto,*,Auto,Auto"> VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox <ListBox
Grid.Row="1" Grid.Row="1"
BorderThickness="0" BorderThickness="0"
@@ -1,3 +1,4 @@
using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
@@ -10,7 +11,7 @@ using System.IO;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserFirmwareAvatarSelectorView : RyujinxControl<UserFirmwareAvatarSelectorViewModel> public partial class UserFirmwareAvatarSelectorView : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private TempProfile _profile; private TempProfile _profile;
@@ -19,6 +20,8 @@ namespace Ryujinx.Ava.UI.Views.User
{ {
ContentManager = contentManager; ContentManager = contentManager;
DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
} }
@@ -52,6 +55,8 @@ namespace Ryujinx.Ava.UI.Views.User
public ContentManager ContentManager { get; private set; } public ContentManager ContentManager { get; private set; }
internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; }
private void GoBack(object sender, RoutedEventArgs e) private void GoBack(object sender, RoutedEventArgs e)
{ {
_parent.GoBack(); _parent.GoBack();
@@ -17,7 +17,12 @@
</Design.DataContext> </Design.DataContext>
<Grid <Grid
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" RowDefinitions="Auto,70,Auto"> VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="70" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock <TextBlock
Grid.Row="0" Grid.Row="0"
TextWrapping="Wrap" TextWrapping="Wrap"
@@ -15,12 +15,14 @@ using System.IO;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserProfileImageSelectorView : RyujinxControl<UserProfileImageSelectorViewModel> public partial class UserProfileImageSelectorView : UserControl
{ {
private ContentManager _contentManager; private ContentManager _contentManager;
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
private TempProfile _profile; private TempProfile _profile;
internal UserProfileImageSelectorViewModel ViewModel { get; private set; }
public UserProfileImageSelectorView() public UserProfileImageSelectorView()
{ {
InitializeComponent(); InitializeComponent();
@@ -18,7 +18,11 @@
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" <Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" RowDefinitions="*,Auto"> VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border <Border
CornerRadius="5" CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
@@ -37,7 +41,11 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="5"> CornerRadius="5">
<Grid Margin="0" ColumnDefinitions="*,Auto"> <Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding UserId}" Text="{Binding UserId}"
@@ -4,11 +4,10 @@ using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserRecovererView : RyujinxControl<UserProfileViewModel> public partial class UserRecovererView : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
@@ -19,10 +19,19 @@
<Design.DataContext> <Design.DataContext>
<viewModels:UserSaveManagerViewModel /> <viewModels:UserSaveManagerViewModel />
</Design.DataContext> </Design.DataContext>
<Grid RowDefinitions="Auto,*,Auto"> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid <Grid
Grid.Row="0" Grid.Row="0"
HorizontalAlignment="Stretch" ColumnDefinitions="Auto,*"> HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel <StackPanel
Spacing="10" Spacing="10"
Orientation="Horizontal" Orientation="Horizontal"
@@ -71,7 +80,11 @@
<Grid <Grid
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="10,0, 0, 0" ColumnDefinitions="Auto,*"> Margin="10,0, 0, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="{ext:Locale Search}" VerticalAlignment="Center" /> <Label Content="{ext:Locale Search}" VerticalAlignment="Center" />
<TextBox <TextBox
Margin="5,0,0,0" Margin="5,0,0,0"
@@ -105,7 +118,11 @@
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="models:SaveModel"> <DataTemplate x:DataType="models:SaveModel">
<Grid HorizontalAlignment="Stretch" ColumnDefinitions="*,Auto"> <Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Orientation="Horizontal" Orientation="Horizontal"
@@ -23,8 +23,10 @@ using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserSaveManagerView : RyujinxControl<UserSaveManagerViewModel> public partial class UserSaveManagerView : UserControl
{ {
internal UserSaveManagerViewModel ViewModel { get; private set; }
private AccountManager _accountManager; private AccountManager _accountManager;
private HorizonClient _horizonClient; private HorizonClient _horizonClient;
private VirtualFileSystem _virtualFileSystem; private VirtualFileSystem _virtualFileSystem;
@@ -64,7 +66,7 @@ namespace Ryujinx.Ava.UI.Views.User
public void LoadSaves() public void LoadSaves()
{ {
Dispatcher.UIThread.Post(() => ViewModel.Saves.Clear()); ViewModel.Saves.Clear();
ObservableCollection<SaveModel> saves = []; ObservableCollection<SaveModel> saves = [];
SaveDataFilter saveDataFilter = SaveDataFilter.Make( SaveDataFilter saveDataFilter = SaveDataFilter.Make(
programId: default, programId: default,
@@ -18,7 +18,11 @@
<Design.DataContext> <Design.DataContext>
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*,Auto"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border <Border
CornerRadius="5" CornerRadius="5"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}" BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
@@ -11,10 +11,12 @@ using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
public partial class UserSelectorViews : RyujinxControl<UserProfileViewModel> public partial class UserSelectorViews : UserControl
{ {
private NavigationDialogHost _parent; private NavigationDialogHost _parent;
public UserProfileViewModel ViewModel { get; set; }
public UserSelectorViews() public UserSelectorViews()
{ {
InitializeComponent(); InitializeComponent();
@@ -53,12 +53,18 @@
<!-- For image --> <!-- For image -->
<ui:NavigationView.PaneHeader> <ui:NavigationView.PaneHeader>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" RowDefinitions="Auto,Auto,Auto,5"> <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding GameId}" <TextBlock Text="{Binding GameId}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="0,0,0,10" Margin="0,0,0,10"
TextAlignment="Center" Grid.Row="0" /> TextAlignment="Center" Grid.Row="0" />
<Image Source="{Binding GameIcon}" <Image Source="{Binding GameIcon}"
Width="160" Width="160"
Height="160" Height="160"
@@ -64,6 +64,7 @@ namespace Ryujinx.Ava.UI.Windows
public LibHacHorizonManager LibHacHorizonManager { get; private set; } public LibHacHorizonManager LibHacHorizonManager { get; private set; }
public InputManager InputManager { get; private set; } public InputManager InputManager { get; private set; }
public AutoAssignController AutoAssignController { get; private set; }
public SettingsWindow SettingsWindow { get; set; } public SettingsWindow SettingsWindow { get; set; }
@@ -110,6 +111,7 @@ namespace Ryujinx.Ava.UI.Windows
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
AutoAssignController = new AutoAssignController(InputManager, ViewModel);
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it); _ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
this.ScalingChanged += OnScalingChanged; this.ScalingChanged += OnScalingChanged;
@@ -70,7 +70,11 @@
<DataTemplate <DataTemplate
DataType="models:ModModel"> DataType="models:ModModel">
<Panel Margin="10"> <Panel Margin="10">
<Grid ColumnDefinitions="*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
+1 -1
View File
@@ -12,7 +12,7 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common" xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
Width="1100" Width="1100"
Height="927" Height="918"
MinWidth="800" MinWidth="800"
MinHeight="480" MinHeight="480"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
+38 -6
View File
@@ -13,7 +13,14 @@
x:DataType="viewModels:XCITrimmerViewModel" x:DataType="viewModels:XCITrimmerViewModel"
Focusable="True" Focusable="True"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Margin="20 0 20 0" RowDefinitions="Auto,Auto,*,Auto,Auto"> <Grid Margin="20 0 20 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Panel <Panel
Margin="10 10 10 10" Margin="10 10 10 10"
Grid.Row="0"> Grid.Row="0">
@@ -23,7 +30,12 @@
Margin="0 0 10 10" Margin="0 0 10 10"
IsVisible="{Binding !Processing}" IsVisible="{Binding !Processing}"
Grid.Row="1"> Grid.Row="1">
<Grid ColumnDefinitions="Auto,*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Orientation="Horizontal"> Orientation="Horizontal">
@@ -133,7 +145,11 @@
<DataTemplate <DataTemplate
DataType="models:XCITrimmerFileModel"> DataType="models:XCITrimmerFileModel">
<Panel Margin="10"> <Panel Margin="10">
<Grid ColumnDefinitions="65*,35*"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65*" />
<ColumnDefinition Width="35*" />
</Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"
Margin="10 0 10 0" Margin="10 0 10 0"
@@ -144,7 +160,11 @@
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
Text="{Binding Name}"> Text="{Binding Name}">
</TextBlock> </TextBlock>
<Grid Grid.Column="1" ColumnDefinitions="45*,55*"> <Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="45*" />
<ColumnDefinition Width="55*" />
</Grid.ColumnDefinitions>
<ProgressBar <ProgressBar
Height="10" Height="10"
Margin="10 0 10 0" Margin="10 0 10 0"
@@ -206,7 +226,15 @@
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
Padding="2.5"> Padding="2.5">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
@@ -246,7 +274,11 @@
<Panel <Panel
Grid.Row="4" Grid.Row="4"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<Grid ColumnDefinitions="*,Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel <StackPanel
Grid.Column="0" Grid.Column="0"
Orientation="Horizontal" Orientation="Horizontal"
@@ -389,6 +389,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
public bool EnableMouse { get; set; } public bool EnableMouse { get; set; }
/// <summary>
/// Enable or disable automatic controller assignment for players
/// </summary>
public bool EnableAutoAssign { get; set; }
/// <summary> /// <summary>
/// Enable/disable the ability to control Ryujinx when it's not the currently focused window. /// Enable/disable the ability to control Ryujinx when it's not the currently focused window.
/// </summary> /// </summary>
@@ -144,6 +144,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
Hid.EnableKeyboard.Value = cff.EnableKeyboard; Hid.EnableKeyboard.Value = cff.EnableKeyboard;
Hid.EnableMouse.Value = cff.EnableMouse; Hid.EnableMouse.Value = cff.EnableMouse;
Hid.EnableAutoAssign.Value = cff.EnableAutoAssign;
Hid.DisableInputWhenOutOfFocus.Value = shouldLoadFromFile ? cff.DisableInputWhenOutOfFocus: Hid.DisableInputWhenOutOfFocus.Value; // Get from global config only Hid.DisableInputWhenOutOfFocus.Value = shouldLoadFromFile ? cff.DisableInputWhenOutOfFocus: Hid.DisableInputWhenOutOfFocus.Value; // Get from global config only
Hid.Hotkeys.Value = shouldLoadFromFile ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only Hid.Hotkeys.Value = shouldLoadFromFile ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only
Hid.InputConfig.Value = cff.InputConfig ?? []; Hid.InputConfig.Value = cff.InputConfig ?? [];
@@ -449,6 +449,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> EnableMouse { get; private set; } public ReactiveObject<bool> EnableMouse { get; private set; }
/// <summary>
/// Enable or disable auto-assigning controllers to players
/// </summary>
public ReactiveObject<bool> EnableAutoAssign { get; private set; }
/// <summary> /// <summary>
/// Enable/disable the ability to control Ryujinx when it's not the currently focused window. /// Enable/disable the ability to control Ryujinx when it's not the currently focused window.
/// </summary> /// </summary>
@@ -475,6 +480,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
{ {
EnableKeyboard = new ReactiveObject<bool>(); EnableKeyboard = new ReactiveObject<bool>();
EnableMouse = new ReactiveObject<bool>(); EnableMouse = new ReactiveObject<bool>();
EnableAutoAssign = new ReactiveObject<bool>();
DisableInputWhenOutOfFocus = new ReactiveObject<bool>(); DisableInputWhenOutOfFocus = new ReactiveObject<bool>();
Hotkeys = new ReactiveObject<KeyboardHotkeys>(); Hotkeys = new ReactiveObject<KeyboardHotkeys>();
InputConfig = new ReactiveObject<List<InputConfig>>(); InputConfig = new ReactiveObject<List<InputConfig>>();
@@ -133,6 +133,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ShowConsole = UI.ShowConsole, ShowConsole = UI.ShowConsole,
EnableKeyboard = Hid.EnableKeyboard, EnableKeyboard = Hid.EnableKeyboard,
EnableMouse = Hid.EnableMouse, EnableMouse = Hid.EnableMouse,
EnableAutoAssign = Hid.EnableAutoAssign,
DisableInputWhenOutOfFocus = Hid.DisableInputWhenOutOfFocus, DisableInputWhenOutOfFocus = Hid.DisableInputWhenOutOfFocus,
Hotkeys = Hid.Hotkeys, Hotkeys = Hid.Hotkeys,
InputConfig = Hid.InputConfig, InputConfig = Hid.InputConfig,
@@ -249,6 +250,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
UI.WindowStartup.WindowMaximized.Value = false; UI.WindowStartup.WindowMaximized.Value = false;
Hid.EnableKeyboard.Value = false; Hid.EnableKeyboard.Value = false;
Hid.EnableMouse.Value = false; Hid.EnableMouse.Value = false;
Hid.EnableAutoAssign.Value = false;
Hid.DisableInputWhenOutOfFocus.Value = false; Hid.DisableInputWhenOutOfFocus.Value = false;
Hid.Hotkeys.Value = new KeyboardHotkeys Hid.Hotkeys.Value = new KeyboardHotkeys
{ {