ApplicationNavigation.cs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. using System.Diagnostics;
  2. namespace Terminal.Gui.App;
  3. /// <summary>
  4. /// Helper class for <see cref="Application"/> navigation. Held by <see cref="Application.Navigation"/>
  5. /// </summary>
  6. public class ApplicationNavigation
  7. {
  8. /// <summary>
  9. /// Initializes a new instance of the <see cref="ApplicationNavigation"/> class.
  10. /// </summary>
  11. public ApplicationNavigation ()
  12. {
  13. // TODO: Move navigation key bindings here from KeyboardImpl
  14. }
  15. /// <summary>
  16. /// The <see cref="IApplication"/> instance used by this instance.
  17. /// </summary>
  18. public IApplication? App { get; set; }
  19. private View? _focused;
  20. /// <summary>
  21. /// Raised when the most focused <see cref="View"/> in the application has changed.
  22. /// </summary>
  23. public event EventHandler<EventArgs>? FocusedChanged;
  24. /// <summary>
  25. /// Gets the most focused <see cref="View"/> in the application, if there is one.
  26. /// </summary>
  27. public View? GetFocused ()
  28. {
  29. return _focused;
  30. }
  31. // BUGBUG: This only gets Subviews and ignores Adornments. Should it use View.IsInHierarchy?
  32. /// <summary>
  33. /// Gets whether <paramref name="view"/> is in the SubView hierarchy of <paramref name="start"/>.
  34. /// </summary>
  35. /// <param name="start"></param>
  36. /// <param name="view"></param>
  37. /// <returns></returns>
  38. public static bool IsInHierarchy (View? start, View? view)
  39. {
  40. if (view is null)
  41. {
  42. return false;
  43. }
  44. if (view == start || start is null)
  45. {
  46. return true;
  47. }
  48. foreach (View subView in start.SubViews)
  49. {
  50. if (view == subView)
  51. {
  52. return true;
  53. }
  54. bool found = IsInHierarchy (subView, view);
  55. if (found)
  56. {
  57. return found;
  58. }
  59. }
  60. return false;
  61. }
  62. /// <summary>
  63. /// INTERNAL method to record the most focused <see cref="View"/> in the application.
  64. /// </summary>
  65. /// <remarks>
  66. /// Raises <see cref="FocusedChanged"/>.
  67. /// </remarks>
  68. internal void SetFocused (View? value)
  69. {
  70. if (_focused == value)
  71. {
  72. return;
  73. }
  74. Debug.Assert (value is null or { CanFocus: true, HasFocus: true });
  75. _focused = value;
  76. FocusedChanged?.Invoke (null, EventArgs.Empty);
  77. }
  78. /// <summary>
  79. /// Advances the focus to the next or previous view in the focus chain, based on
  80. /// <paramref name="direction"/>.
  81. /// </summary>
  82. /// <remarks>
  83. /// <para>
  84. /// If there is no next/previous view, the focus remains on the current view.
  85. /// </para>
  86. /// </remarks>
  87. /// <param name="direction">The direction to advance.</param>
  88. /// <param name="behavior">The tab behavior.</param>
  89. /// <returns>
  90. /// <see langword="true"/> if focus was changed to another subview (or stayed on this one), <see langword="false"/>
  91. /// otherwise.
  92. /// </returns>
  93. public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior)
  94. {
  95. if (App?.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover)
  96. {
  97. return visiblePopover.AdvanceFocus (direction, behavior);
  98. }
  99. return App?.TopRunnable is { } && App.TopRunnable.AdvanceFocus (direction, behavior);
  100. }
  101. }