Merge branch 'events' of https://github.com/tznind/gui.cs into events

This commit is contained in:
tznind
2023-03-15 19:35:29 +00:00
5 changed files with 243 additions and 30 deletions

View File

@@ -162,7 +162,7 @@ namespace Terminal.Gui {
if (alternateForwardKey != value) { if (alternateForwardKey != value) {
var oldKey = alternateForwardKey; var oldKey = alternateForwardKey;
alternateForwardKey = value; alternateForwardKey = value;
OnAlternateForwardKeyChanged (new KeyChangedEventArgs(oldKey,value)); OnAlternateForwardKeyChanged (new KeyChangedEventArgs (oldKey, value));
} }
} }
} }
@@ -186,7 +186,7 @@ namespace Terminal.Gui {
if (alternateBackwardKey != value) { if (alternateBackwardKey != value) {
var oldKey = alternateBackwardKey; var oldKey = alternateBackwardKey;
alternateBackwardKey = value; alternateBackwardKey = value;
OnAlternateBackwardKeyChanged (new KeyChangedEventArgs(oldKey,value)); OnAlternateBackwardKeyChanged (new KeyChangedEventArgs (oldKey, value));
} }
} }
} }
@@ -210,7 +210,7 @@ namespace Terminal.Gui {
if (quitKey != value) { if (quitKey != value) {
var oldKey = quitKey; var oldKey = quitKey;
quitKey = value; quitKey = value;
OnQuitKeyChanged (new KeyChangedEventArgs(oldKey,value)); OnQuitKeyChanged (new KeyChangedEventArgs (oldKey, value));
} }
} }
} }
@@ -720,6 +720,16 @@ namespace Terminal.Gui {
/// </summary> /// </summary>
public static View MouseGrabView => mouseGrabView; public static View MouseGrabView => mouseGrabView;
/// <summary>
/// Event to be invoked when a view want grab the mouse which can be canceled.
/// </summary>
public static event EventHandler<GrabMouseEventArgs> GrabbingMouse;
/// <summary>
/// Event to be invoked when a view want ungrab the mouse which can be canceled.
/// </summary>
public static event EventHandler<GrabMouseEventArgs> UnGrabbingMouse;
/// <summary> /// <summary>
/// Event to be invoked when a view grab the mouse. /// Event to be invoked when a view grab the mouse.
/// </summary> /// </summary>
@@ -739,9 +749,11 @@ namespace Terminal.Gui {
{ {
if (view == null) if (view == null)
return; return;
OnGrabbedMouse (view); if (!OnGrabbingMouse (view)) {
mouseGrabView = view; OnGrabbedMouse (view);
Driver.UncookMouse (); mouseGrabView = view;
Driver.UncookMouse ();
}
} }
/// <summary> /// <summary>
@@ -751,16 +763,36 @@ namespace Terminal.Gui {
{ {
if (mouseGrabView == null) if (mouseGrabView == null)
return; return;
OnUnGrabbedMouse (mouseGrabView); if (!OnUnGrabbingMouse (mouseGrabView)) {
mouseGrabView = null; OnUnGrabbedMouse (mouseGrabView);
Driver.CookMouse (); mouseGrabView = null;
Driver.CookMouse ();
}
}
static bool OnGrabbingMouse (View view)
{
if (view == null)
return false;
var evArgs = new GrabMouseEventArgs (view);
GrabbingMouse?.Invoke (view, evArgs);
return evArgs.Cancel;
}
static bool OnUnGrabbingMouse (View view)
{
if (view == null)
return false;
var evArgs = new GrabMouseEventArgs (view);
UnGrabbingMouse?.Invoke (view, evArgs);
return evArgs.Cancel;
} }
static void OnGrabbedMouse (View view) static void OnGrabbedMouse (View view)
{ {
if (view == null) if (view == null)
return; return;
GrabbedMouse?.Invoke (view, new ViewEventArgs(view)); GrabbedMouse?.Invoke (view, new ViewEventArgs (view));
} }
static void OnUnGrabbedMouse (View view) static void OnUnGrabbedMouse (View view)
@@ -1029,7 +1061,7 @@ namespace Terminal.Gui {
Driver.Refresh (); Driver.Refresh ();
} }
NotifyNewRunState?.Invoke (toplevel, new RunStateEventArgs(rs)); NotifyNewRunState?.Invoke (toplevel, new RunStateEventArgs (rs));
return rs; return rs;
} }
@@ -1497,7 +1529,7 @@ namespace Terminal.Gui {
static void OnNotifyStopRunState (Toplevel top) static void OnNotifyStopRunState (Toplevel top)
{ {
if (ExitRunLoopAfterFirstIteration) { if (ExitRunLoopAfterFirstIteration) {
NotifyStopRunState?.Invoke (top, new ToplevelEventArgs(top)); NotifyStopRunState?.Invoke (top, new ToplevelEventArgs (top));
} }
} }
@@ -1516,7 +1548,7 @@ namespace Terminal.Gui {
t.SetRelativeLayout (full); t.SetRelativeLayout (full);
t.LayoutSubviews (); t.LayoutSubviews ();
t.PositionToplevels (); t.PositionToplevels ();
t.OnResized (new SizeChangedEventArgs(full.Size)); t.OnResized (new SizeChangedEventArgs (full.Size));
} }
Refresh (); Refresh ();
} }

View File

@@ -0,0 +1,29 @@
using System;
namespace Terminal.Gui {
/// <summary>
/// Args for events that relate to specific <see cref="Application.MouseGrabView"/>
/// </summary>
public class GrabMouseEventArgs : EventArgs{
/// <summary>
/// Creates a new instance of the <see cref="GrabMouseEventArgs"/> class.
/// </summary>
/// <param name="view">The view that the event is about.</param>
public GrabMouseEventArgs (View view)
{
View = view;
}
/// <summary>
/// The view that the event is about.
/// </summary>
public View View { get; }
/// <summary>
/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
/// event handler, the event will be canceled.
/// </summary>
public bool Cancel { get; set; }
}
}

View File

@@ -120,7 +120,7 @@ namespace Terminal.Gui {
internal virtual void OnChildUnloaded (Toplevel top) internal virtual void OnChildUnloaded (Toplevel top)
{ {
ChildUnloaded?.Invoke (this, new ToplevelEventArgs(top)); ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top));
} }
internal virtual void OnChildLoaded (Toplevel top) internal virtual void OnChildLoaded (Toplevel top)
@@ -159,7 +159,7 @@ namespace Terminal.Gui {
internal virtual void OnActivate (Toplevel deactivated) internal virtual void OnActivate (Toplevel deactivated)
{ {
Activate?.Invoke (this, new ToplevelEventArgs(deactivated)); Activate?.Invoke (this, new ToplevelEventArgs (deactivated));
} }
/// <summary> /// <summary>
@@ -221,6 +221,9 @@ namespace Terminal.Gui {
{ {
ColorScheme = Colors.TopLevel; ColorScheme = Colors.TopLevel;
Application.GrabbingMouse += Application_GrabbingMouse;
Application.UnGrabbingMouse += Application_UnGrabbingMouse;
// Things this view knows how to do // Things this view knows how to do
AddCommand (Command.QuitToplevel, () => { QuitToplevel (); return true; }); AddCommand (Command.QuitToplevel, () => { QuitToplevel (); return true; });
AddCommand (Command.Suspend, () => { Driver.Suspend (); ; return true; }); AddCommand (Command.Suspend, () => { Driver.Suspend (); ; return true; });
@@ -256,6 +259,20 @@ namespace Terminal.Gui {
AddKeyBinding (Key.L | Key.CtrlMask, Command.Refresh); AddKeyBinding (Key.L | Key.CtrlMask, Command.Refresh);
} }
private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
{
if (Application.MouseGrabView == this && dragPosition.HasValue) {
e.Cancel = true;
}
}
private void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
{
if (Application.MouseGrabView == this && dragPosition.HasValue) {
e.Cancel = true;
}
}
/// <summary> /// <summary>
/// Invoked when the <see cref="Application.AlternateForwardKey"/> is changed. /// Invoked when the <see cref="Application.AlternateForwardKey"/> is changed.
/// </summary> /// </summary>
@@ -824,8 +841,8 @@ namespace Terminal.Gui {
} }
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && dragPosition.HasValue) { if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && dragPosition.HasValue) {
Application.UngrabMouse ();
dragPosition = null; dragPosition = null;
Application.UngrabMouse ();
} }
//System.Diagnostics.Debug.WriteLine ($"dragPosition after: {dragPosition.HasValue}"); //System.Diagnostics.Debug.WriteLine ($"dragPosition after: {dragPosition.HasValue}");

View File

@@ -37,7 +37,8 @@ namespace UICatalog.Scenarios {
Y = 3, Y = 3,
Width = Dim.Fill (3), Width = Dim.Fill (3),
Height = Dim.Fill (3), Height = Dim.Fill (3),
ColorScheme = Colors.Dialog ColorScheme = Colors.Dialog,
Id = "1"
}; };
var embedded2 = new Window ("2") { var embedded2 = new Window ("2") {
@@ -45,7 +46,8 @@ namespace UICatalog.Scenarios {
Y = 3, Y = 3,
Width = Dim.Fill (3), Width = Dim.Fill (3),
Height = Dim.Fill (3), Height = Dim.Fill (3),
ColorScheme = Colors.Error ColorScheme = Colors.Error,
Id = "2"
}; };
embedded1.Add (embedded2); embedded1.Add (embedded2);
@@ -54,7 +56,8 @@ namespace UICatalog.Scenarios {
Y = 3, Y = 3,
Width = Dim.Fill (3), Width = Dim.Fill (3),
Height = Dim.Fill (3), Height = Dim.Fill (3),
ColorScheme = Colors.TopLevel ColorScheme = Colors.TopLevel,
Id = "3"
}; };
var testButton = new Button (2, 2, "click me"); var testButton = new Button (2, 2, "click me");

View File

@@ -154,28 +154,28 @@ namespace Terminal.Gui.TopLevelTests {
var eventInvoked = ""; var eventInvoked = "";
top.ChildUnloaded += (s,e) => eventInvoked = "ChildUnloaded"; top.ChildUnloaded += (s, e) => eventInvoked = "ChildUnloaded";
top.OnChildUnloaded (top); top.OnChildUnloaded (top);
Assert.Equal ("ChildUnloaded", eventInvoked); Assert.Equal ("ChildUnloaded", eventInvoked);
top.ChildLoaded += (s,e) => eventInvoked = "ChildLoaded"; top.ChildLoaded += (s, e) => eventInvoked = "ChildLoaded";
top.OnChildLoaded (top); top.OnChildLoaded (top);
Assert.Equal ("ChildLoaded", eventInvoked); Assert.Equal ("ChildLoaded", eventInvoked);
top.Closed += (s, e) => eventInvoked = "Closed"; top.Closed += (s, e) => eventInvoked = "Closed";
top.OnClosed (top); top.OnClosed (top);
Assert.Equal ("Closed", eventInvoked); Assert.Equal ("Closed", eventInvoked);
top.Closing += (s,e) => eventInvoked = "Closing"; top.Closing += (s, e) => eventInvoked = "Closing";
top.OnClosing (new ToplevelClosingEventArgs (top)); top.OnClosing (new ToplevelClosingEventArgs (top));
Assert.Equal ("Closing", eventInvoked); Assert.Equal ("Closing", eventInvoked);
top.AllChildClosed += (s, e) => eventInvoked = "AllChildClosed"; top.AllChildClosed += (s, e) => eventInvoked = "AllChildClosed";
top.OnAllChildClosed (); top.OnAllChildClosed ();
Assert.Equal ("AllChildClosed", eventInvoked); Assert.Equal ("AllChildClosed", eventInvoked);
top.ChildClosed += (s,e) => eventInvoked = "ChildClosed"; top.ChildClosed += (s, e) => eventInvoked = "ChildClosed";
top.OnChildClosed (top); top.OnChildClosed (top);
Assert.Equal ("ChildClosed", eventInvoked); Assert.Equal ("ChildClosed", eventInvoked);
top.Deactivate += (s,e) => eventInvoked = "Deactivate"; top.Deactivate += (s, e) => eventInvoked = "Deactivate";
top.OnDeactivate (top); top.OnDeactivate (top);
Assert.Equal ("Deactivate", eventInvoked); Assert.Equal ("Deactivate", eventInvoked);
top.Activate += (s,e) => eventInvoked = "Activate"; top.Activate += (s, e) => eventInvoked = "Activate";
top.OnActivate (top); top.OnActivate (top);
Assert.Equal ("Activate", eventInvoked); Assert.Equal ("Activate", eventInvoked);
top.Loaded += (s, e) => eventInvoked = "Loaded"; top.Loaded += (s, e) => eventInvoked = "Loaded";
@@ -356,7 +356,7 @@ namespace Terminal.Gui.TopLevelTests {
var top = Application.Top; var top = Application.Top;
top.Add (win1, win2); top.Add (win1, win2);
top.Loaded += (s, e) => isRunning = true; top.Loaded += (s, e) => isRunning = true;
top.Closing += (s,e) => isRunning = false; top.Closing += (s, e) => isRunning = false;
Application.Begin (top); Application.Begin (top);
top.Running = true; top.Running = true;
@@ -589,9 +589,9 @@ namespace Terminal.Gui.TopLevelTests {
void View_Added (object sender, SuperViewChangedEventArgs e) void View_Added (object sender, SuperViewChangedEventArgs e)
{ {
Assert.Throws<NullReferenceException> (() => Application.Top.AlternateForwardKeyChanged += (s,e) => alternateForwardKey = e.OldKey); Assert.Throws<NullReferenceException> (() => Application.Top.AlternateForwardKeyChanged += (s, e) => alternateForwardKey = e.OldKey);
Assert.Throws<NullReferenceException> (() => Application.Top.AlternateBackwardKeyChanged += (s,e) => alternateBackwardKey = e.OldKey); Assert.Throws<NullReferenceException> (() => Application.Top.AlternateBackwardKeyChanged += (s, e) => alternateBackwardKey = e.OldKey);
Assert.Throws<NullReferenceException> (() => Application.Top.QuitKeyChanged += (s,e) => quitKey = e.OldKey); Assert.Throws<NullReferenceException> (() => Application.Top.QuitKeyChanged += (s, e) => quitKey = e.OldKey);
Assert.False (wasAdded); Assert.False (wasAdded);
wasAdded = true; wasAdded = true;
view.Added -= View_Added; view.Added -= View_Added;
@@ -621,7 +621,7 @@ namespace Terminal.Gui.TopLevelTests {
void View_Initialized (object sender, EventArgs e) void View_Initialized (object sender, EventArgs e)
{ {
Application.Top.AlternateForwardKeyChanged += (s,e) => alternateForwardKey = e.OldKey; Application.Top.AlternateForwardKeyChanged += (s, e) => alternateForwardKey = e.OldKey;
Application.Top.AlternateBackwardKeyChanged += (s, e) => alternateBackwardKey = e.OldKey; Application.Top.AlternateBackwardKeyChanged += (s, e) => alternateBackwardKey = e.OldKey;
Application.Top.QuitKeyChanged += (s, e) => quitKey = e.OldKey; Application.Top.QuitKeyChanged += (s, e) => quitKey = e.OldKey;
} }
@@ -1081,5 +1081,137 @@ namespace Terminal.Gui.TopLevelTests {
Assert.Equal (new Rect (1, 3, 10, 5), view.Frame); Assert.Equal (new Rect (1, 3, 10, 5), view.Frame);
Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay); Assert.Equal (new Rect (0, 0, 10, 5), view.NeedDisplay);
} }
[Fact, AutoInitShutdown]
public void Toplevel_Inside_ScrollView_MouseGrabView ()
{
var scrollView = new ScrollView () {
X = 3,
Y = 3,
Width = 40,
Height = 16,
ContentSize = new Size (200, 100)
};
var win = new Window ("Window") { X = 3, Y = 3, Width = Dim.Fill (3), Height = Dim.Fill (3) };
scrollView.Add (win);
var top = Application.Top;
top.Add (scrollView);
Application.Begin (top);
Assert.Equal (new Rect (0, 0, 80, 25), top.Frame);
Assert.Equal (new Rect (3, 3, 40, 16), scrollView.Frame);
Assert.Equal (new Rect (0, 0, 200, 100), scrollView.Subviews [0].Frame);
Assert.Equal (new Rect (3, 3, 194, 94), win.Frame);
TestHelpers.AssertDriverContentsWithFrameAre (@"
┌ Window ───────────────────────────┴
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ▼
◄├──────┤░░░░░░░░░░░░░░░░░░░░░░░░░░░░░► ", output);
ReflectionTools.InvokePrivate (
typeof (Application),
"ProcessMouseEvent",
new MouseEvent () {
X = 6,
Y = 6,
Flags = MouseFlags.Button1Pressed
});
Assert.Equal (win, Application.MouseGrabView);
Assert.Equal (new Rect (3, 3, 194, 94), win.Frame);
ReflectionTools.InvokePrivate (
typeof (Application),
"ProcessMouseEvent",
new MouseEvent () {
X = 9,
Y = 9,
Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
});
Assert.Equal (win, Application.MouseGrabView);
top.SetNeedsLayout ();
top.LayoutSubviews ();
Assert.Equal (new Rect (6, 6, 191, 91), win.Frame);
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
┌ Window ────────────────────────░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ▼
◄├──────┤░░░░░░░░░░░░░░░░░░░░░░░░░░░░░► ", output);
ReflectionTools.InvokePrivate (
typeof (Application),
"ProcessMouseEvent",
new MouseEvent () {
X = 5,
Y = 5,
Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
});
Assert.Equal (win, Application.MouseGrabView);
top.SetNeedsLayout ();
top.LayoutSubviews ();
Assert.Equal (new Rect (2, 2, 195, 95), win.Frame);
Application.Refresh ();
TestHelpers.AssertDriverContentsWithFrameAre (@"
┌ Window ────────────────────────────│
│ ┴
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ░
│ ▼
◄├──────┤░░░░░░░░░░░░░░░░░░░░░░░░░░░░░► ", output);
ReflectionTools.InvokePrivate (
typeof (Application),
"ProcessMouseEvent",
new MouseEvent () {
X = 5,
Y = 5,
Flags = MouseFlags.Button1Released
});
Assert.Null (Application.MouseGrabView);
ReflectionTools.InvokePrivate (
typeof (Application),
"ProcessMouseEvent",
new MouseEvent () {
X = 4,
Y = 4,
Flags = MouseFlags.ReportMousePosition
});
Assert.Equal (scrollView, Application.MouseGrabView);
}
} }
} }