From ec6cb0abb4b7669895b6e96fd7581c93b5abd691 Mon Sep 17 00:00:00 2001 From: Mary Guillemard Date: Sat, 2 Mar 2024 12:51:05 +0100 Subject: infra: Make Avalonia the default UI (#6375) * misc: Move Ryujinx project to Ryujinx.Gtk3 This breaks release CI for now but that's fine. Signed-off-by: Mary Guillemard * misc: Move Ryujinx.Ava project to Ryujinx This breaks CI for now, but it's fine. Signed-off-by: Mary Guillemard * infra: Make Avalonia the default UI Should fix CI after the previous changes. GTK3 isn't build by the release job anymore, only by PR CI. This also ensure that the test-ava update package is still generated to allow update from the old testing channel. Signed-off-by: Mary Guillemard * Fix missing copy in create_app_bundle.sh Signed-off-by: Mary Guillemard * Fix syntax error Signed-off-by: Mary Guillemard --------- Signed-off-by: Mary Guillemard --- src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml | 270 ---------- src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs | 63 --- src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml | 75 --- src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs | 60 --- src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml | 126 ----- src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs | 123 ----- .../UI/Windows/ContentDialogOverlayWindow.axaml | 25 - .../UI/Windows/ContentDialogOverlayWindow.axaml.cs | 21 - .../Windows/DownloadableContentManagerWindow.axaml | 192 ------- .../DownloadableContentManagerWindow.axaml.cs | 115 ----- src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs | 194 -------- src/Ryujinx.Ava/UI/Windows/MainWindow.axaml | 205 -------- src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs | 551 --------------------- src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml | 179 ------- .../UI/Windows/ModManagerWindow.axaml.cs | 139 ------ src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml | 130 ----- src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs | 103 ---- src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs | 44 -- src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml | 133 ----- .../UI/Windows/TitleUpdateWindow.axaml.cs | 96 ---- 20 files changed, 2844 deletions(-) delete mode 100644 src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/MainWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/StyleableWindow.cs delete mode 100644 src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml delete mode 100644 src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs (limited to 'src/Ryujinx.Ava/UI/Windows') diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml deleted file mode 100644 index 69fa8251..00000000 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml +++ /dev/null @@ -1,270 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs deleted file mode 100644 index c32661b0..00000000 --- a/src/Ryujinx.Ava/UI/Windows/AboutWindow.axaml.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Interactivity; -using Avalonia.Layout; -using Avalonia.Styling; -using FluentAvalonia.UI.Controls; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Helpers; -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 AboutWindow : UserControl - { - public AboutWindow() - { - DataContext = new AboutWindowViewModel(); - - InitializeComponent(); - } - - public static async Task Show() - { - ContentDialog contentDialog = new() - { - PrimaryButtonText = "", - SecondaryButtonText = "", - CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], - Content = new AboutWindow(), - }; - - Style closeButton = new(x => x.Name("CloseButton")); - closeButton.Setters.Add(new Setter(WidthProperty, 80d)); - - Style closeButtonParent = new(x => x.Name("CommandSpace")); - closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Right)); - - contentDialog.Styles.Add(closeButton); - contentDialog.Styles.Add(closeButtonParent); - - await ContentDialogHelper.ShowAsync(contentDialog); - } - - private void Button_OnClick(object sender, RoutedEventArgs e) - { - if (sender is Button button) - { - OpenHelper.OpenUrl(button.Tag.ToString()); - } - } - - private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e) - { - if (sender is TextBlock) - { - OpenHelper.OpenUrl("https://amiiboapi.com"); - } - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml b/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml deleted file mode 100644 index c587aa87..00000000 --- a/src/Ryujinx.Ava/UI/Windows/AmiiboWindow.axaml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs deleted file mode 100644 index d78e48a4..00000000 --- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Avalonia.Collections; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.UI.App.Common; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; - -namespace Ryujinx.Ava.UI.Windows -{ - public partial class CheatWindow : StyleableWindow - { - private readonly string _enabledCheatsPath; - public bool NoCheatsFound { get; } - - public AvaloniaList LoadedCheats { get; } - - public string Heading { get; } - public string BuildId { get; } - - public CheatWindow() - { - DataContext = this; - - InitializeComponent(); - - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle]; - } - - public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath) - { - LoadedCheats = new AvaloniaList(); - - Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper()); - BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath); - - InitializeComponent(); - - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, titleId); - ulong titleIdValue = ulong.Parse(titleId, NumberStyles.HexNumber); - - _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); - - string[] enabled = Array.Empty(); - - if (File.Exists(_enabledCheatsPath)) - { - enabled = File.ReadAllLines(_enabledCheatsPath); - } - - int cheatAdded = 0; - - var mods = new ModLoader.ModCache(); - - ModLoader.QueryContentsDir(mods, new DirectoryInfo(Path.Combine(modsBasePath, "contents")), titleIdValue); - - string currentCheatFile = string.Empty; - string buildId = string.Empty; - - CheatNode currentGroup = null; - - foreach (var cheat in mods.Cheats) - { - if (cheat.Path.FullName != currentCheatFile) - { - currentCheatFile = cheat.Path.FullName; - string parentPath = currentCheatFile.Replace(titleModsPath, ""); - - buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper(); - currentGroup = new CheatNode("", buildId, parentPath, true); - - LoadedCheats.Add(currentGroup); - } - - var model = new CheatNode(cheat.Name, buildId, "", false, enabled.Contains($"{buildId}-{cheat.Name}")); - currentGroup?.SubNodes.Add(model); - - cheatAdded++; - } - - if (cheatAdded == 0) - { - NoCheatsFound = true; - } - - DataContext = this; - - Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle]; - } - - public void Save() - { - if (NoCheatsFound) - { - return; - } - - List enabledCheats = new(); - - foreach (var cheats in LoadedCheats) - { - foreach (var cheat in cheats.SubNodes) - { - if (cheat.IsEnabled) - { - enabledCheats.Add(cheat.BuildIdKey); - } - } - } - - Directory.CreateDirectory(Path.GetDirectoryName(_enabledCheatsPath)); - - File.WriteAllLines(_enabledCheatsPath, enabledCheats); - - Close(); - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml deleted file mode 100644 index 8b52bade..00000000 --- a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs deleted file mode 100644 index 2b12d72f..00000000 --- a/src/Ryujinx.Ava/UI/Windows/ContentDialogOverlayWindow.axaml.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Media; - -namespace Ryujinx.Ava.UI.Windows -{ - public partial class ContentDialogOverlayWindow : StyleableWindow - { - public ContentDialogOverlayWindow() - { - InitializeComponent(); - - ExtendClientAreaToDecorationsHint = true; - TransparencyLevelHint = new[] { WindowTransparencyLevel.Transparent }; - WindowStartupLocation = WindowStartupLocation.Manual; - SystemDecorations = SystemDecorations.None; - ExtendClientAreaTitleBarHeightHint = 0; - Background = Brushes.Transparent; - CanResize = false; - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml deleted file mode 100644 index 99cf28e7..00000000 --- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs deleted file mode 100644 index 0c02fa0f..00000000 --- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ /dev/null @@ -1,115 +0,0 @@ -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.HLE.FileSystem; -using Ryujinx.UI.Common.Helper; -using System.Threading.Tasks; -using Button = Avalonia.Controls.Button; - -namespace Ryujinx.Ava.UI.Windows -{ - public partial class DownloadableContentManagerWindow : UserControl - { - public DownloadableContentManagerViewModel ViewModel; - - public DownloadableContentManagerWindow() - { - DataContext = this; - - InitializeComponent(); - } - - public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId) - { - DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId); - - InitializeComponent(); - } - - public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) - { - ContentDialog contentDialog = new() - { - PrimaryButtonText = "", - SecondaryButtonText = "", - CloseButtonText = "", - Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId), - Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")), - }; - - Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); - bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); - - contentDialog.Styles.Add(bottomBorder); - - await ContentDialogHelper.ShowAsync(contentDialog); - } - - private void SaveAndClose(object sender, RoutedEventArgs routedEventArgs) - { - ViewModel.Save(); - ((ContentDialog)Parent).Hide(); - } - - private void Close(object sender, RoutedEventArgs e) - { - ((ContentDialog)Parent).Hide(); - } - - private void RemoveDLC(object sender, RoutedEventArgs e) - { - if (sender is Button button) - { - if (button.DataContext is DownloadableContentModel model) - { - ViewModel.Remove(model); - } - } - } - - private void OpenLocation(object sender, RoutedEventArgs e) - { - if (sender is Button button) - { - if (button.DataContext is DownloadableContentModel model) - { - OpenHelper.LocateFile(model.ContainerPath); - } - } - } - - private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) - { - foreach (var content in e.AddedItems) - { - if (content is DownloadableContentModel model) - { - var index = ViewModel.DownloadableContents.IndexOf(model); - - if (index != -1) - { - ViewModel.DownloadableContents[index].Enabled = true; - } - } - } - - foreach (var content in e.RemovedItems) - { - if (content is DownloadableContentModel model) - { - var index = ViewModel.DownloadableContents.IndexOf(model); - - if (index != -1) - { - ViewModel.DownloadableContents[index].Enabled = false; - } - } - } - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs b/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs deleted file mode 100644 index 4c75a5ff..00000000 --- a/src/Ryujinx.Ava/UI/Windows/IconColorPicker.cs +++ /dev/null @@ -1,194 +0,0 @@ -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Ava.UI.Windows -{ - static class IconColorPicker - { - private const int ColorsPerLine = 64; - private const int TotalColors = ColorsPerLine * ColorsPerLine; - - private const int UvQuantBits = 3; - private const int UvQuantShift = BitsPerComponent - UvQuantBits; - - private const int SatQuantBits = 5; - private const int SatQuantShift = BitsPerComponent - SatQuantBits; - - private const int BitsPerComponent = 8; - - private const int CutOffLuminosity = 64; - - private readonly struct PaletteColor - { - public int Qck { get; } - public byte R { get; } - public byte G { get; } - public byte B { get; } - - public PaletteColor(int qck, byte r, byte g, byte b) - { - Qck = qck; - R = r; - G = g; - B = b; - } - } - - public static Color GetFilteredColor(Image image) - { - var color = GetColor(image).ToPixel(); - - // We don't want colors that are too dark. - // If the color is too dark, make it brighter by reducing the range - // and adding a constant color. - int luminosity = GetColorApproximateLuminosity(color.R, color.G, color.B); - if (luminosity < CutOffLuminosity) - { - color = Color.FromRgb( - (byte)Math.Min(CutOffLuminosity + color.R, byte.MaxValue), - (byte)Math.Min(CutOffLuminosity + color.G, byte.MaxValue), - (byte)Math.Min(CutOffLuminosity + color.B, byte.MaxValue)); - } - - return color; - } - - public static Color GetColor(Image image) - { - var colors = new PaletteColor[TotalColors]; - - var dominantColorBin = new Dictionary(); - - var buffer = GetBuffer(image); - - int w = image.Width; - - int w8 = w << 8; - int h8 = image.Height << 8; - -#pragma warning disable IDE0059 // Unnecessary assignment - int xStep = w8 / ColorsPerLine; - int yStep = h8 / ColorsPerLine; -#pragma warning restore IDE0059 - - int i = 0; - int maxHitCount = 0; - - for (int y = 0; y < image.Height; y++) - { - int yOffset = y * image.Width; - - for (int x = 0; x < image.Width && i < TotalColors; x++) - { - int offset = x + yOffset; - - byte cb = buffer[offset].B; - byte cg = buffer[offset].G; - byte cr = buffer[offset].R; - - var qck = GetQuantizedColorKey(cr, cg, cb); - - if (dominantColorBin.TryGetValue(qck, out int hitCount)) - { - dominantColorBin[qck] = hitCount + 1; - - if (maxHitCount < hitCount) - { - maxHitCount = hitCount; - } - } - else - { - dominantColorBin.Add(qck, 1); - } - - colors[i++] = new PaletteColor(qck, cr, cg, cb); - } - } - - int highScore = -1; - PaletteColor bestCandidate = default; - - for (i = 0; i < TotalColors; i++) - { - var score = GetColorScore(dominantColorBin, maxHitCount, colors[i]); - - if (highScore < score) - { - highScore = score; - bestCandidate = colors[i]; - } - } - - return Color.FromRgb(bestCandidate.R, bestCandidate.G, bestCandidate.B); - } - - public static Bgra32[] GetBuffer(Image image) - { - return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : Array.Empty(); - } - - private static int GetColorScore(Dictionary dominantColorBin, int maxHitCount, PaletteColor color) - { - var hitCount = dominantColorBin[color.Qck]; - var balancedHitCount = BalanceHitCount(hitCount, maxHitCount); - var quantSat = (GetColorSaturation(color) >> SatQuantShift) << SatQuantShift; - var value = GetColorValue(color); - - // If the color is rarely used on the image, - // then chances are that theres a better candidate, even if the saturation value - // is high. By multiplying the saturation value with a weight, we can lower - // it if the color is almost never used (hit count is low). - var satWeighted = quantSat; - var satWeight = balancedHitCount << 5; - if (satWeight < 0x100) - { - satWeighted = (satWeighted * satWeight) >> 8; - } - - // Compute score from saturation and dominance of the color. - // We prefer more vivid colors over dominant ones, so give more weight to the saturation. - var score = ((satWeighted << 1) + balancedHitCount) * value; - - return score; - } - - private static int BalanceHitCount(int hitCount, int maxHitCount) - { - return (hitCount << 8) / maxHitCount; - } - - private static int GetColorApproximateLuminosity(byte r, byte g, byte b) - { - return (r + g + b) / 3; - } - - private static int GetColorSaturation(PaletteColor color) - { - int cMax = Math.Max(Math.Max(color.R, color.G), color.B); - - if (cMax == 0) - { - return 0; - } - - int cMin = Math.Min(Math.Min(color.R, color.G), color.B); - int delta = cMax - cMin; - return (delta << 8) / cMax; - } - - private static int GetColorValue(PaletteColor color) - { - return Math.Max(Math.Max(color.R, color.G), color.B); - } - - private static int GetQuantizedColorKey(byte r, byte g, byte b) - { - int u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; - int v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; - return (v >> UvQuantShift) | ((u >> UvQuantShift) << UvQuantBits); - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml deleted file mode 100644 index 4def7c28..00000000 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs deleted file mode 100644 index 33a9af5b..00000000 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ /dev/null @@ -1,551 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Primitives; -using Avalonia.Interactivity; -using Avalonia.Threading; -using FluentAvalonia.UI.Controls; -using Ryujinx.Ava.Common; -using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.Input; -using Ryujinx.Ava.UI.Applet; -using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Gpu; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.Input.HLE; -using Ryujinx.Input.SDL2; -using Ryujinx.Modules; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using System; -using System.IO; -using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; - -namespace Ryujinx.Ava.UI.Windows -{ - public partial class MainWindow : StyleableWindow - { - internal static MainWindowViewModel MainWindowViewModel { get; private set; } - - private bool _isLoading; - - private UserChannelPersistence _userChannelPersistence; - private static bool _deferLoad; - private static string _launchPath; - private static bool _startFullscreen; - internal readonly AvaHostUIHandler UiHandler; - - public VirtualFileSystem VirtualFileSystem { get; private set; } - public ContentManager ContentManager { get; private set; } - public AccountManager AccountManager { get; private set; } - - public LibHacHorizonManager LibHacHorizonManager { get; private set; } - - public InputManager InputManager { get; private set; } - - internal MainWindowViewModel ViewModel { get; private set; } - public SettingsWindow SettingsWindow { get; set; } - - public static bool ShowKeyErrorOnLoad { get; set; } - public ApplicationLibrary ApplicationLibrary { get; set; } - - public MainWindow() - { - ViewModel = new MainWindowViewModel(); - - MainWindowViewModel = ViewModel; - - DataContext = ViewModel; - - SetWindowSizePosition(); - - InitializeComponent(); - Load(); - - UiHandler = new AvaHostUIHandler(this); - - ViewModel.Title = $"Ryujinx {Program.Version}"; - - // NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point. - double barHeight = MenuBar.MinHeight + StatusBarView.StatusBar.MinHeight; - Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight; - Width /= Program.WindowScaleFactor; - - if (Program.PreviewerDetached) - { - InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); - - this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged); - this.ScalingChanged += OnScalingChanged; - } - } - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - - NotificationHelper.SetNotificationManager(this); - } - - private void IsActiveChanged(bool obj) - { - ViewModel.IsActive = obj; - } - - private void OnScalingChanged(object sender, EventArgs e) - { - Program.DesktopScaleFactor = this.RenderScaling; - } - - private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e) - { - Dispatcher.UIThread.Post(() => - { - ViewModel.Applications.Add(e.AppData); - }); - } - - private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) - { - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound); - - Dispatcher.UIThread.Post(() => - { - ViewModel.StatusBarProgressValue = e.NumAppsLoaded; - ViewModel.StatusBarProgressMaximum = e.NumAppsFound; - - if (e.NumAppsFound == 0) - { - StatusBarView.LoadProgressBar.IsVisible = false; - } - - if (e.NumAppsLoaded == e.NumAppsFound) - { - StatusBarView.LoadProgressBar.IsVisible = false; - } - }); - } - - public void Application_Opened(object sender, ApplicationOpenedEventArgs args) - { - if (args.Application != null) - { - ViewModel.SelectedIcon = args.Application.Icon; - - string path = new FileInfo(args.Application.Path).FullName; - - ViewModel.LoadApplication(path).Wait(); - } - - args.Handled = true; - } - - internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg) - { - _deferLoad = true; - _launchPath = launchPathArg; - _startFullscreen = startFullscreenArg; - } - - public void SwitchToGameControl(bool startFullscreen = false) - { - ViewModel.ShowLoadProgress = false; - ViewModel.ShowContent = true; - ViewModel.IsLoadingIndeterminate = false; - - if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen) - { - ViewModel.ToggleFullscreen(); - } - } - - public void ShowLoading(bool startFullscreen = false) - { - ViewModel.ShowContent = false; - ViewModel.ShowLoadProgress = true; - ViewModel.IsLoadingIndeterminate = true; - - if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen) - { - ViewModel.ToggleFullscreen(); - } - } - - private void Initialize() - { - _userChannelPersistence = new UserChannelPersistence(); - VirtualFileSystem = VirtualFileSystem.CreateInstance(); - LibHacHorizonManager = new LibHacHorizonManager(); - ContentManager = new ContentManager(VirtualFileSystem); - - LibHacHorizonManager.InitializeFsServer(VirtualFileSystem); - LibHacHorizonManager.InitializeArpServer(); - LibHacHorizonManager.InitializeBcatServer(); - LibHacHorizonManager.InitializeSystemClients(); - - ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem); - - // Save data created before we supported extra data in directory save data will not work properly if - // given empty extra data. Luckily some of that extra data can be created using the data from the - // save data indexer, which should be enough to check access permissions for user saves. - // Every single save data's extra data will be checked and fixed if needed each time the emulator is opened. - // Consider removing this at some point in the future when we don't need to worry about old saves. - VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient); - - AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile); - - VirtualFileSystem.ReloadKeySet(); - - ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient); - } - - [SupportedOSPlatform("linux")] - private static async Task ShowVmMaxMapCountWarning() - { - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary, - LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount); - - await ContentDialogHelper.CreateWarningDialog( - LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextPrimary], - LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary] - ); - } - - [SupportedOSPlatform("linux")] - private static async Task ShowVmMaxMapCountDialog() - { - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary, - LinuxHelper.RecommendedVmMaxMapCount); - - UserResult response = await ContentDialogHelper.ShowTextDialog( - $"Ryujinx - {LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTitle]}", - LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary], - LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextSecondary], - LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonUntilRestart], - LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonPersistent], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - (int)Symbol.Help - ); - - int rc; - - switch (response) - { - case UserResult.Ok: - rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}"); - if (rc == 0) - { - Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart."); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}"); - } - break; - case UserResult.No: - rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}"); - if (rc == 0) - { - Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}"); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}"); - } - break; - } - } - - private async Task CheckLaunchState() - { - if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) - { - Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})"); - - if (LinuxHelper.PkExecPath is not null) - { - await Dispatcher.UIThread.InvokeAsync(ShowVmMaxMapCountDialog); - } - else - { - await Dispatcher.UIThread.InvokeAsync(ShowVmMaxMapCountWarning); - } - } - - if (!ShowKeyErrorOnLoad) - { - if (_deferLoad) - { - _deferLoad = false; - - ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait(); - } - } - else - { - ShowKeyErrorOnLoad = false; - - await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys)); - } - - if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) - { - await Updater.BeginParse(this, false).ContinueWith(task => - { - Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - } - - private void Load() - { - StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged; - - ApplicationGrid.ApplicationOpened += Application_Opened; - - ApplicationGrid.DataContext = ViewModel; - - ApplicationList.ApplicationOpened += Application_Opened; - - ApplicationList.DataContext = ViewModel; - } - - private void SetWindowSizePosition() - { - PixelPoint savedPoint = new(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX, - ConfigurationState.Instance.UI.WindowStartup.WindowPositionY); - - ViewModel.WindowHeight = ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor; - ViewModel.WindowWidth = ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor; - - ViewModel.WindowState = ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal; - - if (CheckScreenBounds(savedPoint)) - { - Position = savedPoint; - } - else - { - WindowStartupLocation = WindowStartupLocation.CenterScreen; - } - } - - private bool CheckScreenBounds(PixelPoint configPoint) - { - for (int i = 0; i < Screens.ScreenCount; i++) - { - if (Screens.All[i].Bounds.Contains(configPoint)) - { - return true; - } - } - - Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center."); - return false; - } - - private void SaveWindowSizePosition() - { - ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = (int)Height; - ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = (int)Width; - - ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = Position.X; - ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = Position.Y; - - ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized; - - MainWindowViewModel.SaveConfig(); - } - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - - Initialize(); - - ViewModel.Initialize( - ContentManager, - StorageProvider, - ApplicationLibrary, - VirtualFileSystem, - AccountManager, - InputManager, - _userChannelPersistence, - LibHacHorizonManager, - UiHandler, - ShowLoading, - SwitchToGameControl, - SetMainContent, - this); - - ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; - ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; - - ViewModel.RefreshFirmwareStatus(); - - LoadApplications(); - -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - CheckLaunchState(); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - - private void SetMainContent(Control content = null) - { - content ??= GameLibrary; - - if (MainContent.Content != content) - { - MainContent.Content = content; - } - } - - public static void UpdateGraphicsConfig() - { -#pragma warning disable IDE0055 // Disable formatting - GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; - GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; - GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; - GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; - GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; - GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; -#pragma warning restore IDE0055 - } - - private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e) - { - var volumeSplitButton = sender as ToggleSplitButton; - if (ViewModel.IsGameRunning) - { - if (!volumeSplitButton.IsChecked) - { - ViewModel.AppHost.Device.SetVolume(ViewModel.VolumeBeforeMute); - } - else - { - ViewModel.VolumeBeforeMute = ViewModel.AppHost.Device.GetVolume(); - ViewModel.AppHost.Device.SetVolume(0); - } - - ViewModel.Volume = ViewModel.AppHost.Device.GetVolume(); - } - } - - protected override void OnClosing(WindowClosingEventArgs e) - { - if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit) - { - e.Cancel = true; - - ConfirmExit(); - - return; - } - - ViewModel.IsClosing = true; - - if (ViewModel.AppHost != null) - { - ViewModel.AppHost.AppExit -= ViewModel.AppHost_AppExit; - ViewModel.AppHost.AppExit += (sender, e) => - { - ViewModel.AppHost = null; - - Dispatcher.UIThread.Post(() => - { - MainContent = null; - - Close(); - }); - }; - ViewModel.AppHost?.Stop(); - - e.Cancel = true; - - return; - } - - SaveWindowSizePosition(); - - ApplicationLibrary.CancelLoading(); - InputManager.Dispose(); - Program.Exit(); - - base.OnClosing(e); - } - - private void ConfirmExit() - { - Dispatcher.UIThread.InvokeAsync(async () => - { - ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog(); - - if (ViewModel.IsClosing) - { - Close(); - } - }); - } - - public void LoadApplications() - { - ViewModel.Applications.Clear(); - - StatusBarView.LoadProgressBar.IsVisible = true; - ViewModel.StatusBarProgressMaximum = 0; - ViewModel.StatusBarProgressValue = 0; - - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); - - ReloadGameList(); - } - - public void ToggleFileType(string fileType) - { - _ = fileType switch - { -#pragma warning disable IDE0055 // Disable formatting - "NSP" => ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSP, - "PFS0" => ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.UI.ShownFileTypes.PFS0, - "XCI" => ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.UI.ShownFileTypes.XCI, - "NCA" => ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NCA, - "NRO" => ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NRO, - "NSO" => ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSO, - _ => throw new ArgumentOutOfRangeException(fileType), -#pragma warning restore IDE0055 - }; - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - LoadApplications(); - } - - private void ReloadGameList() - { - if (_isLoading) - { - return; - } - - _isLoading = true; - - Thread applicationLibraryThread = new(() => - { - ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language); - - _isLoading = false; - }) - { - Name = "GUI.ApplicationLibraryThread", - IsBackground = true, - }; - applicationLibraryThread.Start(); - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml deleted file mode 100644 index 0ed05ce3..00000000 --- a/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs deleted file mode 100644 index d9ae0d4f..00000000 --- a/src/Ryujinx.Ava/UI/Windows/ModManagerWindow.axaml.cs +++ /dev/null @@ -1,139 +0,0 @@ -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().Name("DialogSpace").Child().OfType()); - 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; - } - } - } - } - } -} diff --git a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml deleted file mode 100644 index 40cac90d..00000000 --- a/src/Ryujinx.Ava/UI/Windows/SettingsWindow.axaml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs deleted file mode 100644 index f3ac6960..00000000 --- a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs +++ /dev/null @@ -1,96 +0,0 @@ -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.HLE.FileSystem; -using Ryujinx.UI.Common.Helper; -using System.Threading.Tasks; -using Button = Avalonia.Controls.Button; - -namespace Ryujinx.Ava.UI.Windows -{ - public partial class TitleUpdateWindow : UserControl - { - public TitleUpdateViewModel ViewModel; - - public TitleUpdateWindow() - { - DataContext = this; - - InitializeComponent(); - } - - public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId) - { - DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId); - - InitializeComponent(); - } - - public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) - { - ContentDialog contentDialog = new() - { - PrimaryButtonText = "", - SecondaryButtonText = "", - CloseButtonText = "", - Content = new TitleUpdateWindow(virtualFileSystem, titleId), - Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")), - }; - - Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); - bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); - - contentDialog.Styles.Add(bottomBorder); - - await ContentDialogHelper.ShowAsync(contentDialog); - } - - private void Close(object sender, RoutedEventArgs e) - { - ((ContentDialog)Parent).Hide(); - } - - public void Save(object sender, RoutedEventArgs e) - { - ViewModel.Save(); - - if (VisualRoot is MainWindow window) - { - window.LoadApplications(); - } - - ((ContentDialog)Parent).Hide(); - } - - private void OpenLocation(object sender, RoutedEventArgs e) - { - if (sender is Button button) - { - if (button.DataContext is TitleUpdateModel model) - { - OpenHelper.LocateFile(model.Path); - } - } - } - - private void RemoveUpdate(object sender, RoutedEventArgs e) - { - if (sender is Button button) - { - ViewModel.RemoveUpdate((TitleUpdateModel)button.DataContext); - } - } - - private void RemoveAll(object sender, RoutedEventArgs e) - { - ViewModel.TitleUpdates.Clear(); - - ViewModel.SortUpdates(); - } - } -} -- cgit v1.2.3