Application.Navigation.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. /// <summary>
  4. /// Helper class for <see cref="Application"/> navigation.
  5. /// </summary>
  6. internal static class ApplicationNavigation
  7. {
  8. /// <summary>
  9. /// Gets the deepest focused subview of the specified <paramref name="view"/>.
  10. /// </summary>
  11. /// <param name="view"></param>
  12. /// <returns></returns>
  13. internal static View? GetDeepestFocusedSubview (View? view)
  14. {
  15. if (view is null)
  16. {
  17. return null;
  18. }
  19. foreach (View v in view.Subviews)
  20. {
  21. if (v.HasFocus)
  22. {
  23. return GetDeepestFocusedSubview (v);
  24. }
  25. }
  26. return view;
  27. }
  28. /// <summary>
  29. /// Sets the focus to the next view in the <see cref="View.TabIndexes"/> list. If the last view is focused, the first view is focused.
  30. /// </summary>
  31. /// <param name="viewsInTabIndexes"></param>
  32. /// <param name="direction"></param>
  33. internal static void FocusNearestView (IEnumerable<View>? viewsInTabIndexes, View.NavigationDirection direction)
  34. {
  35. if (viewsInTabIndexes is null)
  36. {
  37. return;
  38. }
  39. var found = false;
  40. var focusProcessed = false;
  41. var idx = 0;
  42. foreach (View v in viewsInTabIndexes)
  43. {
  44. if (v == Application.Current)
  45. {
  46. found = true;
  47. }
  48. if (found && v != Application.Current)
  49. {
  50. if (direction == View.NavigationDirection.Forward)
  51. {
  52. Application.Current!.SuperView?.FocusNext ();
  53. }
  54. else
  55. {
  56. Application.Current!.SuperView?.FocusPrev ();
  57. }
  58. focusProcessed = true;
  59. if (Application.Current.SuperView?.Focused is { } && Application.Current.SuperView.Focused != Application.Current)
  60. {
  61. return;
  62. }
  63. }
  64. else if (found && !focusProcessed && idx == viewsInTabIndexes.Count () - 1)
  65. {
  66. viewsInTabIndexes.ToList () [0].SetFocus ();
  67. }
  68. idx++;
  69. }
  70. }
  71. /// <summary>
  72. /// Moves the focus to
  73. /// </summary>
  74. internal static void MoveNextView ()
  75. {
  76. View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
  77. if (!Application.Current.FocusNext ())
  78. {
  79. Application.Current.FocusNext ();
  80. }
  81. if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
  82. {
  83. old?.SetNeedsDisplay ();
  84. Application.Current.Focused?.SetNeedsDisplay ();
  85. }
  86. else
  87. {
  88. FocusNearestView (Application.Current.SuperView?.TabIndexes, View.NavigationDirection.Forward);
  89. }
  90. }
  91. internal static void MoveNextViewOrTop ()
  92. {
  93. if (ApplicationOverlapped.OverlappedTop is null)
  94. {
  95. Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
  96. top!.FocusNext ();
  97. if (top.Focused is null)
  98. {
  99. top.FocusNext ();
  100. }
  101. top.SetNeedsDisplay ();
  102. ApplicationOverlapped.BringOverlappedTopToFront ();
  103. }
  104. else
  105. {
  106. ApplicationOverlapped.OverlappedMoveNext ();
  107. }
  108. }
  109. internal static void MovePreviousView ()
  110. {
  111. View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
  112. if (!Application.Current.FocusPrev ())
  113. {
  114. Application.Current.FocusPrev ();
  115. }
  116. if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
  117. {
  118. old?.SetNeedsDisplay ();
  119. Application.Current.Focused?.SetNeedsDisplay ();
  120. }
  121. else
  122. {
  123. FocusNearestView (Application.Current.SuperView?.TabIndexes?.Reverse (), View.NavigationDirection.Backward);
  124. }
  125. }
  126. internal static void MovePreviousViewOrTop ()
  127. {
  128. if (ApplicationOverlapped.OverlappedTop is null)
  129. {
  130. Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
  131. top!.FocusPrev ();
  132. if (top.Focused is null)
  133. {
  134. top.FocusPrev ();
  135. }
  136. top.SetNeedsDisplay ();
  137. ApplicationOverlapped.BringOverlappedTopToFront ();
  138. }
  139. else
  140. {
  141. ApplicationOverlapped.OverlappedMovePrevious ();
  142. }
  143. }
  144. }