UICatalogTop.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. using System.Collections.ObjectModel;
  2. using System.Diagnostics;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Text.Json.Serialization;
  7. using Microsoft.Extensions.Logging;
  8. using Terminal.Gui;
  9. using static Terminal.Gui.ConfigurationManager;
  10. using Command = Terminal.Gui.Command;
  11. using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
  12. #nullable enable
  13. namespace UICatalog;
  14. /// <summary>
  15. /// This is the main UI Catalog app view. It is run fresh when the app loads (if a Scenario has not been passed on
  16. /// the command line) and each time a Scenario ends.
  17. /// </summary>
  18. public class UICatalogTop : Toplevel
  19. {
  20. // When a scenario is run, the main app is killed. The static
  21. // members are cached so that when the scenario exits the
  22. // main app UI can be restored to previous state
  23. // Theme Management
  24. public static string? CachedTheme { get; set; }
  25. // Note, we used to pass this to scenarios that run, but it just added complexity
  26. // So that was removed. But we still have this here to demonstrate how changing
  27. // the scheme works.
  28. public static string? CachedTopLevelColorScheme { get; set; }
  29. // Diagnostics
  30. private static ViewDiagnosticFlags _diagnosticFlags;
  31. public UICatalogTop ()
  32. {
  33. _diagnosticFlags = Diagnostics;
  34. _menuBar = CreateMenuBar ();
  35. _statusBar = CreateStatusBar ();
  36. _categoryList = CreateCategoryList ();
  37. _scenarioList = CreateScenarioList ();
  38. Add (_menuBar, _categoryList, _scenarioList, _statusBar);
  39. Loaded += LoadedHandler;
  40. Unloaded += UnloadedHandler;
  41. // Restore previous selections
  42. _categoryList.SelectedItem = _cachedCategoryIndex;
  43. _scenarioList.SelectedRow = _cachedScenarioIndex;
  44. Applied += ConfigAppliedHandler;
  45. }
  46. private static bool _isFirstRunning = true;
  47. private void LoadedHandler (object? sender, EventArgs? args)
  48. {
  49. if (_disableMouseCb is { })
  50. {
  51. _disableMouseCb.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
  52. }
  53. if (_shVersion is { })
  54. {
  55. _shVersion.Title = $"{RuntimeEnvironment.OperatingSystem} {RuntimeEnvironment.OperatingSystemVersion}, {Driver!.GetVersionInfo ()}";
  56. }
  57. if (CachedSelectedScenario != null)
  58. {
  59. CachedSelectedScenario = null;
  60. _isFirstRunning = false;
  61. }
  62. if (!_isFirstRunning)
  63. {
  64. _scenarioList.SetFocus ();
  65. }
  66. if (_statusBar is { })
  67. {
  68. _statusBar.VisibleChanged += (s, e) => { ShowStatusBar = _statusBar.Visible; };
  69. }
  70. Loaded -= LoadedHandler;
  71. _categoryList!.EnsureSelectedItemVisible ();
  72. _scenarioList.EnsureSelectedCellIsVisible ();
  73. Apply ();
  74. }
  75. private void UnloadedHandler (object? sender, EventArgs? args)
  76. {
  77. Applied -= ConfigAppliedHandler;
  78. Unloaded -= UnloadedHandler;
  79. }
  80. #region MenuBar
  81. private readonly MenuBarv2? _menuBar;
  82. private CheckBox? _force16ColorsMenuItemCb;
  83. private OptionSelector? _themesRg;
  84. private OptionSelector? _topSchemeRg;
  85. private OptionSelector? _logLevelRg;
  86. private FlagSelector<ViewDiagnosticFlags>? _diagnosticFlagsSelector;
  87. private CheckBox? _disableMouseCb;
  88. private MenuBarv2 CreateMenuBar ()
  89. {
  90. MenuBarv2 menuBar = new (
  91. [
  92. new (
  93. "_File",
  94. [
  95. new MenuItemv2 ()
  96. {
  97. Title ="_Quit",
  98. HelpText = "Quit UI Catalog",
  99. Key = Application.QuitKey,
  100. // By not specifying TargetView the Key Binding will be Application-level
  101. Command = Command.Quit
  102. }
  103. ]),
  104. new ("_Themes", CreateThemeMenuItems ()),
  105. new ("Diag_nostics", CreateDiagnosticMenuItems ()),
  106. new ("_Logging", CreateLoggingMenuItems ()),
  107. new (
  108. "_Help",
  109. [
  110. new MenuItemv2 (
  111. "_Documentation",
  112. "",
  113. () => OpenUrl ("https://gui-cs.github.io/Terminal.GuiV2Docs"),
  114. Key.F1
  115. ),
  116. new MenuItemv2 (
  117. "_README",
  118. "",
  119. () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"),
  120. Key.F2
  121. ),
  122. new MenuItemv2 (
  123. "_About...",
  124. "About UI Catalog",
  125. () => MessageBox.Query (
  126. "",
  127. GetAboutBoxMessage (),
  128. wrapMessage: false,
  129. buttons: "_Ok"
  130. ),
  131. Key.A.WithCtrl
  132. )
  133. ])
  134. ])
  135. {
  136. Title = "menuBar",
  137. Id = "menuBar"
  138. };
  139. return menuBar;
  140. View [] CreateThemeMenuItems ()
  141. {
  142. List<View> menuItems = [];
  143. _force16ColorsMenuItemCb = new ()
  144. {
  145. Title = "Force _16 Colors",
  146. CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked
  147. };
  148. _force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
  149. {
  150. Application.Force16Colors = args.CurrentValue == CheckState.Checked;
  151. _force16ColorsShortcutCb!.CheckedState = args.CurrentValue;
  152. Application.LayoutAndDraw ();
  153. };
  154. menuItems.Add (
  155. new MenuItemv2
  156. {
  157. CommandView = _force16ColorsMenuItemCb
  158. });
  159. menuItems.Add (new Line ());
  160. _themesRg = new ()
  161. {
  162. HighlightStyle = HighlightStyle.None,
  163. SelectedItem = Themes.Keys.ToList ().IndexOf (CachedTheme!.Replace ("_", string.Empty))
  164. };
  165. _themesRg.SelectedItemChanged += (_, args) =>
  166. {
  167. Themes!.Theme = Themes!.Keys.ToArray () [args.SelectedItem!.Value];
  168. CachedTheme = Themes!.Keys.ToArray () [args.SelectedItem!.Value];
  169. Apply ();
  170. SetNeedsDraw ();
  171. };
  172. var menuItem = new MenuItemv2
  173. {
  174. CommandView = _themesRg,
  175. HelpText = "Cycle Through Themes",
  176. Key = Key.T.WithCtrl
  177. };
  178. menuItems.Add (menuItem);
  179. menuItems.Add (new Line ());
  180. _topSchemeRg = new ()
  181. {
  182. HighlightStyle = HighlightStyle.None,
  183. SelectedItem = Colors.ColorSchemes.Keys.ToList().IndexOf(CachedTopLevelColorScheme!)
  184. };
  185. _topSchemeRg.SelectedItemChanged += (_, args) =>
  186. {
  187. CachedTopLevelColorScheme = Colors.ColorSchemes.Keys.ToArray () [args.SelectedItem!.Value];
  188. ColorScheme = Colors.ColorSchemes [CachedTopLevelColorScheme];
  189. SetNeedsDraw ();
  190. };
  191. menuItem = new ()
  192. {
  193. Title = "Color Scheme for Application._Top",
  194. SubMenu = new (
  195. [
  196. new ()
  197. {
  198. CommandView = _topSchemeRg,
  199. HelpText = "Cycle Through Color Schemes",
  200. Key = Key.S.WithCtrl
  201. }
  202. ])
  203. };
  204. menuItems.Add (menuItem);
  205. UpdateThemesMenu ();
  206. return menuItems.ToArray ();
  207. }
  208. View [] CreateDiagnosticMenuItems ()
  209. {
  210. List<View> menuItems = [];
  211. _diagnosticFlagsSelector = new ()
  212. {
  213. CanFocus = true,
  214. Styles = FlagSelectorStyles.ShowNone,
  215. HighlightStyle = HighlightStyle.None,
  216. };
  217. _diagnosticFlagsSelector.UsedHotKeys.Add (Key.D);
  218. _diagnosticFlagsSelector.AssignHotKeysToCheckBoxes = true;
  219. _diagnosticFlagsSelector.Value = Diagnostics;
  220. _diagnosticFlagsSelector.ValueChanged += (sender, args) =>
  221. {
  222. _diagnosticFlags = (ViewDiagnosticFlags)_diagnosticFlagsSelector.Value;
  223. Diagnostics = _diagnosticFlags;
  224. };
  225. menuItems.Add (
  226. new MenuItemv2
  227. {
  228. CommandView = _diagnosticFlagsSelector,
  229. HelpText = "View Diagnostics"
  230. });
  231. menuItems.Add (new Line ());
  232. _disableMouseCb = new ()
  233. {
  234. Title = "_Disable Mouse",
  235. CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked
  236. };
  237. _disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.CurrentValue == CheckState.Checked; };
  238. menuItems.Add (
  239. new MenuItemv2
  240. {
  241. CommandView = _disableMouseCb,
  242. HelpText = "Disable Mouse"
  243. });
  244. return menuItems.ToArray ();
  245. }
  246. View [] CreateLoggingMenuItems ()
  247. {
  248. List<View?> menuItems = [];
  249. LogLevel [] logLevels = Enum.GetValues<LogLevel> ();
  250. _logLevelRg = new ()
  251. {
  252. AssignHotKeysToCheckBoxes = true,
  253. Options = Enum.GetNames<LogLevel> (),
  254. SelectedItem = logLevels.ToList ().IndexOf (Enum.Parse<LogLevel> (UICatalog.Options.DebugLogLevel)),
  255. HighlightStyle = HighlightStyle.Hover
  256. };
  257. _logLevelRg.SelectedItemChanged += (_, args) =>
  258. {
  259. UICatalog.Options = UICatalog.Options with { DebugLogLevel = Enum.GetName (logLevels [args.SelectedItem!.Value])! };
  260. UICatalog.LogLevelSwitch.MinimumLevel =
  261. UICatalog.LogLevelToLogEventLevel (Enum.Parse<LogLevel> (UICatalog.Options.DebugLogLevel));
  262. };
  263. menuItems.Add (
  264. new MenuItemv2
  265. {
  266. CommandView = _logLevelRg,
  267. HelpText = "Cycle Through Log Levels",
  268. Key = Key.L.WithCtrl
  269. });
  270. // add a separator
  271. menuItems.Add (new Line ());
  272. menuItems.Add (
  273. new MenuItemv2 (
  274. "_Open Log Folder",
  275. string.Empty,
  276. () => OpenUrl (UICatalog.LOGFILE_LOCATION)
  277. ));
  278. return menuItems.ToArray ()!;
  279. }
  280. }
  281. private void UpdateThemesMenu ()
  282. {
  283. if (_themesRg is null)
  284. {
  285. return;
  286. }
  287. _themesRg.AssignHotKeysToCheckBoxes = true;
  288. _themesRg.UsedHotKeys.Clear ();
  289. _themesRg.Options = Themes!.Keys.ToArray ();
  290. _themesRg.SelectedItem = Themes.Keys.ToList ().IndexOf (CachedTheme!.Replace ("_", string.Empty));
  291. if (_topSchemeRg is null)
  292. {
  293. return;
  294. }
  295. _topSchemeRg.AssignHotKeysToCheckBoxes = true;
  296. _topSchemeRg.UsedHotKeys.Clear ();
  297. int? selected = _topSchemeRg.SelectedItem;
  298. _topSchemeRg.Options = Colors.ColorSchemes.Keys.ToArray ();
  299. _topSchemeRg.SelectedItem = selected;
  300. if (CachedTopLevelColorScheme is null || !Colors.ColorSchemes.ContainsKey (CachedTopLevelColorScheme))
  301. {
  302. CachedTopLevelColorScheme = "Base";
  303. }
  304. _topSchemeRg.SelectedItem = Array.IndexOf (Colors.ColorSchemes.Keys.ToArray (), CachedTopLevelColorScheme);
  305. }
  306. #endregion MenuBar
  307. #region Scenario List
  308. private readonly TableView _scenarioList;
  309. private static int _cachedScenarioIndex;
  310. public static ObservableCollection<Scenario>? CachedScenarios { get; set; }
  311. // If set, holds the scenario the user selected to run
  312. public static Scenario? CachedSelectedScenario { get; set; }
  313. private TableView CreateScenarioList ()
  314. {
  315. // Create the scenario list. The contents of the scenario list changes whenever the
  316. // Category list selection changes (to show just the scenarios that belong to the selected
  317. // category).
  318. TableView scenarioList = new ()
  319. {
  320. X = Pos.Right (_categoryList!) - 1,
  321. Y = Pos.Bottom (_menuBar!),
  322. Width = Dim.Fill (),
  323. Height = Dim.Fill (
  324. Dim.Func (
  325. () =>
  326. {
  327. if (_statusBar!.NeedsLayout)
  328. {
  329. throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
  330. //_statusBar.Layout ();
  331. }
  332. return _statusBar.Frame.Height;
  333. })),
  334. //AllowsMarking = false,
  335. CanFocus = true,
  336. Title = "_Scenarios",
  337. BorderStyle = _categoryList!.BorderStyle,
  338. SuperViewRendersLineCanvas = true
  339. };
  340. // TableView provides many options for table headers. For simplicity, we turn all
  341. // of these off. By enabling FullRowSelect and turning off headers, TableView looks just
  342. // like a ListView
  343. scenarioList.FullRowSelect = true;
  344. scenarioList.Style.ShowHeaders = false;
  345. scenarioList.Style.ShowHorizontalHeaderOverline = false;
  346. scenarioList.Style.ShowHorizontalHeaderUnderline = false;
  347. scenarioList.Style.ShowHorizontalBottomline = false;
  348. scenarioList.Style.ShowVerticalCellLines = false;
  349. scenarioList.Style.ShowVerticalHeaderLines = false;
  350. /* By default, TableView lays out columns at render time and only
  351. * measures y rows of data at a time. Where y is the height of the
  352. * console. This is for the following reasons:
  353. *
  354. * - Performance, when tables have a large amount of data
  355. * - Defensive, prevents a single wide cell value pushing other
  356. * columns off-screen (requiring horizontal scrolling
  357. *
  358. * In the case of UICatalog here, such an approach is overkill so
  359. * we just measure all the data ourselves and set the appropriate
  360. * max widths as ColumnStyles
  361. */
  362. int longestName = CachedScenarios!.Max (s => s.GetName ().Length);
  363. scenarioList.Style.ColumnStyles.Add (
  364. 0,
  365. new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
  366. );
  367. scenarioList.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 });
  368. scenarioList.CellActivated += ScenarioView_OpenSelectedItem;
  369. // TableView typically is a grid where nav keys are biased for moving left/right.
  370. scenarioList.KeyBindings.Remove (Key.Home);
  371. scenarioList.KeyBindings.Add (Key.Home, Command.Start);
  372. scenarioList.KeyBindings.Remove (Key.End);
  373. scenarioList.KeyBindings.Add (Key.End, Command.End);
  374. // Ideally, TableView.MultiSelect = false would turn off any keybindings for
  375. // multi-select options. But it currently does not. UI Catalog uses Ctrl-A for
  376. // a shortcut to About.
  377. scenarioList.MultiSelect = false;
  378. scenarioList.KeyBindings.Remove (Key.A.WithCtrl);
  379. return scenarioList;
  380. }
  381. /// <summary>Launches the selected scenario, setting the global _selectedScenario</summary>
  382. /// <param name="e"></param>
  383. private void ScenarioView_OpenSelectedItem (object? sender, EventArgs? e)
  384. {
  385. if (CachedSelectedScenario is null)
  386. {
  387. // Save selected item state
  388. _cachedCategoryIndex = _categoryList!.SelectedItem;
  389. _cachedScenarioIndex = _scenarioList.SelectedRow;
  390. // Create new instance of scenario (even though Scenarios contains instances)
  391. var selectedScenarioName = (string)_scenarioList.Table [_scenarioList.SelectedRow, 0];
  392. CachedSelectedScenario = (Scenario)Activator.CreateInstance (
  393. CachedScenarios!.FirstOrDefault (
  394. s => s.GetName ()
  395. == selectedScenarioName
  396. )!
  397. .GetType ()
  398. )!;
  399. // Tell the main app to stop
  400. Application.RequestStop ();
  401. }
  402. }
  403. #endregion Scenario List
  404. #region Category List
  405. private readonly ListView? _categoryList;
  406. private static int _cachedCategoryIndex;
  407. public static ObservableCollection<string>? CachedCategories { get; set; }
  408. private ListView CreateCategoryList ()
  409. {
  410. // Create the Category list view. This list never changes.
  411. ListView categoryList = new ()
  412. {
  413. X = 0,
  414. Y = Pos.Bottom (_menuBar!),
  415. Width = Dim.Auto (),
  416. Height = Dim.Fill (
  417. Dim.Func (
  418. () =>
  419. {
  420. if (_statusBar!.NeedsLayout)
  421. {
  422. throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
  423. //_statusBar.Layout ();
  424. }
  425. return _statusBar.Frame.Height;
  426. })),
  427. AllowsMarking = false,
  428. CanFocus = true,
  429. Title = "_Categories",
  430. BorderStyle = LineStyle.Rounded,
  431. SuperViewRendersLineCanvas = true,
  432. Source = new ListWrapper<string> (CachedCategories)
  433. };
  434. categoryList.OpenSelectedItem += (s, a) => { _scenarioList!.SetFocus (); };
  435. categoryList.SelectedItemChanged += CategoryView_SelectedChanged;
  436. // This enables the scrollbar by causing lazy instantiation to happen
  437. categoryList.VerticalScrollBar.AutoShow = true;
  438. return categoryList;
  439. }
  440. private void CategoryView_SelectedChanged (object? sender, ListViewItemEventArgs? e)
  441. {
  442. string item = CachedCategories! [e!.Item];
  443. ObservableCollection<Scenario> newScenarioList;
  444. if (e.Item == 0)
  445. {
  446. // First category is "All"
  447. newScenarioList = CachedScenarios!;
  448. }
  449. else
  450. {
  451. newScenarioList = new (CachedScenarios!.Where (s => s.GetCategories ().Contains (item)).ToList ());
  452. }
  453. _scenarioList.Table = new EnumerableTableSource<Scenario> (
  454. newScenarioList,
  455. new ()
  456. {
  457. { "Name", s => s.GetName () }, { "Description", s => s.GetDescription () }
  458. }
  459. );
  460. }
  461. #endregion Category List
  462. #region StatusBar
  463. private readonly StatusBar? _statusBar;
  464. [SerializableConfigurationProperty (Scope = typeof (AppScope), OmitClassName = true)]
  465. [JsonPropertyName ("UICatalog.StatusBar")]
  466. public static bool ShowStatusBar { get; set; } = true;
  467. private Shortcut? _shQuit;
  468. private Shortcut? _shVersion;
  469. private CheckBox? _force16ColorsShortcutCb;
  470. private StatusBar CreateStatusBar ()
  471. {
  472. StatusBar statusBar = new ()
  473. {
  474. Visible = ShowStatusBar,
  475. AlignmentModes = AlignmentModes.IgnoreFirstOrLast,
  476. CanFocus = false
  477. };
  478. // ReSharper disable All
  479. statusBar.Height = Dim.Auto (
  480. DimAutoStyle.Auto,
  481. minimumContentDim: Dim.Func (() => statusBar.Visible ? 1 : 0),
  482. maximumContentDim: Dim.Func (() => statusBar.Visible ? 1 : 0));
  483. // ReSharper restore All
  484. _shQuit = new ()
  485. {
  486. CanFocus = false,
  487. Title = "Quit",
  488. Key = Application.QuitKey
  489. };
  490. _shVersion = new ()
  491. {
  492. Title = "Version Info",
  493. CanFocus = false
  494. };
  495. var statusBarShortcut = new Shortcut
  496. {
  497. Key = Key.F10,
  498. Title = "Show/Hide Status Bar",
  499. CanFocus = false
  500. };
  501. statusBarShortcut.Accepting += (sender, args) =>
  502. {
  503. statusBar.Visible = !_statusBar!.Visible;
  504. args.Handled = true;
  505. };
  506. _force16ColorsShortcutCb = new ()
  507. {
  508. Title = "16 color mode",
  509. CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked,
  510. CanFocus = false
  511. };
  512. _force16ColorsShortcutCb.CheckedStateChanging += (sender, args) =>
  513. {
  514. Application.Force16Colors = args.NewValue == CheckState.Checked;
  515. _force16ColorsMenuItemCb!.CheckedState = args.NewValue;
  516. Application.LayoutAndDraw ();
  517. };
  518. statusBar.Add (
  519. _shQuit,
  520. statusBarShortcut,
  521. new Shortcut
  522. {
  523. CanFocus = false,
  524. CommandView = _force16ColorsShortcutCb,
  525. HelpText = "",
  526. BindKeyToApplication = true,
  527. Key = Key.F7
  528. },
  529. _shVersion
  530. );
  531. return statusBar;
  532. }
  533. #endregion StatusBar
  534. #region Configuration Manager
  535. /// <summary>
  536. /// Called when CM has applied changes.
  537. /// </summary>
  538. private void ConfigApplied ()
  539. {
  540. CachedTheme = Themes?.Theme;
  541. UpdateThemesMenu ();
  542. ColorScheme = Colors.ColorSchemes [CachedTopLevelColorScheme!];
  543. if (_shQuit is { })
  544. {
  545. _shQuit.Key = Application.QuitKey;
  546. }
  547. if (_statusBar is { })
  548. {
  549. _statusBar.Visible = ShowStatusBar;
  550. }
  551. _disableMouseCb!.CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked;
  552. _force16ColorsShortcutCb!.CheckedState = Application.Force16Colors ? CheckState.Checked : CheckState.UnChecked;
  553. Application.Top?.SetNeedsDraw ();
  554. }
  555. private void ConfigAppliedHandler (object? sender, ConfigurationManagerEventArgs? a) { ConfigApplied (); }
  556. #endregion Configuration Manager
  557. /// <summary>
  558. /// Gets the message displayed in the About Box. `public` so it can be used from Unit tests.
  559. /// </summary>
  560. /// <returns></returns>
  561. public static string GetAboutBoxMessage ()
  562. {
  563. // NOTE: Do not use multiline verbatim strings here.
  564. // WSL gets all confused.
  565. StringBuilder msg = new ();
  566. msg.AppendLine ("UI Catalog: A comprehensive sample library and test app for");
  567. msg.AppendLine ();
  568. msg.AppendLine (
  569. """
  570. _______ _ _ _____ _
  571. |__ __| (_) | | / ____| (_)
  572. | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _
  573. | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | |
  574. | | __/ | | | | | | | | | | | (_| | || |__| | |_| | |
  575. |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_|
  576. """);
  577. msg.AppendLine ();
  578. msg.AppendLine ("v2 - Pre-Alpha");
  579. msg.AppendLine ();
  580. msg.AppendLine ("https://github.com/gui-cs/Terminal.Gui");
  581. return msg.ToString ();
  582. }
  583. public static void OpenUrl (string url)
  584. {
  585. if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows))
  586. {
  587. url = url.Replace ("&", "^&");
  588. Process.Start (new ProcessStartInfo ("cmd", $"/c start {url}") { CreateNoWindow = true });
  589. }
  590. else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux))
  591. {
  592. using var process = new Process
  593. {
  594. StartInfo = new ()
  595. {
  596. FileName = "xdg-open",
  597. Arguments = url,
  598. RedirectStandardError = true,
  599. RedirectStandardOutput = true,
  600. CreateNoWindow = true,
  601. UseShellExecute = false
  602. }
  603. };
  604. process.Start ();
  605. }
  606. else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX))
  607. {
  608. Process.Start ("open", url);
  609. }
  610. }
  611. }