Skip to content

Commit

Permalink
Refactor MouseInterpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
tznind committed Dec 22, 2024
1 parent 4354712 commit 83b60d2
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 344 deletions.
5 changes: 0 additions & 5 deletions Terminal.Gui/ConsoleDrivers/V2/IInputProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ public interface IInputProcessor

/// <summary>Event fired when a mouse event occurs.</summary>
event EventHandler<MouseEventArgs>? MouseEvent;

/// <summary>
/// Process low level mouse events into atomic operations / sequences
/// </summary>
MouseInterpreter MouseInterpreter { get; }

/// <summary>
/// Called when a key is pressed down. Fires the <see cref="KeyDown"/> event. This is a precursor to
Expand Down
24 changes: 3 additions & 21 deletions Terminal.Gui/ConsoleDrivers/V2/InputProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public abstract class InputProcessor<T> : IInputProcessor

public IAnsiResponseParser GetParser () => Parser;

public MouseInterpreter MouseInterpreter { get; } = new ();
private MouseInterpreter _mouseInterpreter { get; } = new ();

/// <summary>Event fired when a key is pressed down. This is a precursor to <see cref="KeyUp"/>.</summary>
public event EventHandler<Key>? KeyDown;
Expand Down Expand Up @@ -56,27 +56,9 @@ public void OnMouseEvent (MouseEventArgs a)
MouseEvent?.Invoke (this, a);

// Pass on any interpreted states e.g. click/double click etc
foreach (var narrative in MouseInterpreter.Process (a))
foreach (var e in _mouseInterpreter.Process (a))
{
ResolveNarrative (narrative);
}
}

private void ResolveNarrative (ButtonNarrative narrative)
{
if (narrative.NumberOfClicks == 1)
{
var last = narrative.MouseStates.Last ();

// its a click
MouseEvent?.Invoke (this, new MouseEventArgs
{
Handled = false,
Flags = MouseFlags.Button1Clicked,
ScreenPosition = last.Position,
Position = last.ViewportPosition,
View = last.View,
});
MouseEvent?.Invoke (this, e);
}
}

Expand Down
148 changes: 148 additions & 0 deletions Terminal.Gui/ConsoleDrivers/V2/MouseButtonSequence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#nullable enable
using Terminal.Gui.ConsoleDrivers.V2;

namespace Terminal.Gui;

/// <summary>
/// Describes a completed narrative e.g. 'user triple clicked'.
/// </summary>
/// <remarks>Internally we can have a double click narrative that becomes
/// a triple click narrative. But we will not release both i.e. we don't say
/// user clicked then user double-clicked then user triple clicked</remarks>
internal class MouseButtonSequence
{
private readonly IViewFinder _viewFinder;
public int NumberOfClicks { get; set; }

/// <summary>
/// Mouse states during which click was generated.
/// N = 2x<see cref="NumberOfClicks"/>
/// </summary>
public List<MouseButtonStateEx> MouseStates { get; set; } = new ();

public int ButtonIdx { get; }

/// <summary>
/// Function for returning the current time. Use in unit tests to
/// ensure repeatable tests.
/// </summary>
public Func<DateTime> Now { get; set; }

public MouseButtonSequence (int buttonIdx, Func<DateTime> now, IViewFinder viewFinder)
{
ButtonIdx = buttonIdx;
Now = now;
_viewFinder = viewFinder;
}

public MouseEventArgs? Process (Point position, bool pressed)
{
var last = MouseStates.Last ();

// Still pressed
if (last.Pressed && pressed)
{
// No change
return null;
}

var view = _viewFinder.GetViewAt (position, out var viewport);

NumberOfClicks++;
MouseStates.Add (new MouseButtonStateEx
{
Button = ButtonIdx,
At = Now(),
Pressed = false,
Position = position,

View = view,
ViewportPosition = viewport,

/* TODO: Need these probably*/
Shift = false,
Ctrl = false,
Alt = false,

});


if (IsResolveable ())
{
return Resolve ();
}

return null;
}

public bool IsResolveable ()
{
return NumberOfClicks > 0;
}
/// <summary>
/// Resolves the narrative completely with immediate effect.
/// This may return null if e.g. so far all that has accumulated is a mouse down.
/// </summary>
/// <returns></returns>
public MouseEventArgs? Resolve ()
{
if (!IsResolveable ())
{
return null;
}

if (NumberOfClicks == 1)
{
var last = MouseStates.Last ();

// its a click
return new MouseEventArgs
{
Handled = false,
Flags = ToClicks(this.ButtonIdx,NumberOfClicks),
ScreenPosition = last.Position,
Position = last.ViewportPosition,
View = last.View,
};
}

return null;
}

private MouseFlags ToClicks (int buttonIdx, int numberOfClicks)
{
if (numberOfClicks == 0)
{
throw new ArgumentOutOfRangeException (nameof (numberOfClicks), "Zero clicks are not valid.");
}

return buttonIdx switch
{
0 => numberOfClicks switch
{
1 => MouseFlags.Button1Clicked,
2 => MouseFlags.Button1DoubleClicked,
_ => MouseFlags.Button1TripleClicked
},
1 => numberOfClicks switch
{
1 => MouseFlags.Button2Clicked,
2 => MouseFlags.Button2DoubleClicked,
_ => MouseFlags.Button2TripleClicked
},
2 => numberOfClicks switch
{
1 => MouseFlags.Button3Clicked,
2 => MouseFlags.Button3DoubleClicked,
_ => MouseFlags.Button3TripleClicked
},
3 => numberOfClicks switch
{
1 => MouseFlags.Button4Clicked,
2 => MouseFlags.Button4DoubleClicked,
_ => MouseFlags.Button4TripleClicked
},
_ => throw new ArgumentOutOfRangeException (nameof (buttonIdx), "Unsupported button index")
};
}
}
54 changes: 54 additions & 0 deletions Terminal.Gui/ConsoleDrivers/V2/MouseButtonState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#nullable enable
namespace Terminal.Gui;

/// <summary>
/// Not to be confused with <see cref="NetEvents.MouseButtonState"/>
/// </summary>
internal class MouseButtonStateEx
{
public required int Button { get; set; }
/// <summary>
/// When the button entered its current state.
/// </summary>
public DateTime At { get; set; }

/// <summary>
/// <see langword="true"/> if the button is currently down
/// </summary>
public bool Pressed { get; set; }

/// <summary>
/// The screen location when the mouse button entered its current state
/// (became pressed or was released)
/// </summary>
public Point Position { get; set; }

/// <summary>
/// The <see cref="View"/> (if any) that was at the <see cref="Position"/>
/// when the button entered its current state.
/// </summary>
public View? View { get; set; }

/// <summary>
/// Viewport relative position within <see cref="View"/> (if there is one)
/// </summary>
public Point ViewportPosition { get; set; }

/// <summary>
/// True if shift was provided by the console at the time the mouse
/// button entered its current state.
/// </summary>
public bool Shift { get; set; }

/// <summary>
/// True if control was provided by the console at the time the mouse
/// button entered its current state.
/// </summary>
public bool Ctrl { get; set; }

/// <summary>
/// True if alt was held down at the time the mouse
/// button entered its current state.
/// </summary>
public bool Alt { get; set; }
}
Loading

0 comments on commit 83b60d2

Please sign in to comment.