ViewMouse.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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 (!WantContinuousButtonPressed
  90. && Application.MouseGrabView != this
  91. && (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
  92. || mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
  93. || mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
  94. || mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked)))
  95. {
  96. return OnMouseClick (args);
  97. }
  98. if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
  99. || mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
  100. || mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)
  101. || mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
  102. {
  103. // If WantContinuousButtonPressed is true, and this is not the first pressed event,
  104. // invoke Accept (via HotKey)
  105. if (WantContinuousButtonPressed && Application.MouseGrabView == this)
  106. {
  107. return OnMouseClick (args);
  108. }
  109. // The first time we get pressed event, grab the mouse and invert the colors
  110. if (Application.MouseGrabView != this)
  111. {
  112. Application.GrabMouse (this);
  113. _savedColorScheme = ColorScheme;
  114. if (HighlightOnPress && ColorScheme is { })
  115. {
  116. if (CanFocus)
  117. {
  118. // TODO: Make the inverted color configurable
  119. var cs = new ColorScheme (ColorScheme)
  120. {
  121. Focus = new (ColorScheme.Normal.Foreground, ColorScheme.Focus.Background)
  122. };
  123. ColorScheme = cs;
  124. }
  125. else
  126. {
  127. var cs = new ColorScheme (ColorScheme)
  128. {
  129. Normal = new (ColorScheme.Focus.Background, ColorScheme.Normal.Foreground)
  130. };
  131. ColorScheme = cs;
  132. }
  133. }
  134. if (CanFocus)
  135. {
  136. // Set the focus, but don't invoke Accept
  137. SetFocus ();
  138. }
  139. args.Handled = true;
  140. }
  141. }
  142. if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released)
  143. || mouseEvent.Flags.HasFlag (MouseFlags.Button2Released)
  144. || mouseEvent.Flags.HasFlag (MouseFlags.Button3Released)
  145. || mouseEvent.Flags.HasFlag (MouseFlags.Button4Released))
  146. {
  147. // When the mouse is released, if WantContinuousButtonPressed is set, invoke Accept one last time.
  148. if (WantContinuousButtonPressed)
  149. {
  150. OnMouseClick (args);
  151. }
  152. if (Application.MouseGrabView == this)
  153. {
  154. Application.UngrabMouse ();
  155. if (HighlightOnPress && _savedColorScheme is { })
  156. {
  157. ColorScheme = _savedColorScheme;
  158. _savedColorScheme = null;
  159. }
  160. args.Handled = true;
  161. }
  162. }
  163. if (args.Handled != true)
  164. {
  165. MouseEvent?.Invoke (this, args);
  166. }
  167. return args.Handled;
  168. }
  169. /// <summary>Event fired when a mouse event occurs.</summary>
  170. /// <remarks>
  171. /// <para>
  172. /// The coordinates are relative to <see cref="View.Bounds"/>.
  173. /// </para>
  174. /// </remarks>
  175. public event EventHandler<MouseEventEventArgs> MouseEvent;
  176. /// <summary>Invokes the MouseClick event.</summary>
  177. /// <remarks>
  178. /// <para>
  179. /// Called when the mouse is either clicked or double-clicked. Check
  180. /// <see cref="MouseEvent.Flags"/> to see which button was clicked.
  181. /// </para>
  182. /// </remarks>
  183. protected bool OnMouseClick (MouseEventEventArgs args)
  184. {
  185. if (!Enabled)
  186. {
  187. // QUESTION: Is this right? Should a disabled view eat mouse clicks?
  188. args.Handled = true;
  189. return true;
  190. }
  191. MouseClick?.Invoke (this, args);
  192. if (args.Handled)
  193. {
  194. return true;
  195. }
  196. if (!HasFocus && CanFocus)
  197. {
  198. args.Handled = true;
  199. SetFocus ();
  200. }
  201. return args.Handled;
  202. }
  203. /// <summary>Event fired when a mouse click occurs.</summary>
  204. /// <remarks>
  205. /// <para>
  206. /// Fired when the mouse is either clicked or double-clicked. Check
  207. /// <see cref="MouseEvent.Flags"/> to see which button was clicked.
  208. /// </para>
  209. /// <para>
  210. /// The coordinates are relative to <see cref="View.Bounds"/>.
  211. /// </para>
  212. /// </remarks>
  213. public event EventHandler<MouseEventEventArgs> MouseClick;
  214. }