Improved interaction with "Input" settings. #708

Open
Goodfeat wants to merge 27 commits from Goodfeat/Master_PR into master
3 changed files with 113 additions and 149 deletions
Showing only changes of commit f2329d0e8a - Show all commits

View File

@ -88,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsKeyboard => !IsController; public bool IsKeyboard => !IsController;
public bool IsRight { get; set; } public bool IsRight { get; set; }
public bool IsLeft { get; set; } public bool IsLeft { get; set; }
public int DeviceIndexBeforeChange { get; set; } public string RevertDeviceId { get; set; }
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led); public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense"); public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
@ -116,7 +116,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
// When you select a profile, the settings from the profile will be applied. // When you select a profile, the settings from the profile will be applied.
// To save the settings, you still need to click the apply button // To save the settings, you still need to click the apply button
_profileChoose = value; _profileChoose = value;
LoadProfile(); LoadProfile();
OnPropertyChanged(); OnPropertyChanged();
@ -167,7 +166,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
LoadDevice(); LoadDevice();
LoadProfiles(); LoadProfiles();
DeviceIndexBeforeChange = Device; RevertDeviceId = Devices[Device].Id;
_isLoaded = true; _isLoaded = true;
_isChangeTrackingActive = true; _isChangeTrackingActive = true;
OnPropertyChanged(); OnPropertyChanged();
@ -179,6 +178,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
get => _controller; get => _controller;
set set
{ {
MarkAsChanged();
_controller = value; _controller = value;
if (_controller == -1) if (_controller == -1)
@ -216,7 +217,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
LoadInputDriver(); LoadInputDriver();
LoadProfiles(); LoadProfiles();
SetChangeTrackingActive();
} }
OnPropertyChanged(); OnPropertyChanged();
@ -258,10 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
get => _device; get => _device;
set set
{ {
if (!IsModified) MarkAsChanged();
{
DeviceIndexBeforeChange = _device;
}
_device = value < 0 ? 0 : value; _device = value < 0 ? 0 : value;
@ -282,8 +279,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
FindPairedDevice(); FindPairedDeviceInConfigFile();
SetChangeTrackingActive();
OnPropertyChanged(); OnPropertyChanged();
NotifyChanges(); NotifyChanges();
} }
@ -363,50 +359,35 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick); ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
} }
FindPairedDevice(); FindPairedDeviceInConfigFile();
} }
private void FindPairedDevice() private void FindPairedDeviceInConfigFile()
{ {
// This feature allows you to display a notification // This function allows you to output a message about the device configuration found in the file
// if a configuration is found, but the gamepad is not connected. // NOTE: if the configuration is found, we display the message "Waiting for controller connection",
if (Config != null) // but only if the id gamepad belongs to the selected player
{ NotificationView = Config != null && Devices.FirstOrDefault(d => d.Id == Config.Id).Id != Config.Id && Config.PlayerIndex == PlayerId;
(DeviceType Type, string Id, string Name) activeDevice = Devices.FirstOrDefault(d => d.Id == Config.Id);
if (activeDevice.Id != Config.Id)
{
// display notification when input device is turned off, and
// if device and configuration do not match (different controllers)
NotificationView = true;
}
else
{
NotificationView = false;
}
}
else
{
NotificationView = false;
}
} }
private void SetChangeTrackingActive()
private void MarkAsChanged()
{ {
//If tracking is active, then allow changing the modifier
if (_isChangeTrackingActive) if (!IsModified && _isChangeTrackingActive)
{ {
RevertDeviceId = Devices[Device].Id; // Remember the device to undo changes
IsModified = true; IsModified = true;
} }
} }
public void DisableDeviceForSaving() public void UnlinkDevice()
{ {
// "Disabled" mode is available after unbinding the device // "Disabled" mode is available after unbinding the device
// NOTE: the IsModified flag to be able to apply the settings. // NOTE: the IsModified flag to be able to apply the settings.
IsModified = true;
NotificationView = false; NotificationView = false;
IsModified = true;
} }
@ -477,34 +458,31 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private async void HandleOnGamepadDisconnected(string id) private async void HandleOnGamepadDisconnected(string id)
{ {
_isChangeTrackingActive = false; _isChangeTrackingActive = false; // Disable configuration change tracking
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
LoadDevices(); LoadDevices();
IsModified = true; IsModified = true;
LoadSavedConfiguration(); RevertChanges();
FindPairedDevice(); FindPairedDeviceInConfigFile();
_isChangeTrackingActive = true; _isChangeTrackingActive = true; // Enable configuration change tracking
return System.Threading.Tasks.Task.CompletedTask; return System.Threading.Tasks.Task.CompletedTask;
}); });
} }
private async void HandleOnGamepadConnected(string id) private async void HandleOnGamepadConnected(string id)
{ {
_isChangeTrackingActive = false; _isChangeTrackingActive = false; // Disable configuration change tracking
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
{ {
LoadDevices(); LoadDevices();
if (Config != null) IsModified = true;
{ RevertChanges();
// Load configuration after connection if it is in the configuration file
IsModified = true; _isChangeTrackingActive = true;// Enable configuration change tracking
LoadSavedConfiguration();
}
_isChangeTrackingActive = true;
}); });
} }
@ -809,9 +787,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
public void LoadProfileButton() public void LoadProfileButton()
{ {
IsModified = true;
LoadProfile(); LoadProfile();
IsModified = true;
} }
public async void LoadProfile() public async void LoadProfile()
@ -865,12 +843,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
_isLoaded = false; _isLoaded = false;
config.Id = null; // ignore device IDs (there is no longer a need to store device IDs for presets due to their independence from devices) config.Id = Config.Id; // Set current device id instead of changing device(independent profiles)
LoadConfiguration(config); LoadConfiguration(config);
// This line of code hard-links profiles to controllers, the commented line allows profiles to be applied to all controllers //LoadDevice(); This line of code hard-links profiles to controllers, the commented line allows profiles to be applied to all controllers
// LoadDevice();
_isLoaded = true; _isLoaded = true;
@ -880,56 +857,58 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public async void SaveProfile() public async void SaveProfile()
{ {
if (Device == 0)
{ if (Device == 0)
return; {
} return;
}
if (ConfigViewModel == null)
{
return;
}
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
if (ConfigViewModel == null) return;
{ }
return; else
} {
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault]) if (validFileName)
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]); string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
return; InputConfig config = null;
}
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (validFileName) if (IsKeyboard)
{ {
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json"); config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
}
else if (IsController)
{
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
}
InputConfig config = null; config.ControllerType = Controllers[_controller].Type;
if (IsKeyboard) string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
{
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
}
else if (IsController)
{
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
}
config.ControllerType = Controllers[_controller].Type; await File.WriteAllTextAsync(path, jsonString);
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig); LoadProfiles();
await File.WriteAllTextAsync(path, jsonString); ProfileChoose = ProfileName; // Show new profile
}
LoadProfiles(); else
{
ProfileChoose = ProfileName; // Show new profile await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
} }
else }
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
}
}
} }
public async void RemoveProfile() public async void RemoveProfile()
@ -961,22 +940,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
public void LoadSavedConfiguration() public void RevertChanges()
{ {
// Restores settings and sets the previously selected device to the last saved state Device = Devices.ToList().FindIndex(d => d.Id == RevertDeviceId);
// NOTE: The current order allows the configuration and device to be loaded correctly until the configuration is changed.
if (IsModified) // Fixes random gamepad appearance in "disabled" option
{
Device = DeviceIndexBeforeChange;
LoadDevice(); LoadDevice();
LoadConfiguration(); LoadConfiguration();
IsModified = false;
OnPropertyChanged(); OnPropertyChanged();
} IsModified = false;
} }
public void Save() public void Save()
@ -988,7 +958,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
IsModified = false; IsModified = false;
DeviceIndexBeforeChange = Device;
RevertDeviceId = Devices[Device].Id; // Remember selected device after saving
List <InputConfig> newConfig = []; List <InputConfig> newConfig = [];
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value); newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);

View File

@ -75,7 +75,7 @@
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="{ext:Locale ControllerSettingsCancelCurrentChangesToolTip}" ToolTip.Tip="{ext:Locale ControllerSettingsCancelCurrentChangesToolTip}"
Command="{Binding LoadSavedConfiguration}"> Command="{Binding RevertChanges}">
<ui:SymbolIcon <ui:SymbolIcon
Symbol="Undo" Symbol="Undo"
FontSize="15" FontSize="15"
@ -206,15 +206,14 @@
<StackPanel> <StackPanel>
<TextBlock <TextBlock
Margin="5,20,0,0" Margin="5,20,0,0"
Text="{ext:Locale ControllerSettingsDisableDeviceForSaving}" /> Text="{ext:Locale ControllerSettingsDisableDeviceForSaving}" />
<Button <Button
MinWidth="0" MinWidth="0"
Width="90" Width="90"
Height="27" Height="27"
Margin="5,10,0,0" Margin="5,10,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{Binding DisableDeviceForSaving}"> Command="{Binding UnlinkDevice}">
<TextBlock <TextBlock
Text="{ext:Locale ControllerSettingsUnlink}" Text="{ext:Locale ControllerSettingsUnlink}"
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@ -184,11 +184,6 @@ namespace Ryujinx.Ava.UI.Views.Input
} }
} }
private void FlagInputConfigChanged()
{
(DataContext as KeyboardInputViewModel)!.ParentModel.IsModified = true;
}
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
@ -207,47 +202,45 @@ namespace Ryujinx.Ava.UI.Views.Input
private void DeleteBind() private void DeleteBind()
{ {
if (DataContext is not KeyboardInputViewModel viewModel)
return;
if (_currentAssigner != null) if (_currentAssigner != null)
{ {
Dictionary<string, Action> buttonActions = new Dictionary<string, Action> Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
{ {
{ "ButtonZl", () => viewModel.Config.ButtonZl = Key.Unbound }, { "ButtonZl", () => ViewModel.Config.ButtonZl = Key.Unbound },
{ "ButtonL", () => viewModel.Config.ButtonL = Key.Unbound }, { "ButtonL", () => ViewModel.Config.ButtonL = Key.Unbound },
{ "ButtonMinus", () => viewModel.Config.ButtonMinus = Key.Unbound }, { "ButtonMinus", () => ViewModel.Config.ButtonMinus = Key.Unbound },
{ "LeftStickButton", () => viewModel.Config.LeftStickButton = Key.Unbound }, { "LeftStickButton", () => ViewModel.Config.LeftStickButton = Key.Unbound },
{ "LeftStickUp", () => viewModel.Config.LeftStickUp = Key.Unbound }, { "LeftStickUp", () => ViewModel.Config.LeftStickUp = Key.Unbound },
{ "LeftStickDown", () => viewModel.Config.LeftStickDown = Key.Unbound }, { "LeftStickDown", () => ViewModel.Config.LeftStickDown = Key.Unbound },
{ "LeftStickRight", () => viewModel.Config.LeftStickRight = Key.Unbound }, { "LeftStickRight", () => ViewModel.Config.LeftStickRight = Key.Unbound },
{ "LeftStickLeft", () => viewModel.Config.LeftStickLeft = Key.Unbound }, { "LeftStickLeft", () => ViewModel.Config.LeftStickLeft = Key.Unbound },
{ "DpadUp", () => viewModel.Config.DpadUp = Key.Unbound }, { "DpadUp", () => ViewModel.Config.DpadUp = Key.Unbound },
{ "DpadDown", () => viewModel.Config.DpadDown = Key.Unbound }, { "DpadDown", () => ViewModel.Config.DpadDown = Key.Unbound },
{ "DpadLeft", () => viewModel.Config.DpadLeft = Key.Unbound }, { "DpadLeft", () => ViewModel.Config.DpadLeft = Key.Unbound },
{ "DpadRight", () => viewModel.Config.DpadRight = Key.Unbound }, { "DpadRight", () => ViewModel.Config.DpadRight = Key.Unbound },
{ "LeftButtonSr", () => viewModel.Config.LeftButtonSr = Key.Unbound }, { "LeftButtonSr", () => ViewModel.Config.LeftButtonSr = Key.Unbound },
{ "LeftButtonSl", () => viewModel.Config.LeftButtonSl = Key.Unbound }, { "LeftButtonSl", () => ViewModel.Config.LeftButtonSl = Key.Unbound },
{ "RightButtonSr", () => viewModel.Config.RightButtonSr = Key.Unbound }, { "RightButtonSr", () => ViewModel.Config.RightButtonSr = Key.Unbound },
{ "RightButtonSl", () => viewModel.Config.RightButtonSl = Key.Unbound }, { "RightButtonSl", () => ViewModel.Config.RightButtonSl = Key.Unbound },
{ "ButtonZr", () => viewModel.Config.ButtonZr = Key.Unbound }, { "ButtonZr", () => ViewModel.Config.ButtonZr = Key.Unbound },
{ "ButtonR", () => viewModel.Config.ButtonR = Key.Unbound }, { "ButtonR", () => ViewModel.Config.ButtonR = Key.Unbound },
{ "ButtonPlus", () => viewModel.Config.ButtonPlus = Key.Unbound }, { "ButtonPlus", () => ViewModel.Config.ButtonPlus = Key.Unbound },
{ "ButtonA", () => viewModel.Config.ButtonA = Key.Unbound }, { "ButtonA", () => ViewModel.Config.ButtonA = Key.Unbound },
{ "ButtonB", () => viewModel.Config.ButtonB = Key.Unbound }, { "ButtonB", () => ViewModel.Config.ButtonB = Key.Unbound },
{ "ButtonX", () => viewModel.Config.ButtonX = Key.Unbound }, { "ButtonX", () => ViewModel.Config.ButtonX = Key.Unbound },
{ "ButtonY", () => viewModel.Config.ButtonY = Key.Unbound }, { "ButtonY", () => ViewModel.Config.ButtonY = Key.Unbound },
{ "RightStickButton", () => viewModel.Config.RightStickButton = Key.Unbound }, { "RightStickButton", () => ViewModel.Config.RightStickButton = Key.Unbound },
{ "RightStickUp", () => viewModel.Config.RightStickUp = Key.Unbound }, { "RightStickUp", () => ViewModel.Config.RightStickUp = Key.Unbound },
{ "RightStickDown", () => viewModel.Config.RightStickDown = Key.Unbound }, { "RightStickDown", () => ViewModel.Config.RightStickDown = Key.Unbound },
{ "RightStickRight", () => viewModel.Config.RightStickRight = Key.Unbound }, { "RightStickRight", () => ViewModel.Config.RightStickRight = Key.Unbound },
{ "RightStickLeft", () => viewModel.Config.RightStickLeft = Key.Unbound } { "RightStickLeft", () => ViewModel.Config.RightStickLeft = Key.Unbound }
}; };
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action)) if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
{ {
action(); action();
FlagInputConfigChanged(); ViewModel.ParentModel.IsModified = true;
} }
} }
} }