From 862a686c5e8dc62d5fab3d3785b5a57d8d38c4c9 Mon Sep 17 00:00:00 2001 From: GreemDev Date: Mon, 17 Nov 2025 00:15:58 -0600 Subject: [PATCH] UI: Improve "Show Changelog" button in the updater Now it no longer causes the dialog to disappear (which then promptly re-appears so you can click yes/no to accept/deny the update) --- src/Ryujinx/Systems/Updater/Updater.GitLab.cs | 10 +- src/Ryujinx/Systems/Updater/Updater.cs | 18 +-- src/Ryujinx/UI/Helpers/ContentDialogHelper.cs | 139 +++++++++++++++--- src/Ryujinx/UI/Helpers/ControlExtensions.cs | 40 +++++ 4 files changed, 164 insertions(+), 43 deletions(-) create mode 100644 src/Ryujinx/UI/Helpers/ControlExtensions.cs diff --git a/src/Ryujinx/Systems/Updater/Updater.GitLab.cs b/src/Ryujinx/Systems/Updater/Updater.GitLab.cs index 17f01c136..deb515797 100644 --- a/src/Ryujinx/Systems/Updater/Updater.GitLab.cs +++ b/src/Ryujinx/Systems/Updater/Updater.GitLab.cs @@ -88,14 +88,10 @@ namespace Ryujinx.Ava.Systems { if (showVersionUpToDate) { - UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( + await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], - string.Empty); - - if (userResult is UserResult.Ok) - { - OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion)); - } + string.Empty, + _versionResponse.ReleaseUrlFormat.Format(currentVersion)); } Logger.Info?.Print(LogClass.Application, "Up to date."); diff --git a/src/Ryujinx/Systems/Updater/Updater.cs b/src/Ryujinx/Systems/Updater/Updater.cs index a02d3c940..bc45f8ff6 100644 --- a/src/Ryujinx/Systems/Updater/Updater.cs +++ b/src/Ryujinx/Systems/Updater/Updater.cs @@ -60,14 +60,10 @@ namespace Ryujinx.Ava.Systems { if (showVersionUpToDate) { - UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( + await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], - string.Empty); - - if (userResult is UserResult.Ok) - { - OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion)); - } + string.Empty, + changelogUrl: _versionResponse.ReleaseUrlFormat.Format(currentVersion)); } Logger.Info?.Print(LogClass.Application, "Up to date."); @@ -106,22 +102,18 @@ namespace Ryujinx.Ava.Systems Logger.Info?.Print(LogClass.Application, $"Version found: {newVersionString.Replace("→", "->")}"); - RequestUserToUpdate: // Show a message asking the user if they want to update UserResult shouldUpdate = await ContentDialogHelper.CreateUpdaterChoiceDialog( LocaleManager.Instance[LocaleKeys.RyujinxUpdater], LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage], - newVersionString); + newVersionString, + ReleaseInformation.GetChangelogUrl(currentVersion, newVersion)); switch (shouldUpdate) { case UserResult.Yes: await UpdateRyujinx(_versionResponse.ArtifactUrl); break; - // Secondary button maps to no, which in this case is the show changelog button. - case UserResult.No: - OpenHelper.OpenUrl(ReleaseInformation.GetChangelogUrl(currentVersion, newVersion)); - goto RequestUserToUpdate; default: _running = false; break; diff --git a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs index e8730913c..65de07e6e 100644 --- a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs @@ -10,6 +10,7 @@ using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common.Helper; using Ryujinx.Common.Logging; using System; using System.Threading; @@ -102,6 +103,25 @@ namespace Ryujinx.Ava.UI.Helpers return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction); } + + public async static Task ShowTextDialogWithButton( + string title, + string primaryText, + string secondaryText, + string primaryButton, + string secondaryButton, + string closeButton, + int iconSymbol, + string buttonText, + Action onClick, + UserResult primaryButtonResult = UserResult.Ok, + ManualResetEvent deferResetEvent = null, + TypedEventHandler deferCloseAction = null) + { + Grid content = CreateTextDialogContentWithButton(primaryText, secondaryText, iconSymbol, buttonText, onClick); + + return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction); + } public static async Task ShowDeferredContentDialog( Window window, @@ -173,43 +193,109 @@ namespace Ryujinx.Ava.UI.Helpers MinHeight = 80, }; - SymbolIcon icon = new() + content.Children.Add(new SymbolIcon { Symbol = (Symbol)symbol, Margin = new Thickness(10), FontSize = 40, FlowDirection = FlowDirection.LeftToRight, VerticalAlignment = VerticalAlignment.Center, - }; + GridColumn = 0, + GridRow = 0, + GridRowSpan = 2 + }); - Grid.SetColumn(icon, 0); - Grid.SetRowSpan(icon, 2); - Grid.SetRow(icon, 0); - - TextBlock primaryLabel = new() + content.Children.Add(new TextBlock { Text = primaryText, Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, MaxWidth = 450, - }; + GridColumn = 1, + GridRow = 0 + }); - TextBlock secondaryLabel = new() + content.Children.Add(new TextBlock { Text = secondaryText, Margin = new Thickness(5), TextWrapping = TextWrapping.Wrap, MaxWidth = 450, + GridColumn = 1, + GridRow = 1 + }); + + return content; + } + + private static Grid CreateTextDialogContentWithButton(string primaryText, string secondaryText, int symbol, string buttonName, Action onClick) + { + Grid content = new() + { + RowDefinitions = [new(), new(), new(GridLength.Star), new()], + ColumnDefinitions = [new(GridLength.Auto), new()], + + MinHeight = 80, }; - Grid.SetColumn(primaryLabel, 1); - Grid.SetColumn(secondaryLabel, 1); - Grid.SetRow(primaryLabel, 0); - Grid.SetRow(secondaryLabel, 1); + content.Children.Add(new SymbolIcon + { + Symbol = (Symbol)symbol, + Margin = new Thickness(10), + FontSize = 40, + FlowDirection = FlowDirection.LeftToRight, + VerticalAlignment = VerticalAlignment.Center, + GridColumn = 0, + GridRow = 0, + GridRowSpan = 2 + }); - content.Children.Add(icon); - content.Children.Add(primaryLabel); - content.Children.Add(secondaryLabel); + StackPanel buttonContent = new() + { + Orientation = Orientation.Horizontal, + Spacing = 2 + }; + + buttonContent.Children.Add(new TextBlock + { + Text = buttonName, + Margin = new Thickness(2) + }); + + buttonContent.Children.Add(new SymbolIcon + { + FlowDirection = FlowDirection.LeftToRight, + Symbol = Symbol.Open + }); + + content.Children.Add(new TextBlock + { + Text = primaryText, + Margin = new Thickness(5), + TextWrapping = TextWrapping.Wrap, + MaxWidth = 450, + GridColumn = 1, + GridRow = 0 + }); + + content.Children.Add(new TextBlock + { + Text = secondaryText, + Margin = new Thickness(5), + TextWrapping = TextWrapping.Wrap, + MaxWidth = 450, + GridColumn = 1, + GridRow = 1 + }); + + content.Children.Add(new Button + { + Content = buttonContent, + HorizontalAlignment = HorizontalAlignment.Center, + Command = Commands.Create(onClick), + GridRow = 2, + GridColumnSpan = 2, + }); return content; } @@ -282,15 +368,20 @@ namespace Ryujinx.Ava.UI.Helpers LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Important); - internal static async Task CreateUpdaterUpToDateInfoDialog(string primary, string secondaryText) - => await ShowTextDialog( + internal static async Task CreateUpdaterUpToDateInfoDialog(string primary, string secondaryText, + string changelogUrl) + { + await ShowTextDialogWithButton( LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle], primary, secondaryText, - LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage], + string.Empty, string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], - (int)Symbol.Important); + (int)Symbol.Important, + LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage], + () => OpenHelper.OpenUrl(changelogUrl)); + } internal static async Task CreateWarningDialog(string primary, string secondaryText) => await ShowTextDialog( @@ -340,7 +431,7 @@ namespace Ryujinx.Ava.UI.Helpers return response == UserResult.Yes; } - internal static async Task CreateUpdaterChoiceDialog(string title, string primary, string secondaryText) + internal static async Task CreateUpdaterChoiceDialog(string title, string primary, string secondaryText, string changelogUrl) { if (_isChoiceDialogOpen) { @@ -349,14 +440,16 @@ namespace Ryujinx.Ava.UI.Helpers _isChoiceDialogOpen = true; - UserResult response = await ShowTextDialog( + UserResult response = await ShowTextDialogWithButton( title, primary, secondaryText, LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage], + string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogNo], (int)Symbol.Help, + LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage], + () => OpenHelper.OpenUrl(changelogUrl), UserResult.Yes); _isChoiceDialogOpen = false; diff --git a/src/Ryujinx/UI/Helpers/ControlExtensions.cs b/src/Ryujinx/UI/Helpers/ControlExtensions.cs new file mode 100644 index 000000000..734128757 --- /dev/null +++ b/src/Ryujinx/UI/Helpers/ControlExtensions.cs @@ -0,0 +1,40 @@ +using Avalonia.Controls; + +namespace Ryujinx.Ava.UI.Helpers +{ + public static class ControlExtensions + { + extension(Control ctrl) + { + public int GridRow + { + get => Grid.GetRow(ctrl); + set => Grid.SetRow(ctrl, value); + } + + public int GridColumn + { + get => Grid.GetColumn(ctrl); + set => Grid.SetColumn(ctrl, value); + } + + public int GridRowSpan + { + get => Grid.GetRowSpan(ctrl); + set => Grid.SetRowSpan(ctrl, value); + } + + public int GridColumnSpan + { + get => Grid.GetColumnSpan(ctrl); + set => Grid.SetColumnSpan(ctrl, value); + } + + public bool GridIsSharedSizeScope + { + get => Grid.GetIsSharedSizeScope(ctrl); + set => Grid.SetIsSharedSizeScope(ctrl, value); + } + } + } +}