PopoverBaseImpl.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. namespace Terminal.Gui.App;
  2. /// <summary>
  3. /// Abstract base class for popover views in Terminal.Gui. Implements <see cref="IPopover"/>.
  4. /// </summary>
  5. /// <remarks>
  6. /// <para>
  7. /// <b>IMPORTANT:</b> Popovers must be registered with <see cref="Application.Popover"/> using
  8. /// <see cref="ApplicationPopover.Register"/> before they can be shown.
  9. /// </para>
  10. /// <para>
  11. /// <b>Requirements:</b><br/>
  12. /// Derived classes must:
  13. /// </para>
  14. /// <list type="bullet">
  15. /// <item>Set <see cref="View.ViewportSettings"/> to include <see cref="ViewportSettingsFlags.Transparent"/> and <see cref="ViewportSettingsFlags.TransparentMouse"/>.</item>
  16. /// <item>Add a key binding for <see cref="Command.Quit"/> (typically bound to <see cref="Application.QuitKey"/>).</item>
  17. /// </list>
  18. /// <para>
  19. /// <b>Default Behavior:</b><br/>
  20. /// This base class provides:
  21. /// </para>
  22. /// <list type="bullet">
  23. /// <item>Fills the screen by default (<see cref="View.Width"/> = <see cref="Dim.Fill()"/>, <see cref="View.Height"/> = <see cref="Dim.Fill()"/>).</item>
  24. /// <item>Transparent viewport settings for proper mouse event handling.</item>
  25. /// <item>Automatic layout when becoming visible.</item>
  26. /// <item>Focus restoration when hidden.</item>
  27. /// <item>Default <see cref="Command.Quit"/> implementation that hides the popover.</item>
  28. /// </list>
  29. /// <para>
  30. /// <b>Lifecycle:</b><br/>
  31. /// Use <see cref="ApplicationPopover.Show"/> to display and <see cref="ApplicationPopover.Hide"/> or
  32. /// set <see cref="View.Visible"/> to <see langword="false"/> to hide.
  33. /// </para>
  34. /// </remarks>
  35. public abstract class PopoverBaseImpl : View, IPopover
  36. {
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="PopoverBaseImpl"/> class.
  39. /// </summary>
  40. /// <remarks>
  41. /// <para>
  42. /// Sets up default popover behavior:
  43. /// </para>
  44. /// <list type="bullet">
  45. /// <item>Fills the screen (<see cref="View.Width"/> = <see cref="Dim.Fill()"/>, <see cref="View.Height"/> = <see cref="Dim.Fill()"/>).</item>
  46. /// <item>Sets <see cref="View.CanFocus"/> to <see langword="true"/>.</item>
  47. /// <item>Configures <see cref="View.ViewportSettings"/> with <see cref="ViewportSettingsFlags.Transparent"/> and <see cref="ViewportSettingsFlags.TransparentMouse"/>.</item>
  48. /// <item>Adds <see cref="Command.Quit"/> bound to <see cref="Application.QuitKey"/> which hides the popover when invoked.</item>
  49. /// </list>
  50. /// </remarks>
  51. protected PopoverBaseImpl ()
  52. {
  53. Id = "popoverBaseImpl";
  54. CanFocus = true;
  55. Width = Dim.Fill ();
  56. Height = Dim.Fill ();
  57. ViewportSettings = ViewportSettingsFlags.Transparent | ViewportSettingsFlags.TransparentMouse;
  58. // TODO: Add a diagnostic setting for this?
  59. //TextFormatter.VerticalAlignment = Alignment.End;
  60. //TextFormatter.Alignment = Alignment.End;
  61. //base.Text = "popover";
  62. AddCommand (Command.Quit, Quit);
  63. KeyBindings.Add (Application.QuitKey, Command.Quit);
  64. return;
  65. bool? Quit (ICommandContext? ctx)
  66. {
  67. if (!Visible)
  68. {
  69. return false;
  70. }
  71. Visible = false;
  72. return true;
  73. }
  74. }
  75. private IRunnable? _current;
  76. /// <inheritdoc/>
  77. public IRunnable? Current
  78. {
  79. get => _current;
  80. set
  81. {
  82. _current = value;
  83. App ??= (_current as View)?.App;
  84. }
  85. }
  86. /// <summary>
  87. /// Called when the <see cref="View.Visible"/> property is changing. Handles layout and focus management.
  88. /// </summary>
  89. /// <returns>
  90. /// <see langword="true"/> to cancel the visibility change; otherwise, <see langword="false"/>.
  91. /// </returns>
  92. /// <remarks>
  93. /// <para>
  94. /// <b>When becoming visible:</b> Lays out the popover to fit the screen.
  95. /// </para>
  96. /// <para>
  97. /// <b>When becoming hidden:</b> Restores focus to the previously focused view in the view hierarchy.
  98. /// </para>
  99. /// </remarks>
  100. protected override bool OnVisibleChanging ()
  101. {
  102. bool ret = base.OnVisibleChanging ();
  103. if (ret)
  104. {
  105. return ret;
  106. }
  107. if (!Visible)
  108. {
  109. // Whenever visible is changing to true, we need to resize;
  110. // it's our only chance because we don't get laid out until we're visible
  111. if (App is { })
  112. {
  113. Layout (App.Screen.Size);
  114. }
  115. }
  116. else
  117. {
  118. // Whenever visible is changing to false, we need to reset the focus
  119. if (ApplicationNavigation.IsInHierarchy (this, App?.Navigation?.GetFocused ()))
  120. {
  121. App?.Navigation?.SetFocused (App?.TopRunnableView?.MostFocused);
  122. }
  123. }
  124. return ret;
  125. }
  126. }