-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SuperTextField][iOS] Ignore auto-corrections after handling input actions (Resolves #2004) #2012
[SuperTextField][iOS] Ignore auto-corrections after handling input actions (Resolves #2004) #2012
Conversation
@miguelcmedeiros Can you please try this PR and see if it work for you? It will be required that |
@angelosilvestre if the user presses "done" and then do not call Is that possible? |
@miguelcmedeiros If you do not intend to call Would that work for you? |
@angelosilvestre I guess that would work. The goal is to be able to handle the "done" key press and only apply a custom behaviour from our side. |
@miguelcmedeiros can you please elaborate on what that means in practice? What custom behavior would you want to add, and what default behavior are you trying to avoid? We can't make any guarantees about completely stopping all default behavior, because it seems that some default behavior isn't in our control. Also, do you feel that you understand the nature of the problem that we've discovered, and what we're doing to solve it? I want to make sure we're on the same page because I'm not a fan of the option to add a very obscure public method that no implementer will understand. |
@matthew-carroll similar to keyboard handlers, it would be great that as a developer I can intercept the pressing of the return button in the soft keyboard so that I can define what happens, e.g. perform a custom action defined on the app side, and then block or allow it to execute the default action. |
I understand the abstract desire. Can you please provide some examples of what's needed, concretely? As I mentioned, we do not have the level of control to provide the abstract goal. Instead, we'll need to look at general areas of control that you definitely want, and then see what we need to do to make those possible. |
A concrete can be what is shown in the screen recording in the ticket description. The user typed some text and then presses return. In our case, we are presenting the user with a due date suggestion, which the user can accept by tapping on the suggestion overlay or press return. Currently, if IME suggests a change to the text, then it will be applied, which is not desired for our use case. |
So does Angelo's change fix that problem (excluding the idea of a new public method)? If so, what new problems does Angelo's approach create for you? Or what other needs are not being met by that change? |
@angelosilvestre let's automatically apply this delta-ignoring behavior outside of the Then, the purpose of the Make sense? |
@matthew-carroll This is the current implementation of this PR. |
@angelosilvestre I tested out the changes with the example code and it still performs the text replacement when pressing return. |
@miguelcmedeiros Did you call super.performAction? |
Nope. My understanding was that if super.performAction is not called, then nothing else happens to the text field. |
@miguelcmedeiros The original issue isn't caused by the default performAction implementation. The issue is that the iOS IME is reporting a replacement due to autocorrection after reporting the input action. The fix in this PR consists in discarding replacement deltas reported in the same frame that an input action is performed. For that, the default performAction implementation needs to be called, so we can keep track that an action was performed. |
@angelosilvestre my point in my earlier comment is that we need this statement to not be true. We do not want to require any particular use, or non-use, of |
@matthew-carroll But then how do we know we need to ignore the replacement? Should we provided a method to signal that we should ignore the deltas as I suggested in this comment? #2012 (comment) |
We should always ignore the replacement on the frame after an action. That's the same thing we're doing in super editor, right? |
@matthew-carroll But if the IME controller is extended to avoid calling the default performAction implementation we cannot know that an action was performed. |
Why can't we install our own private callback with the IME, implement ignoring deltas after actions, and then forward from our private callback to the regular callback in the controller? |
@matthew-carroll I think that, for SuperTextField, the IME controller IS the delta input client, isn't it? The performAction is called directly on the IME controller. |
So what are our options to control that callback? |
@matthew-carroll Maybe introduce a Then, our performAction implementation will be just setting the flag to ignore the deltas and calling the new handleAction method. |
Ok, let's see how that looks in code. |
@matthew-carroll Updated. With this change, the controller subclass will look like the following: class _TextController extends ImeAttributedTextEditingController {
@override
void handleTextInputAction(TextInputAction action) {
// Do not perform actions, while still letting the base controller to keep track of internal flags.
}
} |
@@ -112,6 +112,9 @@ class ImeAttributedTextEditingController extends AttributedTextEditingController | |||
/// Used to determine whether or not we need to send our editing value to the IME. | |||
TextEditingValue _osCurrentTextEditingValue = const TextEditingValue(); | |||
|
|||
/// Whether or not a `TextInputAction` differente from `TextInputAction.newLine` was performed on the current frame. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please include why we're tracking this. Also "differente" -> "different"
@@ -381,7 +394,20 @@ class ImeAttributedTextEditingController extends AttributedTextEditingController | |||
|
|||
@override | |||
void performAction(TextInputAction action) { | |||
_onPerformActionPressed?.call(action); | |||
handleTextInputAction(action); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method call is directly forwarding to another method call. Why do we need the handleTextInputAction()
?
I know what you mentioned in the PR description, but it seems to me like the situation here isn't exactly what was described.
As you can see, we already have a _onPerformActionPressed
callback. That callback is publicly settable. It's called every time an action is pressed.
Why was there ever a need for anyone to try to override performAction()
? If a client wants to customize the action behavior, why wouldn't that client just set their own value on onPerformActionPressed
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@angelosilvestre I tried again (by setting onPerformActionPressed
) and it works as expected.
I'm guessing that yesterday when I tested, I picked the wrong commit and so, was hitting the same issue continuously since your fix was not included. Sorry about the confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matthew-carroll So I guess we can just revert the last commit, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@angelosilvestre I don't know. I can't tell exactly what the situation is. If you want to revert the commit then I can take a look at what that looks like. I still think we want the deltas to automatically be ignored in the next frame, no matter what the app chooses to override. If that was already accomplished in the previous commit, then maybe that commit works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matthew-carroll Yeah, I believe the previous commit will work.
This reverts commit 0a70323.
@@ -382,6 +395,19 @@ class ImeAttributedTextEditingController extends AttributedTextEditingController | |||
@override | |||
void performAction(TextInputAction action) { | |||
_onPerformActionPressed?.call(action); | |||
|
|||
// Keep track that we have performed a text input action on this frame so we can ignore auto-corrections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we're back to the situation where if this method is overridden by a subclass without calling super(), we're not going to get the expected behavior to ignore the deltas.
Either this method should be annotated with "must call super", or this behavior needs to be moved to a place where its guaranteed to run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@matthew-carroll Added @mustCallSuper
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
[SuperTextField][iOS] Ignore auto-corrections after handling input actions. Resolves #2004
On iOS, auto-corrections can happen after pressing the action button. For example, the user might edit some content, press "done", and after the "done" action is handled the IME reports the text replacement.
This PR prevents that by ignoring text replacements in the same frame that input actions are reported, except for
TextInputAction.newLine
.TextInputAction.newLine
can't prevent replacements from being applied, otherwise the user won't be able to replace the selected content with a new line.