diff options
| author | Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> | 2024-01-26 01:02:28 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-26 02:02:28 +0100 |
| commit | 35fb409e85ef07b8e1c3a582cdc6615e6da71429 (patch) | |
| tree | 1696d9498a73fe94d228a6bbf3ed06ff92f88a0c /src/Ryujinx.Ava/UI | |
| parent | d7ec4308b45d4ecb8d77cdc8d98ee618944292ed (diff) | |
Ava UI: Mod Manager (#4390)
* Let’s start again
* Read folders and such
* Remove Open Mod Folder menu items
* Fix folder opening, Selecting/deselecting
* She works
* Fix GTK
* AddMod
* Delete
* Fix duplicate entries
* Fix file check
* Avalonia 11
* Style fixes
* Final style fixes
* Might be too general
* Remove unnecessary using
* Enable new mods by default
* More cleanup
* Fix saving metadata
* Dont deseralise ModMetadata several times
* Avalonia I hate you
* Confirmation dialgoues
* Allow selecting multiple folders
* Add back secondary folder
* Search both paths
* Fix formatting
* Apply suggestions from code review
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Rename Title to Application
* Generic locale key
* Apply suggestions from code review
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Locale Updates
* GDK Feedback
* Fix
---------
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Diffstat (limited to 'src/Ryujinx.Ava/UI')
9 files changed, 619 insertions, 32 deletions
diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml index b8fe7e76..7f786cf3 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -47,13 +47,9 @@ Header="{locale:Locale GameListContextMenuManageCheat}" ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" /> <MenuItem - Click="OpenModsDirectory_Click" - Header="{locale:Locale GameListContextMenuOpenModsDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" /> - <MenuItem - Click="OpenSdModsDirectory_Click" - Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}" - ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" /> + Click="OpenModManager_Click" + Header="{locale:Locale GameListContextMenuManageMod}" + ToolTip.Tip="{locale:Locale GameListContextMenuManageModToolTip}" /> <Separator /> <MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}"> <MenuItem diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs index 0f007106..01d97709 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -126,29 +126,13 @@ namespace Ryujinx.Ava.UI.Controls } } - public void OpenModsDirectory_Click(object sender, RoutedEventArgs args) + public async void OpenModManager_Click(object sender, RoutedEventArgs args) { var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; if (viewModel?.SelectedApplication != null) { - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } - } - - public void OpenSdModsDirectory_Click(object sender, RoutedEventArgs args) - { - var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; - - if (viewModel?.SelectedApplication != null) - { - string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId); - - OpenHelper.OpenFolder(titleModsPath); + await ModManagerWindow.Show(ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); } } diff --git a/src/Ryujinx.Ava/UI/Models/ModModel.cs b/src/Ryujinx.Ava/UI/Models/ModModel.cs new file mode 100644 index 00000000..f68e1593 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Models/ModModel.cs @@ -0,0 +1,30 @@ +using Ryujinx.Ava.UI.ViewModels; +using System.IO; + +namespace Ryujinx.Ava.UI.Models +{ + public class ModModel : BaseModel + { + private bool _enabled; + + public bool Enabled + { + get => _enabled; + set + { + _enabled = value; + OnPropertyChanged(); + } + } + + public string Path { get; } + public string Name { get; } + + public ModModel(string path, string name, bool enabled) + { + Path = path; + Name = name; + Enabled = enabled; + } + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index cdecae77..2cd714f4 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -39,6 +39,7 @@ namespace Ryujinx.Ava.UI.ViewModels private string _search; private readonly ulong _titleId; + private readonly IStorageProvider _storageProvider; private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); @@ -90,8 +91,6 @@ namespace Ryujinx.Ava.UI.ViewModels get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); } - public IStorageProvider StorageProvider; - public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId) { _virtualFileSystem = virtualFileSystem; @@ -100,7 +99,7 @@ namespace Ryujinx.Ava.UI.ViewModels if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - StorageProvider = desktop.MainWindow.StorageProvider; + _storageProvider = desktop.MainWindow.StorageProvider; } _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); @@ -194,7 +193,7 @@ namespace Ryujinx.Ava.UI.ViewModels { Dispatcher.UIThread.InvokeAsync(async () => { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogLoadNcaErrorMessage], ex.Message, containerPath)); + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogLoadFileErrorMessage], ex.Message, containerPath)); }); } @@ -203,7 +202,7 @@ namespace Ryujinx.Ava.UI.ViewModels public async void Add() { - var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], AllowMultiple = true, diff --git a/src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs new file mode 100644 index 00000000..7340873a --- /dev/null +++ b/src/Ryujinx.Ava/UI/ViewModels/ModManagerViewModel.cs @@ -0,0 +1,260 @@ +using Avalonia; +using Avalonia.Collections; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform.Storage; +using Avalonia.Threading; +using DynamicData; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS; +using System.IO; +using System.Linq; + +namespace Ryujinx.Ava.UI.ViewModels +{ + public class ModManagerViewModel : BaseModel + { + private readonly string _modJsonPath; + + private AvaloniaList<ModModel> _mods = new(); + private AvaloniaList<ModModel> _views = new(); + private AvaloniaList<ModModel> _selectedMods = new(); + + private string _search; + private readonly ulong _applicationId; + private readonly IStorageProvider _storageProvider; + + private static readonly ModMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + public AvaloniaList<ModModel> Mods + { + get => _mods; + set + { + _mods = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ModCount)); + Sort(); + } + } + + public AvaloniaList<ModModel> Views + { + get => _views; + set + { + _views = value; + OnPropertyChanged(); + } + } + + public AvaloniaList<ModModel> SelectedMods + { + get => _selectedMods; + set + { + _selectedMods = value; + OnPropertyChanged(); + } + } + + public string Search + { + get => _search; + set + { + _search = value; + OnPropertyChanged(); + Sort(); + } + } + + public string ModCount + { + get => string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], Mods.Count); + } + + public ModManagerViewModel(ulong applicationId) + { + _applicationId = applicationId; + + _modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json"); + + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + _storageProvider = desktop.MainWindow.StorageProvider; + } + + LoadMods(applicationId); + } + + private void LoadMods(ulong applicationId) + { + Mods.Clear(); + SelectedMods.Clear(); + + string[] modsBasePaths = [ModLoader.GetSdModsBasePath(), ModLoader.GetModsBasePath()]; + + foreach (var path in modsBasePaths) + { + var modCache = new ModLoader.ModCache(); + + ModLoader.QueryContentsDir(modCache, new DirectoryInfo(Path.Combine(path, "contents")), applicationId); + + foreach (var mod in modCache.RomfsDirs) + { + var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled); + if (Mods.All(x => x.Path != mod.Path.Parent.FullName)) + { + Mods.Add(modModel); + } + } + + foreach (var mod in modCache.RomfsContainers) + { + Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled)); + } + + foreach (var mod in modCache.ExefsDirs) + { + var modModel = new ModModel(mod.Path.Parent.FullName, mod.Name, mod.Enabled); + if (Mods.All(x => x.Path != mod.Path.Parent.FullName)) + { + Mods.Add(modModel); + } + } + + foreach (var mod in modCache.ExefsContainers) + { + Mods.Add(new ModModel(mod.Path.FullName, mod.Name, mod.Enabled)); + } + } + + Sort(); + } + + public void Sort() + { + Mods.AsObservableChangeSet() + .Filter(Filter) + .Bind(out var view).AsObservableList(); + + _views.Clear(); + _views.AddRange(view); + + SelectedMods = new(Views.Where(x => x.Enabled)); + + OnPropertyChanged(nameof(ModCount)); + OnPropertyChanged(nameof(Views)); + OnPropertyChanged(nameof(SelectedMods)); + } + + private bool Filter(object arg) + { + if (arg is ModModel content) + { + return string.IsNullOrWhiteSpace(_search) || content.Name.ToLower().Contains(_search.ToLower()); + } + + return false; + } + + public void Save() + { + ModMetadata modData = new(); + + foreach (ModModel mod in Mods) + { + modData.Mods.Add(new Mod + { + Name = mod.Name, + Path = mod.Path, + Enabled = SelectedMods.Contains(mod), + }); + } + + JsonHelper.SerializeToFile(_modJsonPath, modData, _serializerContext.ModMetadata); + } + + public void Delete(ModModel model) + { + Directory.Delete(model.Path, true); + + Mods.Remove(model); + OnPropertyChanged(nameof(ModCount)); + Sort(); + } + + private void AddMod(DirectoryInfo directory) + { + var directories = Directory.GetDirectories(directory.ToString(), "*", SearchOption.AllDirectories); + var destinationDir = ModLoader.GetApplicationDir(ModLoader.GetSdModsBasePath(), _applicationId.ToString("x16")); + + foreach (var dir in directories) + { + string dirToCreate = dir.Replace(directory.Parent.ToString(), destinationDir); + + // Mod already exists + if (Directory.Exists(dirToCreate)) + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadFileErrorMessage, LocaleKeys.DialogModAlreadyExistsMessage, dirToCreate)); + }); + + return; + } + + Directory.CreateDirectory(dirToCreate); + } + + var files = Directory.GetFiles(directory.ToString(), "*", SearchOption.AllDirectories); + + foreach (var file in files) + { + File.Copy(file, file.Replace(directory.Parent.ToString(), destinationDir), true); + } + + LoadMods(_applicationId); + } + + public async void Add() + { + var result = await _storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions + { + Title = LocaleManager.Instance[LocaleKeys.SelectModDialogTitle], + AllowMultiple = true, + }); + + foreach (var folder in result) + { + AddMod(new DirectoryInfo(folder.Path.LocalPath)); + } + } + + public void DeleteAll() + { + foreach (var mod in Mods) + { + Directory.Delete(mod.Path, true); + } + + Mods.Clear(); + OnPropertyChanged(nameof(ModCount)); + Sort(); + } + + public void EnableAll() + { + SelectedMods = new(Mods); + } + + public void DisableAll() + { + SelectedMods.Clear(); + } + } +} diff --git a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs index 5090a8c7..8a287eca 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -192,7 +192,7 @@ namespace Ryujinx.Ava.UI.ViewModels } catch (Exception ex) { - Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path))); + Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadFileErrorMessage, ex.Message, path))); } } } diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs index c2de67ab..46441faa 100644 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); + string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, titleId); ulong titleIdValue = ulong.Parse(titleId, NumberStyles.HexNumber); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); diff --git a/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml new file mode 100644 index 00000000..d9f58640 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml @@ -0,0 +1,179 @@ +<UserControl + xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" + xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + Width="500" + Height="380" + mc:Ignorable="d" + x:Class="Ryujinx.Ava.UI.Windows.ModManagerWindow" + x:CompileBindings="True" + x:DataType="viewModels:ModManagerViewModel" + Focusable="True"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Panel + Margin="0 0 0 10" + Grid.Row="0"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock + Grid.Column="0" + Text="{Binding ModCount}" /> + <StackPanel + Margin="10 0" + Grid.Column="1" + Orientation="Horizontal"> + <Button + Name="EnableAllButton" + MinWidth="90" + Margin="5" + Command="{ReflectionBinding EnableAll}"> + <TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" /> + </Button> + <Button + Name="DisableAllButton" + MinWidth="90" + Margin="5" + Command="{ReflectionBinding DisableAll}"> + <TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" /> + </Button> + </StackPanel> + <TextBox + Grid.Column="2" + MinHeight="27" + MaxHeight="27" + HorizontalAlignment="Stretch" + Watermark="{locale:Locale Search}" + Text="{Binding Search}" /> + </Grid> + </Panel> + <Border + Grid.Row="1" + Margin="0 0 0 24" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" + BorderBrush="{DynamicResource AppListHoverBackgroundColor}" + BorderThickness="1" + CornerRadius="5" + Padding="2.5"> + <ListBox + AutoScrollToSelectedItem="False" + SelectionMode="Multiple, Toggle" + Background="Transparent" + SelectionChanged="OnSelectionChanged" + SelectedItems="{Binding SelectedMods, Mode=TwoWay}" + ItemsSource="{Binding Views}"> + <ListBox.DataTemplates> + <DataTemplate + DataType="models:ModModel"> + <Panel Margin="10"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + MaxLines="2" + TextWrapping="Wrap" + TextTrimming="CharacterEllipsis" + Text="{Binding Name}" /> + <StackPanel + Grid.Column="1" + Spacing="10" + Orientation="Horizontal" + HorizontalAlignment="Right"> + <Button + VerticalAlignment="Center" + HorizontalAlignment="Right" + Padding="10" + MinWidth="0" + MinHeight="0" + Click="OpenLocation"> + <ui:SymbolIcon + Symbol="OpenFolder" + HorizontalAlignment="Center" + VerticalAlignment="Center" /> + </Button> + <Button + VerticalAlignment="Center" + HorizontalAlignment="Right" + Padding="10" + MinWidth="0" + MinHeight="0" + Click="DeleteMod"> + <ui:SymbolIcon + Symbol="Cancel" + HorizontalAlignment="Center" + VerticalAlignment="Center" /> + </Button> + </StackPanel> + </Grid> + </Panel> + </DataTemplate> + </ListBox.DataTemplates> + <ListBox.Styles> + <Style Selector="ListBoxItem"> + <Setter Property="Background" Value="Transparent" /> + </Style> + </ListBox.Styles> + </ListBox> + </Border> + <Panel + Grid.Row="2" + HorizontalAlignment="Stretch"> + <StackPanel + Orientation="Horizontal" + Spacing="10" + HorizontalAlignment="Left"> + <Button + Name="AddButton" + MinWidth="90" + Margin="5" + Command="{Binding Add}"> + <TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" /> + </Button> + <Button + Name="RemoveAllButton" + MinWidth="90" + Margin="5" + Click="DeleteAll"> + <TextBlock Text="{locale:Locale ModManagerDeleteAllButton}" /> + </Button> + </StackPanel> + <StackPanel + Orientation="Horizontal" + Spacing="10" + HorizontalAlignment="Right"> + <Button + Name="SaveButton" + MinWidth="90" + Margin="5" + Click="SaveAndClose"> + <TextBlock Text="{locale:Locale SettingsButtonSave}" /> + </Button> + <Button + Name="CancelButton" + MinWidth="90" + Margin="5" + Click="Close"> + <TextBlock Text="{locale:Locale InputDialogCancel}" /> + </Button> + </StackPanel> + </Panel> + </Grid> +</UserControl> diff --git a/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs new file mode 100644 index 00000000..5de09ba0 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs @@ -0,0 +1,139 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ui.Common.Helper; +using System.Threading.Tasks; +using Button = Avalonia.Controls.Button; + +namespace Ryujinx.Ava.UI.Windows +{ + public partial class ModManagerWindow : UserControl + { + public ModManagerViewModel ViewModel; + + public ModManagerWindow() + { + DataContext = this; + + InitializeComponent(); + } + + public ModManagerWindow(ulong titleId) + { + DataContext = ViewModel = new ModManagerViewModel(titleId); + + InitializeComponent(); + } + + public static async Task Show(ulong titleId, string titleName) + { + ContentDialog contentDialog = new() + { + PrimaryButtonText = "", + SecondaryButtonText = "", + CloseButtonText = "", + Content = new ModManagerWindow(titleId), + Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], titleName, titleId.ToString("X16")), + }; + + Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); + bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); + + contentDialog.Styles.Add(bottomBorder); + + await contentDialog.ShowAsync(); + } + + private void SaveAndClose(object sender, RoutedEventArgs e) + { + ViewModel.Save(); + ((ContentDialog)Parent).Hide(); + } + + private void Close(object sender, RoutedEventArgs e) + { + ((ContentDialog)Parent).Hide(); + } + + private async void DeleteMod(object sender, RoutedEventArgs e) + { + if (sender is Button button) + { + if (button.DataContext is ModModel model) + { + var result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogWarning], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogModManagerDeletionWarningMessage, model.Name), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + ViewModel.Delete(model); + } + } + } + } + + private async void DeleteAll(object sender, RoutedEventArgs e) + { + var result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogWarning], + LocaleManager.Instance[LocaleKeys.DialogModManagerDeletionAllWarningMessage], + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + ViewModel.DeleteAll(); + } + } + + private void OpenLocation(object sender, RoutedEventArgs e) + { + if (sender is Button button) + { + if (button.DataContext is ModModel model) + { + OpenHelper.OpenFolder(model.Path); + } + } + } + + private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + foreach (var content in e.AddedItems) + { + if (content is ModModel model) + { + var index = ViewModel.Mods.IndexOf(model); + + if (index != -1) + { + ViewModel.Mods[index].Enabled = true; + } + } + } + + foreach (var content in e.RemovedItems) + { + if (content is ModModel model) + { + var index = ViewModel.Mods.IndexOf(model); + + if (index != -1) + { + ViewModel.Mods[index].Enabled = false; + } + } + } + } + } +} |
