From c7b772b26201c588a3b91527d9a3b462bc363a82 Mon Sep 17 00:00:00 2001 From: AndrewSt Date: Tue, 13 Sep 2016 23:27:52 +0300 Subject: [PATCH 01/12] TextBox fix NullReference bug when Text=Empty and press Ctrl+A (SelectAll) --- src/Avalonia.Controls/TextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 104b1ba2a7..16a1e10a45 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -695,7 +695,7 @@ namespace Avalonia.Controls private void SelectAll() { SelectionStart = 0; - SelectionEnd = Text.Length; + SelectionEnd = Text?.Length ?? 0; } private bool DeleteSelection() From e3f94011bc8f82241bcf661bae374428b87a0d75 Mon Sep 17 00:00:00 2001 From: AndrewSt Date: Tue, 13 Sep 2016 23:57:31 +0300 Subject: [PATCH 02/12] Fix bug Pressing Ctrl-Z in a TextBox with no changes removes text #493 --- src/Avalonia.Controls/TextBox.cs | 2 +- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 50 +++++++++---------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 16a1e10a45..e94c49f5e5 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -148,7 +148,7 @@ namespace Avalonia.Controls { value = CoerceCaretIndex(value); SetAndRaise(CaretIndexProperty, ref _caretIndex, value); - if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState.Text == Text) + if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState?.Text == Text) _undoRedoHelper.UpdateLastState(); } } diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 2301198a01..25d3175153 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -8,20 +8,19 @@ using Avalonia.Utilities; namespace Avalonia.Controls.Utils { - class UndoRedoHelper : WeakTimer.IWeakTimerSubscriber where TState : IEquatable + class UndoRedoHelper : WeakTimer.IWeakTimerSubscriber where TState : struct, IEquatable { private readonly IUndoRedoHost _host; public interface IUndoRedoHost { - TState UndoRedoState { get; set; } + TState UndoRedoState { get; set; } } - + private readonly LinkedList _states = new LinkedList(); - [NotNull] private LinkedListNode _currentNode; public int Limit { get; set; } = 10; @@ -29,23 +28,21 @@ namespace Avalonia.Controls.Utils public UndoRedoHelper(IUndoRedoHost host) { _host = host; - _states.AddFirst(_host.UndoRedoState); - _currentNode = _states.First; - WeakTimer.StartWeakTimer(this, new TimeSpan(0, 0, 1)); - + //_states.AddFirst(_host.UndoRedoState); + //_currentNode = _states.First; + WeakTimer.StartWeakTimer(this, TimeSpan.FromSeconds(1)); } public void Undo() { - if (_currentNode?.Previous != null) - { - _currentNode = _currentNode.Previous; - } - - _host.UndoRedoState = _currentNode.Value; + if (_currentNode?.Previous != null) + { + _currentNode = _currentNode.Previous; + _host.UndoRedoState = _currentNode.Value; + } } - public bool IsLastState => _currentNode.Next == null; + public bool IsLastState => _currentNode != null && _currentNode.Next == null; public void UpdateLastState(TState state) { @@ -57,34 +54,33 @@ namespace Avalonia.Controls.Utils _states.Last.Value = _host.UndoRedoState; } - public TState LastState => _currentNode.Value; + public TState? LastState => _currentNode?.Value; public void DiscardRedo() { - //Linked list sucks, so we are doing this - while (_currentNode.Next != null) + while (_currentNode?.Next != null) _states.Remove(_currentNode.Next); } public void Redo() - { - if (_currentNode?.Next != null) { - _currentNode = _currentNode.Next; - } - - _host.UndoRedoState = _currentNode.Value; + { + if (_currentNode?.Next != null) + { + _currentNode = _currentNode.Next; + _host.UndoRedoState = _currentNode.Value; + } } public void Snapshot() { var current = _host.UndoRedoState; - if (!_currentNode.Value.Equals(current)) + if (_currentNode == null || !_currentNode.Value.Equals(current)) { - if(_currentNode.Next != null) + if (_currentNode?.Next != null) DiscardRedo(); _states.AddLast(current); _currentNode = _states.Last; - if(_states.Count > Limit) + if (_states.Count > Limit) _states.RemoveFirst(); } } From 03aa3c0b823553fd054244fd8d425ebeea6b4bc7 Mon Sep 17 00:00:00 2001 From: AndrewSt Date: Wed, 14 Sep 2016 08:57:52 +0300 Subject: [PATCH 03/12] UndoRedoHelper.cs remove the commented-out code --- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 25d3175153..4382d5c3cb 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -28,8 +28,6 @@ namespace Avalonia.Controls.Utils public UndoRedoHelper(IUndoRedoHost host) { _host = host; - //_states.AddFirst(_host.UndoRedoState); - //_currentNode = _states.First; WeakTimer.StartWeakTimer(this, TimeSpan.FromSeconds(1)); } From 795cb3bb9bbc29e580d0bfbb0bdbc03a945408de Mon Sep 17 00:00:00 2001 From: AndrewSt Date: Wed, 14 Sep 2016 22:53:38 +0300 Subject: [PATCH 04/12] Change property LastState on TryGetLastState method and add HasState property in UndoRedoHelper --- src/Avalonia.Controls/TextBox.cs | 3 ++- src/Avalonia.Controls/Utils/UndoRedoHelper.cs | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index e94c49f5e5..94342b8e3a 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -148,7 +148,8 @@ namespace Avalonia.Controls { value = CoerceCaretIndex(value); SetAndRaise(CaretIndexProperty, ref _caretIndex, value); - if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState?.Text == Text) + UndoRedoState state; + if (_undoRedoHelper.TryGetLastState(out state) && state.Text == Text) _undoRedoHelper.UpdateLastState(); } } diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 4382d5c3cb..71ba3793d1 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -42,6 +42,17 @@ namespace Avalonia.Controls.Utils public bool IsLastState => _currentNode != null && _currentNode.Next == null; + public bool TryGetLastState(out TState _state) + { + _state = default(TState); + if (!IsLastState) + return false; + + _state = _currentNode.Value; + return true; + } + + public bool HasState => _currentNode != null; public void UpdateLastState(TState state) { _states.Last.Value = state; @@ -52,8 +63,6 @@ namespace Avalonia.Controls.Utils _states.Last.Value = _host.UndoRedoState; } - public TState? LastState => _currentNode?.Value; - public void DiscardRedo() { while (_currentNode?.Next != null) From f15a136e00cbd741cf92f9d9a3cd239151d66b1a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 17 Sep 2016 20:46:48 +0200 Subject: [PATCH 05/12] Throw an exception if FontWeight <= 0 In FormattedText. Fixes #700. --- .../Media/FormattedText.cs | 11 +++++++- .../Media/FormattedTextTests.cs | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.SceneGraph.UnitTests/Media/FormattedTextTests.cs diff --git a/src/Avalonia.SceneGraph/Media/FormattedText.cs b/src/Avalonia.SceneGraph/Media/FormattedText.cs index 1169a56343..f6aeaf5f5f 100644 --- a/src/Avalonia.SceneGraph/Media/FormattedText.cs +++ b/src/Avalonia.SceneGraph/Media/FormattedText.cs @@ -33,7 +33,16 @@ namespace Avalonia.Media { Contract.Requires(text != null); Contract.Requires(fontFamilyName != null); - Contract.Requires(fontSize > 0); + + if (fontSize <= 0) + { + throw new ArgumentException("FontSize must be greater than 0"); + } + + if (fontWeight <= 0) + { + throw new ArgumentException("FontWeight must be greater than 0"); + } Text = text; FontFamilyName = fontFamilyName; diff --git a/tests/Avalonia.SceneGraph.UnitTests/Media/FormattedTextTests.cs b/tests/Avalonia.SceneGraph.UnitTests/Media/FormattedTextTests.cs new file mode 100644 index 0000000000..d36e23a6d8 --- /dev/null +++ b/tests/Avalonia.SceneGraph.UnitTests/Media/FormattedTextTests.cs @@ -0,0 +1,28 @@ +using System; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.SceneGraph.UnitTests.Media +{ + public class FormattedTextTests + { + [Fact] + public void Exception_Should_Be_Thrown_If_FontSize_0() + { + Assert.Throws(() => new FormattedText( + "foo", + "Ariel", + 0)); + } + + [Fact] + public void Exception_Should_Be_Thrown_If_FontWeight_0() + { + Assert.Throws(() => new FormattedText( + "foo", + "Ariel", + 12, + fontWeight: 0)); + } + } +} From b2141da4308e7508bfa66bc3de3325b77f4ef27a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 17 Sep 2016 20:54:10 +0200 Subject: [PATCH 06/12] Forgot to save .csproj. --- .../Avalonia.SceneGraph.UnitTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Avalonia.SceneGraph.UnitTests/Avalonia.SceneGraph.UnitTests.csproj b/tests/Avalonia.SceneGraph.UnitTests/Avalonia.SceneGraph.UnitTests.csproj index 34f12d16ed..9d6f861ddf 100644 --- a/tests/Avalonia.SceneGraph.UnitTests/Avalonia.SceneGraph.UnitTests.csproj +++ b/tests/Avalonia.SceneGraph.UnitTests/Avalonia.SceneGraph.UnitTests.csproj @@ -77,6 +77,7 @@ + From fc6115735ee39145451a0b08de9323be702f76eb Mon Sep 17 00:00:00 2001 From: AndrewSt Date: Sun, 18 Sep 2016 00:00:30 +0300 Subject: [PATCH 07/12] Add unit test check Ctrl+A, Ctrl+Z --- .../TextBoxTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 168d9786c2..6bbdae49c7 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -41,6 +41,58 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Press_Ctrl_A_Select_All_Text() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate(), + Text = "1234" + }; + + RaiseKeyEvent(target, Key.A, InputModifiers.Control); + + Assert.Equal(0, target.SelectionStart); + Assert.Equal(4, target.SelectionEnd); + } + } + + [Fact] + public void Press_Ctrl_A_Select_All_Null_Text() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate() + }; + + RaiseKeyEvent(target, Key.A, InputModifiers.Control); + + Assert.Equal(0, target.SelectionStart); + Assert.Equal(0, target.SelectionEnd); + } + } + + [Fact] + public void Press_Ctrl_Z_With_Not_Modify_Text() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Template = CreateTemplate(), + Text = "1234" + }; + + RaiseKeyEvent(target, Key.Z, InputModifiers.Control); + + Assert.Equal("1234", target.Text); + } + } + [Fact] public void Typing_Beginning_With_0_Should_Not_Modify_Text_When_Bound_To_Int() { From 6b9c7c1c4735b4eda01192bc82a54dae04ef768e Mon Sep 17 00:00:00 2001 From: Robbie Knuth Date: Mon, 19 Sep 2016 12:41:05 -0700 Subject: [PATCH 08/12] Textboxes should take whitespace into account when calculating size. --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index f9c55e1f3b..d3cf4e5509 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -219,7 +219,7 @@ namespace Avalonia.Controls.Presenters { var text = Text; - if (!string.IsNullOrWhiteSpace(text)) + if (!string.IsNullOrEmpty(text)) { return base.MeasureOverride(availableSize); } From 507bc51b595337a3d3db99eb3f72d2c77ba05112 Mon Sep 17 00:00:00 2001 From: Robbie Knuth Date: Mon, 19 Sep 2016 12:43:26 -0700 Subject: [PATCH 09/12] Handle special case of deleting carriage returns and linefeeds in multiline textboxes. --- src/Avalonia.Controls/TextBox.cs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index ed73472c49..b86d3e0d4e 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -379,8 +379,20 @@ namespace Avalonia.Controls if (!DeleteSelection() && CaretIndex > 0) { - SetTextInternal(text.Substring(0, caretIndex - 1) + text.Substring(caretIndex)); - --CaretIndex; + var removedCharacters = 1; + // handle deleting /r/n + // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if + // a /r should also be deleted. + if (CaretIndex > 1 && + text[CaretIndex - 1] == '\n' && + text[CaretIndex - 2] == '\r') + { + removedCharacters = 2; + } + + SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex)); + CaretIndex -= removedCharacters; + SelectionStart = SelectionEnd = CaretIndex; } break; @@ -393,7 +405,18 @@ namespace Avalonia.Controls if (!DeleteSelection() && caretIndex < text.Length) { - SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + 1)); + var removedCharacters = 1; + // handle deleting /r/n + // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if + // a /r should also be deleted. + if (CaretIndex < text.Length - 1 && + text[caretIndex + 1] == '\n' && + text[caretIndex] == '\r') + { + removedCharacters = 2; + } + + SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters)); } break; From 7872b6e2bfa6c203bd8e82c694b132dc1b87d7c4 Mon Sep 17 00:00:00 2001 From: AndrewSt Date: Mon, 19 Sep 2016 23:50:33 +0300 Subject: [PATCH 10/12] Fix typo in TextBoxTest.cs --- tests/Avalonia.Controls.UnitTests/TextBoxTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 6bbdae49c7..851657ab6c 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -77,7 +77,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Press_Ctrl_Z_With_Not_Modify_Text() + public void Press_Ctrl_Z_Will_Not_Modify_Text() { using (UnitTestApplication.Start(Services)) { From 96bd07e193bf4b428cbc2965b27b465a991e8b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Wed, 21 Sep 2016 21:43:25 +0200 Subject: [PATCH 11/12] Use mono 4.4.2 until latest 4.6 is fixed https://bugzilla.xamarin.com/show_bug.cgi?id=44323 https://bugzilla.xamarin.com/show_bug.cgi?id=44402 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index af7d5dbebf..9693d21b7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ os: - linux - osx mono: - - latest + - 4.4.2 script: - ./build.sh --target "Travis" --platform "Mono" --configuration "Release" notifications: From 256465cd730da46d4b9b9853e722f7aede88b7d0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 29 Sep 2016 22:55:37 +0100 Subject: [PATCH 12/12] Improved text rendering quality on skia backend. --- src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index 5a93538757..e24b6053b0 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -28,7 +28,9 @@ namespace Avalonia.Skia //Paint.TextEncoding = SKTextEncoding.Utf8; _paint.TextEncoding = SKTextEncoding.Utf16; _paint.IsStroke = false; - _paint.IsAntialias = true; + _paint.IsAntialias = true; + _paint.LcdRenderText = true; + _paint.SubpixelText = true; _paint.Typeface = typeface; _paint.TextSize = (float)fontSize; _paint.TextAlign = textAlignment.ToSKTextAlign();