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)
This commit is contained in:
GreemDev 2025-11-17 00:15:58 -06:00
parent e8751e1c40
commit 862a686c5e
4 changed files with 164 additions and 43 deletions

View file

@ -88,14 +88,10 @@ namespace Ryujinx.Ava.Systems
{ {
if (showVersionUpToDate) if (showVersionUpToDate)
{ {
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
string.Empty); string.Empty,
_versionResponse.ReleaseUrlFormat.Format(currentVersion));
if (userResult is UserResult.Ok)
{
OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion));
}
} }
Logger.Info?.Print(LogClass.Application, "Up to date."); Logger.Info?.Print(LogClass.Application, "Up to date.");

View file

@ -60,14 +60,10 @@ namespace Ryujinx.Ava.Systems
{ {
if (showVersionUpToDate) if (showVersionUpToDate)
{ {
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
string.Empty); string.Empty,
changelogUrl: _versionResponse.ReleaseUrlFormat.Format(currentVersion));
if (userResult is UserResult.Ok)
{
OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion));
}
} }
Logger.Info?.Print(LogClass.Application, "Up to date."); 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("", "->")}"); Logger.Info?.Print(LogClass.Application, $"Version found: {newVersionString.Replace("", "->")}");
RequestUserToUpdate:
// Show a message asking the user if they want to update // Show a message asking the user if they want to update
UserResult shouldUpdate = await ContentDialogHelper.CreateUpdaterChoiceDialog( UserResult shouldUpdate = await ContentDialogHelper.CreateUpdaterChoiceDialog(
LocaleManager.Instance[LocaleKeys.RyujinxUpdater], LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage], LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
newVersionString); newVersionString,
ReleaseInformation.GetChangelogUrl(currentVersion, newVersion));
switch (shouldUpdate) switch (shouldUpdate)
{ {
case UserResult.Yes: case UserResult.Yes:
await UpdateRyujinx(_versionResponse.ArtifactUrl); await UpdateRyujinx(_versionResponse.ArtifactUrl);
break; 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: default:
_running = false; _running = false;
break; break;

View file

@ -10,6 +10,7 @@ using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Threading; using System.Threading;
@ -103,6 +104,25 @@ namespace Ryujinx.Ava.UI.Helpers
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction); return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction);
} }
public async static Task<UserResult> 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<ContentDialog, ContentDialogButtonClickEventArgs> 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<UserResult> ShowDeferredContentDialog( public static async Task<UserResult> ShowDeferredContentDialog(
Window window, Window window,
string title, string title,
@ -173,43 +193,109 @@ namespace Ryujinx.Ava.UI.Helpers
MinHeight = 80, MinHeight = 80,
}; };
SymbolIcon icon = new() content.Children.Add(new SymbolIcon
{ {
Symbol = (Symbol)symbol, Symbol = (Symbol)symbol,
Margin = new Thickness(10), Margin = new Thickness(10),
FontSize = 40, FontSize = 40,
FlowDirection = FlowDirection.LeftToRight, FlowDirection = FlowDirection.LeftToRight,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
}; GridColumn = 0,
GridRow = 0,
GridRowSpan = 2
});
Grid.SetColumn(icon, 0); content.Children.Add(new TextBlock
Grid.SetRowSpan(icon, 2);
Grid.SetRow(icon, 0);
TextBlock primaryLabel = new()
{ {
Text = primaryText, Text = primaryText,
Margin = new Thickness(5), Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
MaxWidth = 450, MaxWidth = 450,
}; GridColumn = 1,
GridRow = 0
});
TextBlock secondaryLabel = new() content.Children.Add(new TextBlock
{ {
Text = secondaryText, Text = secondaryText,
Margin = new Thickness(5), Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
MaxWidth = 450, 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); content.Children.Add(new SymbolIcon
Grid.SetColumn(secondaryLabel, 1); {
Grid.SetRow(primaryLabel, 0); Symbol = (Symbol)symbol,
Grid.SetRow(secondaryLabel, 1); Margin = new Thickness(10),
FontSize = 40,
FlowDirection = FlowDirection.LeftToRight,
VerticalAlignment = VerticalAlignment.Center,
GridColumn = 0,
GridRow = 0,
GridRowSpan = 2
});
content.Children.Add(icon); StackPanel buttonContent = new()
content.Children.Add(primaryLabel); {
content.Children.Add(secondaryLabel); 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; return content;
} }
@ -282,15 +368,20 @@ namespace Ryujinx.Ava.UI.Helpers
LocaleManager.Instance[LocaleKeys.InputDialogOk], LocaleManager.Instance[LocaleKeys.InputDialogOk],
(int)Symbol.Important); (int)Symbol.Important);
internal static async Task<UserResult> CreateUpdaterUpToDateInfoDialog(string primary, string secondaryText) internal static async Task CreateUpdaterUpToDateInfoDialog(string primary, string secondaryText,
=> await ShowTextDialog( string changelogUrl)
{
await ShowTextDialogWithButton(
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
primary, primary,
secondaryText, secondaryText,
LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage], string.Empty,
string.Empty, string.Empty,
LocaleManager.Instance[LocaleKeys.InputDialogOk], 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) internal static async Task CreateWarningDialog(string primary, string secondaryText)
=> await ShowTextDialog( => await ShowTextDialog(
@ -340,7 +431,7 @@ namespace Ryujinx.Ava.UI.Helpers
return response == UserResult.Yes; return response == UserResult.Yes;
} }
internal static async Task<UserResult> CreateUpdaterChoiceDialog(string title, string primary, string secondaryText) internal static async Task<UserResult> CreateUpdaterChoiceDialog(string title, string primary, string secondaryText, string changelogUrl)
{ {
if (_isChoiceDialogOpen) if (_isChoiceDialogOpen)
{ {
@ -349,14 +440,16 @@ namespace Ryujinx.Ava.UI.Helpers
_isChoiceDialogOpen = true; _isChoiceDialogOpen = true;
UserResult response = await ShowTextDialog( UserResult response = await ShowTextDialogWithButton(
title, title,
primary, primary,
secondaryText, secondaryText,
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage], string.Empty,
LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.InputDialogNo],
(int)Symbol.Help, (int)Symbol.Help,
LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage],
() => OpenHelper.OpenUrl(changelogUrl),
UserResult.Yes); UserResult.Yes);
_isChoiceDialogOpen = false; _isChoiceDialogOpen = false;

View file

@ -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);
}
}
}
}