2022-09-19 20:05:26 +02:00
|
|
|
using Avalonia;
|
2022-05-15 13:30:15 +02:00
|
|
|
using Avalonia.Controls;
|
2022-09-19 20:05:26 +02:00
|
|
|
using Avalonia.Controls.ApplicationLifetimes;
|
|
|
|
using Avalonia.Media;
|
2022-05-15 13:30:15 +02:00
|
|
|
using Avalonia.Threading;
|
2022-10-23 11:15:45 +02:00
|
|
|
using FluentAvalonia.Core;
|
2022-05-15 13:30:15 +02:00
|
|
|
using FluentAvalonia.UI.Controls;
|
|
|
|
using Ryujinx.Ava.Common.Locale;
|
2022-12-29 15:24:05 +01:00
|
|
|
using Ryujinx.Ava.UI.Controls;
|
|
|
|
using Ryujinx.Ava.UI.Windows;
|
2022-05-15 13:30:15 +02:00
|
|
|
using Ryujinx.Common.Logging;
|
|
|
|
using System;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
2022-12-29 15:24:05 +01:00
|
|
|
namespace Ryujinx.Ava.UI.Helpers
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
public static class ContentDialogHelper
|
|
|
|
{
|
|
|
|
private static bool _isChoiceDialogOpen;
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
public async static Task<UserResult> ShowContentDialog(
|
|
|
|
string title,
|
|
|
|
object content,
|
|
|
|
string primaryButton,
|
|
|
|
string secondaryButton,
|
|
|
|
string closeButton,
|
|
|
|
UserResult primaryButtonResult = UserResult.Ok,
|
|
|
|
ManualResetEvent deferResetEvent = null,
|
|
|
|
Func<Window, Task> doWhileDeferred = null,
|
|
|
|
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
UserResult result = UserResult.None;
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
ContentDialog contentDialog = new()
|
2022-09-19 20:05:26 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
Title = title,
|
|
|
|
PrimaryButtonText = primaryButton,
|
|
|
|
SecondaryButtonText = secondaryButton,
|
|
|
|
CloseButtonText = closeButton,
|
|
|
|
Content = content
|
|
|
|
};
|
2022-09-19 20:05:26 +02:00
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
|
2022-09-19 20:05:26 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
result = primaryButtonResult;
|
|
|
|
});
|
2022-09-19 20:05:26 +02:00
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
|
2022-09-19 20:05:26 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
result = UserResult.No;
|
|
|
|
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
|
|
|
});
|
2022-05-15 13:30:15 +02:00
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
result = UserResult.Cancel;
|
|
|
|
contentDialog.PrimaryButtonClick -= deferCloseAction;
|
|
|
|
});
|
2022-09-19 20:05:26 +02:00
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
if (deferResetEvent != null)
|
2022-09-19 20:05:26 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
contentDialog.PrimaryButtonClick += deferCloseAction;
|
2022-05-15 13:30:15 +02:00
|
|
|
}
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
await ShowAsync(contentDialog);
|
|
|
|
|
2022-05-15 13:30:15 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
private async static Task<UserResult> ShowTextDialog(
|
|
|
|
string title,
|
|
|
|
string primaryText,
|
|
|
|
string secondaryText,
|
|
|
|
string primaryButton,
|
|
|
|
string secondaryButton,
|
|
|
|
string closeButton,
|
|
|
|
int iconSymbol,
|
|
|
|
UserResult primaryButtonResult = UserResult.Ok,
|
|
|
|
ManualResetEvent deferResetEvent = null,
|
|
|
|
Func<Window, Task> doWhileDeferred = null,
|
|
|
|
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
|
|
|
{
|
|
|
|
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
|
|
|
|
|
|
|
|
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction);
|
|
|
|
}
|
|
|
|
|
2022-05-15 13:30:15 +02:00
|
|
|
public async static Task<UserResult> ShowDeferredContentDialog(
|
|
|
|
StyleableWindow window,
|
|
|
|
string title,
|
|
|
|
string primaryText,
|
|
|
|
string secondaryText,
|
|
|
|
string primaryButton,
|
|
|
|
string secondaryButton,
|
|
|
|
string closeButton,
|
|
|
|
int iconSymbol,
|
|
|
|
ManualResetEvent deferResetEvent,
|
|
|
|
Func<Window, Task> doWhileDeferred = null)
|
|
|
|
{
|
|
|
|
bool startedDeferring = false;
|
|
|
|
UserResult result = UserResult.None;
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
return await ShowTextDialog(
|
2022-10-23 11:15:45 +02:00
|
|
|
title,
|
|
|
|
primaryText,
|
|
|
|
secondaryText,
|
|
|
|
primaryButton,
|
|
|
|
secondaryButton,
|
|
|
|
closeButton,
|
|
|
|
iconSymbol,
|
2023-01-03 19:45:08 +01:00
|
|
|
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
|
2022-10-23 11:15:45 +02:00
|
|
|
deferResetEvent,
|
|
|
|
doWhileDeferred,
|
|
|
|
DeferClose);
|
2022-05-15 13:30:15 +02:00
|
|
|
|
|
|
|
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
|
|
|
{
|
|
|
|
if (startedDeferring)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-23 11:15:45 +02:00
|
|
|
sender.PrimaryButtonClick -= DeferClose;
|
2022-07-05 20:06:31 +02:00
|
|
|
|
2022-05-15 13:30:15 +02:00
|
|
|
startedDeferring = true;
|
|
|
|
|
|
|
|
var deferral = args.GetDeferral();
|
|
|
|
|
2023-01-03 19:45:08 +01:00
|
|
|
result = primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok;
|
2022-05-15 13:30:15 +02:00
|
|
|
|
2022-10-23 11:15:45 +02:00
|
|
|
sender.PrimaryButtonClick -= DeferClose;
|
2022-05-15 13:30:15 +02:00
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
_ = Task.Run(() =>
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
deferResetEvent.WaitOne();
|
|
|
|
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
|
|
{
|
|
|
|
deferral.Complete();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
if (doWhileDeferred != null)
|
|
|
|
{
|
2022-07-24 19:38:38 +02:00
|
|
|
await doWhileDeferred(window);
|
2022-05-15 13:30:15 +02:00
|
|
|
|
|
|
|
deferResetEvent.Set();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
private static Grid CreateTextDialogContent(string primaryText, string secondaryText, int symbol)
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
Grid content = new()
|
|
|
|
{
|
|
|
|
RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() },
|
|
|
|
ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() },
|
2022-05-15 13:30:15 +02:00
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
MinHeight = 80
|
|
|
|
};
|
|
|
|
|
|
|
|
SymbolIcon icon = new()
|
|
|
|
{
|
|
|
|
Symbol = (Symbol)symbol,
|
|
|
|
Margin = new Thickness(10),
|
|
|
|
FontSize = 40,
|
|
|
|
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
|
|
|
};
|
2022-05-15 13:30:15 +02:00
|
|
|
|
|
|
|
Grid.SetColumn(icon, 0);
|
|
|
|
Grid.SetRowSpan(icon, 2);
|
|
|
|
Grid.SetRow(icon, 0);
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
TextBlock primaryLabel = new()
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
Text = primaryText,
|
|
|
|
Margin = new Thickness(5),
|
2022-12-10 21:21:13 +01:00
|
|
|
TextWrapping = TextWrapping.Wrap,
|
2023-01-09 04:37:20 +01:00
|
|
|
MaxWidth = 450
|
2022-05-15 13:30:15 +02:00
|
|
|
};
|
2023-01-09 04:37:20 +01:00
|
|
|
|
|
|
|
TextBlock secondaryLabel = new()
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
Text = secondaryText,
|
|
|
|
Margin = new Thickness(5),
|
2022-12-10 21:21:13 +01:00
|
|
|
TextWrapping = TextWrapping.Wrap,
|
2023-01-09 04:37:20 +01:00
|
|
|
MaxWidth = 450
|
2022-05-15 13:30:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Grid.SetColumn(primaryLabel, 1);
|
|
|
|
Grid.SetColumn(secondaryLabel, 1);
|
|
|
|
Grid.SetRow(primaryLabel, 0);
|
|
|
|
Grid.SetRow(secondaryLabel, 1);
|
|
|
|
|
|
|
|
content.Children.Add(icon);
|
|
|
|
content.Children.Add(primaryLabel);
|
|
|
|
content.Children.Add(secondaryLabel);
|
|
|
|
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static async Task<UserResult> CreateInfoDialog(
|
|
|
|
string primary,
|
|
|
|
string secondaryText,
|
|
|
|
string acceptButton,
|
|
|
|
string closeButton,
|
|
|
|
string title)
|
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
return await ShowTextDialog(
|
2022-05-15 13:30:15 +02:00
|
|
|
title,
|
|
|
|
primary,
|
|
|
|
secondaryText,
|
|
|
|
acceptButton,
|
|
|
|
"",
|
|
|
|
closeButton,
|
|
|
|
(int)Symbol.Important);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static async Task<UserResult> CreateConfirmationDialog(
|
|
|
|
string primaryText,
|
|
|
|
string secondaryText,
|
|
|
|
string acceptButtonText,
|
|
|
|
string cancelButtonText,
|
|
|
|
string title,
|
|
|
|
UserResult primaryButtonResult = UserResult.Yes)
|
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
return await ShowTextDialog(
|
2023-01-03 19:45:08 +01:00
|
|
|
string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title,
|
2022-05-15 13:30:15 +02:00
|
|
|
primaryText,
|
|
|
|
secondaryText,
|
|
|
|
acceptButtonText,
|
|
|
|
"",
|
|
|
|
cancelButtonText,
|
|
|
|
(int)Symbol.Help,
|
|
|
|
primaryButtonResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static UpdateWaitWindow CreateWaitingDialog(string mainText, string secondaryText)
|
|
|
|
{
|
|
|
|
return new(mainText, secondaryText);
|
|
|
|
}
|
|
|
|
|
2022-07-24 19:38:38 +02:00
|
|
|
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
await ShowTextDialog(
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
|
2022-05-15 13:30:15 +02:00
|
|
|
primary,
|
|
|
|
secondaryText,
|
|
|
|
"",
|
|
|
|
"",
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
2022-05-15 13:30:15 +02:00
|
|
|
(int)Symbol.Important);
|
|
|
|
}
|
|
|
|
|
2022-07-24 19:38:38 +02:00
|
|
|
internal static async Task CreateWarningDialog(string primary, string secondaryText)
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
2023-01-09 04:37:20 +01:00
|
|
|
await ShowTextDialog(
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
|
2022-05-15 13:30:15 +02:00
|
|
|
primary,
|
|
|
|
secondaryText,
|
|
|
|
"",
|
|
|
|
"",
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
2022-05-15 13:30:15 +02:00
|
|
|
(int)Symbol.Important);
|
|
|
|
}
|
|
|
|
|
2022-07-24 19:38:38 +02:00
|
|
|
internal static async Task CreateErrorDialog(string errorMessage, string secondaryErrorMessage = "")
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
Logger.Error?.Print(LogClass.Application, errorMessage);
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
await ShowTextDialog(
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.DialogErrorTitle],
|
|
|
|
LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
|
2022-05-15 13:30:15 +02:00
|
|
|
errorMessage,
|
|
|
|
secondaryErrorMessage,
|
|
|
|
"",
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
2022-05-15 13:30:15 +02:00
|
|
|
(int)Symbol.Dismiss);
|
|
|
|
}
|
|
|
|
|
2022-07-24 19:38:38 +02:00
|
|
|
internal static async Task<bool> CreateChoiceDialog(string title, string primary, string secondaryText)
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
if (_isChoiceDialogOpen)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_isChoiceDialogOpen = true;
|
|
|
|
|
2023-01-09 04:37:20 +01:00
|
|
|
UserResult response = await ShowTextDialog(
|
|
|
|
title,
|
|
|
|
primary,
|
|
|
|
secondaryText,
|
|
|
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
|
|
|
"",
|
|
|
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
|
|
|
(int)Symbol.Help,
|
|
|
|
UserResult.Yes);
|
2022-05-15 13:30:15 +02:00
|
|
|
|
|
|
|
_isChoiceDialogOpen = false;
|
|
|
|
|
|
|
|
return response == UserResult.Yes;
|
|
|
|
}
|
|
|
|
|
2022-07-24 19:38:38 +02:00
|
|
|
internal static async Task<bool> CreateExitDialog()
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
return await CreateChoiceDialog(
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.DialogExitTitle],
|
|
|
|
LocaleManager.Instance[LocaleKeys.DialogExitMessage],
|
|
|
|
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
|
2022-05-15 13:30:15 +02:00
|
|
|
}
|
|
|
|
|
2022-07-24 19:38:38 +02:00
|
|
|
internal static async Task<bool> CreateStopEmulationDialog()
|
2022-05-15 13:30:15 +02:00
|
|
|
{
|
|
|
|
return await CreateChoiceDialog(
|
2023-01-03 19:45:08 +01:00
|
|
|
LocaleManager.Instance[LocaleKeys.DialogStopEmulationTitle],
|
|
|
|
LocaleManager.Instance[LocaleKeys.DialogStopEmulationMessage],
|
|
|
|
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
|
2022-05-15 13:30:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-01-09 04:37:20 +01:00
|
|
|
|
|
|
|
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
|
|
|
{
|
|
|
|
ContentDialogResult result;
|
|
|
|
|
|
|
|
ContentDialogOverlayWindow contentDialogOverlayWindow = null;
|
|
|
|
|
|
|
|
Window parent = GetMainWindow();
|
|
|
|
|
|
|
|
if (parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning)
|
|
|
|
{
|
|
|
|
contentDialogOverlayWindow = new()
|
|
|
|
{
|
|
|
|
Height = parent.Bounds.Height,
|
|
|
|
Width = parent.Bounds.Width,
|
|
|
|
Position = parent.PointToScreen(new Point()),
|
|
|
|
ShowInTaskbar = false
|
|
|
|
};
|
|
|
|
|
|
|
|
parent.PositionChanged += OverlayOnPositionChanged;
|
|
|
|
|
|
|
|
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
|
|
|
|
{
|
|
|
|
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
|
|
|
}
|
|
|
|
|
|
|
|
contentDialogOverlayWindow.ContentDialog = contentDialog;
|
|
|
|
|
|
|
|
bool opened = false;
|
|
|
|
|
|
|
|
contentDialogOverlayWindow.Opened += OverlayOnActivated;
|
|
|
|
|
|
|
|
async void OverlayOnActivated(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
if (opened)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
opened = true;
|
|
|
|
|
|
|
|
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
|
|
|
|
|
|
|
|
result = await ShowDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
result = await contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = await ShowDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
async Task<ContentDialogResult> ShowDialog()
|
|
|
|
{
|
|
|
|
if (contentDialogOverlayWindow is not null)
|
|
|
|
{
|
|
|
|
result = await contentDialog.ShowAsync(contentDialogOverlayWindow);
|
|
|
|
|
|
|
|
contentDialogOverlayWindow!.Close();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = await contentDialog.ShowAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (contentDialogOverlayWindow is not null)
|
|
|
|
{
|
|
|
|
contentDialogOverlayWindow.Content = null;
|
|
|
|
contentDialogOverlayWindow.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Window GetMainWindow()
|
|
|
|
{
|
|
|
|
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
|
|
|
|
{
|
|
|
|
foreach (Window item in al.Windows)
|
|
|
|
{
|
|
|
|
if (item.IsActive && item is MainWindow window)
|
|
|
|
{
|
|
|
|
return window;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2022-05-15 13:30:15 +02:00
|
|
|
}
|
2022-11-29 06:33:46 +01:00
|
|
|
}
|