pull/1437/head
Ahmed Hnewa 2 years ago
parent 21bab1d21d
commit 7d3731aaa0
No known key found for this signature in database
GPG Key ID: C488CC70BBCEF0D1
  1. 8
      example/android/app/src/main/AndroidManifest.xml
  2. 2
      flutter_quill_extensions/lib/embeds/toolbar/media_button.dart
  3. 2
      flutter_quill_extensions/pubspec.yaml
  4. 40
      lib/src/models/rules/delete.dart
  5. 40
      lib/src/models/rules/format.dart
  6. 111
      lib/src/models/rules/insert.dart
  7. 28
      lib/src/models/rules/rule.dart
  8. 8
      lib/src/widgets/controller.dart
  9. 39
      lib/src/widgets/raw_editor.dart
  10. 2
      lib/src/widgets/toolbar/link_style_button.dart
  11. 2
      lib/src/widgets/toolbar/link_style_button2.dart

@ -1,7 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<application
android:name="${applicationName}"
android:label="app"

@ -344,6 +344,8 @@ class _MediaLinkDialogState extends State<MediaLinkDialog> {
void _submitLink() => Navigator.pop(context, _linkController.text);
String? _validateLink(String? value) {
// TODO: Use [AutoFormatMultipleLinksRule.oneLineRegExp]
// in the next update
if ((value?.isEmpty ?? false) ||
!AutoFormatMultipleLinksRule.linkRegExp.hasMatch(value!)) {
return widget.validationMessage ?? 'That is not a valid URL';

@ -15,7 +15,7 @@ dependencies:
flutter_quill: ^7.4.14
# In case you are working on changes for both libraries,
# flutter_quill:
# path: ~/development/playground/framework_based/flutter/flutter-quill
# path: /Users/ahmedhnewa/development/playground/framework_based/flutter/flutter-quill
http: ^1.1.0
image_picker: ">=1.0.4"

@ -22,8 +22,14 @@ class EnsureLastLineBreakDeleteRule extends DeleteRule {
const EnsureLastLineBreakDeleteRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
final itr = DeltaIterator(document)..skip(index + len!);
return Delta()
@ -38,8 +44,14 @@ class CatchAllDeleteRule extends DeleteRule {
const CatchAllDeleteRule();
@override
Delta applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
final itr = DeltaIterator(document)..skip(index + len!);
return Delta()
@ -58,8 +70,14 @@ class PreserveLineStyleOnMergeRule extends DeleteRule {
const PreserveLineStyleOnMergeRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
final itr = DeltaIterator(document)..skip(index);
var op = itr.next(1);
if (op.data != '\n') {
@ -116,8 +134,14 @@ class EnsureEmbedLineRule extends DeleteRule {
const EnsureEmbedLineRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
final itr = DeltaIterator(document);
var op = itr.skip(index);

@ -23,8 +23,14 @@ class ResolveLineFormatRule extends FormatRule {
const ResolveLineFormatRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (attribute!.scope != AttributeScope.BLOCK) {
return null;
}
@ -108,8 +114,14 @@ class FormatLinkAtCaretPositionRule extends FormatRule {
const FormatLinkAtCaretPositionRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (attribute!.key != Attribute.link.key || len! > 0) {
return null;
}
@ -142,8 +154,14 @@ class ResolveInlineFormatRule extends FormatRule {
const ResolveInlineFormatRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (attribute!.scope != AttributeScope.INLINE) {
return null;
}
@ -182,8 +200,14 @@ class ResolveImageFormatRule extends FormatRule {
const ResolveImageFormatRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (attribute == null || attribute.key != Attribute.style.key) {
return null;
}

@ -27,8 +27,16 @@ class PreserveLineStyleOnSplitRule extends InsertRule {
const PreserveLineStyleOnSplitRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
// TODO: If the maintainer are not okay with this change then tell me
// so I can change it back
Map<String, Object?> extraData = const {},
}) {
if (data is! String || data != '\n') {
return null;
}
@ -72,8 +80,14 @@ class PreserveBlockStyleOnInsertRule extends InsertRule {
const PreserveBlockStyleOnInsertRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (data is! String || !data.contains('\n')) {
// Only interested in text containing at least one newline character.
return null;
@ -153,8 +167,14 @@ class AutoExitBlockRule extends InsertRule {
}
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (data is! String || data != '\n') {
return null;
}
@ -217,8 +237,14 @@ class ResetLineFormatOnNewLineRule extends InsertRule {
const ResetLineFormatOnNewLineRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (data is! String || data != '\n') {
return null;
}
@ -248,8 +274,14 @@ class InsertEmbedsRule extends InsertRule {
const InsertEmbedsRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (data is String) {
return null;
}
@ -329,8 +361,30 @@ class AutoFormatMultipleLinksRule extends InsertRule {
// http://www.example.com/?action=birds&brass=apparatus
// https://example.net/
// URL generator tool (https://www.randomlists.com/urls) is used.
static const _linkPattern = r'^https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/.*)?$';
static final linkRegExp = RegExp(_linkPattern, caseSensitive: false);
// TODO: You might want to rename those but everywhere even in
// flutter_quill_extensions
static const _oneLinePattern =
r'^https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/.*)?$';
static const _detectLinkPattern =
r'https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/[^\s]*)?';
/// It requires a valid link in one link
static final oneLineRegExp = RegExp(
_oneLinePattern,
caseSensitive: false,
);
/// It detect if there is a link in the text whatever if it in the middle etc
static final detectLinkRegExp = RegExp(
_detectLinkPattern,
caseSensitive: false,
);
@Deprecated(
'Please use [linkRegExp1] or [linkRegExp2]',
)
static final linkRegExp = oneLineRegExp;
@override
Delta? applyRule(
@ -339,6 +393,7 @@ class AutoFormatMultipleLinksRule extends InsertRule {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
// Only format when inserting text.
if (data is! String) return null;
@ -374,7 +429,7 @@ class AutoFormatMultipleLinksRule extends InsertRule {
final affectedWords = '$leftWordPart$data$rightWordPart';
// Check for URL pattern.
final matches = linkRegExp.allMatches(affectedWords);
final matches = detectLinkRegExp.allMatches(affectedWords);
// If there are no matches, do not apply any format.
if (matches.isEmpty) return null;
@ -428,8 +483,14 @@ class AutoFormatLinksRule extends InsertRule {
const AutoFormatLinksRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (data is! String || data != ' ') {
return null;
}
@ -468,8 +529,14 @@ class PreserveInlineStylesRule extends InsertRule {
const PreserveInlineStylesRule();
@override
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
if (data is! String || data.contains('\n')) {
return null;
}
@ -514,8 +581,14 @@ class CatchAllInsertRule extends InsertRule {
const CatchAllInsertRule();
@override
Delta applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
}) {
return Delta()
..retain(index + (len ?? 0))
..insert(data);

@ -10,19 +10,35 @@ enum RuleType { INSERT, DELETE, FORMAT }
abstract class Rule {
const Rule();
Delta? apply(Delta document, int index,
{int? len, Object? data, Attribute? attribute}) {
Delta? apply(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
}) {
validateArgs(len, data, attribute);
return applyRule(document, index,
len: len, data: data, attribute: attribute);
return applyRule(
document,
index,
len: len,
data: data,
attribute: attribute,
);
}
void validateArgs(int? len, Object? data, Attribute? attribute);
/// Applies heuristic rule to an operation on a [document] and returns
/// resulting [Delta].
Delta? applyRule(Delta document, int index,
{int? len, Object? data, Attribute? attribute});
Delta? applyRule(
Delta document,
int index, {
int? len,
Object? data,
Attribute? attribute,
Map<String, Object?> extraData = const {},
});
RuleType get type;
}

@ -227,8 +227,12 @@ class QuillController extends ChangeNotifier {
}
void replaceText(
int index, int len, Object? data, TextSelection? textSelection,
{bool ignoreFocus = false}) {
int index,
int len,
Object? data,
TextSelection? textSelection, {
bool ignoreFocus = false,
}) {
assert(data is String || data is Embeddable);
if (onReplaceText != null && !onReplaceText!(index, len, data)) {

@ -533,7 +533,8 @@ class RawEditorState extends EditorState
? const BoxConstraints.expand()
: BoxConstraints(
minHeight: widget.minHeight ?? 0.0,
maxHeight: widget.maxHeight ?? double.infinity);
maxHeight: widget.maxHeight ?? double.infinity,
);
// Please notice that this change will make the check fixed
// so if we ovveride the platform in material app theme data
@ -1512,13 +1513,23 @@ class RawEditorState extends EditorState
final index = textEditingValue.selection.baseOffset;
final length = textEditingValue.selection.extentOffset - index;
final copied = controller.copiedImageUrl!;
controller.replaceText(index, length, BlockEmbed.image(copied.url), null);
controller.replaceText(
index,
length,
BlockEmbed.image(copied.url),
null,
);
if (copied.styleString.isNotEmpty) {
controller.formatText(getEmbedNode(controller, index + 1).offset, 1,
StyleAttribute(copied.styleString));
controller.formatText(
getEmbedNode(controller, index + 1).offset,
1,
StyleAttribute(copied.styleString),
);
}
controller.copiedImageUrl = null;
await Clipboard.setData(const ClipboardData(text: ''));
await Clipboard.setData(
const ClipboardData(text: ''),
);
return;
}
@ -1531,7 +1542,13 @@ class RawEditorState extends EditorState
final text = await Clipboard.getData(Clipboard.kTextPlain);
if (text != null) {
_replaceText(
ReplaceTextIntent(textEditingValue, text.text!, selection, cause));
ReplaceTextIntent(
textEditingValue,
text.text!,
selection,
cause,
),
);
bringIntoView(textEditingValue.selection.extent);
@ -1539,8 +1556,9 @@ class RawEditorState extends EditorState
userUpdateTextEditingValue(
TextEditingValue(
text: textEditingValue.text,
selection:
TextSelection.collapsed(offset: textEditingValue.selection.end),
selection: TextSelection.collapsed(
offset: textEditingValue.selection.end,
),
),
cause,
);
@ -1548,14 +1566,15 @@ class RawEditorState extends EditorState
return;
}
if (widget.onImagePaste != null) {
final onImagePaste = widget.onImagePaste;
if (onImagePaste != null) {
final image = await Pasteboard.image;
if (image == null) {
return;
}
final imageUrl = await widget.onImagePaste!(image);
final imageUrl = await onImagePaste(image);
if (imageUrl == null) {
return;
}

@ -184,7 +184,7 @@ class _LinkDialogState extends State<_LinkDialog> {
super.initState();
_link = widget.link ?? '';
_text = widget.text ?? '';
linkRegExp = widget.linkRegExp ?? AutoFormatMultipleLinksRule.linkRegExp;
linkRegExp = widget.linkRegExp ?? AutoFormatMultipleLinksRule.oneLineRegExp;
_linkController = TextEditingController(text: _link);
_textController = TextEditingController(text: _text);
}

@ -379,7 +379,7 @@ class _LinkStyleDialogState extends State<LinkStyleDialog> {
String? _validateLink(String? value) {
if ((value?.isEmpty ?? false) ||
!AutoFormatMultipleLinksRule.linkRegExp.hasMatch(value!)) {
!AutoFormatMultipleLinksRule.oneLineRegExp.hasMatch(value!)) {
return widget.validationMessage ?? 'That is not a valid URL';
}

Loading…
Cancel
Save