ApplicationPopover.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #nullable enable
  2. using System.Diagnostics;
  3. namespace Terminal.Gui;
  4. /// <summary>
  5. /// Helper class for support of <see cref="IPopover"/> views for <see cref="Application"/>. Held by <see cref="Application.Popover"/>
  6. /// </summary>
  7. public class ApplicationPopover
  8. {
  9. /// <summary>
  10. /// Initializes a new instance of the <see cref="ApplicationPopover"/> class.
  11. /// </summary>
  12. public ApplicationPopover () { }
  13. private readonly List<IPopover> _popovers = [];
  14. /// <summary></summary>
  15. public IReadOnlyCollection<IPopover> Popovers => _popovers.AsReadOnly ();
  16. /// <summary>
  17. /// Registers <paramref name="popover"/> with the application.
  18. /// This enables the popover to receive keyboard events even when when it is not active.
  19. /// </summary>
  20. /// <param name="popover"></param>
  21. public void Register (IPopover? popover)
  22. {
  23. if (popover is { } && !_popovers.Contains (popover))
  24. {
  25. _popovers.Add (popover);
  26. }
  27. }
  28. /// <summary>
  29. /// De-registers <paramref name="popover"/> with the application. Use this to remove the popover and it's
  30. /// keyboard bindings from the application.
  31. /// </summary>
  32. /// <param name="popover"></param>
  33. /// <returns></returns>
  34. public bool DeRegister (IPopover? popover)
  35. {
  36. if (popover is { } && _popovers.Contains (popover))
  37. {
  38. if (GetActivePopover () == popover)
  39. {
  40. _activePopover = null;
  41. }
  42. _popovers.Remove (popover);
  43. return true;
  44. }
  45. return false;
  46. }
  47. private IPopover? _activePopover;
  48. /// <summary>
  49. /// Gets the active popover, if any.
  50. /// </summary>
  51. /// <returns></returns>
  52. public IPopover? GetActivePopover () { return _activePopover; }
  53. /// <summary>
  54. /// Shows <paramref name="popover"/>. IPopover implementations should use OnVisibleChnaged/VisibleChanged to be
  55. /// notified when the user has done something to cause the popover to be hidden.
  56. /// </summary>
  57. /// <remarks>
  58. /// <para>
  59. /// Note, this API calls <see cref="Register"/>. To disable the popover from processing keyboard events,
  60. /// either call <see cref="DeRegister"/> to
  61. /// remove the popover from the application or set <see cref="View.Enabled"/> to <see langword="false"/>.
  62. /// </para>
  63. /// </remarks>
  64. /// <param name="popover"></param>
  65. public void ShowPopover (IPopover? popover)
  66. {
  67. // If there's an existing popover, hide it.
  68. if (_activePopover is View popoverView)
  69. {
  70. popoverView.Visible = false;
  71. _activePopover = null;
  72. }
  73. if (popover is View newPopover)
  74. {
  75. Register (popover);
  76. if (!newPopover.IsInitialized)
  77. {
  78. newPopover.BeginInit ();
  79. newPopover.EndInit ();
  80. }
  81. _activePopover = newPopover as IPopover;
  82. newPopover.Enabled = true;
  83. newPopover.Visible = true;
  84. }
  85. }
  86. /// <summary>
  87. /// Causes the specified popover to be hidden.
  88. /// If the popover is dervied from <see cref="PopoverBaseImpl"/>, this is the same as setting <see cref="View.Visible"/> to <see langword="false"/>.
  89. /// </summary>
  90. /// <param name="popover"></param>
  91. public void HidePopover (IPopover? popover)
  92. {
  93. // If there's an existing popover, hide it.
  94. if (_activePopover is View popoverView && popoverView == popover)
  95. {
  96. popoverView.Visible = false;
  97. _activePopover = null;
  98. Application.Top?.SetNeedsDraw ();
  99. }
  100. }
  101. /// <summary>
  102. /// Called when the user presses a key. Dispatches the key to the active popover, if any,
  103. /// otherwise to the popovers in the order they were registered. Inactive popovers only get hotkeys.
  104. /// </summary>
  105. /// <param name="key"></param>
  106. /// <returns></returns>
  107. internal bool DispatchKeyDown (Key key)
  108. {
  109. // Do active first - Active gets all key down events.
  110. if (GetActivePopover () as View is { Visible: true } visiblePopover)
  111. {
  112. if (visiblePopover.NewKeyDownEvent (key))
  113. {
  114. return true;
  115. }
  116. }
  117. // If the active popover didn't handle the key, try the inactive ones.
  118. // Inactive only get hotkeys
  119. bool? hotKeyHandled = null;
  120. foreach (IPopover popover in _popovers)
  121. {
  122. if (GetActivePopover () == popover || popover is not View popoverView)
  123. {
  124. continue;
  125. }
  126. // hotKeyHandled = popoverView.InvokeCommandsBoundToHotKey (key);
  127. hotKeyHandled = popoverView.NewKeyDownEvent (key);
  128. if (hotKeyHandled is true)
  129. {
  130. return true;
  131. }
  132. }
  133. return hotKeyHandled is true;
  134. }
  135. }