ContextMenu.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. namespace Terminal.Gui {
  3. /// <summary>
  4. /// A context menu window derived from <see cref="MenuBar"/> containing menu items
  5. /// which can be opened in any position.
  6. /// </summary>
  7. public sealed class ContextMenu : IDisposable {
  8. private static MenuBar menuBar;
  9. private Key key = Key.F10 | Key.ShiftMask;
  10. private MouseFlags mouseFlags = MouseFlags.Button3Clicked;
  11. private Toplevel container;
  12. /// <summary>
  13. /// Initialize a context menu with empty menu items.
  14. /// </summary>
  15. public ContextMenu () : this (0, 0, new MenuBarItem ()) { }
  16. /// <summary>
  17. /// Initialize a context menu with menu items from a host <see cref="View"/>.
  18. /// </summary>
  19. /// <param name="host">The host view.</param>
  20. /// <param name="menuItems">The menu items.</param>
  21. public ContextMenu (View host, MenuBarItem menuItems) :
  22. this (host.Frame.X, host.Frame.Y, menuItems)
  23. {
  24. Host = host;
  25. }
  26. /// <summary>
  27. /// Initialize a context menu with menu items.
  28. /// </summary>
  29. /// <param name="x">The left position.</param>
  30. /// <param name="y">The top position.</param>
  31. /// <param name="menuItems">The menu items.</param>
  32. public ContextMenu (int x, int y, MenuBarItem menuItems)
  33. {
  34. if (IsShow) {
  35. Hide ();
  36. }
  37. MenuItems = menuItems;
  38. Position = new Point (x, y);
  39. }
  40. private void MenuBar_MenuAllClosed ()
  41. {
  42. Dispose ();
  43. }
  44. /// <summary>
  45. /// Disposes the all the context menu objects instances.
  46. /// </summary>
  47. public void Dispose ()
  48. {
  49. if (IsShow) {
  50. menuBar.MenuAllClosed -= MenuBar_MenuAllClosed;
  51. menuBar.Dispose ();
  52. menuBar = null;
  53. IsShow = false;
  54. }
  55. if (container != null) {
  56. container.Closing -= Container_Closing;
  57. container.Resized -= Container_Resized;
  58. }
  59. }
  60. /// <summary>
  61. /// Open the <see cref="MenuItems"/> menu items.
  62. /// </summary>
  63. public void Show ()
  64. {
  65. if (menuBar != null) {
  66. Hide ();
  67. }
  68. container = Application.Current;
  69. container.Closing += Container_Closing;
  70. container.Resized += Container_Resized;
  71. var frame = container.Frame;
  72. var position = Position;
  73. if (Host != null) {
  74. Host.ViewToScreen (container.Frame.X, container.Frame.Y, out int x, out int y);
  75. var pos = new Point (x, y);
  76. pos.Y += Host.Frame.Height - 1;
  77. if (position != pos) {
  78. Position = position = pos;
  79. }
  80. }
  81. var rect = Menu.MakeFrame (position.X, position.Y, MenuItems.Children);
  82. if (rect.Right >= frame.Right) {
  83. if (frame.Right - rect.Width >= 0 || !ForceMinimumPosToZero) {
  84. position.X = frame.Right - rect.Width;
  85. } else if (ForceMinimumPosToZero) {
  86. position.X = 0;
  87. }
  88. } else if (ForceMinimumPosToZero && position.X < 0) {
  89. position.X = 0;
  90. }
  91. if (rect.Bottom >= frame.Bottom) {
  92. if (frame.Bottom - rect.Height - 1 >= 0 || !ForceMinimumPosToZero) {
  93. if (Host == null) {
  94. position.Y = frame.Bottom - rect.Height - 1;
  95. } else {
  96. Host.ViewToScreen (container.Frame.X, container.Frame.Y, out int x, out int y);
  97. var pos = new Point (x, y);
  98. position.Y = pos.Y - rect.Height - 1;
  99. }
  100. } else if (ForceMinimumPosToZero) {
  101. position.Y = 0;
  102. }
  103. } else if (ForceMinimumPosToZero && position.Y < 0) {
  104. position.Y = 0;
  105. }
  106. menuBar = new MenuBar (new [] { MenuItems }) {
  107. X = position.X,
  108. Y = position.Y,
  109. Width = 0,
  110. Height = 0,
  111. UseSubMenusSingleFrame = UseSubMenusSingleFrame
  112. };
  113. menuBar.isContextMenuLoading = true;
  114. menuBar.MenuAllClosed += MenuBar_MenuAllClosed;
  115. IsShow = true;
  116. menuBar.OpenMenu ();
  117. }
  118. private void Container_Resized (Size obj)
  119. {
  120. if (IsShow) {
  121. Show ();
  122. }
  123. }
  124. private void Container_Closing (ToplevelClosingEventArgs obj)
  125. {
  126. Hide ();
  127. }
  128. /// <summary>
  129. /// Close the <see cref="MenuItems"/> menu items.
  130. /// </summary>
  131. public void Hide ()
  132. {
  133. menuBar.CleanUp ();
  134. Dispose ();
  135. }
  136. /// <summary>
  137. /// Event invoked when the <see cref="ContextMenu.Key"/> is changed.
  138. /// </summary>
  139. public event Action<Key> KeyChanged;
  140. /// <summary>
  141. /// Event invoked when the <see cref="ContextMenu.MouseFlags"/> is changed.
  142. /// </summary>
  143. public event Action<MouseFlags> MouseFlagsChanged;
  144. /// <summary>
  145. /// Gets or set the menu position.
  146. /// </summary>
  147. public Point Position { get; set; }
  148. /// <summary>
  149. /// Gets or sets the menu items for this context menu.
  150. /// </summary>
  151. public MenuBarItem MenuItems { get; set; }
  152. /// <summary>
  153. /// The <see cref="Gui.Key"/> used to activate the context menu by keyboard.
  154. /// </summary>
  155. public Key Key {
  156. get => key;
  157. set {
  158. var oldKey = key;
  159. key = value;
  160. KeyChanged?.Invoke (oldKey);
  161. }
  162. }
  163. /// <summary>
  164. /// The <see cref="Gui.MouseFlags"/> used to activate the context menu by mouse.
  165. /// </summary>
  166. public MouseFlags MouseFlags {
  167. get => mouseFlags;
  168. set {
  169. var oldFlags = mouseFlags;
  170. mouseFlags = value;
  171. MouseFlagsChanged?.Invoke (oldFlags);
  172. }
  173. }
  174. /// <summary>
  175. /// Gets information whether menu is showing or not.
  176. /// </summary>
  177. public static bool IsShow { get; private set; }
  178. /// <summary>
  179. /// The host <see cref="View "/> which position will be used,
  180. /// otherwise if it's null the container will be used.
  181. /// </summary>
  182. public View Host { get; set; }
  183. /// <summary>
  184. /// Gets or sets whether forces the minimum position to zero
  185. /// if the left or right position are negative.
  186. /// </summary>
  187. public bool ForceMinimumPosToZero { get; set; } = true;
  188. /// <summary>
  189. /// Gets the <see cref="Gui.MenuBar"/> that is hosting this context menu.
  190. /// </summary>
  191. public MenuBar MenuBar { get => menuBar; }
  192. /// <summary>
  193. /// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
  194. /// </summary>
  195. public bool UseSubMenusSingleFrame { get; set; }
  196. }
  197. }