mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 07:47:54 +01:00
605 lines
25 KiB
C#
605 lines
25 KiB
C#
using System.Collections.ObjectModel;
|
|
|
|
namespace UICatalog.Scenarios;
|
|
|
|
[ScenarioMetadata ("Mouse Tester", "Illustrates Mouse event flow and handling")]
|
|
[ScenarioCategory ("Mouse and Keyboard")]
|
|
public class MouseTester : Scenario
|
|
{
|
|
public override void Main ()
|
|
{
|
|
Application.Init ();
|
|
// Prepping for modern app model
|
|
using IApplication app = Application.Instance;
|
|
|
|
using Runnable runnable = new ()
|
|
{
|
|
Id = "runnable",
|
|
};
|
|
|
|
MenuBar menuBar = new ();
|
|
menuBar.Add (new MenuBarItem ("_File", [new MenuItem { Title = "_Quit", Action = () => app.RequestStop () }]));
|
|
|
|
FlagSelector<DemoMouseFlags> mouseFlagsFilter = new ()
|
|
{
|
|
AssignHotKeys = true,
|
|
Value = (DemoMouseFlags.All & ~DemoMouseFlags.PositionReport),
|
|
};
|
|
|
|
menuBar.Add (
|
|
new MenuBarItem (
|
|
"_Filter",
|
|
[
|
|
new MenuItem
|
|
{
|
|
CommandView = mouseFlagsFilter
|
|
},
|
|
]
|
|
),
|
|
new MenuBarItem (runnable, Command.DeleteAll, "_Clear Logs")
|
|
);
|
|
runnable.Add (menuBar);
|
|
|
|
View lastDriverEvent = new ()
|
|
{
|
|
Height = 1,
|
|
Width = Dim.Auto (),
|
|
Y = Pos.Bottom (menuBar),
|
|
Text = "Last Driver Event: "
|
|
};
|
|
|
|
runnable.Add (lastDriverEvent);
|
|
|
|
View lastAppEvent = new ()
|
|
{
|
|
Height = 1,
|
|
Width = Dim.Auto (),
|
|
Y = Pos.Bottom (lastDriverEvent),
|
|
Text = "Last App Event: "
|
|
};
|
|
|
|
runnable.Add (lastAppEvent);
|
|
|
|
View lastViewEvent = new ()
|
|
{
|
|
Height = 1,
|
|
Width = Dim.Auto (),
|
|
Y = Pos.Bottom (lastAppEvent),
|
|
Text = "Last View Event: "
|
|
};
|
|
|
|
runnable.Add (lastViewEvent);
|
|
|
|
CheckBox cbRepeatOnHold = new ()
|
|
{
|
|
Y = Pos.Bottom (lastViewEvent),
|
|
Title = "_Repeat On Hold"
|
|
};
|
|
|
|
runnable.Add (cbRepeatOnHold);
|
|
|
|
CheckBox cbHighlightOnPressed = new ()
|
|
{
|
|
Y = Pos.Bottom (cbRepeatOnHold),
|
|
Title = "_Highlight on Pressed"
|
|
};
|
|
|
|
runnable.Add (cbHighlightOnPressed);
|
|
|
|
CheckBox cbHighlightOnPressedOutside = new ()
|
|
{
|
|
Y = Pos.Bottom (cbHighlightOnPressed),
|
|
Title = "_Highlight on PressedOutside"
|
|
};
|
|
|
|
runnable.Add (cbHighlightOnPressedOutside);
|
|
|
|
MouseEventDemoView demo = new ()
|
|
{
|
|
Id = "demo",
|
|
Y = Pos.Bottom (cbHighlightOnPressedOutside),
|
|
Width = Dim.Fill (),
|
|
Height = 15,
|
|
Title = "Enter/Leave Demo"
|
|
};
|
|
|
|
MouseEventDemoView demoInPadding = new ()
|
|
{
|
|
X = 0,
|
|
Y = 0,
|
|
Width = Dim.Fill (),
|
|
Height = Dim.Func (_ => demo.Padding!.Thickness.Top),
|
|
Title = "inPadding",
|
|
Id = "inPadding",
|
|
};
|
|
demo.Padding!.Add (demoInPadding);
|
|
|
|
demo.Padding!.Initialized += DemoPaddingOnInitialized;
|
|
|
|
void DemoPaddingOnInitialized (object o, EventArgs eventArgs)
|
|
{
|
|
demo.Padding!.Thickness = demo.Padding.Thickness with { Top = 5 };
|
|
}
|
|
|
|
MouseEventDemoView sub1 = new ()
|
|
{
|
|
X = 0,
|
|
Y = 0,
|
|
Width = Dim.Percent (20),
|
|
Height = Dim.Fill (),
|
|
Title = "sub1",
|
|
Id = "sub1"
|
|
};
|
|
demo.Add (sub1);
|
|
|
|
MouseEventDemoView sub2 = new ()
|
|
{
|
|
X = Pos.Right (sub1) - 4,
|
|
Y = Pos.Top (sub1) + 1,
|
|
Width = Dim.Percent (20),
|
|
Height = Dim.Fill (1),
|
|
Title = "sub2",
|
|
Id = "sub2"
|
|
};
|
|
|
|
demo.Add (sub2);
|
|
|
|
runnable.Add (demo);
|
|
|
|
cbHighlightOnPressed.CheckedState = demo.HighlightStates.HasFlag (MouseState.Pressed) ? CheckState.Checked : CheckState.UnChecked;
|
|
|
|
cbHighlightOnPressed.CheckedStateChanging += (_, e) =>
|
|
{
|
|
if (e.Result == CheckState.Checked)
|
|
{
|
|
demo.HighlightStates |= MouseState.Pressed;
|
|
}
|
|
else
|
|
{
|
|
demo.HighlightStates &= ~MouseState.Pressed;
|
|
}
|
|
|
|
foreach (View subview in demo.SubViews)
|
|
{
|
|
if (e.Result == CheckState.Checked)
|
|
{
|
|
subview.HighlightStates |= MouseState.Pressed;
|
|
}
|
|
else
|
|
{
|
|
subview.HighlightStates &= ~MouseState.Pressed;
|
|
}
|
|
}
|
|
|
|
foreach (View subview in demo.Padding.SubViews)
|
|
{
|
|
if (e.Result == CheckState.Checked)
|
|
{
|
|
subview.HighlightStates |= MouseState.Pressed;
|
|
}
|
|
else
|
|
{
|
|
subview.HighlightStates &= ~MouseState.Pressed;
|
|
}
|
|
}
|
|
};
|
|
|
|
cbHighlightOnPressedOutside.CheckedState = demo.HighlightStates.HasFlag (MouseState.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
|
|
|
|
cbHighlightOnPressedOutside.CheckedStateChanging += (_, e) =>
|
|
{
|
|
if (e.Result == CheckState.Checked)
|
|
{
|
|
demo.HighlightStates |= MouseState.PressedOutside;
|
|
}
|
|
else
|
|
{
|
|
demo.HighlightStates &= ~MouseState.PressedOutside;
|
|
}
|
|
|
|
foreach (View subview in demo.SubViews)
|
|
{
|
|
if (e.Result == CheckState.Checked)
|
|
{
|
|
subview.HighlightStates |= MouseState.PressedOutside;
|
|
}
|
|
else
|
|
{
|
|
subview.HighlightStates &= ~MouseState.PressedOutside;
|
|
}
|
|
}
|
|
|
|
foreach (View subview in demo.Padding.SubViews)
|
|
{
|
|
if (e.Result == CheckState.Checked)
|
|
{
|
|
subview.HighlightStates |= MouseState.PressedOutside;
|
|
}
|
|
else
|
|
{
|
|
subview.HighlightStates &= ~MouseState.PressedOutside;
|
|
}
|
|
}
|
|
};
|
|
|
|
cbRepeatOnHold.CheckedStateChanging += (_, _) =>
|
|
{
|
|
demo.WantContinuousButtonPressed = !demo.WantContinuousButtonPressed;
|
|
|
|
foreach (View subview in demo.SubViews)
|
|
{
|
|
subview.WantContinuousButtonPressed = demo.WantContinuousButtonPressed;
|
|
}
|
|
|
|
foreach (View subview in demo.Padding.SubViews)
|
|
{
|
|
subview.WantContinuousButtonPressed = demo.WantContinuousButtonPressed;
|
|
}
|
|
};
|
|
|
|
Label label = new ()
|
|
{
|
|
Text = "Dri_ver Events:",
|
|
//X = Pos.Right (filterSlider),
|
|
Y = Pos.Bottom (demo)
|
|
};
|
|
|
|
ObservableCollection<string> driverLogList = new ();
|
|
|
|
ListView driverLog = new ()
|
|
{
|
|
X = Pos.Left (label),
|
|
Y = Pos.Bottom (label),
|
|
Width = Dim.Auto (minimumContentDim: Dim.Percent (20)),
|
|
Height = Dim.Fill (),
|
|
SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Base),
|
|
Source = new ListWrapper<string> (driverLogList)
|
|
};
|
|
runnable.Add (label, driverLog);
|
|
|
|
app.Driver!.MouseEvent += (_, mouse) =>
|
|
{
|
|
if (!mouseFlagsFilter.Value.HasValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mouseFlagsFilter.Value.Value.HasFlag ((DemoMouseFlags)mouse.Flags))
|
|
{
|
|
lastDriverEvent.Text = $"Last Driver Event: {mouse}";
|
|
Logging.Trace (lastDriverEvent.Text);
|
|
driverLogList.Add ($"{mouse.Position}:{mouse.Flags}");
|
|
driverLog.MoveEnd ();
|
|
}
|
|
};
|
|
label = new ()
|
|
{
|
|
Text = "_App Events:",
|
|
X = Pos.Right (driverLog) + 1,
|
|
Y = Pos.Bottom (demo)
|
|
};
|
|
|
|
ObservableCollection<string> appLogList = new ();
|
|
|
|
ListView appLog = new ()
|
|
{
|
|
X = Pos.Left (label),
|
|
Y = Pos.Bottom (label),
|
|
Width = Dim.Auto (minimumContentDim: Dim.Percent (20)),
|
|
Height = Dim.Fill (),
|
|
SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Base),
|
|
Source = new ListWrapper<string> (appLogList)
|
|
};
|
|
runnable.Add (label, appLog);
|
|
|
|
app.Mouse.MouseEvent += (_, mouse) =>
|
|
{
|
|
if (!mouseFlagsFilter.Value.HasValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mouseFlagsFilter.Value.Value.HasFlag ((DemoMouseFlags)mouse.Flags))
|
|
{
|
|
lastAppEvent.Text = $" Last App Event: {mouse}";
|
|
appLogList.Add ($"{mouse.Position}:{mouse.Flags}");
|
|
appLog.MoveEnd ();
|
|
}
|
|
};
|
|
|
|
label = new ()
|
|
{
|
|
Text = "_View Events:",
|
|
X = Pos.Right (appLog) + 1,
|
|
Y = Pos.Top (label)
|
|
};
|
|
ObservableCollection<string> viewLogList = [];
|
|
|
|
ListView viewLog = new ()
|
|
{
|
|
X = Pos.Left (label),
|
|
Y = Pos.Bottom (label),
|
|
Width = Dim.Auto (minimumContentDim: Dim.Percent (20)),
|
|
Height = Dim.Fill (),
|
|
SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Base),
|
|
Source = new ListWrapper<string> (viewLogList)
|
|
};
|
|
runnable.Add (label, viewLog);
|
|
|
|
|
|
demo.MouseEvent += (_, mouse) =>
|
|
{
|
|
if (mouseFlagsFilter.Value.Value.HasFlag ((DemoMouseFlags)mouse.Flags))
|
|
{
|
|
lastViewEvent.Text = $" Last View Event: {mouse}";
|
|
viewLogList.Add ($"{mouse.Position}:{mouse.View!.Id}:{mouse.Flags}");
|
|
viewLog.MoveEnd ();
|
|
}
|
|
};
|
|
|
|
demoInPadding.MouseEvent += (_, mouse) =>
|
|
{
|
|
if (mouseFlagsFilter.Value.Value.HasFlag ((DemoMouseFlags)mouse.Flags))
|
|
{
|
|
lastViewEvent.Text = $" Last View Event: {mouse}";
|
|
viewLogList.Add ($"{mouse.Position}:{mouse.View!.Id}:{mouse.Flags}");
|
|
viewLog.MoveEnd ();
|
|
}
|
|
};
|
|
|
|
sub1.MouseEvent += (_, mouse) =>
|
|
{
|
|
if (mouseFlagsFilter.Value.Value.HasFlag ((DemoMouseFlags)mouse.Flags))
|
|
{
|
|
lastViewEvent.Text = $" Last View Event: {mouse}";
|
|
viewLogList.Add ($"{mouse.Position}:{mouse.View!.Id}:{mouse.Flags}");
|
|
viewLog.MoveEnd ();
|
|
}
|
|
};
|
|
|
|
sub2.MouseEvent += (_, mouse) =>
|
|
{
|
|
if (mouseFlagsFilter.Value.Value.HasFlag ((DemoMouseFlags)mouse.Flags))
|
|
{
|
|
lastViewEvent.Text = $" Last View Event: {mouse}";
|
|
viewLogList.Add ($"{mouse.Position}:{mouse.View!.Id}:{mouse.Flags}");
|
|
viewLog.MoveEnd ();
|
|
}
|
|
};
|
|
label = new ()
|
|
{
|
|
Text = "_Commands:",
|
|
X = Pos.Right (viewLog) + 1,
|
|
Y = Pos.Top (label)
|
|
};
|
|
ObservableCollection<string> commandLogList = [];
|
|
|
|
ListView commandLog = new ()
|
|
{
|
|
X = Pos.Left (label),
|
|
Y = Pos.Bottom (label),
|
|
Width = Dim.Auto (minimumContentDim: Dim.Percent (15)),
|
|
Height = Dim.Fill (),
|
|
SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Base),
|
|
Source = new ListWrapper<string> (commandLogList)
|
|
};
|
|
runnable.Add (label, commandLog);
|
|
|
|
demo.Activating += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
demo.Accepting += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
demoInPadding.Activating += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
demoInPadding.Accepting += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
sub1.Activating += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
sub1.Accepting += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
sub2.Activating += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
sub2.Accepting += (_, args) =>
|
|
{
|
|
commandLogList.Add ($"{args.Context!.Source!.Id}:{args.Context!.Command}");
|
|
commandLog.MoveEnd ();
|
|
args.Handled = true;
|
|
};
|
|
|
|
|
|
runnable.CommandNotBound += (_, args) =>
|
|
{
|
|
if (args.Context!.Command == Command.DeleteAll)
|
|
{
|
|
driverLogList.Clear ();
|
|
driverLog.SetSource (driverLogList);
|
|
appLogList.Clear ();
|
|
appLog.SetSource (appLogList);
|
|
viewLogList.Clear ();
|
|
viewLog.SetSource (viewLogList);
|
|
commandLogList.Clear ();
|
|
commandLog.SetSource (commandLogList);
|
|
args.Handled = true;
|
|
}
|
|
};
|
|
|
|
app.Run (runnable);
|
|
}
|
|
|
|
public class MouseEventDemoView : View
|
|
{
|
|
public MouseEventDemoView ()
|
|
{
|
|
CanFocus = true;
|
|
Id = "mouseEventDemoView";
|
|
|
|
MouseLeave += (_, _) => { Text = "Leave"; };
|
|
MouseEnter += (_, _) => { Text = "Enter"; };
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void EndInit ()
|
|
{
|
|
SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Base);
|
|
|
|
TextAlignment = Alignment.Center;
|
|
VerticalTextAlignment = Alignment.Center;
|
|
|
|
Padding!.Thickness = new (1, 1, 1, 1);
|
|
Padding!.SetScheme (new (new Attribute (Color.DarkGray)));
|
|
Padding.Id = $"{Id}.Padding";
|
|
|
|
Border!.Thickness = new (1);
|
|
Border.LineStyle = LineStyle.Rounded;
|
|
Border.Id = $"{Id}.Border";
|
|
base.EndInit ();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnMouseStateChanged (EventArgs<MouseState> args)
|
|
{
|
|
base.OnMouseStateChanged (args);
|
|
Border!.LineStyle = args.Value.HasFlag (MouseState.PressedOutside) ? LineStyle.Dotted : LineStyle.Single;
|
|
|
|
SetNeedsDraw ();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute)
|
|
{
|
|
switch (role)
|
|
{
|
|
case VisualRole.Normal when MouseState.HasFlag (MouseState.Pressed) && HighlightStates.HasFlag (MouseState.Pressed):
|
|
currentAttribute = currentAttribute with { Background = currentAttribute.Foreground.GetBrighterColor () };
|
|
|
|
return true;
|
|
default:
|
|
return base.OnGettingAttributeForRole (in role, ref currentAttribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// All the MouseFlags we can set for filtering
|
|
[Flags]
|
|
internal enum DemoMouseFlags
|
|
{
|
|
/// <summary>
|
|
/// No mouse event. This is the default value for <see cref="MouseEventArgs.Flags"/> when no mouse event is being
|
|
/// reported.
|
|
/// </summary>
|
|
None = 0,
|
|
|
|
/// <summary>The first mouse button was pressed.</summary>
|
|
Button1Pressed = 0x2,
|
|
|
|
/// <summary>The first mouse button was released.</summary>
|
|
Button1Released = 0x1,
|
|
|
|
/// <summary>The first mouse button was clicked (press+release).</summary>
|
|
Button1Clicked = 0x4,
|
|
|
|
/// <summary>The first mouse button was double-clicked.</summary>
|
|
Button1DoubleClicked = 0x8,
|
|
|
|
/// <summary>The first mouse button was triple-clicked.</summary>
|
|
Button1TripleClicked = 0x10,
|
|
|
|
/// <summary>The second mouse button was pressed.</summary>
|
|
MiddleButtonPressed = 0x80,
|
|
|
|
/// <summary>The second mouse button was released.</summary>
|
|
MiddleButtonReleased = 0x40,
|
|
|
|
/// <summary>The second mouse button was clicked (press+release).</summary>
|
|
MiddleButtonClicked = 0x100,
|
|
|
|
/// <summary>The second mouse button was double-clicked.</summary>
|
|
MiddleButtonDoubleClicked = 0x200,
|
|
|
|
/// <summary>The second mouse button was triple-clicked.</summary>
|
|
MiddleButtonTripleClicked = 0x400,
|
|
|
|
/// <summary>The third mouse button was pressed.</summary>
|
|
Button3Pressed = 0x2000,
|
|
|
|
/// <summary>The third mouse button was released.</summary>
|
|
Button3Released = 0x1000,
|
|
|
|
/// <summary>The third mouse button was clicked (press+release).</summary>
|
|
Button3Clicked = 0x4000,
|
|
|
|
/// <summary>The third mouse button was double-clicked.</summary>
|
|
Button3DoubleClicked = 0x8000,
|
|
|
|
/// <summary>The third mouse button was triple-clicked.</summary>
|
|
Button3TripleClicked = 0x10000,
|
|
|
|
/// <summary>The fourth mouse button was pressed.</summary>
|
|
Button4Pressed = 0x80000,
|
|
|
|
/// <summary>The fourth mouse button was released.</summary>
|
|
Button4Released = 0x40000,
|
|
|
|
/// <summary>The fourth mouse button was clicked.</summary>
|
|
Button4Clicked = 0x100000,
|
|
|
|
/// <summary>The fourth mouse button was double-clicked.</summary>
|
|
Button4DoubleClicked = 0x200000,
|
|
|
|
/// <summary>The fourth mouse button was triple-clicked.</summary>
|
|
Button4TripleClicked = 0x400000,
|
|
|
|
/// <summary>The mouse position is being reported in this event.</summary>
|
|
PositionReport = 0x8000000,
|
|
|
|
/// <summary>Vertical button wheeled up.</summary>
|
|
WheeledUp = 0x10000000,
|
|
|
|
/// <summary>Vertical button wheeled down.</summary>
|
|
WheeledDown = 0x20000000,
|
|
|
|
/// <summary>Vertical button wheeled up while pressing Ctrl.</summary>
|
|
WheeledLeft = 0x1000000 | WheeledUp,
|
|
|
|
/// <summary>Vertical button wheeled down while pressing Ctrl.</summary>
|
|
WheeledRight = 0x1000000 | WheeledDown,
|
|
|
|
All = -1
|
|
}
|