ViewMouse.cs 8.4 KB

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