Browse Source

Merge branch 'draw-clip-bounds-fix' into wide-runes-render-issues

BDisp 3 years ago
parent
commit
cd5c13f388

+ 3 - 6
Terminal.Gui/Core/TextFormatter.cs

@@ -1145,20 +1145,17 @@ namespace Terminal.Gui {
 				var start = isVertical ? bounds.Top : bounds.Left;
 				var size = isVertical ? bounds.Height : bounds.Width;
 				var current = start;
-				var startX = start < 0
-					? start
-					: isVertical ? start - y : start - x;
 				var savedClip = Application.Driver?.Clip;
 				if (Application.Driver != null && containerBounds != default) {
 					Application.Driver.Clip = containerBounds == default
 						? bounds
 						: new Rect (Math.Max (containerBounds.X, bounds.X),
 						Math.Max (containerBounds.Y, bounds.Y),
-						Math.Min (containerBounds.Width, containerBounds.Right - bounds.Left),
-						Math.Min (containerBounds.Height, containerBounds.Bottom - bounds.Top));
+						Math.Max (Math.Min (containerBounds.Width, containerBounds.Right - bounds.Left), 0),
+						Math.Max (Math.Min (containerBounds.Height, containerBounds.Bottom - bounds.Top), 0));
 				}
 
-				for (var idx = startX; current < start + size; idx++) {
+				for (var idx = (isVertical ? start - y : start - x); current < start + size; idx++) {
 					if (idx < 0) {
 						current++;
 						continue;

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

@@ -1417,15 +1417,20 @@ namespace Terminal.Gui {
 				Border.DrawContent (this);
 			}
 
-			if (!ustring.IsNullOrEmpty (Text) || (this is Label && !AutoSize)) {
+			if (!ustring.IsNullOrEmpty (TextFormatter.Text) || (this is Label && !AutoSize)) {
 				Clear ();
 				// Draw any Text
 				if (TextFormatter != null) {
 					TextFormatter.NeedsFormat = true;
 				}
+				var containerBounds = SuperView == null ? default : SuperView.ViewToScreen (SuperView.Bounds);
+				containerBounds.X = Math.Max (containerBounds.X, Driver.Clip.X);
+				containerBounds.Y = Math.Max (containerBounds.Y, Driver.Clip.Y);
+				containerBounds.Width = Math.Min (containerBounds.Width, Driver.Clip.Width);
+				containerBounds.Height = Math.Min (containerBounds.Height, Driver.Clip.Height);
 				TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : GetNormalColor (),
 					HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled,
-					SuperView == null ? default : SuperView.ViewToScreen (SuperView.Bounds));
+					containerBounds);
 			}
 
 			// Invoke DrawContentEvent

+ 0 - 20
Terminal.Gui/Views/Button.cs

@@ -221,26 +221,6 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
-		/// <inheritdoc/>
-		public override void Redraw (Rect bounds)
-		{
-			if (ColorScheme != null) {
-				Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
-			}
-
-			if (Border != null) {
-				Border.DrawContent (this);
-			}
-
-			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
-				Clear ();
-				TextFormatter.NeedsFormat = true;
-				TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : GetNormalColor (),
-					HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled,
-					SuperView == null ? default : SuperView.ViewToScreen (SuperView.Bounds));
-			}
-		}
-
 		///<inheritdoc/>
 		public override bool ProcessHotKey (KeyEvent kb)
 		{

+ 53 - 51
UnitTests/ScenarioTests.cs

@@ -26,15 +26,15 @@ namespace Terminal.Gui {
 		int CreateInput (string input)
 		{
 			// Put a control-q in at the end
-			Console.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
+			FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true));
 			foreach (var c in input.Reverse ()) {
 				if (char.IsLetter (c)) {
-					Console.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
+					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false));
 				} else {
-					Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
+					FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false));
 				}
 			}
-			return Console.MockKeyPresses.Count;
+			return FakeConsole.MockKeyPresses.Count;
 		}
 
 		/// <summary>
@@ -48,59 +48,61 @@ namespace Terminal.Gui {
 			List<Type> scenarioClasses = Scenario.GetDerivedClasses<Scenario> ();
 			Assert.NotEmpty (scenarioClasses);
 
-			foreach (var scenarioClass in scenarioClasses) {
+			lock (FakeConsole.MockKeyPresses) {
+				foreach (var scenarioClass in scenarioClasses) {
+
+					// Setup some fake keypresses 
+					// Passing empty string will cause just a ctrl-q to be fired
+					FakeConsole.MockKeyPresses.Clear ();
+					int stackSize = CreateInput ("");
+
+					Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+					int iterations = 0;
+					Application.Iteration = () => {
+						iterations++;
+						// Stop if we run out of control...
+						if (iterations > 10) {
+							Application.RequestStop ();
+						}
+					};
+
+					int ms;
+					if (scenarioClass.Name == "CharacterMap") {
+						ms = 2000;
+					} else {
+						ms = 1000;
+					}
+					var abortCount = 0;
+					Func<MainLoop, bool> abortCallback = (MainLoop loop) => {
+						abortCount++;
+						Application.RequestStop ();
+						return false;
+					};
+					var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
 
-				// Setup some fake keypresses 
-				// Passing empty string will cause just a ctrl-q to be fired
-				Console.MockKeyPresses.Clear ();
-				int stackSize = CreateInput ("");
+					var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
+					scenario.Init (Application.Top, Colors.Base);
+					scenario.Setup ();
+					// There is no need to call Application.Begin because Init already creates the Application.Top
+					// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.
+					//var rs = Application.Begin (Application.Top);
+					scenario.Run ();
 
-				Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+					//Application.End (rs);
 
-				int iterations = 0;
-				Application.Iteration = () => {
-					iterations++;
-					// Stop if we run out of control...
-					if (iterations > 10) {
-						Application.RequestStop ();
+					// Shutdown must be called to safely clean up Application if Init has been called
+					Application.Shutdown ();
+
+					if (abortCount != 0) {
+						output.WriteLine ($"Scenario {scenarioClass} had abort count of {abortCount}");
 					}
-				};
 
-				int ms;
-				if (scenarioClass.Name == "CharacterMap") {
-					ms = 1500;
-				}else {
-					ms = 1000;
+					Assert.Equal (0, abortCount);
+					// # of key up events should match # of iterations
+					Assert.Equal (1, iterations);
+					Assert.Equal (stackSize, iterations);
 				}
-				var abortCount = 0;
-				Func<MainLoop, bool> abortCallback = (MainLoop loop) => {
-					abortCount++;
-					Application.RequestStop ();
-					return false;
-				};
-				var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
-
-				var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
-				scenario.Init (Application.Top, Colors.Base);
-				scenario.Setup ();
-				// There is no need to call Application.Begin because Init already creates the Application.Top
-				// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.
-				//var rs = Application.Begin (Application.Top);
-				scenario.Run ();
-
-				//Application.End (rs);
-
-				// Shutdown must be called to safely clean up Application if Init has been called
-				Application.Shutdown ();
-				
-				if(abortCount != 0) {
-					output.WriteLine ($"Scenario {scenarioClass} had abort count of {abortCount}");
-				}
-
-				Assert.Equal (0, abortCount);
-				// # of key up events should match # of iterations
-				Assert.Equal (1, iterations);
-				Assert.Equal (stackSize, iterations);
 			}
 #if DEBUG_IDISPOSABLE
 			foreach (var inst in Responder.Instances) {

+ 266 - 4
UnitTests/ViewTests.cs

@@ -1985,14 +1985,276 @@ Y
 			view.Frame = new Rect (0, 0, 8, 4);
 			((FakeDriver)Application.Driver).SetBufferSize (7, 3);
 
+		}
+
+		[Fact, AutoInitShutdown]
+		public void DrawTextFormatter_Respects_The_Clip_Bounds ()
+		{
+			var view = new View (new Rect (0, 0, 20, 20));
+			view.Add (new Label ("0123456789abcdefghij"));
+			view.Add (new Label (0, 1, "1\n2\n3\n4\n5\n6\n7\n8\n9\n0"));
+			view.Add (new Button (1, 1, "Press me!"));
+			var scrollView = new ScrollView (new Rect (1, 1, 15, 10)) {
+				ContentSize = new Size (40, 40),
+				ShowHorizontalScrollIndicator = true,
+				ShowVerticalScrollIndicator = true
+			};
+			scrollView.Add (view);
+			var win = new Window (new Rect (1, 1, 20, 14), "Test");
+			win.Add (scrollView);
+			Application.Top.Add (win);
+			Application.Begin (Application.Top);
+
+			var expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 0123456789abcd▲  │
+ │ 1[ Press me! ]┬  │
+ │ 2             │  │
+ │ 3             ┴  │
+ │ 4             ░  │
+ │ 5             ░  │
+ │ 6             ░  │
+ │ 7             ░  │
+ │ 8             ▼  │
+ │ ◄├───┤░░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
 			expected = @"
-┌──────
-│
-│
+ ┌ Test ────────────┐
+ │                  │
+ │ 123456789abcde▲  │
+ │ [ Press me! ] ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄├───┤░░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 23456789abcdef▲  │
+ │  Press me! ]  ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄├────┤░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 3456789abcdefg▲  │
+ │ Press me! ]   ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄├────┤░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 456789abcdefgh▲  │
+ │ ress me! ]    ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄░├───┤░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 56789abcdefghi▲  │
+ │ ess me! ]     ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄░├────┤░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 6789abcdefghij▲  │
+ │ ss me! ]      ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄░├────┤░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 789abcdefghij ▲  │
+ │ s me! ]       ┬  │
+ │               │  │
+ │               ┴  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ░  │
+ │               ▼  │
+ │ ◄░░├───┤░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CtrlMask | Key.Home, new KeyModifiers ())));
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 1[ Press me! ]▲  │
+ │ 2             ┬  │
+ │ 3             │  │
+ │ 4             ┴  │
+ │ 5             ░  │
+ │ 6             ░  │
+ │ 7             ░  │
+ │ 8             ░  │
+ │ 9             ▼  │
+ │ ◄├───┤░░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 2             ▲  │
+ │ 3             ┬  │
+ │ 4             │  │
+ │ 5             ┴  │
+ │ 6             ░  │
+ │ 7             ░  │
+ │ 8             ░  │
+ │ 9             ░  │
+ │ 0             ▼  │
+ │ ◄├───┤░░░░░░░►   │
+ │                  │
+ └──────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
+
+			Assert.True (scrollView.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ())));
+			Application.Top.Redraw (Application.Top.Bounds);
+
+			expected = @"
+ ┌ Test ────────────┐
+ │                  │
+ │ 3             ▲  │
+ │ 4             ┬  │
+ │ 5             │  │
+ │ 6             ┴  │
+ │ 7             ░  │
+ │ 8             ░  │
+ │ 9             ░  │
+ │ 0             ░  │
+ │               ▼  │
+ │ ◄├───┤░░░░░░░►   │
+ │                  │
+ └──────────────────┘
 ";
 
 			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
-			Assert.Equal (new Rect (0, 0, 7, 3), pos);
+			Assert.Equal (new Rect (1, 1, 21, 14), pos);
 		}
 	}
 }