From 75d210913ea23a156d7ce6a9411a269b9b6c82ab Mon Sep 17 00:00:00 2001 From: Thomas Bailly <3940727+Authfix@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:57:10 +0200 Subject: [PATCH 1/6] feat(textbox): add character casing to TextBox --- samples/ControlCatalog/Pages/TextBoxPage.xaml | 6 +++++ src/Avalonia.Base/Media/CharacterCasing.cs | 23 +++++++++++++++++++ src/Avalonia.Controls/TextBox.cs | 22 ++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 src/Avalonia.Base/Media/CharacterCasing.cs diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 7408399873..f5f45797d3 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -88,6 +88,12 @@ + + + + + + + /// Defines how text is cased. + /// + public enum CharacterCasing + { + /// + /// The text casing is normal. + /// + Normal, + + /// + /// The text casing is upper. + /// + Upper, + + /// + /// The text casing is lower. + /// + Lower + } +} diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index b12c23c597..a4a982641e 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -146,6 +146,9 @@ namespace Avalonia.Controls public static readonly StyledProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(); + public static readonly StyledProperty CharacterCasingProperty = + AvaloniaProperty.Register(nameof(CharacterCasing), defaultValue: CharacterCasing.Normal); + /// /// Defines the property. /// @@ -632,6 +635,12 @@ namespace Avalonia.Controls set => SetValue(TextAlignmentProperty, value); } + public CharacterCasing CharacterCasing + { + get => GetValue(CharacterCasingProperty); + set => SetValue(CharacterCasingProperty, value); + } + /// /// Gets or sets the placeholder or descriptive text that is displayed even if the /// property is not yet set. @@ -915,6 +924,10 @@ namespace Avalonia.Controls UpdatePseudoclasses(); UpdateCommandStates(); } + else if (change.Property == CharacterCasingProperty) + { + Text = AdjustCasing(Text); + } else if (change.Property == CaretIndexProperty) { OnCaretIndexChanged(change); @@ -1017,6 +1030,7 @@ namespace Avalonia.Controls } input = SanitizeInputText(input); + input = AdjustCasing(input); if (string.IsNullOrEmpty(input)) { @@ -2112,6 +2126,14 @@ namespace Avalonia.Controls return text.Substring(start, end - start); } + private string? AdjustCasing(string? text) => CharacterCasing switch + { + CharacterCasing.Lower => text?.ToLower(System.Globalization.CultureInfo.CurrentCulture), + CharacterCasing.Upper => text?.ToUpper(System.Globalization.CultureInfo.CurrentCulture), + CharacterCasing.Normal => text, + _ => text + }; + /// /// Returns the sum of any vertical whitespace added between the and in the control template. /// From 2e006302ff164197b6fb8b7e316b6146a9e8da47 Mon Sep 17 00:00:00 2001 From: Thomas Bailly <3940727+Authfix@users.noreply.github.com> Date: Wed, 4 Sep 2024 00:24:33 +0200 Subject: [PATCH 2/6] refactor(textbox): update code to match existing conventions --- src/Avalonia.Controls/TextBox.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index a4a982641e..efb59f7f10 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -403,6 +403,14 @@ namespace Avalonia.Controls set => SetValue(CaretIndexProperty, value); } + private void OnCharacterCasingChanged(AvaloniaPropertyChangedEventArgs e) + { + var tb = (TextBox)e.Sender; + var newValue = AdjustCasing(tb.Text); + + SetCurrentValue(TextProperty, newValue); + } + private void OnCaretIndexChanged(AvaloniaPropertyChangedEventArgs e) { UndoRedoState state; @@ -926,7 +934,7 @@ namespace Avalonia.Controls } else if (change.Property == CharacterCasingProperty) { - Text = AdjustCasing(Text); + OnCharacterCasingChanged(change); } else if (change.Property == CaretIndexProperty) { From 1254f13e5cc6019b40620e4867b2d8d3558464cc Mon Sep 17 00:00:00 2001 From: Thomas Bailly <3940727+Authfix@users.noreply.github.com> Date: Wed, 4 Sep 2024 01:12:10 +0200 Subject: [PATCH 3/6] feat(textbox): use coerce and input --- src/Avalonia.Controls/TextBox.cs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index efb59f7f10..d7b30ea2f6 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -406,7 +406,7 @@ namespace Avalonia.Controls private void OnCharacterCasingChanged(AvaloniaPropertyChangedEventArgs e) { var tb = (TextBox)e.Sender; - var newValue = AdjustCasing(tb.Text); + var newValue = AdjustCasing(tb.Text, e.GetNewValue()); SetCurrentValue(TextProperty, newValue); } @@ -591,8 +591,8 @@ namespace Avalonia.Controls { textBox.SnapshotUndoRedo(); } - - return value; + + return AdjustCasing(value, textBox.CharacterCasing); } /// @@ -643,6 +643,9 @@ namespace Avalonia.Controls set => SetValue(TextAlignmentProperty, value); } + /// + /// Gets or sets the of the TextBox. + /// public CharacterCasing CharacterCasing { get => GetValue(CharacterCasingProperty); @@ -926,7 +929,7 @@ namespace Avalonia.Controls CoerceValue(CaretIndexProperty); CoerceValue(SelectionStartProperty); CoerceValue(SelectionEndProperty); - + RaiseTextChangeEvents(); UpdatePseudoclasses(); @@ -1038,7 +1041,7 @@ namespace Avalonia.Controls } input = SanitizeInputText(input); - input = AdjustCasing(input); + input = AdjustCasing(input, CharacterCasing); if (string.IsNullOrEmpty(input)) { @@ -2134,7 +2137,13 @@ namespace Avalonia.Controls return text.Substring(start, end - start); } - private string? AdjustCasing(string? text) => CharacterCasing switch + /// + /// Adjust the text casing. + /// + /// The text to adjust. + /// The character casing we want. + /// + private static string? AdjustCasing(string? text, CharacterCasing characterCasing) => characterCasing switch { CharacterCasing.Lower => text?.ToLower(System.Globalization.CultureInfo.CurrentCulture), CharacterCasing.Upper => text?.ToUpper(System.Globalization.CultureInfo.CurrentCulture), From fb76189f1c5ab28d534bb803cfb24a679d7f28c0 Mon Sep 17 00:00:00 2001 From: Thomas Bailly <3940727+Authfix@users.noreply.github.com> Date: Wed, 4 Sep 2024 01:14:57 +0200 Subject: [PATCH 4/6] refactor: remove typo --- src/Avalonia.Controls/TextBox.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index d7b30ea2f6..5576e6a496 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -929,7 +929,6 @@ namespace Avalonia.Controls CoerceValue(CaretIndexProperty); CoerceValue(SelectionStartProperty); CoerceValue(SelectionEndProperty); - RaiseTextChangeEvents(); UpdatePseudoclasses(); From 0f103271c7dc5bec299f9b6bdd70f6417c3e9075 Mon Sep 17 00:00:00 2001 From: Thomas Bailly <3940727+Authfix@users.noreply.github.com> Date: Wed, 4 Sep 2024 01:20:42 +0200 Subject: [PATCH 5/6] tests(textbox): add tests for casing --- .../Avalonia.Controls.UnitTests/TextBoxTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index 30f82533ce..bcc444c6bd 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -1385,6 +1385,22 @@ namespace Avalonia.Controls.UnitTests } } + [Theory] + [InlineData(CharacterCasing.Lower, "LoWer", "lower")] + [InlineData(CharacterCasing.Upper, "uppEr", "UPPER")] + [InlineData(CharacterCasing.Normal, "NorMal", "NorMal")] + public void Should_Respect_Casing(CharacterCasing casing, string value, string expected) + { + var target = new TextBox + { + Template = CreateTemplate(), + CharacterCasing = casing, + Text = value + }; + + Assert.Equal(target.Text, expected); + } + [Fact] public void Should_Move_Caret_To_EndOfLine() { From 6eba5592d6779eb8fb5735be6565b6ec85b1a491 Mon Sep 17 00:00:00 2001 From: Thomas Bailly <3940727+Authfix@users.noreply.github.com> Date: Wed, 4 Sep 2024 01:25:14 +0200 Subject: [PATCH 6/6] tests(textbox): check casing update --- tests/Avalonia.Controls.UnitTests/TextBoxTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index bcc444c6bd..1338592e5a 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -1401,6 +1401,21 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(target.Text, expected); } + [Fact] + public void Should_Update_Casing_After_First_Render() + { + var tb = new TextBox + { + Template = CreateTemplate(), + Text = "abc" + }; + + tb.Measure(Size.Infinity); + tb.CharacterCasing = CharacterCasing.Upper; + + Assert.Equal("ABC", tb.Text); + } + [Fact] public void Should_Move_Caret_To_EndOfLine() {