reorganize RyujinxSetupWizard

additionally, the CreateHelpContent method is now no longer locked to returning an Avalonia `Control`.
the WithHelpContent method also has logic to handle it returning a string directly, and will wrap it in a textblock with h1 style and size 20 font. otherwise it's up to the ContentPresenter for the help content to choose how to display it if it's none of the above mentioned types.
This commit is contained in:
GreemDev 2025-11-27 21:00:44 -06:00
parent 18fe8d4eff
commit 51692a94d9
8 changed files with 152 additions and 118 deletions

View file

@ -0,0 +1,31 @@
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Styling;
using Gommon;
using System;
namespace Ryujinx.Ava.Common
{
public static class EmbeddedAvaloniaResources
{
public const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
public static Bitmap LoadBitmap(string uri)
=> new(AssetLoader.Open(new Uri(uri)));
public static Bitmap GetIconByNameAndTheme(string iconName, bool isDarkTheme)
{
string themeName = isDarkTheme ? "Dark" : "Light";
return LoadBitmap(LogoPathFormat.Format(iconName, themeName));
}
public static Bitmap GetIconByNameAndTheme(string iconName, string theme)
{
bool isDarkTheme = theme == "Dark" ||
(theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark);
return GetIconByNameAndTheme(iconName, isDarkTheme);
}
}
}

View file

@ -71,7 +71,7 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages
} }
} }
public override Control CreateHelpContent() public override object CreateHelpContent()
{ {
Grid grid = new() Grid grid = new()
{ {

View file

@ -26,7 +26,7 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages
? Result.Fail ? Result.Fail
: InstallKeys(KeysFolderPath); : InstallKeys(KeysFolderPath);
public override Control CreateHelpContent() public override object CreateHelpContent()
{ {
Grid grid = new() Grid grid = new()
{ {

View file

@ -0,0 +1,57 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.SetupWizard.Pages;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.SetupWizard
{
public partial class RyujinxSetupWizard
{
private async ValueTask<bool> SetupKeys()
{
if (_overwrite || !RyujinxApp.MainWindow.VirtualFileSystem.HasKeySet)
{
Retry:
bool result = await NextPage()
.WithTitle(LocaleKeys.SetupWizardKeysPageTitle)
.WithContent<SetupKeysPage, SetupKeysPageContext>(out SetupKeysPageContext keyContext)
.Show();
if (!result)
return false;
if (!keyContext.CompleteStep())
goto Retry;
}
return true;
}
private async ValueTask<bool> SetupFirmware()
{
if (_overwrite || !HasFirmware)
{
if (!RyujinxApp.MainWindow.VirtualFileSystem.HasKeySet)
{
NotificationManager.Error("Keys still seem to not be installed. Please try again.");
return false;
}
Retry:
bool result = await NextPage()
.WithTitle(LocaleKeys.SetupWizardFirmwarePageTitle)
.WithContent<SetupFirmwarePage, SetupFirmwarePageContext>(out SetupFirmwarePageContext fwContext)
.Show();
if (!result)
return false;
if (!fwContext.CompleteStep())
goto Retry;
OnPropertyChanged(nameof(HasFirmware));
}
return true;
}
}
}

View file

@ -3,24 +3,46 @@ using Avalonia.Controls.Notifications;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using Gommon; using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.SetupWizard.Pages; using Ryujinx.Ava.UI.ViewModels;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.SetupWizard namespace Ryujinx.Ava.UI.SetupWizard
{ {
public class RyujinxSetupWizard : IDisposable, INotifyPropertyChanged public partial class RyujinxSetupWizard : BaseModel, IDisposable
{ {
private bool _configWasModified; private bool _configWasModified;
public bool HasFirmware => RyujinxApp.MainWindow.ContentManager.GetCurrentFirmwareVersion() != null; private readonly RyujinxSetupWizardWindow _window;
private readonly bool _overwrite;
public RyujinxSetupWizard(RyujinxSetupWizardWindow wizardWindow, bool overwriteMode)
{
_window = wizardWindow;
_overwrite = overwriteMode;
if (Program.PreviewerDetached)
{
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle);
RyujinxApp.ThemeChanged += Ryujinx_ThemeChanged;
}
}
private SetupWizardPage FirstPage() => new(_window.WizardPresenter, this, isFirstPage: true);
private SetupWizardPage NextPage() => new(_window.WizardPresenter, this);
public void SignalConfigModified()
{
_configWasModified = true;
}
public static bool HasFirmware => RyujinxApp.MainWindow.ContentManager.GetCurrentFirmwareVersion() != null;
public RyujinxNotificationManager NotificationManager { get; private set; } public RyujinxNotificationManager NotificationManager { get; private set; }
@ -40,7 +62,7 @@ namespace Ryujinx.Ava.UI.SetupWizard
.WithTitle(LocaleKeys.SetupWizardFirstPageTitle) .WithTitle(LocaleKeys.SetupWizardFirstPageTitle)
.WithContent(LocaleKeys.SetupWizardFirstPageContent) .WithContent(LocaleKeys.SetupWizardFirstPageContent)
.WithActionContent(LocaleKeys.SetupWizardFirstPageAction) .WithActionContent(LocaleKeys.SetupWizardFirstPageAction)
.Show(); .Show();
// result is unhandled as the first page cannot display anything other than the next button. // result is unhandled as the first page cannot display anything other than the next button.
// back does not need to be handled // back does not need to be handled
@ -61,99 +83,22 @@ namespace Ryujinx.Ava.UI.SetupWizard
RyujinxSetupWizardWindow.IsOpen = false; RyujinxSetupWizardWindow.IsOpen = false;
} }
public Bitmap DiscordLogo #region Discord logo stuff
{
get; [ObservableProperty] public partial Bitmap DiscordLogo { get; set; }
set => SetField(ref field, value);
}
private void Ryujinx_ThemeChanged() private void Ryujinx_ThemeChanged()
{ {
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value)); Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle));
} }
private const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
private void UpdateLogoTheme(string theme) private void UpdateLogoTheme(string theme)
{ {
bool isDarkTheme = theme == "Dark" || bool isDarkTheme = theme == "Dark" ||
(theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark); (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark);
string themeName = isDarkTheme ? "Dark" : "Light"; DiscordLogo = EmbeddedAvaloniaResources
.GetIconByNameAndTheme("Discord", isDarkTheme);
DiscordLogo = LoadBitmap(LogoPathFormat.Format("Discord", themeName));
}
private static Bitmap LoadBitmap(string uri) => new(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
private async ValueTask<bool> SetupKeys()
{
if (_overwrite || !RyujinxApp.MainWindow.VirtualFileSystem.HasKeySet)
{
Retry:
bool result = await NextPage()
.WithTitle(LocaleKeys.SetupWizardKeysPageTitle)
.WithContent<SetupKeysPage, SetupKeysPageContext>(out SetupKeysPageContext keyContext)
.Show();
if (!result)
return false;
if (!keyContext.CompleteStep())
goto Retry;
}
return true;
}
private async ValueTask<bool> SetupFirmware()
{
if (_overwrite || !HasFirmware)
{
if (!RyujinxApp.MainWindow.VirtualFileSystem.HasKeySet)
{
NotificationManager.Error("Keys still seem to not be installed. Please try again.");
return false;
}
Retry:
bool result = await NextPage()
.WithTitle(LocaleKeys.SetupWizardFirmwarePageTitle)
.WithContent<SetupFirmwarePage, SetupFirmwarePageContext>(out SetupFirmwarePageContext fwContext)
.Show();
if (!result)
return false;
if (!fwContext.CompleteStep())
goto Retry;
}
return true;
}
private SetupWizardPage FirstPage() => new(_window.WizardPresenter, this, isFirstPage: true);
private SetupWizardPage NextPage() => new(_window.WizardPresenter, this);
public void SignalConfigModified()
{
_configWasModified = true;
}
private readonly RyujinxSetupWizardWindow _window;
private readonly bool _overwrite;
public RyujinxSetupWizard(RyujinxSetupWizardWindow wizardWindow, bool overwriteMode)
{
_window = wizardWindow;
_overwrite = overwriteMode;
if (Program.PreviewerDetached)
{
UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle);
RyujinxApp.ThemeChanged += Ryujinx_ThemeChanged;
}
} }
public void Dispose() public void Dispose()
@ -165,19 +110,6 @@ namespace Ryujinx.Ava.UI.SetupWizard
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public event PropertyChangedEventHandler PropertyChanged; #endregion
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
} }
} }

View file

@ -1,5 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Gommon; using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
@ -35,6 +37,23 @@ namespace Ryujinx.Ava.UI.SetupWizard
public SetupWizardPage WithHelpContent(object? content) public SetupWizardPage WithHelpContent(object? content)
{ {
if (content is string str)
{
TextBlock tb = new()
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Center,
TextWrapping = TextWrapping.Wrap,
FontSize = 20.0,
Text = str
};
tb.Classes.Add("h1");
content = tb;
}
HelpContent = content; HelpContent = content;
HasHelpContent = content != null; HasHelpContent = content != null;
return this; return this;

View file

@ -1,4 +1,3 @@
using Avalonia.Controls;
using Gommon; using Gommon;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
@ -10,8 +9,9 @@ namespace Ryujinx.Ava.UI.SetupWizard
public RyujinxNotificationManager NotificationManager { get; init; } public RyujinxNotificationManager NotificationManager { get; init; }
public abstract Result CompleteStep(); public abstract Result CompleteStep();
#nullable enable
public virtual Control CreateHelpContent() public virtual object? CreateHelpContent()
#nullable disable
{ {
return null; return null;
} }

View file

@ -3,6 +3,7 @@ using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Gommon; using Gommon;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.Configuration; using Ryujinx.Ava.Systems.Configuration;
using System; using System;
@ -36,21 +37,15 @@ namespace Ryujinx.Ava.UI.ViewModels
Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value)); Dispatcher.UIThread.Post(() => UpdateLogoTheme(ConfigurationState.Instance.UI.BaseStyle.Value));
} }
private const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
private void UpdateLogoTheme(string theme) private void UpdateLogoTheme(string theme)
{ {
bool isDarkTheme = theme == "Dark" || bool isDarkTheme = theme == "Dark" ||
(theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark); (theme == "Auto" && RyujinxApp.DetectSystemTheme() == ThemeVariant.Dark);
string themeName = isDarkTheme ? "Dark" : "Light"; DiscordLogo = EmbeddedAvaloniaResources.GetIconByNameAndTheme("Discord", isDarkTheme);
GitLabLogo = EmbeddedAvaloniaResources.GetIconByNameAndTheme("GitLab", isDarkTheme);
DiscordLogo = LoadBitmap(LogoPathFormat.Format("Discord", themeName));
GitLabLogo = LoadBitmap(LogoPathFormat.Format("GitLab", themeName));
} }
private static Bitmap LoadBitmap(string uri) => new(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
public void Dispose() public void Dispose()
{ {
RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged; RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged;