ViewMouse.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. namespace Terminal.Gui;
  2. public partial class View
  3. {
  4. /// <summary>
  5. /// Gets or sets whether the <see cref="View"/> will highlight the view visually when the mouse button is pressed/released.
  6. /// </summary>
  7. public bool HighlightOnPress { get; set; }
  8. /// <summary>Gets or sets a value indicating whether this <see cref="View"/> want continuous button pressed event.</summary>
  9. public virtual bool WantContinuousButtonPressed { get; set; }
  10. /// <summary>Gets or sets a value indicating whether this <see cref="View"/> wants mouse position reports.</summary>
  11. /// <value><see langword="true"/> if want mouse position reports; otherwise, <see langword="false"/>.</value>
  12. public virtual bool WantMousePositionReports { get; set; }
  13. /// <summary>Event fired when a mouse event occurs.</summary>
  14. /// <remarks>
  15. /// <para>
  16. /// The coordinates are relative to <see cref="View.Bounds"/>.
  17. /// </para>
  18. /// </remarks>
  19. public event EventHandler<MouseEventEventArgs> MouseEvent;
  20. /// <summary>Event fired when a mouse click occurs.</summary>
  21. /// <remarks>
  22. /// <para>
  23. /// Fired when the mouse is either clicked or double-clicked. Check
  24. /// <see cref="MouseEvent.Flags"/> to see which button was clicked.
  25. /// </para>
  26. /// <para>
  27. /// The coordinates are relative to <see cref="View.Bounds"/>.
  28. /// </para>
  29. /// </remarks>
  30. public event EventHandler<MouseEventEventArgs> MouseClick;
  31. /// <summary>Event fired when the mouse moves into the View's <see cref="Bounds"/>.</summary>
  32. public event EventHandler<MouseEventEventArgs> MouseEnter;
  33. /// <summary>Event fired when the mouse leaves the View's <see cref="Bounds"/>.</summary>
  34. public event EventHandler<MouseEventEventArgs> MouseLeave;
  35. // TODO: OnMouseEnter should not be public virtual, but protected.
  36. /// <summary>
  37. /// Called when the mouse enters the View's <see cref="Bounds"/>. The view will now receive mouse events until the mouse leaves
  38. /// the view. At which time, <see cref="OnMouseLeave(Gui.MouseEvent)"/> will be called.
  39. /// </summary>
  40. /// <remarks>
  41. /// The coordinates are relative to <see cref="View.Bounds"/>.
  42. /// </remarks>
  43. /// <param name="mouseEvent"></param>
  44. /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
  45. protected internal virtual bool OnMouseEnter (MouseEvent mouseEvent)
  46. {
  47. if (!Enabled)
  48. {
  49. return true;
  50. }
  51. if (!CanBeVisible (this))
  52. {
  53. return false;
  54. }
  55. var args = new MouseEventEventArgs (mouseEvent);
  56. MouseEnter?.Invoke (this, args);
  57. return args.Handled;
  58. }
  59. // TODO: OnMouseLeave should not be public virtual, but protected.
  60. /// <summary>
  61. /// Called when the mouse has moved out of the View's <see cref="Bounds"/>. The view will no longer receive mouse events (until the
  62. /// mouse moves within the view again and <see cref="OnMouseEnter(Gui.MouseEvent)"/> is called).
  63. /// </summary>
  64. /// <remarks>
  65. /// The coordinates are relative to <see cref="View.Bounds"/>.
  66. /// </remarks>
  67. /// <param name="mouseEvent"></param>
  68. /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
  69. protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
  70. {
  71. if (!Enabled)
  72. {
  73. return true;
  74. }
  75. if (!CanBeVisible (this))
  76. {
  77. return false;
  78. }
  79. var args = new MouseEventEventArgs (mouseEvent);
  80. MouseLeave?.Invoke (this, args);
  81. return args.Handled;
  82. }
  83. [CanBeNull]
  84. private ColorScheme _savedColorScheme;
  85. // TODO: OnMouseEvent should not be public virtual, but protected.
  86. /// <summary>Called when a mouse event occurs within the view's <see cref="Bounds"/>.</summary>
  87. /// <remarks>
  88. /// <para>
  89. /// The coordinates are relative to <see cref="View.Bounds"/>.
  90. /// </para>
  91. /// </remarks>
  92. /// <param name="mouseEvent"></param>
  93. /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
  94. protected internal virtual bool OnMouseEvent (MouseEvent mouseEvent)
  95. {
  96. if (!Enabled)
  97. {
  98. // A disabled view should not eat mouse events
  99. return false;
  100. }
  101. if (!CanBeVisible (this))
  102. {
  103. return false;
  104. }
  105. var args = new MouseEventEventArgs (mouseEvent);
  106. // Default behavior is to invoke Accept (via HotKey) on clicked.
  107. if ((!WantContinuousButtonPressed &&
  108. Application.MouseGrabView != this &&
  109. mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
  110. || mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
  111. || mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
  112. || mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked))
  113. {
  114. return OnMouseClick (args);
  115. }
  116. if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
  117. {
  118. // If WantContinuousButtonPressed is true, and this is not the first pressed event,
  119. // invoke Accept (via HotKey)
  120. if (WantContinuousButtonPressed && Application.MouseGrabView == this)
  121. {
  122. return OnMouseClick (args);
  123. }
  124. // The first time we get pressed event, grab the mouse and invert the colors
  125. if (Application.MouseGrabView != this)
  126. {
  127. Application.GrabMouse (this);
  128. _savedColorScheme = ColorScheme;
  129. if (HighlightOnPress && ColorScheme is { })
  130. {
  131. if (CanFocus)
  132. {
  133. // TODO: Make the inverted color configurable
  134. var cs = new ColorScheme (ColorScheme)
  135. {
  136. Focus = new Attribute (ColorScheme.Normal.Foreground, ColorScheme.Focus.Background)
  137. };
  138. ColorScheme = cs;
  139. }
  140. else
  141. {
  142. var cs = new ColorScheme (ColorScheme)
  143. {
  144. Normal = new Attribute (ColorScheme.Focus.Background, ColorScheme.Normal.Foreground)
  145. };
  146. ColorScheme = cs;
  147. }
  148. }
  149. if (CanFocus){
  150. // Set the focus, but don't invoke Accept
  151. SetFocus ();
  152. }
  153. }
  154. }
  155. if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released))
  156. {
  157. // When the mouse is released, if WantContinuousButtonPressed is set, invoke Accept one last time.
  158. if (WantContinuousButtonPressed)
  159. {
  160. OnMouseClick (args);
  161. }
  162. if (Application.MouseGrabView == this)
  163. {
  164. Application.UngrabMouse ();
  165. if (HighlightOnPress && _savedColorScheme is { })
  166. {
  167. ColorScheme = _savedColorScheme;
  168. _savedColorScheme = null;
  169. }
  170. }
  171. }
  172. //// Clicked support for all buttons and single and double click
  173. //if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
  174. // || mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
  175. // || mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
  176. // || mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked))
  177. //{
  178. // OnMouseClick (args);
  179. //}
  180. //if (mouseEvent.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
  181. // || mouseEvent.Flags.HasFlag (MouseFlags.Button2DoubleClicked)
  182. // || mouseEvent.Flags.HasFlag (MouseFlags.Button3DoubleClicked)
  183. // || mouseEvent.Flags.HasFlag (MouseFlags.Button4DoubleClicked))
  184. //{
  185. // OnMouseClick (args);
  186. //}
  187. if (args.Handled != true)
  188. {
  189. MouseEvent?.Invoke (this, args);
  190. }
  191. return args.Handled == true;
  192. }
  193. /// <summary>Invokes the MouseClick event.</summary>
  194. /// <remarks>
  195. /// <para>
  196. /// Called when the mouse is either clicked or double-clicked. Check
  197. /// <see cref="MouseEvent.Flags"/> to see which button was clicked.
  198. /// </para>
  199. /// </remarks>
  200. protected bool OnMouseClick (MouseEventEventArgs args)
  201. {
  202. if (!Enabled)
  203. {
  204. // QUESTION: Is this right? Should a disabled view eat mouse clicks?
  205. args.Handled = true;
  206. return true;
  207. }
  208. MouseClick?.Invoke (this, args);
  209. if (args.Handled)
  210. {
  211. return true;
  212. }
  213. if (!HasFocus && CanFocus)
  214. {
  215. args.Handled = true;
  216. SetFocus ();
  217. }
  218. return args.Handled;
  219. }
  220. }