Ava: Fix nca extraction window never closing & minor cleanup (#4569)

* ava: Remove unused doWhileDeferred parameters

* ava: Minimally improve swkbd dialog

It's currently impossible to get the dialog to redirect focus to the InputBox.

* ava: Fix nca extraction dialog never closing

Also contains some minor cleanup
This commit is contained in:
TSRBerry 2023-04-16 09:09:02 +02:00 committed by GitHub
parent c5258cf082
commit 6dbcdfea47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 45 additions and 184 deletions

View file

@ -13,6 +13,7 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@ -152,25 +153,17 @@ namespace Ryujinx.Ava.Common
string destination = await folderDialog.ShowAsync(_owner); string destination = await folderDialog.ShowAsync(_owner);
var cancellationToken = new CancellationTokenSource(); var cancellationToken = new CancellationTokenSource();
UpdateWaitWindow waitingDialog = new(
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
cancellationToken);
if (!string.IsNullOrWhiteSpace(destination)) if (!string.IsNullOrWhiteSpace(destination))
{ {
Thread extractorThread = new(() => Thread extractorThread = new(() =>
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(waitingDialog.Show);
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
"",
"",
LocaleManager.Instance[LocaleKeys.InputDialogCancel],
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
if (result == UserResult.Cancel)
{
cancellationToken.Cancel();
}
});
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null; Nca mainNca = null;
@ -222,6 +215,8 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
waitingDialog.Close();
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
}); });
@ -263,11 +258,15 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
waitingDialog.Close();
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
}); });
} }
else if (resultCode.Value.IsSuccess()) else if (resultCode.Value.IsSuccess())
{ {
Dispatcher.UIThread.Post(waitingDialog.Close);
NotificationHelper.Show( NotificationHelper.Show(
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle], LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}", $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
@ -284,6 +283,8 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
waitingDialog.Close();
await ContentDialogHelper.CreateErrorDialog(ex.Message); await ContentDialogHelper.CreateErrorDialog(ex.Message);
}); });
} }

View file

@ -48,6 +48,7 @@
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center"
Focusable="True"
KeyUp="Message_KeyUp" KeyUp="Message_KeyUp"
Text="{Binding Message}" Text="{Binding Message}"
TextInput="Message_TextInput" TextInput="Message_TextInput"
@ -61,4 +62,4 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</Grid> </Grid>
</UserControl> </UserControl>

View file

@ -45,6 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent(); InitializeComponent();
} }
protected override void OnGotFocus(GotFocusEventArgs e)
{
// FIXME: This does not work. Might be a bug in Avalonia with DialogHost
// Currently focus will be redirected to the overlay window instead.
Input.Focus();
}
public string Message { get; set; } = ""; public string Message { get; set; } = "";
public string MainText { get; set; } = ""; public string MainText { get; set; } = "";
public string SecondaryText { get; set; } = ""; public string SecondaryText { get; set; } = "";
@ -59,24 +66,6 @@ namespace Ryujinx.Ava.UI.Controls
string input = string.Empty; string input = string.Empty;
var overlay = new ContentDialogOverlayWindow()
{
Height = window.Bounds.Height,
Width = window.Bounds.Width,
Position = window.PointToScreen(new Point())
};
window.PositionChanged += OverlayOnPositionChanged;
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
{
overlay.Position = window.PointToScreen(new Point());
}
contentDialog = overlay.ContentDialog;
bool opened = false;
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
content._host = contentDialog; content._host = contentDialog;
@ -97,25 +86,7 @@ namespace Ryujinx.Ava.UI.Controls
}; };
contentDialog.Closed += handler; contentDialog.Closed += handler;
overlay.Opened += OverlayOnActivated; await ContentDialogHelper.ShowAsync(contentDialog);
async void OverlayOnActivated(object sender, EventArgs e)
{
if (opened)
{
return;
}
opened = true;
overlay.Position = window.PointToScreen(new Point());
await contentDialog.ShowAsync(overlay);
contentDialog.Closed -= handler;
overlay.Close();
};
await overlay.ShowDialog(window);
return (result, input); return (result, input);
} }

View file

@ -1,32 +0,0 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Controls.InputDialog"
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"
mc:Ignorable="d"
Focusable="True">
<Grid
Margin="5,10,5,5"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center" Text="{Binding Message}" />
<TextBox
Grid.Row="1"
Width="300"
Margin="10"
HorizontalAlignment="Center"
MaxLength="{Binding MaxLength}"
Text="{Binding Input, Mode=TwoWay}" />
<TextBlock
Grid.Row="2"
Margin="5,5,5,10"
HorizontalAlignment="Center"
Text="{Binding SubMessage}" />
</Grid>
</UserControl>

View file

@ -1,57 +0,0 @@
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Controls
{
public partial class InputDialog : UserControl
{
public string Message { get; set; }
public string Input { get; set; }
public string SubMessage { get; set; }
public uint MaxLength { get; }
public InputDialog(string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue)
{
Message = message;
Input = input;
SubMessage = subMessage;
MaxLength = maxLength;
DataContext = this;
}
public InputDialog()
{
InitializeComponent();
}
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message,
string input = "", string subMessage = "", uint maxLength = int.MaxValue)
{
UserResult result = UserResult.Cancel;
InputDialog content = new InputDialog(message, input, subMessage, maxLength);
ContentDialog contentDialog = new ContentDialog
{
Title = title,
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel],
Content = content,
PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.Ok;
input = content.Input;
})
};
await contentDialog.ShowAsync();
return (result, input);
}
}
}

View file

@ -1,15 +1,26 @@
using Avalonia.Controls; using Avalonia.Controls;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using System.Threading;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
public partial class UpdateWaitWindow : StyleableWindow public partial class UpdateWaitWindow : StyleableWindow
{ {
public UpdateWaitWindow(string primaryText, string secondaryText, CancellationTokenSource cancellationToken) : this(primaryText, secondaryText)
{
SystemDecorations = SystemDecorations.Full;
ShowInTaskbar = true;
Closing += (_, _) => cancellationToken.Cancel();
}
public UpdateWaitWindow(string primaryText, string secondaryText) : this() public UpdateWaitWindow(string primaryText, string secondaryText) : this()
{ {
PrimaryText.Text = primaryText; PrimaryText.Text = primaryText;
SecondaryText.Text = secondaryText; SecondaryText.Text = secondaryText;
WindowStartupLocation = WindowStartupLocation.CenterOwner; WindowStartupLocation = WindowStartupLocation.CenterOwner;
SystemDecorations = SystemDecorations.BorderOnly;
ShowInTaskbar = false;
} }
public UpdateWaitWindow() public UpdateWaitWindow()

View file

@ -27,7 +27,6 @@ namespace Ryujinx.Ava.UI.Helpers
string closeButton, string closeButton,
UserResult primaryButtonResult = UserResult.Ok, UserResult primaryButtonResult = UserResult.Ok,
ManualResetEvent deferResetEvent = null, ManualResetEvent deferResetEvent = null,
Func<Window, Task> doWhileDeferred = null,
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null) TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
{ {
UserResult result = UserResult.None; UserResult result = UserResult.None;
@ -78,12 +77,11 @@ namespace Ryujinx.Ava.UI.Helpers
int iconSymbol, int iconSymbol,
UserResult primaryButtonResult = UserResult.Ok, UserResult primaryButtonResult = UserResult.Ok,
ManualResetEvent deferResetEvent = null, ManualResetEvent deferResetEvent = null,
Func<Window, Task> doWhileDeferred = null,
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null) TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
{ {
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol); Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction); return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction);
} }
public async static Task<UserResult> ShowDeferredContentDialog( public async static Task<UserResult> ShowDeferredContentDialog(
@ -111,7 +109,6 @@ namespace Ryujinx.Ava.UI.Helpers
iconSymbol, iconSymbol,
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok, primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
deferResetEvent, deferResetEvent,
doWhileDeferred,
DeferClose); DeferClose);
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args) async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
@ -236,11 +233,6 @@ namespace Ryujinx.Ava.UI.Helpers
primaryButtonResult); primaryButtonResult);
} }
internal static UpdateWaitWindow CreateWaitingDialog(string mainText, string secondaryText)
{
return new(mainText, secondaryText);
}
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText) internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
{ {
await ShowTextDialog( await ShowTextDialog(
@ -319,28 +311,6 @@ namespace Ryujinx.Ava.UI.Helpers
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]); LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
} }
internal static async Task<string> CreateInputDialog(
string title,
string mainText,
string subText,
uint maxLength = int.MaxValue,
string input = "")
{
var result = await InputDialog.ShowInputDialog(
title,
mainText,
input,
subText,
maxLength);
if (result.Result == UserResult.Ok)
{
return result.Input;
}
return string.Empty;
}
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog) public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
{ {
ContentDialogResult result; ContentDialogResult result;

View file

@ -972,7 +972,7 @@ namespace Ryujinx.Ava.UI.ViewModels
LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]); UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
if (result == UserResult.Yes) if (result == UserResult.Yes)
{ {

View file

@ -1,4 +1,4 @@
<window:StyleableWindow <window:StyleableWindow
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@ -10,20 +10,16 @@
d:DesignHeight="450" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow" x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow"
Title="ContentDialogOverlayWindow" Title="ContentDialogOverlayWindow"
Focusable="True"> Focusable="False">
<window:StyleableWindow.Styles> <window:StyleableWindow.Styles>
<Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot"> <Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot">
<Setter Property="Background" <Setter Property="Background"
Value="Transparent" /> Value="Transparent" />
</Style> </Style>
</window:StyleableWindow.Styles> </window:StyleableWindow.Styles>
<ContentControl <ui:ContentDialog Name="ContentDialog"
Focusable="False" IsPrimaryButtonEnabled="True"
IsVisible="False" IsSecondaryButtonEnabled="True"
KeyboardNavigation.IsTabStop="False"> IsVisible="False"
<ui:ContentDialog Name="ContentDialog" Focusable="True"/>
IsPrimaryButtonEnabled="True"
IsSecondaryButtonEnabled="True"
IsVisible="False" />
</ContentControl>
</window:StyleableWindow> </window:StyleableWindow>