From ec6cb0abb4b7669895b6e96fd7581c93b5abd691 Mon Sep 17 00:00:00 2001
From: Mary Guillemard <mary@mary.zone>
Date: Sat, 2 Mar 2024 12:51:05 +0100
Subject: infra: Make Avalonia the default UI  (#6375)

* misc: Move Ryujinx project to Ryujinx.Gtk3

This breaks release CI for now but that's fine.

Signed-off-by: Mary Guillemard <mary@mary.zone>

* misc: Move Ryujinx.Ava project to Ryujinx

This breaks CI for now, but it's fine.

Signed-off-by: Mary Guillemard <mary@mary.zone>

* infra: Make Avalonia the default UI

Should fix CI after the previous changes.

GTK3 isn't build by the release job anymore, only by PR CI.

This also ensure that the test-ava update package is still generated to
allow update from the old testing channel.

Signed-off-by: Mary Guillemard <mary@mary.zone>

* Fix missing copy in create_app_bundle.sh

Signed-off-by: Mary Guillemard <mary@mary.zone>

* Fix syntax error

Signed-off-by: Mary Guillemard <mary@mary.zone>

---------

Signed-off-by: Mary Guillemard <mary@mary.zone>
---
 src/Ryujinx/UI/Helpers/ContentDialogHelper.cs | 425 ++++++++++++++++++++++++++
 1 file changed, 425 insertions(+)
 create mode 100644 src/Ryujinx/UI/Helpers/ContentDialogHelper.cs

(limited to 'src/Ryujinx/UI/Helpers/ContentDialogHelper.cs')

diff --git a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs
new file mode 100644
index 00000000..15b7ddd1
--- /dev/null
+++ b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs
@@ -0,0 +1,425 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Layout;
+using Avalonia.Media;
+using Avalonia.Threading;
+using FluentAvalonia.Core;
+using FluentAvalonia.UI.Controls;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.UI.Windows;
+using Ryujinx.Common.Logging;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ryujinx.Ava.UI.Helpers
+{
+    public static class ContentDialogHelper
+    {
+        private static bool _isChoiceDialogOpen;
+        private static ContentDialogOverlayWindow _contentDialogOverlayWindow;
+
+        private async static Task<UserResult> ShowContentDialog(
+             string title,
+             object content,
+             string primaryButton,
+             string secondaryButton,
+             string closeButton,
+             UserResult primaryButtonResult = UserResult.Ok,
+             ManualResetEvent deferResetEvent = null,
+             TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
+        {
+            UserResult result = UserResult.None;
+
+            ContentDialog contentDialog = new()
+            {
+                Title = title,
+                PrimaryButtonText = primaryButton,
+                SecondaryButtonText = secondaryButton,
+                CloseButtonText = closeButton,
+                Content = content,
+                PrimaryButtonCommand = MiniCommand.Create(() =>
+                {
+                    result = primaryButtonResult;
+                }),
+            };
+
+            contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
+            {
+                result = UserResult.No;
+                contentDialog.PrimaryButtonClick -= deferCloseAction;
+            });
+
+            contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
+            {
+                result = UserResult.Cancel;
+                contentDialog.PrimaryButtonClick -= deferCloseAction;
+            });
+
+            if (deferResetEvent != null)
+            {
+                contentDialog.PrimaryButtonClick += deferCloseAction;
+            }
+
+            await ShowAsync(contentDialog);
+
+            return result;
+        }
+
+        public 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,
+            TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
+        {
+            Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
+
+            return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction);
+        }
+
+        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;
+
+            return await ShowTextDialog(
+                title,
+                primaryText,
+                secondaryText,
+                primaryButton,
+                secondaryButton,
+                closeButton,
+                iconSymbol,
+                primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
+                deferResetEvent,
+                DeferClose);
+
+            async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
+            {
+                if (startedDeferring)
+                {
+                    return;
+                }
+
+                sender.PrimaryButtonClick -= DeferClose;
+
+                startedDeferring = true;
+
+                var deferral = args.GetDeferral();
+
+                sender.PrimaryButtonClick -= DeferClose;
+
+                _ = Task.Run(() =>
+                {
+                    deferResetEvent.WaitOne();
+
+                    Dispatcher.UIThread.Post(() =>
+                    {
+                        deferral.Complete();
+                    });
+                });
+
+                if (doWhileDeferred != null)
+                {
+                    await doWhileDeferred(window);
+
+                    deferResetEvent.Set();
+                }
+            }
+        }
+
+        private static Grid CreateTextDialogContent(string primaryText, string secondaryText, int symbol)
+        {
+            Grid content = new()
+            {
+                RowDefinitions = new RowDefinitions { new(), new() },
+                ColumnDefinitions = new ColumnDefinitions { new(GridLength.Auto), new() },
+
+                MinHeight = 80,
+            };
+
+            SymbolIcon icon = new()
+            {
+                Symbol = (Symbol)symbol,
+                Margin = new Thickness(10),
+                FontSize = 40,
+                VerticalAlignment = VerticalAlignment.Center,
+            };
+
+            Grid.SetColumn(icon, 0);
+            Grid.SetRowSpan(icon, 2);
+            Grid.SetRow(icon, 0);
+
+            TextBlock primaryLabel = new()
+            {
+                Text = primaryText,
+                Margin = new Thickness(5),
+                TextWrapping = TextWrapping.Wrap,
+                MaxWidth = 450,
+            };
+
+            TextBlock secondaryLabel = new()
+            {
+                Text = secondaryText,
+                Margin = new Thickness(5),
+                TextWrapping = TextWrapping.Wrap,
+                MaxWidth = 450,
+            };
+
+            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)
+        {
+            return await ShowTextDialog(
+                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)
+        {
+            return await ShowTextDialog(
+                string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title,
+                primaryText,
+                secondaryText,
+                acceptButtonText,
+                "",
+                cancelButtonText,
+                (int)Symbol.Help,
+                primaryButtonResult);
+        }
+
+        internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
+        {
+            await ShowTextDialog(
+                LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
+                primary,
+                secondaryText,
+                "",
+                "",
+                LocaleManager.Instance[LocaleKeys.InputDialogOk],
+                (int)Symbol.Important);
+        }
+
+        internal static async Task CreateWarningDialog(string primary, string secondaryText)
+        {
+            await ShowTextDialog(
+                LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
+                primary,
+                secondaryText,
+                "",
+                "",
+                LocaleManager.Instance[LocaleKeys.InputDialogOk],
+                (int)Symbol.Important);
+        }
+
+        internal static async Task CreateErrorDialog(string errorMessage, string secondaryErrorMessage = "")
+        {
+            Logger.Error?.Print(LogClass.Application, errorMessage);
+
+            await ShowTextDialog(
+                LocaleManager.Instance[LocaleKeys.DialogErrorTitle],
+                LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
+                errorMessage,
+                secondaryErrorMessage,
+                "",
+                LocaleManager.Instance[LocaleKeys.InputDialogOk],
+                (int)Symbol.Dismiss);
+        }
+
+        internal static async Task<bool> CreateChoiceDialog(string title, string primary, string secondaryText)
+        {
+            if (_isChoiceDialogOpen)
+            {
+                return false;
+            }
+
+            _isChoiceDialogOpen = true;
+
+            UserResult response = await ShowTextDialog(
+                title,
+                primary,
+                secondaryText,
+                LocaleManager.Instance[LocaleKeys.InputDialogYes],
+                "",
+                LocaleManager.Instance[LocaleKeys.InputDialogNo],
+                (int)Symbol.Help,
+                UserResult.Yes);
+
+            _isChoiceDialogOpen = false;
+
+            return response == UserResult.Yes;
+        }
+
+        internal static async Task<bool> CreateExitDialog()
+        {
+            return await CreateChoiceDialog(
+                LocaleManager.Instance[LocaleKeys.DialogExitTitle],
+                LocaleManager.Instance[LocaleKeys.DialogExitMessage],
+                LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
+        }
+
+        internal static async Task<bool> CreateStopEmulationDialog()
+        {
+            return await CreateChoiceDialog(
+                LocaleManager.Instance[LocaleKeys.DialogStopEmulationTitle],
+                LocaleManager.Instance[LocaleKeys.DialogStopEmulationMessage],
+                LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
+        }
+
+        public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
+        {
+            ContentDialogResult result;
+            bool isTopDialog = true;
+
+            Window parent = GetMainWindow();
+
+            if (_contentDialogOverlayWindow != null)
+            {
+                isTopDialog = false;
+            }
+
+            if (parent is MainWindow window)
+            {
+                parent.Activate();
+
+                _contentDialogOverlayWindow = new ContentDialogOverlayWindow
+                {
+                    Height = parent.Bounds.Height,
+                    Width = parent.Bounds.Width,
+                    Position = parent.PointToScreen(new Point()),
+                    ShowInTaskbar = false,
+                };
+
+                parent.PositionChanged += OverlayOnPositionChanged;
+
+                void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
+                {
+                    if (_contentDialogOverlayWindow is null)
+                    {
+                        return;
+                    }
+
+                    _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 = ContentDialogResult.None;
+
+                    Logger.Warning?.Print(LogClass.UI, "Content dialog overlay failed to populate. Default value has been returned.");
+                }
+
+                return result;
+            }
+
+            if (isTopDialog && _contentDialogOverlayWindow is not null)
+            {
+                _contentDialogOverlayWindow.Content = null;
+                _contentDialogOverlayWindow.Close();
+                _contentDialogOverlayWindow = null;
+            }
+
+            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)
+            {
+                foreach (Window item in al.Windows)
+                {
+                    if (item is MainWindow window)
+                    {
+                        return window;
+                    }
+                }
+            }
+
+            return null;
+        }
+    }
+}
-- 
cgit v1.2.3-70-g09d2