diff options
| author | Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> | 2023-01-15 06:11:52 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-15 11:11:52 +0000 |
| commit | 719dc97bbd321e98083f47267feb01db769e5fa6 (patch) | |
| tree | ba249fdf3b41bb5636f183cdcb4134ef1703049c /Ryujinx.Ava/UI | |
| parent | 41bba5310a5324f54fa5c0200aff2bf697ced000 (diff) | |
Ava UI: `TitleUpdateWindow` Refactor (#4276)
* Start Refactor
* Dialogue opens
* Changes
* Switch to ListBox
* Fix bugs and stuff
* Fix spacing
* Implement OpenLocation
* Change icon
* Color
* Color
* Remove background
* Make no update the same height
* Fix height and smooth scroll
* Height
* Fix update selection
* Make window smaller
* Add back remove all button
* Make selection more obvious
* Hide selection bar on SaveManager
* Fix autoscroll
* Fix no update not staying selected
* Better file opener
* Fix
* Revert that
* Update Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
Co-authored-by: Ac_K <Acoustik666@gmail.com>
* Log warning
* Update Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
Diffstat (limited to 'Ryujinx.Ava/UI')
| -rw-r--r-- | Ryujinx.Ava/UI/Models/TitleUpdateModel.cs | 12 | ||||
| -rw-r--r-- | Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs | 8 | ||||
| -rw-r--r-- | Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs | 226 | ||||
| -rw-r--r-- | Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml | 4 | ||||
| -rw-r--r-- | Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml | 172 | ||||
| -rw-r--r-- | Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs | 255 |
6 files changed, 381 insertions, 296 deletions
diff --git a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs b/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs index c3ba6230..c57b3a26 100644 --- a/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs +++ b/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs @@ -3,23 +3,17 @@ using Ryujinx.Ava.Common.Locale; namespace Ryujinx.Ava.UI.Models { - internal class TitleUpdateModel + public class TitleUpdateModel { - public bool IsEnabled { get; set; } - public bool IsNoUpdate { get; } public ApplicationControlProperty Control { get; } public string Path { get; } - public string Label => IsNoUpdate - ? LocaleManager.Instance[LocaleKeys.NoUpdate] - : string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString(), - Path); + public string Label => string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString()); - public TitleUpdateModel(ApplicationControlProperty control, string path, bool isNoUpdate = false) + public TitleUpdateModel(ApplicationControlProperty control, string path) { Control = control; Path = path; - IsNoUpdate = isNoUpdate; } } }
\ No newline at end of file diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f86cda21..29540215 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1601,13 +1601,9 @@ namespace Ryujinx.Ava.UI.ViewModels public async void OpenTitleUpdateManager() { - ApplicationData selection = SelectedApplication; - if (selection != null) + if (SelectedApplication != null) { - if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - await new TitleUpdateWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow); - } + await TitleUpdateWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); } } diff --git a/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs new file mode 100644 index 00000000..131ebd25 --- /dev/null +++ b/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs @@ -0,0 +1,226 @@ +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Ns; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.HOS; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SpanHelpers = LibHac.Common.SpanHelpers; +using Path = System.IO.Path; + +namespace Ryujinx.Ava.UI.ViewModels; + +public class TitleUpdateViewModel : BaseModel +{ + public TitleUpdateMetadata _titleUpdateWindowData; + public readonly string _titleUpdateJsonPath; + private VirtualFileSystem _virtualFileSystem { get; } + private ulong _titleId { get; } + private string _titleName { get; } + + private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); + private AvaloniaList<object> _views = new(); + private object _selectedUpdate; + + public AvaloniaList<TitleUpdateModel> TitleUpdates + { + get => _titleUpdates; + set + { + _titleUpdates = value; + OnPropertyChanged(); + } + } + + public AvaloniaList<object> Views + { + get => _views; + set + { + _views = value; + OnPropertyChanged(); + } + } + + public object SelectedUpdate + { + get => _selectedUpdate; + set + { + _selectedUpdate = value; + OnPropertyChanged(); + } + } + + public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); + + try + { + _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath); + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); + + _titleUpdateWindowData = new TitleUpdateMetadata + { + Selected = "", + Paths = new List<string>() + }; + } + + LoadUpdates(); + } + + private void LoadUpdates() + { + foreach (string path in _titleUpdateWindowData.Paths) + { + AddUpdate(path); + } + + TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); + + SelectedUpdate = selected; + + SortUpdates(); + } + + public void SortUpdates() + { + var list = TitleUpdates.ToList(); + + list.Sort((first, second) => + { + if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) + { + return -1; + } + else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) + { + return 1; + } + + return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; + }); + + Views.Clear(); + Views.Add(new BaseModel()); + Views.AddRange(list); + + if (SelectedUpdate == null) + { + SelectedUpdate = Views[0]; + } + else if (!TitleUpdates.Contains(SelectedUpdate)) + { + if (Views.Count > 1) + { + SelectedUpdate = Views[1]; + } + else + { + SelectedUpdate = Views[0]; + } + } + } + + private void AddUpdate(string path) + { + if (File.Exists(path) && TitleUpdates.All(x => x.Path != path)) + { + using FileStream file = new(path, FileMode.Open, FileAccess.Read); + + try + { + (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); + + if (controlNca != null && patchNca != null) + { + ApplicationControlProperty controlData = new(); + + using UniqueRef<IFile> nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); + + TitleUpdates.Add(new TitleUpdateModel(controlData, path)); + } + else + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); + }); + } + } + catch (Exception ex) + { + Dispatcher.UIThread.Post(async () => + { + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path)); + }); + } + } + } + + public void RemoveUpdate(TitleUpdateModel update) + { + TitleUpdates.Remove(update); + + SortUpdates(); + } + + public async void Add() + { + OpenFileDialog dialog = new() + { + Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) + { + foreach (string file in files) + { + AddUpdate(file); + } + } + } + + SortUpdates(); + } +}
\ No newline at end of file diff --git a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml index b4f2e101..ec931dd9 100644 --- a/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml +++ b/Ryujinx.Ava/UI/Views/User/UserSaveManagerView.axaml @@ -107,6 +107,7 @@ VerticalAlignment="Stretch"> <ListBox Name="SaveList" + VirtualizationMode="None" Items="{Binding Views}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> @@ -116,6 +117,9 @@ <Setter Property="Margin" Value="5" /> <Setter Property="CornerRadius" Value="4" /> </Style> + <Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator"> + <Setter Property="IsVisible" Value="False" /> + </Style> </ListBox.Styles> <ListBox.ItemTemplate> <DataTemplate x:DataType="models:SaveModel"> diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml index 5a69be9b..e9858038 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml @@ -1,115 +1,135 @@ -<window:StyleableWindow +<UserControl x:Class="Ryujinx.Ava.UI.Windows.TitleUpdateWindow" 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:window="clr-namespace:Ryujinx.Ava.UI.Windows" - Width="600" - Height="400" - MinWidth="600" - MinHeight="400" - MaxWidth="600" - MaxHeight="400" - SizeToContent="Height" - WindowStartupLocation="CenterOwner" + 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="300" mc:Ignorable="d" + x:CompileBindings="True" + x:DataType="viewModels:TitleUpdateViewModel" Focusable="True"> - <Grid Margin="15"> + <Grid> <Grid.RowDefinitions> - <RowDefinition Height="Auto" /> - <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> - <TextBlock - Name="Heading" - Grid.Row="1" - MaxWidth="500" - Margin="20,15,20,20" - HorizontalAlignment="Center" - VerticalAlignment="Center" - LineHeight="18" - TextAlignment="Center" - TextWrapping="Wrap" /> <Border - Grid.Row="2" - Margin="5" + Grid.Row="0" + Margin="0 0 0 24" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - BorderBrush="Gray" - BorderThickness="1"> - <ScrollViewer - VerticalAlignment="Stretch" - HorizontalScrollBarVisibility="Auto" - VerticalScrollBarVisibility="Auto"> - <ItemsControl - Margin="10" - HorizontalAlignment="Stretch" - VerticalAlignment="Stretch" - Items="{Binding _titleUpdates}"> - <ItemsControl.ItemTemplate> - <DataTemplate> - <RadioButton - Padding="8,0" - VerticalContentAlignment="Center" - GroupName="Update" - IsChecked="{Binding IsEnabled, Mode=TwoWay}"> - <Label - Margin="0" + BorderBrush="{DynamicResource AppListHoverBackgroundColor}" + BorderThickness="1" + CornerRadius="5" + Padding="2.5"> + <ListBox + VirtualizationMode="None" + Background="Transparent" + SelectedItem="{Binding SelectedUpdate, Mode=TwoWay}" + Items="{Binding Views}"> + <ListBox.DataTemplates> + <DataTemplate + DataType="models:TitleUpdateModel"> + <Panel Margin="10"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + TextWrapping="Wrap" + Text="{Binding Label}" /> + <StackPanel + 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" - Content="{Binding Label}" - FontSize="12" /> - </RadioButton> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - </ScrollViewer> + HorizontalAlignment="Right" + Padding="10" + MinWidth="0" + MinHeight="0" + Click="RemoveUpdate"> + <ui:SymbolIcon + Symbol="Cancel" + HorizontalAlignment="Center" + VerticalAlignment="Center" /> + </Button> + </StackPanel> + </Panel> + </DataTemplate> + <DataTemplate + DataType="viewModels:BaseModel"> + <Panel + Height="33" + Margin="10"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + TextWrapping="Wrap" + Text="{locale:Locale NoUpdate}" /> + </Panel> + </DataTemplate> + </ListBox.DataTemplates> + <ListBox.Styles> + <Style Selector="ListBoxItem"> + <Setter Property="Background" Value="Transparent" /> + </Style> + </ListBox.Styles> + </ListBox> </Border> - <DockPanel - Grid.Row="3" - Margin="0" + <Panel + Grid.Row="1" HorizontalAlignment="Stretch"> - <DockPanel Margin="0" HorizontalAlignment="Left"> + <StackPanel + Orientation="Horizontal" + Spacing="10" + HorizontalAlignment="Left"> <Button Name="AddButton" MinWidth="90" - Margin="5" - Command="{Binding Add}"> + Command="{ReflectionBinding Add}"> <TextBlock Text="{locale:Locale SettingsTabGeneralAdd}" /> </Button> <Button - Name="RemoveButton" - MinWidth="90" - Margin="5" - Command="{Binding RemoveSelected}"> - <TextBlock Text="{locale:Locale SettingsTabGeneralRemove}" /> - </Button> - <Button Name="RemoveAllButton" MinWidth="90" - Margin="5" - Command="{Binding RemoveAll}"> + Click="RemoveAll"> <TextBlock Text="{locale:Locale DlcManagerRemoveAllButton}" /> </Button> - </DockPanel> - <DockPanel Margin="0" HorizontalAlignment="Right"> + </StackPanel> + <StackPanel + Orientation="Horizontal" + Spacing="10" + HorizontalAlignment="Right"> <Button Name="SaveButton" MinWidth="90" - Margin="5" - Command="{Binding Save}"> + Click="Save"> <TextBlock Text="{locale:Locale SettingsButtonSave}" /> </Button> <Button Name="CancelButton" MinWidth="90" - Margin="5" - Command="{Binding Close}"> + Click="Close"> <TextBlock Text="{locale:Locale InputDialogCancel}" /> </Button> - </DockPanel> - </DockPanel> + </StackPanel> + </Panel> </Grid> -</window:StyleableWindow>
\ No newline at end of file +</UserControl>
\ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs index 848c5587..9d8b9a7b 100644 --- a/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs @@ -1,271 +1,116 @@ -using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Threading; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ns; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; +using Avalonia.Interactivity; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; -using Ryujinx.Common.Configuration; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using System; -using System.Collections.Generic; +using Ryujinx.Ui.Common.Helper; using System.IO; -using System.Linq; using System.Text; -using Path = System.IO.Path; -using SpanHelpers = LibHac.Common.SpanHelpers; +using System.Threading.Tasks; +using Button = Avalonia.Controls.Button; namespace Ryujinx.Ava.UI.Windows { - public partial class TitleUpdateWindow : StyleableWindow + public partial class TitleUpdateWindow : UserControl { - private readonly string _titleUpdateJsonPath; - private TitleUpdateMetadata _titleUpdateWindowData; - - private VirtualFileSystem _virtualFileSystem { get; } - private AvaloniaList<TitleUpdateModel> _titleUpdates { get; set; } - - private ulong _titleId { get; } - private string _titleName { get; } + public TitleUpdateViewModel ViewModel; public TitleUpdateWindow() { DataContext = this; InitializeComponent(); - - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})"; } public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - _virtualFileSystem = virtualFileSystem; - _titleUpdates = new AvaloniaList<TitleUpdateModel>(); - - _titleId = titleId; - _titleName = titleName; + DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId, titleName); - _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); + InitializeComponent(); + } - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath); - } - catch + public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + ContentDialog contentDialog = new() { - _titleUpdateWindowData = new TitleUpdateMetadata - { - Selected = "", - Paths = new List<string>() - }; - } + PrimaryButtonText = "", + SecondaryButtonText = "", + CloseButtonText = "", + Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName), + Title = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], titleName, titleId.ToString("X16")) + }; - DataContext = this; + Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); + bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); - InitializeComponent(); + contentDialog.Styles.Add(bottomBorder); - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.UpdateWindowTitle]} - {_titleName} ({_titleId:X16})"; - - LoadUpdates(); - PrintHeading(); + await ContentDialogHelper.ShowAsync(contentDialog); } - private void PrintHeading() + private void Close(object sender, RoutedEventArgs e) { - Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], _titleUpdates.Count - 1, _titleName, _titleId.ToString("X16")); + ((ContentDialog)Parent).Hide(); } - private void LoadUpdates() + public void Save(object sender, RoutedEventArgs e) { - _titleUpdates.Add(new TitleUpdateModel(default, string.Empty, true)); + ViewModel._titleUpdateWindowData.Paths.Clear(); - foreach (string path in _titleUpdateWindowData.Paths) - { - AddUpdate(path); - } + ViewModel._titleUpdateWindowData.Selected = ""; - if (_titleUpdateWindowData.Selected == "") - { - _titleUpdates[0].IsEnabled = true; - } - else + foreach (TitleUpdateModel update in ViewModel.TitleUpdates) { - TitleUpdateModel selected = _titleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected); - List<TitleUpdateModel> enabled = _titleUpdates.Where(x => x.IsEnabled).ToList(); - - foreach (TitleUpdateModel update in enabled) - { - update.IsEnabled = false; - } + ViewModel._titleUpdateWindowData.Paths.Add(update.Path); - if (selected != null) + if (update == ViewModel.SelectedUpdate) { - selected.IsEnabled = true; + ViewModel._titleUpdateWindowData.Selected = update.Path; } } - SortUpdates(); - } - - private void AddUpdate(string path) - { - if (File.Exists(path) && !_titleUpdates.Any(x => x.Path == path)) + using (FileStream titleUpdateJsonStream = File.Create(ViewModel._titleUpdateJsonPath, 4096, FileOptions.WriteThrough)) { - using FileStream file = new(path, FileMode.Open, FileAccess.Read); - - try - { - (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); - - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using UniqueRef<IFile> nacpFile = new(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - _titleUpdates.Add(new TitleUpdateModel(controlData, path)); - - foreach (var update in _titleUpdates) - { - update.IsEnabled = false; - } - - _titleUpdates.Last().IsEnabled = true; - } - else - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); - }); - } - } - catch (Exception ex) - { - Dispatcher.UIThread.Post(async () => - { - await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path)); - }); - } + titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(ViewModel._titleUpdateWindowData, true))); } - } - private void RemoveUpdates(bool removeSelectedOnly = false) - { - if (removeSelectedOnly) - { - _titleUpdates.RemoveAll(_titleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList()); - } - else + if (VisualRoot is MainWindow window) { - _titleUpdates.RemoveAll(_titleUpdates.Where(x => !x.IsNoUpdate).ToList()); + window.ViewModel.LoadApplications(); } - _titleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true; - - SortUpdates(); - PrintHeading(); - } - - public void RemoveSelected() - { - RemoveUpdates(true); + ((ContentDialog)Parent).Hide(); } - public void RemoveAll() + private void OpenLocation(object sender, RoutedEventArgs e) { - RemoveUpdates(); - } - - public async void Add() - { - OpenFileDialog dialog = new() + if (sender is Button button) { - Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], - AllowMultiple = true - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" } - }); - - string[] files = await dialog.ShowAsync(this); - - if (files != null) - { - foreach (string file in files) + if (button.DataContext is TitleUpdateModel model) { - AddUpdate(file); + OpenHelper.LocateFile(model.Path); } } - - SortUpdates(); - PrintHeading(); } - private void SortUpdates() + private void RemoveUpdate(object sender, RoutedEventArgs e) { - var list = _titleUpdates.ToList(); - - list.Sort((first, second) => + if (sender is Button button) { - if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString())) - { - return -1; - } - else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString())) - { - return 1; - } - - return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1; - }); - - _titleUpdates.Clear(); - _titleUpdates.AddRange(list); + ViewModel.RemoveUpdate((TitleUpdateModel)button.DataContext); + } } - public void Save() + private void RemoveAll(object sender, RoutedEventArgs e) { - _titleUpdateWindowData.Paths.Clear(); - - _titleUpdateWindowData.Selected = ""; - - foreach (TitleUpdateModel update in _titleUpdates) - { - _titleUpdateWindowData.Paths.Add(update.Path); - - if (update.IsEnabled) - { - _titleUpdateWindowData.Selected = update.Path; - } - } - - using (FileStream titleUpdateJsonStream = File.Create(_titleUpdateJsonPath, 4096, FileOptions.WriteThrough)) - { - titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); - } - - if (Owner is MainWindow window) - { - window.ViewModel.LoadApplications(); - } + ViewModel.TitleUpdates.Clear(); - Close(); + ViewModel.SortUpdates(); } } }
\ No newline at end of file |
