|
|
|
@ -119,6 +119,7 @@ class QuillEditor extends StatefulWidget { |
|
|
|
|
required this.focusNode, |
|
|
|
|
required this.scrollController, |
|
|
|
|
required this.scrollable, |
|
|
|
|
required this.scrollBottomInset, |
|
|
|
|
required this.padding, |
|
|
|
|
required this.autoFocus, |
|
|
|
|
required this.readOnly, |
|
|
|
@ -133,7 +134,12 @@ class QuillEditor extends StatefulWidget { |
|
|
|
|
this.keyboardAppearance = Brightness.light, |
|
|
|
|
this.scrollPhysics, |
|
|
|
|
this.onLaunchUrl, |
|
|
|
|
this.embedBuilder = _defaultEmbedBuilder, |
|
|
|
|
this.onTapDown, |
|
|
|
|
this.onTapUp, |
|
|
|
|
this.onSingleLongTapStart, |
|
|
|
|
this.onSingleLongTapMoveUpdate, |
|
|
|
|
this.onSingleLongTapEnd, |
|
|
|
|
this.embedBuilder = _defaultEmbedBuilder |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
factory QuillEditor.basic({ |
|
|
|
@ -148,6 +154,7 @@ class QuillEditor extends StatefulWidget { |
|
|
|
|
autoFocus: true, |
|
|
|
|
readOnly: readOnly, |
|
|
|
|
expands: false, |
|
|
|
|
scrollBottomInset: 0, |
|
|
|
|
padding: EdgeInsets.zero); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -155,6 +162,7 @@ class QuillEditor extends StatefulWidget { |
|
|
|
|
final FocusNode focusNode; |
|
|
|
|
final ScrollController scrollController; |
|
|
|
|
final bool scrollable; |
|
|
|
|
final double scrollBottomInset; |
|
|
|
|
final EdgeInsetsGeometry padding; |
|
|
|
|
final bool autoFocus; |
|
|
|
|
final bool? showCursor; |
|
|
|
@ -169,6 +177,16 @@ class QuillEditor extends StatefulWidget { |
|
|
|
|
final Brightness keyboardAppearance; |
|
|
|
|
final ScrollPhysics? scrollPhysics; |
|
|
|
|
final ValueChanged<String>? onLaunchUrl; |
|
|
|
|
// Returns whether gesture is handled |
|
|
|
|
final bool Function(TapDownDetails details, TextPosition textPosition)? onTapDown; |
|
|
|
|
// Returns whether gesture is handled |
|
|
|
|
final bool Function(TapUpDetails details, TextPosition textPosition)? onTapUp; |
|
|
|
|
// Returns whether gesture is handled |
|
|
|
|
final bool Function(LongPressStartDetails details, TextPosition textPosition)? onSingleLongTapStart; |
|
|
|
|
// Returns whether gesture is handled |
|
|
|
|
final bool Function(LongPressMoveUpdateDetails details, TextPosition textPosition)? onSingleLongTapMoveUpdate; |
|
|
|
|
// Returns whether gesture is handled |
|
|
|
|
final bool Function(LongPressEndDetails details, TextPosition textPosition)? onSingleLongTapEnd; |
|
|
|
|
final EmbedBuilder embedBuilder; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@ -239,6 +257,7 @@ class _QuillEditorState extends State<QuillEditor> |
|
|
|
|
widget.focusNode, |
|
|
|
|
widget.scrollController, |
|
|
|
|
widget.scrollable, |
|
|
|
|
widget.scrollBottomInset, |
|
|
|
|
widget.padding, |
|
|
|
|
widget.readOnly, |
|
|
|
|
widget.placeholder, |
|
|
|
@ -315,6 +334,15 @@ class _QuillEditorSelectionGestureDetectorBuilder |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { |
|
|
|
|
if (_state.widget.onSingleLongTapMoveUpdate != null) { |
|
|
|
|
final renderEditor = getRenderEditor(); |
|
|
|
|
if (renderEditor != null) { |
|
|
|
|
if (_state.widget.onSingleLongTapMoveUpdate!(details, renderEditor.getPositionForOffset(details.globalPosition) |
|
|
|
|
)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!delegate.getSelectionEnabled()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
@ -434,8 +462,30 @@ class _QuillEditorSelectionGestureDetectorBuilder |
|
|
|
|
await launch(url); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void onTapDown(TapDownDetails details) { |
|
|
|
|
if (_state.widget.onTapDown != null) { |
|
|
|
|
final renderEditor = getRenderEditor(); |
|
|
|
|
if (renderEditor != null) { |
|
|
|
|
if (_state.widget.onTapDown!(details, renderEditor.getPositionForOffset(details.globalPosition))) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
super.onTapDown(details); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void onSingleTapUp(TapUpDetails details) { |
|
|
|
|
if (_state.widget.onTapUp != null) { |
|
|
|
|
final renderEditor = getRenderEditor(); |
|
|
|
|
if (renderEditor != null) { |
|
|
|
|
if (_state.widget.onTapUp!(details, renderEditor.getPositionForOffset(details.globalPosition))) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getEditor()!.hideToolbar(); |
|
|
|
|
|
|
|
|
|
final positionSelected = _onTapping(details); |
|
|
|
@ -469,6 +519,15 @@ class _QuillEditorSelectionGestureDetectorBuilder |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void onSingleLongTapStart(LongPressStartDetails details) { |
|
|
|
|
if (_state.widget.onSingleLongTapStart != null) { |
|
|
|
|
final renderEditor = getRenderEditor(); |
|
|
|
|
if (renderEditor != null) { |
|
|
|
|
if (_state.widget.onSingleLongTapStart!(details, renderEditor.getPositionForOffset(details.globalPosition))) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (delegate.getSelectionEnabled()) { |
|
|
|
|
switch (Theme.of(_state.context).platform) { |
|
|
|
|
case TargetPlatform.iOS: |
|
|
|
@ -491,6 +550,19 @@ class _QuillEditorSelectionGestureDetectorBuilder |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
void onSingleLongTapEnd(LongPressEndDetails details) { |
|
|
|
|
if (_state.widget.onSingleLongTapEnd != null) { |
|
|
|
|
final renderEditor = getRenderEditor(); |
|
|
|
|
if (renderEditor != null) { |
|
|
|
|
if (_state.widget.onSingleLongTapEnd!(details, renderEditor.getPositionForOffset(details.globalPosition))) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
super.onSingleLongTapEnd(details); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef TextSelectionChangedHandler = void Function( |
|
|
|
@ -501,6 +573,7 @@ class RenderEditor extends RenderEditableContainerBox |
|
|
|
|
RenderEditor( |
|
|
|
|
List<RenderEditableBox>? children, |
|
|
|
|
TextDirection textDirection, |
|
|
|
|
double scrollBottomInset, |
|
|
|
|
EdgeInsetsGeometry padding, |
|
|
|
|
this.document, |
|
|
|
|
this.selection, |
|
|
|
@ -513,6 +586,7 @@ class RenderEditor extends RenderEditableContainerBox |
|
|
|
|
children, |
|
|
|
|
document.root, |
|
|
|
|
textDirection, |
|
|
|
|
scrollBottomInset, |
|
|
|
|
padding, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
@ -571,6 +645,14 @@ class RenderEditor extends RenderEditableContainerBox |
|
|
|
|
markNeedsPaint(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void setScrollBottomInset(double value) { |
|
|
|
|
if (scrollBottomInset == value) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
scrollBottomInset = value; |
|
|
|
|
markNeedsPaint(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
List<TextSelectionPoint> getEndpointsForSelection( |
|
|
|
|
TextSelection textSelection) { |
|
|
|
@ -843,8 +925,9 @@ class RenderEditor extends RenderEditableContainerBox |
|
|
|
|
child.preferredLineHeight(TextPosition( |
|
|
|
|
offset: selection.extentOffset - child.getContainer().offset)) - |
|
|
|
|
kMargin + |
|
|
|
|
offsetInViewport; |
|
|
|
|
final caretBottom = endpoint.point.dy + kMargin + offsetInViewport; |
|
|
|
|
offsetInViewport + |
|
|
|
|
scrollBottomInset; |
|
|
|
|
final caretBottom = endpoint.point.dy + kMargin + offsetInViewport + scrollBottomInset; |
|
|
|
|
double? dy; |
|
|
|
|
if (caretTop < scrollOffset) { |
|
|
|
|
dy = caretTop; |
|
|
|
@ -871,6 +954,7 @@ class RenderEditableContainerBox extends RenderBox |
|
|
|
|
List<RenderEditableBox>? children, |
|
|
|
|
this._container, |
|
|
|
|
this.textDirection, |
|
|
|
|
this.scrollBottomInset, |
|
|
|
|
this._padding, |
|
|
|
|
) : assert(_padding.isNonNegative) { |
|
|
|
|
addAll(children); |
|
|
|
@ -879,6 +963,7 @@ class RenderEditableContainerBox extends RenderBox |
|
|
|
|
container_node.Container _container; |
|
|
|
|
TextDirection textDirection; |
|
|
|
|
EdgeInsetsGeometry _padding; |
|
|
|
|
double scrollBottomInset; |
|
|
|
|
EdgeInsets? _resolvedPadding; |
|
|
|
|
|
|
|
|
|
container_node.Container getContainer() { |
|
|
|
|