diff --git a/.vscode/launch.json b/.vscode/launch.json index f9c59272..3db95f2b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "flutter-quill", "request": "launch", "type": "dart", - "program": "app/lib/main.dart" + "program": "example/lib/main.dart" }, ] diff --git a/app/lib/generated_plugin_registrant.dart b/app/lib/generated_plugin_registrant.dart new file mode 100644 index 00000000..feba7443 --- /dev/null +++ b/app/lib/generated_plugin_registrant.dart @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// ignore_for_file: lines_longer_than_80_chars + +import 'package:url_launcher_web/url_launcher_web.dart'; + +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +// ignore: public_member_api_docs +void registerPlugins(Registrar registrar) { + UrlLauncherPlugin.registerWith(registrar); + registrar.registerMessageHandler(); +} diff --git a/app/windows/flutter/ephemeral/.plugin_symlinks/url_launcher_windows b/app/windows/flutter/ephemeral/.plugin_symlinks/url_launcher_windows new file mode 120000 index 00000000..7d4953a0 --- /dev/null +++ b/app/windows/flutter/ephemeral/.plugin_symlinks/url_launcher_windows @@ -0,0 +1 @@ +C:/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.0.1+3/ \ No newline at end of file diff --git a/lib/src/models/documents/attribute.dart b/lib/src/models/documents/attribute.dart index acd979cb..e6d5b882 100644 --- a/lib/src/models/documents/attribute.dart +++ b/lib/src/models/documents/attribute.dart @@ -165,9 +165,10 @@ class Attribute { Map toJson() => {key: value}; - static Attribute fromKeyValue(String key, dynamic value) { + static Attribute? fromKeyValue(String key, dynamic value) { if (!_registry.containsKey(key)) { - throw ArgumentError.value(key, 'key "$key" not found.'); + return null; + // throw ArgumentError.value(key, 'key "$key" not found.'); } final origin = _registry[key]!; final attribute = clone(origin, value); diff --git a/lib/src/models/documents/style.dart b/lib/src/models/documents/style.dart index fade1bb5..2e69bd9a 100644 --- a/lib/src/models/documents/style.dart +++ b/lib/src/models/documents/style.dart @@ -18,7 +18,8 @@ class Style { final result = attributes.map((key, dynamic value) { final attr = Attribute.fromKeyValue(key, value); - return MapEntry(key, attr); + // ignore: lines_longer_than_80_chars + return MapEntry(key, attr??Attribute(key, AttributeScope.INLINE, value)); }); return Style.attr(result); } diff --git a/lib/src/widgets/delegate.dart b/lib/src/widgets/delegate.dart index f22f66fc..c537c7da 100644 --- a/lib/src/widgets/delegate.dart +++ b/lib/src/widgets/delegate.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import '../../models/documents/attribute.dart'; import '../models/documents/nodes/leaf.dart'; import 'editor.dart'; @@ -9,7 +10,7 @@ import 'text_selection.dart'; typedef EmbedBuilder = Widget Function( BuildContext context, Embed node, bool readOnly); - +typedef StyleBuilder = TextStyle Function(Attribute attribute); abstract class EditorTextSelectionGestureDetectorBuilderDelegate { GlobalKey getEditableTextKey(); diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index c6d229fb..7be6b4fb 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -144,7 +144,7 @@ String _standardizeImageUrl(String url) { } bool _isMobile() => io.Platform.isAndroid || io.Platform.isIOS; - +TextStyle _defaultStyleBuilder(_)=>const TextStyle(); Widget _defaultEmbedBuilder( BuildContext context, leaf.Embed node, bool readOnly) { assert(!kIsWeb, 'Please provide EmbedBuilder for Web'); @@ -225,33 +225,35 @@ Widget _defaultEmbedBuilder( } class QuillEditor extends StatefulWidget { - const QuillEditor( - {required this.controller, - required this.focusNode, - required this.scrollController, - required this.scrollable, - required this.padding, - required this.autoFocus, - required this.readOnly, - required this.expands, - this.showCursor, - this.paintCursorAboveText, - this.placeholder, - this.enableInteractiveSelection = true, - this.scrollBottomInset = 0, - this.minHeight, - this.maxHeight, - this.customStyles, - this.textCapitalization = TextCapitalization.sentences, - this.keyboardAppearance = Brightness.light, - this.scrollPhysics, - this.onLaunchUrl, - this.onTapDown, - this.onTapUp, - this.onSingleLongTapStart, - this.onSingleLongTapMoveUpdate, - this.onSingleLongTapEnd, - this.embedBuilder = _defaultEmbedBuilder}); + const QuillEditor({ + required this.controller, + required this.focusNode, + required this.scrollController, + required this.scrollable, + required this.padding, + required this.autoFocus, + required this.readOnly, + required this.expands, + this.showCursor, + this.paintCursorAboveText, + this.placeholder, + this.enableInteractiveSelection = true, + this.scrollBottomInset = 0, + this.minHeight, + this.maxHeight, + this.customStyles, + this.textCapitalization = TextCapitalization.sentences, + this.keyboardAppearance = Brightness.light, + this.scrollPhysics, + this.onLaunchUrl, + this.onTapDown, + this.onTapUp, + this.onSingleLongTapStart, + this.onSingleLongTapMoveUpdate, + this.onSingleLongTapEnd, + this.embedBuilder = _defaultEmbedBuilder, + this.styleBuilder = _defaultStyleBuilder, + }); factory QuillEditor.basic({ required QuillController controller, @@ -310,6 +312,7 @@ class QuillEditor extends StatefulWidget { onSingleLongTapEnd; final EmbedBuilder embedBuilder; + final StyleBuilder styleBuilder; @override _QuillEditorState createState() => _QuillEditorState(); @@ -413,7 +416,9 @@ class _QuillEditorState extends State widget.keyboardAppearance, widget.enableInteractiveSelection, widget.scrollPhysics, - widget.embedBuilder), + widget.embedBuilder, + widget.styleBuilder, + ), ); } diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index fdfcec6b..7de2cec1 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -57,6 +57,7 @@ class RawEditor extends StatefulWidget { this.enableInteractiveSelection, this.scrollPhysics, this.embedBuilder, + this.styleBuilder, ) : assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'), assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'), assert(maxHeight == null || minHeight == null || maxHeight >= minHeight, @@ -89,7 +90,7 @@ class RawEditor extends StatefulWidget { final bool enableInteractiveSelection; final ScrollPhysics? scrollPhysics; final EmbedBuilder embedBuilder; - + final StyleBuilder styleBuilder; @override State createState() => RawEditorState(); } @@ -244,6 +245,7 @@ class RawEditorState extends EditorState ? const EdgeInsets.all(16) : null, widget.embedBuilder, + widget.styleBuilder, _cursorCont, indentLevelCounts, _handleCheckboxTap, @@ -262,6 +264,7 @@ class RawEditorState extends EditorState line: node, textDirection: _textDirection, embedBuilder: widget.embedBuilder, + styleBuilder: widget.styleBuilder, styles: _styles!, readOnly: widget.readOnly, ); diff --git a/lib/src/widgets/simple_viewer.dart b/lib/src/widgets/simple_viewer.dart index 45b65502..dc400e33 100644 --- a/lib/src/widgets/simple_viewer.dart +++ b/lib/src/widgets/simple_viewer.dart @@ -37,6 +37,7 @@ class QuillSimpleViewer extends StatefulWidget { this.padding = EdgeInsets.zero, this.embedBuilder, Key? key, + this.styleBuilder, }) : assert(truncate || ((truncateScale == null) && (truncateAlignment == null) && @@ -54,6 +55,7 @@ class QuillSimpleViewer extends StatefulWidget { final double scrollBottomInset; final EdgeInsetsGeometry padding; final EmbedBuilder? embedBuilder; + final StyleBuilder? styleBuilder; final bool readOnly; @override @@ -100,6 +102,7 @@ class _QuillSimpleViewerState extends State } EmbedBuilder get embedBuilder => widget.embedBuilder ?? _defaultEmbedBuilder; + StyleBuilder get styleBuilder => widget.styleBuilder ??(a)=>const TextStyle(); Widget _defaultEmbedBuilder( BuildContext context, leaf.Embed node, bool readOnly) { @@ -218,6 +221,7 @@ class _QuillSimpleViewerState extends State ? const EdgeInsets.all(16) : null, embedBuilder, + styleBuilder, _cursorCont, indentLevelCounts, _handleCheckboxTap, @@ -247,6 +251,7 @@ class _QuillSimpleViewerState extends State line: node, textDirection: _textDirection, embedBuilder: embedBuilder, + styleBuilder: styleBuilder, styles: _styles, readOnly: widget.readOnly, ); diff --git a/lib/src/widgets/text_block.dart b/lib/src/widgets/text_block.dart index 93130884..ab106e70 100644 --- a/lib/src/widgets/text_block.dart +++ b/lib/src/widgets/text_block.dart @@ -59,6 +59,7 @@ class EditableTextBlock extends StatelessWidget { this.hasFocus, this.contentPadding, this.embedBuilder, + this.styleBuilder, this.cursorCont, this.indentLevelCounts, this.onCheckboxTap, @@ -76,6 +77,7 @@ class EditableTextBlock extends StatelessWidget { final bool hasFocus; final EdgeInsets? contentPadding; final EmbedBuilder embedBuilder; + final StyleBuilder styleBuilder; final CursorCont cursorCont; final Map indentLevelCounts; final Function(int, bool) onCheckboxTap; @@ -123,6 +125,7 @@ class EditableTextBlock extends StatelessWidget { line: line, textDirection: textDirection, embedBuilder: embedBuilder, + styleBuilder: styleBuilder, styles: styles!, readOnly: readOnly, ), diff --git a/lib/src/widgets/text_line.dart b/lib/src/widgets/text_line.dart index bf3c415c..c557ee2c 100644 --- a/lib/src/widgets/text_line.dart +++ b/lib/src/widgets/text_line.dart @@ -24,6 +24,7 @@ class TextLine extends StatelessWidget { const TextLine({ required this.line, required this.embedBuilder, + required this.styleBuilder, required this.styles, required this.readOnly, this.textDirection, @@ -35,7 +36,7 @@ class TextLine extends StatelessWidget { final EmbedBuilder embedBuilder; final DefaultStyles styles; final bool readOnly; - + final StyleBuilder? styleBuilder; @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); @@ -150,6 +151,20 @@ class TextLine extends StatelessWidget { textStyle = textStyle.merge(toMerge); + if (styleBuilder != null) { + line.style.attributes.keys.forEach((key) { + final attribute = + Attribute.fromKeyValue(key, line.style.attributes[key]); + if (attribute != null) { + /// + ///Unkown Attribute + /// + final call = styleBuilder?.call(attribute); + textStyle = textStyle.merge(call); + } + }); + } + return textStyle; }