diff --git a/CHANGELOG.md b/CHANGELOG.md index 2971f876..10d679ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [7.0.5] +* Fix IME position bug for Mac and Windows. +* Unfocus when tap outside editor. fix the bug that cant refocus in afterButtonPressed after click ToggleStyleButton on Mac. + +# [7.0.4] +* Have text selection span full line height for uneven sized text. + # [7.0.3] * Fix ordered list numeration for lists with more than one level of list. diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index 13187808..442fb43d 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.2.0 + +* Allow widgets to override widget span properties [b7951b0](https://github.com/singerdmx/flutter-quill/commit/b7951b02c9086ea42e7aad6d78e6c9b0297562e5) +* Remove tuples [3e9452e](https://github.com/singerdmx/flutter-quill/commit/3e9452e675e8734ff50364c5f7b5d34088d5ff05) +* Remove transparent color of ImageVideoUtils dialog [74544bd](https://github.com/singerdmx/flutter-quill/commit/74544bd945a9d212ca1e8d6b3053dbecee22b720) +* Migrate to `youtube_player_flutter` from `youtube_player_flutter_quill` +* Updates to forumla button [5228f38](https://github.com/singerdmx/flutter-quill/commit/5228f389ba6f37d61d445cfe138c19fcf8766d71) + ## 0.1.0 * Initial release diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index 937fa233..5ca94b5c 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill_extensions description: Embed extensions for flutter_quill including image, video, formula and etc. -version: 0.1.0 +version: 0.2.0 homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions @@ -21,6 +21,7 @@ dependencies: gallery_saver: ^2.3.2 math_keyboard: ^0.1.8 string_validator: ^1.0.0 + url_launcher: ^6.1.9 # dependency_overrides: # flutter_quill: diff --git a/lib/src/widgets/proxy.dart b/lib/src/widgets/proxy.dart index 8f4d231f..155e620a 100644 --- a/lib/src/widgets/proxy.dart +++ b/lib/src/widgets/proxy.dart @@ -290,7 +290,7 @@ class RenderParagraphProxy extends RenderProxyBox @override List getBoxesForSelection(TextSelection selection) => child! - .getBoxesForSelection(selection, boxHeightStyle: BoxHeightStyle.strut); + .getBoxesForSelection(selection, boxHeightStyle: BoxHeightStyle.max); @override void performLayout() { diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index b0e2ea0d..474326d7 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:math' as math; // ignore: unnecessary_import import 'dart:typed_data'; +import 'dart:ui' as ui hide TextStyle; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -371,6 +372,40 @@ class RawEditorState extends EditorState ); } + void _defaultOnTapOutside(PointerDownEvent event) { + /// The focus dropping behavior is only present on desktop platforms + /// and mobile browsers. + switch (defaultTargetPlatform) { + case TargetPlatform.android: + case TargetPlatform.iOS: + case TargetPlatform.fuchsia: + // On mobile platforms, we don't unfocus on touch events unless they're + // in the web browser, but we do unfocus for all other kinds of events. + switch (event.kind) { + case ui.PointerDeviceKind.touch: + if (kIsWeb) { + widget.focusNode.unfocus(); + } + break; + case ui.PointerDeviceKind.mouse: + case ui.PointerDeviceKind.stylus: + case ui.PointerDeviceKind.invertedStylus: + case ui.PointerDeviceKind.unknown: + widget.focusNode.unfocus(); + break; + case ui.PointerDeviceKind.trackpad: + throw UnimplementedError( + 'Unexpected pointer down event for trackpad'); + } + break; + case TargetPlatform.linux: + case TargetPlatform.macOS: + case TargetPlatform.windows: + widget.focusNode.unfocus(); + break; + } + } + @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); @@ -454,78 +489,85 @@ class RawEditorState extends EditorState minHeight: widget.minHeight ?? 0.0, maxHeight: widget.maxHeight ?? double.infinity); - return QuillStyles( - data: _styles!, - child: Shortcuts( - shortcuts: { - // shortcuts added for Desktop platforms. - LogicalKeySet(LogicalKeyboardKey.escape): - const HideSelectionToolbarIntent(), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyZ): - const UndoTextIntent(SelectionChangedCause.keyboard), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyY): - const RedoTextIntent(SelectionChangedCause.keyboard), - - // Selection formatting. - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyB): - const ToggleTextStyleIntent(Attribute.bold), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyU): - const ToggleTextStyleIntent(Attribute.underline), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyI): - const ToggleTextStyleIntent(Attribute.italic), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, - LogicalKeyboardKey.keyS): - const ToggleTextStyleIntent(Attribute.strikeThrough), - LogicalKeySet( - LogicalKeyboardKey.control, LogicalKeyboardKey.backquote): - const ToggleTextStyleIntent(Attribute.inlineCode), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyL): - const ToggleTextStyleIntent(Attribute.ul), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyO): - const ToggleTextStyleIntent(Attribute.ol), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, - LogicalKeyboardKey.keyB): - const ToggleTextStyleIntent(Attribute.blockQuote), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, - LogicalKeyboardKey.tilde): - const ToggleTextStyleIntent(Attribute.codeBlock), - // Indent - LogicalKeySet( - LogicalKeyboardKey.control, LogicalKeyboardKey.bracketRight): - const IndentSelectionIntent(true), - LogicalKeySet( - LogicalKeyboardKey.control, LogicalKeyboardKey.bracketLeft): - const IndentSelectionIntent(false), - - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): - const OpenSearchIntent(), - - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.digit1): - const ApplyHeaderIntent(Attribute.h1), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.digit2): - const ApplyHeaderIntent(Attribute.h2), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.digit3): - const ApplyHeaderIntent(Attribute.h3), - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.digit0): - const ApplyHeaderIntent(Attribute.header), - - LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, - LogicalKeyboardKey.keyL): const ApplyCheckListIntent(), - - if (widget.customShortcuts != null) ...widget.customShortcuts!, - }, - child: Actions( - actions: { - ..._actions, - if (widget.customActions != null) ...widget.customActions!, + return TextFieldTapRegion( + onTapOutside: _defaultOnTapOutside, + child: QuillStyles( + data: _styles!, + child: Shortcuts( + shortcuts: { + // shortcuts added for Desktop platforms. + LogicalKeySet(LogicalKeyboardKey.escape): + const HideSelectionToolbarIntent(), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyZ): + const UndoTextIntent(SelectionChangedCause.keyboard), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyY): + const RedoTextIntent(SelectionChangedCause.keyboard), + + // Selection formatting. + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyB): + const ToggleTextStyleIntent(Attribute.bold), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyU): + const ToggleTextStyleIntent(Attribute.underline), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyI): + const ToggleTextStyleIntent(Attribute.italic), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, + LogicalKeyboardKey.keyS): + const ToggleTextStyleIntent(Attribute.strikeThrough), + LogicalKeySet( + LogicalKeyboardKey.control, LogicalKeyboardKey.backquote): + const ToggleTextStyleIntent(Attribute.inlineCode), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyL): + const ToggleTextStyleIntent(Attribute.ul), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyO): + const ToggleTextStyleIntent(Attribute.ol), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, + LogicalKeyboardKey.keyB): + const ToggleTextStyleIntent(Attribute.blockQuote), + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, + LogicalKeyboardKey.tilde): + const ToggleTextStyleIntent(Attribute.codeBlock), + // Indent + LogicalKeySet(LogicalKeyboardKey.control, + LogicalKeyboardKey.bracketRight): + const IndentSelectionIntent(true), + LogicalKeySet( + LogicalKeyboardKey.control, LogicalKeyboardKey.bracketLeft): + const IndentSelectionIntent(false), + + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyF): + const OpenSearchIntent(), + + LogicalKeySet( + LogicalKeyboardKey.control, LogicalKeyboardKey.digit1): + const ApplyHeaderIntent(Attribute.h1), + LogicalKeySet( + LogicalKeyboardKey.control, LogicalKeyboardKey.digit2): + const ApplyHeaderIntent(Attribute.h2), + LogicalKeySet( + LogicalKeyboardKey.control, LogicalKeyboardKey.digit3): + const ApplyHeaderIntent(Attribute.h3), + LogicalKeySet( + LogicalKeyboardKey.control, LogicalKeyboardKey.digit0): + const ApplyHeaderIntent(Attribute.header), + + LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.shift, + LogicalKeyboardKey.keyL): const ApplyCheckListIntent(), + + if (widget.customShortcuts != null) ...widget.customShortcuts!, }, - child: Focus( - focusNode: widget.focusNode, - onKey: _onKey, - child: QuillKeyboardListener( - child: Container( - constraints: constraints, - child: child, + child: Actions( + actions: { + ..._actions, + if (widget.customActions != null) ...widget.customActions!, + }, + child: Focus( + focusNode: widget.focusNode, + onKey: _onKey, + child: QuillKeyboardListener( + child: Container( + constraints: constraints, + child: child, + ), ), ), ), diff --git a/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart b/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart index 5eced2bf..5f98d56a 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart @@ -63,12 +63,44 @@ mixin RawEditorStateTextInputClientMixin on EditorState ); _updateSizeAndTransform(); + //update IME position for Windows + _updateComposingRectIfNeeded(); + //update IME position for Macos + _updateCaretRectIfNeeded(); _textInputConnection!.setEditingState(_lastKnownRemoteTextEditingValue!); } - _textInputConnection!.show(); } + void _updateComposingRectIfNeeded() { + final composingRange = _lastKnownRemoteTextEditingValue?.composing ?? + textEditingValue.composing; + if (hasConnection) { + assert(mounted); + final offset = composingRange.isValid ? composingRange.start : 0; + final composingRect = + renderEditor.getLocalRectForCaret(TextPosition(offset: offset)); + _textInputConnection!.setComposingRect(composingRect); + SchedulerBinding.instance + .addPostFrameCallback((_) => _updateComposingRectIfNeeded()); + } + } + + void _updateCaretRectIfNeeded() { + if (hasConnection) { + if (renderEditor.selection.isValid && + renderEditor.selection.isCollapsed) { + final currentTextPosition = + TextPosition(offset: renderEditor.selection.baseOffset); + final caretRect = + renderEditor.getLocalRectForCaret(currentTextPosition); + _textInputConnection!.setCaretRect(caretRect); + } + SchedulerBinding.instance + .addPostFrameCallback((_) => _updateCaretRectIfNeeded()); + } + } + /// Closes input connection if it's currently open. Otherwise does nothing. void closeConnectionIfNeeded() { if (!hasConnection) { diff --git a/lib/src/widgets/text_line.dart b/lib/src/widgets/text_line.dart index 3d4e02e0..b7174a2f 100644 --- a/lib/src/widgets/text_line.dart +++ b/lib/src/widgets/text_line.dart @@ -1107,6 +1107,18 @@ class RenderEditableTextLine extends RenderEditableBox { _selectedRects ??= _body!.getBoxesForSelection( local, ); + + // Paint a small rect at the start of empty lines that + // are contained by the selection. + if (line.isEmpty && + textSelection.baseOffset <= line.offset && + textSelection.extentOffset > line.offset) { + final lineHeight = + preferredLineHeight(TextPosition(offset: line.offset)); + _selectedRects + ?.add(TextBox.fromLTRBD(0, 0, 3, lineHeight, textDirection)); + } + _paintSelection(context, effectiveOffset); } } diff --git a/pubspec.yaml b/pubspec.yaml index 60ce2a16..6b051d89 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill description: A rich text editor supporting mobile and web (Demo App @ bulletjournal.us) -version: 7.0.3 +version: 7.0.5 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill