From da3f4e1d3a2929efdc8ddc64729ab75a84134c33 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Sun, 2 Mar 2025 21:24:39 -0600 Subject: [PATCH] misc: Created generic type RyujinxControl to allow for more unified control view model definitions --- .../UI/Applet/ProfileSelectorDialog.axaml.cs | 5 +- .../Controls/ApplicationContextMenu.axaml.cs | 1 + .../UI/Controls/ApplicationDataView.axaml.cs | 4 +- .../UI/Controls/ApplicationGridView.axaml.cs | 3 +- .../UI/Controls/ApplicationListView.axaml.cs | 10 +-- .../UI/Controls/DlcSelectView.axaml.cs | 4 +- .../UI/Controls/NavigationDialogHost.axaml.cs | 3 +- src/Ryujinx/UI/Controls/RyujinxControl.cs | 18 +++++ .../UserFirmwareAvatarSelectorViewModel.cs | 2 +- .../UserProfileImageSelectorViewModel.cs | 2 +- .../Views/Input/ControllerInputView.axaml.cs | 19 ++--- src/Ryujinx/UI/Views/Input/InputView.axaml.cs | 6 +- .../UI/Views/Input/KeyboardInputView.axaml.cs | 74 +++++++++---------- .../UI/Views/Input/LedInputView.axaml.cs | 31 ++++---- .../UI/Views/Input/MotionInputView.axaml.cs | 26 +++---- .../UI/Views/Input/RumbleInputView.axaml.cs | 15 ++-- .../UI/Views/Main/MainMenuBarView.axaml.cs | 4 +- .../UI/Views/Main/MainStatusBarView.axaml.cs | 6 +- .../UI/Views/Main/MainViewControls.axaml.cs | 7 +- .../UI/Views/User/UserEditorView.axaml.cs | 27 +++---- .../UserFirmwareAvatarSelectorView.axaml.cs | 7 +- .../UserProfileImageSelectorView.axaml.cs | 4 +- .../UI/Views/User/UserRecovererView.axaml.cs | 3 +- .../Views/User/UserSaveManagerView.axaml.cs | 6 +- .../UI/Views/User/UserSelectorView.axaml.cs | 4 +- 25 files changed, 136 insertions(+), 155 deletions(-) create mode 100644 src/Ryujinx/UI/Controls/RyujinxControl.cs diff --git a/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml.cs b/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml.cs index b2c396b69..babd8e5ee 100644 --- a/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml.cs +++ b/src/Ryujinx/UI/Applet/ProfileSelectorDialog.axaml.cs @@ -9,17 +9,14 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Account.Acc; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Threading.Tasks; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; using UserProfileSft = Ryujinx.HLE.HOS.Services.Account.Acc.UserProfile; namespace Ryujinx.Ava.UI.Applet { - public partial class ProfileSelectorDialog : UserControl + public partial class ProfileSelectorDialog : RyujinxControl { - public ProfileSelectorDialogViewModel ViewModel { get; set; } - public ProfileSelectorDialog(ProfileSelectorDialogViewModel viewModel) { DataContext = ViewModel = viewModel; diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs index b2beb23e0..897f7325c 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Ava.UI.Controls { public class ApplicationContextMenu : MenuFlyout { + public ApplicationContextMenu() { InitializeComponent(); diff --git a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs index 57dba3228..af18482d5 100644 --- a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationDataView.axaml.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Controls { - public partial class ApplicationDataView : UserControl + public partial class ApplicationDataView : RyujinxControl { public static async Task Show(ApplicationData appData) { @@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Controls SecondaryButtonText = string.Empty, CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose], MinWidth = 256, - Content = new ApplicationDataView { DataContext = new ApplicationDataViewModel(appData) } + Content = new ApplicationDataView { ViewModel = new ApplicationDataViewModel(appData) } }; Style closeButton = new(x => x.Name("CloseButton")); diff --git a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs index 22aefc3b9..59cbd0a9b 100644 --- a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationGridView.axaml.cs @@ -2,12 +2,13 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.Utilities.AppLibrary; using System; namespace Ryujinx.Ava.UI.Controls { - public partial class ApplicationGridView : UserControl + public partial class ApplicationGridView : RyujinxControl { public static readonly RoutedEvent ApplicationOpenedEvent = RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); diff --git a/src/Ryujinx/UI/Controls/ApplicationListView.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationListView.axaml.cs index fb38fdb72..20a0cae7c 100644 --- a/src/Ryujinx/UI/Controls/ApplicationListView.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationListView.axaml.cs @@ -11,7 +11,7 @@ using System.Linq; namespace Ryujinx.Ava.UI.Controls { - public partial class ApplicationListView : UserControl + public partial class ApplicationListView : RyujinxControl { public static readonly RoutedEvent ApplicationOpenedEvent = RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); @@ -32,9 +32,6 @@ namespace Ryujinx.Ava.UI.Controls private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e) { - if (DataContext is not MainWindowViewModel mwvm) - return; - if (sender is not Button { Content: TextBlock playabilityLabel }) return; @@ -43,16 +40,13 @@ namespace Ryujinx.Ava.UI.Controls private async void IdString_OnClick(object sender, RoutedEventArgs e) { - if (DataContext is not MainWindowViewModel mwvm) - return; - if (sender is not Button { Content: TextBlock idText }) return; if (!RyujinxApp.IsClipboardAvailable(out IClipboard clipboard)) return; - ApplicationData appData = mwvm.Applications.FirstOrDefault(it => it.IdString == idText.Text); + ApplicationData appData = ViewModel.Applications.FirstOrDefault(it => it.IdString == idText.Text); if (appData is null) return; diff --git a/src/Ryujinx/UI/Controls/DlcSelectView.axaml.cs b/src/Ryujinx/UI/Controls/DlcSelectView.axaml.cs index 939acc0a3..1fc06d950 100644 --- a/src/Ryujinx/UI/Controls/DlcSelectView.axaml.cs +++ b/src/Ryujinx/UI/Controls/DlcSelectView.axaml.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Controls { - public partial class DlcSelectView : UserControl + public partial class DlcSelectView : RyujinxControl { public DlcSelectView() { @@ -28,7 +28,7 @@ namespace Ryujinx.Ava.UI.Controls PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue], SecondaryButtonText = string.Empty, CloseButtonText = string.Empty, - Content = new DlcSelectView { DataContext = viewModel } + Content = new DlcSelectView { ViewModel = viewModel } }; Style closeButton = new(x => x.Name("CloseButton")); diff --git a/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs b/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs index fdfc588e2..12c6a9daf 100644 --- a/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs +++ b/src/Ryujinx/UI/Controls/NavigationDialogHost.axaml.cs @@ -23,13 +23,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Controls { - public partial class NavigationDialogHost : UserControl + public partial class NavigationDialogHost : RyujinxControl { public AccountManager AccountManager { get; } public ContentManager ContentManager { get; } public VirtualFileSystem VirtualFileSystem { get; } public HorizonClient HorizonClient { get; } - public UserProfileViewModel ViewModel { get; set; } public NavigationDialogHost() { diff --git a/src/Ryujinx/UI/Controls/RyujinxControl.cs b/src/Ryujinx/UI/Controls/RyujinxControl.cs new file mode 100644 index 000000000..5e220faa6 --- /dev/null +++ b/src/Ryujinx/UI/Controls/RyujinxControl.cs @@ -0,0 +1,18 @@ +using Avalonia.Controls; +using Gommon; +using Ryujinx.Ava.UI.ViewModels; +using System; + +namespace Ryujinx.Ava.UI.Controls +{ + public class RyujinxControl : 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; + } + } +} diff --git a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index cc5b35dc6..6b4d7795d 100644 --- a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -21,7 +21,7 @@ using Image = SkiaSharp.SKImage; namespace Ryujinx.Ava.UI.ViewModels { - internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel + public partial class UserFirmwareAvatarSelectorViewModel : BaseModel { private static readonly Dictionary _avatarStore = new(); diff --git a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs index 36a9a62f9..f9f9ca2f5 100644 --- a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs @@ -2,7 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace Ryujinx.Ava.UI.ViewModels { - internal partial class UserProfileImageSelectorViewModel : BaseModel + public partial class UserProfileImageSelectorViewModel : BaseModel { [ObservableProperty] private bool _firmwareFound; } diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs index d04085a89..aa6cda606 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Common.Configuration.Hid.Controller; @@ -14,7 +15,7 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; namespace Ryujinx.Ava.UI.Views.Input { - public partial class ControllerInputView : UserControl + public partial class ControllerInputView : RyujinxControl { private ButtonKeyAssigner _currentAssigner; @@ -217,20 +218,12 @@ namespace Ryujinx.Ava.UI.Views.Input PointerPressed -= MouseClick; } - private IButtonAssigner CreateButtonAssigner(bool forStick) - { - IButtonAssigner assigner; - - ControllerInputViewModel controllerInputViewModel = DataContext as ControllerInputViewModel; - - assigner = new GamepadButtonAssigner( - controllerInputViewModel.ParentModel.SelectedGamepad, - (controllerInputViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold, + private IButtonAssigner CreateButtonAssigner(bool forStick) => + new GamepadButtonAssigner( + ViewModel.ParentModel.SelectedGamepad, + (ViewModel.ParentModel.Config as StandardControllerInputConfig).TriggerThreshold, forStick); - return assigner; - } - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); diff --git a/src/Ryujinx/UI/Views/Input/InputView.axaml.cs b/src/Ryujinx/UI/Views/Input/InputView.axaml.cs index b1061f70d..aa9e25fbf 100644 --- a/src/Ryujinx/UI/Views/Input/InputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/InputView.axaml.cs @@ -1,19 +1,19 @@ using Avalonia.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels.Input; namespace Ryujinx.Ava.UI.Views.Input { - public partial class InputView : UserControl + public partial class InputView : RyujinxControl { private bool _dialogOpen; - private InputViewModel ViewModel { get; set; } public InputView() { - DataContext = ViewModel = new InputViewModel(this); + ViewModel = new InputViewModel(this); InitializeComponent(); } diff --git a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs index 99e424d4f..9126e8531 100644 --- a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Input; @@ -13,7 +14,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key; namespace Ryujinx.Ava.UI.Views.Input { - public partial class KeyboardInputView : UserControl + public partial class KeyboardInputView : RyujinxControl { private ButtonKeyAssigner _currentAssigner; @@ -60,106 +61,103 @@ namespace Ryujinx.Ava.UI.Views.Input PointerPressed += MouseClick; - if (DataContext is not KeyboardInputViewModel viewModel) - return; - IKeyboard keyboard = - (IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. + (IKeyboard)ViewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. IButtonAssigner assigner = - new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad); + new KeyboardKeyAssigner((IKeyboard)ViewModel.ParentModel.SelectedGamepad); - _currentAssigner.ButtonAssigned += (_, e) => + _currentAssigner.ButtonAssigned += (_, be) => { - if (e.ButtonValue.HasValue) + if (be.ButtonValue.HasValue) { - Button buttonValue = e.ButtonValue.Value; - viewModel.ParentModel.IsModified = true; + Button buttonValue = be.ButtonValue.Value; + ViewModel.ParentModel.IsModified = true; switch (button.Name) { case "ButtonZl": - viewModel.Config.ButtonZl = buttonValue.AsHidType(); + ViewModel.Config.ButtonZl = buttonValue.AsHidType(); break; case "ButtonL": - viewModel.Config.ButtonL = buttonValue.AsHidType(); + ViewModel.Config.ButtonL = buttonValue.AsHidType(); break; case "ButtonMinus": - viewModel.Config.ButtonMinus = buttonValue.AsHidType(); + ViewModel.Config.ButtonMinus = buttonValue.AsHidType(); break; case "LeftStickButton": - viewModel.Config.LeftStickButton = buttonValue.AsHidType(); + ViewModel.Config.LeftStickButton = buttonValue.AsHidType(); break; case "LeftStickUp": - viewModel.Config.LeftStickUp = buttonValue.AsHidType(); + ViewModel.Config.LeftStickUp = buttonValue.AsHidType(); break; case "LeftStickDown": - viewModel.Config.LeftStickDown = buttonValue.AsHidType(); + ViewModel.Config.LeftStickDown = buttonValue.AsHidType(); break; case "LeftStickRight": - viewModel.Config.LeftStickRight = buttonValue.AsHidType(); + ViewModel.Config.LeftStickRight = buttonValue.AsHidType(); break; case "LeftStickLeft": - viewModel.Config.LeftStickLeft = buttonValue.AsHidType(); + ViewModel.Config.LeftStickLeft = buttonValue.AsHidType(); break; case "DpadUp": - viewModel.Config.DpadUp = buttonValue.AsHidType(); + ViewModel.Config.DpadUp = buttonValue.AsHidType(); break; case "DpadDown": - viewModel.Config.DpadDown = buttonValue.AsHidType(); + ViewModel.Config.DpadDown = buttonValue.AsHidType(); break; case "DpadLeft": - viewModel.Config.DpadLeft = buttonValue.AsHidType(); + ViewModel.Config.DpadLeft = buttonValue.AsHidType(); break; case "DpadRight": - viewModel.Config.DpadRight = buttonValue.AsHidType(); + ViewModel.Config.DpadRight = buttonValue.AsHidType(); break; case "LeftButtonSr": - viewModel.Config.LeftButtonSr = buttonValue.AsHidType(); + ViewModel.Config.LeftButtonSr = buttonValue.AsHidType(); break; case "LeftButtonSl": - viewModel.Config.LeftButtonSl = buttonValue.AsHidType(); + ViewModel.Config.LeftButtonSl = buttonValue.AsHidType(); break; case "RightButtonSr": - viewModel.Config.RightButtonSr = buttonValue.AsHidType(); + ViewModel.Config.RightButtonSr = buttonValue.AsHidType(); break; case "RightButtonSl": - viewModel.Config.RightButtonSl = buttonValue.AsHidType(); + ViewModel.Config.RightButtonSl = buttonValue.AsHidType(); break; case "ButtonZr": - viewModel.Config.ButtonZr = buttonValue.AsHidType(); + ViewModel.Config.ButtonZr = buttonValue.AsHidType(); break; case "ButtonR": - viewModel.Config.ButtonR = buttonValue.AsHidType(); + ViewModel.Config.ButtonR = buttonValue.AsHidType(); break; case "ButtonPlus": - viewModel.Config.ButtonPlus = buttonValue.AsHidType(); + ViewModel.Config.ButtonPlus = buttonValue.AsHidType(); break; case "ButtonA": - viewModel.Config.ButtonA = buttonValue.AsHidType(); + ViewModel.Config.ButtonA = buttonValue.AsHidType(); break; case "ButtonB": - viewModel.Config.ButtonB = buttonValue.AsHidType(); + ViewModel.Config.ButtonB = buttonValue.AsHidType(); break; case "ButtonX": - viewModel.Config.ButtonX = buttonValue.AsHidType(); + ViewModel.Config.ButtonX = buttonValue.AsHidType(); break; case "ButtonY": - viewModel.Config.ButtonY = buttonValue.AsHidType(); + ViewModel.Config.ButtonY = buttonValue.AsHidType(); break; case "RightStickButton": - viewModel.Config.RightStickButton = buttonValue.AsHidType(); + ViewModel.Config.RightStickButton = buttonValue.AsHidType(); break; case "RightStickUp": - viewModel.Config.RightStickUp = buttonValue.AsHidType(); + ViewModel.Config.RightStickUp = buttonValue.AsHidType(); break; case "RightStickDown": - viewModel.Config.RightStickDown = buttonValue.AsHidType(); + ViewModel.Config.RightStickDown = buttonValue.AsHidType(); break; case "RightStickRight": - viewModel.Config.RightStickRight = buttonValue.AsHidType(); + ViewModel.Config.RightStickRight = buttonValue.AsHidType(); break; case "RightStickLeft": - viewModel.Config.RightStickLeft = buttonValue.AsHidType(); + ViewModel.Config.RightStickLeft = buttonValue.AsHidType(); break; } } diff --git a/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs index ce28f6c1c..3252ee7e2 100644 --- a/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/LedInputView.axaml.cs @@ -2,19 +2,18 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.ViewModels.Input; using System.Threading.Tasks; namespace Ryujinx.UI.Views.Input { - public partial class LedInputView : UserControl + public partial class LedInputView : RyujinxControl { - private readonly LedInputViewModel _viewModel; - public LedInputView(ControllerInputViewModel viewModel) { - DataContext = _viewModel = new LedInputViewModel + ViewModel = new LedInputViewModel { ParentModel = viewModel.ParentModel, TurnOffLed = viewModel.Config.TurnOffLed, @@ -29,20 +28,18 @@ namespace Ryujinx.UI.Views.Input private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args) { if (!args.NewColor.HasValue) return; - if (DataContext is not LedInputViewModel lvm) return; - if (!lvm.EnableLedChanging) return; - if (lvm.TurnOffLed) return; + if (!ViewModel.EnableLedChanging) return; + if (ViewModel.TurnOffLed) return; - lvm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); + ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); } private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) { - if (DataContext is not LedInputViewModel lvm) return; - if (!lvm.EnableLedChanging) return; - if (lvm.TurnOffLed) return; + if (!ViewModel.EnableLedChanging) return; + if (ViewModel.TurnOffLed) return; - lvm.ParentModel.SelectedGamepad.SetLed(lvm.LedColor.ToUInt32()); + ViewModel.ParentModel.SelectedGamepad.SetLed(ViewModel.LedColor.ToUInt32()); } public static async Task Show(ControllerInputViewModel viewModel) @@ -57,13 +54,13 @@ namespace Ryujinx.UI.Views.Input CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], Content = content, }; - contentDialog.PrimaryButtonClick += (sender, args) => + contentDialog.PrimaryButtonClick += (_, _) => { GamepadInputConfig config = viewModel.Config; - config.EnableLedChanging = content._viewModel.EnableLedChanging; - config.LedColor = content._viewModel.LedColor; - config.UseRainbowLed = content._viewModel.UseRainbowLed; - config.TurnOffLed = content._viewModel.TurnOffLed; + config.EnableLedChanging = content.ViewModel.EnableLedChanging; + config.LedColor = content.ViewModel.LedColor; + config.UseRainbowLed = content.ViewModel.UseRainbowLed; + config.TurnOffLed = content.ViewModel.TurnOffLed; }; await contentDialog.ShowAsync(); diff --git a/src/Ryujinx/UI/Views/Input/MotionInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/MotionInputView.axaml.cs index 36068821f..329b9128e 100644 --- a/src/Ryujinx/UI/Views/Input/MotionInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/MotionInputView.axaml.cs @@ -1,16 +1,15 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.ViewModels.Input; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Input { - public partial class MotionInputView : UserControl + public partial class MotionInputView : RyujinxControl { - private readonly MotionInputViewModel _viewModel; - public MotionInputView() { InitializeComponent(); @@ -20,7 +19,7 @@ namespace Ryujinx.Ava.UI.Views.Input { GamepadInputConfig config = viewModel.Config; - _viewModel = new MotionInputViewModel + ViewModel = new MotionInputViewModel { Slot = config.Slot, AltSlot = config.AltSlot, @@ -33,7 +32,6 @@ namespace Ryujinx.Ava.UI.Views.Input }; InitializeComponent(); - DataContext = _viewModel; } public static async Task Show(ControllerInputViewModel viewModel) @@ -48,17 +46,17 @@ namespace Ryujinx.Ava.UI.Views.Input CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], Content = content, }; - contentDialog.PrimaryButtonClick += (sender, args) => + contentDialog.PrimaryButtonClick += (_, _) => { GamepadInputConfig config = viewModel.Config; - config.Slot = content._viewModel.Slot; - config.Sensitivity = content._viewModel.Sensitivity; - config.GyroDeadzone = content._viewModel.GyroDeadzone; - config.AltSlot = content._viewModel.AltSlot; - config.DsuServerHost = content._viewModel.DsuServerHost; - config.DsuServerPort = content._viewModel.DsuServerPort; - config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion; - config.MirrorInput = content._viewModel.MirrorInput; + config.Slot = content.ViewModel.Slot; + config.Sensitivity = content.ViewModel.Sensitivity; + config.GyroDeadzone = content.ViewModel.GyroDeadzone; + config.AltSlot = content.ViewModel.AltSlot; + config.DsuServerHost = content.ViewModel.DsuServerHost; + config.DsuServerPort = content.ViewModel.DsuServerPort; + config.EnableCemuHookMotion = content.ViewModel.EnableCemuHookMotion; + config.MirrorInput = content.ViewModel.MirrorInput; }; await contentDialog.ShowAsync(); diff --git a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs index 6a76ea59b..2745ceac7 100644 --- a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs @@ -1,16 +1,15 @@ using Avalonia.Controls; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.ViewModels.Input; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Input { - public partial class RumbleInputView : UserControl + public partial class RumbleInputView : RyujinxControl { - private readonly RumbleInputViewModel _viewModel; - public RumbleInputView() { InitializeComponent(); @@ -20,15 +19,13 @@ namespace Ryujinx.Ava.UI.Views.Input { GamepadInputConfig config = viewModel.Config; - _viewModel = new RumbleInputViewModel + ViewModel = new RumbleInputViewModel { StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble, }; InitializeComponent(); - - DataContext = _viewModel; } public static async Task Show(ControllerInputViewModel viewModel) @@ -44,11 +41,11 @@ namespace Ryujinx.Ava.UI.Views.Input Content = content, }; - contentDialog.PrimaryButtonClick += (sender, args) => + contentDialog.PrimaryButtonClick += (_, _) => { GamepadInputConfig config = viewModel.Config; - config.StrongRumble = content._viewModel.StrongRumble; - config.WeakRumble = content._viewModel.WeakRumble; + config.StrongRumble = content.ViewModel.StrongRumble; + config.WeakRumble = content.ViewModel.WeakRumble; }; await contentDialog.ShowAsync(); diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 2cab0915d..22ac23dc7 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -6,6 +6,7 @@ using Gommon; using LibHac.Common; using LibHac.Ns; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; @@ -25,10 +26,9 @@ using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Views.Main { - public partial class MainMenuBarView : UserControl + public partial class MainMenuBarView : RyujinxControl { public MainWindow Window { get; private set; } - public MainWindowViewModel ViewModel { get; private set; } public MainMenuBarView() { diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs index 78747013c..5a023910c 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs @@ -4,6 +4,8 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Threading; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; @@ -13,7 +15,7 @@ using System; namespace Ryujinx.Ava.UI.Views.Main { - public partial class MainStatusBarView : UserControl + public partial class MainStatusBarView : RyujinxControl { public MainWindow Window; @@ -29,7 +31,7 @@ namespace Ryujinx.Ava.UI.Views.Main if (VisualRoot is MainWindow window) { Window = window; - DataContext = window.ViewModel; + ViewModel = window.ViewModel; LocaleManager.Instance.LocaleChanged += () => Dispatcher.UIThread.Post(() => { if (Window.ViewModel.EnableNonGameRunningControls) diff --git a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs index 7a83a36de..8ebc89f26 100644 --- a/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainViewControls.axaml.cs @@ -3,16 +3,15 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; using Ryujinx.Ava.Common; +using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using System; namespace Ryujinx.Ava.UI.Views.Main { - public partial class MainViewControls : UserControl + public partial class MainViewControls : RyujinxControl { - public MainWindowViewModel ViewModel; - public MainViewControls() { InitializeComponent(); @@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Views.Main if (VisualRoot is MainWindow window) { - DataContext = ViewModel = window.ViewModel; + ViewModel = window.ViewModel; } } diff --git a/src/Ryujinx/UI/Views/User/UserEditorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserEditorView.axaml.cs index 695586a36..afd102d07 100644 --- a/src/Ryujinx/UI/Views/User/UserEditorView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserEditorView.axaml.cs @@ -13,13 +13,12 @@ using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; namespace Ryujinx.Ava.UI.Views.User { - public partial class UserEditorView : UserControl + public partial class UserEditorView : RyujinxControl { private NavigationDialogHost _parent; private UserProfile _profile; private bool _isNewUser; - - public TempProfile TempProfile { get; set; } + public static uint MaxProfileNameLength => 0x20; public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId; @@ -42,7 +41,7 @@ namespace Ryujinx.Ava.UI.Views.User (NavigationDialogHost parent, UserProfile profile, bool isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; _isNewUser = isNewUser; _profile = profile; - TempProfile = new TempProfile(_profile); + ViewModel = new TempProfile(_profile); _parent = parent; break; @@ -51,8 +50,6 @@ namespace Ryujinx.Ava.UI.Views.User ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " + $"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; - DataContext = TempProfile; - AddPictureButton.IsVisible = _isNewUser; ChangePictureButton.IsVisible = !_isNewUser; IdLabel.IsVisible = _profile != null; @@ -72,7 +69,7 @@ namespace Ryujinx.Ava.UI.Views.User { if (_isNewUser) { - if (TempProfile.Name != String.Empty || TempProfile.Image != null) + if (ViewModel.Name != string.Empty || ViewModel.Image != null) { if (await ContentDialogHelper.CreateChoiceDialog( LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], @@ -89,7 +86,7 @@ namespace Ryujinx.Ava.UI.Views.User } else { - if (_profile.Name != TempProfile.Name || _profile.Image != TempProfile.Image) + if (_profile.Name != ViewModel.Name || _profile.Image != ViewModel.Image) { if (await ContentDialogHelper.CreateChoiceDialog( LocaleManager.Instance[LocaleKeys.DialogUserProfileUnsavedChangesTitle], @@ -115,31 +112,31 @@ namespace Ryujinx.Ava.UI.Views.User { DataValidationErrors.ClearErrors(NameBox); - if (string.IsNullOrWhiteSpace(TempProfile.Name)) + if (string.IsNullOrWhiteSpace(ViewModel.Name)) { DataValidationErrors.SetError(NameBox, new DataValidationException(LocaleManager.Instance[LocaleKeys.UserProfileEmptyNameError])); return; } - if (TempProfile.Image == null) + if (ViewModel.Image == null) { - _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile)); + _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); return; } if (_profile != null && !_isNewUser) { - _profile.Name = TempProfile.Name; - _profile.Image = TempProfile.Image; + _profile.Name = ViewModel.Name; + _profile.Image = ViewModel.Image; _profile.UpdateState(); _parent.AccountManager.SetUserName(_profile.UserId, _profile.Name); _parent.AccountManager.SetUserImage(_profile.UserId, _profile.Image); } else if (_isNewUser) { - _parent.AccountManager.AddUser(TempProfile.Name, TempProfile.Image, TempProfile.UserId); + _parent.AccountManager.AddUser(ViewModel.Name, ViewModel.Image, ViewModel.UserId); } else { @@ -151,7 +148,7 @@ namespace Ryujinx.Ava.UI.Views.User public void SelectProfileImage() { - _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, TempProfile)); + _parent.Navigate(typeof(UserProfileImageSelectorView), (_parent, ViewModel)); } private void ChangePictureButton_Click(object sender, RoutedEventArgs e) diff --git a/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs index fced8dc6f..f34d8f603 100644 --- a/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserFirmwareAvatarSelectorView.axaml.cs @@ -1,4 +1,3 @@ -using Avalonia.Controls; using Avalonia.Interactivity; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; @@ -11,7 +10,7 @@ using System.IO; namespace Ryujinx.Ava.UI.Views.User { - public partial class UserFirmwareAvatarSelectorView : UserControl + public partial class UserFirmwareAvatarSelectorView : RyujinxControl { private NavigationDialogHost _parent; private TempProfile _profile; @@ -20,8 +19,6 @@ namespace Ryujinx.Ava.UI.Views.User { ContentManager = contentManager; - DataContext = ViewModel; - InitializeComponent(); } @@ -55,8 +52,6 @@ namespace Ryujinx.Ava.UI.Views.User public ContentManager ContentManager { get; private set; } - internal UserFirmwareAvatarSelectorViewModel ViewModel { get; set; } - private void GoBack(object sender, RoutedEventArgs e) { _parent.GoBack(); diff --git a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs index bd328b0c7..ae8ebec1a 100644 --- a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs @@ -15,14 +15,12 @@ using System.IO; namespace Ryujinx.Ava.UI.Views.User { - public partial class UserProfileImageSelectorView : UserControl + public partial class UserProfileImageSelectorView : RyujinxControl { private ContentManager _contentManager; private NavigationDialogHost _parent; private TempProfile _profile; - internal UserProfileImageSelectorViewModel ViewModel { get; private set; } - public UserProfileImageSelectorView() { InitializeComponent(); diff --git a/src/Ryujinx/UI/Views/User/UserRecovererView.axaml.cs b/src/Ryujinx/UI/Views/User/UserRecovererView.axaml.cs index 98d7ceac9..34dc3edab 100644 --- a/src/Ryujinx/UI/Views/User/UserRecovererView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserRecovererView.axaml.cs @@ -4,10 +4,11 @@ using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Navigation; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.ViewModels; namespace Ryujinx.Ava.UI.Views.User { - public partial class UserRecovererView : UserControl + public partial class UserRecovererView : RyujinxControl { private NavigationDialogHost _parent; diff --git a/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml.cs b/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml.cs index bf11e878c..8aff9c5f9 100644 --- a/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserSaveManagerView.axaml.cs @@ -23,10 +23,8 @@ using UserId = LibHac.Fs.UserId; namespace Ryujinx.Ava.UI.Views.User { - public partial class UserSaveManagerView : UserControl + public partial class UserSaveManagerView : RyujinxControl { - internal UserSaveManagerViewModel ViewModel { get; private set; } - private AccountManager _accountManager; private HorizonClient _horizonClient; private VirtualFileSystem _virtualFileSystem; @@ -66,7 +64,7 @@ namespace Ryujinx.Ava.UI.Views.User public void LoadSaves() { - ViewModel.Saves.Clear(); + Dispatcher.UIThread.Post(() => ViewModel.Saves.Clear()); ObservableCollection saves = []; SaveDataFilter saveDataFilter = SaveDataFilter.Make( programId: default, diff --git a/src/Ryujinx/UI/Views/User/UserSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserSelectorView.axaml.cs index fa3383aa9..135e2c074 100644 --- a/src/Ryujinx/UI/Views/User/UserSelectorView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserSelectorView.axaml.cs @@ -11,12 +11,10 @@ using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Views.User { - public partial class UserSelectorViews : UserControl + public partial class UserSelectorViews : RyujinxControl { private NavigationDialogHost _parent; - public UserProfileViewModel ViewModel { get; set; } - public UserSelectorViews() { InitializeComponent();