Enable tap exclusion for inline widgets in SuperReader#3
Conversation
Introduces ContentTapExclusion to allow subtree widgets to bypass SuperReader's tap gesture handling, enabling direct tap handling for inline widgets such as placeholders. Updates gesture recognizers to consult a pointer predicate, preventing tap interception when over inline placeholders. Refactors hit testing in sliver hybrid stack for correct child hit aggregation.
There was a problem hiding this comment.
Pull Request Overview
This PR enables inline widgets (such as placeholders) in SuperReader to handle tap gestures independently without interference from SuperReader's gesture recognizers. The implementation adds exclusion mechanisms and platform-specific logic to detect when taps occur over placeholders and allow them to be handled by the inline widgets directly.
- Added
ContentTapExclusionwidget and render object to mark subtrees for exclusion from SuperReader's tap handling - Enhanced
TapSequenceGestureRecognizerwithisPointerAllowedPredicatefor custom tap exclusion logic - Implemented platform-specific pointer allowance checking in Android, iOS, and mouse interactors
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| super_editor.dart | Exports the new content tap exclusion widget |
| content_tap_exclusion.dart | Implements the exclusion widget and render object |
| multi_tap_gesture.dart | Adds predicate support to gesture recognizer |
| read_only_document_*_interactor.dart | Implements placeholder detection logic for each platform |
| sliver_hybrid_stack.dart | Improves hit testing aggregation logic |
| text.dart | Contains formatting changes to class properties |
| bool hitTest(BoxHitTestResult result, {required Offset position}) { | ||
| final hit = super.hitTest(result, position: position); | ||
| return hit; | ||
| } |
There was a problem hiding this comment.
The hitTest method doesn't add any functionality beyond calling super.hitTest(). Consider adding documentation to explain why this override is necessary or remove it if it serves no purpose.
| } |
| if (node is! TextNode) { | ||
| return true; | ||
| } | ||
| final offset = (docPosition.nodePosition as TextPosition).offset; |
There was a problem hiding this comment.
The check uses TextPosition but should use TextNodePosition to match the TextNode type check. This inconsistency could cause incorrect type checking.
| final offset = (docPosition.nodePosition as TextPosition).offset; | |
| if (docPosition.nodePosition is! TextNodePosition) { | |
| return true; | |
| } | |
| final node = widget.readerContext.document.getNodeById(docPosition.nodeId); | |
| if (node is! TextNode) { | |
| return true; | |
| } | |
| final offset = (docPosition.nodePosition as TextNodePosition).offset; |
| if (docPosition == null) { | ||
| return true; | ||
| } | ||
| if (docPosition.nodePosition is! TextPosition) { |
There was a problem hiding this comment.
The check uses TextPosition but should use TextNodePosition to match the TextNode type check. This inconsistency could cause incorrect type checking.
| if (docPosition.nodePosition is! TextPosition) { | |
| if (docPosition.nodePosition is! TextNodePosition) { |
| if (node is! TextNode) { | ||
| return true; | ||
| } | ||
| final offset = (docPosition.nodePosition as TextPosition).offset; |
There was a problem hiding this comment.
The cast to TextPosition should be TextNodePosition to match the type returned by getDocumentPositionNearestToOffset for text nodes.
| final offset = (docPosition.nodePosition as TextPosition).offset; | |
| final offset = (docPosition.nodePosition as TextNodePosition).offset; |
| if (node is! TextNode) { | ||
| return true; | ||
| } | ||
| final offset = (docPosition.nodePosition as TextPosition).offset; |
There was a problem hiding this comment.
The cast to TextPosition should be TextNodePosition to match the type returned by getDocumentPositionNearestToOffset for text nodes.
| final offset = (docPosition.nodePosition as TextPosition).offset; | |
| final offset = (docPosition.nodePosition as TextNodePosition).offset; |
| if (node is! TextNode) { | ||
| return true; | ||
| } | ||
| final offset = (docPosition.nodePosition as TextPosition).offset; |
There was a problem hiding this comment.
The cast to TextPosition should be TextNodePosition to match the type returned by getDocumentPositionNearestToOffset for text nodes.
| final offset = (docPosition.nodePosition as TextPosition).offset; | |
| final offset = (docPosition.nodePosition as TextNodePosition).offset; |
Moved tap exclusion logic for inline placeholders to a shared function, isTapAllowedAtDocumentPosition, in content_tap_exclusion.dart. Updated Android, iOS, and mouse interactors to use this function for consistent tap handling over inline widgets.
Introduces a markdown document explaining the rationale, implementation, and usage of tap exclusion for inline widgets in SuperEditor. Details the ContentTapExclusion marker, shared tap-allowance utility, and updates to interactors for consistent tap handling across platforms.
This pull request introduces improvements to gesture handling in the SuperEditor package, specifically enabling inline widgets (such as placeholders) to independently handle tap gestures without interference from SuperReader's gesture recognizers. The changes include a new exclusion widget, updates to gesture recognizers, and platform-specific logic to allow inline widgets to receive taps directly.
Gesture Handling Enhancements
ContentTapExclusionwidget and its render object (RenderContentTapExclusion) to mark subtrees that should be excluded from SuperReader's internal tap gesture handling, enabling inline widgets to handle taps independently. (super_editor/lib/src/infrastructure/content_tap_exclusion.dart)TapSequenceGestureRecognizerto support anisPointerAllowedPredicate, allowing custom logic to decide whether a tap gesture should be handled by SuperReader or passed to child widgets. (super_editor/lib/src/infrastructure/multi_tap_gesture.dart) [1] [2]Platform-Specific Tap Handling
_isPointerAllowedForTaplogic in Android, iOS, and mouse document interactors to check if a tap occurs over a placeholder; if so, the tap is excluded from SuperReader handling and passed to the inline widget. (super_editor/lib/src/super_reader/read_only_document_android_touch_interactor.dart,super_editor/lib/src/super_reader/read_only_document_ios_touch_interactor.dart,super_editor/lib/src/super_reader/read_only_document_mouse_interactor.dart) [1] [2] [3]API and Export Updates
content_tap_exclusion.dartfrom the mainsuper_editor.dartlibrary, making the exclusion widget available for public use. (super_editor/lib/super_editor.dart)Hit Testing Improvements
_RenderSliverHybridStackto correctly aggregate hit results from child render objects, improving gesture detection reliability for complex layouts. (super_editor/lib/src/infrastructure/sliver_hybrid_stack.dart) [1] [2] [3]These changes collectively improve the flexibility and reliability of tap gesture handling in SuperEditor, especially for documents containing interactive inline widgets.