浏览代码

Discard additional lines upon inserting when AcceptsReturn=false (#14173)

* Discard additional lines upon inserting when AcceptReturn=false

* Use LineBreakEnumerator for detecting line breaks

* Remove unused member

Co-authored-by: Max Katz <[email protected]>

* Use Grapheme enumerator

---------

Co-authored-by: Markus <[email protected]>
Co-authored-by: Max Katz <[email protected]>
Co-authored-by: Benedikt Stebner <[email protected]>
markl65536 1 年之前
父节点
当前提交
33d95c6af5
共有 2 个文件被更改,包括 65 次插入7 次删除
  1. 27 7
      src/Avalonia.Controls/TextBox.cs
  2. 38 0
      tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

+ 27 - 7
src/Avalonia.Controls/TextBox.cs

@@ -16,6 +16,7 @@ using Avalonia.Utilities;
 using Avalonia.Controls.Metadata;
 using Avalonia.Media.TextFormatting;
 using Avalonia.Automation.Peers;
+using Avalonia.Media.TextFormatting.Unicode;
 using Avalonia.Threading;
 
 namespace Avalonia.Controls
@@ -837,7 +838,7 @@ namespace Avalonia.Controls
 
             _scrollViewer = e.NameScope.Find<ScrollViewer>("PART_ScrollViewer");
 
-            if(_scrollViewer != null)
+            if (_scrollViewer != null)
             {
                 _scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
             }
@@ -886,9 +887,9 @@ namespace Avalonia.Controls
 
         private void PresenterPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
         {
-            if(e.Property == TextPresenter.PreeditTextProperty)
+            if (e.Property == TextPresenter.PreeditTextProperty)
             {
-                if(string.IsNullOrEmpty(e.OldValue as string) && !string.IsNullOrEmpty(e.NewValue as string))
+                if (string.IsNullOrEmpty(e.OldValue as string) && !string.IsNullOrEmpty(e.NewValue as string))
                 {
                     PseudoClasses.Set(":empty", false);
 
@@ -1013,7 +1014,7 @@ namespace Avalonia.Controls
                 return;
             }
 
-            input = RemoveInvalidCharacters(input);
+            input = SanitizeInputText(input);
 
             if (string.IsNullOrEmpty(input))
             {
@@ -1066,11 +1067,30 @@ namespace Avalonia.Controls
             }
         }
 
-        private string? RemoveInvalidCharacters(string? text)
+        private string? SanitizeInputText(string? text)
         {
             if (text is null)
                 return null;
 
+            if (!AcceptsReturn)
+            {
+                var lineBreakStart = 0;
+                var graphemeEnumerator = new GraphemeEnumerator(text.AsSpan());
+
+                while (graphemeEnumerator.MoveNext(out var grapheme))
+                {
+                    if (grapheme.FirstCodepoint.IsBreakChar)
+                    {
+                        break;
+                    }
+
+                    lineBreakStart += grapheme.Length;
+                }
+
+                // All lines except the first one are discarded when TextBox does not accept Return key
+                text = text.Substring(0, lineBreakStart);
+            }
+
             for (var i = 0; i < invalidCharacters.Length; i++)
             {
                 text = text.Replace(invalidCharacters[i], string.Empty);
@@ -1757,7 +1777,7 @@ namespace Avalonia.Controls
                     SetCurrentValue(SelectionEndProperty, caretIndex);
                 }
 
-                if(SelectionStart != SelectionEnd)
+                if (SelectionStart != SelectionEnd)
                 {
                     _presenter.TextSelectionHandleCanvas?.ShowContextMenu();
                 }
@@ -2228,7 +2248,7 @@ namespace Avalonia.Controls
 
         protected override Size MeasureOverride(Size availableSize)
         {
-            if(_scrollViewer != null)
+            if (_scrollViewer != null)
             {
                 var maxHeight = double.PositiveInfinity;
 

+ 38 - 0
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@@ -949,6 +949,44 @@ namespace Avalonia.Controls.UnitTests
             }
         }
 
+        [Fact]
+        public void Insert_Multiline_Text_Should_Accept_Extra_Lines_When_AcceptsReturn_Is_True()
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var target = new TextBox
+                {
+                    AcceptsReturn = true
+                };
+
+                RaiseTextEvent(target, $"123 {Environment.NewLine}456");
+
+                Assert.Equal($"123 {Environment.NewLine}456", target.Text);
+            }
+        }
+
+        [Fact]
+        public void Insert_Multiline_Text_Should_Discard_Extra_Lines_When_AcceptsReturn_Is_False()
+        {
+            using (UnitTestApplication.Start(Services))
+            {
+                var target = new TextBox
+                {
+                    AcceptsReturn = false
+                };
+
+                RaiseTextEvent(target, $"123 {"\r"}456");
+
+                Assert.Equal("123 ", target.Text);
+
+                target.Text = "";
+
+                RaiseTextEvent(target, $"123 {"\r\n"}456");
+
+                Assert.Equal("123 ", target.Text);
+            }
+        }
+
         [Fact]
         public void Should_Fullfill_MaxLines_Contraint()
         {