AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
25.59k stars 2.22k forks source link

Support ChacaracterCasing in TextBox #11931

Open cesarchefinho opened 1 year ago

cesarchefinho commented 1 year ago

Is your feature request related to a problem? Please describe. Fix character casing on TextBoxes.

ALL FRAMEWORKS have it. (WInUI / UWP / WPF / WinForms)

Describe the solution you'd like

control how characters are cased when they are manually entered into the text box.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context

basicaly:

Add an enum

**public enum CharacterCasing
{
    Normal = 0,
    Upper,
    Lower
}**

modify textbox.cs

        **public static readonly StyledProperty<CharacterCasing> CharacterCasingProperty =
            AvaloniaProperty.Register<TextBox, CharacterCasing>(nameof(CharacterCasing));**

        **private string? AdjustCasing(string? text)
        {
            return CharacterCasing switch
            {
                CharacterCasing.Lower => text?.ToLower(),
                CharacterCasing.Upper => text?.ToUpper(),
                CharacterCasing.Normal => text,
                _ => text
            };
        }**

        **public CharacterCasing CharacterCasing
        {
            get => GetValue(CharacterCasingProperty);
            set => SetValue(CharacterCasingProperty, value);
        }**

        /// <summary>
        /// Gets or sets the Text content of the TextBox
        /// </summary>
        **[Content]
        public string? Text
        {
            get => AdjustCasing(GetValue(TextProperty));
            set => SetValue(TextProperty, AdjustCasing (value));
        }**

        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
        {
            base.OnPropertyChanged(change);

            if (change.Property == TextProperty)
            {
                CoerceValue(CaretIndexProperty);
                CoerceValue(SelectionStartProperty);
                CoerceValue(SelectionEndProperty);

                RaiseTextChangeEvents();

                UpdatePseudoclasses();
                UpdateCommandStates();
            }
            **else if (change.Property == CharacterCasingProperty)
            {
                Text = AdjustCasing(Text);
            }**
            else if (change.Property == CaretIndexProperty)
            {
                OnCaretIndexChanged(change);
            }
            else if (change.Property == SelectionStartProperty)
            {
                OnSelectionStartChanged(change);
            }
            else if (change.Property == SelectionEndProperty)
            {
                OnSelectionEndChanged(change);
            }
            else if (change.Property == MaxLinesProperty)
            {
                InvalidateMeasure();
            }
            else if (change.Property == UndoLimitProperty)
            {
                OnUndoLimitChanged(change.GetNewValue<int>());
            }
            else if (change.Property == IsUndoEnabledProperty && change.GetNewValue<bool>() == false)
            {
                // from docs at
                // https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.textboxbase.isundoenabled:
                // "Setting this property to false clears the undo stack.
                // Therefore, if you disable undo and then re-enable it, undo commands still do not work
                // because the undo stack was emptied when you disabled undo."
                _undoRedoHelper.Clear();
                _selectedTextChangesMadeSinceLastUndoSnapshot = 0;
                _hasDoneSnapshotOnce = false;
            }
        }

        private void HandleTextInput(string? input)
        {
            if (IsReadOnly)
            {
                return;
            }

            **input = AdjustCasing(RemoveInvalidCharacters(input));**

            if (string.IsNullOrEmpty(input))
            {
                return;
            }

            _selectedTextChangesMadeSinceLastUndoSnapshot++;
            SnapshotUndoRedo(ignoreChangeCount: false);

            var currentText = Text ?? string.Empty;

            var selectionLength = Math.Abs(SelectionStart - SelectionEnd);
            var newLength = input.Length + currentText.Length - selectionLength;

            if (MaxLength > 0 && newLength > MaxLength)
            {
                input = input.Remove(Math.Max(0, input.Length - (newLength - MaxLength)));
                newLength = MaxLength;
            }

            if (!string.IsNullOrEmpty(input))
            {
                var textBuilder = StringBuilderCache.Acquire(Math.Max(currentText.Length, newLength));
                textBuilder.Append(currentText);

                var caretIndex = CaretIndex;

                if (selectionLength != 0)
                {
                    var (start, _) = GetSelectionRange();

                    textBuilder.Remove(start, selectionLength);

                    caretIndex = start;
                }

                textBuilder.Insert(caretIndex, input);

                SetCurrentValue(TextProperty, StringBuilderCache.GetStringAndRelease(textBuilder));

                ClearSelection();

                if (IsUndoEnabled)
                {
                    _undoRedoHelper.DiscardRedo();
                }

                SetCurrentValue(CaretIndexProperty, caretIndex + input.Length);
            }
        }

TextBoxCs.txt

cesarchefinho commented 1 year ago

Can someone make a PR using this code and submit it to avalonia?

I don't have knowledge of working with open source projects, if someone can help I will be glad.

Thank you

timunie commented 1 year ago

@cesarchefinho communtiy PR will be opened up again after v11 dropped. Then you are welcome to learn how to contribute ;-) You can also chat on telegram with us if you need assistence to get started

cesarchefinho commented 1 year ago

@timunie can you comment about this proposal and code changes marked with ?

thanks

timunie commented 1 year ago

I'll leave commenting on the impl details to @Gillibald . He is strong in this area.

Gillibald commented 1 year ago

The part that handles OnPropertyChanged should be left out because it changes the casing of existing text. CharacterCasing is only meant to adjust input. I would accept such a change for the next version of Avalonia.

cesarchefinho commented 1 year ago

thanks a lot.

Avalonia is the only framework tht haven't CharacterCasing and it is impirtant to a lot of people.

cesarchefinho commented 1 year ago

@Gillibald , now avalinia is released, pease use my code to imorove texbox.

In future i will make other contributions by Pull Request, but now i dont have the knolowge to make it.

Thank you very much

timunie commented 1 year ago

@cesarchefinho we can guide you through the process I think. So your PR would be appreciated. Join us on telegram for chat support helping you to provide your very first PR. You'd benefit from learning new things as well.

FraserElectronics commented 6 months ago

What is the status with this ? Is there still no option to limit the case in a TextBox?

Andy

Gillibald commented 6 months ago

Contributions by the community are welcome.

The only acceptable solution is to add needed character properties to our Unicode data.

https://www.unicode.org/versions/Unicode15.1.0/ch05.pdf Section 5.18 Case Mappings https://www.[unicode.org/Public/UNIDATA/CaseFolding.txt](https://www.unicode.org/Public/UNIDATA/CaseFolding.txt)