diff --git a/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg b/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg
new file mode 100644
index 00000000..cf78cf12
--- /dev/null
+++ b/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg
@@ -0,0 +1,155 @@
+
+
+
diff --git a/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg b/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg
new file mode 100644
index 00000000..8097762d
--- /dev/null
+++ b/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg
@@ -0,0 +1,341 @@
+
+
+
diff --git a/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg b/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg
new file mode 100644
index 00000000..adb6e1e1
--- /dev/null
+++ b/src/Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg
@@ -0,0 +1,185 @@
+
+
+
diff --git a/src/Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg b/src/Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg
new file mode 100644
index 00000000..53eef82c
--- /dev/null
+++ b/src/Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg
@@ -0,0 +1,84 @@
+
+
\ No newline at end of file
diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json
index 493aaa81..72b5e8e3 100644
--- a/src/Ryujinx.Ava/Assets/Locales/en_US.json
+++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json
@@ -549,9 +549,10 @@
"SoftwareKeyboardModeNumeric": "Must be 0-9 or '.' only",
"SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only",
"SoftwareKeyboardModeASCII": "Must be ASCII text only",
- "DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
- "DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
- "DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n",
+ "ControllerAppletControllers": "Supported Controllers:",
+ "ControllerAppletPlayers": "Players:",
+ "ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.",
+ "ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.",
"UpdaterRenaming": "Renaming Old Files...",
"UpdaterRenameFailed": "Updater was unable to rename file: {0}",
"UpdaterAddingFiles": "Adding New Files...",
diff --git a/src/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj
index 6812e57c..054a5c7f 100644
--- a/src/Ryujinx.Ava/Ryujinx.Ava.csproj
+++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj
@@ -132,6 +132,10 @@
+
+
+
+
@@ -151,6 +155,10 @@
+
+
+
+
diff --git a/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs
index 9fc7c6b6..e1193910 100644
--- a/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs
+++ b/src/Ryujinx.Ava/UI/Applet/AvaHostUiHandler.cs
@@ -29,14 +29,24 @@ namespace Ryujinx.Ava.UI.Applet
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
{
- string message = LocaleManager.Instance.UpdateAndGetDynamicValue(
- args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange,
- args.PlayerCountMin == args.PlayerCountMax ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}",
- args.SupportedStyles,
- string.Join(", ", args.SupportedPlayers),
- args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
+ ManualResetEvent dialogCloseEvent = new(false);
- return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message);
+ bool okPressed = false;
+
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ var response = await ControllerAppletDialog.ShowControllerAppletDialog(_parent, args);
+ if (response == UserResult.Ok)
+ {
+ okPressed = true;
+ }
+
+ dialogCloseEvent.Set();
+ });
+
+ dialogCloseEvent.WaitOne();
+
+ return okPressed;
}
public bool DisplayMessageDialog(string title, string message)
@@ -75,6 +85,8 @@ namespace Ryujinx.Ava.UI.Applet
await _parent.SettingsWindow.ShowDialog(window);
+ _parent.SettingsWindow = null;
+
opened = false;
});
diff --git a/src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml b/src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml
new file mode 100644
index 00000000..b2c22f6b
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs b/src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs
new file mode 100644
index 00000000..34de5223
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Applet/ControllerAppletDialog.axaml.cs
@@ -0,0 +1,139 @@
+using Avalonia.Controls;
+using Avalonia.Styling;
+using Avalonia.Svg.Skia;
+using Avalonia.Threading;
+using FluentAvalonia.UI.Controls;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Helpers;
+using Ryujinx.Ava.UI.Windows;
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Applets;
+using Ryujinx.HLE.HOS.Services.Hid;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Ryujinx.Ava.UI.Applet
+{
+ internal partial class ControllerAppletDialog : UserControl
+ {
+ private const string ProControllerResource = "Ryujinx.Ava/Assets/Icons/Controller_ProCon.svg";
+ private const string JoyConPairResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConPair.svg";
+ private const string JoyConLeftResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConLeft.svg";
+ private const string JoyConRightResource = "Ryujinx.Ava/Assets/Icons/Controller_JoyConRight.svg";
+
+ public static SvgImage ProControllerImage => GetResource(ProControllerResource);
+ public static SvgImage JoyconPairImage => GetResource(JoyConPairResource);
+ public static SvgImage JoyconLeftImage => GetResource(JoyConLeftResource);
+ public static SvgImage JoyconRightImage => GetResource(JoyConRightResource);
+
+ public string PlayerCount { get; set; } = "";
+ public bool SupportsProController { get; set; }
+ public bool SupportsLeftJoycon { get; set; }
+ public bool SupportsRightJoycon { get; set; }
+ public bool SupportsJoyconPair { get; set; }
+ public bool IsDocked { get; set; }
+
+ private readonly MainWindow _mainWindow;
+
+ public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUiArgs args)
+ {
+ if (args.PlayerCountMin == args.PlayerCountMax)
+ {
+ PlayerCount = args.PlayerCountMin.ToString();
+ }
+ else
+ {
+ PlayerCount = $"{args.PlayerCountMin} - {args.PlayerCountMax}";
+ }
+
+ SupportsProController = (args.SupportedStyles & ControllerType.ProController) != 0;
+ SupportsLeftJoycon = (args.SupportedStyles & ControllerType.JoyconLeft) != 0;
+ SupportsRightJoycon = (args.SupportedStyles & ControllerType.JoyconRight) != 0;
+ SupportsJoyconPair = (args.SupportedStyles & ControllerType.JoyconPair) != 0;
+
+ IsDocked = args.IsDocked;
+
+ _mainWindow = mainWindow;
+
+ DataContext = this;
+
+ InitializeComponent();
+ }
+
+ public ControllerAppletDialog(MainWindow mainWindow)
+ {
+ _mainWindow = mainWindow;
+ DataContext = this;
+
+ InitializeComponent();
+ }
+
+ public static async Task ShowControllerAppletDialog(MainWindow window, ControllerAppletUiArgs args)
+ {
+ ContentDialog contentDialog = new();
+ UserResult result = UserResult.Cancel;
+ ControllerAppletDialog content = new(window, args);
+
+ contentDialog.Title = LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle];
+ contentDialog.Content = content;
+
+ void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
+ {
+ if (eventArgs.Result == ContentDialogResult.Primary)
+ {
+ result = UserResult.Ok;
+ }
+ }
+
+ contentDialog.Closed += Handler;
+
+ Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType());
+ bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false));
+
+ contentDialog.Styles.Add(bottomBorder);
+
+ await ContentDialogHelper.ShowAsync(contentDialog);
+
+ return result;
+ }
+
+ private static SvgImage GetResource(string path)
+ {
+ SvgImage image = new();
+
+ if (!string.IsNullOrWhiteSpace(path))
+ {
+ SvgSource source = new();
+
+ source.Load(EmbeddedResources.GetStream(path));
+
+ image.Source = source;
+ }
+
+ return image;
+ }
+
+ public void OpenSettingsWindow()
+ {
+ if (_mainWindow.SettingsWindow == null)
+ {
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ _mainWindow.SettingsWindow = new SettingsWindow(_mainWindow.VirtualFileSystem, _mainWindow.ContentManager);
+ _mainWindow.SettingsWindow.NavPanel.Content = _mainWindow.SettingsWindow.InputPage;
+ _mainWindow.SettingsWindow.NavPanel.SelectedItem = _mainWindow.SettingsWindow.NavPanel.MenuItems.ElementAt(1);
+
+ await ContentDialogHelper.ShowWindowAsync(_mainWindow.SettingsWindow, _mainWindow);
+ _mainWindow.SettingsWindow = null;
+ this.Close();
+ });
+ }
+ }
+
+ public void Close()
+ {
+ ((ContentDialog)Parent)?.Hide();
+ }
+ }
+}
+
diff --git a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs
index 086de953..a57deb1a 100644
--- a/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs
+++ b/src/Ryujinx.Ava/UI/Helpers/ContentDialogHelper.cs
@@ -18,6 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
public static class ContentDialogHelper
{
private static bool _isChoiceDialogOpen;
+ private static ContentDialogOverlayWindow _contentDialogOverlayWindow;
private async static Task ShowContentDialog(
string title,
@@ -310,16 +311,20 @@ namespace Ryujinx.Ava.UI.Helpers
public static async Task ShowAsync(ContentDialog contentDialog)
{
ContentDialogResult result;
-
- ContentDialogOverlayWindow contentDialogOverlayWindow = null;
+ bool isTopDialog = true;
Window parent = GetMainWindow();
+ if (_contentDialogOverlayWindow != null)
+ {
+ isTopDialog = false;
+ }
+
if (parent is MainWindow window)
{
parent.Activate();
- contentDialogOverlayWindow = new()
+ _contentDialogOverlayWindow = new ContentDialogOverlayWindow
{
Height = parent.Bounds.Height,
Width = parent.Bounds.Width,
@@ -331,14 +336,14 @@ namespace Ryujinx.Ava.UI.Helpers
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
{
- contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
+ _contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
}
- contentDialogOverlayWindow.ContentDialog = contentDialog;
+ _contentDialogOverlayWindow.ContentDialog = contentDialog;
bool opened = false;
- contentDialogOverlayWindow.Opened += OverlayOnActivated;
+ _contentDialogOverlayWindow.Opened += OverlayOnActivated;
async void OverlayOnActivated(object sender, EventArgs e)
{
@@ -349,12 +354,12 @@ namespace Ryujinx.Ava.UI.Helpers
opened = true;
- contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
+ _contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
result = await ShowDialog();
}
- result = await contentDialogOverlayWindow.ShowDialog(parent);
+ result = await _contentDialogOverlayWindow.ShowDialog(parent);
}
else
{
@@ -363,11 +368,11 @@ namespace Ryujinx.Ava.UI.Helpers
async Task ShowDialog()
{
- if (contentDialogOverlayWindow is not null)
+ if (_contentDialogOverlayWindow is not null)
{
- result = await contentDialog.ShowAsync(contentDialogOverlayWindow);
+ result = await contentDialog.ShowAsync(_contentDialogOverlayWindow);
- contentDialogOverlayWindow!.Close();
+ _contentDialogOverlayWindow!.Close();
}
else
{
@@ -379,15 +384,22 @@ namespace Ryujinx.Ava.UI.Helpers
return result;
}
- if (contentDialogOverlayWindow is not null)
+ if (isTopDialog && _contentDialogOverlayWindow is not null)
{
- contentDialogOverlayWindow.Content = null;
- contentDialogOverlayWindow.Close();
+ _contentDialogOverlayWindow.Content = null;
+ _contentDialogOverlayWindow.Close();
}
return result;
}
+ public static Task ShowWindowAsync(Window dialogWindow, Window mainWindow = null)
+ {
+ mainWindow ??= GetMainWindow();
+
+ return dialogWindow.ShowDialog(_contentDialogOverlayWindow ?? mainWindow);
+ }
+
private static Window GetMainWindow()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
index b71fa82b..9c0e683a 100644
--- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
@@ -122,6 +122,8 @@ namespace Ryujinx.Ava.UI.Views.Main
await Window.SettingsWindow.ShowDialog(Window);
+ Window.SettingsWindow = null;
+
ViewModel.LoadConfigurableHotKeys();
}