Application.Navigation.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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, View.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. if (direction == View.NavigationDirection.Forward)
  52. {
  53. Application.Current!.SuperView?.FocusNext ();
  54. }
  55. else
  56. {
  57. Application.Current!.SuperView?.FocusPrev ();
  58. }
  59. focusProcessed = true;
  60. if (Application.Current.SuperView?.Focused is { } && Application.Current.SuperView.Focused != Application.Current)
  61. {
  62. return;
  63. }
  64. }
  65. else if (found && !focusProcessed && idx == viewsInTabIndexes.Count () - 1)
  66. {
  67. viewsInTabIndexes.ToList () [0].SetFocus ();
  68. }
  69. idx++;
  70. }
  71. }
  72. /// <summary>
  73. /// Moves the focus to the next view. Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
  74. /// if the current and next subviews are not overlapped.
  75. /// </summary>
  76. internal static void MoveNextView ()
  77. {
  78. View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
  79. if (!Application.Current.FocusNext ())
  80. {
  81. Application.Current.FocusNext ();
  82. }
  83. if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
  84. {
  85. old?.SetNeedsDisplay ();
  86. Application.Current.Focused?.SetNeedsDisplay ();
  87. }
  88. else
  89. {
  90. FocusNearestView (Application.Current.SuperView?.TabIndexes, View.NavigationDirection.Forward);
  91. }
  92. }
  93. /// <summary>
  94. /// Moves the focus to the next <see cref="Toplevel"/> subview or the next subview that has <see cref="ApplicationOverlapped.OverlappedTop"/> set.
  95. /// </summary>
  96. internal static void MoveNextViewOrTop ()
  97. {
  98. if (ApplicationOverlapped.OverlappedTop is null)
  99. {
  100. Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
  101. if (!Application.Current.FocusNext ())
  102. {
  103. Application.Current.FocusNext ();
  104. }
  105. if (top != Application.Current.Focused && top != Application.Current.Focused?.Focused)
  106. {
  107. top?.SetNeedsDisplay ();
  108. Application.Current.Focused?.SetNeedsDisplay ();
  109. }
  110. else
  111. {
  112. FocusNearestView (Application.Current.SuperView?.TabIndexes, View.NavigationDirection.Forward);
  113. }
  114. //top!.FocusNext ();
  115. //if (top.Focused is null)
  116. //{
  117. // top.FocusNext ();
  118. //}
  119. //top.SetNeedsDisplay ();
  120. ApplicationOverlapped.BringOverlappedTopToFront ();
  121. }
  122. else
  123. {
  124. ApplicationOverlapped.OverlappedMoveNext ();
  125. }
  126. }
  127. /// <summary>
  128. /// Moves the focus to the next view. Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
  129. /// if the current and next subviews are not overlapped.
  130. /// </summary>
  131. internal static void MovePreviousView ()
  132. {
  133. View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
  134. if (!Application.Current.FocusPrev ())
  135. {
  136. Application.Current.FocusPrev ();
  137. }
  138. if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
  139. {
  140. old?.SetNeedsDisplay ();
  141. Application.Current.Focused?.SetNeedsDisplay ();
  142. }
  143. else
  144. {
  145. FocusNearestView (Application.Current.SuperView?.TabIndexes?.Reverse (), View.NavigationDirection.Backward);
  146. }
  147. }
  148. internal static void MovePreviousViewOrTop ()
  149. {
  150. if (ApplicationOverlapped.OverlappedTop is null)
  151. {
  152. Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
  153. top!.FocusPrev ();
  154. if (top.Focused is null)
  155. {
  156. top.FocusPrev ();
  157. }
  158. top.SetNeedsDisplay ();
  159. ApplicationOverlapped.BringOverlappedTopToFront ();
  160. }
  161. else
  162. {
  163. ApplicationOverlapped.OverlappedMovePrevious ();
  164. }
  165. }
  166. }