Skip to content

Conversation

@LongCatIsLooong
Copy link
Contributor

@LongCatIsLooong LongCatIsLooong commented Sep 24, 2021

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

- Adds a `Focus` widget to the `EditableText` tree. Fixes flutter#90562
- Improves vertical caret movement: flutter#75572
- Introduces `TextEditingValue.replaced`
- `TextSelection.base` and `TextSelection.extent` no longer inherits the text affinity from the `TextSelection` when the selection is not collapsed.
- Removes `TextEditingActionTarget`. Reimplemented text editing `Intent`s and `Action`s in `EditableTextState`.
- `DirectionalFocusAction` no longer does the `is EditableText` type check or assumes the focus node is attached to `EditableText`'s `BuildContext`.
@flutter-dashboard flutter-dashboard bot added f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. labels Sep 24, 2021
@google-cla google-cla bot added the cla: yes label Sep 24, 2021
@LongCatIsLooong LongCatIsLooong removed the request for review from GaryQian September 24, 2021 17:57
@flutter-dashboard flutter-dashboard bot added d: examples Sample code and demos c: contributor-productivity Team-specific productivity, code health, technical debt. labels Sep 25, 2021
@LongCatIsLooong LongCatIsLooong marked this pull request as ready for review September 25, 2021 23:21
@fluttergithubbot fluttergithubbot merged commit ffcd32e into flutter:master Nov 3, 2021
@LongCatIsLooong LongCatIsLooong deleted the text-editing-actions branch November 3, 2021 18:27
cgestes added a commit to cgestes/zefyr that referenced this pull request Nov 10, 2021
@passsy
Copy link
Contributor

passsy commented Nov 12, 2021

Argh, just ran into the removal of DefaultTextEditingActions. I wish it would have been deprecated first

@LongCatIsLooong
Copy link
Contributor Author

@passsy sorry for the breakage, the removal wasn't considered a breaking change because no test failed on that (see https://github.com/flutter/flutter/wiki/Tree-hygiene#1-determine-if-your-change-is-a-breaking-change). Registering your tests (https://github.com/flutter/tests) will hopefully prevent such breakages in the future.

Also I'm a bit curious why DefaultTextEditingActions was referenced in your app. Wasn't it part of WidgetsApp already?

@passsy
Copy link
Contributor

passsy commented Nov 12, 2021

I'd like to give some context:

The wiredash sdk wraps the full app, including the users WidgetsApp/MaterialApp. We kind of life outside the WidgetsApp, no Navigator, no MediaQuery.

The version of wiredash using DefaultTextEditingActions is not yet released/open-source, so you won't find it in the repository. We're fully aware of https://github.com/flutter/tests and will add the new sdk once released, as I did it before for another repo.

We have a TextField where users can leave app feedback. With some glue code it was fully functional

// Both DefaultTextEditingShortcuts and WidgetsApp.defaultActions are
// required to make text edits like deletion of characters possible on macOS
child: DefaultTextEditingShortcuts(
  child: DefaultTextEditingActions(
    child: WiredashTheme(
      data: _theme,
      child: MediaQueryFromWindow(
        // Directionality required for all Text widgets
        child: Directionality(
          textDirection:
              widget.options?.textDirection ?? TextDirection.ltr,
          // Localizations required for all Flutter UI widgets
          child: Localizations(
            locale: widget.options?.currentLocale ?? window.locale,
            delegates: const [
              DefaultMaterialLocalizations.delegate,
              DefaultCupertinoLocalizations.delegate,
              DefaultWidgetsLocalizations.delegate,
            ],

DefaultTextEditingActions was required to make backspace (remove a character) work on macOS. (Android/iOS were working without it)


This breaking change leaves us in a unfortunate state. To support users before and after this breaking changes we have to release and support two different versions of the sdk.

SDK before this change:

  • Use DefaultTextEditingActions to make backspace work on macOS
  • WidgetsApp.defaultActions alone doesn't work

SDK after this change:

  • Use Actions(actions: WidgetsApp.defaultActions, to make backspace work
  • DefaultTextEditingActions has been removed.

It's also not possible to ship our own defaultActions to all users because DeleteTextIntent has also been renamed to DeleteCharacterIntent (another breaking change).

@LongCatIsLooong
Copy link
Contributor Author

Thanks for the context. The easiest workaround I can think of is to remap the backspace key on macOS to your own private intent and handle it yourself. But the framework actually shouldn't be handling shortcuts on macOS as on macOS users can define their own custom system-wide shortcuts, so the current default macOS shortcuts in the framework are probably going to be removed soon and when that happens it will likely break the workaround. In general the desktop support is still in beta so these things are still subject to change.

Android/iOS were working without it

That's probably because DefaultTextEditingActions was only used by hardware key events, not virtual keyboard/IME events. If you type using a hardware keyboard it probably won't work on Android/iOS either.

@pulyaevskiy
Copy link
Contributor

Removes TextEditingActionTarget. Reimplemented text editing Intents and Actions in EditableTextState.

Not sure why this is not marked as breaking change, but aside of that this made it impossible for other editable implementations to re-use the default editable actions implemented in the SDK itself. Now they will have to implement those from scratch or copy-paste what's now private in EditableTextState. Both options are rather undesirable.

@justinmc
Copy link
Contributor

justinmc commented Feb 3, 2022

Here is our definition of a breaking change, I believe this didn't break any external tests at the time?

I agree with you 100% about exposing this logic to the public. I should have brought it up in this review. It will be vital for users that are implementing their own text editors to be able to reuse our shortcuts logic.

How would you prefer this to be exposed, similar to how it was before this PR? @Renzo-Olivares and I are going to be working on getting these shortcuts to work with this text editor demo by exposing them in some way.

@pulyaevskiy
Copy link
Contributor

The previous solution with TextEditingActionTarget actually worked quite well, I was able to integrate it into zefyr editor without any issues.

@pulyaevskiy
Copy link
Contributor

@matthew-carroll and SuperEditor folks may be able to provide more inputs here as well.

@matthew-carroll
Copy link
Contributor

@pulyaevskiy what's the question?

@pulyaevskiy
Copy link
Contributor

@matthew-carroll

Flutter provides default set of shortcuts for handling common texting editing operations (e.g. move cursor up/down/left/right, jump word left/right, similar for selection, copy-paste, and so on). Before this change, most of those shortcuts were encapsulated in a class called TextEditingActionTarget which Zefyr was able to reuse as a mixin and get the default behaviors covered.

This PR, removed TextEditingActionTarget and internalized all the logic responsible for handling all those shortcuts so that there is no way now for custom editable widgets to reuse this logic.

I believe there is little to no reason for different "editable" implementations to come up with their own handling for these shortcuts and there should be some sort of API interface to leverage the built-in logic (possibly with an option to override some of it).

@matthew-carroll
Copy link
Contributor

@pulyaevskiy That point sounds reasonable in theory. If lots of people are applying the same rules, for the same reasons, and those rules and reasons change for everyone at the same time, then it would be counterproductive for Flutter to have a secret implementation and then force everyone else to figure it out.

I have found the monolithic nature of Flutter's text input widgets a problem in general. This sounds like it's probably an extension of that issue. It sounds like the existing monolith is incentivizing the expansion of the monolith, rather than breaking things down into useful pieces.

My guess is that if IME content editing, text layout, and gesture controls were each well encapsulated, this situation probably wouldn't have come up to begin with. That composition probably would have forced these actions to be made publicly available.

But, I've harped on this point for over a year now. There's not much more I can say on that front. Unless and until Flutter is willing to do the legwork to break down the text monolith, I think we're likely to see growth of the monolith, not composable tools. Most of Super Editor's implementation is a re-invention of the tools that are baked into that monolith - we couldn't use them without using all of RenderEditable, which was a no-go.

@pulyaevskiy
Copy link
Contributor

Well, it sounds like @justinmc and the team are planning some work on making at least this part of text editing behavior a bit more modular?

I do agree though that EditableText/RenderEditable do not make it easy to reuse the functionality, but I also can understand that it is hard to come up with a generic interface if you only have (focus) one use case to begin with (that being a basic text field).

Also to be clear, the team did introduce TextEditingActionTarget not so long ago, and I believe it was a step in the right direction, towards modularity. Which is why I was a bit surprised to see it go away in 2.10.

My only concern with the original implementation of TextEditingActionTarget is that it was designed to use as a mixin and I personally prefer good-old composition over mixins. Mixins are a bit like magic and it's hard to see the exact API contract when using them in a big code base.

@matthew-carroll
Copy link
Contributor

There are definitely some new pieces being introduced, and that's good. I'm not sure how far those pieces can go with all the stuff combined under the surface. I guess we'll see.

I would agree that objects should be preferred over mixins whenever possible. Mixins should be reserved for situations where you have to assume some other superclass, such as a mixin on a State. And even then, I hope the developer is making sure that they really do need access to State.

@justinmc
Copy link
Contributor

justinmc commented Feb 4, 2022

It's high priority for me to get this logic exposed again because a fully featured rich text editor is pretty impractical without it, as you two have attested. I'm definitely on board with doing it in a composable way if we can come up with a better design than TextEditingActionTarget.

I've created an issue specifically for this that we can keep up to date with progress: #97830.

@matthew-carroll
Copy link
Contributor

Just to clarify, I doubt Super Editor will use this. We already have handlers for every key that might be pressed on the keyboard. Others may want it, including Zefyr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c: contributor-productivity Team-specific productivity, code health, technical debt. d: examples Sample code and demos f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EditableText should use a Focus widget instead of managing its own FocusNode

6 participants