diff --git a/lib/widgets/controller.dart b/lib/widgets/controller.dart index 3bd66b6f..1b7a8180 100644 --- a/lib/widgets/controller.dart +++ b/lib/widgets/controller.dart @@ -11,7 +11,7 @@ import '../models/quill_delta.dart'; import '../utils/diff_delta.dart'; 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() { return QuillController( @@ -22,7 +22,11 @@ class QuillController extends ChangeNotifier { final Document document; TextSelection selection; + double iconSize; + double toolbarHeightFactor; + Style toggledStyle = Style(); + bool ignoreFocusOnTextChange = false; // item1: Document state before [change]. // @@ -76,7 +80,7 @@ class QuillController extends ChangeNotifier { bool get hasRedo => document.hasRedo; 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); Delta? delta; @@ -124,7 +128,12 @@ class QuillController extends ChangeNotifier { ); } } + + if (ignoreFocus) { + ignoreFocusOnTextChange = true; + } notifyListeners(); + ignoreFocusOnTextChange = false; } void formatText(int index, int len, Attribute? attribute) { diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index 5be0dd24..42fd769c 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -176,20 +176,19 @@ class QuillEditor extends StatefulWidget { final ScrollPhysics? scrollPhysics; final ValueChanged? onLaunchUrl; // Returns whether gesture is handled - final bool Function(TapDownDetails details, TextPosition textPosition)? - onTapDown; + final bool Function(TapDownDetails details, TextPosition Function(Offset offset))? onTapDown; + // 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 - final bool Function(LongPressStartDetails details, TextPosition textPosition)? - onSingleLongTapStart; + final bool Function(LongPressStartDetails details, TextPosition Function(Offset offset))? onSingleLongTapStart; + // Returns whether gesture is handled - final bool Function( - LongPressMoveUpdateDetails details, TextPosition textPosition)? - onSingleLongTapMoveUpdate; + final bool Function(LongPressMoveUpdateDetails details, TextPosition Function(Offset offset))? onSingleLongTapMoveUpdate; // Returns whether gesture is handled - final bool Function(LongPressEndDetails details, TextPosition textPosition)? - onSingleLongTapEnd; + final bool Function(LongPressEndDetails details, TextPosition Function(Offset offset))? onSingleLongTapEnd; + final EmbedBuilder embedBuilder; @override @@ -340,8 +339,7 @@ class _QuillEditorSelectionGestureDetectorBuilder if (_state.widget.onSingleLongTapMoveUpdate != null) { final renderEditor = getRenderEditor(); if (renderEditor != null) { - if (_state.widget.onSingleLongTapMoveUpdate!(details, - renderEditor.getPositionForOffset(details.globalPosition))) { + if (_state.widget.onSingleLongTapMoveUpdate!(details, renderEditor.getPositionForOffset)) { return; } } @@ -470,8 +468,7 @@ class _QuillEditorSelectionGestureDetectorBuilder if (_state.widget.onTapDown != null) { final renderEditor = getRenderEditor(); if (renderEditor != null) { - if (_state.widget.onTapDown!(details, - renderEditor.getPositionForOffset(details.globalPosition))) { + if (_state.widget.onTapDown!(details, renderEditor.getPositionForOffset)) { return; } } @@ -484,8 +481,7 @@ class _QuillEditorSelectionGestureDetectorBuilder if (_state.widget.onTapUp != null) { final renderEditor = getRenderEditor(); if (renderEditor != null) { - if (_state.widget.onTapUp!(details, - renderEditor.getPositionForOffset(details.globalPosition))) { + if (_state.widget.onTapUp!(details, renderEditor.getPositionForOffset)) { return; } } @@ -527,8 +523,7 @@ class _QuillEditorSelectionGestureDetectorBuilder if (_state.widget.onSingleLongTapStart != null) { final renderEditor = getRenderEditor(); if (renderEditor != null) { - if (_state.widget.onSingleLongTapStart!(details, - renderEditor.getPositionForOffset(details.globalPosition))) { + if (_state.widget.onSingleLongTapStart!(details, renderEditor.getPositionForOffset)) { return; } } @@ -562,8 +557,7 @@ class _QuillEditorSelectionGestureDetectorBuilder if (_state.widget.onSingleLongTapEnd != null) { final renderEditor = getRenderEditor(); if (renderEditor != null) { - if (_state.widget.onSingleLongTapEnd!(details, - renderEditor.getPositionForOffset(details.globalPosition))) { + if (_state.widget.onSingleLongTapEnd!(details, renderEditor.getPositionForOffset)) { return; } } diff --git a/lib/widgets/raw_editor.dart b/lib/widgets/raw_editor.dart index 1627ebc3..7114e670 100644 --- a/lib/widgets/raw_editor.dart +++ b/lib/widgets/raw_editor.dart @@ -675,7 +675,9 @@ class RawEditorState extends EditorState _clipboardStatus?.addListener(_onChangedClipboardStatus); - widget.controller.addListener(_didChangeTextEditingValue); + widget.controller.addListener(() { + _didChangeTextEditingValue(widget.controller.ignoreFocusOnTextChange); + }); _scrollController = widget.scrollController; _scrollController!.addListener(_updateSelectionOverlayForScroll); @@ -883,15 +885,17 @@ class RawEditorState extends EditorState _selectionOverlay?.markNeedsBuild(); } - void _didChangeTextEditingValue() { + void _didChangeTextEditingValue([bool ignoreFocus = false]) { if (kIsWeb) { - _onChangeTextEditingValue(); - requestKeyboard(); + _onChangeTextEditingValue(ignoreFocus); + if (!ignoreFocus) { + requestKeyboard(); + } return; } - if (_keyboardVisible) { - _onChangeTextEditingValue(); + if (ignoreFocus || _keyboardVisible) { + _onChangeTextEditingValue(ignoreFocus); } else { requestKeyboard(); if (mounted) { @@ -903,19 +907,20 @@ class RawEditorState extends EditorState } } - void _onChangeTextEditingValue() { - _showCaretOnScreen(); + void _onChangeTextEditingValue([bool ignoreCaret = false]) { updateRemoteValueIfNeeded(); - _cursorCont.startOrStopCursorTimerIfNeeded( - _hasFocus, widget.controller.selection); + if (ignoreCaret) { + return; + } + _showCaretOnScreen(); + _cursorCont.startOrStopCursorTimerIfNeeded(_hasFocus, widget.controller.selection); if (hasConnection) { _cursorCont ..stopCursorTimer(resetCharTicks: false) ..startCursorTimer(); } - SchedulerBinding.instance!.addPostFrameCallback( - (_) => _updateOrDisposeSelectionOverlayIfNeeded()); + SchedulerBinding.instance!.addPostFrameCallback((_) => _updateOrDisposeSelectionOverlayIfNeeded()); if (mounted) { setState(() { // Use widget.controller.value in build() diff --git a/lib/widgets/toolbar.dart b/lib/widgets/toolbar.dart index 82ab6125..8bcb3329 100644 --- a/lib/widgets/toolbar.dart +++ b/lib/widgets/toolbar.dart @@ -14,9 +14,6 @@ import '../models/documents/style.dart'; import '../utils/color.dart'; import 'controller.dart'; -double iconSize = 18; -double kToolbarHeight = iconSize * 2; - typedef OnImagePickCallback = Future Function(File file); typedef ImagePickImpl = Future Function(ImageSource source); @@ -37,10 +34,10 @@ class InsertEmbedButton extends StatelessWidget { return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, + size: controller.iconSize * 1.77, icon: Icon( icon, - size: iconSize, + size: controller.iconSize, color: Theme.of(context).iconTheme.color, ), fillColor: fillColor ?? Theme.of(context).canvasColor, @@ -101,10 +98,10 @@ class _LinkStyleButtonState extends State { return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, + size: widget.controller.iconSize * 1.77, icon: Icon( widget.icon ?? Icons.link, - size: iconSize, + size: widget.controller.iconSize, color: isEnabled ? theme.iconTheme.color : theme.disabledColor, ), fillColor: Theme.of(context).canvasColor, @@ -372,8 +369,8 @@ Widget defaultToggleStyleButtonBuilder( return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, - icon: Icon(icon, size: iconSize, color: iconColor), + size: 18 * 1.77, + icon: Icon(icon, size: 18, color: iconColor), fillColor: fill, onPressed: onPressed, ); @@ -435,12 +432,12 @@ class _SelectHeaderStyleButtonState extends State { @override Widget build(BuildContext context) { - return _selectHeadingStyleButtonBuilder(context, _value, _selectAttribute); + return _selectHeadingStyleButtonBuilder(context, _value, _selectAttribute, widget.controller.iconSize); } } Widget _selectHeadingStyleButtonBuilder(BuildContext context, Attribute? value, - ValueChanged onSelected) { + ValueChanged onSelected, double iconSize) { final _valueToText = { Attribute.header: 'N', Attribute.h1: 'H1', @@ -532,12 +529,12 @@ class _ImageButtonState extends State { return QuillIconButton( icon: Icon( widget.icon, - size: iconSize, + size: widget.controller.iconSize, color: theme.iconTheme.color, ), highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, + size: widget.controller.iconSize * 1.77, fillColor: theme.canvasColor, onPressed: _handleImageButtonTap, ); @@ -708,9 +705,9 @@ class _ColorButtonState extends State { return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, + size: widget.controller.iconSize * 1.77, icon: Icon(widget.icon, - size: iconSize, + size: widget.controller.iconSize, color: widget.background ? iconColorBackground : iconColor), fillColor: widget.background ? fillColorBackground : fillColor, onPressed: _showColorPicker, @@ -776,8 +773,8 @@ class _HistoryButtonState extends State { return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, - icon: Icon(widget.icon, size: iconSize, color: _iconColor), + size: widget.controller.iconSize * 1.77, + icon: Icon(widget.icon, size: widget.controller.iconSize, color: _iconColor), fillColor: fillColor, onPressed: _changeHistory, ); @@ -841,8 +838,8 @@ class _IndentButtonState extends State { return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, - icon: Icon(widget.icon, size: iconSize, color: iconColor), + size: widget.controller.iconSize * 1.77, + icon: Icon(widget.icon, size: widget.controller.iconSize, color: iconColor), fillColor: fillColor, onPressed: () { final indent = widget.controller @@ -895,8 +892,8 @@ class _ClearFormatButtonState extends State { return QuillIconButton( highlightElevation: 0, hoverElevation: 0, - size: iconSize * 1.77, - icon: Icon(widget.icon, size: iconSize, color: iconColor), + size: widget.controller.iconSize * 1.77, + icon: Icon(widget.icon, size: widget.controller.iconSize, color: iconColor), fillColor: fillColor, onPressed: () { for (final k @@ -908,7 +905,7 @@ class _ClearFormatButtonState extends State { } 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({ required QuillController controller, @@ -933,8 +930,9 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { OnImagePickCallback? onImagePickCallback, Key? key, }) { - iconSize = toolbarIconSize; - return QuillToolbar(key: key, children: [ + controller.iconSize = toolbarIconSize; + + return QuillToolbar(key: key, toolBarHeight: toolbarIconSize * controller.toolbarHeightFactor, children: [ Visibility( visible: showHistory, child: HistoryButton( @@ -1034,12 +1032,8 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { ), ), Visibility( - visible: showHeaderStyle, - child: VerticalDivider( - indent: 12, endIndent: 12, color: Colors.grey.shade400)), - Visibility( - visible: showHeaderStyle, - child: SelectHeaderStyleButton(controller: controller)), + visible: showHeaderStyle, child: VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400)), + Visibility(visible: showHeaderStyle, child: SelectHeaderStyleButton(controller: controller)), VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400), Visibility( visible: showListNumbers, @@ -1074,12 +1068,8 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { ), ), Visibility( - visible: !showListNumbers && - !showListBullets && - !showListCheck && - !showCodeBlock, - child: VerticalDivider( - indent: 12, endIndent: 12, color: Colors.grey.shade400)), + visible: !showListNumbers && !showListBullets && !showListCheck && !showCodeBlock, + child: VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400)), Visibility( visible: showQuote, child: ToggleStyleButton( @@ -1104,12 +1094,8 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { isIncrease: false, ), ), - Visibility( - visible: showQuote, - child: VerticalDivider( - indent: 12, endIndent: 12, color: Colors.grey.shade400)), - Visibility( - visible: showLink, child: LinkStyleButton(controller: controller)), + Visibility(visible: showQuote, child: VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400)), + Visibility(visible: showLink, child: LinkStyleButton(controller: controller)), Visibility( visible: showHorizontalRule, child: InsertEmbedButton( @@ -1121,12 +1107,13 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { } final List children; + final double toolBarHeight; @override _QuillToolbarState createState() => _QuillToolbarState(); @override - Size get preferredSize => Size.fromHeight(kToolbarHeight); + Size get preferredSize => Size.fromHeight(toolBarHeight); } class _QuillToolbarState extends State {