ViewMouse.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. namespace Terminal.Gui;
  2. public partial class View
  3. {
  4. /// <summary>
  5. /// Gets or sets whether the <see cref="View"/> will invert the colors when the mouse button is pressed/released.
  6. /// </summary>
  7. public bool InvertColorsOnPress { 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 (InvertColorsOnPress && 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. var cs = new ColorScheme (new Attribute (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground));
  130. ColorScheme = cs;
  131. // Set the focus, but don't invoke Accept
  132. SetFocus ();
  133. }
  134. }
  135. if (InvertColorsOnPress && mouseEvent.Flags.HasFlag (MouseFlags.Button1Released))
  136. {
  137. // When the mouse is released, if WantContinuousButtonPressed is set, invoke Accept one last time.
  138. if (WantContinuousButtonPressed)
  139. {
  140. OnMouseClick (args);
  141. }
  142. if (Application.MouseGrabView == this)
  143. {
  144. Application.UngrabMouse ();
  145. if (_savedColorScheme is { })
  146. {
  147. ColorScheme = _savedColorScheme;
  148. _savedColorScheme = null;
  149. }
  150. }
  151. }
  152. //// Clicked support for all buttons and single and double click
  153. //if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
  154. // || mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
  155. // || mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
  156. // || mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked))
  157. //{
  158. // OnMouseClick (args);
  159. //}
  160. //if (mouseEvent.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
  161. // || mouseEvent.Flags.HasFlag (MouseFlags.Button2DoubleClicked)
  162. // || mouseEvent.Flags.HasFlag (MouseFlags.Button3DoubleClicked)
  163. // || mouseEvent.Flags.HasFlag (MouseFlags.Button4DoubleClicked))
  164. //{
  165. // OnMouseClick (args);
  166. //}
  167. if (args.Handled != true)
  168. {
  169. MouseEvent?.Invoke (this, args);
  170. }
  171. return args.Handled == true;
  172. }
  173. /// <summary>Invokes the MouseClick event.</summary>
  174. /// <remarks>
  175. /// <para>
  176. /// Called when the mouse is either clicked or double-clicked. Check
  177. /// <see cref="MouseEvent.Flags"/> to see which button was clicked.
  178. /// </para>
  179. /// </remarks>
  180. protected bool OnMouseClick (MouseEventEventArgs args)
  181. {
  182. if (!Enabled)
  183. {
  184. // QUESTION: Is this right? Should a disabled view eat mouse clicks?
  185. args.Handled = true;
  186. return true;
  187. }
  188. MouseClick?.Invoke (this, args);
  189. if (args.Handled)
  190. {
  191. return true;
  192. }
  193. if (!HasFocus && CanFocus)
  194. {
  195. args.Handled = true;
  196. SetFocus ();
  197. }
  198. return args.Handled;
  199. }
  200. }