Application.Navigation.cs 5.7 KB

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