MouseInterpreter.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. internal class MouseInterpreter
  4. {
  5. /// <summary>
  6. /// Function for returning the current time. Use in unit tests to
  7. /// ensure repeatable tests.
  8. /// </summary>
  9. public Func<DateTime> Now { get; set; }
  10. /// <summary>
  11. /// How long to wait for a second, third, fourth click after the first before giving up and
  12. /// releasing event as a 'click'
  13. /// </summary>
  14. public TimeSpan RepeatedClickThreshold { get; set; }
  15. private readonly MouseButtonStateEx [] _buttonStates;
  16. public MouseInterpreter (
  17. Func<DateTime>? now = null,
  18. TimeSpan? doubleClickThreshold = null
  19. )
  20. {
  21. Now = now ?? (() => DateTime.Now);
  22. RepeatedClickThreshold = doubleClickThreshold ?? TimeSpan.FromMilliseconds (500);
  23. _buttonStates = new []
  24. {
  25. new MouseButtonStateEx (Now, RepeatedClickThreshold, 0),
  26. new MouseButtonStateEx (Now, RepeatedClickThreshold, 1),
  27. new MouseButtonStateEx (Now, RepeatedClickThreshold, 2),
  28. new MouseButtonStateEx (Now, RepeatedClickThreshold, 3)
  29. };
  30. }
  31. public IEnumerable<MouseEventArgs> Process (MouseEventArgs e)
  32. {
  33. yield return e;
  34. // For each mouse button
  35. for (var i = 0; i < 4; i++)
  36. {
  37. _buttonStates [i].UpdateState (e, out int? numClicks);
  38. if (numClicks.HasValue)
  39. {
  40. yield return RaiseClick (i, numClicks.Value, e);
  41. }
  42. }
  43. }
  44. private MouseEventArgs RaiseClick (int button, int numberOfClicks, MouseEventArgs mouseEventArgs)
  45. {
  46. var newClick = new MouseEventArgs
  47. {
  48. Handled = false,
  49. Flags = ToClicks (button, numberOfClicks),
  50. ScreenPosition = mouseEventArgs.ScreenPosition,
  51. View = mouseEventArgs.View,
  52. Position = mouseEventArgs.Position
  53. };
  54. Logging.Trace ($"Raising click event:{newClick.Flags} at screen {newClick.ScreenPosition}");
  55. return newClick;
  56. }
  57. private MouseFlags ToClicks (int buttonIdx, int numberOfClicks)
  58. {
  59. if (numberOfClicks == 0)
  60. {
  61. throw new ArgumentOutOfRangeException (nameof (numberOfClicks), "Zero clicks are not valid.");
  62. }
  63. return buttonIdx switch
  64. {
  65. 0 => numberOfClicks switch
  66. {
  67. 1 => MouseFlags.Button1Clicked,
  68. 2 => MouseFlags.Button1DoubleClicked,
  69. _ => MouseFlags.Button1TripleClicked
  70. },
  71. 1 => numberOfClicks switch
  72. {
  73. 1 => MouseFlags.Button2Clicked,
  74. 2 => MouseFlags.Button2DoubleClicked,
  75. _ => MouseFlags.Button2TripleClicked
  76. },
  77. 2 => numberOfClicks switch
  78. {
  79. 1 => MouseFlags.Button3Clicked,
  80. 2 => MouseFlags.Button3DoubleClicked,
  81. _ => MouseFlags.Button3TripleClicked
  82. },
  83. 3 => numberOfClicks switch
  84. {
  85. 1 => MouseFlags.Button4Clicked,
  86. 2 => MouseFlags.Button4DoubleClicked,
  87. _ => MouseFlags.Button4TripleClicked
  88. },
  89. _ => throw new ArgumentOutOfRangeException (nameof (buttonIdx), "Unsupported button index")
  90. };
  91. }
  92. }