Merge pull request #2 from singerdmx/master

Merging master from origin
pull/756/head
shiju80 3 years ago committed by GitHub
commit 19055de317
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      CHANGELOG.md
  2. 1
      README.md
  3. 2
      lib/src/models/documents/document.dart
  4. 8
      lib/src/models/rules/insert.dart
  5. 16
      lib/src/widgets/editor.dart
  6. 47
      lib/src/widgets/embeds/default_embed_builder.dart
  7. 31
      lib/src/widgets/embeds/image.dart
  8. 7
      lib/src/widgets/raw_editor.dart
  9. 7
      lib/src/widgets/toolbar.dart
  10. 4
      pubspec.yaml

@ -1,3 +1,9 @@
# [3.9.6]
* Apply locale to QuillEditor(contents).
# [3.9.5]
* Fix image pasting.
# [3.9.4]
* Hiding dialog after selecting action for image.

@ -114,6 +114,7 @@ Define `mobileWidth`, `mobileHeight`, `mobileMargin`, `mobileAlignment` as follo
The package offers translations for the quill toolbar, it will follow the system locale unless you set your own locale with:
```
QuillToolbar(locale: Locale('fr'), ...)
QuillEditor(locale: Locale('fr'), ...)
```
Currently, translations are available for these locales:
* `Locale('en')`

@ -270,7 +270,7 @@ class Document {
for (var i = 0; i < ops.length; i++) {
final op = ops[i];
res.push(op);
_autoAppendNewlineAfterEmbeddable(i, ops, op, res, 'video');
_autoAppendNewlineAfterEmbeddable(i, ops, op, res, BlockEmbed.videoType);
}
return res;
}

@ -2,6 +2,7 @@ import 'package:tuple/tuple.dart';
import '../../models/documents/document.dart';
import '../documents/attribute.dart';
import '../documents/nodes/embeddable.dart';
import '../documents/style.dart';
import '../quill_delta.dart';
import 'rule.dart';
@ -239,6 +240,7 @@ class ResetLineFormatOnNewLineRule extends InsertRule {
}
/// Handles all format operations which manipulate embeds.
/// This rule wraps line breaks around video, not image.
class InsertEmbedsRule extends InsertRule {
const InsertEmbedsRule();
@ -249,6 +251,12 @@ class InsertEmbedsRule extends InsertRule {
return null;
}
assert(data is Map);
if (!(data as Map).containsKey(BlockEmbed.videoType)) {
return null;
}
final delta = Delta()..retain(index + (len ?? 0));
final itr = DeltaIterator(document);
final prev = itr.skip(index), cur = itr.next();

@ -6,6 +6,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:i18n_extension/i18n_widget.dart';
import 'package:tuple/tuple.dart';
import '../models/documents/document.dart';
@ -169,6 +170,7 @@ class QuillEditor extends StatefulWidget {
this.embedBuilder = defaultEmbedBuilder,
this.linkActionPickerDelegate = defaultLinkActionPickerDelegate,
this.customStyleBuilder,
this.locale,
this.floatingCursorDisabled = false,
Key? key})
: super(key: key);
@ -339,6 +341,10 @@ class QuillEditor extends StatefulWidget {
final EmbedBuilder embedBuilder;
final CustomStyleBuilder? customStyleBuilder;
/// The locale to use for the editor toolbar, defaults to system locale
/// and more https://github.com/singerdmx/flutter-quill#translation-of-toolbar
final Locale? locale;
/// Delegate function responsible for showing menu with link actions on
/// mobile platforms (iOS, Android).
///
@ -452,10 +458,12 @@ class QuillEditorState extends State<QuillEditor>
floatingCursorDisabled: widget.floatingCursorDisabled,
);
final editor = _selectionGestureDetectorBuilder.build(
behavior: HitTestBehavior.translucent,
child: child,
);
final editor = I18n(
initialLocale: widget.locale,
child: _selectionGestureDetectorBuilder.build(
behavior: HitTestBehavior.translucent,
child: child,
));
if (kIsWeb) {
// Intercept RawKeyEvent on Web to prevent it from propagating to parents

@ -1,5 +1,3 @@
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -7,8 +5,8 @@ import 'package:gallery_saver/gallery_saver.dart';
import 'package:tuple/tuple.dart';
import '../../models/documents/attribute.dart';
import '../../models/documents/nodes/embeddable.dart';
import '../../models/documents/nodes/leaf.dart' as leaf;
import '../../models/documents/style.dart';
import '../../translations/toolbar.i18n.dart';
import '../../utils/platform.dart';
import '../../utils/string.dart';
@ -24,7 +22,7 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
Tuple2<double?, double?>? _widthHeight;
switch (node.value.type) {
case 'image':
case BlockEmbed.imageType:
final imageUrl = standardizeImageUrl(node.value.data);
var image;
final style = node.style.attributes['style'];
@ -76,9 +74,10 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
final _screenSize = MediaQuery.of(context).size;
return ImageResizer(
onImageResize: (w, h) {
final res = _getImageNode(controller);
final res = getImageNode(
controller, controller.selection.start);
final attr = replaceStyleString(
_getImageStyleString(controller), w, h);
getImageStyleString(controller), w, h);
controller.formatText(
res.item1, 1, StyleAttribute(attr));
},
@ -94,10 +93,12 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
color: Colors.cyanAccent,
text: 'Copy'.i18n,
onPressed: () {
final imageNode = _getImageNode(controller).item2;
final imageNode =
getImageNode(controller, controller.selection.start)
.item2;
final imageUrl = imageNode.value.data;
controller.copiedImageUrl =
Tuple2(imageUrl, _getImageStyleString(controller));
Tuple2(imageUrl, getImageStyleString(controller));
Navigator.pop(context);
},
);
@ -106,7 +107,9 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
color: Colors.red.shade200,
text: 'Remove'.i18n,
onPressed: () {
final offset = _getImageNode(controller).item1;
final offset =
getImageNode(controller, controller.selection.start)
.item1;
controller.replaceText(offset, 1, '',
TextSelection.collapsed(offset: offset));
Navigator.pop(context);
@ -131,7 +134,7 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
// We provide option menu for mobile platform excluding base64 image
return _menuOptionsForReadonlyImage(context, imageUrl, image);
case 'video':
case BlockEmbed.videoType:
final videoUrl = node.value.data;
if (videoUrl.contains('youtube.com') || videoUrl.contains('youtu.be')) {
return YoutubeVideoApp(
@ -147,30 +150,6 @@ Widget defaultEmbedBuilder(BuildContext context, QuillController controller,
}
}
String _getImageStyleString(QuillController controller) {
final String? s = controller
.getAllSelectionStyles()
.firstWhere((s) => s.attributes.containsKey(Attribute.style.key),
orElse: () => Style())
.attributes[Attribute.style.key]
?.value;
return s ?? '';
}
Tuple2<int, leaf.Embed> _getImageNode(QuillController controller) {
var offset = controller.selection.start;
var imageNode = controller.queryNode(offset);
if (imageNode == null || !(imageNode is leaf.Embed)) {
offset = max(0, offset - 1);
imageNode = controller.queryNode(offset);
}
if (imageNode != null && imageNode is leaf.Embed) {
return Tuple2(offset, imageNode);
}
return throw 'Image node not found by offset $offset';
}
Widget _menuOptionsForReadonlyImage(
BuildContext context, String imageUrl, Image image) {
return GestureDetector(

@ -1,14 +1,45 @@
import 'dart:convert';
import 'dart:io' as io;
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:string_validator/string_validator.dart';
import 'package:tuple/tuple.dart';
import '../../models/documents/attribute.dart';
import '../../models/documents/nodes/leaf.dart';
import '../../models/documents/style.dart';
import '../controller.dart';
bool isImageBase64(String imageUrl) {
return !imageUrl.startsWith('http') && isBase64(imageUrl);
}
Tuple2<int, Embed> getImageNode(QuillController controller, int offset) {
var offset = controller.selection.start;
var imageNode = controller.queryNode(offset);
if (imageNode == null || !(imageNode is Embed)) {
offset = max(0, offset - 1);
imageNode = controller.queryNode(offset);
}
if (imageNode != null && imageNode is Embed) {
return Tuple2(offset, imageNode);
}
return throw 'Image node not found by offset $offset';
}
String getImageStyleString(QuillController controller) {
final String? s = controller
.getAllSelectionStyles()
.firstWhere((s) => s.attributes.containsKey(Attribute.style.key),
orElse: () => Style())
.attributes[Attribute.style.key]
?.value;
return s ?? '';
}
Image imageByUrl(String imageUrl,
{double? width,
double? height,

@ -26,6 +26,7 @@ import 'default_styles.dart';
import 'delegate.dart';
import 'editor.dart';
import 'embeds/default_embed_builder.dart';
import 'embeds/image.dart';
import 'keyboard_listener.dart';
import 'link.dart';
import 'proxy.dart';
@ -927,8 +928,10 @@ class RawEditorState extends EditorState
widget.controller
.replaceText(index, length, BlockEmbed.image(copied.item1), null);
if (copied.item2.isNotEmpty) {
widget.controller
.formatText(index + 1, 1, StyleAttribute(copied.item2));
widget.controller.formatText(
getImageNode(widget.controller, index + 1).item1,
1,
StyleAttribute(copied.item2));
}
widget.controller.copiedImageUrl = null;
await Clipboard.setData(const ClipboardData(text: ''));

@ -424,12 +424,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
final FilePickImpl? filePickImpl;
///The locale to use for the editor toolbar, defaults to system locale
///Currently the supported locales are:
/// * Locale('en')
/// * Locale('de')
/// * Locale('fr')
/// * Locale('zh', 'CN')
/// The locale to use for the editor toolbar, defaults to system locale
/// and more https://github.com/singerdmx/flutter-quill#translation-of-toolbar
final Locale? locale;

@ -1,6 +1,6 @@
name: flutter_quill
description: A rich text editor supporting mobile and web (Demo App @ bulletjournal.us)
version: 3.9.4
version: 3.9.6
#author: bulletjournal
homepage: https://bulletjournal.us/home/index.html
repository: https://github.com/singerdmx/flutter-quill
@ -30,7 +30,7 @@ dependencies:
characters: ^1.1.0
youtube_player_flutter: ^8.0.0
diff_match_patch: ^0.4.1
i18n_extension: ^4.1.3
i18n_extension: ^4.2.0
gallery_saver: ^2.3.2
dev_dependencies:

Loading…
Cancel
Save