diff --git a/Terminal.Gui/Application.MainLoopSyncContext.cs b/Terminal.Gui/Application.MainLoopSyncContext.cs
deleted file mode 100644
index 513608e8c..000000000
--- a/Terminal.Gui/Application.MainLoopSyncContext.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-namespace Terminal.Gui;
-
-public static partial class Application
-{
- ///
- /// provides the sync context set while executing code in Terminal.Gui, to let
- /// users use async/await on their code
- ///
- private sealed class MainLoopSyncContext : SynchronizationContext
- {
- public override SynchronizationContext CreateCopy () { return new MainLoopSyncContext (); }
-
- public override void Post (SendOrPostCallback d, object state)
- {
- MainLoop?.AddIdle (
- () =>
- {
- d (state);
-
- return false;
- }
- );
- }
-
- //_mainLoop.Driver.Wakeup ();
- public override void Send (SendOrPostCallback d, object state)
- {
- if (Thread.CurrentThread.ManagedThreadId == _mainThreadId)
- {
- d (state);
- }
- else
- {
- var wasExecuted = false;
-
- Invoke (
- () =>
- {
- d (state);
- wasExecuted = true;
- }
- );
-
- while (!wasExecuted)
- {
- Thread.Sleep (15);
- }
- }
- }
- }
-}
diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application/Application.cs
similarity index 72%
rename from Terminal.Gui/Application.cs
rename to Terminal.Gui/Application/Application.cs
index 8e414969c..37358db72 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application/Application.cs
@@ -1426,602 +1426,3 @@ public static partial class Application
#endregion Toplevel handling
- #region Mouse handling
-
- /// Disable or enable the mouse. The mouse is enabled by default.
- [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
- public static bool IsMouseDisabled { get; set; }
-
- /// The current object that wants continuous mouse button pressed events.
- public static View WantContinuousButtonPressedView { get; private set; }
-
- ///
- /// Gets the view that grabbed the mouse (e.g. for dragging). When this is set, all mouse events will be routed to
- /// this view until the view calls or the mouse is released.
- ///
- public static View MouseGrabView { get; private set; }
-
- /// Invoked when a view wants to grab the mouse; can be canceled.
- public static event EventHandler GrabbingMouse;
-
- /// Invoked when a view wants un-grab the mouse; can be canceled.
- public static event EventHandler UnGrabbingMouse;
-
- /// Invoked after a view has grabbed the mouse.
- public static event EventHandler GrabbedMouse;
-
- /// Invoked after a view has un-grabbed the mouse.
- public static event EventHandler UnGrabbedMouse;
-
- ///
- /// Grabs the mouse, forcing all mouse events to be routed to the specified view until
- /// is called.
- ///
- /// View that will receive all mouse events until is invoked.
- public static void GrabMouse (View view)
- {
- if (view is null)
- {
- return;
- }
-
- if (!OnGrabbingMouse (view))
- {
- OnGrabbedMouse (view);
- MouseGrabView = view;
- }
- }
-
- /// Releases the mouse grab, so mouse events will be routed to the view on which the mouse is.
- public static void UngrabMouse ()
- {
- if (MouseGrabView is null)
- {
- return;
- }
-
- if (!OnUnGrabbingMouse (MouseGrabView))
- {
- View view = MouseGrabView;
- MouseGrabView = null;
- OnUnGrabbedMouse (view);
- }
- }
-
- private static bool OnGrabbingMouse (View view)
- {
- if (view is null)
- {
- return false;
- }
-
- var evArgs = new GrabMouseEventArgs (view);
- GrabbingMouse?.Invoke (view, evArgs);
-
- return evArgs.Cancel;
- }
-
- private static bool OnUnGrabbingMouse (View view)
- {
- if (view is null)
- {
- return false;
- }
-
- var evArgs = new GrabMouseEventArgs (view);
- UnGrabbingMouse?.Invoke (view, evArgs);
-
- return evArgs.Cancel;
- }
-
- private static void OnGrabbedMouse (View view)
- {
- if (view is null)
- {
- return;
- }
-
- GrabbedMouse?.Invoke (view, new (view));
- }
-
- private static void OnUnGrabbedMouse (View view)
- {
- if (view is null)
- {
- return;
- }
-
- UnGrabbedMouse?.Invoke (view, new (view));
- }
-
-#nullable enable
-
- // Used by OnMouseEvent to track the last view that was clicked on.
- internal static View? _mouseEnteredView;
-
- /// Event fired when a mouse move or click occurs. Coordinates are screen relative.
- ///
- ///
- /// Use this event to receive mouse events in screen coordinates. Use to
- /// receive mouse events relative to a .
- ///
- /// The will contain the that contains the mouse coordinates.
- ///
- public static event EventHandler? MouseEvent;
-
- /// Called when a mouse event occurs. Raises the event.
- /// This method can be used to simulate a mouse event, e.g. in unit tests.
- /// The mouse event with coordinates relative to the screen.
- internal static void OnMouseEvent (MouseEvent mouseEvent)
- {
- if (IsMouseDisabled)
- {
- return;
- }
-
- var view = View.FindDeepestView (Current, mouseEvent.Position);
-
- if (view is { })
- {
- mouseEvent.View = view;
- }
-
- MouseEvent?.Invoke (null, mouseEvent);
-
- if (mouseEvent.Handled)
- {
- return;
- }
-
- if (MouseGrabView is { })
- {
- // If the mouse is grabbed, send the event to the view that grabbed it.
- // The coordinates are relative to the Bounds of the view that grabbed the mouse.
- Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
-
- var viewRelativeMouseEvent = new MouseEvent
- {
- Position = frameLoc,
- Flags = mouseEvent.Flags,
- ScreenPosition = mouseEvent.Position,
- View = MouseGrabView
- };
-
- if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
- {
- // The mouse has moved outside the bounds of the view that grabbed the mouse
- _mouseEnteredView?.NewMouseLeaveEvent (mouseEvent);
- }
-
- //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
- if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) == true)
- {
- return;
- }
- }
-
- if (view is { WantContinuousButtonPressed: true })
- {
- WantContinuousButtonPressedView = view;
- }
- else
- {
- WantContinuousButtonPressedView = null;
- }
-
- if (view is not Adornment)
- {
- if ((view is null || view == OverlappedTop)
- && Current is { Modal: false }
- && OverlappedTop != null
- && mouseEvent.Flags != MouseFlags.ReportMousePosition
- && mouseEvent.Flags != 0)
- {
- // This occurs when there are multiple overlapped "tops"
- // E.g. "Mdi" - in the Background Worker Scenario
- View? top = FindDeepestTop (Top, mouseEvent.Position);
- view = View.FindDeepestView (top, mouseEvent.Position);
-
- if (view is { } && view != OverlappedTop && top != Current && top is { })
- {
- MoveCurrent ((Toplevel)top);
- }
- }
- }
-
- if (view is null)
- {
- return;
- }
-
- MouseEvent? me = null;
-
- if (view is Adornment adornment)
- {
- Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
-
- me = new ()
- {
- Position = frameLoc,
- Flags = mouseEvent.Flags,
- ScreenPosition = mouseEvent.Position,
- View = view
- };
- }
- else if (view.ViewportToScreen (Rectangle.Empty with { Size = view.Viewport.Size }).Contains (mouseEvent.Position))
- {
- Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
-
- me = new ()
- {
- Position = viewportLocation,
- Flags = mouseEvent.Flags,
- ScreenPosition = mouseEvent.Position,
- View = view
- };
- }
-
- if (me is null)
- {
- return;
- }
-
- if (_mouseEnteredView is null)
- {
- _mouseEnteredView = view;
- view.NewMouseEnterEvent (me);
- }
- else if (_mouseEnteredView != view)
- {
- _mouseEnteredView.NewMouseLeaveEvent (me);
- view.NewMouseEnterEvent (me);
- _mouseEnteredView = view;
- }
-
- if (!view.WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
- {
- return;
- }
-
- WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
-
- //Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
-
- while (view.NewMouseEvent (me) != true)
- {
- if (MouseGrabView is { })
- {
- break;
- }
-
- if (view is Adornment adornmentView)
- {
- view = adornmentView.Parent.SuperView;
- }
- else
- {
- view = view.SuperView;
- }
-
- if (view is null)
- {
- break;
- }
-
- Point boundsPoint = view.ScreenToViewport (mouseEvent.Position);
-
- me = new ()
- {
- Position = boundsPoint,
- Flags = mouseEvent.Flags,
- ScreenPosition = mouseEvent.Position,
- View = view
- };
- }
-
- BringOverlappedTopToFront ();
- }
-#nullable restore
-
- #endregion Mouse handling
-
- #region Keyboard handling
-
- private static Key _alternateForwardKey = Key.Empty; // Defined in config.json
-
- /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.
- [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
- [JsonConverter (typeof (KeyJsonConverter))]
- public static Key AlternateForwardKey
- {
- get => _alternateForwardKey;
- set
- {
- if (_alternateForwardKey != value)
- {
- Key oldKey = _alternateForwardKey;
- _alternateForwardKey = value;
- OnAlternateForwardKeyChanged (new (oldKey, value));
- }
- }
- }
-
- private static void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
- {
- foreach (Toplevel top in _topLevels.ToArray ())
- {
- top.OnAlternateForwardKeyChanged (e);
- }
- }
-
- private static Key _alternateBackwardKey = Key.Empty; // Defined in config.json
-
- /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.
- [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
- [JsonConverter (typeof (KeyJsonConverter))]
- public static Key AlternateBackwardKey
- {
- get => _alternateBackwardKey;
- set
- {
- if (_alternateBackwardKey != value)
- {
- Key oldKey = _alternateBackwardKey;
- _alternateBackwardKey = value;
- OnAlternateBackwardKeyChanged (new (oldKey, value));
- }
- }
- }
-
- private static void OnAlternateBackwardKeyChanged (KeyChangedEventArgs oldKey)
- {
- foreach (Toplevel top in _topLevels.ToArray ())
- {
- top.OnAlternateBackwardKeyChanged (oldKey);
- }
- }
-
- private static Key _quitKey = Key.Empty; // Defined in config.json
-
- /// Gets or sets the key to quit the application.
- [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
- [JsonConverter (typeof (KeyJsonConverter))]
- public static Key QuitKey
- {
- get => _quitKey;
- set
- {
- if (_quitKey != value)
- {
- Key oldKey = _quitKey;
- _quitKey = value;
- OnQuitKeyChanged (new (oldKey, value));
- }
- }
- }
-
- private static void OnQuitKeyChanged (KeyChangedEventArgs e)
- {
- // Duplicate the list so if it changes during enumeration we're safe
- foreach (Toplevel top in _topLevels.ToArray ())
- {
- top.OnQuitKeyChanged (e);
- }
- }
-
- ///
- /// Event fired when the user presses a key. Fired by .
- ///
- /// Set to to indicate the key was handled and to prevent
- /// additional processing.
- ///
- ///
- ///
- /// All drivers support firing the event. Some drivers (Curses) do not support firing the
- /// and events.
- /// Fired after and before .
- ///
- public static event EventHandler KeyDown;
-
- ///
- /// Called by the when the user presses a key. Fires the event
- /// then calls on all top level views. Called after and
- /// before .
- ///
- /// Can be used to simulate key press events.
- ///
- /// if the key was handled.
- public static bool OnKeyDown (Key keyEvent)
- {
- if (!_initialized)
- {
- return true;
- }
-
- KeyDown?.Invoke (null, keyEvent);
-
- if (keyEvent.Handled)
- {
- return true;
- }
-
- foreach (Toplevel topLevel in _topLevels.ToList ())
- {
- if (topLevel.NewKeyDownEvent (keyEvent))
- {
- return true;
- }
-
- if (topLevel.Modal)
- {
- break;
- }
- }
-
- // Invoke any global (Application-scoped) KeyBindings.
- // The first view that handles the key will stop the loop.
- foreach (KeyValuePair> binding in _keyBindings.Where (b => b.Key == keyEvent.KeyCode))
- {
- foreach (View view in binding.Value)
- {
- bool? handled = view?.OnInvokingKeyBindings (keyEvent);
-
- if (handled != null && (bool)handled)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- ///
- /// Event fired when the user releases a key. Fired by .
- ///
- /// Set to to indicate the key was handled and to prevent
- /// additional processing.
- ///
- ///
- ///
- /// All drivers support firing the event. Some drivers (Curses) do not support firing the
- /// and events.
- /// Fired after .
- ///
- public static event EventHandler KeyUp;
-
- ///
- /// Called by the when the user releases a key. Fires the event
- /// then calls on all top level views. Called after .
- ///
- /// Can be used to simulate key press events.
- ///
- /// if the key was handled.
- public static bool OnKeyUp (Key a)
- {
- if (!_initialized)
- {
- return true;
- }
-
- KeyUp?.Invoke (null, a);
-
- if (a.Handled)
- {
- return true;
- }
-
- foreach (Toplevel topLevel in _topLevels.ToList ())
- {
- if (topLevel.NewKeyUpEvent (a))
- {
- return true;
- }
-
- if (topLevel.Modal)
- {
- break;
- }
- }
-
- return false;
- }
-
- ///
- /// The key bindings.
- ///
- private static readonly Dictionary> _keyBindings = new ();
-
- ///
- /// Adds an scoped key binding.
- ///
- ///
- /// This is an internal method used by the class to add Application key bindings.
- ///
- /// The key being bound.
- /// The view that is bound to the key.
- internal static void AddKeyBinding (Key key, View view)
- {
- if (!_keyBindings.ContainsKey (key))
- {
- _keyBindings [key] = [];
- }
- _keyBindings [key].Add (view);
- }
-
- ///
- /// Gets the list of Views that have key bindings.
- ///
- ///
- /// This is an internal method used by the class to add Application key bindings.
- ///
- /// The list of Views that have Application-scoped key bindings.
- internal static List GetViewsWithKeyBindings ()
- {
- return _keyBindings.Values.SelectMany (v => v).ToList ();
- }
-
- ///
- /// Gets the list of Views that have key bindings for the specified key.
- ///
- ///
- /// This is an internal method used by the class to add Application key bindings.
- ///
- /// The key to check.
- /// Outputs the list of views bound to
- /// if successful.
- internal static bool TryGetKeyBindings (Key key, out List views)
- {
- return _keyBindings.TryGetValue (key, out views);
- }
-
- ///
- /// Removes an scoped key binding.
- ///
- ///
- /// This is an internal method used by the class to remove Application key bindings.
- ///
- /// The key that was bound.
- /// The view that is bound to the key.
- internal static void RemoveKeyBinding (Key key, View view)
- {
- if (_keyBindings.TryGetValue (key, out List views))
- {
- views.Remove (view);
-
- if (views.Count == 0)
- {
- _keyBindings.Remove (key);
- }
- }
- }
-
- ///
- /// Removes all scoped key bindings for the specified view.
- ///
- ///
- /// This is an internal method used by the class to remove Application key bindings.
- ///
- /// The view that is bound to the key.
- internal static void ClearKeyBindings (View view)
- {
- foreach (Key key in _keyBindings.Keys)
- {
- _keyBindings [key].Remove (view);
- }
- }
-
- ///
- /// Removes all scoped key bindings for the specified view.
- ///
- ///
- /// This is an internal method used by the class to remove Application key bindings.
- ///
- /// The view that is bound to the key.
- internal static void ClearKeyBindings ()
- {
- _keyBindings.Clear ();
- }
-
- #endregion Keyboard handling
-}
diff --git a/Terminal.Gui/Application/ApplicationKeyboard.cs b/Terminal.Gui/Application/ApplicationKeyboard.cs
new file mode 100644
index 000000000..167e1dfb0
--- /dev/null
+++ b/Terminal.Gui/Application/ApplicationKeyboard.cs
@@ -0,0 +1,294 @@
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui;
+
+partial class Application
+{
+ private static Key _alternateForwardKey = Key.Empty; // Defined in config.json
+
+ /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.
+ [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+ [JsonConverter (typeof (KeyJsonConverter))]
+ public static Key AlternateForwardKey
+ {
+ get => _alternateForwardKey;
+ set
+ {
+ if (_alternateForwardKey != value)
+ {
+ Key oldKey = _alternateForwardKey;
+ _alternateForwardKey = value;
+ OnAlternateForwardKeyChanged (new (oldKey, value));
+ }
+ }
+ }
+
+ private static void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
+ {
+ foreach (Toplevel top in _topLevels.ToArray ())
+ {
+ top.OnAlternateForwardKeyChanged (e);
+ }
+ }
+
+ private static Key _alternateBackwardKey = Key.Empty; // Defined in config.json
+
+ /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.
+ [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+ [JsonConverter (typeof (KeyJsonConverter))]
+ public static Key AlternateBackwardKey
+ {
+ get => _alternateBackwardKey;
+ set
+ {
+ if (_alternateBackwardKey != value)
+ {
+ Key oldKey = _alternateBackwardKey;
+ _alternateBackwardKey = value;
+ OnAlternateBackwardKeyChanged (new (oldKey, value));
+ }
+ }
+ }
+
+ private static void OnAlternateBackwardKeyChanged (KeyChangedEventArgs oldKey)
+ {
+ foreach (Toplevel top in _topLevels.ToArray ())
+ {
+ top.OnAlternateBackwardKeyChanged (oldKey);
+ }
+ }
+
+ private static Key _quitKey = Key.Empty; // Defined in config.json
+
+ /// Gets or sets the key to quit the application.
+ [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+ [JsonConverter (typeof (KeyJsonConverter))]
+ public static Key QuitKey
+ {
+ get => _quitKey;
+ set
+ {
+ if (_quitKey != value)
+ {
+ Key oldKey = _quitKey;
+ _quitKey = value;
+ OnQuitKeyChanged (new (oldKey, value));
+ }
+ }
+ }
+
+ private static void OnQuitKeyChanged (KeyChangedEventArgs e)
+ {
+ // Duplicate the list so if it changes during enumeration we're safe
+ foreach (Toplevel top in _topLevels.ToArray ())
+ {
+ top.OnQuitKeyChanged (e);
+ }
+ }
+
+ ///
+ /// Event fired when the user presses a key. Fired by .
+ ///
+ /// Set to to indicate the key was handled and to prevent
+ /// additional processing.
+ ///
+ ///
+ ///
+ /// All drivers support firing the event. Some drivers (Curses) do not support firing the
+ /// and events.
+ /// Fired after and before .
+ ///
+ public static event EventHandler KeyDown;
+
+ ///
+ /// Called by the when the user presses a key. Fires the event
+ /// then calls on all top level views. Called after and
+ /// before .
+ ///
+ /// Can be used to simulate key press events.
+ ///
+ /// if the key was handled.
+ public static bool OnKeyDown (Key keyEvent)
+ {
+ if (!_initialized)
+ {
+ return true;
+ }
+
+ KeyDown?.Invoke (null, keyEvent);
+
+ if (keyEvent.Handled)
+ {
+ return true;
+ }
+
+ foreach (Toplevel topLevel in _topLevels.ToList ())
+ {
+ if (topLevel.NewKeyDownEvent (keyEvent))
+ {
+ return true;
+ }
+
+ if (topLevel.Modal)
+ {
+ break;
+ }
+ }
+
+ // Invoke any global (Application-scoped) KeyBindings.
+ // The first view that handles the key will stop the loop.
+ foreach (KeyValuePair> binding in _keyBindings.Where (b => b.Key == keyEvent.KeyCode))
+ {
+ foreach (View view in binding.Value)
+ {
+ bool? handled = view?.OnInvokingKeyBindings (keyEvent);
+
+ if (handled != null && (bool)handled)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Event fired when the user releases a key. Fired by .
+ ///
+ /// Set to to indicate the key was handled and to prevent
+ /// additional processing.
+ ///
+ ///
+ ///
+ /// All drivers support firing the event. Some drivers (Curses) do not support firing the
+ /// and events.
+ /// Fired after .
+ ///
+ public static event EventHandler KeyUp;
+
+ ///
+ /// Called by the when the user releases a key. Fires the event
+ /// then calls on all top level views. Called after .
+ ///
+ /// Can be used to simulate key press events.
+ ///
+ /// if the key was handled.
+ public static bool OnKeyUp (Key a)
+ {
+ if (!_initialized)
+ {
+ return true;
+ }
+
+ KeyUp?.Invoke (null, a);
+
+ if (a.Handled)
+ {
+ return true;
+ }
+
+ foreach (Toplevel topLevel in _topLevels.ToList ())
+ {
+ if (topLevel.NewKeyUpEvent (a))
+ {
+ return true;
+ }
+
+ if (topLevel.Modal)
+ {
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// The key bindings.
+ ///
+ private static readonly Dictionary> _keyBindings = new ();
+
+ ///
+ /// Adds an scoped key binding.
+ ///
+ ///
+ /// This is an internal method used by the class to add Application key bindings.
+ ///
+ /// The key being bound.
+ /// The view that is bound to the key.
+ internal static void AddKeyBinding (Key key, View view)
+ {
+ if (!_keyBindings.ContainsKey (key))
+ {
+ _keyBindings [key] = [];
+ }
+
+ _keyBindings [key].Add (view);
+ }
+
+ ///
+ /// Gets the list of Views that have key bindings.
+ ///
+ ///
+ /// This is an internal method used by the class to add Application key bindings.
+ ///
+ /// The list of Views that have Application-scoped key bindings.
+ internal static List GetViewsWithKeyBindings () { return _keyBindings.Values.SelectMany (v => v).ToList (); }
+
+ ///
+ /// Gets the list of Views that have key bindings for the specified key.
+ ///
+ ///
+ /// This is an internal method used by the class to add Application key bindings.
+ ///
+ /// The key to check.
+ /// Outputs the list of views bound to
+ /// if successful.
+ internal static bool TryGetKeyBindings (Key key, out List views) { return _keyBindings.TryGetValue (key, out views); }
+
+ ///
+ /// Removes an scoped key binding.
+ ///
+ ///
+ /// This is an internal method used by the class to remove Application key bindings.
+ ///
+ /// The key that was bound.
+ /// The view that is bound to the key.
+ internal static void RemoveKeyBinding (Key key, View view)
+ {
+ if (_keyBindings.TryGetValue (key, out List views))
+ {
+ views.Remove (view);
+
+ if (views.Count == 0)
+ {
+ _keyBindings.Remove (key);
+ }
+ }
+ }
+
+ ///
+ /// Removes all scoped key bindings for the specified view.
+ ///
+ ///
+ /// This is an internal method used by the class to remove Application key bindings.
+ ///
+ /// The view that is bound to the key.
+ internal static void ClearKeyBindings (View view)
+ {
+ foreach (Key key in _keyBindings.Keys)
+ {
+ _keyBindings [key].Remove (view);
+ }
+ }
+
+ ///
+ /// Removes all scoped key bindings for the specified view.
+ ///
+ ///
+ /// This is an internal method used by the class to remove Application key bindings.
+ ///
+ /// The view that is bound to the key.
+ internal static void ClearKeyBindings () { _keyBindings.Clear (); }
+}
diff --git a/Terminal.Gui/Application/ApplicationMouse.cs b/Terminal.Gui/Application/ApplicationMouse.cs
new file mode 100644
index 000000000..9f2a95339
--- /dev/null
+++ b/Terminal.Gui/Application/ApplicationMouse.cs
@@ -0,0 +1,302 @@
+namespace Terminal.Gui;
+
+partial class Application
+{
+ #region Mouse handling
+
+ /// Disable or enable the mouse. The mouse is enabled by default.
+ [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+ public static bool IsMouseDisabled { get; set; }
+
+ /// The current object that wants continuous mouse button pressed events.
+ public static View WantContinuousButtonPressedView { get; private set; }
+
+ ///
+ /// Gets the view that grabbed the mouse (e.g. for dragging). When this is set, all mouse events will be routed to
+ /// this view until the view calls or the mouse is released.
+ ///
+ public static View MouseGrabView { get; private set; }
+
+ /// Invoked when a view wants to grab the mouse; can be canceled.
+ public static event EventHandler GrabbingMouse;
+
+ /// Invoked when a view wants un-grab the mouse; can be canceled.
+ public static event EventHandler UnGrabbingMouse;
+
+ /// Invoked after a view has grabbed the mouse.
+ public static event EventHandler GrabbedMouse;
+
+ /// Invoked after a view has un-grabbed the mouse.
+ public static event EventHandler UnGrabbedMouse;
+
+ ///
+ /// Grabs the mouse, forcing all mouse events to be routed to the specified view until
+ /// is called.
+ ///
+ /// View that will receive all mouse events until is invoked.
+ public static void GrabMouse (View view)
+ {
+ if (view is null)
+ {
+ return;
+ }
+
+ if (!OnGrabbingMouse (view))
+ {
+ OnGrabbedMouse (view);
+ MouseGrabView = view;
+ }
+ }
+
+ /// Releases the mouse grab, so mouse events will be routed to the view on which the mouse is.
+ public static void UngrabMouse ()
+ {
+ if (MouseGrabView is null)
+ {
+ return;
+ }
+
+ if (!OnUnGrabbingMouse (MouseGrabView))
+ {
+ View view = MouseGrabView;
+ MouseGrabView = null;
+ OnUnGrabbedMouse (view);
+ }
+ }
+
+ private static bool OnGrabbingMouse (View view)
+ {
+ if (view is null)
+ {
+ return false;
+ }
+
+ var evArgs = new GrabMouseEventArgs (view);
+ GrabbingMouse?.Invoke (view, evArgs);
+
+ return evArgs.Cancel;
+ }
+
+ private static bool OnUnGrabbingMouse (View view)
+ {
+ if (view is null)
+ {
+ return false;
+ }
+
+ var evArgs = new GrabMouseEventArgs (view);
+ UnGrabbingMouse?.Invoke (view, evArgs);
+
+ return evArgs.Cancel;
+ }
+
+ private static void OnGrabbedMouse (View view)
+ {
+ if (view is null)
+ {
+ return;
+ }
+
+ GrabbedMouse?.Invoke (view, new (view));
+ }
+
+ private static void OnUnGrabbedMouse (View view)
+ {
+ if (view is null)
+ {
+ return;
+ }
+
+ UnGrabbedMouse?.Invoke (view, new (view));
+ }
+
+#nullable enable
+
+ // Used by OnMouseEvent to track the last view that was clicked on.
+ internal static View? _mouseEnteredView;
+
+ /// Event fired when a mouse move or click occurs. Coordinates are screen relative.
+ ///
+ ///
+ /// Use this event to receive mouse events in screen coordinates. Use to
+ /// receive mouse events relative to a .
+ ///
+ /// The will contain the that contains the mouse coordinates.
+ ///
+ public static event EventHandler? MouseEvent;
+
+ /// Called when a mouse event occurs. Raises the event.
+ /// This method can be used to simulate a mouse event, e.g. in unit tests.
+ /// The mouse event with coordinates relative to the screen.
+ internal static void OnMouseEvent (MouseEvent mouseEvent)
+ {
+ if (IsMouseDisabled)
+ {
+ return;
+ }
+
+ var view = View.FindDeepestView (Current, mouseEvent.Position);
+
+ if (view is { })
+ {
+ mouseEvent.View = view;
+ }
+
+ MouseEvent?.Invoke (null, mouseEvent);
+
+ if (mouseEvent.Handled)
+ {
+ return;
+ }
+
+ if (MouseGrabView is { })
+ {
+ // If the mouse is grabbed, send the event to the view that grabbed it.
+ // The coordinates are relative to the Bounds of the view that grabbed the mouse.
+ Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
+
+ var viewRelativeMouseEvent = new MouseEvent
+ {
+ Position = frameLoc,
+ Flags = mouseEvent.Flags,
+ ScreenPosition = mouseEvent.Position,
+ View = MouseGrabView
+ };
+
+ if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
+ {
+ // The mouse has moved outside the bounds of the view that grabbed the mouse
+ _mouseEnteredView?.NewMouseLeaveEvent (mouseEvent);
+ }
+
+ //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
+ if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) == true)
+ {
+ return;
+ }
+ }
+
+ if (view is { WantContinuousButtonPressed: true })
+ {
+ WantContinuousButtonPressedView = view;
+ }
+ else
+ {
+ WantContinuousButtonPressedView = null;
+ }
+
+ if (view is not Adornment)
+ {
+ if ((view is null || view == OverlappedTop)
+ && Current is { Modal: false }
+ && OverlappedTop != null
+ && mouseEvent.Flags != MouseFlags.ReportMousePosition
+ && mouseEvent.Flags != 0)
+ {
+ // This occurs when there are multiple overlapped "tops"
+ // E.g. "Mdi" - in the Background Worker Scenario
+ View? top = FindDeepestTop (Top, mouseEvent.Position);
+ view = View.FindDeepestView (top, mouseEvent.Position);
+
+ if (view is { } && view != OverlappedTop && top != Current && top is { })
+ {
+ MoveCurrent ((Toplevel)top);
+ }
+ }
+ }
+
+ if (view is null)
+ {
+ return;
+ }
+
+ MouseEvent? me = null;
+
+ if (view is Adornment adornment)
+ {
+ Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
+
+ me = new ()
+ {
+ Position = frameLoc,
+ Flags = mouseEvent.Flags,
+ ScreenPosition = mouseEvent.Position,
+ View = view
+ };
+ }
+ else if (view.ViewportToScreen (Rectangle.Empty with { Size = view.Viewport.Size }).Contains (mouseEvent.Position))
+ {
+ Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
+
+ me = new ()
+ {
+ Position = viewportLocation,
+ Flags = mouseEvent.Flags,
+ ScreenPosition = mouseEvent.Position,
+ View = view
+ };
+ }
+
+ if (me is null)
+ {
+ return;
+ }
+
+ if (_mouseEnteredView is null)
+ {
+ _mouseEnteredView = view;
+ view.NewMouseEnterEvent (me);
+ }
+ else if (_mouseEnteredView != view)
+ {
+ _mouseEnteredView.NewMouseLeaveEvent (me);
+ view.NewMouseEnterEvent (me);
+ _mouseEnteredView = view;
+ }
+
+ if (!view.WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
+ {
+ return;
+ }
+
+ WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+
+ //Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
+
+ while (view.NewMouseEvent (me) != true)
+ {
+ if (MouseGrabView is { })
+ {
+ break;
+ }
+
+ if (view is Adornment adornmentView)
+ {
+ view = adornmentView.Parent.SuperView;
+ }
+ else
+ {
+ view = view.SuperView;
+ }
+
+ if (view is null)
+ {
+ break;
+ }
+
+ Point boundsPoint = view.ScreenToViewport (mouseEvent.Position);
+
+ me = new ()
+ {
+ Position = boundsPoint,
+ Flags = mouseEvent.Flags,
+ ScreenPosition = mouseEvent.Position,
+ View = view
+ };
+ }
+
+ BringOverlappedTopToFront ();
+ }
+
+ #endregion Mouse handling
+}
diff --git a/Terminal.Gui/IterationEventArgs.cs b/Terminal.Gui/Application/IterationEventArgs.cs
similarity index 100%
rename from Terminal.Gui/IterationEventArgs.cs
rename to Terminal.Gui/Application/IterationEventArgs.cs
diff --git a/Terminal.Gui/MainLoop.cs b/Terminal.Gui/Application/MainLoop.cs
similarity index 100%
rename from Terminal.Gui/MainLoop.cs
rename to Terminal.Gui/Application/MainLoop.cs
diff --git a/Terminal.Gui/Application/MainLoopSyncContext.cs b/Terminal.Gui/Application/MainLoopSyncContext.cs
new file mode 100644
index 000000000..5290a2076
--- /dev/null
+++ b/Terminal.Gui/Application/MainLoopSyncContext.cs
@@ -0,0 +1,48 @@
+namespace Terminal.Gui;
+
+///
+/// provides the sync context set while executing code in Terminal.Gui, to let
+/// users use async/await on their code
+///
+internal sealed class MainLoopSyncContext : SynchronizationContext
+{
+ public override SynchronizationContext CreateCopy () { return new MainLoopSyncContext (); }
+
+ public override void Post (SendOrPostCallback d, object state)
+ {
+ Application.MainLoop?.AddIdle (
+ () =>
+ {
+ d (state);
+
+ return false;
+ }
+ );
+ }
+
+ //_mainLoop.Driver.Wakeup ();
+ public override void Send (SendOrPostCallback d, object state)
+ {
+ if (Thread.CurrentThread.ManagedThreadId == Application._mainThreadId)
+ {
+ d (state);
+ }
+ else
+ {
+ var wasExecuted = false;
+
+ Application.Invoke (
+ () =>
+ {
+ d (state);
+ wasExecuted = true;
+ }
+ );
+
+ while (!wasExecuted)
+ {
+ Thread.Sleep (15);
+ }
+ }
+ }
+}
diff --git a/Terminal.Gui/RunState.cs b/Terminal.Gui/Application/RunState.cs
similarity index 100%
rename from Terminal.Gui/RunState.cs
rename to Terminal.Gui/Application/RunState.cs
diff --git a/Terminal.Gui/RunStateEventArgs.cs b/Terminal.Gui/Application/RunStateEventArgs.cs
similarity index 100%
rename from Terminal.Gui/RunStateEventArgs.cs
rename to Terminal.Gui/Application/RunStateEventArgs.cs
diff --git a/Terminal.Gui/Timeout.cs b/Terminal.Gui/Application/Timeout.cs
similarity index 100%
rename from Terminal.Gui/Timeout.cs
rename to Terminal.Gui/Application/Timeout.cs
diff --git a/Terminal.Gui/TimeoutEventArgs.cs b/Terminal.Gui/Application/TimeoutEventArgs.cs
similarity index 100%
rename from Terminal.Gui/TimeoutEventArgs.cs
rename to Terminal.Gui/Application/TimeoutEventArgs.cs