Pārlūkot izejas kodu

Broke Application.cs up and put into subfolder

Tig 1 gadu atpakaļ
vecāks
revīzija
c12578cf14

+ 0 - 51
Terminal.Gui/Application.MainLoopSyncContext.cs

@@ -1,51 +0,0 @@
-namespace Terminal.Gui;
-
-public static partial class Application
-{
-    /// <summary>
-    ///     provides the sync context set while executing code in Terminal.Gui, to let
-    ///     users use async/await on their code
-    /// </summary>
-    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);
-                }
-            }
-        }
-    }
-}

+ 0 - 599
Terminal.Gui/Application.cs → Terminal.Gui/Application/Application.cs

@@ -1426,602 +1426,3 @@ public static partial class Application
 
 
     #endregion Toplevel handling
     #endregion Toplevel handling
 
 
-    #region Mouse handling
-
-    /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static bool IsMouseDisabled { get; set; }
-
-    /// <summary>The current <see cref="View"/> object that wants continuous mouse button pressed events.</summary>
-    public static View WantContinuousButtonPressedView { get; private set; }
-
-    /// <summary>
-    ///     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 <see cref="UngrabMouse"/> or the mouse is released.
-    /// </summary>
-    public static View MouseGrabView { get; private set; }
-
-    /// <summary>Invoked when a view wants to grab the mouse; can be canceled.</summary>
-    public static event EventHandler<GrabMouseEventArgs> GrabbingMouse;
-
-    /// <summary>Invoked when a view wants un-grab the mouse; can be canceled.</summary>
-    public static event EventHandler<GrabMouseEventArgs> UnGrabbingMouse;
-
-    /// <summary>Invoked after a view has grabbed the mouse.</summary>
-    public static event EventHandler<ViewEventArgs> GrabbedMouse;
-
-    /// <summary>Invoked after a view has un-grabbed the mouse.</summary>
-    public static event EventHandler<ViewEventArgs> UnGrabbedMouse;
-
-    /// <summary>
-    ///     Grabs the mouse, forcing all mouse events to be routed to the specified view until <see cref="UngrabMouse"/>
-    ///     is called.
-    /// </summary>
-    /// <param name="view">View that will receive all mouse events until <see cref="UngrabMouse"/> is invoked.</param>
-    public static void GrabMouse (View view)
-    {
-        if (view is null)
-        {
-            return;
-        }
-
-        if (!OnGrabbingMouse (view))
-        {
-            OnGrabbedMouse (view);
-            MouseGrabView = view;
-        }
-    }
-
-    /// <summary>Releases the mouse grab, so mouse events will be routed to the view on which the mouse is.</summary>
-    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;
-
-    /// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         Use this event to receive mouse events in screen coordinates. Use <see cref="MouseEvent"/> to
-    ///         receive mouse events relative to a <see cref="View.Viewport"/>.
-    ///     </para>
-    ///     <para>The <see cref="MouseEvent.View"/> will contain the <see cref="View"/> that contains the mouse coordinates.</para>
-    /// </remarks>
-    public static event EventHandler<MouseEvent>? MouseEvent;
-
-    /// <summary>Called when a mouse event occurs. Raises the <see cref="MouseEvent"/> event.</summary>
-    /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
-    /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
-    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
-
-    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
-    [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
-
-    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
-    [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
-
-    /// <summary>Gets or sets the key to quit the application.</summary>
-    [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);
-        }
-    }
-
-    /// <summary>
-    ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
-    ///     <para>
-    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
-    ///         additional processing.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
-    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
-    ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
-    /// </remarks>
-    public static event EventHandler<Key> KeyDown;
-
-    /// <summary>
-    ///     Called by the <see cref="ConsoleDriver"/> when the user presses a key. Fires the <see cref="KeyDown"/> event
-    ///     then calls <see cref="View.NewKeyDownEvent"/> on all top level views. Called after <see cref="OnKeyDown"/> and
-    ///     before <see cref="OnKeyUp"/>.
-    /// </summary>
-    /// <remarks>Can be used to simulate key press events.</remarks>
-    /// <param name="keyEvent"></param>
-    /// <returns><see langword="true"/> if the key was handled.</returns>
-    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<Key, List<View>> 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;
-    }
-
-    /// <summary>
-    ///     Event fired when the user releases a key. Fired by <see cref="OnKeyUp"/>.
-    ///     <para>
-    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
-    ///         additional processing.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
-    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
-    ///     <para>Fired after <see cref="KeyDown"/>.</para>
-    /// </remarks>
-    public static event EventHandler<Key> KeyUp;
-
-    /// <summary>
-    ///     Called by the <see cref="ConsoleDriver"/> when the user releases a key. Fires the <see cref="KeyUp"/> event
-    ///     then calls <see cref="View.NewKeyUpEvent"/> on all top level views. Called after <see cref="OnKeyDown"/>.
-    /// </summary>
-    /// <remarks>Can be used to simulate key press events.</remarks>
-    /// <param name="a"></param>
-    /// <returns><see langword="true"/> if the key was handled.</returns>
-    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;
-    }
-
-    /// <summary>
-    ///     The <see cref="KeyBindingScope.Application"/> key bindings.
-    /// </summary>
-    private static readonly Dictionary<Key, List<View>> _keyBindings = new ();
-
-    /// <summary>
-    ///     Adds an  <see cref="KeyBindingScope.Application"/> scoped key binding.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
-    /// </remarks>
-    /// <param name="key">The key being bound.</param>
-    /// <param name="view">The view that is bound to the key.</param>
-    internal static void AddKeyBinding (Key key, View view)
-    {
-        if (!_keyBindings.ContainsKey (key))
-        {
-            _keyBindings [key] = [];
-        }
-        _keyBindings [key].Add (view);
-    }
-
-    /// <summary>
-    ///    Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
-    /// </remarks>
-    /// <returns>The list of Views that have Application-scoped key bindings.</returns>
-    internal static List<View> GetViewsWithKeyBindings ()
-    {
-        return _keyBindings.Values.SelectMany (v => v).ToList ();
-    }
-
-    /// <summary>
-    ///    Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings for the specified key.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
-    /// </remarks>
-    /// <param name="key">The key to check.</param>
-    /// <param name="views">Outputs the list of views bound to <paramref name="key"/></param>
-    /// <returns><see langword="True"/> if successful.</returns>
-    internal static bool TryGetKeyBindings (Key key, out List<View> views)
-    {
-        return _keyBindings.TryGetValue (key, out views);
-    }
-
-    /// <summary>
-    ///     Removes an <see cref="KeyBindingScope.Application"/> scoped key binding.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
-    /// </remarks>
-    /// <param name="key">The key that was bound.</param>
-    /// <param name="view">The view that is bound to the key.</param>
-    internal static void RemoveKeyBinding (Key key, View view)
-    {
-        if (_keyBindings.TryGetValue (key, out List<View> views))
-        {
-            views.Remove (view);
-
-            if (views.Count == 0)
-            {
-                _keyBindings.Remove (key);
-            }
-        }
-    }
-
-    /// <summary>
-    ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
-    /// </remarks>
-    /// <param name="view">The view that is bound to the key.</param>
-    internal static void ClearKeyBindings (View view)
-    {
-        foreach (Key key in _keyBindings.Keys)
-        {
-            _keyBindings [key].Remove (view);
-        }
-    }
-
-    /// <summary>
-    ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
-    /// </remarks>
-    /// <param name="view">The view that is bound to the key.</param>
-    internal static void ClearKeyBindings ()
-    {
-        _keyBindings.Clear ();
-    }
-
-    #endregion Keyboard handling
-}

+ 294 - 0
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
+
+    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
+    [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
+
+    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
+    [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
+
+    /// <summary>Gets or sets the key to quit the application.</summary>
+    [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);
+        }
+    }
+
+    /// <summary>
+    ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
+    ///     <para>
+    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
+    ///         additional processing.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+    ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
+    /// </remarks>
+    public static event EventHandler<Key> KeyDown;
+
+    /// <summary>
+    ///     Called by the <see cref="ConsoleDriver"/> when the user presses a key. Fires the <see cref="KeyDown"/> event
+    ///     then calls <see cref="View.NewKeyDownEvent"/> on all top level views. Called after <see cref="OnKeyDown"/> and
+    ///     before <see cref="OnKeyUp"/>.
+    /// </summary>
+    /// <remarks>Can be used to simulate key press events.</remarks>
+    /// <param name="keyEvent"></param>
+    /// <returns><see langword="true"/> if the key was handled.</returns>
+    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<Key, List<View>> 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;
+    }
+
+    /// <summary>
+    ///     Event fired when the user releases a key. Fired by <see cref="OnKeyUp"/>.
+    ///     <para>
+    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
+    ///         additional processing.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+    ///     <para>Fired after <see cref="KeyDown"/>.</para>
+    /// </remarks>
+    public static event EventHandler<Key> KeyUp;
+
+    /// <summary>
+    ///     Called by the <see cref="ConsoleDriver"/> when the user releases a key. Fires the <see cref="KeyUp"/> event
+    ///     then calls <see cref="View.NewKeyUpEvent"/> on all top level views. Called after <see cref="OnKeyDown"/>.
+    /// </summary>
+    /// <remarks>Can be used to simulate key press events.</remarks>
+    /// <param name="a"></param>
+    /// <returns><see langword="true"/> if the key was handled.</returns>
+    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;
+    }
+
+    /// <summary>
+    ///     The <see cref="KeyBindingScope.Application"/> key bindings.
+    /// </summary>
+    private static readonly Dictionary<Key, List<View>> _keyBindings = new ();
+
+    /// <summary>
+    ///     Adds an  <see cref="KeyBindingScope.Application"/> scoped key binding.
+    /// </summary>
+    /// <remarks>
+    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
+    /// </remarks>
+    /// <param name="key">The key being bound.</param>
+    /// <param name="view">The view that is bound to the key.</param>
+    internal static void AddKeyBinding (Key key, View view)
+    {
+        if (!_keyBindings.ContainsKey (key))
+        {
+            _keyBindings [key] = [];
+        }
+
+        _keyBindings [key].Add (view);
+    }
+
+    /// <summary>
+    ///     Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings.
+    /// </summary>
+    /// <remarks>
+    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
+    /// </remarks>
+    /// <returns>The list of Views that have Application-scoped key bindings.</returns>
+    internal static List<View> GetViewsWithKeyBindings () { return _keyBindings.Values.SelectMany (v => v).ToList (); }
+
+    /// <summary>
+    ///     Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings for the specified key.
+    /// </summary>
+    /// <remarks>
+    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
+    /// </remarks>
+    /// <param name="key">The key to check.</param>
+    /// <param name="views">Outputs the list of views bound to <paramref name="key"/></param>
+    /// <returns><see langword="True"/> if successful.</returns>
+    internal static bool TryGetKeyBindings (Key key, out List<View> views) { return _keyBindings.TryGetValue (key, out views); }
+
+    /// <summary>
+    ///     Removes an <see cref="KeyBindingScope.Application"/> scoped key binding.
+    /// </summary>
+    /// <remarks>
+    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
+    /// </remarks>
+    /// <param name="key">The key that was bound.</param>
+    /// <param name="view">The view that is bound to the key.</param>
+    internal static void RemoveKeyBinding (Key key, View view)
+    {
+        if (_keyBindings.TryGetValue (key, out List<View> views))
+        {
+            views.Remove (view);
+
+            if (views.Count == 0)
+            {
+                _keyBindings.Remove (key);
+            }
+        }
+    }
+
+    /// <summary>
+    ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
+    /// </summary>
+    /// <remarks>
+    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
+    /// </remarks>
+    /// <param name="view">The view that is bound to the key.</param>
+    internal static void ClearKeyBindings (View view)
+    {
+        foreach (Key key in _keyBindings.Keys)
+        {
+            _keyBindings [key].Remove (view);
+        }
+    }
+
+    /// <summary>
+    ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
+    /// </summary>
+    /// <remarks>
+    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
+    /// </remarks>
+    /// <param name="view">The view that is bound to the key.</param>
+    internal static void ClearKeyBindings () { _keyBindings.Clear (); }
+}

+ 302 - 0
Terminal.Gui/Application/ApplicationMouse.cs

@@ -0,0 +1,302 @@
+namespace Terminal.Gui;
+
+partial class Application
+{
+    #region Mouse handling
+
+    /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static bool IsMouseDisabled { get; set; }
+
+    /// <summary>The current <see cref="View"/> object that wants continuous mouse button pressed events.</summary>
+    public static View WantContinuousButtonPressedView { get; private set; }
+
+    /// <summary>
+    ///     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 <see cref="UngrabMouse"/> or the mouse is released.
+    /// </summary>
+    public static View MouseGrabView { get; private set; }
+
+    /// <summary>Invoked when a view wants to grab the mouse; can be canceled.</summary>
+    public static event EventHandler<GrabMouseEventArgs> GrabbingMouse;
+
+    /// <summary>Invoked when a view wants un-grab the mouse; can be canceled.</summary>
+    public static event EventHandler<GrabMouseEventArgs> UnGrabbingMouse;
+
+    /// <summary>Invoked after a view has grabbed the mouse.</summary>
+    public static event EventHandler<ViewEventArgs> GrabbedMouse;
+
+    /// <summary>Invoked after a view has un-grabbed the mouse.</summary>
+    public static event EventHandler<ViewEventArgs> UnGrabbedMouse;
+
+    /// <summary>
+    ///     Grabs the mouse, forcing all mouse events to be routed to the specified view until <see cref="UngrabMouse"/>
+    ///     is called.
+    /// </summary>
+    /// <param name="view">View that will receive all mouse events until <see cref="UngrabMouse"/> is invoked.</param>
+    public static void GrabMouse (View view)
+    {
+        if (view is null)
+        {
+            return;
+        }
+
+        if (!OnGrabbingMouse (view))
+        {
+            OnGrabbedMouse (view);
+            MouseGrabView = view;
+        }
+    }
+
+    /// <summary>Releases the mouse grab, so mouse events will be routed to the view on which the mouse is.</summary>
+    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;
+
+    /// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         Use this event to receive mouse events in screen coordinates. Use <see cref="MouseEvent"/> to
+    ///         receive mouse events relative to a <see cref="View.Viewport"/>.
+    ///     </para>
+    ///     <para>The <see cref="MouseEvent.View"/> will contain the <see cref="View"/> that contains the mouse coordinates.</para>
+    /// </remarks>
+    public static event EventHandler<MouseEvent>? MouseEvent;
+
+    /// <summary>Called when a mouse event occurs. Raises the <see cref="MouseEvent"/> event.</summary>
+    /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
+    /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
+    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
+}

+ 0 - 0
Terminal.Gui/IterationEventArgs.cs → Terminal.Gui/Application/IterationEventArgs.cs


+ 0 - 0
Terminal.Gui/MainLoop.cs → Terminal.Gui/Application/MainLoop.cs


+ 48 - 0
Terminal.Gui/Application/MainLoopSyncContext.cs

@@ -0,0 +1,48 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     provides the sync context set while executing code in Terminal.Gui, to let
+///     users use async/await on their code
+/// </summary>
+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);
+            }
+        }
+    }
+}

+ 0 - 0
Terminal.Gui/RunState.cs → Terminal.Gui/Application/RunState.cs


+ 0 - 0
Terminal.Gui/RunStateEventArgs.cs → Terminal.Gui/Application/RunStateEventArgs.cs


+ 0 - 0
Terminal.Gui/Timeout.cs → Terminal.Gui/Application/Timeout.cs


+ 0 - 0
Terminal.Gui/TimeoutEventArgs.cs → Terminal.Gui/Application/TimeoutEventArgs.cs