Feature: add selection completed callback (#576)

* Implement selection completion handlers

* Selection completion customizable via callback
pull/577/head
frmatthew 3 years ago committed by GitHub
parent 1ce8188c47
commit 6c39fcbc54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      lib/src/widgets/controller.dart
  2. 35
      lib/src/widgets/editor.dart
  3. 12
      lib/src/widgets/raw_editor.dart

@ -20,6 +20,7 @@ class QuillController extends ChangeNotifier {
bool keepStyleOnNewLine = false,
this.onReplaceText,
this.onDelete,
this.onSelectionCompleted,
}) : _selection = selection,
_keepStyleOnNewLine = keepStyleOnNewLine;
@ -48,6 +49,8 @@ class QuillController extends ChangeNotifier {
/// Custom delete handler
DeleteCallback? onDelete;
void Function()? onSelectionCompleted;
/// Store any styles attribute that got toggled by the tap of a button
/// and that has not been applied yet.
/// It gets reset after each format action within the [document].

@ -712,11 +712,14 @@ class _QuillEditorSelectionGestureDetectorBuilder
// If `Shift` key is pressed then
// extend current selection instead.
if (isShiftClick(details.kind)) {
getRenderEditor()!.extendSelection(details.globalPosition,
cause: SelectionChangedCause.tap);
getRenderEditor()!
..extendSelection(details.globalPosition,
cause: SelectionChangedCause.tap)
..onSelectionCompleted();
} else {
getRenderEditor()!
.selectPosition(cause: SelectionChangedCause.tap);
..selectPosition(cause: SelectionChangedCause.tap)
..onSelectionCompleted();
}
break;
@ -725,7 +728,9 @@ class _QuillEditorSelectionGestureDetectorBuilder
// On macOS/iOS/iPadOS a touch tap places the cursor at the edge
// of the word.
try {
getRenderEditor()!.selectWordEdge(SelectionChangedCause.tap);
getRenderEditor()!
..selectWordEdge(SelectionChangedCause.tap)
..onSelectionCompleted();
} finally {
break;
}
@ -736,7 +741,9 @@ class _QuillEditorSelectionGestureDetectorBuilder
case TargetPlatform.linux:
case TargetPlatform.windows:
try {
getRenderEditor()!.selectPosition(cause: SelectionChangedCause.tap);
getRenderEditor()!
..selectPosition(cause: SelectionChangedCause.tap)
..onSelectionCompleted();
} finally {
break;
}
@ -788,6 +795,10 @@ class _QuillEditorSelectionGestureDetectorBuilder
details, renderEditor.getPositionForOffset)) {
return;
}
if (delegate.getSelectionEnabled()) {
renderEditor.onSelectionCompleted();
}
}
}
super.onSingleLongTapEnd(details);
@ -801,6 +812,17 @@ class _QuillEditorSelectionGestureDetectorBuilder
typedef TextSelectionChangedHandler = void Function(
TextSelection selection, SelectionChangedCause cause);
/// Signature for the callback that reports when a selection action is actually
/// completed and ratified. Completion is defined as when the user input has
/// concluded for an entire selection action. For simple taps and keyboard input
/// events that change the selection, this callback is invoked immediately
/// following the TextSelectionChangedHandler. For long taps, the selection is
/// considered complete at the up event of a long tap. For drag selections, the
/// selection completes once the drag/pan event ends or is interrupted.
///
/// Used by [RenderEditor.onSelectionCompleted].
typedef TextSelectionCompletedHandler = void Function();
// The padding applied to text field. Used to determine the bounds when
// moving the floating cursor.
const EdgeInsets _kFloatingCursorAddedMargin = EdgeInsets.fromLTRB(4, 4, 4, 5);
@ -827,6 +849,7 @@ class RenderEditor extends RenderEditableContainerBox
required EdgeInsetsGeometry padding,
required CursorCont cursorController,
required this.onSelectionChanged,
required this.onSelectionCompleted,
required double scrollBottomInset,
required this.floatingCursorDisabled,
ViewportOffset? offset,
@ -859,6 +882,7 @@ class RenderEditor extends RenderEditableContainerBox
/// Called when the selection changes.
TextSelectionChangedHandler onSelectionChanged;
TextSelectionCompletedHandler onSelectionCompleted;
final ValueNotifier<bool> _selectionStartInViewport =
ValueNotifier<bool>(true);
@ -1067,6 +1091,7 @@ class RenderEditor extends RenderEditableContainerBox
void handleDragEnd(DragEndDetails details) {
_isDragging = false;
onSelectionCompleted();
}
@override

@ -288,6 +288,7 @@ class RawEditorState extends EditorState
startHandleLayerLink: _startHandleLayerLink,
endHandleLayerLink: _endHandleLayerLink,
onSelectionChanged: _handleSelectionChanged,
onSelectionCompleted: _handleSelectionCompleted,
scrollBottomInset: widget.scrollBottomInset,
padding: widget.padding,
maxContentWidth: widget.maxContentWidth,
@ -318,6 +319,7 @@ class RawEditorState extends EditorState
startHandleLayerLink: _startHandleLayerLink,
endHandleLayerLink: _endHandleLayerLink,
onSelectionChanged: _handleSelectionChanged,
onSelectionCompleted: _handleSelectionCompleted,
scrollBottomInset: widget.scrollBottomInset,
padding: widget.padding,
maxContentWidth: widget.maxContentWidth,
@ -374,6 +376,10 @@ class RawEditorState extends EditorState
}
}
void _handleSelectionCompleted() {
widget.controller.onSelectionCompleted?.call();
}
/// Updates the checkbox positioned at [offset] in document
/// by changing its attribute according to [value].
void _handleCheckboxTap(int offset, bool value) {
@ -793,6 +799,9 @@ class RawEditorState extends EditorState
}
textEditingValue = value;
userUpdateTextEditingValue(value, cause);
// keyboard and text input force a selection completion
_handleSelectionCompleted();
}
@override
@ -914,6 +923,7 @@ class _Editor extends MultiChildRenderObjectWidget {
required this.startHandleLayerLink,
required this.endHandleLayerLink,
required this.onSelectionChanged,
required this.onSelectionCompleted,
required this.scrollBottomInset,
required this.cursorController,
required this.floatingCursorDisabled,
@ -930,6 +940,7 @@ class _Editor extends MultiChildRenderObjectWidget {
final LayerLink startHandleLayerLink;
final LayerLink endHandleLayerLink;
final TextSelectionChangedHandler onSelectionChanged;
final TextSelectionCompletedHandler onSelectionCompleted;
final double scrollBottomInset;
final EdgeInsetsGeometry padding;
final double? maxContentWidth;
@ -947,6 +958,7 @@ class _Editor extends MultiChildRenderObjectWidget {
startHandleLayerLink: startHandleLayerLink,
endHandleLayerLink: endHandleLayerLink,
onSelectionChanged: onSelectionChanged,
onSelectionCompleted: onSelectionCompleted,
cursorController: cursorController,
padding: padding,
maxContentWidth: maxContentWidth,

Loading…
Cancel
Save