Merge pull request #157 from singerdmx/ignoreFocus

Pass getTextPosition instead of passing the tap text position to be more flexible; optional ignoreFocus on replaceText
pull/205/head
hyouuu 4 years ago committed by GitHub
commit 0b33c9512c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      lib/widgets/controller.dart
  2. 34
      lib/widgets/editor.dart
  3. 27
      lib/widgets/raw_editor.dart
  4. 73
      lib/widgets/toolbar.dart

@ -11,7 +11,7 @@ import '../models/quill_delta.dart';
import '../utils/diff_delta.dart'; import '../utils/diff_delta.dart';
class QuillController extends ChangeNotifier { class QuillController extends ChangeNotifier {
QuillController({required this.document, required this.selection}); QuillController({required this.document, required this.selection, this.iconSize = 18, this.toolbarHeightFactor = 2});
factory QuillController.basic() { factory QuillController.basic() {
return QuillController( return QuillController(
@ -22,7 +22,11 @@ class QuillController extends ChangeNotifier {
final Document document; final Document document;
TextSelection selection; TextSelection selection;
double iconSize;
double toolbarHeightFactor;
Style toggledStyle = Style(); Style toggledStyle = Style();
bool ignoreFocusOnTextChange = false;
// item1: Document state before [change]. // item1: Document state before [change].
// //
@ -76,7 +80,7 @@ class QuillController extends ChangeNotifier {
bool get hasRedo => document.hasRedo; bool get hasRedo => document.hasRedo;
void replaceText( void replaceText(
int index, int len, Object? data, TextSelection? textSelection) { int index, int len, Object? data, TextSelection? textSelection, {bool ignoreFocus = false}) {
assert(data is String || data is Embeddable); assert(data is String || data is Embeddable);
Delta? delta; Delta? delta;
@ -124,7 +128,12 @@ class QuillController extends ChangeNotifier {
); );
} }
} }
if (ignoreFocus) {
ignoreFocusOnTextChange = true;
}
notifyListeners(); notifyListeners();
ignoreFocusOnTextChange = false;
} }
void formatText(int index, int len, Attribute? attribute) { void formatText(int index, int len, Attribute? attribute) {

@ -176,20 +176,19 @@ class QuillEditor extends StatefulWidget {
final ScrollPhysics? scrollPhysics; final ScrollPhysics? scrollPhysics;
final ValueChanged<String>? onLaunchUrl; final ValueChanged<String>? onLaunchUrl;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function(TapDownDetails details, TextPosition textPosition)? final bool Function(TapDownDetails details, TextPosition Function(Offset offset))? onTapDown;
onTapDown;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function(TapUpDetails details, TextPosition textPosition)? onTapUp; final bool Function(TapUpDetails details, TextPosition Function(Offset offset))? onTapUp;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function(LongPressStartDetails details, TextPosition textPosition)? final bool Function(LongPressStartDetails details, TextPosition Function(Offset offset))? onSingleLongTapStart;
onSingleLongTapStart;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function( final bool Function(LongPressMoveUpdateDetails details, TextPosition Function(Offset offset))? onSingleLongTapMoveUpdate;
LongPressMoveUpdateDetails details, TextPosition textPosition)?
onSingleLongTapMoveUpdate;
// Returns whether gesture is handled // Returns whether gesture is handled
final bool Function(LongPressEndDetails details, TextPosition textPosition)? final bool Function(LongPressEndDetails details, TextPosition Function(Offset offset))? onSingleLongTapEnd;
onSingleLongTapEnd;
final EmbedBuilder embedBuilder; final EmbedBuilder embedBuilder;
@override @override
@ -340,8 +339,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
if (_state.widget.onSingleLongTapMoveUpdate != null) { if (_state.widget.onSingleLongTapMoveUpdate != null) {
final renderEditor = getRenderEditor(); final renderEditor = getRenderEditor();
if (renderEditor != null) { if (renderEditor != null) {
if (_state.widget.onSingleLongTapMoveUpdate!(details, if (_state.widget.onSingleLongTapMoveUpdate!(details, renderEditor.getPositionForOffset)) {
renderEditor.getPositionForOffset(details.globalPosition))) {
return; return;
} }
} }
@ -470,8 +468,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
if (_state.widget.onTapDown != null) { if (_state.widget.onTapDown != null) {
final renderEditor = getRenderEditor(); final renderEditor = getRenderEditor();
if (renderEditor != null) { if (renderEditor != null) {
if (_state.widget.onTapDown!(details, if (_state.widget.onTapDown!(details, renderEditor.getPositionForOffset)) {
renderEditor.getPositionForOffset(details.globalPosition))) {
return; return;
} }
} }
@ -484,8 +481,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
if (_state.widget.onTapUp != null) { if (_state.widget.onTapUp != null) {
final renderEditor = getRenderEditor(); final renderEditor = getRenderEditor();
if (renderEditor != null) { if (renderEditor != null) {
if (_state.widget.onTapUp!(details, if (_state.widget.onTapUp!(details, renderEditor.getPositionForOffset)) {
renderEditor.getPositionForOffset(details.globalPosition))) {
return; return;
} }
} }
@ -527,8 +523,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
if (_state.widget.onSingleLongTapStart != null) { if (_state.widget.onSingleLongTapStart != null) {
final renderEditor = getRenderEditor(); final renderEditor = getRenderEditor();
if (renderEditor != null) { if (renderEditor != null) {
if (_state.widget.onSingleLongTapStart!(details, if (_state.widget.onSingleLongTapStart!(details, renderEditor.getPositionForOffset)) {
renderEditor.getPositionForOffset(details.globalPosition))) {
return; return;
} }
} }
@ -562,8 +557,7 @@ class _QuillEditorSelectionGestureDetectorBuilder
if (_state.widget.onSingleLongTapEnd != null) { if (_state.widget.onSingleLongTapEnd != null) {
final renderEditor = getRenderEditor(); final renderEditor = getRenderEditor();
if (renderEditor != null) { if (renderEditor != null) {
if (_state.widget.onSingleLongTapEnd!(details, if (_state.widget.onSingleLongTapEnd!(details, renderEditor.getPositionForOffset)) {
renderEditor.getPositionForOffset(details.globalPosition))) {
return; return;
} }
} }

@ -675,7 +675,9 @@ class RawEditorState extends EditorState
_clipboardStatus?.addListener(_onChangedClipboardStatus); _clipboardStatus?.addListener(_onChangedClipboardStatus);
widget.controller.addListener(_didChangeTextEditingValue); widget.controller.addListener(() {
_didChangeTextEditingValue(widget.controller.ignoreFocusOnTextChange);
});
_scrollController = widget.scrollController; _scrollController = widget.scrollController;
_scrollController!.addListener(_updateSelectionOverlayForScroll); _scrollController!.addListener(_updateSelectionOverlayForScroll);
@ -883,15 +885,17 @@ class RawEditorState extends EditorState
_selectionOverlay?.markNeedsBuild(); _selectionOverlay?.markNeedsBuild();
} }
void _didChangeTextEditingValue() { void _didChangeTextEditingValue([bool ignoreFocus = false]) {
if (kIsWeb) { if (kIsWeb) {
_onChangeTextEditingValue(); _onChangeTextEditingValue(ignoreFocus);
if (!ignoreFocus) {
requestKeyboard(); requestKeyboard();
}
return; return;
} }
if (_keyboardVisible) { if (ignoreFocus || _keyboardVisible) {
_onChangeTextEditingValue(); _onChangeTextEditingValue(ignoreFocus);
} else { } else {
requestKeyboard(); requestKeyboard();
if (mounted) { if (mounted) {
@ -903,19 +907,20 @@ class RawEditorState extends EditorState
} }
} }
void _onChangeTextEditingValue() { void _onChangeTextEditingValue([bool ignoreCaret = false]) {
_showCaretOnScreen();
updateRemoteValueIfNeeded(); updateRemoteValueIfNeeded();
_cursorCont.startOrStopCursorTimerIfNeeded( if (ignoreCaret) {
_hasFocus, widget.controller.selection); return;
}
_showCaretOnScreen();
_cursorCont.startOrStopCursorTimerIfNeeded(_hasFocus, widget.controller.selection);
if (hasConnection) { if (hasConnection) {
_cursorCont _cursorCont
..stopCursorTimer(resetCharTicks: false) ..stopCursorTimer(resetCharTicks: false)
..startCursorTimer(); ..startCursorTimer();
} }
SchedulerBinding.instance!.addPostFrameCallback( SchedulerBinding.instance!.addPostFrameCallback((_) => _updateOrDisposeSelectionOverlayIfNeeded());
(_) => _updateOrDisposeSelectionOverlayIfNeeded());
if (mounted) { if (mounted) {
setState(() { setState(() {
// Use widget.controller.value in build() // Use widget.controller.value in build()

@ -14,9 +14,6 @@ import '../models/documents/style.dart';
import '../utils/color.dart'; import '../utils/color.dart';
import 'controller.dart'; import 'controller.dart';
double iconSize = 18;
double kToolbarHeight = iconSize * 2;
typedef OnImagePickCallback = Future<String> Function(File file); typedef OnImagePickCallback = Future<String> Function(File file);
typedef ImagePickImpl = Future<String?> Function(ImageSource source); typedef ImagePickImpl = Future<String?> Function(ImageSource source);
@ -37,10 +34,10 @@ class InsertEmbedButton extends StatelessWidget {
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: controller.iconSize * 1.77,
icon: Icon( icon: Icon(
icon, icon,
size: iconSize, size: controller.iconSize,
color: Theme.of(context).iconTheme.color, color: Theme.of(context).iconTheme.color,
), ),
fillColor: fillColor ?? Theme.of(context).canvasColor, fillColor: fillColor ?? Theme.of(context).canvasColor,
@ -101,10 +98,10 @@ class _LinkStyleButtonState extends State<LinkStyleButton> {
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: widget.controller.iconSize * 1.77,
icon: Icon( icon: Icon(
widget.icon ?? Icons.link, widget.icon ?? Icons.link,
size: iconSize, size: widget.controller.iconSize,
color: isEnabled ? theme.iconTheme.color : theme.disabledColor, color: isEnabled ? theme.iconTheme.color : theme.disabledColor,
), ),
fillColor: Theme.of(context).canvasColor, fillColor: Theme.of(context).canvasColor,
@ -372,8 +369,8 @@ Widget defaultToggleStyleButtonBuilder(
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: 18 * 1.77,
icon: Icon(icon, size: iconSize, color: iconColor), icon: Icon(icon, size: 18, color: iconColor),
fillColor: fill, fillColor: fill,
onPressed: onPressed, onPressed: onPressed,
); );
@ -435,12 +432,12 @@ class _SelectHeaderStyleButtonState extends State<SelectHeaderStyleButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _selectHeadingStyleButtonBuilder(context, _value, _selectAttribute); return _selectHeadingStyleButtonBuilder(context, _value, _selectAttribute, widget.controller.iconSize);
} }
} }
Widget _selectHeadingStyleButtonBuilder(BuildContext context, Attribute? value, Widget _selectHeadingStyleButtonBuilder(BuildContext context, Attribute? value,
ValueChanged<Attribute?> onSelected) { ValueChanged<Attribute?> onSelected, double iconSize) {
final _valueToText = <Attribute, String>{ final _valueToText = <Attribute, String>{
Attribute.header: 'N', Attribute.header: 'N',
Attribute.h1: 'H1', Attribute.h1: 'H1',
@ -532,12 +529,12 @@ class _ImageButtonState extends State<ImageButton> {
return QuillIconButton( return QuillIconButton(
icon: Icon( icon: Icon(
widget.icon, widget.icon,
size: iconSize, size: widget.controller.iconSize,
color: theme.iconTheme.color, color: theme.iconTheme.color,
), ),
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: widget.controller.iconSize * 1.77,
fillColor: theme.canvasColor, fillColor: theme.canvasColor,
onPressed: _handleImageButtonTap, onPressed: _handleImageButtonTap,
); );
@ -708,9 +705,9 @@ class _ColorButtonState extends State<ColorButton> {
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: widget.controller.iconSize * 1.77,
icon: Icon(widget.icon, icon: Icon(widget.icon,
size: iconSize, size: widget.controller.iconSize,
color: widget.background ? iconColorBackground : iconColor), color: widget.background ? iconColorBackground : iconColor),
fillColor: widget.background ? fillColorBackground : fillColor, fillColor: widget.background ? fillColorBackground : fillColor,
onPressed: _showColorPicker, onPressed: _showColorPicker,
@ -776,8 +773,8 @@ class _HistoryButtonState extends State<HistoryButton> {
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: widget.controller.iconSize * 1.77,
icon: Icon(widget.icon, size: iconSize, color: _iconColor), icon: Icon(widget.icon, size: widget.controller.iconSize, color: _iconColor),
fillColor: fillColor, fillColor: fillColor,
onPressed: _changeHistory, onPressed: _changeHistory,
); );
@ -841,8 +838,8 @@ class _IndentButtonState extends State<IndentButton> {
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: widget.controller.iconSize * 1.77,
icon: Icon(widget.icon, size: iconSize, color: iconColor), icon: Icon(widget.icon, size: widget.controller.iconSize, color: iconColor),
fillColor: fillColor, fillColor: fillColor,
onPressed: () { onPressed: () {
final indent = widget.controller final indent = widget.controller
@ -895,8 +892,8 @@ class _ClearFormatButtonState extends State<ClearFormatButton> {
return QuillIconButton( return QuillIconButton(
highlightElevation: 0, highlightElevation: 0,
hoverElevation: 0, hoverElevation: 0,
size: iconSize * 1.77, size: widget.controller.iconSize * 1.77,
icon: Icon(widget.icon, size: iconSize, color: iconColor), icon: Icon(widget.icon, size: widget.controller.iconSize, color: iconColor),
fillColor: fillColor, fillColor: fillColor,
onPressed: () { onPressed: () {
for (final k for (final k
@ -908,7 +905,7 @@ class _ClearFormatButtonState extends State<ClearFormatButton> {
} }
class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
const QuillToolbar({required this.children, Key? key}) : super(key: key); const QuillToolbar({required this.children, this.toolBarHeight = 36, Key? key}) : super(key: key);
factory QuillToolbar.basic({ factory QuillToolbar.basic({
required QuillController controller, required QuillController controller,
@ -933,8 +930,9 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
OnImagePickCallback? onImagePickCallback, OnImagePickCallback? onImagePickCallback,
Key? key, Key? key,
}) { }) {
iconSize = toolbarIconSize; controller.iconSize = toolbarIconSize;
return QuillToolbar(key: key, children: [
return QuillToolbar(key: key, toolBarHeight: toolbarIconSize * controller.toolbarHeightFactor, children: [
Visibility( Visibility(
visible: showHistory, visible: showHistory,
child: HistoryButton( child: HistoryButton(
@ -1034,12 +1032,8 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
), ),
), ),
Visibility( Visibility(
visible: showHeaderStyle, visible: showHeaderStyle, child: VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400)),
child: VerticalDivider( Visibility(visible: showHeaderStyle, child: SelectHeaderStyleButton(controller: controller)),
indent: 12, endIndent: 12, color: Colors.grey.shade400)),
Visibility(
visible: showHeaderStyle,
child: SelectHeaderStyleButton(controller: controller)),
VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400), VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400),
Visibility( Visibility(
visible: showListNumbers, visible: showListNumbers,
@ -1074,12 +1068,8 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
), ),
), ),
Visibility( Visibility(
visible: !showListNumbers && visible: !showListNumbers && !showListBullets && !showListCheck && !showCodeBlock,
!showListBullets && child: VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400)),
!showListCheck &&
!showCodeBlock,
child: VerticalDivider(
indent: 12, endIndent: 12, color: Colors.grey.shade400)),
Visibility( Visibility(
visible: showQuote, visible: showQuote,
child: ToggleStyleButton( child: ToggleStyleButton(
@ -1104,12 +1094,8 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
isIncrease: false, isIncrease: false,
), ),
), ),
Visibility( Visibility(visible: showQuote, child: VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400)),
visible: showQuote, Visibility(visible: showLink, child: LinkStyleButton(controller: controller)),
child: VerticalDivider(
indent: 12, endIndent: 12, color: Colors.grey.shade400)),
Visibility(
visible: showLink, child: LinkStyleButton(controller: controller)),
Visibility( Visibility(
visible: showHorizontalRule, visible: showHorizontalRule,
child: InsertEmbedButton( child: InsertEmbedButton(
@ -1121,12 +1107,13 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget {
} }
final List<Widget> children; final List<Widget> children;
final double toolBarHeight;
@override @override
_QuillToolbarState createState() => _QuillToolbarState(); _QuillToolbarState createState() => _QuillToolbarState();
@override @override
Size get preferredSize => Size.fromHeight(kToolbarHeight); Size get preferredSize => Size.fromHeight(toolBarHeight);
} }
class _QuillToolbarState extends State<QuillToolbar> { class _QuillToolbarState extends State<QuillToolbar> {

Loading…
Cancel
Save