2
0

ViewMouse.cs 8.7 KB

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