Browse Source

WIP: Started migrating to View.App

Refactored `ApplicationImpl` to ensure proper handling of the `App`
property for `Toplevel` instances, improving modularity. Replaced
direct references to `Application` with `App` in `Border`, `ShadowView`,
and other classes to enhance flexibility and maintainability.

Introduced `GetApp` in `View` to allow overrides for retrieving the
`App` instance. Updated `Adornment` to use this method. Moved mouse
event subscriptions in `Border` to `BeginInit` for proper lifecycle
management.

Updated unit tests in `ArrangementTests` to use `App.Mouse` instead of
`Application.Mouse`, ensuring alignment with the refactored design.
Added `BeginInit` and `EndInit` calls for proper initialization during
tests. Removed redundant code and improved test assertions.
Tig 1 month ago
parent
commit
acfcce8533

+ 9 - 1
Terminal.Gui/App/ApplicationImpl.Run.cs

@@ -52,6 +52,7 @@ public partial class ApplicationImpl
                 // clean it up here if is the same as _CachedSessionTokenToplevel
                 if (Current == CachedSessionTokenToplevel)
                 {
+                    Current.App = null;
                     Current = null;
                 }
                 else
@@ -91,6 +92,7 @@ public partial class ApplicationImpl
 
         if (Current is null)
         {
+            toplevel.App = Instance;
             Current = toplevel;
         }
 
@@ -116,8 +118,8 @@ public partial class ApplicationImpl
 
                 previousTop.App = null;
 
-                toplevel.App = Instance;
                 Current = toplevel;
+                Current.App = Instance;
                 Current.OnActivate (previousTop);
             }
         }
@@ -254,6 +256,12 @@ public partial class ApplicationImpl
 
         if (SessionStack.TryPeek (out Toplevel? newTop))
         {
+            if (Current is { })
+            {
+                Current.App = null;
+            }
+
+            newTop.App = Instance;
             Current = newTop;
             Current?.SetNeedsDraw ();
         }

+ 0 - 1
Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs

@@ -69,7 +69,6 @@ public class AnsiRequestScheduler
     ///     Sends the <paramref name="request"/> immediately or queues it if there is already
     ///     an outstanding request for the given <see cref="AnsiEscapeSequence.Terminator"/>.
     /// </summary>
-    /// <param name="driverImpl"></param>
     /// <param name="driver"></param>
     /// <param name="request"></param>
     /// <returns><see langword="true"/> if request was sent immediately. <see langword="false"/> if it was queued.</returns>

+ 0 - 3
Terminal.Gui/Drivers/DriverImpl.cs

@@ -414,9 +414,6 @@ internal class DriverImpl : IDriver
             Logging.Error ($"Error suspending terminal: {ex.Message}");
         }
 
-        Application.LayoutAndDraw ();
-
-
         Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
     }
 

+ 4 - 0
Terminal.Gui/ViewBase/Adornment/Adornment.cs

@@ -84,6 +84,10 @@ public class Adornment : View, IDesignable
 
     #region View Overrides
 
+    /// <inheritdoc />
+    protected override IApplication? GetApp () { return Parent?.App; }
+
+
     // If a scheme is explicitly set, use that. Otherwise, use the scheme of the parent view.
     private Scheme? _scheme;
 

+ 22 - 13
Terminal.Gui/ViewBase/Adornment/Border.Arrangment.cs

@@ -39,7 +39,10 @@ public partial class Border
         // Add Commands and KeyBindings - Note it's ok these get added each time. KeyBindings are cleared in EndArrange()
         AddArrangeModeKeyBindings ();
 
-        Application.MouseEvent += ApplicationOnMouseEvent;
+        if (App is { })
+        {
+            App.Mouse.MouseEvent += ApplicationOnMouseEvent;
+        }
 
         // Create all necessary arrangement buttons
         CreateArrangementButtons ();
@@ -428,11 +431,14 @@ public partial class Border
 
         MouseState &= ~MouseState.Pressed;
 
-        Application.MouseEvent -= ApplicationOnMouseEvent;
-
-        if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue)
+        if (App is { })
         {
-            Application.Mouse.UngrabMouse ();
+            App.Mouse.MouseEvent -= ApplicationOnMouseEvent;
+
+            if (App.Mouse.MouseGrabView == this && _dragPosition.HasValue)
+            {
+                App.Mouse.UngrabMouse ();
+            }
         }
 
         // Clean up all arrangement buttons
@@ -497,7 +503,7 @@ public partial class Border
                 // Set the start grab point to the Frame coords
                 _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
                 _dragPosition = mouseEvent.Position;
-                Application.Mouse.GrabMouse (this);
+                App?.Mouse.GrabMouse (this);
 
                 // Determine the mode based on where the click occurred
                 ViewArrangement arrangeMode = DetermineArrangeModeFromClick ();
@@ -510,7 +516,7 @@ public partial class Border
             return true;
         }
 
-        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.Mouse.MouseGrabView == this)
+        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && App?.Mouse.MouseGrabView == this)
         {
             if (_dragPosition.HasValue)
             {
@@ -522,7 +528,7 @@ public partial class Border
         if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && _dragPosition.HasValue)
         {
             _dragPosition = null;
-            Application.Mouse.UngrabMouse ();
+            App?.Mouse.UngrabMouse ();
 
             EndArrangeMode ();
 
@@ -651,7 +657,7 @@ public partial class Border
         if (Parent!.SuperView is null)
         {
             // Redraw the entire app window.
-            Application.Current!.SetNeedsDraw ();
+            App?.Current?.SetNeedsDraw ();
         }
         else
         {
@@ -762,7 +768,7 @@ public partial class Border
 
     private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
-        if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue)
+        if (App?.Mouse.MouseGrabView == this && _dragPosition.HasValue)
         {
             e.Cancel = true;
         }
@@ -770,7 +776,7 @@ public partial class Border
 
     private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
-        if (Application.Mouse.MouseGrabView == this && _dragPosition.HasValue)
+        if (App?.Mouse.MouseGrabView == this && _dragPosition.HasValue)
         {
             e.Cancel = true;
         }
@@ -783,8 +789,11 @@ public partial class Border
     /// <inheritdoc/>
     protected override void Dispose (bool disposing)
     {
-        Application.Mouse.GrabbingMouse -= Application_GrabbingMouse;
-        Application.Mouse.UnGrabbingMouse -= Application_UnGrabbingMouse;
+        if (App is { })
+        {
+            App.Mouse.GrabbingMouse -= Application_GrabbingMouse;
+            App.Mouse.UnGrabbingMouse -= Application_UnGrabbingMouse;
+        }
 
         _dragPosition = null;
         base.Dispose (disposing);

+ 6 - 4
Terminal.Gui/ViewBase/Adornment/Border.cs

@@ -48,10 +48,6 @@ public partial class Border : Adornment
         Parent = parent;
         CanFocus = false;
         TabStop = TabBehavior.TabGroup;
-
-        Application.Mouse.GrabbingMouse += Application_GrabbingMouse;
-        Application.Mouse.UnGrabbingMouse += Application_UnGrabbingMouse;
-
         ThicknessChanged += OnThicknessChanged;
     }
 
@@ -112,6 +108,12 @@ public partial class Border : Adornment
     {
         base.BeginInit ();
 
+        if (App is { })
+        {
+            App.Mouse.GrabbingMouse += Application_GrabbingMouse;
+            App.Mouse.UnGrabbingMouse += Application_UnGrabbingMouse;
+        }
+
         if (Parent is null)
         {
             return;

+ 2 - 2
Terminal.Gui/ViewBase/Adornment/ShadowView.cs

@@ -144,9 +144,9 @@ internal class ShadowView : View
     {
         if (SuperView is not Adornment adornment
             || location.X < 0
-            || location.X >= Application.Screen.Width
+            || location.X >= App?.Screen.Width
             || location.Y < 0
-            || location.Y >= Application.Screen.Height)
+            || location.Y >= App?.Screen.Height)
         {
             return Attribute.Default;
         }

+ 7 - 1
Terminal.Gui/ViewBase/View.cs

@@ -118,10 +118,16 @@ public partial class View : IDisposable, ISupportInitializeNotification
     /// </summary>
     public IApplication? App
     {
-        get => _app ?? SuperView?.App;
+        get => GetApp ();
         internal set => _app = value;
     }
 
+    /// <summary>
+    ///     Gets the <see cref="IApplication"/> instance this view is running in. Used internally to allow overrides by <see cref="Adornment"/>.
+    /// </summary>
+    /// <returns></returns>
+    protected virtual IApplication? GetApp () => _app ?? SuperView?.App;
+
     private IDriver? _driver;
 
     /// <summary>

+ 15 - 10
Tests/UnitTests/View/ArrangementTests.cs

@@ -5,7 +5,7 @@ namespace UnitTests.ViewTests;
 public class ArrangementTests (ITestOutputHelper output)
 {
     private readonly ITestOutputHelper _output = output;
-    
+
     [Fact]
     public void MouseGrabHandler_WorksWithMovableView_UsingNewMouseEvent ()
     {
@@ -17,6 +17,7 @@ public class ArrangementTests (ITestOutputHelper output)
             Width = 80,
             Height = 25
         };
+        superView.App = new ApplicationImpl ();
 
         var movableView = new View
         {
@@ -45,7 +46,7 @@ public class ArrangementTests (ITestOutputHelper output)
 
         // The border should have grabbed the mouse
         Assert.True (result);
-        Assert.Equal (movableView.Border, Application.Mouse.MouseGrabView);
+        Assert.Equal (movableView.Border, superView.App.Mouse.MouseGrabView);
 
         // Simulate mouse drag
         var dragEvent = new MouseEventArgs
@@ -58,7 +59,7 @@ public class ArrangementTests (ITestOutputHelper output)
         Assert.True (result);
 
         // Mouse should still be grabbed
-        Assert.Equal (movableView.Border, Application.Mouse.MouseGrabView);
+        Assert.Equal (movableView.Border, superView.App.Mouse.MouseGrabView);
 
         // Simulate mouse release to end dragging
         var releaseEvent = new MouseEventArgs
@@ -71,7 +72,7 @@ public class ArrangementTests (ITestOutputHelper output)
         Assert.True (result);
 
         // Mouse should be released
-        Assert.Null (Application.Mouse.MouseGrabView);
+        Assert.Null (superView.App.Mouse.MouseGrabView);
     }
 
     [Fact]
@@ -81,6 +82,7 @@ public class ArrangementTests (ITestOutputHelper output)
 
         var superView = new View
         {
+            App = new ApplicationImpl(),
             Width = 80,
             Height = 25
         };
@@ -113,7 +115,7 @@ public class ArrangementTests (ITestOutputHelper output)
 
         // The border should have grabbed the mouse for resizing
         Assert.True (result);
-        Assert.Equal (resizableView.Border, Application.Mouse.MouseGrabView);
+        Assert.Equal (resizableView.Border, superView.App.Mouse.MouseGrabView);
 
         // Simulate dragging to resize
         var dragEvent = new MouseEventArgs
@@ -124,7 +126,7 @@ public class ArrangementTests (ITestOutputHelper output)
 
         result = resizableView.Border.NewMouseEvent (dragEvent);
         Assert.True (result);
-        Assert.Equal (resizableView.Border, Application.Mouse.MouseGrabView);
+        Assert.Equal (resizableView.Border, superView.App.Mouse.MouseGrabView);
 
         // Simulate mouse release
         var releaseEvent = new MouseEventArgs
@@ -137,7 +139,7 @@ public class ArrangementTests (ITestOutputHelper output)
         Assert.True (result);
 
         // Mouse should be released
-        Assert.Null (Application.Mouse.MouseGrabView);
+        Assert.Null (superView.App.Mouse.MouseGrabView);
     }
 
     [Fact]
@@ -146,6 +148,7 @@ public class ArrangementTests (ITestOutputHelper output)
         // This test verifies MouseGrabHandler properly releases when switching between views
 
         var superView = new View { Width = 80, Height = 25 };
+        superView.App = new ApplicationImpl ();
 
         var view1 = new View
         {
@@ -168,6 +171,8 @@ public class ArrangementTests (ITestOutputHelper output)
         };
 
         superView.Add (view1, view2);
+        superView.BeginInit ();
+        superView.EndInit ();
 
         // Grab mouse on first view
         var pressEvent1 = new MouseEventArgs
@@ -177,7 +182,7 @@ public class ArrangementTests (ITestOutputHelper output)
         };
 
         view1.Border!.NewMouseEvent (pressEvent1);
-        Assert.Equal (view1.Border, Application.Mouse.MouseGrabView);
+        Assert.Equal (view1.Border, superView.App.Mouse.MouseGrabView);
 
         // Release on first view
         var releaseEvent1 = new MouseEventArgs
@@ -197,7 +202,7 @@ public class ArrangementTests (ITestOutputHelper output)
         };
 
         view2.Border!.NewMouseEvent (pressEvent2);
-        Assert.Equal (view2.Border, Application.Mouse.MouseGrabView);
+        Assert.Equal (view2.Border, superView.App.Mouse.MouseGrabView);
 
         // Release on second view
         var releaseEvent2 = new MouseEventArgs
@@ -207,6 +212,6 @@ public class ArrangementTests (ITestOutputHelper output)
         };
 
         view2.Border.NewMouseEvent (releaseEvent2);
-        Assert.Null (Application.Mouse.MouseGrabView);
+        Assert.Null (superView.App.Mouse.MouseGrabView);
     }
 }