-
Notifications
You must be signed in to change notification settings - Fork 246
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
[SuperEditor][mobile] Implement spellchecking support (Resolves #2353) #2378
base: main
Are you sure you want to change the base?
Conversation
@angelosilvestre - Is this the PR you wanted me to review? If so, there should still be a description explaining what you did and how you did it - that's what directs the PR review. Also, whenever you want a review, please tag me in the PR. |
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.
I did a partial review. Before reviewing the rest, I think we need to align on why this spell check controller was injected in the places that it was. When we talked about this online, I mentioned treating this spell check toolbar the same way we treat the mobile editing toolbars, but as far as I can tell, that's not what this PR is currently doing.
super_editor/lib/src/default_editor/document_gestures_touch_android.dart
Outdated
Show resolved
Hide resolved
super_editor/lib/src/default_editor/document_gestures_touch_android.dart
Outdated
Show resolved
Hide resolved
|
||
/// Attaches this controller to a delegate that knows how to | ||
/// show a popover with spelling suggestions. | ||
void attach(SpellCheckerPopoverDelegate delegate) { |
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.
Did we use an attach/detach approach for the existing mobile editing toolbar, too?
@@ -0,0 +1,35 @@ | |||
import 'package:super_editor/src/core/document_selection.dart'; | |||
|
|||
/// Shows/hides a popover with spelling suggestions. |
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.
There should be a lot more information in this Dart Doc. Imagine reading this as someone who needs to use this - there's no guiding information about where/when/how.
} | ||
} | ||
|
||
abstract class SpellCheckerPopoverDelegate { |
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.
Missing Dart Doc. This should have a lot of info about who is supposed to implement this.
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.
Added some docs, can you take a look to see what else should be detailed?
} | ||
|
||
abstract class SpellCheckerPopoverDelegate { | ||
/// Shows spelling suggestions for the word at [wordRange]. |
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.
What if the document changes? What if the user's selection changes? Please fully describe expected behaviors.
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.
Added some docs, can you take a look to see what else should be detailed?
This PR is meant to review the controller usage only. I should have added that to the description, sorry. |
@matthew-carroll I moved the controller to |
super_editor/lib/src/default_editor/document_gestures_touch_android.dart
Outdated
Show resolved
Hide resolved
widget.contentTapHandler?.addListener(_updateMouseCursorAtLatestOffset); | ||
if (widget.contentTapHandlers != null) { | ||
for (final handler in widget.contentTapHandlers!) { | ||
handler.addListener(_updateMouseCursorAtLatestOffset); |
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.
I don't remember what this callback is for. Are you sure it makes sense to run that callback for every handler?
@@ -62,9 +63,9 @@ class DocumentMouseInteractor extends StatefulWidget { | |||
final Stream<DocumentSelectionChange> selectionChanges; | |||
final ValueListenable<DocumentSelection?> selectionNotifier; | |||
|
|||
/// Optional handler that responds to taps on content, e.g., opening | |||
/// Optional list of handlers that respond to taps on content, e.g., opening |
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.
For each Dart Doc related to these handlers, please specify the chain of responsibility behavior. The current Dart Docs here, and elsewhere, don't explain that only a single handler is ever run...
|
||
if (widget.contentTapHandlers != null && docPosition != null) { | ||
for (final handler in widget.contentTapHandlers!) { | ||
final result = handler.onTap(docPosition); |
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.
I see that this was moved up above the long tap and scrolling handler. I'm not sure that's correct. Can you explain why they belong up here?
@@ -34,6 +35,15 @@ class SpellingAndGrammarStyler extends SingleColumnLayoutStylePhase { | |||
markDirty(); | |||
} | |||
|
|||
/// Whether or not we should to override the default selection color with [selectionHighlightColor]. | |||
/// | |||
/// On mobile platforms, when the suggestions popover is opened, the selected text uses a different |
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 line is a bit confusing. Does this line mean that this property will be true
on mobile?
@@ -847,7 +861,13 @@ class SuperEditorState extends State<SuperEditor> { | |||
getDocumentLayout: () => editContext.documentLayout, | |||
selectionChanges: editContext.composer.selectionChanges, | |||
selectionNotifier: editContext.composer.selectionNotifier, | |||
contentTapHandler: _contentTapDelegate, | |||
contentTapHandlers: [ | |||
if (_contentTapDelegate != null) // |
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.
The term "delegate" probably needs to be replaced with "handler" because typically there's only one delegate. There might be many handlers.
Given that SuperEditor can take a list of handlers, is there a reason that plugins only get one?
@@ -311,6 +314,12 @@ class AndroidControlsDocumentLayerState | |||
}); | |||
} | |||
|
|||
void _onSelectionHandlesAllowedChange() { | |||
setState(() { | |||
// |
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 specify what's being rebuilt and why
@@ -319,6 +328,11 @@ class AndroidControlsDocumentLayerState | |||
return null; | |||
} | |||
|
|||
if (_controlsController!.areSelectionHandlesAllowed.value == false) { |
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.
Is this value nullable? It seems strange that areSelectionHandlesAllowed
would ever be null
...
autofocus: true, | ||
editor: _editor, | ||
appendedStylePhases: [ | ||
_spellingAndGrammarPlugin.styler, |
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.
Shouldn't this be added automatically as part of the plugin?
@matthew-carroll This should wait on #2397, because we will have some conflicts. |
[SuperEditor][mobile] Implement spellchecking support. Resolves #2353
Our current spellchecker plugin only works on macOS. This PR adds support for spellchecking on Android and iOS on the spellchecker plugin.
Currently, the desktop implementation is showing the suggestions toolbar whenever the currently selection sits within a misspelled word with available suggestions. However, this wouldn't work for mobile because, on mobile platforms, the suggestions toolbar is displayed when tapping on a misspelled word, but not when dragging the handle over a misspelled word. This creates an implementation problem, because a gesture (tap up) should trigger the suggestions popover and we don't have an extension point for plugins to do that.
To solve this issue, this PR adds the ability for plugins to install a delegate for tap handlers. The plugin now has the opportunity to handle the tap the way it wants, preventing our gesture system for handling them.
The tap delegate shows/hides the suggestions popover by interacting with a
SpellCheckerPopoverController
, a class that exposes the spellchecker popover functionality, which uses aSpellCheckerPopoverDelegate
(usually a document layer) to effectively display the popover toolbar.This PR also add an option to temporarily disable selection handles. This is needed because, when tapping on a misspelled word, we want to select the whole word, but prevent the selection handles from being displayed.
On mobile platforms, the selection color is different when the suggestions popover is visible. Currently, we don't have an option to override the selection color. On our style pipeline, the selection styler is always last, so even if we use a custom style phase, the selection styler always resets the selection color.
To solve that, this PR exposes an
appendedStylePhases
property onSuperEditor
, so we can add style phases that are positioned after the selection styler, adding the ability to override the selection color.This PR adds default implementations for iOS-style and Android-style suggestions popovers.
Screen.Recording.2024-10-28.at.21.27.40.mov
android_spellcheck.webm