ApplicationNavigation.cs 3.3 KB

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