mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-12 16:25:46 +00:00
Initial work on a setup wizard
Setup wizard abstraction & architecture from TKMM
This commit is contained in:
@@ -15,6 +15,7 @@ using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
|
||||
22
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml
Normal file
22
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:pages="clr-namespace:Ryujinx.UI.SetupWizard.Pages"
|
||||
xmlns:markup="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.UI.SetupWizard.Pages.SetupKeysPage"
|
||||
x:DataType="pages:SetupKeysPageViewModel">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{markup:Locale SetupWizardKeysPageDescription}" Margin="0,0,0,10"/>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBox Name="KeysFolderPathField" Text="{Binding KeysFolderPath}" IsReadOnly="True" />
|
||||
<Button Grid.Column="1"
|
||||
Content="..."
|
||||
Command="{Binding BrowseCommand}"
|
||||
CommandParameter="{Binding #KeysFolderPathField}"
|
||||
Margin="5,0,0,0"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
13
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs
Normal file
13
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPage.axaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
|
||||
namespace Ryujinx.UI.SetupWizard.Pages
|
||||
{
|
||||
public partial class SetupKeysPage : RyujinxControl<SetupKeysPageViewModel>
|
||||
{
|
||||
public SetupKeysPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs
Normal file
34
src/Ryujinx/UI/SetupWizard/Pages/SetupKeysPageViewModel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.UI.SetupWizard.Pages
|
||||
{
|
||||
public partial class SetupKeysPageViewModel : BaseModel
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial string? KeysFolderPath { get; set; }
|
||||
|
||||
[RelayCommand]
|
||||
private static async Task Browse(TextBox tb)
|
||||
{
|
||||
var result = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions {
|
||||
Title = LocaleManager.Instance[LocaleKeys.SetupWizardKeysPageFolderPopupTitle],
|
||||
AllowMultiple = false
|
||||
}) switch {
|
||||
[var target] => target.TryGetLocalPath(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
tb.Text = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs
Normal file
78
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.Systems.SetupWizard;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using Ryujinx.UI.SetupWizard.Pages;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Logger = Ryujinx.Common.Logging.Logger;
|
||||
|
||||
namespace Ryujinx.Ava.UI.SetupWizard
|
||||
{
|
||||
public class RyujinxSetupWizard(ContentPresenter presenter, MainWindowViewModel mwvm, Action onClose) : BaseSetupWizard(presenter)
|
||||
{
|
||||
private bool _configWasModified = false;
|
||||
|
||||
public bool HasFirmware => mwvm.ContentManager.GetCurrentFirmwareVersion() != null;
|
||||
|
||||
public override async ValueTask Start()
|
||||
{
|
||||
RyujinxSetupWizardWindow.IsUsingSetupWizard = true;
|
||||
Start:
|
||||
await FirstPage();
|
||||
|
||||
Keys:
|
||||
if (!mwvm.VirtualFileSystem.HasKeySet)
|
||||
{
|
||||
Retry:
|
||||
SetupKeysPageViewModel kpvm = new();
|
||||
bool result = await NextPage()
|
||||
.WithTitle(LocaleKeys.SetupWizardKeysPageTitle)
|
||||
.WithContent<SetupKeysPage>(kpvm)
|
||||
.Show();
|
||||
|
||||
if (!result)
|
||||
goto Start;
|
||||
|
||||
if (!Directory.Exists(kpvm.KeysFolderPath))
|
||||
goto Retry;
|
||||
|
||||
await mwvm.HandleKeysInstallation(kpvm.KeysFolderPath);
|
||||
}
|
||||
|
||||
Firmware:
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
// i know its always false thats the fucking point, its not done
|
||||
if (!HasFirmware && false)
|
||||
{
|
||||
if (!mwvm.VirtualFileSystem.HasKeySet)
|
||||
goto Keys;
|
||||
|
||||
Retry:
|
||||
SetupKeysPageViewModel kpvm = new();
|
||||
bool result = await NextPage()
|
||||
.WithTitle(LocaleKeys.SetupWizardKeysPageTitle)
|
||||
.WithContent<SetupKeysPage>(kpvm)
|
||||
.Show();
|
||||
|
||||
if (!result)
|
||||
goto Keys;
|
||||
|
||||
if (!Directory.Exists(kpvm.KeysFolderPath))
|
||||
goto Retry;
|
||||
|
||||
await mwvm.HandleKeysInstallation(kpvm.KeysFolderPath);
|
||||
}
|
||||
|
||||
Return:
|
||||
onClose();
|
||||
|
||||
if (_configWasModified)
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.GlobalConfigurationPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml
Normal file
20
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml
Normal file
@@ -0,0 +1,20 @@
|
||||
<windows:StyleableAppWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:markup="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:windows="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:setupWizard="clr-namespace:Ryujinx.Ava.UI.SetupWizard"
|
||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||
CanResize="False"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Ryujinx.UI.SetupWizard.RyujinxSetupWizardWindow"
|
||||
x:DataType="setupWizard:RyujinxSetupWizard"
|
||||
Title="{markup:Locale SetupWizardFirstPageTitle}">
|
||||
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" Name="FlushControls">
|
||||
<controls:RyujinxLogo ToolTip.Tip="{markup:Locale SetupWizardFirstPageTitle}"/>
|
||||
</Grid>
|
||||
<ContentPresenter Grid.Row="1" Name="WizardPresenter"/>
|
||||
</Grid>
|
||||
</windows:StyleableAppWindow>
|
||||
83
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs
Normal file
83
src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using Ryujinx.Ava;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.SetupWizard;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.UI.SetupWizard
|
||||
{
|
||||
public partial class RyujinxSetupWizardWindow : StyleableAppWindow
|
||||
{
|
||||
public static bool IsUsingSetupWizard { get; set; }
|
||||
|
||||
public RyujinxSetupWizardWindow() : base(useCustomTitleBar: true)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
FlushControls.IsVisible = !ConfigurationState.Instance.ShowOldUI;
|
||||
}
|
||||
}
|
||||
|
||||
public static RyujinxSetupWizardWindow CreateWindow(MainWindowViewModel mwvm, out RyujinxSetupWizard setupWizard)
|
||||
{
|
||||
RyujinxSetupWizardWindow window = new();
|
||||
window.DataContext = setupWizard = new RyujinxSetupWizard(window.WizardPresenter, mwvm, () =>
|
||||
{
|
||||
window.Close();
|
||||
IsUsingSetupWizard = false;
|
||||
});
|
||||
window.Height = 600;
|
||||
window.Width = 750;
|
||||
return window;
|
||||
}
|
||||
|
||||
public static bool CanShowSetupWizard =>
|
||||
!File.Exists(Path.Combine(AppDataManager.BaseDirPath, ".DoNotShowSetupWizard"));
|
||||
|
||||
public static bool DisableSetupWizard()
|
||||
{
|
||||
if (!CanShowSetupWizard)
|
||||
return false; //cannot disable; file already doesn't exist, so it's disabled.
|
||||
|
||||
string disableFile = Path.Combine(AppDataManager.BaseDirPath, ".DoNotShowSetupWizard");
|
||||
|
||||
try
|
||||
{
|
||||
File.Create(disableFile, 0).Dispose();
|
||||
File.SetAttributes(disableFile, File.GetAttributes(disableFile) | FileAttributes.Hidden);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.PrintStack(LogClass.Application, e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EnableSetupWizard()
|
||||
{
|
||||
if (CanShowSetupWizard)
|
||||
return false; //cannot enable; file already exists, so it's enabled.
|
||||
|
||||
string disableFile = Path.Combine(AppDataManager.BaseDirPath, ".DoNotShowSetupWizard");
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(disableFile);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.PrintStack(LogClass.Application, e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -870,7 +871,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private void RefreshGrid()
|
||||
{
|
||||
IObservableList<ApplicationData> appsList = Applications.ToObservableChangeSet()
|
||||
_ = Applications.ToObservableChangeSet()
|
||||
.Filter(Filter)
|
||||
.Sort(GetComparer())
|
||||
.Bind(out ReadOnlyObservableCollection<ApplicationData> apps)
|
||||
@@ -1013,7 +1014,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleKeysInstallation(string filename)
|
||||
public async Task HandleKeysInstallation(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
<viewModels:CompatibilityViewModel />
|
||||
</window:StyleableAppWindow.DataContext>
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
|
||||
<!-- UI FlushControls -->
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto" Name="FlushControls">
|
||||
<controls:RyujinxLogo
|
||||
|
||||
@@ -30,6 +30,7 @@ using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL3;
|
||||
using Ryujinx.UI.SetupWizard;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -138,7 +139,18 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
Executor.ExecuteBackgroundAsync(async () =>
|
||||
{
|
||||
await ShowIntelMacWarningAsync();
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ShowIntelMacWarningAsync();
|
||||
|
||||
if (Program.IsFirstStart && RyujinxSetupWizardWindow.CanShowSetupWizard)
|
||||
{
|
||||
Task windowTask = ShowAsync(RyujinxSetupWizardWindow.CreateWindow(ViewModel, out var wiz), this);
|
||||
_ = wiz.Start();
|
||||
await windowTask;
|
||||
}
|
||||
});
|
||||
|
||||
if (CommandLineState.FirmwareToInstallPathArg.TryGet(out FilePath fwPath))
|
||||
{
|
||||
if (fwPath is { ExistsAsFile: true, Extension: "xci" or "zip" } || fwPath.ExistsAsDirectory)
|
||||
@@ -150,6 +162,8 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
else
|
||||
Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file.");
|
||||
}
|
||||
|
||||
await CheckLaunchState();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -399,7 +413,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!RyujinxSetupWizardWindow.IsUsingSetupWizard)
|
||||
{
|
||||
ShowKeyErrorOnLoad = false;
|
||||
|
||||
@@ -538,8 +552,6 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
LoadApplications();
|
||||
}
|
||||
|
||||
_ = CheckLaunchState();
|
||||
}
|
||||
|
||||
private void SetMainContent(Control content = null)
|
||||
|
||||
Reference in New Issue
Block a user