From 27c5cba10b679e75f12bcae50b36a1cb3bc49acd Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Tue, 31 Dec 2024 21:11:57 -0600 Subject: [PATCH] misc: More Mvvm usage instead of writing out the observable properties --- .../UI/ViewModels/AboutWindowViewModel.cs | 40 ++------- .../DownloadableContentManagerViewModel.cs | 39 ++------- .../UI/ViewModels/ModManagerViewModel.cs | 31 ++----- .../UI/ViewModels/SettingsHacksViewModel.cs | 31 ++----- .../UI/ViewModels/SettingsViewModel.cs | 2 +- .../UI/ViewModels/TitleUpdateViewModel.cs | 70 ++++------------ .../UserFirmwareAvatarSelectorViewModel.cs | 33 ++------ .../UserProfileImageSelectorViewModel.cs | 17 +--- .../UI/ViewModels/UserSaveManagerViewModel.cs | 82 +++++-------------- .../UI/Views/Settings/SettingsHacksView.axaml | 2 +- .../UI/Windows/TitleUpdateWindow.axaml | 2 +- 11 files changed, 72 insertions(+), 277 deletions(-) diff --git a/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs index 6bc1e1f03..979ae8253 100644 --- a/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/AboutWindowViewModel.cs @@ -1,6 +1,7 @@ using Avalonia.Media.Imaging; using Avalonia.Styling; using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Utilities.Configuration; @@ -8,42 +9,11 @@ using System; namespace Ryujinx.Ava.UI.ViewModels { - public class AboutWindowViewModel : BaseModel, IDisposable + public partial class AboutWindowViewModel : BaseModel, IDisposable { - private Bitmap _githubLogo; - private Bitmap _discordLogo; - - private string _version; - - public Bitmap GithubLogo - { - get => _githubLogo; - set - { - _githubLogo = value; - OnPropertyChanged(); - } - } - - public Bitmap DiscordLogo - { - get => _discordLogo; - set - { - _discordLogo = value; - OnPropertyChanged(); - } - } - - public string Version - { - get => _version; - set - { - _version = value; - OnPropertyChanged(); - } - } + [ObservableProperty] private Bitmap _githubLogo; + [ObservableProperty] private Bitmap _discordLogo; + [ObservableProperty] private string _version; public string Developers => "GreemDev"; diff --git a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs index acc26decb..658568909 100644 --- a/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -2,6 +2,7 @@ using Avalonia.Collections; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform.Storage; using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; using DynamicData; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; @@ -17,13 +18,13 @@ using Application = Avalonia.Application; namespace Ryujinx.Ava.UI.ViewModels { - public class DownloadableContentManagerViewModel : BaseModel + public partial class DownloadableContentManagerViewModel : BaseModel { private readonly ApplicationLibrary _applicationLibrary; private AvaloniaList _downloadableContents = new(); - private AvaloniaList _selectedDownloadableContents = new(); - private AvaloniaList _views = new(); - private bool _showBundledContentNotice = false; + [ObservableProperty] private AvaloniaList _selectedDownloadableContents = new(); + [ObservableProperty] private AvaloniaList _views = new(); + [ObservableProperty] private bool _showBundledContentNotice = false; private string _search; private readonly ApplicationData _applicationData; @@ -41,26 +42,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public AvaloniaList Views - { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } - } - - public AvaloniaList SelectedDownloadableContents - { - get => _selectedDownloadableContents; - set - { - _selectedDownloadableContents = value; - OnPropertyChanged(); - } - } - public string Search { get => _search; @@ -77,16 +58,6 @@ namespace Ryujinx.Ava.UI.ViewModels get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); } - public bool ShowBundledContentNotice - { - get => _showBundledContentNotice; - set - { - _showBundledContentNotice = value; - OnPropertyChanged(); - } - } - public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { _applicationLibrary = applicationLibrary; diff --git a/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs index 9c26376ce..ce40ce16c 100644 --- a/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/ModManagerViewModel.cs @@ -1,8 +1,7 @@ -using Avalonia; using Avalonia.Collections; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform.Storage; using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; using DynamicData; using Gommon; using Ryujinx.Ava.Common.Locale; @@ -18,13 +17,13 @@ using System.Linq; namespace Ryujinx.Ava.UI.ViewModels { - public class ModManagerViewModel : BaseModel + public partial class ModManagerViewModel : BaseModel { private readonly string _modJsonPath; private AvaloniaList _mods = new(); - private AvaloniaList _views = new(); - private AvaloniaList _selectedMods = new(); + [ObservableProperty] private AvaloniaList _views = new(); + [ObservableProperty] private AvaloniaList _selectedMods = new(); private string _search; private readonly ulong _applicationId; @@ -44,26 +43,6 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public AvaloniaList Views - { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } - } - - public AvaloniaList SelectedMods - { - get => _selectedMods; - set - { - _selectedMods = value; - OnPropertyChanged(); - } - } - public string Search { get => _search; @@ -143,8 +122,10 @@ namespace Ryujinx.Ava.UI.ViewModels .Filter(Filter) .Bind(out var view).AsObservableList(); +#pragma warning disable MVVMTK0034 // Event to update is fired below _views.Clear(); _views.AddRange(view); +#pragma warning restore MVVMTK0034 SelectedMods = new(Views.Where(x => x.Enabled)); diff --git a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs index 4cfbc8957..5096a716d 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsHacksViewModel.cs @@ -1,9 +1,10 @@ -using Gommon; +using CommunityToolkit.Mvvm.ComponentModel; +using Gommon; using Ryujinx.Ava.Utilities.Configuration; namespace Ryujinx.Ava.UI.ViewModels { - public class SettingsHacksViewModel : BaseModel + public partial class SettingsHacksViewModel : BaseModel { private readonly SettingsViewModel _baseViewModel; @@ -14,31 +15,9 @@ namespace Ryujinx.Ava.UI.ViewModels _baseViewModel = settingsVm; } - private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; - private bool _shaderTranslationThreadSleep = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; + [ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; + [ObservableProperty] private bool _shaderTranslationDelayEnabled = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay; - - public bool Xc2MenuSoftlockFixEnabled - { - get => _xc2MenuSoftlockFix; - set - { - _xc2MenuSoftlockFix = value; - - OnPropertyChanged(); - } - } - - public bool ShaderTranslationDelayEnabled - { - get => _shaderTranslationThreadSleep; - set - { - _shaderTranslationThreadSleep = value; - - OnPropertyChanged(); - } - } public string ShaderTranslationDelayValueText => $"{ShaderTranslationDelay}ms"; diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index a5bdd2f88..39df76aa4 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -756,7 +756,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.Multiplayer.LdnServer.Value = LdnServer; // Dirty Hacks - config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFixEnabled; + config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix; config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; diff --git a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs index 0748efeb4..86d59d6b4 100644 --- a/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/TitleUpdateViewModel.cs @@ -1,74 +1,32 @@ using Avalonia.Collections; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Platform.Storage; using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.Utilities.AppLibrary; -using Ryujinx.HLE.FileSystem; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using Application = Avalonia.Application; namespace Ryujinx.Ava.UI.ViewModels { - public record TitleUpdateViewNoUpdateSentinal(); + public record TitleUpdateViewModelNoUpdate; - public class TitleUpdateViewModel : BaseModel + public partial class TitleUpdateViewModel : BaseModel { private ApplicationLibrary ApplicationLibrary { get; } private ApplicationData ApplicationData { get; } - private AvaloniaList _titleUpdates = new(); - private AvaloniaList _views = new(); - private object _selectedUpdate = new TitleUpdateViewNoUpdateSentinal(); - private bool _showBundledContentNotice = false; + [ObservableProperty] private AvaloniaList _titleUpdates = new(); + [ObservableProperty] private AvaloniaList _views = new(); + [ObservableProperty] private object _selectedUpdate = new TitleUpdateViewModelNoUpdate(); + [ObservableProperty] private bool _showBundledContentNotice; - public AvaloniaList TitleUpdates - { - get => _titleUpdates; - set - { - _titleUpdates = value; - OnPropertyChanged(); - } - } - - public AvaloniaList Views - { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } - } - - public object SelectedUpdate - { - get => _selectedUpdate; - set - { - _selectedUpdate = value; - OnPropertyChanged(); - } - } - - public bool ShowBundledContentNotice - { - get => _showBundledContentNotice; - set - { - _showBundledContentNotice = value; - OnPropertyChanged(); - } - } - - public IStorageProvider StorageProvider; + private readonly IStorageProvider _storageProvider; public TitleUpdateViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { @@ -76,7 +34,7 @@ namespace Ryujinx.Ava.UI.ViewModels ApplicationData = applicationData; - StorageProvider = RyujinxApp.MainWindow.StorageProvider; + _storageProvider = RyujinxApp.MainWindow.StorageProvider; LoadUpdates(); } @@ -87,7 +45,7 @@ namespace Ryujinx.Ava.UI.ViewModels .Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase); bool hasBundledContent = false; - SelectedUpdate = new TitleUpdateViewNoUpdateSentinal(); + SelectedUpdate = new TitleUpdateViewModelNoUpdate(); foreach ((TitleUpdateModel update, bool isSelected) in updates) { TitleUpdates.Add(update); @@ -113,12 +71,12 @@ namespace Ryujinx.Ava.UI.ViewModels var selected = SelectedUpdate; Views.Clear(); - Views.Add(new TitleUpdateViewNoUpdateSentinal()); + Views.Add(new TitleUpdateViewModelNoUpdate()); Views.AddRange(sortedUpdates); SelectedUpdate = selected; - if (SelectedUpdate is TitleUpdateViewNoUpdateSentinal) + if (SelectedUpdate is TitleUpdateViewModelNoUpdate) { SelectedUpdate = Views[0]; } @@ -176,7 +134,7 @@ namespace Ryujinx.Ava.UI.ViewModels } else if (update == SelectedUpdate as TitleUpdateModel) { - SelectedUpdate = new TitleUpdateViewNoUpdateSentinal(); + SelectedUpdate = new TitleUpdateViewModelNoUpdate(); } SortUpdates(); @@ -184,7 +142,7 @@ namespace Ryujinx.Ava.UI.ViewModels public async Task Add() { - var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = true, FileTypeFilter = new List diff --git a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs index b07bf78b9..29c81308b 100644 --- a/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserFirmwareAvatarSelectorViewModel.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using CommunityToolkit.Mvvm.ComponentModel; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; @@ -20,12 +21,12 @@ using Image = SkiaSharp.SKImage; namespace Ryujinx.Ava.UI.ViewModels { - internal class UserFirmwareAvatarSelectorViewModel : BaseModel + internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel { private static readonly Dictionary _avatarStore = new(); - private ObservableCollection _images; - private Color _backgroundColor = Colors.White; + [ObservableProperty] private ObservableCollection _images; + [ObservableProperty] private Color _backgroundColor = Colors.White; private int _selectedIndex; @@ -34,27 +35,11 @@ namespace Ryujinx.Ava.UI.ViewModels _images = new ObservableCollection(); LoadImagesFromStore(); - } - - public Color BackgroundColor - { - get => _backgroundColor; - set + PropertyChanged += (_, args) => { - _backgroundColor = value; - OnPropertyChanged(); - ChangeImageBackground(); - } - } - - public ObservableCollection Images - { - get => _images; - set - { - _images = value; - OnPropertyChanged(); - } + if (args.PropertyName == nameof(BackgroundColor)) + ChangeImageBackground(); + }; } public int SelectedIndex @@ -70,7 +55,7 @@ namespace Ryujinx.Ava.UI.ViewModels } else { - SelectedImage = _images[_selectedIndex].Data; + SelectedImage = Images[_selectedIndex].Data; } OnPropertyChanged(); diff --git a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs index 8e7d41a55..36a9a62f9 100644 --- a/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserProfileImageSelectorViewModel.cs @@ -1,18 +1,9 @@ +using CommunityToolkit.Mvvm.ComponentModel; + namespace Ryujinx.Ava.UI.ViewModels { - internal class UserProfileImageSelectorViewModel : BaseModel + internal partial class UserProfileImageSelectorViewModel : BaseModel { - private bool _firmwareFound; - - public bool FirmwareFound - { - get => _firmwareFound; - - set - { - _firmwareFound = value; - OnPropertyChanged(); - } - } + [ObservableProperty] private bool _firmwareFound; } } diff --git a/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs b/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs index 85adef005..187df0449 100644 --- a/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/UserSaveManagerViewModel.cs @@ -1,3 +1,4 @@ +using CommunityToolkit.Mvvm.ComponentModel; using DynamicData; using DynamicData.Binding; using Ryujinx.Ava.Common.Locale; @@ -8,74 +9,31 @@ using System.Collections.ObjectModel; namespace Ryujinx.Ava.UI.ViewModels { - public class UserSaveManagerViewModel : BaseModel + public partial class UserSaveManagerViewModel : BaseModel { - private int _sortIndex; - private int _orderIndex; - private string _search; - private ObservableCollection _saves = new(); - private ObservableCollection _views = new(); + [ObservableProperty] private int _sortIndex; + [ObservableProperty] private int _orderIndex; + [ObservableProperty] private string _search; + [ObservableProperty] private ObservableCollection _saves = new(); + [ObservableProperty] private ObservableCollection _views = new(); private readonly AccountManager _accountManager; public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); - public int SortIndex - { - get => _sortIndex; - set - { - _sortIndex = value; - OnPropertyChanged(); - Sort(); - } - } - - public int OrderIndex - { - get => _orderIndex; - set - { - _orderIndex = value; - OnPropertyChanged(); - Sort(); - } - } - - public string Search - { - get => _search; - set - { - _search = value; - OnPropertyChanged(); - Sort(); - } - } - - public ObservableCollection Saves - { - get => _saves; - set - { - _saves = value; - OnPropertyChanged(); - Sort(); - } - } - - public ObservableCollection Views - { - get => _views; - set - { - _views = value; - OnPropertyChanged(); - } - } - public UserSaveManagerViewModel(AccountManager accountManager) { _accountManager = accountManager; + PropertyChanged += (_, evt) => + { + if (evt.PropertyName is + nameof(SortIndex) or + nameof(OrderIndex) or + nameof(Search) or + nameof(Saves)) + { + Sort(); + } + }; } public void Sort() @@ -85,8 +43,10 @@ namespace Ryujinx.Ava.UI.ViewModels .Sort(GetComparer()) .Bind(out var view).AsObservableList(); +#pragma warning disable MVVMTK0034 _views.Clear(); _views.AddRange(view); +#pragma warning restore MVVMTK0034 OnPropertyChanged(nameof(Views)); } @@ -94,7 +54,7 @@ namespace Ryujinx.Ava.UI.ViewModels { if (arg is SaveModel save) { - return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower()); + return string.IsNullOrWhiteSpace(Search) || save.Title.ToLower().Contains(Search.ToLower()); } return false; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml index 2ef0cc74f..f1900a69a 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHacksView.axaml @@ -37,7 +37,7 @@ ToolTip.Tip="{Binding DirtyHacks.Xc2MenuFixTooltip}"> + IsChecked="{Binding DirtyHacks.Xc2MenuSoftlockFix}"/> diff --git a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml index 0ba9bc7d8..a8ec5d29a 100644 --- a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml +++ b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml @@ -95,7 +95,7 @@ + DataType="viewModels:TitleUpdateViewModelNoUpdate">