From ac455785755a54811966dc565f3de95878cf7a12 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Jul 2021 11:37:28 +0100 Subject: [PATCH 1/7] initial attempt at unit test --- .../TextBoxTests.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 64c0020f92..0031d005e7 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -556,6 +556,37 @@ namespace Avalonia.Controls.UnitTests Assert.True(true); } } + + [Fact] + public void Textbox_Cannot_Focus_When_not_Visible() + { + using (UnitTestApplication.Start(FocusServices)) + { + var target1 = new TextBox + { + Template = CreateTemplate(), + Text = "1234", + IsVisible = true + }; + + target1.ApplyTemplate(); + + var root = new TestRoot { Child = target1 }; + + var gfcount = 0; + var lfcount = 0; + + target1.GotFocus += (s, e) => gfcount++; + + target1.Focus(); + Assert.True(target1.IsFocused); + + RaiseKeyEvent(target1, Key.Up, KeyModifiers.None); + + Assert.Equal(1, gfcount); + Assert.Equal(1, lfcount); + } + } [Fact] public void TextBox_GotFocus_And_LostFocus_Work_Properly() From 73aaefcc32d9c9853ffaf5daf88606e09e81bdb6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Jul 2021 12:29:59 +0100 Subject: [PATCH 2/7] add a failing unit test to demonstrate the issue. --- .../TextBoxTests.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 0031d005e7..dec9daa73e 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; @@ -558,7 +559,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Textbox_Cannot_Focus_When_not_Visible() + public void Textbox_doesnt_crash_when_Receives_input_and_hidden() { using (UnitTestApplication.Start(FocusServices)) { @@ -566,25 +567,17 @@ namespace Avalonia.Controls.UnitTests { Template = CreateTemplate(), Text = "1234", - IsVisible = true + IsVisible = false }; - target1.ApplyTemplate(); - var root = new TestRoot { Child = target1 }; - var gfcount = 0; - var lfcount = 0; - - target1.GotFocus += (s, e) => gfcount++; + root.Measure(new Size(1000, 1000)); target1.Focus(); Assert.True(target1.IsFocused); RaiseKeyEvent(target1, Key.Up, KeyModifiers.None); - - Assert.Equal(1, gfcount); - Assert.Equal(1, lfcount); } } @@ -794,6 +787,9 @@ namespace Avalonia.Controls.UnitTests keyboardDevice: () => new KeyboardDevice(), keyboardNavigation: new KeyboardNavigationHandler(), inputManager: new InputManager(), + renderInterface: new MockPlatformRenderInterface(), + fontManagerImpl: new MockFontManagerImpl(), + textShaperImpl: new MockTextShaperImpl(), standardCursorFactory: Mock.Of()); private static TestServices Services => TestServices.MockThreadingInterface.With( From a295cac93050cb3020225702472a8243d892005a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Jul 2021 12:33:13 +0100 Subject: [PATCH 3/7] test more keys. --- tests/Avalonia.Controls.UnitTests/TextBoxTests.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index dec9daa73e..3e71c38335 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -558,8 +558,12 @@ namespace Avalonia.Controls.UnitTests } } - [Fact] - public void Textbox_doesnt_crash_when_Receives_input_and_hidden() + [Theory] + [InlineData(Key.Up)] + [InlineData(Key.Down)] + [InlineData(Key.Home)] + [InlineData(Key.End)] + public void Textbox_doesnt_crash_when_Receives_input_and_hidden(Key key) { using (UnitTestApplication.Start(FocusServices)) { @@ -577,7 +581,7 @@ namespace Avalonia.Controls.UnitTests target1.Focus(); Assert.True(target1.IsFocused); - RaiseKeyEvent(target1, Key.Up, KeyModifiers.None); + RaiseKeyEvent(target1, key, KeyModifiers.None); } } From d0133ebb8cf485e54a43e0cc7aeebeb34b9db8b3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Jul 2021 12:35:09 +0100 Subject: [PATCH 4/7] check we have a presenter before allowing navigation inside textbox. --- src/Avalonia.Controls/TextBox.cs | 45 ++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index c1516613b3..40c075c7af 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1098,29 +1098,35 @@ namespace Avalonia.Controls private bool MoveVertical(int count) { - var formattedText = _presenter.FormattedText; - var lines = formattedText.GetLines().ToList(); - var caretIndex = CaretIndex; - var lineIndex = GetLine(caretIndex, lines) + count; - - if (lineIndex >= 0 && lineIndex < lines.Count) - { - var line = lines[lineIndex]; - var rect = formattedText.HitTestTextPosition(caretIndex); - var y = count < 0 ? rect.Y : rect.Bottom; - var point = new Point(rect.X, y + (count * (line.Height / 2))); - var hit = formattedText.HitTestPoint(point); - CaretIndex = hit.TextPosition + (hit.IsTrailing ? 1 : 0); - return true; - } - else + if (_presenter != null) { - return false; + var formattedText = _presenter.FormattedText; + var lines = formattedText.GetLines().ToList(); + var caretIndex = CaretIndex; + var lineIndex = GetLine(caretIndex, lines) + count; + + if (lineIndex >= 0 && lineIndex < lines.Count) + { + var line = lines[lineIndex]; + var rect = formattedText.HitTestTextPosition(caretIndex); + var y = count < 0 ? rect.Y : rect.Bottom; + var point = new Point(rect.X, y + (count * (line.Height / 2))); + var hit = formattedText.HitTestPoint(point); + CaretIndex = hit.TextPosition + (hit.IsTrailing ? 1 : 0); + return true; + } } + + return false; } private void MoveHome(bool document) { + if (_presenter == null) + { + return; + } + var text = Text ?? string.Empty; var caretIndex = CaretIndex; @@ -1151,6 +1157,11 @@ namespace Avalonia.Controls private void MoveEnd(bool document) { + if (_presenter == null) + { + return; + } + var text = Text ?? string.Empty; var caretIndex = CaretIndex; From b250fce59c1c51724c57046c3ed51d059adabf6b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Jul 2021 12:56:16 +0100 Subject: [PATCH 5/7] rename test to describe the actual issue. --- tests/Avalonia.Controls.UnitTests/TextBoxTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 3e71c38335..cb20071860 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -563,7 +563,7 @@ namespace Avalonia.Controls.UnitTests [InlineData(Key.Down)] [InlineData(Key.Home)] [InlineData(Key.End)] - public void Textbox_doesnt_crash_when_Receives_input_and_hidden(Key key) + public void Textbox_doesnt_crash_when_Receives_input_and_template_not_applied(Key key) { using (UnitTestApplication.Start(FocusServices)) { @@ -576,8 +576,6 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot { Child = target1 }; - root.Measure(new Size(1000, 1000)); - target1.Focus(); Assert.True(target1.IsFocused); From dc3b323dc1b0852f0d071b9fd284b5e5563e35fe Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 8 Jul 2021 12:56:41 +0100 Subject: [PATCH 6/7] refactor a little. --- src/Avalonia.Controls/TextBox.cs | 36 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 40c075c7af..3aef2abac5 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1098,23 +1098,25 @@ namespace Avalonia.Controls private bool MoveVertical(int count) { - if (_presenter != null) + if (_presenter is null) { - var formattedText = _presenter.FormattedText; - var lines = formattedText.GetLines().ToList(); - var caretIndex = CaretIndex; - var lineIndex = GetLine(caretIndex, lines) + count; + return false; + } - if (lineIndex >= 0 && lineIndex < lines.Count) - { - var line = lines[lineIndex]; - var rect = formattedText.HitTestTextPosition(caretIndex); - var y = count < 0 ? rect.Y : rect.Bottom; - var point = new Point(rect.X, y + (count * (line.Height / 2))); - var hit = formattedText.HitTestPoint(point); - CaretIndex = hit.TextPosition + (hit.IsTrailing ? 1 : 0); - return true; - } + var formattedText = _presenter.FormattedText; + var lines = formattedText.GetLines().ToList(); + var caretIndex = CaretIndex; + var lineIndex = GetLine(caretIndex, lines) + count; + + if (lineIndex >= 0 && lineIndex < lines.Count) + { + var line = lines[lineIndex]; + var rect = formattedText.HitTestTextPosition(caretIndex); + var y = count < 0 ? rect.Y : rect.Bottom; + var point = new Point(rect.X, y + (count * (line.Height / 2))); + var hit = formattedText.HitTestPoint(point); + CaretIndex = hit.TextPosition + (hit.IsTrailing ? 1 : 0); + return true; } return false; @@ -1122,7 +1124,7 @@ namespace Avalonia.Controls private void MoveHome(bool document) { - if (_presenter == null) + if (_presenter is null) { return; } @@ -1157,7 +1159,7 @@ namespace Avalonia.Controls private void MoveEnd(bool document) { - if (_presenter == null) + if (_presenter is null) { return; } From ea269261a7a7bc1bf725ba2e09cabf2fc6903509 Mon Sep 17 00:00:00 2001 From: dvdsvdv vrfsv Date: Thu, 15 Jul 2021 13:15:42 +0300 Subject: [PATCH 7/7] match OSX behavior with Win behavior --- src/Avalonia.Native/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index f3b60f07be..f740be44a2 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -114,7 +114,7 @@ namespace Avalonia.Native { var visual = (_inputRoot as Window).Renderer.HitTestFirst(e.Position, _inputRoot as Window, x => { - if (x is IInputElement ie && !ie.IsHitTestVisible) + if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsVisible)) { return false; }