-
Notifications
You must be signed in to change notification settings - Fork 255
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][Android] Fix toolbar being hidden when releasing a long press (Resolves #2481) #2552
base: main
Are you sure you want to change the base?
Changes from 1 commit
6d70bca
f9d29da
4031adf
e0f8dd4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
… press (Resolves #2481)
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -430,6 +430,7 @@ class AndroidDocumentTouchInteractor extends StatefulWidget { | |
required this.selection, | ||
this.openKeyboardWhenTappingExistingSelection = true, | ||
required this.openSoftwareKeyboard, | ||
required this.isImeConnected, | ||
required this.scrollController, | ||
required this.fillViewport, | ||
this.contentTapHandlers, | ||
|
@@ -452,6 +453,10 @@ class AndroidDocumentTouchInteractor extends StatefulWidget { | |
/// A callback that should open the software keyboard when invoked. | ||
final VoidCallback openSoftwareKeyboard; | ||
|
||
/// A [ValueListenable] that should notify this widget when the IME connects | ||
/// and disconnects. | ||
final ValueListenable<bool> isImeConnected; | ||
|
||
/// Optional list of handlers that respond to taps on content, e.g., opening | ||
/// a link when the user taps on text with a link attribution. | ||
/// | ||
|
@@ -671,6 +676,9 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt | |
} | ||
|
||
void _onDocumentChange(_) { | ||
// The user might start typing when the toolbar is visible. Hide it. | ||
_controlsController!.hideToolbar(); | ||
|
||
onNextFrame((_) { | ||
_ensureSelectionExtentIsVisible(); | ||
}); | ||
|
@@ -751,7 +759,7 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt | |
if (_isLongPressInProgress) { | ||
_longPressStrategy = null; | ||
_magnifierGlobalOffset.value = null; | ||
_showAndHideEditingControlsAfterTapSelection(didTapOnExistingSelection: false); | ||
_onLongPressEnd(); | ||
return; | ||
} | ||
|
||
|
@@ -985,7 +993,7 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt | |
..hideMagnifier() | ||
..blinkCaret(); | ||
|
||
if (didTapOnExistingSelection && _isKeyboardOpen) { | ||
if (didTapOnExistingSelection && widget.isImeConnected.value) { | ||
// Toggle the toolbar display when the user taps on the collapsed caret, | ||
// or on top of an existing selection. | ||
// | ||
|
@@ -1000,16 +1008,6 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt | |
} | ||
} | ||
|
||
/// Returns `true` if we *think* the software keyboard is currently open, or | ||
/// `false` otherwise. | ||
/// | ||
/// We say "think" because Flutter doesn't report this info to us. Instead, we | ||
/// inspect the bottom insets on the window, and we assume any insets greater than | ||
/// zero means a keyboard is visible. | ||
bool get _isKeyboardOpen { | ||
return MediaQuery.viewInsetsOf(context).bottom > 0; | ||
} | ||
|
||
void _onPanStart(DragStartDetails details) { | ||
// Stop waiting for a long-press to start, if a long press isn't already in-progress. | ||
_tapDownLongPressTimer?.cancel(); | ||
|
@@ -1168,7 +1166,7 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt | |
} | ||
|
||
void _onLongPressEnd() { | ||
_longPressStrategy!.onLongPressEnd(); | ||
_longPressStrategy?.onLongPressEnd(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why change to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was calling |
||
|
||
// Cancel any on-going long-press. | ||
_longPressStrategy = null; | ||
|
@@ -1178,10 +1176,12 @@ class _AndroidDocumentTouchInteractorState extends State<AndroidDocumentTouchInt | |
|
||
_controlsController!.hideMagnifier(); | ||
if (!widget.selection.value!.isCollapsed) { | ||
_controlsController! | ||
..showExpandedHandles() | ||
..showToolbar(); | ||
_controlsController!.showExpandedHandles(); | ||
} | ||
|
||
// Show the toolbar even the selection is collapsed, because the user might | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused about the timing of the user interactions that we're solving with this PR. I thought the PR described the toolbar disappearing after it was already visible. But this code seems to be making the toolbar appear. What's the actual bug here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems we don't need this code. Just need the |
||
// be long-pressing on an empty paragraph or on a whitespace. | ||
_controlsController!.showToolbar(); | ||
} | ||
|
||
void _onCaretDragEnd() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,6 +168,36 @@ void main() { | |
expect(SuperEditorInspector.isMobileMagnifierVisible(), isFalse); | ||
}); | ||
|
||
testWidgetsOnAndroid("shows toolbar when long pressing on an empty paragraph and hides it after typing", | ||
(tester) async { | ||
await tester // | ||
.createDocument() | ||
.withSingleEmptyParagraph() | ||
.pump(); | ||
|
||
// Ensure the toolbar is not visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isFalse); | ||
|
||
// Long press to show the toolbar. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't long press open the magnifier? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On Android, it shouldn't. The magnifier is displayed when dragging a handle. |
||
final gesture = await tester.longPressDownInParagraph('1', 0); | ||
|
||
// Ensure the toolbar is visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isTrue); | ||
|
||
// Release the finger. | ||
await gesture.up(); | ||
await tester.pumpAndSettle(); | ||
|
||
// Ensure the toolbar is still visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isTrue); | ||
|
||
// Type a character to hide the toolbar. | ||
await tester.typeImeText('a'); | ||
|
||
// Ensure the toolbar is not visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isFalse); | ||
}); | ||
|
||
testWidgetsOnAndroid("shows magnifier when dragging expanded handle", (tester) async { | ||
await _pumpSingleParagraphApp(tester); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -132,6 +132,36 @@ void main() { | |
expect(SuperEditorInspector.isMobileMagnifierVisible(), isFalse); | ||
}); | ||
|
||
testWidgetsOnIos("shows toolbar when long pressing on an empty paragraph and hides it after typing", | ||
(tester) async { | ||
await tester // | ||
.createDocument() | ||
.withSingleEmptyParagraph() | ||
.pump(); | ||
|
||
// Ensure the toolbar is not visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isFalse); | ||
|
||
// Long press, this shouldn't show the toolbar. | ||
final gesture = await tester.longPressDownInParagraph('1', 0); | ||
|
||
// Ensure the toolbar is not visible yet. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isFalse); | ||
|
||
// Release the finger. | ||
await gesture.up(); | ||
await tester.pumpAndSettle(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to settle? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems |
||
|
||
// Ensure the toolbar is visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isTrue); | ||
|
||
// Type a character to hide the toolbar. | ||
await tester.typeImeText('a'); | ||
|
||
// Ensure the toolbar is not visible. | ||
expect(SuperEditorInspector.isMobileToolbarVisible(), isFalse); | ||
}); | ||
|
||
testWidgetsOnIos("does not show toolbar upon first tap", (tester) async { | ||
await tester // | ||
.createDocument() | ||
|
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 was "is keyboard open" now it's "is IME connected". These aren't the same thing. Which one do we actually want here?
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.
We actually want to know if the keyboard is open, but the method this is using isn't reliable. It will always report
false
if there is an ancestorScaffold
that resizes to avoid the bottom insets.We already did this change on iOS.
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.
Can you use super_keyboard to get this info?
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.
Updated. O also modified to do the same for iOS.