2
0
Эх сурвалжийг харах

Fix negative bounds on draw text and thus fixes scrolling.

BDisp 2 жил өмнө
parent
commit
2eb05113af

+ 20 - 9
Terminal.Gui/Core/TextFormatter.cs

@@ -1176,22 +1176,29 @@ namespace Terminal.Gui {
 			}
 
 			var isVertical = IsVerticalDirection (textDirection);
-			var savedClip = Application.Driver?.Clip;
 			var maxBounds = bounds;
 			if (Application.Driver != null) {
-				Application.Driver.Clip = maxBounds = containerBounds == default
+				maxBounds = containerBounds == default
 					? bounds
 					: new Rect (Math.Max (containerBounds.X, bounds.X),
 					Math.Max (containerBounds.Y, bounds.Y),
 					Math.Max (Math.Min (containerBounds.Width, containerBounds.Right - bounds.Left), 0),
 					Math.Max (Math.Min (containerBounds.Height, containerBounds.Bottom - bounds.Top), 0));
 			}
+			if (maxBounds.Width == 0 || maxBounds.Height == 0) {
+				return;
+			}
+			var savedClip = Application.Driver?.Clip;
+			if (Application.Driver != null) {
+				Application.Driver.Clip = maxBounds;
+			}
+			var lineOffset = !isVertical && bounds.Y < 0 ? Math.Abs (bounds.Y) : 0;
 
-			for (int line = 0; line < linesFormated.Count; line++) {
+			for (int line = lineOffset; line < linesFormated.Count; line++) {
 				if ((isVertical && line > bounds.Width) || (!isVertical && line > bounds.Height))
 					continue;
 				if ((isVertical && line >= maxBounds.Left + maxBounds.Width)
-					|| (!isVertical && line >= maxBounds.Top + maxBounds.Height))
+					|| (!isVertical && line >= maxBounds.Top + maxBounds.Height + lineOffset))
 
 					break;
 
@@ -1267,18 +1274,21 @@ namespace Terminal.Gui {
 					throw new ArgumentOutOfRangeException ();
 				}
 
+				var colOffset = bounds.X < 0 ? Math.Abs (bounds.X) : 0;
 				var start = isVertical ? bounds.Top : bounds.Left;
 				var size = isVertical ? bounds.Height : bounds.Width;
-				var current = start;
+				var current = start + colOffset;
 
-				for (var idx = (isVertical ? start - y : start - x); current < start + size; idx++) {
-					if (!fillRemaining && idx < 0) {
+				for (var idx = (isVertical ? start - y : start - x) + colOffset; current < start + size; idx++) {
+					if (idx < 0 || x + current + colOffset < 0) {
 						current++;
 						continue;
 					} else if (!fillRemaining && idx > runes.Length - 1) {
 						break;
 					}
-					if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X) || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
+					if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
+						|| (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
+
 						break;
 
 					var rune = (Rune)' ';
@@ -1316,8 +1326,9 @@ namespace Terminal.Gui {
 					}
 				}
 			}
-			if (Application.Driver != null)
+			if (Application.Driver != null) {
 				Application.Driver.Clip = (Rect)savedClip;
+			}
 		}
 	}
 }

+ 10 - 8
Terminal.Gui/Core/View.cs

@@ -1511,15 +1511,17 @@ namespace Terminal.Gui {
 
 			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
 				Rect containerBounds = GetContainerBounds ();
-				Clear (GetNeedDisplay (containerBounds));
-				SetChildNeedsDisplay ();
-				// Draw any Text
-				if (TextFormatter != null) {
-					TextFormatter.NeedsFormat = true;
+				if (!containerBounds.IsEmpty) {
+					Clear (GetNeedDisplay (containerBounds));
+					SetChildNeedsDisplay ();
+					// Draw any Text
+					if (TextFormatter != null) {
+						TextFormatter.NeedsFormat = true;
+					}
+					TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+					    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+					    containerBounds);
 				}
-				TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
-				    HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
-				    containerBounds);
 			}
 
 			// Invoke DrawContentEvent

+ 14 - 3
UnitTests/Core/LayoutTests.cs

@@ -328,14 +328,17 @@ namespace Terminal.Gui.CoreTests {
 			Application.Begin (Application.Top);
 
 			Assert.True (label.AutoSize);
+			// Here the AutoSize ensuring the right size with width 28 (Dim.Fill)
+			// and height 0 because wasn't set and the text is empty
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=0}", label.Bounds.ToString ());
 
 			label.Text = "First line\nSecond line";
 			Application.Refresh ();
 
-			// Here the AutoSize ensuring the right size
+			// Here the AutoSize ensuring the right size with width 28 (Dim.Fill)
+			// and height 2 because wasn't set and the text has 2 lines
 			Assert.True (label.AutoSize);
-			Assert.Equal ("{X=0,Y=0,Width=11,Height=2}", label.Bounds.ToString ());
+			Assert.Equal ("{X=0,Y=0,Width=28,Height=2}", label.Bounds.ToString ());
 
 			label.AutoSize = false;
 			Application.Refresh ();
@@ -347,12 +350,16 @@ namespace Terminal.Gui.CoreTests {
 			label.Text = "First changed line\nSecond changed line\nNew line";
 			Application.Refresh ();
 
+			// Here the AutoSize is false and the width 28 (Dim.Fill) and
+			// height 1 because wasn't set and SetMinWidthHeight ensuring the minimum height
 			Assert.False (label.AutoSize);
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=1}", label.Bounds.ToString ());
 
 			label.AutoSize = true;
 			Application.Refresh ();
 
+			// Here the AutoSize ensuring the right size with width 28 (Dim.Fill)
+			// and height 3 because wasn't set and the text has 3 lines
 			Assert.True (label.AutoSize);
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=3}", label.Bounds.ToString ());
 		}
@@ -461,9 +468,13 @@ Y
 			Assert.Equal ("123 ", GetContents ());
 
 			lbl.Text = "12";
+			// Here the AutoSize ensuring the right size with width 3 (Dim.Absolute)
+			// that was set on the OnAdded method with the text length of 3
+			// and height 1 because wasn't set and the text has 1 line
 			Assert.Equal (new Rect (0, 0, 3, 1), lbl.Frame);
 			Assert.Equal (new Rect (0, 0, 3, 1), lbl.NeedDisplay);
-			Assert.Equal (new Rect (0, 0, 80, 25), lbl.SuperView.NeedDisplay);
+			Assert.Equal (new Rect (0, 0, 0, 0), lbl.SuperView.NeedDisplay);
+			Assert.True (lbl.SuperView.LayoutNeeded);
 			lbl.SuperView.Redraw (lbl.SuperView.Bounds);
 			Assert.Equal ("12  ", GetContents ());
 

+ 184 - 6
UnitTests/Text/TextFormatterTests.cs

@@ -2162,7 +2162,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 8;
 			var wrappedLines = TextFormatter.WordWrap (text, width, true);
 			var breakLines = "";
-			foreach (var line in wrappedLines) 				breakLines += $"{line}{Environment.NewLine}";
+			foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}";
 			var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () };
 			var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
 
@@ -2200,7 +2200,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 3;
 			var wrappedLines = TextFormatter.WordWrap (text, height, true);
 			var breakLines = "";
-			for (int i = 0; i < wrappedLines.Count; i++) 				breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
+			for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
 			var label = new Label (breakLines) {
 				TextDirection = TextDirection.TopBottom_LeftRight,
 				Width = Dim.Fill (),
@@ -2237,7 +2237,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 8;
 			var wrappedLines = TextFormatter.WordWrap (text, width, true);
 			var breakLines = "";
-			foreach (var line in wrappedLines) 				breakLines += $"{line}{Environment.NewLine}";
+			foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}";
 			var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () };
 			var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
 
@@ -2276,7 +2276,7 @@ namespace Terminal.Gui.TextTests {
 			var height = 4;
 			var wrappedLines = TextFormatter.WordWrap (text, width, true);
 			var breakLines = "";
-			for (int i = 0; i < wrappedLines.Count; i++) 				breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
+			for (int i = 0; i < wrappedLines.Count; i++) breakLines += $"{wrappedLines [i]}{(i < wrappedLines.Count - 1 ? Environment.NewLine : string.Empty)}";
 			var label = new Label (breakLines) {
 				TextDirection = TextDirection.TopBottom_LeftRight,
 				Width = Dim.Fill (),
@@ -2888,7 +2888,7 @@ namespace Terminal.Gui.TextTests {
 			Assert.Equal ("nd", list1 [10].ToString ());
 			Assert.Equal ("Line", list1 [11].ToString ());
 			Assert.Equal ("- 2.", list1 [^1].ToString ());
-			foreach (var txt in list1) 				wrappedText1 += txt;
+			foreach (var txt in list1) wrappedText1 += txt;
 			Assert.Equal (" Asentencehaswords.  This isthesecondLine- 2.", wrappedText1);
 
 			// With preserveTrailingSpaces = true.
@@ -2910,7 +2910,7 @@ namespace Terminal.Gui.TextTests {
 			Assert.Equal ("Line", list2 [13].ToString ());
 			Assert.Equal (" - ", list2 [14].ToString ());
 			Assert.Equal ("2. ", list2 [^1].ToString ());
-			foreach (var txt in list2) 				wrappedText2 += txt;
+			foreach (var txt in list2) wrappedText2 += txt;
 			Assert.Equal (" A sentence has words.  This is the second Line - 2. ", wrappedText2);
 		}
 
@@ -4288,5 +4288,183 @@ t     ", output);
 0
 0", new Attribute [] { Colors.Base.Normal });
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Negative_Bounds_Horizontal_Without_New_Lines ()
+		{
+			var subView = new View () { Id = "subView", Y = 1, Width = 7, Text = "subView" };
+			var view = new View () { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" };
+			view.Add (subView);
+			var content = new View () { Id = "content", Width = 20, Height = 20 };
+			content.Add (view);
+			var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 };
+			container.Add (content);
+			var top = Application.Top;
+			top.Add (container);
+			Application.Driver.Clip = container.Frame;
+			Application.Begin (top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 01234
+ subVi", output);
+
+			content.X = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 12345
+ ubVie", output);
+
+			content.Y = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ ubVie", output);
+
+			content.Y = -2;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			content.X = -20;
+			content.Y = 0;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Negative_Bounds_Horizontal_With_New_Lines ()
+		{
+			var subView = new View () { Id = "subView", X = 1, Width = 1, Height = 7, Text = "s\nu\nb\nV\ni\ne\nw" };
+			var view = new View () { Id = "view", Width = 2, Height = 20, Text = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" };
+			view.Add (subView);
+			var content = new View () { Id = "content", Width = 20, Height = 20 };
+			content.Add (view);
+			var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 };
+			container.Add (content);
+			var top = Application.Top;
+			top.Add (container);
+			Application.Driver.Clip = container.Frame;
+			Application.Begin (top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 0s
+ 1u
+ 2b
+ 3V
+ 4i", output);
+
+			content.X = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ s
+ u
+ b
+ V
+ i", output);
+
+			content.X = -2;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
+
+			content.X = 0;
+			content.Y = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 1u
+ 2b
+ 3V
+ 4i
+ 5e", output);
+
+			content.Y = -6;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 6w
+ 7 
+ 8 
+ 9 
+ 0 ", output);
+
+			content.Y = -19;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 9", output);
+
+			content.Y = -20;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			content.X = -2;
+			content.Y = 0;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void Draw_Negative_Bounds_Vertical ()
+		{
+			var subView = new View () { Id = "subView", X = 1, Width = 1, Height = 7, Text = "subView", TextDirection = TextDirection.TopBottom_LeftRight };
+			var view = new View () { Id = "view", Width = 2, Height = 20, Text = "01234567890123456789", TextDirection = TextDirection.TopBottom_LeftRight };
+			view.Add (subView);
+			var content = new View () { Id = "content", Width = 20, Height = 20 };
+			content.Add (view);
+			var container = new View () { Id = "container", X = 1, Y = 1, Width = 5, Height = 5 };
+			container.Add (content);
+			var top = Application.Top;
+			top.Add (container);
+			Application.Driver.Clip = container.Frame;
+			Application.Begin (top);
+
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 0s
+ 1u
+ 2b
+ 3V
+ 4i", output);
+
+			content.X = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ s
+ u
+ b
+ V
+ i", output);
+
+			content.X = -2;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);
+
+			content.X = 0;
+			content.Y = -1;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 1u
+ 2b
+ 3V
+ 4i
+ 5e", output);
+
+			content.Y = -6;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 6w
+ 7 
+ 8 
+ 9 
+ 0 ", output);
+
+			content.Y = -19;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre (@"
+ 9", output);
+
+			content.Y = -20;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+
+			content.X = -2;
+			content.Y = 0;
+			Application.Refresh ();
+			TestHelpers.AssertDriverContentsWithFrameAre ("", output);
+		}
 	}
 }