Browse Source

Fixes #2478. Currently it isn't possible to draw over a modal view.

BDisp 2 years ago
parent
commit
edd53173d1

+ 10 - 1
Terminal.Gui/Core/Application.cs

@@ -277,6 +277,14 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static Action Iteration;
 
+		/// <summary>
+		///   This event is raised on each iteration of the <see cref="MainLoop"/> after all processes are completed.
+		/// </summary>
+		/// <remarks>
+		///   See also <see cref="Timeout"/>
+		/// </remarks>
+		public static Action IterationComplete;
+
 		/// <summary>
 		/// Returns a rectangle that is centered in the screen for the provided size.
 		/// </summary>
@@ -1262,6 +1270,7 @@ namespace Terminal.Gui {
 
 			if (state.Toplevel != Top
 				&& (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+				state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 				Top.Redraw (Top.Bounds);
 				foreach (var top in toplevels.Reverse ()) {
 					if (top != Top && top != state.Toplevel) {
@@ -1269,7 +1278,6 @@ namespace Terminal.Gui {
 						top.Redraw (top.Bounds);
 					}
 				}
-				state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 			}
 			if (toplevels.Count == 1 && state.Toplevel == Top
 				&& (Driver.Cols != state.Toplevel.Frame.Width || Driver.Rows != state.Toplevel.Frame.Height)
@@ -1294,6 +1302,7 @@ namespace Terminal.Gui {
 				&& (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
 				Top.Redraw (Top.Bounds);
 			}
+			IterationComplete?.Invoke ();
 		}
 
 		static void EnsureModalOrVisibleAlwaysOnTop (Toplevel toplevel)

+ 3 - 0
Terminal.Gui/Core/Toplevel.cs

@@ -766,6 +766,9 @@ namespace Terminal.Gui {
 						view.SetNeedsDisplay (view.Bounds);
 					}
 				}
+
+				ClearLayoutNeeded ();
+				ClearNeedsDisplay ();
 			}
 
 			base.Redraw (Bounds);

+ 5 - 2
Terminal.Gui/Core/View.cs

@@ -939,8 +939,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void SetSubViewNeedsDisplay ()
 		{
+			if (ChildNeedsDisplay) {
+				return;
+			}
 			ChildNeedsDisplay = true;
-			if (container != null)
+			if (container != null && !container.ChildNeedsDisplay)
 				container.SetSubViewNeedsDisplay ();
 		}
 
@@ -1485,7 +1488,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Removes the <see cref="SetNeedsDisplay()"/> and the <see cref="ChildNeedsDisplay"/> setting on this view.
+		/// Removes the <see cref="NeedDisplay"/> and the <see cref="ChildNeedsDisplay"/> setting on this view.
 		/// </summary>
 		protected void ClearNeedsDisplay ()
 		{

+ 118 - 1
UnitTests/TopLevels/ToplevelTests.cs

@@ -1407,7 +1407,8 @@ namespace Terminal.Gui.TopLevelTests {
 				});
 
 			var firstIteration = false;
-			Application.RunMainLoopIteration (ref rs, true, ref firstIteration); Assert.Equal (dialog, Application.MouseGrabView);
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			Assert.Equal (dialog, Application.MouseGrabView);
 
 			Assert.Equal (new Rect (25, 7, 30, 10), dialog.Frame);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
@@ -1449,5 +1450,121 @@ namespace Terminal.Gui.TopLevelTests {
 
 			Application.End (rs);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_A_Top_Subview_On_A_Dialog ()
+		{
+			var top = Application.Top;
+			var win = new Window ("Window");
+			top.Add (win);
+			Application.Begin (top);
+			((FakeDriver)Application.Driver).SetBufferSize (20, 20);
+
+			Assert.Equal (new Rect (0, 0, 20, 20), win.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌ Window ──────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			var btnPopup = new Button ("Popup");
+			btnPopup.Clicked += (s, e) => {
+				var viewToScreen = btnPopup.ViewToScreen (top.Frame);
+				var view = new View () { X = 1, Y = viewToScreen.Y + 1, Width = 18, Height = 5 };
+				Application.IterationComplete += Application_IterationComplete;
+				top.Add (view);
+
+				void Application_IterationComplete ()
+				{
+					Assert.Equal (new Rect (1, 14, 18, 5), view.Frame);
+
+					top.DrawFrame (view.Frame);
+					top.Move (2, 15);
+					View.Driver.AddStr ("One");
+					top.Move (2, 16);
+					View.Driver.AddStr ("Two");
+					top.Move (2, 17);
+					View.Driver.AddStr ("Three");
+
+					Application.IterationComplete -= Application_IterationComplete;
+				}
+			};
+			var dialog = new Dialog ("Dialog", 15, 10, btnPopup);
+			var rs = Application.Begin (dialog);
+
+			Assert.Equal (new Rect (2, 5, 15, 10), dialog.Frame);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌ Window ──────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│ ┌ Dialog ─────┐  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │  [ Popup ]  │  │
+│ └─────────────┘  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘", output);
+
+			ReflectionTools.InvokePrivate (
+				typeof (Application),
+				"ProcessMouseEvent",
+				new MouseEvent () {
+					X = 9,
+					Y = 13,
+					Flags = MouseFlags.Button1Clicked
+				});
+
+			var firstIteration = false;
+			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+┌ Window ──────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│ ┌ Dialog ─────┐  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │             │  │
+│ │  [ Popup ]  │  │
+│┌────────────────┐│
+││One             ││
+││Two             ││
+││Three           ││
+│└────────────────┘│
+└──────────────────┘", output);
+
+			Application.End (rs);
+		}
 	}
 }