ContentDialogHelper.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. using Avalonia.Controls;
  2. using Avalonia.Threading;
  3. using FluentAvalonia.UI.Controls;
  4. using Ryujinx.Ava.Common.Locale;
  5. using Ryujinx.Ava.Ui.Models;
  6. using Ryujinx.Ava.Ui.Windows;
  7. using Ryujinx.Common.Logging;
  8. using System;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. namespace Ryujinx.Ava.Ui.Controls
  12. {
  13. public static class ContentDialogHelper
  14. {
  15. private static bool _isChoiceDialogOpen;
  16. private async static Task<UserResult> ShowContentDialog(
  17. StyleableWindow window,
  18. string title,
  19. string primaryText,
  20. string secondaryText,
  21. string primaryButton,
  22. string secondaryButton,
  23. string closeButton,
  24. int iconSymbol,
  25. UserResult primaryButtonResult = UserResult.Ok)
  26. {
  27. UserResult result = UserResult.None;
  28. ContentDialog contentDialog = window.ContentDialog;
  29. await ShowDialog();
  30. async Task ShowDialog()
  31. {
  32. if (contentDialog != null)
  33. {
  34. contentDialog.Title = title;
  35. contentDialog.PrimaryButtonText = primaryButton;
  36. contentDialog.SecondaryButtonText = secondaryButton;
  37. contentDialog.CloseButtonText = closeButton;
  38. contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
  39. contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
  40. {
  41. result = primaryButtonResult;
  42. });
  43. contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
  44. {
  45. result = UserResult.No;
  46. });
  47. contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
  48. {
  49. result = UserResult.Cancel;
  50. });
  51. await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
  52. };
  53. }
  54. return result;
  55. }
  56. public async static Task<UserResult> ShowDeferredContentDialog(
  57. StyleableWindow window,
  58. string title,
  59. string primaryText,
  60. string secondaryText,
  61. string primaryButton,
  62. string secondaryButton,
  63. string closeButton,
  64. int iconSymbol,
  65. ManualResetEvent deferResetEvent,
  66. Func<Window, Task> doWhileDeferred = null)
  67. {
  68. bool startedDeferring = false;
  69. UserResult result = UserResult.None;
  70. ContentDialog contentDialog = window.ContentDialog;
  71. Window overlay = window;
  72. if (contentDialog != null)
  73. {
  74. contentDialog.PrimaryButtonClick += DeferClose;
  75. contentDialog.Title = title;
  76. contentDialog.PrimaryButtonText = primaryButton;
  77. contentDialog.SecondaryButtonText = secondaryButton;
  78. contentDialog.CloseButtonText = closeButton;
  79. contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
  80. contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
  81. {
  82. result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
  83. });
  84. contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
  85. {
  86. contentDialog.PrimaryButtonClick -= DeferClose;
  87. result = UserResult.No;
  88. });
  89. contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
  90. {
  91. contentDialog.PrimaryButtonClick -= DeferClose;
  92. result = UserResult.Cancel;
  93. });
  94. await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
  95. };
  96. return result;
  97. async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
  98. {
  99. if (startedDeferring)
  100. {
  101. return;
  102. }
  103. contentDialog.PrimaryButtonClick -= DeferClose;
  104. startedDeferring = true;
  105. var deferral = args.GetDeferral();
  106. result = primaryButton == LocaleManager.Instance["InputDialogYes"] ? UserResult.Yes : UserResult.Ok;
  107. contentDialog.PrimaryButtonClick -= DeferClose;
  108. #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
  109. Task.Run(() =>
  110. {
  111. deferResetEvent.WaitOne();
  112. Dispatcher.UIThread.Post(() =>
  113. {
  114. deferral.Complete();
  115. });
  116. });
  117. #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
  118. if (doWhileDeferred != null)
  119. {
  120. await doWhileDeferred(overlay);
  121. deferResetEvent.Set();
  122. }
  123. }
  124. }
  125. private static Grid CreateDialogTextContent(string primaryText, string secondaryText, int symbol)
  126. {
  127. Grid content = new Grid();
  128. content.RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() };
  129. content.ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() };
  130. content.MinHeight = 80;
  131. SymbolIcon icon = new SymbolIcon { Symbol = (Symbol)symbol, Margin = new Avalonia.Thickness(10) };
  132. icon.FontSize = 40;
  133. icon.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
  134. Grid.SetColumn(icon, 0);
  135. Grid.SetRowSpan(icon, 2);
  136. Grid.SetRow(icon, 0);
  137. TextBlock primaryLabel = new TextBlock()
  138. {
  139. Text = primaryText,
  140. Margin = new Avalonia.Thickness(5),
  141. TextWrapping = Avalonia.Media.TextWrapping.Wrap,
  142. MaxWidth = 450
  143. };
  144. TextBlock secondaryLabel = new TextBlock()
  145. {
  146. Text = secondaryText,
  147. Margin = new Avalonia.Thickness(5),
  148. TextWrapping = Avalonia.Media.TextWrapping.Wrap,
  149. MaxWidth = 450
  150. };
  151. Grid.SetColumn(primaryLabel, 1);
  152. Grid.SetColumn(secondaryLabel, 1);
  153. Grid.SetRow(primaryLabel, 0);
  154. Grid.SetRow(secondaryLabel, 1);
  155. content.Children.Add(icon);
  156. content.Children.Add(primaryLabel);
  157. content.Children.Add(secondaryLabel);
  158. return content;
  159. }
  160. public static async Task<UserResult> CreateInfoDialog(
  161. StyleableWindow window,
  162. string primary,
  163. string secondaryText,
  164. string acceptButton,
  165. string closeButton,
  166. string title)
  167. {
  168. return await ShowContentDialog(
  169. window,
  170. title,
  171. primary,
  172. secondaryText,
  173. acceptButton,
  174. "",
  175. closeButton,
  176. (int)Symbol.Important);
  177. }
  178. internal static async Task<UserResult> CreateConfirmationDialog(
  179. StyleableWindow window,
  180. string primaryText,
  181. string secondaryText,
  182. string acceptButtonText,
  183. string cancelButtonText,
  184. string title,
  185. UserResult primaryButtonResult = UserResult.Yes)
  186. {
  187. return await ShowContentDialog(
  188. window,
  189. string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance["DialogConfirmationTitle"] : title,
  190. primaryText,
  191. secondaryText,
  192. acceptButtonText,
  193. "",
  194. cancelButtonText,
  195. (int)Symbol.Help,
  196. primaryButtonResult);
  197. }
  198. internal static UpdateWaitWindow CreateWaitingDialog(string mainText, string secondaryText)
  199. {
  200. return new(mainText, secondaryText);
  201. }
  202. internal static async Task CreateUpdaterInfoDialog(StyleableWindow window, string primary, string secondaryText)
  203. {
  204. await ShowContentDialog(
  205. window,
  206. LocaleManager.Instance["DialogUpdaterTitle"],
  207. primary,
  208. secondaryText,
  209. "",
  210. "",
  211. LocaleManager.Instance["InputDialogOk"],
  212. (int)Symbol.Important);
  213. }
  214. internal static async Task ShowNotAvailableMessage(StyleableWindow window)
  215. {
  216. // Temporary placeholder for features to be added
  217. await ShowContentDialog(
  218. window,
  219. "Feature Not Available",
  220. "The selected feature is not available in this version.",
  221. "",
  222. "",
  223. "",
  224. LocaleManager.Instance["InputDialogOk"],
  225. (int)Symbol.Important);
  226. }
  227. internal static async Task CreateWarningDialog(StyleableWindow window, string primary, string secondaryText)
  228. {
  229. await ShowContentDialog(
  230. window,
  231. LocaleManager.Instance["DialogWarningTitle"],
  232. primary,
  233. secondaryText,
  234. "",
  235. "",
  236. LocaleManager.Instance["InputDialogOk"],
  237. (int)Symbol.Important);
  238. }
  239. internal static async Task CreateErrorDialog(StyleableWindow owner, string errorMessage, string secondaryErrorMessage = "")
  240. {
  241. Logger.Error?.Print(LogClass.Application, errorMessage);
  242. await ShowContentDialog(
  243. owner,
  244. LocaleManager.Instance["DialogErrorTitle"],
  245. LocaleManager.Instance["DialogErrorMessage"],
  246. errorMessage,
  247. secondaryErrorMessage,
  248. "",
  249. LocaleManager.Instance["InputDialogOk"],
  250. (int)Symbol.Dismiss);
  251. }
  252. internal static async Task<bool> CreateChoiceDialog(StyleableWindow window, string title, string primary, string secondaryText)
  253. {
  254. if (_isChoiceDialogOpen)
  255. {
  256. return false;
  257. }
  258. _isChoiceDialogOpen = true;
  259. UserResult response =
  260. await ShowContentDialog(
  261. window,
  262. title,
  263. primary,
  264. secondaryText,
  265. LocaleManager.Instance["InputDialogYes"],
  266. "",
  267. LocaleManager.Instance["InputDialogNo"],
  268. (int)Symbol.Help,
  269. UserResult.Yes);
  270. _isChoiceDialogOpen = false;
  271. return response == UserResult.Yes;
  272. }
  273. internal static async Task<bool> CreateExitDialog(StyleableWindow owner)
  274. {
  275. return await CreateChoiceDialog(
  276. owner,
  277. LocaleManager.Instance["DialogExitTitle"],
  278. LocaleManager.Instance["DialogExitMessage"],
  279. LocaleManager.Instance["DialogExitSubMessage"]);
  280. }
  281. internal static async Task<bool> CreateStopEmulationDialog(StyleableWindow owner)
  282. {
  283. return await CreateChoiceDialog(
  284. owner,
  285. LocaleManager.Instance["DialogStopEmulationTitle"],
  286. LocaleManager.Instance["DialogStopEmulationMessage"],
  287. LocaleManager.Instance["DialogExitSubMessage"]);
  288. }
  289. internal static async Task<string> CreateInputDialog(
  290. string title,
  291. string mainText,
  292. string subText,
  293. StyleableWindow owner,
  294. uint maxLength = int.MaxValue,
  295. string input = "")
  296. {
  297. var result = await InputDialog.ShowInputDialog(
  298. owner,
  299. title,
  300. mainText,
  301. input,
  302. subText,
  303. maxLength);
  304. if (result.Result == UserResult.Ok)
  305. {
  306. return result.Input;
  307. }
  308. return string.Empty;
  309. }
  310. }
  311. }