maui icon indicating copy to clipboard operation
maui copied to clipboard

Entry control CursorPosition does not update on TextChanged event [iOS Maui 8.0.7]

Open jurijr opened this issue 11 months ago • 5 comments

Description

When typing into Entry control on iOS, the CursorPosition property is not updated. For example output when typing text "Test"

->T expectedPosition[1] actualPosition[0]
T->Te expectedPosition[2] actualPosition[0]
Te->Tes expectedPosition[3] actualPosition[0]
Tes->Test expectedPosition[4] actualPosition[0]

Steps to Reproduce

  1. Add Entry control with TextChanged event <Entry TextChanged="Entry_TextChanged" />
  2. Add TextChanged event handler
void Entry_TextChanged(object sender, TextChangedEventArgs e)
    {
		Entry entry = sender as Entry;

#if IOS
        if (entry?.Handler?.PlatformView is UIKit.UITextField entryTextField)
        {
            var position = entryTextField.GetOffsetFromPosition(entryTextField.BeginningOfDocument, entryTextField.SelectedTextRange.Start);

            Console.WriteLine($"'{e.OldTextValue}'->'{e.NewTextValue}' expectedPosition=[{position}] actualPosition=[{entry.CursorPosition}]");
        }
#endif
    }
  1. Observe actualPosition (entry.CursorPosition) does not get updated, compared to native control

Link to public reproduction project repository

No response

Version with bug

8.0.7 SR2

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

No response

Did you find any workaround?

Use position from native UITextField control.

Relevant log output

No response

jurijr avatar Feb 29 '24 08:02 jurijr

Can confirm something strange is going on. I get this error: (however, not every time I enter text). I don't know if the error is directly related, or if it somehow interfers with the cursor position update.

2024-02-29 11:09:29.589485+0100 MauiButtons[31806:18792457] [Unknown process name] Error: this application, or a library it uses, has passed an invalid numeric value (NaN, or not-a-number) to CoreGraphics API and this value is being ignored. Please fix this problem.

Other behavior that i observed: The cursor value is updated if one moves the cursor a inside the text and either types or deletes. The position is still incorrect though.

IrisClasson avatar Feb 29 '24 10:02 IrisClasson

I took a look at the EntryHandler git history and found the commit that added the SelectionChanged. I looked through the commits trying to get a better understanding for how the cursor update code had changed.

From what I can tell, the UpdateText in TextExtensions used to update the cursor position. At first the direct call to a method that updates the position was used. textView.UpdateCursorPosition(oldText, newText, currentCursorPosition); and later with

var cursorPosition = textView.GetCursorPosition(cursorOffset);  
// other code
 ((ITextInput)inputView).CursorPosition = cursorPosition;  

which even later was replaced with textView.SetTextRange(cursorPosition, 0); SetTextRange sets the platformView.SelectedTextRange which in turn is overridden in the MauiTextField class and in its setter the SelectionChanged handler is called which in turn updates the position. So I guess the question is wether we want to set the position directly, or let that handler handle it. The code history suggests both options have been used in the UpdateText method. (Side note, wonder why the tests didn't pick this up?)

I'll update this post with screenshots so it all makes sense, I just want to confirm that I've understood the flow correctly.

I believe, and I could be very wrong, that the condition that was added later, that checked if the new text was transformed would skip the part that would update the cursor. Unless transformed, the old and new text was always the same.

Debugging-MAUI-for-Beginners-and-the-erroneous-cursor-position-1

Adding the else, with a previously removed line, updates the cursor:

static void UpdateText(this IUITextInput textInput, InputView inputView, bool isEditing)
{
    // Setting the text causes the cursor to be reset to the end of the IUITextInput.
    // So, let's set back the cursor to the last known position and calculate a new
    // position if needed when the text was modified by a Converter.
    var textRange = textInput.GetTextRange(textInput.BeginningOfDocument, textInput.EndOfDocument);
    var oldText = textInput.TextInRange(textRange) ?? string.Empty;
    var newText = TextTransformUtilites.GetTransformedText(
        inputView?.Text,
        textInput.GetSecureTextEntry() ? TextTransform.Default : inputView.TextTransform
        );

    if (oldText != newText)
    {
        // Re-calculate the cursor offset position if the text was modified by a Converter.
        // but if the text is being set by code, let's just move the cursor to the end.
        var cursorOffset = newText.Length - oldText.Length;
        var cursorPosition = isEditing ? textInput.GetCursorPosition(cursorOffset) : newText.Length;

        textInput.ReplaceText(textRange, newText);

        textInput.SetTextRange(cursorPosition, 0);
    }
    else
    {
        // This works. But I'm not sure if this is the way.
        ((ITextInput)inputView).CursorPosition = textInput.GetCursorPosition();
    }
}

I'm not able to do a PR right now, and frankly I'm not confident in my solution/findings. But hope this helps nonetheless. I'll take another look and maybe put together a PR later.

IrisClasson avatar Feb 29 '24 18:02 IrisClasson

Do you know what version this used to work on?

PureWeen avatar Feb 29 '24 23:02 PureWeen

Verified this issue with Visual Studio 17.10.0 Preview 2. Can repro on iOS platform. image

Zhanglirong-Winnie avatar Mar 13 '24 08:03 Zhanglirong-Winnie

Can confirm this very annoying behavior on MacCatalyst (MAUI 8.0.10) as well.

As a workaround, I add this line of code to @jurijr's event handler:

entry.CursorPosition = (int)position;

This way, I can at least move on until this is fixed.

MSicc avatar Mar 17 '24 07:03 MSicc