Browse Source

Performing text reverse during formatting instead during drawing.

BDisp 1 year ago
parent
commit
b7293e0470
2 changed files with 212 additions and 30 deletions
  1. 40 30
      Terminal.Gui/Text/TextFormatter.cs
  2. 172 0
      UnitTests/Text/TextFormatterTests.cs

+ 40 - 30
Terminal.Gui/Text/TextFormatter.cs

@@ -269,17 +269,6 @@ public class TextFormatter
 
         List<string> linesFormatted = GetLines ();
 
-        switch (Direction)
-        {
-            case TextDirection.TopBottom_RightLeft:
-            case TextDirection.LeftRight_BottomTop:
-            case TextDirection.RightLeft_BottomTop:
-            case TextDirection.BottomTop_RightLeft:
-                linesFormatted.Reverse ();
-
-                break;
-        }
-
         bool isVertical = IsVerticalDirection (Direction);
         Rectangle maxScreen = screen;
 
@@ -327,15 +316,6 @@ public class TextFormatter
 
             Rune [] runes = linesFormatted [line].ToRunes ();
 
-            runes = Direction switch
-            {
-                TextDirection.RightLeft_BottomTop => runes.Reverse ().ToArray (),
-                TextDirection.RightLeft_TopBottom => runes.Reverse ().ToArray (),
-                TextDirection.BottomTop_LeftRight => runes.Reverse ().ToArray (),
-                TextDirection.BottomTop_RightLeft => runes.Reverse ().ToArray (),
-                _ => runes
-            };
-
             // When text is justified, we lost left or right, so we use the direction to align. 
 
             int x, y;
@@ -467,8 +447,8 @@ public class TextFormatter
                         break;
                     }
 
-                    if ((!isVertical && current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset)
-                        || (isVertical && current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y))
+                    if ((!isVertical && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset || (idx < runes.Length && runes [idx].GetColumns () > screen.Width)))
+                        || (isVertical && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y) || (idx < runes.Length && runes [idx].GetColumns () > screen.Width))))
                     {
                         break;
                     }
@@ -1368,7 +1348,7 @@ public class TextFormatter
     {
         return StringExtensions.ToString (
                                           runes.GetRange (
-                                                          index,
+                                                          Math.Max (index, 0),
                                                           GetLengthThatFits (text, width, tabWidth, textDirection)
                                                          )
                                          );
@@ -1581,16 +1561,17 @@ public class TextFormatter
 
                 foreach (string line in lines)
                 {
-                    lineResult.Add (ClipAndJustify (line, width, justify, textDirection, tabWidth, textFormatter));
+
+                    lineResult.Add (ClipAndJustify (PerformCorrectFormatDirection (textDirection, line), width, justify, textDirection, tabWidth, textFormatter));
                 }
 
-                return lineResult;
+                return PerformCorrectFormatDirection (textDirection, lineResult);
             }
 
             text = ReplaceCRLFWithSpace (text);
-            lineResult.Add (ClipAndJustify (text, width, justify, textDirection, tabWidth, textFormatter));
+            lineResult.Add (ClipAndJustify (PerformCorrectFormatDirection (textDirection, text), width, justify, textDirection, tabWidth, textFormatter));
 
-            return lineResult;
+            return PerformCorrectFormatDirection (textDirection, lineResult);
         }
 
         List<Rune> runes = StripCRLF (text, true).ToRuneList ();
@@ -1605,7 +1586,7 @@ public class TextFormatter
             {
                 List<string> wrappedLines =
                     WordWrapText (
-                                  StringExtensions.ToString (runes.GetRange (lp, i - lp)),
+                                  StringExtensions.ToString (PerformCorrectFormatDirection (textDirection, runes.GetRange (lp, i - lp))),
                                   width,
                                   preserveTrailingSpaces,
                                   tabWidth,
@@ -1628,7 +1609,7 @@ public class TextFormatter
         }
 
         foreach (string line in WordWrapText (
-                                              StringExtensions.ToString (runes.GetRange (lp, runeCount - lp)),
+                                              StringExtensions.ToString (PerformCorrectFormatDirection (textDirection, runes.GetRange (lp, runeCount - lp))),
                                               width,
                                               preserveTrailingSpaces,
                                               tabWidth,
@@ -1639,7 +1620,36 @@ public class TextFormatter
             lineResult.Add (ClipAndJustify (line, width, justify, textDirection, tabWidth));
         }
 
-        return lineResult;
+        return PerformCorrectFormatDirection (textDirection, lineResult);
+    }
+
+    private static string PerformCorrectFormatDirection (TextDirection textDirection, string line)
+    {
+        return textDirection switch
+               {
+                   TextDirection.RightLeft_BottomTop
+                       or TextDirection.RightLeft_TopBottom
+                       or TextDirection.BottomTop_LeftRight
+                       or TextDirection.BottomTop_RightLeft => StringExtensions.ToString (line.EnumerateRunes ().Reverse ()),
+                   _ => line
+               };
+    }
+
+    private static List<Rune> PerformCorrectFormatDirection (TextDirection textDirection, List<Rune> runes)
+    {
+        return PerformCorrectFormatDirection (textDirection, StringExtensions.ToString (runes)).ToRuneList ();
+    }
+
+    private static List<string> PerformCorrectFormatDirection (TextDirection textDirection, List<string> lines)
+    {
+        return textDirection switch
+               {
+                   TextDirection.TopBottom_RightLeft
+                       or TextDirection.LeftRight_BottomTop
+                       or TextDirection.RightLeft_BottomTop
+                       or TextDirection.BottomTop_RightLeft => lines.ToArray ().Reverse ().ToList (),
+                   _ => lines
+               };
     }
 
     /// <summary>Returns the number of lines needed to render the specified text given the width.</summary>

+ 172 - 0
UnitTests/Text/TextFormatterTests.cs

@@ -3872,4 +3872,176 @@ B")]
         };
         Assert.Equal (new (1, expected), tf.Size);
     }
+
+    [SetupFakeDriver]
+    [Theory]
+    [InlineData ("A", 1, 0, false, "")]
+    [InlineData ("A", 0, 1, false, "")]
+    [InlineData ("AB1 2", 2, 1, false, "2")]
+    [InlineData ("AB12", 5, 1, false, "21BA")]
+    [InlineData ("AB\n12", 5, 2, false, "BA\n21")]
+    [InlineData ("ABC 123 456", 7, 2, false, "654 321\nCBA    ")]
+    [InlineData ("こんにちは", 1, 1, false, "")]
+    [InlineData ("こんにちは", 2, 1, false, "は")]
+    [InlineData ("こんにちは", 5, 1, false, "はち")]
+    [InlineData ("こんにちは", 10, 1, false, "はちにんこ")]
+    [InlineData ("こんにちは\nAB\n12", 10, 3, false, "はちにんこ\nBA        \n21        ")]
+
+    [InlineData ("A", 1, 0, true, "")]
+    [InlineData ("A", 0, 1, true, "")]
+    [InlineData ("AB1 2", 2, 1, true, "2")]
+    [InlineData ("AB12", 5, 1, true, "21BA")]
+    [InlineData ("AB\n12", 5, 2, true, "BA\n21")]
+    [InlineData ("ABC 123 456", 7, 2, true, "654 321")]
+    [InlineData ("こんにちは", 1, 1, true, "")]
+    [InlineData ("こんにちは", 2, 1, true, "は")]
+    [InlineData ("こんにちは", 5, 1, true, "はち")]
+    [InlineData ("こんにちは", 10, 1, true, "はちにんこ")]
+    [InlineData ("こんにちは\nAB\n12", 10, 3, true, "はちにんこ\nBA        \n21        ")]
+    public void Draw_Horizontal_RightLeft_TopBottom (string text, int width, int height, bool autoSize, string expectedText)
+    {
+        TextFormatter tf = new ()
+        {
+            Text = text,
+            Direction = TextDirection.RightLeft_TopBottom,
+            AutoSize = autoSize,
+        };
+
+        if (!autoSize)
+        {
+            tf.Size = new Size (width, height);
+        }
+        tf.Draw (new Rectangle (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+    }
+
+    [SetupFakeDriver]
+    [Theory]
+    [InlineData ("A", 1, 0, false, "")]
+    [InlineData ("A", 0, 1, false, "")]
+    [InlineData ("AB1 2", 2, 1, false, "2")]
+    [InlineData ("AB12", 5, 1, false, "21BA")]
+    [InlineData ("AB\n12", 5, 2, false, "21\nBA")]
+    [InlineData ("ABC 123 456", 7, 2, false, "CBA    \n654 321")]
+    [InlineData ("こんにちは", 1, 1, false, "")]
+    [InlineData ("こんにちは", 2, 1, false, "は")]
+    [InlineData ("こんにちは", 5, 1, false, "はち")]
+    [InlineData ("こんにちは", 10, 1, false, "はちにんこ")]
+    [InlineData ("こんにちは\nAB\n12", 10, 3, false, "21        \nBA        \nはちにんこ")]
+
+    [InlineData ("A", 1, 0, true, "")]
+    [InlineData ("A", 0, 1, true, "")]
+    [InlineData ("AB1 2", 2, 1, true, "2")]
+    [InlineData ("AB12", 5, 1, true, "21BA")]
+    [InlineData ("AB\n12", 5, 2, true, "21\nBA")]
+    [InlineData ("ABC 123 456", 7, 2, true, "654 321")]
+    [InlineData ("こんにちは", 1, 1, true, "")]
+    [InlineData ("こんにちは", 2, 1, true, "は")]
+    [InlineData ("こんにちは", 5, 1, true, "はち")]
+    [InlineData ("こんにちは", 10, 1, true, "はちにんこ")]
+    [InlineData ("こんにちは\nAB\n12", 10, 3, true, "21        \nBA        \nはちにんこ")]
+    public void Draw_Horizontal_RightLeft_BottomTop (string text, int width, int height, bool autoSize, string expectedText)
+    {
+        TextFormatter tf = new ()
+        {
+            Text = text,
+            Direction = TextDirection.RightLeft_BottomTop,
+            AutoSize = autoSize,
+        };
+
+        if (!autoSize)
+        {
+            tf.Size = new Size (width, height);
+        }
+        tf.Draw (new Rectangle (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+    }
+
+    [SetupFakeDriver]
+    [Theory]
+    [InlineData ("A", 1, 0, false, "")]
+    [InlineData ("A", 0, 1, false, "")]
+    [InlineData ("AB1 2", 1, 2, false, "2")]
+    [InlineData ("AB12", 1, 5, false, "2\n1\nB\nA")]
+    [InlineData ("AB\n12", 2, 5, false, "B2\nA1")]
+    [InlineData ("ABC 123 456", 2, 7, false, "6C\n5B\n4A\n  \n3 \n2 \n1 ")]
+    [InlineData ("こんにちは", 1, 1, false, "")]
+    [InlineData ("こんにちは", 2, 1, false, "は")]
+    [InlineData ("こんにちは", 2, 5, false, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは", 2, 10, false, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは\nAB\n12", 4, 10, false, "はB2\nちA1\nに  \nん  \nこ  ")]
+
+    [InlineData ("A", 1, 0, true, "")]
+    [InlineData ("A", 0, 1, true, "")]
+    [InlineData ("AB1 2", 1, 2, true, "2")]
+    [InlineData ("AB12", 1, 5, true, "2\n1\nB\nA")]
+    [InlineData ("AB\n12", 2, 5, true, "B2\nA1")]
+    [InlineData ("ABC 123 456", 2, 7, true, "6\n5\n4\n \n3\n2\n1")]
+    [InlineData ("こんにちは", 1, 1, true, "")]
+    [InlineData ("こんにちは", 2, 1, true, "は")]
+    [InlineData ("こんにちは", 2, 5, true, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは", 2, 10, true, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは\nAB\n12", 4, 10, true, "はB2\nちA1\nに  \nん  \nこ  ")]
+    public void Draw_Vertical_BottomTop_LeftRight (string text, int width, int height, bool autoSize, string expectedText)
+    {
+        TextFormatter tf = new ()
+        {
+            Text = text,
+            Direction = TextDirection.BottomTop_LeftRight,
+            AutoSize = autoSize,
+        };
+
+        if (!autoSize)
+        {
+            tf.Size = new Size (width, height);
+        }
+        tf.Draw (new Rectangle (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+    }
+
+    [SetupFakeDriver]
+    [Theory]
+    [InlineData ("A", 1, 0, false, "")]
+    [InlineData ("A", 0, 1, false, "")]
+    [InlineData ("AB1 2", 1, 2, false, "2")]
+    [InlineData ("AB12", 1, 5, false, "2\n1\nB\nA")]
+    [InlineData ("AB\n12", 2, 5, false, "2B\n1A")]
+    [InlineData ("ABC 123 456", 2, 7, false, "C6\nB5\nA4\n  \n 3\n 2\n 1")]
+    [InlineData ("こんにちは", 1, 1, false, "")]
+    [InlineData ("こんにちは", 2, 1, false, "は")]
+    [InlineData ("こんにちは", 2, 5, false, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは", 2, 10, false, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは\nAB\n12", 4, 10, false, "2Bは\n1Aち\n  に\n  ん\n  こ")]
+
+    [InlineData ("A", 1, 0, true, "")]
+    [InlineData ("A", 0, 1, true, "")]
+    [InlineData ("AB1 2", 1, 2, true, "2")]
+    [InlineData ("AB12", 1, 5, true, "2\n1\nB\nA")]
+    [InlineData ("AB\n12", 2, 5, true, "2B\n1A")]
+    [InlineData ("ABC 123 456", 2, 7, true, "6\n5\n4\n \n3\n2\n1")]
+    [InlineData ("こんにちは", 1, 1, true, "")]
+    [InlineData ("こんにちは", 2, 1, true, "は")]
+    [InlineData ("こんにちは", 2, 5, true, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは", 2, 10, true, "は\nち\nに\nん\nこ")]
+    [InlineData ("こんにちは\nAB\n12", 4, 10, true, "2Bは\n1Aち\n  に\n  ん\n  こ")]
+    public void Draw_Vertical_BottomTop_RightLeft (string text, int width, int height, bool autoSize, string expectedText)
+    {
+        TextFormatter tf = new ()
+        {
+            Text = text,
+            Direction = TextDirection.BottomTop_RightLeft,
+            AutoSize = autoSize,
+        };
+
+        if (!autoSize)
+        {
+            tf.Size = new Size (width, height);
+        }
+        tf.Draw (new Rectangle (0, 0, width, height), Attribute.Default, Attribute.Default);
+
+        TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
+    }
 }