From ab7ebecfc851ba37d06ae861c4118c1ef8a697ed Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Fri, 22 Sep 2023 20:55:39 +0000 Subject: [PATCH] use compiled binding for controller view --- src/Ryujinx.Ava/Assets/Styles/Styles.xaml | 6 +- .../UI/Models/InputConfiguration.cs | 2 +- .../UI/ViewModels/ControllerInputViewModel.cs | 90 +- .../UI/ViewModels/GamePadInputViewModel.cs | 63 ++ .../UI/ViewModels/InputViewModel.cs | 64 ++ .../UI/ViewModels/KeyboardInputViewModel.cs | 47 + .../UI/Views/Input/ControllerInputView.axaml | 955 +----------------- .../Views/Input/ControllerInputView.axaml.cs | 24 +- .../UI/Views/Input/GamePadInputView.axaml | 741 ++++++++++++++ .../UI/Views/Input/GamePadInputView.axaml.cs | 14 + .../UI/Views/Input/KeyboardInputView.axaml | 672 ++++++++++++ .../UI/Views/Input/KeyboardInputView.axaml.cs | 14 + .../UI/Views/Input/MotionInputView.axaml.cs | 4 +- .../UI/Views/Input/RumbleInputView.axaml.cs | 4 +- 14 files changed, 1716 insertions(+), 984 deletions(-) create mode 100644 src/Ryujinx.Ava/UI/ViewModels/GamePadInputViewModel.cs create mode 100644 src/Ryujinx.Ava/UI/ViewModels/InputViewModel.cs create mode 100644 src/Ryujinx.Ava/UI/ViewModels/KeyboardInputViewModel.cs create mode 100644 src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml create mode 100644 src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml.cs create mode 100644 src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml create mode 100644 src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs diff --git a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml index f7f64be22..5e6ab6faf 100644 --- a/src/Ryujinx.Ava/Assets/Styles/Styles.xaml +++ b/src/Ryujinx.Ava/Assets/Styles/Styles.xaml @@ -14,10 +14,6 @@ - - - @@ -393,4 +389,4 @@ 600 756 - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs index f1352c6d8..a7810124c 100644 --- a/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs +++ b/src/Ryujinx.Ava/UI/Models/InputConfiguration.cs @@ -7,7 +7,7 @@ using System; namespace Ryujinx.Ava.UI.Models { - internal class InputConfiguration : BaseModel + public class InputConfiguration : BaseModel { private float _deadzoneRight; private float _triggerThreshold; diff --git a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs index c0c625321..0177c7f96 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/ControllerInputViewModel.cs @@ -48,10 +48,11 @@ namespace Ryujinx.Ava.UI.ViewModels private int _controllerNumber; private string _controllerImage; private int _device; - private object _configuration; + private InputViewModel _configuration; private string _profileName; private bool _isLoaded; - + private bool _isLeft; + private bool _isRight; private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public IGamepadDriver AvaloniaKeyboardDriver { get; } @@ -63,23 +64,47 @@ namespace Ryujinx.Ava.UI.ViewModels public AvaloniaList ProfilesList { get; set; } public AvaloniaList DeviceList { get; set; } + public event EventHandler ConfigurationChanged; + // XAML Flags public bool ShowSettings => _device > 0; public bool IsController => _device > 1; public bool IsKeyboard => !IsController; - public bool IsRight { get; set; } - public bool IsLeft { get; set; } + public bool IsRight + { + get => _isRight; set + { + _isRight = value; + + _configuration.IsRight = IsRight; + } + } + public bool IsLeft + { + get => _isLeft; set + { + _isLeft = value; + + _configuration.IsLeft = IsLeft; + } + } public bool IsModified { get; set; } - public object Configuration + public InputViewModel Configuration { get => _configuration; set { _configuration = value; + _configuration.IsLeft = IsLeft; + _configuration.IsRight = IsRight; + _configuration.ControllerImage = _controllerImage; + OnPropertyChanged(); + + ConfigurationChanged?.Invoke(this, EventArgs.Empty); } } @@ -167,27 +192,9 @@ namespace Ryujinx.Ava.UI.ViewModels { _controllerImage = value; + if (_configuration != null) + _configuration.ControllerImage = value; OnPropertyChanged(); - OnPropertyChanged(nameof(Image)); - } - } - - public SvgImage Image - { - get - { - SvgImage image = new(); - - if (!string.IsNullOrWhiteSpace(_controllerImage)) - { - SvgSource source = new(); - - source.Load(EmbeddedResources.GetStream(_controllerImage)); - - image.Source = source; - } - - return image; } } @@ -282,12 +289,12 @@ namespace Ryujinx.Ava.UI.ViewModels if (Config is StandardKeyboardInputConfig keyboardInputConfig) { - Configuration = new InputConfiguration(keyboardInputConfig); + Configuration = new KeyboardInputViewModel(new InputConfiguration(keyboardInputConfig)); } if (Config is StandardControllerInputConfig controllerInputConfig) { - Configuration = new InputConfiguration(controllerInputConfig); + Configuration = new GamePadInputViewModel(new InputConfiguration(controllerInputConfig), async () => { await MotionInputView.Show(this); }, async () => { await RumbleInputView.Show(this); }); } } @@ -323,16 +330,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public async void ShowMotionConfig() - { - await MotionInputView.Show(this); - } - - public async void ShowRumbleConfig() - { - await RumbleInputView.Show(this); - } - private void LoadInputDriver() { if (_device < 0) @@ -760,14 +757,7 @@ namespace Ryujinx.Ava.UI.ViewModels InputConfig config = null; - if (IsKeyboard) - { - config = (Configuration as InputConfiguration).GetConfig(); - } - else if (IsController) - { - config = (Configuration as InputConfiguration).GetConfig(); - } + config = Configuration.GetConfig(); config.ControllerType = Controllers[_controller].Type; @@ -830,18 +820,16 @@ namespace Ryujinx.Ava.UI.ViewModels if (device.Type == DeviceType.Keyboard) { - var inputConfig = Configuration as InputConfiguration; + var inputConfig = Configuration.Config as InputConfiguration; inputConfig.Id = device.Id; } else { - var inputConfig = Configuration as InputConfiguration; + var inputConfig = Configuration.Config as InputConfiguration; inputConfig.Id = device.Id.Split(" ")[0]; } - var config = !IsController - ? (Configuration as InputConfiguration).GetConfig() - : (Configuration as InputConfiguration).GetConfig(); + var config = Configuration.GetConfig(); config.ControllerType = Controllers[_controller].Type; config.PlayerIndex = _playerId; @@ -878,6 +866,8 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(nameof(IsKeyboard)); OnPropertyChanged(nameof(IsRight)); OnPropertyChanged(nameof(IsLeft)); + + Configuration?.NotifyChanges(); } public void Dispose() diff --git a/src/Ryujinx.Ava/UI/ViewModels/GamePadInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/GamePadInputViewModel.cs new file mode 100644 index 000000000..c8d98d24d --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/GamePadInputViewModel.cs @@ -0,0 +1,63 @@ +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.Views.Input; +using Ryujinx.Common.Configuration.Hid; +using System; +using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; +using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public class GamePadInputViewModel : InputViewModel + { + private InputConfiguration _configuration; + private Func _showMotionConfigCommand; + private Func _showRumbleConfigCommand; + + + public InputConfiguration Configuration + { + get => _configuration; + set + { + _configuration = value; + + OnPropertyChanged(); + } + } + + internal override object Config => _configuration; + + public GamePadInputViewModel(InputConfiguration configuration, Func showMotionConfigCommand, Func showRumbleConfigCommand) + { + Configuration = configuration; + _showMotionConfigCommand = showMotionConfigCommand; + _showRumbleConfigCommand = showRumbleConfigCommand; + } + + public GamePadInputViewModel() + { + } + + public override void NotifyChanges() + { + OnPropertyChanged(nameof(Configuration)); + + base.NotifyChanges(); + } + + public override InputConfig GetConfig() + { + return _configuration.GetConfig(); + } + + public async void ShowMotionConfig() + { + await _showMotionConfigCommand(); + } + + public async void ShowRumbleConfig() + { + await _showRumbleConfigCommand(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/InputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/InputViewModel.cs new file mode 100644 index 000000000..2021746cf --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/InputViewModel.cs @@ -0,0 +1,64 @@ +using Avalonia.Svg.Skia; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common; +using Ryujinx.Common.Configuration.Hid; +using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; +using Key = Ryujinx.Common.Configuration.Hid.Key; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public abstract class InputViewModel : BaseModel + { + private string _controllerImage; + + public bool IsRight { get; set; } + public bool IsLeft { get; set; } + + internal abstract object Config { get; } + + public void NotifyChange(string property) + { + OnPropertyChanged(property); + } + + public string ControllerImage + { + get => _controllerImage; + set + { + _controllerImage = value; + + OnPropertyChanged(); + OnPropertyChanged(nameof(Image)); + } + } + + public SvgImage Image + { + get + { + SvgImage image = new(); + + if (!string.IsNullOrWhiteSpace(_controllerImage)) + { + SvgSource source = new(); + + source.Load(EmbeddedResources.GetStream(_controllerImage)); + + image.Source = source; + } + + return image; + } + } + + public virtual void NotifyChanges() + { + OnPropertyChanged(nameof(IsRight)); + OnPropertyChanged(nameof(IsLeft)); + OnPropertyChanged(nameof(Image)); + } + + public abstract InputConfig GetConfig(); + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/KeyboardInputViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/KeyboardInputViewModel.cs new file mode 100644 index 000000000..b918e6732 --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/KeyboardInputViewModel.cs @@ -0,0 +1,47 @@ +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common.Configuration.Hid; +using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; +using Key = Ryujinx.Common.Configuration.Hid.Key; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public class KeyboardInputViewModel : InputViewModel + { + private InputConfiguration _configuration; + + + public InputConfiguration Configuration + { + get => _configuration; + set + { + _configuration = value; + + OnPropertyChanged(); + } + } + + internal override object Config => _configuration; + + public KeyboardInputViewModel(InputConfiguration configuration) + { + Configuration = configuration; + } + + public KeyboardInputViewModel() + { + } + + public override void NotifyChanges() + { + OnPropertyChanged(nameof(Configuration)); + + base.NotifyChanges(); + } + + public override InputConfig GetConfig() + { + return _configuration.GetConfig(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml index d636873a3..6813d4c88 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml @@ -8,6 +8,7 @@ xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls" xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input;assembly=Ryujinx.Ava" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" @@ -25,9 +26,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs index 351297060..ab49deccc 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/ControllerInputView.axaml.cs @@ -1,8 +1,10 @@ +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.VisualTree; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; @@ -26,8 +28,19 @@ namespace Ryujinx.Ava.UI.Views.Input DataContext = ViewModel = new ControllerInputViewModel(this); InitializeComponent(); + } - foreach (ILogical visual in SettingButtons.GetLogicalDescendants()) + private void SettingButtons_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) + { + if(e.Property == ContentProperty) + { + RebindEvents(); + } + } + + public void RebindEvents() + { + foreach (var visual in SettingButtons.GetLogicalDescendants()) { if (visual is ToggleButton button && visual is not CheckBox) { @@ -36,6 +49,15 @@ namespace Ryujinx.Ava.UI.Views.Input } } + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + + RebindEvents(); + + SettingButtons.PropertyChanged += SettingButtons_PropertyChanged; + } + protected override void OnPointerReleased(PointerReleasedEventArgs e) { base.OnPointerReleased(e); diff --git a/src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml new file mode 100644 index 000000000..db1e588e6 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml @@ -0,0 +1,741 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml.cs new file mode 100644 index 000000000..cf1037f5d --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/GamePadInputView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ryujinx.Ava.UI.Views.Input +{ + public partial class GamePadInputView : UserControl + { + public GamePadInputView() + { + InitializeComponent(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml new file mode 100644 index 000000000..bcc31873f --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml @@ -0,0 +1,672 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs new file mode 100644 index 000000000..9e1470589 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Views/Input/KeyboardInputView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ryujinx.Ava.UI.Views.Input +{ + public partial class KeyboardInputView : UserControl + { + public KeyboardInputView() + { + InitializeComponent(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs index 1b340752b..c2f55812f 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/MotionInputView.axaml.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input public MotionInputView(ControllerInputViewModel viewModel) { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Configuration.Config as InputConfiguration; _viewModel = new MotionInputViewModel { @@ -51,7 +51,7 @@ namespace Ryujinx.Ava.UI.Views.Input }; contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Configuration.Config as InputConfiguration; config.Slot = content._viewModel.Slot; config.Sensitivity = content._viewModel.Sensitivity; config.GyroDeadzone = content._viewModel.GyroDeadzone; diff --git a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs index 9307f872c..94384f828 100644 --- a/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input public RumbleInputView(ControllerInputViewModel viewModel) { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Configuration.Config as InputConfiguration; _viewModel = new RumbleInputViewModel { @@ -47,7 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Input contentDialog.PrimaryButtonClick += (sender, args) => { - var config = viewModel.Configuration as InputConfiguration; + var config = viewModel.Configuration.Config as InputConfiguration; config.StrongRumble = content._viewModel.StrongRumble; config.WeakRumble = content._viewModel.WeakRumble; };