From 9b146f3fbcd26bae590d9f6ec13490045a6312d1 Mon Sep 17 00:00:00 2001 From: Lin Zhang Date: Mon, 3 Apr 2023 22:41:04 +0800 Subject: [PATCH 01/27] Fix line style loss on new line from non string (#1157) --- lib/src/models/rules/insert.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/models/rules/insert.dart b/lib/src/models/rules/insert.dart index 8ef6c196..44124fbc 100644 --- a/lib/src/models/rules/insert.dart +++ b/lib/src/models/rules/insert.dart @@ -35,20 +35,20 @@ class PreserveLineStyleOnSplitRule extends InsertRule { final itr = DeltaIterator(document); final before = itr.skip(index); - if (before == null || - before.data is! String || - (before.data as String).endsWith('\n')) { + if (before == null) { return null; } - final after = itr.next(); - if (after.data is! String || (after.data as String).startsWith('\n')) { + if (before.data is String && (before.data as String).endsWith('\n')) { return null; } - final text = after.data as String; + final after = itr.next(); + if (after.data is String && (after.data as String).startsWith('\n')) { + return null; + } final delta = Delta()..retain(index + (len ?? 0)); - if (text.contains('\n')) { + if (after.data is String && (after.data as String).contains('\n')) { assert(after.isPlain); delta.insert('\n'); return delta; From d94fa6efa1746fadff131271a409bb3d52659313 Mon Sep 17 00:00:00 2001 From: X Code Date: Mon, 3 Apr 2023 07:52:38 -0700 Subject: [PATCH 02/27] Upgrade to 7.0.6 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10d679ad..2c4c7a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.0.6] +* Fix line style loss on new line from non string. + # [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. diff --git a/pubspec.yaml b/pubspec.yaml index 6b051d89..da1a7e8b 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.5 +version: 7.0.6 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 0d0340adda35a28b6a5f1bdaa7f82eb8eb406cb3 Mon Sep 17 00:00:00 2001 From: Pwiz Date: Tue, 4 Apr 2023 23:26:31 +0800 Subject: [PATCH 03/27] add TextFieldTapRegion for contextMenu (#1158) Co-authored-by: wangpw2 --- lib/src/widgets/raw_editor.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 474326d7..2c21fa5e 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -125,9 +125,11 @@ class RawEditor extends StatefulWidget { BuildContext context, RawEditorState state, ) { - return AdaptiveTextSelectionToolbar.buttonItems( - buttonItems: state.contextMenuButtonItems, - anchors: state.contextMenuAnchors, + return TextFieldTapRegion( + child: AdaptiveTextSelectionToolbar.buttonItems( + buttonItems: state.contextMenuButtonItems, + anchors: state.contextMenuAnchors, + ), ); } From 765f54107ee23886966c24645910a04de591d0b5 Mon Sep 17 00:00:00 2001 From: X Code Date: Tue, 4 Apr 2023 08:40:57 -0700 Subject: [PATCH 04/27] Upgrade to 7.0.7 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c4c7a80..3940be80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.0.7] +* Add TextFieldTapRegion for contextMenu. + # [7.0.6] * Fix line style loss on new line from non string. diff --git a/pubspec.yaml b/pubspec.yaml index da1a7e8b..9965a90d 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.6 +version: 7.0.7 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 50ad50170cfed19084ca44c4c1d4b0e81bc32aa4 Mon Sep 17 00:00:00 2001 From: Pwiz Date: Wed, 5 Apr 2023 12:30:24 +0800 Subject: [PATCH 05/27] Fix IME position bug with scroller (#1159) --- .../raw_editor_state_text_input_client_mixin.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 5f98d56a..e3b9ff5f 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 @@ -333,14 +333,11 @@ mixin RawEditorStateTextInputClientMixin on EditorState if (hasConnection) { // Asking for renderEditor.size here can cause errors if layout hasn't // occurred yet. So we schedule a post frame callback instead. - SchedulerBinding.instance.addPostFrameCallback((_) { - if (!mounted) { - return; - } - final size = renderEditor.size; - final transform = renderEditor.getTransformTo(null); - _textInputConnection?.setEditableSizeAndTransform(size, transform); - }); + final size = renderEditor.size; + final transform = renderEditor.getTransformTo(null); + _textInputConnection?.setEditableSizeAndTransform(size, transform); + SchedulerBinding.instance + .addPostFrameCallback((_) => _updateSizeAndTransform()); } } } From ccdbfb7bd90e99f43c8f93d0e7f34351d3c64757 Mon Sep 17 00:00:00 2001 From: X Code Date: Tue, 4 Apr 2023 21:32:31 -0700 Subject: [PATCH 06/27] Upgrade to 7.0.8 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3940be80..d7c48713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.0.8] +* Fix IME position bug with scroller. + # [7.0.7] * Add TextFieldTapRegion for contextMenu. diff --git a/pubspec.yaml b/pubspec.yaml index 9965a90d..2148afe6 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.7 +version: 7.0.8 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 216b1ff1a88ca8998ade89e9c0f7338fae875ed1 Mon Sep 17 00:00:00 2001 From: veselv2010 <32619716+veselv2010@users.noreply.github.com> Date: Wed, 5 Apr 2023 18:52:16 +0300 Subject: [PATCH 07/27] fix: Use const constructor for EmbedBuilder (#1160) --- lib/src/widgets/embeds.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/widgets/embeds.dart b/lib/src/widgets/embeds.dart index 8cfe2657..8d72cd5b 100644 --- a/lib/src/widgets/embeds.dart +++ b/lib/src/widgets/embeds.dart @@ -6,6 +6,8 @@ import '../models/themes/quill_icon_theme.dart'; import 'controller.dart'; abstract class EmbedBuilder { + const EmbedBuilder(); + String get key; bool get expanded => true; From f0ce4d05800c30829c14d473827902e294cf4c4e Mon Sep 17 00:00:00 2001 From: X Code Date: Wed, 5 Apr 2023 08:54:31 -0700 Subject: [PATCH 08/27] Upgrade to 7.0.9 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7c48713..bfc19ba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.0.9] +* Use const constructor for EmbedBuilder. + # [7.0.8] * Fix IME position bug with scroller. diff --git a/pubspec.yaml b/pubspec.yaml index 2148afe6..8c90b6d2 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.8 +version: 7.0.9 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From d5984dea1726d8ea21178432b1a2e99dc883d6b2 Mon Sep 17 00:00:00 2001 From: spChief Date: Thu, 6 Apr 2023 20:41:07 +0700 Subject: [PATCH 09/27] fix ordered list numeration with several lists in document (#1163) --- lib/src/widgets/raw_editor.dart | 20 ++++++++++++++++++- .../widgets/style_widgets/number_point.dart | 1 + lib/src/widgets/text_block.dart | 11 +++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 2c21fa5e..f27fd604 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -756,13 +756,28 @@ class RawEditorState extends EditorState List _buildChildren(Document doc, BuildContext context) { final result = []; final indentLevelCounts = {}; + // this need for several ordered list in document + // we need to reset indents Map, if list finished + // List finished when there is node without Attribute.ol in styles + // So in this case we set clearIndents=true and send it + // to the next EditableTextBlock + var prevNodeOl = false; + var clearIndents = false; + for (final node in doc.root.children) { + final attrs = node.style.attributes; + + if (prevNodeOl && attrs[Attribute.list.key] != Attribute.ol) { + clearIndents = true; + } + + prevNodeOl = attrs[Attribute.list.key] == Attribute.ol; + if (node is Line) { final editableTextLine = _getEditableTextLineFromNode(node, context); result.add(Directionality( textDirection: getDirectionOfNode(node), child: editableTextLine)); } else if (node is Block) { - final attrs = node.style.attributes; final editableTextBlock = EditableTextBlock( block: node, controller: controller, @@ -782,11 +797,14 @@ class RawEditorState extends EditorState onLaunchUrl: widget.onLaunchUrl, cursorCont: _cursorCont, indentLevelCounts: indentLevelCounts, + clearIndents: clearIndents, onCheckboxTap: _handleCheckboxTap, readOnly: widget.readOnly, customStyleBuilder: widget.customStyleBuilder); result.add(Directionality( textDirection: getDirectionOfNode(node), child: editableTextBlock)); + + clearIndents = false; } else { throw StateError('Unreachable.'); } diff --git a/lib/src/widgets/style_widgets/number_point.dart b/lib/src/widgets/style_widgets/number_point.dart index 1dbf2261..54d5ebc9 100644 --- a/lib/src/widgets/style_widgets/number_point.dart +++ b/lib/src/widgets/style_widgets/number_point.dart @@ -31,6 +31,7 @@ class QuillNumberPoint extends StatelessWidget { int? level = 0; if (!attrs.containsKey(Attribute.indent.key) && indentLevelCounts.isEmpty) { indentLevelCounts.clear(); + indentLevelCounts[0] = 1; return Container( alignment: AlignmentDirectional.topEnd, width: width, diff --git a/lib/src/widgets/text_block.dart b/lib/src/widgets/text_block.dart index 8ece7cf5..09b2af7c 100644 --- a/lib/src/widgets/text_block.dart +++ b/lib/src/widgets/text_block.dart @@ -68,6 +68,7 @@ class EditableTextBlock extends StatelessWidget { required this.linkActionPicker, required this.cursorCont, required this.indentLevelCounts, + required this.clearIndents, required this.onCheckboxTap, required this.readOnly, this.onLaunchUrl, @@ -91,6 +92,7 @@ class EditableTextBlock extends StatelessWidget { final CustomStyleBuilder? customStyleBuilder; final CursorCont cursorCont; final Map indentLevelCounts; + final bool clearIndents; final Function(int, bool) onCheckboxTap; final bool readOnly; @@ -107,7 +109,7 @@ class EditableTextBlock extends StatelessWidget { decoration: _getDecorationForBlock(block, defaultStyles) ?? const BoxDecoration(), contentPadding: contentPadding, - children: _buildChildren(context, indentLevelCounts)); + children: _buildChildren(context, indentLevelCounts, clearIndents)); } BoxDecoration? _getDecorationForBlock( @@ -122,11 +124,14 @@ class EditableTextBlock extends StatelessWidget { return null; } - List _buildChildren( - BuildContext context, Map indentLevelCounts) { + List _buildChildren(BuildContext context, + Map indentLevelCounts, bool clearIndents) { final defaultStyles = QuillStyles.getStyles(context, false); final count = block.children.length; final children = []; + if (clearIndents) { + indentLevelCounts.clear(); + } var index = 0; for (final line in Iterable.castFrom(block.children)) { index++; From 871b05e2372c54b1fd8b9b92594ef9158eb1ff05 Mon Sep 17 00:00:00 2001 From: X Code Date: Thu, 6 Apr 2023 07:47:17 -0700 Subject: [PATCH 10/27] Upgrade to 7.1.0 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc19ba4..a4e7e9bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.0] +* Fix ordered list numeration with several lists in document. + # [7.0.9] * Use const constructor for EmbedBuilder. diff --git a/pubspec.yaml b/pubspec.yaml index 8c90b6d2..76970dbc 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.9 +version: 7.1.0 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 01271b0cd974f1c6d31effac183ec59627d37b9d Mon Sep 17 00:00:00 2001 From: spChief Date: Fri, 7 Apr 2023 10:22:31 +0700 Subject: [PATCH 11/27] customLinkPrefixes parameter - makes possible to open links with custom protocol (deeplinks) (#1164) --- lib/src/widgets/editor.dart | 8 ++++++++ lib/src/widgets/raw_editor.dart | 8 ++++++-- lib/src/widgets/text_block.dart | 3 +++ lib/src/widgets/text_line.dart | 4 +++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index e733c0e0..d808552d 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -182,6 +182,7 @@ class QuillEditor extends StatefulWidget { this.customShortcuts, this.customActions, this.detectWordBoundary = true, + this.customLinkPrefixes = const [], Key? key}) : super(key: key); @@ -401,6 +402,12 @@ class QuillEditor extends StatefulWidget { final bool detectWordBoundary; + /// Additional list if links prefixes, which must not be prepended + /// with "https://" when [LinkMenuAction.launch] happened + /// + /// Useful for deeplinks + final List customLinkPrefixes; + @override QuillEditorState createState() => QuillEditorState(); } @@ -498,6 +505,7 @@ class QuillEditorState extends State onImagePaste: widget.onImagePaste, customShortcuts: widget.customShortcuts, customActions: widget.customActions, + customLinkPrefixes: widget.customLinkPrefixes, ); final editor = I18n( diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index f27fd604..36af8938 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -78,7 +78,8 @@ class RawEditor extends StatefulWidget { this.linkActionPickerDelegate = defaultLinkActionPickerDelegate, this.customStyleBuilder, this.floatingCursorDisabled = false, - this.onImagePaste}) + this.onImagePaste, + this.customLinkPrefixes = const []}) : 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, @@ -250,6 +251,7 @@ class RawEditor extends StatefulWidget { final LinkActionPickerDelegate linkActionPickerDelegate; final CustomStyleBuilder? customStyleBuilder; final bool floatingCursorDisabled; + final List customLinkPrefixes; @override State createState() => RawEditorState(); @@ -800,7 +802,8 @@ class RawEditorState extends EditorState clearIndents: clearIndents, onCheckboxTap: _handleCheckboxTap, readOnly: widget.readOnly, - customStyleBuilder: widget.customStyleBuilder); + customStyleBuilder: widget.customStyleBuilder, + customLinkPrefixes: widget.customLinkPrefixes); result.add(Directionality( textDirection: getDirectionOfNode(node), child: editableTextBlock)); @@ -824,6 +827,7 @@ class RawEditorState extends EditorState controller: controller, linkActionPicker: _linkActionPicker, onLaunchUrl: widget.onLaunchUrl, + customLinkPrefixes: widget.customLinkPrefixes, ); final editableTextLine = EditableTextLine( node, diff --git a/lib/src/widgets/text_block.dart b/lib/src/widgets/text_block.dart index 09b2af7c..b61ad9cf 100644 --- a/lib/src/widgets/text_block.dart +++ b/lib/src/widgets/text_block.dart @@ -73,6 +73,7 @@ class EditableTextBlock extends StatelessWidget { required this.readOnly, this.onLaunchUrl, this.customStyleBuilder, + this.customLinkPrefixes = const [], Key? key}); final Block block; @@ -95,6 +96,7 @@ class EditableTextBlock extends StatelessWidget { final bool clearIndents; final Function(int, bool) onCheckboxTap; final bool readOnly; + final List customLinkPrefixes; @override Widget build(BuildContext context) { @@ -148,6 +150,7 @@ class EditableTextBlock extends StatelessWidget { controller: controller, linkActionPicker: linkActionPicker, onLaunchUrl: onLaunchUrl, + customLinkPrefixes: customLinkPrefixes, ), _getIndentWidth(), _getSpacingForLine(line, index, count, defaultStyles), diff --git a/lib/src/widgets/text_line.dart b/lib/src/widgets/text_line.dart index b7174a2f..22623f4d 100644 --- a/lib/src/widgets/text_line.dart +++ b/lib/src/widgets/text_line.dart @@ -41,6 +41,7 @@ class TextLine extends StatefulWidget { required this.linkActionPicker, this.textDirection, this.customStyleBuilder, + this.customLinkPrefixes = const [], Key? key, }) : super(key: key); @@ -53,6 +54,7 @@ class TextLine extends StatefulWidget { final CustomStyleBuilder? customStyleBuilder; final ValueChanged? onLaunchUrl; final LinkActionPicker linkActionPicker; + final List customLinkPrefixes; @override State createState() => _TextLineState(); @@ -430,7 +432,7 @@ class _TextLineState extends State { launchUrl ??= _launchUrl; link = link.trim(); - if (!linkPrefixes + if (!(widget.customLinkPrefixes + linkPrefixes) .any((linkPrefix) => link!.toLowerCase().startsWith(linkPrefix))) { link = 'https://$link'; } From 160006c4de9d8ac0bcef6dc4668c68f464fed20b Mon Sep 17 00:00:00 2001 From: X Code Date: Thu, 6 Apr 2023 20:24:05 -0700 Subject: [PATCH 12/27] Upgrade to 7.1.1 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e7e9bd..d659f50d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.1] +* customLinkPrefixes parameter - makes possible to open links with custom protoco. + # [7.1.0] * Fix ordered list numeration with several lists in document. diff --git a/pubspec.yaml b/pubspec.yaml index 76970dbc..4b9b4358 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.1.0 +version: 7.1.1 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 2ea29d56a14b72d2da94eadee992a305b2e49649 Mon Sep 17 00:00:00 2001 From: spChief Date: Fri, 7 Apr 2023 14:56:31 +0700 Subject: [PATCH 13/27] fix non scrollable editor exception, when tapped under content (#1167) --- lib/src/widgets/editor.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index d808552d..4ddbfcae 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -1794,7 +1794,10 @@ class RenderEditableContainerBox extends RenderBox dy += child.size.height; child = childAfter(child); } - throw StateError('No child at offset $offset.'); + + // this case possible, when editor not scrollable, + // but minHeight > content height and tap was under content + return lastChild!; } @override From d517847d8a04536ee305840818312f76a662ce53 Mon Sep 17 00:00:00 2001 From: X Code Date: Fri, 7 Apr 2023 09:45:34 -0700 Subject: [PATCH 14/27] Upgrade to 7.1.2 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d659f50d..cd8acc3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.2] +* Fix non scrollable editor exception, when tapped under content. + # [7.1.1] * customLinkPrefixes parameter - makes possible to open links with custom protoco. diff --git a/pubspec.yaml b/pubspec.yaml index 4b9b4358..2dc6d2ff 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.1.1 +version: 7.1.2 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From d62be3d12aca0110b20ec0cbcbeeeeebe4721dab Mon Sep 17 00:00:00 2001 From: Pwiz Date: Sun, 9 Apr 2023 21:26:45 +0800 Subject: [PATCH 15/27] fix ios cursor bug when word.length==1 (#1168) --- lib/src/widgets/editor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index 4ddbfcae..e1f6d220 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -1135,7 +1135,7 @@ class RenderEditor extends RenderEditableContainerBox start: localWord.start + nodeOffset, end: localWord.end + nodeOffset, ); - if (position.offset - word.start <= 1) { + if (position.offset - word.start <= 1 && word.end != position.offset) { _handleSelectionChange( TextSelection.collapsed(offset: word.start), cause, From 351bcb95c862ba5ce70de09e93ff8224aa8def56 Mon Sep 17 00:00:00 2001 From: X Code Date: Sun, 9 Apr 2023 09:04:20 -0700 Subject: [PATCH 16/27] Upgrade to 7.1.3 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd8acc3b..dd28ddeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.3] +* Fix ios cursor bug when word.length==1. + # [7.1.2] * Fix non scrollable editor exception, when tapped under content. diff --git a/pubspec.yaml b/pubspec.yaml index 2dc6d2ff..7adb8ef7 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.1.2 +version: 7.1.3 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 871b41354429231d0f332f4c1496b612056f1fed Mon Sep 17 00:00:00 2001 From: bohdanudreambit Date: Tue, 11 Apr 2023 17:25:29 +0300 Subject: [PATCH 17/27] fix inserting tab character in lists (#1171) --- lib/src/widgets/raw_editor.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 36af8938..4adb058e 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -665,18 +665,22 @@ class RawEditorState extends EditorState return insertTabCharacter(); } - if (node.isNotEmpty && (node.first as leaf.Text).value.isNotEmpty) { - return insertTabCharacter(); - } - final parentBlock = parent; if (parentBlock.style.containsKey(Attribute.ol.key) || parentBlock.style.containsKey(Attribute.ul.key) || parentBlock.style.containsKey(Attribute.checked.key)) { + if (node.isNotEmpty && (node.first as leaf.Text).value.isNotEmpty + && controller.selection.base.offset > node.documentOffset) { + return insertTabCharacter(); + } controller.indentSelection(!event.isShiftPressed); return KeyEventResult.handled; } + if (node.isNotEmpty && (node.first as leaf.Text).value.isNotEmpty) { + return insertTabCharacter(); + } + return insertTabCharacter(); } From e90e6eb70dc5922bbc1246247a214e507f6995f1 Mon Sep 17 00:00:00 2001 From: X Code Date: Tue, 11 Apr 2023 08:13:01 -0700 Subject: [PATCH 18/27] Upgrade to 7.1.4 --- CHANGELOG.md | 3 +++ lib/src/widgets/raw_editor.dart | 5 +++-- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd28ddeb..6d24d17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.4] +* Fix inserting tab character in lists. + # [7.1.3] * Fix ios cursor bug when word.length==1. diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 4adb058e..9531cbde 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -669,8 +669,9 @@ class RawEditorState extends EditorState if (parentBlock.style.containsKey(Attribute.ol.key) || parentBlock.style.containsKey(Attribute.ul.key) || parentBlock.style.containsKey(Attribute.checked.key)) { - if (node.isNotEmpty && (node.first as leaf.Text).value.isNotEmpty - && controller.selection.base.offset > node.documentOffset) { + if (node.isNotEmpty && + (node.first as leaf.Text).value.isNotEmpty && + controller.selection.base.offset > node.documentOffset) { return insertTabCharacter(); } controller.indentSelection(!event.isShiftPressed); diff --git a/pubspec.yaml b/pubspec.yaml index 7adb8ef7..3a11ea64 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.1.3 +version: 7.1.4 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 539357bc63fe349884837ed7cceaaad792b513f7 Mon Sep 17 00:00:00 2001 From: BambinoUA <45417992+bambinoua@users.noreply.github.com> Date: Sat, 15 Apr 2023 08:17:04 +0300 Subject: [PATCH 19/27] Add tooltips for toolbar buttons. (#1175) * Add `UtilityWidgets` * Implement tooltips for toolbar buttons * Add `tooltip` to `QuillCustomButton` * Add `ToolbarButtons` * Update `EmbedButtonBuilder` with `tooltip` * Implement tooltips in `QuillToolbar` * Add tooltip property to embed buttons (for future) * Update version of flutter_quill in pubspec.yaml * Restore `EmbedButtonBuilder` * Update english translations * Implement translations for tooltips * Update translations for `en-us` * Make `tooltips` nullable to hide tooltips. * Remove `tooltips` property from `QuillToolbar` --- .../lib/embeds/toolbar/camera_button.dart | 2 + .../lib/embeds/toolbar/formula_button.dart | 2 + .../lib/embeds/toolbar/image_button.dart | 2 + .../lib/embeds/toolbar/video_button.dart | 2 + flutter_quill_extensions/pubspec.yaml | 6 +- .../models/themes/quill_custom_button.dart | 9 +- lib/src/translations/toolbar.i18n.dart | 52 +++++++++++ lib/src/utils/widgets.dart | 11 +++ lib/src/widgets/toolbar.dart | 80 +++++++++++++++++ .../widgets/toolbar/clear_format_button.dart | 3 + lib/src/widgets/toolbar/color_button.dart | 3 + lib/src/widgets/toolbar/enum.dart | 30 +++++++ lib/src/widgets/toolbar/history_button.dart | 3 + lib/src/widgets/toolbar/indent_button.dart | 3 + .../widgets/toolbar/link_style_button.dart | 3 + .../toolbar/quill_font_family_button.dart | 34 +++++--- .../toolbar/quill_font_size_button.dart | 34 +++++--- .../widgets/toolbar/quill_icon_button.dart | 33 ++++--- lib/src/widgets/toolbar/search_button.dart | 3 + .../toolbar/select_alignment_button.dart | 87 +++++++++++-------- .../toolbar/select_header_style_button.dart | 62 +++++++------ .../toolbar/toggle_check_list_button.dart | 26 +++--- .../widgets/toolbar/toggle_style_button.dart | 26 +++--- 23 files changed, 387 insertions(+), 129 deletions(-) create mode 100644 lib/src/utils/widgets.dart create mode 100644 lib/src/widgets/toolbar/enum.dart diff --git a/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart index bb906f30..202d97b1 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart @@ -19,6 +19,7 @@ class CameraButton extends StatelessWidget { this.webVideoPickImpl, this.cameraPickSettingSelector, this.iconTheme, + this.tooltip, Key? key, }) : super(key: key); @@ -42,6 +43,7 @@ class CameraButton extends StatelessWidget { final MediaPickSettingSelector? cameraPickSettingSelector; final QuillIconTheme? iconTheme; + final String? tooltip; @override Widget build(BuildContext context) { diff --git a/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart index fb0ab679..70c4987f 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart @@ -9,6 +9,7 @@ class FormulaButton extends StatelessWidget { this.fillColor, this.iconTheme, this.dialogTheme, + this.tooltip, Key? key, }) : super(key: key); @@ -23,6 +24,7 @@ class FormulaButton extends StatelessWidget { final QuillIconTheme? iconTheme; final QuillDialogTheme? dialogTheme; + final String? tooltip; @override Widget build(BuildContext context) { diff --git a/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart index 5cc51aff..8cad519c 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart @@ -17,6 +17,7 @@ class ImageButton extends StatelessWidget { this.mediaPickSettingSelector, this.iconTheme, this.dialogTheme, + this.tooltip, Key? key, }) : super(key: key); @@ -38,6 +39,7 @@ class ImageButton extends StatelessWidget { final QuillIconTheme? iconTheme; final QuillDialogTheme? dialogTheme; + final String? tooltip; @override Widget build(BuildContext context) { diff --git a/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart index e6193622..30e0dbf7 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart @@ -17,6 +17,7 @@ class VideoButton extends StatelessWidget { this.mediaPickSettingSelector, this.iconTheme, this.dialogTheme, + this.tooltip, Key? key, }) : super(key: key); @@ -38,6 +39,7 @@ class VideoButton extends StatelessWidget { final QuillIconTheme? iconTheme; final QuillDialogTheme? dialogTheme; + final String? tooltip; @override Widget build(BuildContext context) { diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index 5ca94b5c..e0e1269d 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - flutter_quill: ^7.0.2 + flutter_quill: ^7.1.4 image_picker: ^0.8.5+3 photo_view: ^0.14.0 @@ -23,10 +23,6 @@ dependencies: string_validator: ^1.0.0 url_launcher: ^6.1.9 -# dependency_overrides: -# flutter_quill: -# path: ../ - dev_dependencies: flutter_test: sdk: flutter diff --git a/lib/src/models/themes/quill_custom_button.dart b/lib/src/models/themes/quill_custom_button.dart index 0dbac618..4ea4e4f5 100644 --- a/lib/src/models/themes/quill_custom_button.dart +++ b/lib/src/models/themes/quill_custom_button.dart @@ -1,11 +1,18 @@ import 'package:flutter/material.dart'; class QuillCustomButton { - const QuillCustomButton({this.icon, this.onTap}); + const QuillCustomButton({ + this.icon, + this.onTap, + this.tooltip, + }); ///The icon widget final IconData? icon; ///The function when the icon is tapped final VoidCallback? onTap; + + /// The button tooltip. + final String? tooltip; } diff --git a/lib/src/translations/toolbar.i18n.dart b/lib/src/translations/toolbar.i18n.dart index 2abcfb21..cb6c7853 100644 --- a/lib/src/translations/toolbar.i18n.dart +++ b/lib/src/translations/toolbar.i18n.dart @@ -35,6 +35,32 @@ extension Localization on String { 'Next': 'Next', 'Camera': 'Camera', 'Video': 'Video', + 'Undo': 'Undo', + 'Redo': 'Redo', + 'Font family': 'Font family', + 'Font size': 'Font size', + 'Bold': 'Bold', + 'Italic': 'Italic', + 'Underline': 'Underline', + 'Strike through': 'Strike through', + 'Inline code': 'Inline code', + 'Font color': 'Font color', + 'Background color': 'Background color', + 'Clear format': 'Clear format', + 'Align left': 'Align left', + 'Align center': 'Align center', + 'Align right': 'Align right', + 'Justify win width': 'Justify win width', + 'Text direction': 'Text direction', + 'Header style': 'Header style', + 'Numbered list': 'Numbered list', + 'Bullet list': 'Bullet list', + 'Checked list': 'Checked list', + 'Code block': 'Code block', + 'Quote': 'Quote', + 'Increase indent': 'Increase indent', + 'Decrease indent': 'Decrease indent', + 'Insert URL': 'Insert URL', }, 'en_us': { 'Paste a link': 'Paste a link', @@ -68,6 +94,32 @@ extension Localization on String { 'Next': 'Next', 'Camera': 'Camera', 'Video': 'Video', + 'Undo': 'Undo', + 'Redo': 'Redo', + 'Font family': 'Font family', + 'Font size': 'Font size', + 'Bold': 'Bold', + 'Italic': 'Italic', + 'Underline': 'Underline', + 'Strike through': 'Strike through', + 'Inline code': 'Inline code', + 'Font color': 'Font color', + 'Background color': 'Background color', + 'Clear format': 'Clear format', + 'Align left': 'Align left', + 'Align center': 'Align center', + 'Align right': 'Align right', + 'Justify win width': 'Justify win width', + 'Text direction': 'Text direction', + 'Header style': 'Header style', + 'Numbered list': 'Numbered list', + 'Bullet list': 'Bullet list', + 'Checked list': 'Checked list', + 'Code block': 'Code block', + 'Quote': 'Quote', + 'Increase indent': 'Increase indent', + 'Decrease indent': 'Decrease indent', + 'Insert URL': 'Insert URL', }, 'ar': { 'Paste a link': 'نسخ الرابط', diff --git a/lib/src/utils/widgets.dart b/lib/src/utils/widgets.dart new file mode 100644 index 00000000..1b560e84 --- /dev/null +++ b/lib/src/utils/widgets.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +/// Provides utiulity widgets. +abstract class UtilityWidgets { + /// Conditionally wraps the [child] with [Tooltip] widget if [message] + /// is not null and not empty. + static Widget maybeTooltip({required Widget child, String? message}) => + (message ?? '').isNotEmpty + ? Tooltip(message: message!, child: child) + : child; +} diff --git a/lib/src/widgets/toolbar.dart b/lib/src/widgets/toolbar.dart index 58171ad1..08c7c6fe 100644 --- a/lib/src/widgets/toolbar.dart +++ b/lib/src/widgets/toolbar.dart @@ -12,6 +12,7 @@ import 'embeds.dart'; import 'toolbar/arrow_indicated_button_list.dart'; import 'toolbar/clear_format_button.dart'; import 'toolbar/color_button.dart'; +import 'toolbar/enum.dart'; import 'toolbar/history_button.dart'; import 'toolbar/indent_button.dart'; import 'toolbar/link_style_button.dart'; @@ -117,6 +118,20 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { /// Is called after whatever logic the button performs has run. VoidCallback? afterButtonPressed, + ///Map of tooltips for toolbar buttons + /// + ///The example is: + ///```dart + /// tooltips = { + /// ToolbarButtons.undo: 'Undo', + /// ToolbarButtons.redo: 'Redo', + /// } + /// + ///``` + /// + /// To disable tooltips just pass empty map as well. + Map? tooltips, + /// The locale to use for the editor toolbar, defaults to system locale /// More at https://github.com/singerdmx/flutter-quill#translation Locale? locale, @@ -172,6 +187,39 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { 'Clear'.i18n: 'Clear' }; + //default button tooltips + final buttonTooltips = tooltips ?? + { + ToolbarButtons.undo: 'Undo'.i18n, + ToolbarButtons.redo: 'Redo'.i18n, + ToolbarButtons.fontFamily: 'Font family'.i18n, + ToolbarButtons.fontSize: 'Font size'.i18n, + ToolbarButtons.bold: 'Bold'.i18n, + ToolbarButtons.italic: 'Italic'.i18n, + ToolbarButtons.small: 'Small'.i18n, + ToolbarButtons.underline: 'Underline'.i18n, + ToolbarButtons.strikeThrough: 'Strike through'.i18n, + ToolbarButtons.inlineCode: 'Inline code'.i18n, + ToolbarButtons.color: 'Font color'.i18n, + ToolbarButtons.backgroundColor: 'Background color'.i18n, + ToolbarButtons.clearFormat: 'Clear format'.i18n, + ToolbarButtons.leftAlignment: 'Align left'.i18n, + ToolbarButtons.centerAlignment: 'Align center'.i18n, + ToolbarButtons.rightAlignment: 'Align right'.i18n, + ToolbarButtons.justifyAlignment: 'Justify win width'.i18n, + ToolbarButtons.direction: 'Text direction'.i18n, + ToolbarButtons.headerStyle: 'Header style'.i18n, + ToolbarButtons.listNumbers: 'Numbered list'.i18n, + ToolbarButtons.listBullets: 'Bullet list'.i18n, + ToolbarButtons.listChecks: 'Checked list'.i18n, + ToolbarButtons.codeBlock: 'Code block'.i18n, + ToolbarButtons.quote: 'Quote'.i18n, + ToolbarButtons.indentIncrease: 'Increase indent'.i18n, + ToolbarButtons.indentDecrease: 'Decrease indent'.i18n, + ToolbarButtons.link: 'Insert URL'.i18n, + ToolbarButtons.search: 'Search'.i18n, + }; + return QuillToolbar( key: key, axis: axis, @@ -189,6 +237,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { HistoryButton( icon: Icons.undo_outlined, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.undo], controller: controller, undo: true, iconTheme: iconTheme, @@ -198,6 +247,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { HistoryButton( icon: Icons.redo_outlined, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.redo], controller: controller, undo: false, iconTheme: iconTheme, @@ -207,6 +257,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { QuillFontFamilyButton( iconTheme: iconTheme, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.fontFamily], attribute: Attribute.font, controller: controller, items: [ @@ -231,6 +282,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { QuillFontSizeButton( iconTheme: iconTheme, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.fontSize], attribute: Attribute.size, controller: controller, items: [ @@ -255,6 +307,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { attribute: Attribute.bold, icon: Icons.format_bold, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.bold], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -264,6 +317,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { attribute: Attribute.italic, icon: Icons.format_italic, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.italic], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -273,6 +327,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { attribute: Attribute.small, icon: Icons.format_size, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.small], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -282,6 +337,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { attribute: Attribute.underline, icon: Icons.format_underline, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.underline], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -291,6 +347,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { attribute: Attribute.strikeThrough, icon: Icons.format_strikethrough, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.strikeThrough], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -300,6 +357,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { attribute: Attribute.inlineCode, icon: Icons.code, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.inlineCode], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -308,6 +366,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { ColorButton( icon: Icons.color_lens, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.color], controller: controller, background: false, iconTheme: iconTheme, @@ -317,6 +376,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { ColorButton( icon: Icons.format_color_fill, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.backgroundColor], controller: controller, background: true, iconTheme: iconTheme, @@ -326,6 +386,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { ClearFormatButton( icon: Icons.format_clear, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.clearFormat], controller: controller, iconTheme: iconTheme, afterButtonPressed: afterButtonPressed, @@ -344,6 +405,13 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showAlignmentButtons) SelectAlignmentButton( controller: controller, + tooltips: Map.of(buttonTooltips) + ..removeWhere((key, value) => ![ + ToolbarButtons.leftAlignment, + ToolbarButtons.centerAlignment, + ToolbarButtons.rightAlignment, + ToolbarButtons.justifyAlignment, + ].contains(key)), iconSize: toolbarIconSize, iconTheme: iconTheme, showLeftAlignment: showLeftAlignment, @@ -355,6 +423,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showDirection) ToggleStyleButton( attribute: Attribute.rtl, + tooltip: buttonTooltips[ToolbarButtons.direction], controller: controller, icon: Icons.format_textdirection_r_to_l, iconSize: toolbarIconSize, @@ -370,6 +439,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { _dividerOnAxis(axis), if (showHeaderStyle) SelectHeaderStyleButton( + tooltip: buttonTooltips[ToolbarButtons.headerStyle], controller: controller, axis: axis, iconSize: toolbarIconSize, @@ -386,6 +456,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showListNumbers) ToggleStyleButton( attribute: Attribute.ol, + tooltip: buttonTooltips[ToolbarButtons.listNumbers], controller: controller, icon: Icons.format_list_numbered, iconSize: toolbarIconSize, @@ -395,6 +466,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showListBullets) ToggleStyleButton( attribute: Attribute.ul, + tooltip: buttonTooltips[ToolbarButtons.listBullets], controller: controller, icon: Icons.format_list_bulleted, iconSize: toolbarIconSize, @@ -404,6 +476,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showListCheck) ToggleCheckListButton( attribute: Attribute.unchecked, + tooltip: buttonTooltips[ToolbarButtons.listChecks], controller: controller, icon: Icons.check_box, iconSize: toolbarIconSize, @@ -413,6 +486,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showCodeBlock) ToggleStyleButton( attribute: Attribute.codeBlock, + tooltip: buttonTooltips[ToolbarButtons.codeBlock], controller: controller, icon: Icons.code, iconSize: toolbarIconSize, @@ -426,6 +500,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showQuote) ToggleStyleButton( attribute: Attribute.blockQuote, + tooltip: buttonTooltips[ToolbarButtons.quote], controller: controller, icon: Icons.format_quote, iconSize: toolbarIconSize, @@ -436,6 +511,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { IndentButton( icon: Icons.format_indent_increase, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.indentIncrease], controller: controller, isIncrease: true, iconTheme: iconTheme, @@ -445,6 +521,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { IndentButton( icon: Icons.format_indent_decrease, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.indentDecrease], controller: controller, isIncrease: false, iconTheme: iconTheme, @@ -454,6 +531,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { _dividerOnAxis(axis), if (showLink) LinkStyleButton( + tooltip: buttonTooltips[ToolbarButtons.link], controller: controller, iconSize: toolbarIconSize, iconTheme: iconTheme, @@ -464,6 +542,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { SearchButton( icon: Icons.search, iconSize: toolbarIconSize, + tooltip: buttonTooltips[ToolbarButtons.search], controller: controller, iconTheme: iconTheme, dialogTheme: dialogTheme, @@ -477,6 +556,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { hoverElevation: 0, size: toolbarIconSize * kIconButtonFactor, icon: Icon(customButton.icon, size: toolbarIconSize), + tooltip: customButton.tooltip, borderRadius: iconTheme?.borderRadius ?? 2, onPressed: customButton.onTap, afterPressed: afterButtonPressed, diff --git a/lib/src/widgets/toolbar/clear_format_button.dart b/lib/src/widgets/toolbar/clear_format_button.dart index f601bd28..14610232 100644 --- a/lib/src/widgets/toolbar/clear_format_button.dart +++ b/lib/src/widgets/toolbar/clear_format_button.dart @@ -12,6 +12,7 @@ class ClearFormatButton extends StatefulWidget { this.iconSize = kDefaultIconSize, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -22,6 +23,7 @@ class ClearFormatButton extends StatefulWidget { final QuillIconTheme? iconTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _ClearFormatButtonState createState() => _ClearFormatButtonState(); @@ -36,6 +38,7 @@ class _ClearFormatButtonState extends State { final fillColor = widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; return QuillIconButton( + tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, size: widget.iconSize * kIconButtonFactor, diff --git a/lib/src/widgets/toolbar/color_button.dart b/lib/src/widgets/toolbar/color_button.dart index 087ae94f..60f0a591 100644 --- a/lib/src/widgets/toolbar/color_button.dart +++ b/lib/src/widgets/toolbar/color_button.dart @@ -21,6 +21,7 @@ class ColorButton extends StatefulWidget { this.iconSize = kDefaultIconSize, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -30,6 +31,7 @@ class ColorButton extends StatefulWidget { final QuillController controller; final QuillIconTheme? iconTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _ColorButtonState createState() => _ColorButtonState(); @@ -119,6 +121,7 @@ class _ColorButtonState extends State { : (widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor); return QuillIconButton( + tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, size: widget.iconSize * kIconButtonFactor, diff --git a/lib/src/widgets/toolbar/enum.dart b/lib/src/widgets/toolbar/enum.dart new file mode 100644 index 00000000..197bea56 --- /dev/null +++ b/lib/src/widgets/toolbar/enum.dart @@ -0,0 +1,30 @@ +enum ToolbarButtons { + undo, + redo, + fontFamily, + fontSize, + bold, + italic, + small, + underline, + strikeThrough, + inlineCode, + color, + backgroundColor, + clearFormat, + centerAlignment, + leftAlignment, + rightAlignment, + justifyAlignment, + direction, + headerStyle, + listNumbers, + listBullets, + listChecks, + codeBlock, + quote, + indentIncrease, + indentDecrease, + link, + search, +} diff --git a/lib/src/widgets/toolbar/history_button.dart b/lib/src/widgets/toolbar/history_button.dart index a3abc7f2..6d3c29ad 100644 --- a/lib/src/widgets/toolbar/history_button.dart +++ b/lib/src/widgets/toolbar/history_button.dart @@ -12,6 +12,7 @@ class HistoryButton extends StatefulWidget { this.iconSize = kDefaultIconSize, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -21,6 +22,7 @@ class HistoryButton extends StatefulWidget { final QuillController controller; final QuillIconTheme? iconTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _HistoryButtonState createState() => _HistoryButtonState(); @@ -41,6 +43,7 @@ class _HistoryButtonState extends State { _setIconColor(); }); return QuillIconButton( + tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, size: widget.iconSize * 1.77, diff --git a/lib/src/widgets/toolbar/indent_button.dart b/lib/src/widgets/toolbar/indent_button.dart index 129ef8b1..1ce83e99 100644 --- a/lib/src/widgets/toolbar/indent_button.dart +++ b/lib/src/widgets/toolbar/indent_button.dart @@ -12,6 +12,7 @@ class IndentButton extends StatefulWidget { this.iconSize = kDefaultIconSize, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -22,6 +23,7 @@ class IndentButton extends StatefulWidget { final VoidCallback? afterButtonPressed; final QuillIconTheme? iconTheme; + final String? tooltip; @override _IndentButtonState createState() => _IndentButtonState(); @@ -37,6 +39,7 @@ class _IndentButtonState extends State { final iconFillColor = widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; return QuillIconButton( + tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, size: widget.iconSize * 1.77, diff --git a/lib/src/widgets/toolbar/link_style_button.dart b/lib/src/widgets/toolbar/link_style_button.dart index f48a15e4..a0865941 100644 --- a/lib/src/widgets/toolbar/link_style_button.dart +++ b/lib/src/widgets/toolbar/link_style_button.dart @@ -17,6 +17,7 @@ class LinkStyleButton extends StatefulWidget { this.iconTheme, this.dialogTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -26,6 +27,7 @@ class LinkStyleButton extends StatefulWidget { final QuillIconTheme? iconTheme; final QuillDialogTheme? dialogTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _LinkStyleButtonState createState() => _LinkStyleButtonState(); @@ -63,6 +65,7 @@ class _LinkStyleButtonState extends State { final isToggled = _getLinkAttributeValue() != null; final pressedHandler = () => _openLinkDialog(context); return QuillIconButton( + tooltip: widget.tooltip, highlightElevation: 0, hoverElevation: 0, size: widget.iconSize * kIconButtonFactor, diff --git a/lib/src/widgets/toolbar/quill_font_family_button.dart b/lib/src/widgets/toolbar/quill_font_family_button.dart index 88b3dda4..bdddc832 100644 --- a/lib/src/widgets/toolbar/quill_font_family_button.dart +++ b/lib/src/widgets/toolbar/quill_font_family_button.dart @@ -4,6 +4,7 @@ import '../../models/documents/attribute.dart'; import '../../models/documents/style.dart'; import '../../models/themes/quill_icon_theme.dart'; import '../../translations/toolbar.i18n.dart'; +import '../../utils/widgets.dart'; import '../controller.dart'; class QuillFontFamilyButton extends StatefulWidget { @@ -19,6 +20,7 @@ class QuillFontFamilyButton extends StatefulWidget { this.highlightElevation = 1, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -33,6 +35,7 @@ class QuillFontFamilyButton extends StatefulWidget { final Attribute attribute; final QuillController controller; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _QuillFontFamilyButtonState createState() => _QuillFontFamilyButtonState(); @@ -88,20 +91,23 @@ class _QuillFontFamilyButtonState extends State { Widget build(BuildContext context) { return ConstrainedBox( constraints: BoxConstraints.tightFor(height: widget.iconSize * 1.81), - child: RawMaterialButton( - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(widget.iconTheme?.borderRadius ?? 2)), - fillColor: widget.fillColor, - elevation: 0, - hoverElevation: widget.hoverElevation, - highlightElevation: widget.hoverElevation, - onPressed: () { - _showMenu(); - widget.afterButtonPressed?.call(); - }, - child: _buildContent(context), + child: UtilityWidgets.maybeTooltip( + message: widget.tooltip, + child: RawMaterialButton( + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(widget.iconTheme?.borderRadius ?? 2)), + fillColor: widget.fillColor, + elevation: 0, + hoverElevation: widget.hoverElevation, + highlightElevation: widget.hoverElevation, + onPressed: () { + _showMenu(); + widget.afterButtonPressed?.call(); + }, + child: _buildContent(context), + ), ), ); } diff --git a/lib/src/widgets/toolbar/quill_font_size_button.dart b/lib/src/widgets/toolbar/quill_font_size_button.dart index da6fa187..f82175c2 100644 --- a/lib/src/widgets/toolbar/quill_font_size_button.dart +++ b/lib/src/widgets/toolbar/quill_font_size_button.dart @@ -5,6 +5,7 @@ import '../../models/documents/style.dart'; import '../../models/themes/quill_icon_theme.dart'; import '../../translations/toolbar.i18n.dart'; import '../../utils/font.dart'; +import '../../utils/widgets.dart'; import '../controller.dart'; class QuillFontSizeButton extends StatefulWidget { @@ -20,6 +21,7 @@ class QuillFontSizeButton extends StatefulWidget { this.highlightElevation = 1, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -34,6 +36,7 @@ class QuillFontSizeButton extends StatefulWidget { final Attribute attribute; final QuillController controller; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _QuillFontSizeButtonState createState() => _QuillFontSizeButtonState(); @@ -89,20 +92,23 @@ class _QuillFontSizeButtonState extends State { Widget build(BuildContext context) { return ConstrainedBox( constraints: BoxConstraints.tightFor(height: widget.iconSize * 1.81), - child: RawMaterialButton( - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(widget.iconTheme?.borderRadius ?? 2)), - fillColor: widget.fillColor, - elevation: 0, - hoverElevation: widget.hoverElevation, - highlightElevation: widget.hoverElevation, - onPressed: () { - _showMenu(); - widget.afterButtonPressed?.call(); - }, - child: _buildContent(context), + child: UtilityWidgets.maybeTooltip( + message: widget.tooltip, + child: RawMaterialButton( + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(widget.iconTheme?.borderRadius ?? 2)), + fillColor: widget.fillColor, + elevation: 0, + hoverElevation: widget.hoverElevation, + highlightElevation: widget.hoverElevation, + onPressed: () { + _showMenu(); + widget.afterButtonPressed?.call(); + }, + child: _buildContent(context), + ), ), ); } diff --git a/lib/src/widgets/toolbar/quill_icon_button.dart b/lib/src/widgets/toolbar/quill_icon_button.dart index 7714187c..86c5b30b 100644 --- a/lib/src/widgets/toolbar/quill_icon_button.dart +++ b/lib/src/widgets/toolbar/quill_icon_button.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import '../../utils/widgets.dart'; + class QuillIconButton extends StatelessWidget { const QuillIconButton({ required this.onPressed, @@ -10,6 +12,7 @@ class QuillIconButton extends StatelessWidget { this.hoverElevation = 1, this.highlightElevation = 1, this.borderRadius = 2, + this.tooltip, Key? key, }) : super(key: key); @@ -21,24 +24,28 @@ class QuillIconButton extends StatelessWidget { final double hoverElevation; final double highlightElevation; final double borderRadius; + final String? tooltip; @override Widget build(BuildContext context) { return ConstrainedBox( constraints: BoxConstraints.tightFor(width: size, height: size), - child: RawMaterialButton( - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius)), - fillColor: fillColor, - elevation: 0, - hoverElevation: hoverElevation, - highlightElevation: hoverElevation, - onPressed: () { - onPressed?.call(); - afterPressed?.call(); - }, - child: icon, + child: UtilityWidgets.maybeTooltip( + message: tooltip, + child: RawMaterialButton( + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius)), + fillColor: fillColor, + elevation: 0, + hoverElevation: hoverElevation, + highlightElevation: hoverElevation, + onPressed: () { + onPressed?.call(); + afterPressed?.call(); + }, + child: icon, + ), ), ); } diff --git a/lib/src/widgets/toolbar/search_button.dart b/lib/src/widgets/toolbar/search_button.dart index b9436bf1..9233cf45 100644 --- a/lib/src/widgets/toolbar/search_button.dart +++ b/lib/src/widgets/toolbar/search_button.dart @@ -15,6 +15,7 @@ class SearchButton extends StatelessWidget { this.iconTheme, this.dialogTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -27,6 +28,7 @@ class SearchButton extends StatelessWidget { final QuillDialogTheme? dialogTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override Widget build(BuildContext context) { @@ -37,6 +39,7 @@ class SearchButton extends StatelessWidget { iconTheme?.iconUnselectedFillColor ?? (fillColor ?? theme.canvasColor); return QuillIconButton( + tooltip: tooltip, icon: Icon(icon, size: iconSize, color: iconColor), highlightElevation: 0, hoverElevation: 0, diff --git a/lib/src/widgets/toolbar/select_alignment_button.dart b/lib/src/widgets/toolbar/select_alignment_button.dart index 596b095d..4d22b2cf 100644 --- a/lib/src/widgets/toolbar/select_alignment_button.dart +++ b/lib/src/widgets/toolbar/select_alignment_button.dart @@ -4,8 +4,10 @@ import 'package:flutter/material.dart'; import '../../models/documents/attribute.dart'; import '../../models/documents/style.dart'; import '../../models/themes/quill_icon_theme.dart'; +import '../../utils/widgets.dart'; import '../controller.dart'; import '../toolbar.dart'; +import 'enum.dart'; class SelectAlignmentButton extends StatefulWidget { const SelectAlignmentButton({ @@ -17,6 +19,7 @@ class SelectAlignmentButton extends StatefulWidget { this.showRightAlignment, this.showJustifyAlignment, this.afterButtonPressed, + this.tooltips = const {}, Key? key, }) : super(key: key); @@ -29,6 +32,7 @@ class SelectAlignmentButton extends StatefulWidget { final bool? showRightAlignment; final bool? showJustifyAlignment; final VoidCallback? afterButtonPressed; + final Map tooltips; @override _SelectAlignmentButtonState createState() => _SelectAlignmentButtonState(); @@ -74,6 +78,16 @@ class _SelectAlignmentButtonState extends State { if (widget.showRightAlignment!) Attribute.rightAlignment.value!, if (widget.showJustifyAlignment!) Attribute.justifyAlignment.value!, ]; + final _valueToButtons = { + if (widget.showLeftAlignment!) + Attribute.leftAlignment: ToolbarButtons.leftAlignment, + if (widget.showCenterAlignment!) + Attribute.centerAlignment: ToolbarButtons.centerAlignment, + if (widget.showRightAlignment!) + Attribute.rightAlignment: ToolbarButtons.rightAlignment, + if (widget.showJustifyAlignment!) + Attribute.justifyAlignment: ToolbarButtons.justifyAlignment, + }; final theme = Theme.of(context); @@ -93,40 +107,45 @@ class _SelectAlignmentButtonState extends State { width: widget.iconSize * kIconButtonFactor, height: widget.iconSize * kIconButtonFactor, ), - child: RawMaterialButton( - hoverElevation: 0, - highlightElevation: 0, - elevation: 0, - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - widget.iconTheme?.borderRadius ?? 2)), - fillColor: _valueToText[_value] == _valueString[index] - ? (widget.iconTheme?.iconSelectedFillColor ?? - Theme.of(context).primaryColor) - : (widget.iconTheme?.iconUnselectedFillColor ?? - theme.canvasColor), - onPressed: () { - _valueAttribute[index] == Attribute.leftAlignment - ? widget.controller - .formatSelection(Attribute.clone(Attribute.align, null)) - : widget.controller.formatSelection(_valueAttribute[index]); - widget.afterButtonPressed?.call(); - }, - child: Icon( - _valueString[index] == Attribute.leftAlignment.value - ? Icons.format_align_left - : _valueString[index] == Attribute.centerAlignment.value - ? Icons.format_align_center - : _valueString[index] == Attribute.rightAlignment.value - ? Icons.format_align_right - : Icons.format_align_justify, - size: widget.iconSize, - color: _valueToText[_value] == _valueString[index] - ? (widget.iconTheme?.iconSelectedColor ?? - theme.primaryIconTheme.color) - : (widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color), + child: UtilityWidgets.maybeTooltip( + message: widget.tooltips[_valueToButtons[_valueAttribute[index]]], + child: RawMaterialButton( + hoverElevation: 0, + highlightElevation: 0, + elevation: 0, + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + widget.iconTheme?.borderRadius ?? 2)), + fillColor: _valueToText[_value] == _valueString[index] + ? (widget.iconTheme?.iconSelectedFillColor ?? + Theme.of(context).primaryColor) + : (widget.iconTheme?.iconUnselectedFillColor ?? + theme.canvasColor), + onPressed: () { + _valueAttribute[index] == Attribute.leftAlignment + ? widget.controller.formatSelection( + Attribute.clone(Attribute.align, null)) + : widget.controller + .formatSelection(_valueAttribute[index]); + widget.afterButtonPressed?.call(); + }, + child: Icon( + _valueString[index] == Attribute.leftAlignment.value + ? Icons.format_align_left + : _valueString[index] == Attribute.centerAlignment.value + ? Icons.format_align_center + : _valueString[index] == + Attribute.rightAlignment.value + ? Icons.format_align_right + : Icons.format_align_justify, + size: widget.iconSize, + color: _valueToText[_value] == _valueString[index] + ? (widget.iconTheme?.iconSelectedColor ?? + theme.primaryIconTheme.color) + : (widget.iconTheme?.iconUnselectedColor ?? + theme.iconTheme.color), + ), ), ), ), diff --git a/lib/src/widgets/toolbar/select_header_style_button.dart b/lib/src/widgets/toolbar/select_header_style_button.dart index f27998b8..986abc22 100644 --- a/lib/src/widgets/toolbar/select_header_style_button.dart +++ b/lib/src/widgets/toolbar/select_header_style_button.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import '../../models/documents/attribute.dart'; import '../../models/documents/style.dart'; import '../../models/themes/quill_icon_theme.dart'; +import '../../utils/widgets.dart'; import '../controller.dart'; import '../toolbar.dart'; @@ -20,6 +21,7 @@ class SelectHeaderStyleButton extends StatefulWidget { Attribute.h3, ], this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -29,6 +31,7 @@ class SelectHeaderStyleButton extends StatefulWidget { final QuillIconTheme? iconTheme; final List attributes; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _SelectHeaderStyleButtonState createState() => @@ -79,34 +82,37 @@ class _SelectHeaderStyleButtonState extends State { width: widget.iconSize * kIconButtonFactor, height: widget.iconSize * kIconButtonFactor, ), - child: RawMaterialButton( - hoverElevation: 0, - highlightElevation: 0, - elevation: 0, - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(widget.iconTheme?.borderRadius ?? 2)), - fillColor: isSelected - ? (widget.iconTheme?.iconSelectedFillColor ?? - Theme.of(context).primaryColor) - : (widget.iconTheme?.iconUnselectedFillColor ?? - theme.canvasColor), - onPressed: () { - final _attribute = _selectedAttribute == attribute - ? Attribute.header - : attribute; - widget.controller.formatSelection(_attribute); - widget.afterButtonPressed?.call(); - }, - child: Text( - _valueToText[attribute] ?? '', - style: style.copyWith( - color: isSelected - ? (widget.iconTheme?.iconSelectedColor ?? - theme.primaryIconTheme.color) - : (widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color), + child: UtilityWidgets.maybeTooltip( + message: widget.tooltip, + child: RawMaterialButton( + hoverElevation: 0, + highlightElevation: 0, + elevation: 0, + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + widget.iconTheme?.borderRadius ?? 2)), + fillColor: isSelected + ? (widget.iconTheme?.iconSelectedFillColor ?? + Theme.of(context).primaryColor) + : (widget.iconTheme?.iconUnselectedFillColor ?? + theme.canvasColor), + onPressed: () { + final _attribute = _selectedAttribute == attribute + ? Attribute.header + : attribute; + widget.controller.formatSelection(_attribute); + widget.afterButtonPressed?.call(); + }, + child: Text( + _valueToText[attribute] ?? '', + style: style.copyWith( + color: isSelected + ? (widget.iconTheme?.iconSelectedColor ?? + theme.primaryIconTheme.color) + : (widget.iconTheme?.iconUnselectedColor ?? + theme.iconTheme.color), + ), ), ), ), diff --git a/lib/src/widgets/toolbar/toggle_check_list_button.dart b/lib/src/widgets/toolbar/toggle_check_list_button.dart index 037c47cd..6912916b 100644 --- a/lib/src/widgets/toolbar/toggle_check_list_button.dart +++ b/lib/src/widgets/toolbar/toggle_check_list_button.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../models/documents/attribute.dart'; import '../../models/documents/style.dart'; import '../../models/themes/quill_icon_theme.dart'; +import '../../utils/widgets.dart'; import '../controller.dart'; import '../toolbar.dart'; @@ -16,6 +17,7 @@ class ToggleCheckListButton extends StatefulWidget { this.childBuilder = defaultToggleStyleButtonBuilder, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -32,6 +34,7 @@ class ToggleCheckListButton extends StatefulWidget { final QuillIconTheme? iconTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _ToggleCheckListButtonState createState() => _ToggleCheckListButtonState(); @@ -91,16 +94,19 @@ class _ToggleCheckListButtonState extends State { @override Widget build(BuildContext context) { - return widget.childBuilder( - context, - Attribute.unchecked, - widget.icon, - widget.fillColor, - _isToggled, - _toggleAttribute, - widget.afterButtonPressed, - widget.iconSize, - widget.iconTheme, + return UtilityWidgets.maybeTooltip( + message: widget.tooltip, + child: widget.childBuilder( + context, + Attribute.unchecked, + widget.icon, + widget.fillColor, + _isToggled, + _toggleAttribute, + widget.afterButtonPressed, + widget.iconSize, + widget.iconTheme, + ), ); } diff --git a/lib/src/widgets/toolbar/toggle_style_button.dart b/lib/src/widgets/toolbar/toggle_style_button.dart index 176b96b0..1eb0eb57 100644 --- a/lib/src/widgets/toolbar/toggle_style_button.dart +++ b/lib/src/widgets/toolbar/toggle_style_button.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../models/documents/attribute.dart'; import '../../models/documents/style.dart'; import '../../models/themes/quill_icon_theme.dart'; +import '../../utils/widgets.dart'; import '../controller.dart'; import '../toolbar.dart'; @@ -28,6 +29,7 @@ class ToggleStyleButton extends StatefulWidget { this.childBuilder = defaultToggleStyleButtonBuilder, this.iconTheme, this.afterButtonPressed, + this.tooltip, Key? key, }) : super(key: key); @@ -46,6 +48,7 @@ class ToggleStyleButton extends StatefulWidget { final QuillIconTheme? iconTheme; final VoidCallback? afterButtonPressed; + final String? tooltip; @override _ToggleStyleButtonState createState() => _ToggleStyleButtonState(); @@ -65,16 +68,19 @@ class _ToggleStyleButtonState extends State { @override Widget build(BuildContext context) { - return widget.childBuilder( - context, - widget.attribute, - widget.icon, - widget.fillColor, - _isToggled, - _toggleAttribute, - widget.afterButtonPressed, - widget.iconSize, - widget.iconTheme, + return UtilityWidgets.maybeTooltip( + message: widget.tooltip, + child: widget.childBuilder( + context, + widget.attribute, + widget.icon, + widget.fillColor, + _isToggled, + _toggleAttribute, + widget.afterButtonPressed, + widget.iconSize, + widget.iconTheme, + ), ); } From f7e1a50df1ac306e829dec88a70338a31e8603f1 Mon Sep 17 00:00:00 2001 From: Cheryl Date: Fri, 14 Apr 2023 22:29:34 -0700 Subject: [PATCH 20/27] Upgrade to 7.1.5 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d24d17b..3eb12046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.5] +* Add tooltips for toolbar buttons. + # [7.1.4] * Fix inserting tab character in lists. diff --git a/pubspec.yaml b/pubspec.yaml index 3a11ea64..c6d50420 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.1.4 +version: 7.1.5 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 3f7437b863aee2cd2329d7fb57151b1623f1e067 Mon Sep 17 00:00:00 2001 From: bohdanudreambit Date: Sat, 15 Apr 2023 19:21:09 +0300 Subject: [PATCH 21/27] add enableUnfocusOnTapOutside field to RawEditor and Editor widgets (#1177) --- lib/src/widgets/editor.dart | 5 +++++ lib/src/widgets/raw_editor.dart | 3 +++ 2 files changed, 8 insertions(+) diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index e1f6d220..2a1bc5e3 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -182,6 +182,7 @@ class QuillEditor extends StatefulWidget { this.customShortcuts, this.customActions, this.detectWordBoundary = true, + this.enableUnfocusOnTapOutside = true, this.customLinkPrefixes = const [], Key? key}) : super(key: key); @@ -246,6 +247,9 @@ class QuillEditor extends StatefulWidget { /// Defaults to `false`. Cannot be `null`. final bool autoFocus; + /// Whether focus should be revoked on tap outside the editor. + final bool enableUnfocusOnTapOutside; + /// Whether to show cursor. /// /// The cursor refers to the blinking caret when the editor is focused. @@ -506,6 +510,7 @@ class QuillEditorState extends State customShortcuts: widget.customShortcuts, customActions: widget.customActions, customLinkPrefixes: widget.customLinkPrefixes, + enableUnfocusOnTapOutside: widget.enableUnfocusOnTapOutside, ); final editor = I18n( diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 9531cbde..64059983 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -72,6 +72,7 @@ class RawEditor extends StatefulWidget { this.customActions, this.expands = false, this.autoFocus = false, + this.enableUnfocusOnTapOutside = true, this.keyboardAppearance = Brightness.light, this.enableInteractiveSelection = true, this.scrollPhysics, @@ -95,6 +96,7 @@ class RawEditor extends StatefulWidget { final ScrollController scrollController; final bool scrollable; final double scrollBottomInset; + final bool enableUnfocusOnTapOutside; /// Additional space around the editor contents. final EdgeInsetsGeometry padding; @@ -494,6 +496,7 @@ class RawEditorState extends EditorState maxHeight: widget.maxHeight ?? double.infinity); return TextFieldTapRegion( + enabled: widget.enableUnfocusOnTapOutside, onTapOutside: _defaultOnTapOutside, child: QuillStyles( data: _styles!, From c3e410c2a2ff5b6b26fbbd82b604ec86c3068a7e Mon Sep 17 00:00:00 2001 From: Cheryl Date: Sat, 15 Apr 2023 10:16:15 -0700 Subject: [PATCH 22/27] Upgrade to 7.1.6 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eb12046..bf092c36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.6] +* Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets. + # [7.1.5] * Add tooltips for toolbar buttons. diff --git a/pubspec.yaml b/pubspec.yaml index c6d50420..208761ac 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.1.5 +version: 7.1.6 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 431efc1eda5f1a3e72fb7cd26282b7b37e0a4a44 Mon Sep 17 00:00:00 2001 From: BambinoUA <45417992+bambinoua@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:13:15 +0300 Subject: [PATCH 23/27] Toolbar tweaks (#1179) --- .../lib/embeds/toolbar/camera_button.dart | 1 + .../lib/embeds/toolbar/formula_button.dart | 1 + .../lib/embeds/toolbar/image_button.dart | 1 + .../lib/embeds/toolbar/video_button.dart | 1 + .../lib/flutter_quill_extensions.dart | 8 ++ flutter_quill_extensions/pubspec.yaml | 2 +- lib/flutter_quill.dart | 1 + lib/src/utils/font.dart | 3 +- lib/src/widgets/toolbar.dart | 135 ++++++++++-------- .../toolbar/quill_font_family_button.dart | 57 ++++++-- .../toolbar/quill_font_size_button.dart | 60 ++++++-- .../toolbar/select_alignment_button.dart | 6 +- 12 files changed, 186 insertions(+), 90 deletions(-) diff --git a/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart index 202d97b1..5ec8e28e 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/camera_button.dart @@ -55,6 +55,7 @@ class CameraButton extends StatelessWidget { return QuillIconButton( icon: Icon(icon, size: iconSize, color: iconColor), + tooltip: tooltip, highlightElevation: 0, hoverElevation: 0, size: iconSize * 1.77, diff --git a/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart index 70c4987f..5c7c5684 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/formula_button.dart @@ -36,6 +36,7 @@ class FormulaButton extends StatelessWidget { return QuillIconButton( icon: Icon(icon, size: iconSize, color: iconColor), + tooltip: tooltip, highlightElevation: 0, hoverElevation: 0, size: iconSize * 1.77, diff --git a/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart index 8cad519c..d05d851d 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart @@ -51,6 +51,7 @@ class ImageButton extends StatelessWidget { return QuillIconButton( icon: Icon(icon, size: iconSize, color: iconColor), + tooltip: tooltip, highlightElevation: 0, hoverElevation: 0, size: iconSize * 1.77, diff --git a/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart index 30e0dbf7..e5c1ab73 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart @@ -51,6 +51,7 @@ class VideoButton extends StatelessWidget { return QuillIconButton( icon: Icon(icon, size: iconSize, color: iconColor), + tooltip: tooltip, highlightElevation: 0, hoverElevation: 0, size: iconSize * 1.77, diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart index 7ef38328..12b3bd82 100644 --- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart +++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart @@ -32,6 +32,10 @@ class FlutterQuillEmbeds { bool showVideoButton = true, bool showCameraButton = true, bool showFormulaButton = false, + String? imageButtonTooltip, + String? videoButtonTooltip, + String? cameraButtonTooltip, + String? formulaButtonTooltip, OnImagePickCallback? onImagePickCallback, OnVideoPickCallback? onVideoPickCallback, MediaPickSettingSelector? mediaPickSettingSelector, @@ -45,6 +49,7 @@ class FlutterQuillEmbeds { (controller, toolbarIconSize, iconTheme, dialogTheme) => ImageButton( icon: Icons.image, iconSize: toolbarIconSize, + tooltip: imageButtonTooltip, controller: controller, onImagePickCallback: onImagePickCallback, filePickImpl: filePickImpl, @@ -57,6 +62,7 @@ class FlutterQuillEmbeds { (controller, toolbarIconSize, iconTheme, dialogTheme) => VideoButton( icon: Icons.movie_creation, iconSize: toolbarIconSize, + tooltip: videoButtonTooltip, controller: controller, onVideoPickCallback: onVideoPickCallback, filePickImpl: filePickImpl, @@ -70,6 +76,7 @@ class FlutterQuillEmbeds { (controller, toolbarIconSize, iconTheme, dialogTheme) => CameraButton( icon: Icons.photo_camera, iconSize: toolbarIconSize, + tooltip: cameraButtonTooltip, controller: controller, onImagePickCallback: onImagePickCallback, onVideoPickCallback: onVideoPickCallback, @@ -83,6 +90,7 @@ class FlutterQuillEmbeds { (controller, toolbarIconSize, iconTheme, dialogTheme) => FormulaButton( icon: Icons.functions, iconSize: toolbarIconSize, + tooltip: formulaButtonTooltip, controller: controller, iconTheme: iconTheme, dialogTheme: dialogTheme, diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index e0e1269d..ba650ad9 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - flutter_quill: ^7.1.4 + flutter_quill: ^7.1.6 image_picker: ^0.8.5+3 photo_view: ^0.14.0 diff --git a/lib/flutter_quill.dart b/lib/flutter_quill.dart index aefc566c..bfc666f1 100644 --- a/lib/flutter_quill.dart +++ b/lib/flutter_quill.dart @@ -25,3 +25,4 @@ export 'src/widgets/embeds.dart'; export 'src/widgets/link.dart' show LinkActionPickerDelegate, LinkMenuAction; export 'src/widgets/style_widgets/style_widgets.dart'; export 'src/widgets/toolbar.dart'; +export 'src/widgets/toolbar/enum.dart'; diff --git a/lib/src/utils/font.dart b/lib/src/utils/font.dart index 4962a9a2..1e996e10 100644 --- a/lib/src/utils/font.dart +++ b/lib/src/utils/font.dart @@ -1,5 +1,6 @@ dynamic getFontSize(dynamic sizeValue) { - if (sizeValue is String && ['small', 'large', 'huge'].contains(sizeValue)) { + if (sizeValue is String && + ['small', 'normal', 'large', 'huge'].contains(sizeValue)) { return sizeValue; } diff --git a/lib/src/widgets/toolbar.dart b/lib/src/widgets/toolbar.dart index 08c7c6fe..d5a5c93a 100644 --- a/lib/src/widgets/toolbar.dart +++ b/lib/src/widgets/toolbar.dart @@ -6,7 +6,6 @@ import '../models/themes/quill_custom_button.dart'; import '../models/themes/quill_dialog_theme.dart'; import '../models/themes/quill_icon_theme.dart'; import '../translations/toolbar.i18n.dart'; -import '../utils/font.dart'; import 'controller.dart'; import 'embeds.dart'; import 'toolbar/arrow_indicated_button_list.dart'; @@ -30,32 +29,39 @@ export 'toolbar/color_button.dart'; export 'toolbar/history_button.dart'; export 'toolbar/indent_button.dart'; export 'toolbar/link_style_button.dart'; +export 'toolbar/quill_font_family_button.dart'; export 'toolbar/quill_font_size_button.dart'; export 'toolbar/quill_icon_button.dart'; +export 'toolbar/search_button.dart'; export 'toolbar/select_alignment_button.dart'; export 'toolbar/select_header_style_button.dart'; export 'toolbar/toggle_check_list_button.dart'; export 'toolbar/toggle_style_button.dart'; -// The default size of the icon of a button. +/// The default size of the icon of a button. const double kDefaultIconSize = 18; -// The factor of how much larger the button is in relation to the icon. +/// The factor of how much larger the button is in relation to the icon. const double kIconButtonFactor = 1.77; +/// The horizontal margin between the contents of each toolbar section. +const double kToolbarSectionSpacing = 4; + class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { const QuillToolbar({ required this.children, this.axis = Axis.horizontal, - this.toolbarSize = 36, + this.toolbarSize = kDefaultIconSize * 2, + this.toolbarSectionSpacing = kToolbarSectionSpacing, this.toolbarIconAlignment = WrapAlignment.center, this.toolbarIconCrossAlignment = WrapCrossAlignment.center, - this.toolbarSectionSpacing = 4, this.multiRowsDisplay = true, this.color, this.customButtons = const [], this.locale, VoidCallback? afterButtonPressed, + this.sectionDividerColor, + this.sectionDividerSpace, Key? key, }) : super(key: key); @@ -63,9 +69,10 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { required QuillController controller, Axis axis = Axis.horizontal, double toolbarIconSize = kDefaultIconSize, - double toolbarSectionSpacing = 4, + double toolbarSectionSpacing = kToolbarSectionSpacing, WrapAlignment toolbarIconAlignment = WrapAlignment.center, WrapCrossAlignment toolbarIconCrossAlignment = WrapCrossAlignment.center, + bool multiRowsDisplay = true, bool showDividers = true, bool showFontFamily = true, bool showFontSize = true, @@ -93,7 +100,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { bool showLink = true, bool showUndo = true, bool showRedo = true, - bool multiRowsDisplay = true, bool showDirection = false, bool showSearchButton = true, List customButtons = const [], @@ -138,6 +144,12 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { /// The color of the toolbar Color? color, + + /// The color of the toolbar section divider + Color? sectionDividerColor, + + /// The space occupied by toolbar divider + double? sectionDividerSpace, Key? key, }) { final isButtonGroupShown = [ @@ -260,21 +272,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { tooltip: buttonTooltips[ToolbarButtons.fontFamily], attribute: Attribute.font, controller: controller, - items: [ - for (MapEntry fontFamily in fontFamilies.entries) - PopupMenuItem( - key: ValueKey(fontFamily.key), - value: fontFamily.value, - child: Text(fontFamily.key.toString(), - style: TextStyle( - color: - fontFamily.value == 'Clear' ? Colors.red : null)), - ), - ], - onSelected: (newFont) { - controller.formatSelection(Attribute.fromKeyValue( - 'font', newFont == 'Clear' ? null : newFont)); - }, rawItemsMap: fontFamilies, afterButtonPressed: afterButtonPressed, ), @@ -285,20 +282,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { tooltip: buttonTooltips[ToolbarButtons.fontSize], attribute: Attribute.size, controller: controller, - items: [ - for (MapEntry fontSize in fontSizes.entries) - PopupMenuItem( - key: ValueKey(fontSize.key), - value: fontSize.value, - child: Text(fontSize.key.toString(), - style: TextStyle( - color: fontSize.value == '0' ? Colors.red : null)), - ), - ], - onSelected: (newSize) { - controller.formatSelection(Attribute.fromKeyValue( - 'size', newSize == '0' ? null : getFontSize(newSize))); - }, rawItemsMap: fontSizes, afterButtonPressed: afterButtonPressed, ), @@ -401,7 +384,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) - _dividerOnAxis(axis), + AxisDivider(axis, + color: sectionDividerColor, space: sectionDividerSpace), if (showAlignmentButtons) SelectAlignmentButton( controller: controller, @@ -436,7 +420,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) - _dividerOnAxis(axis), + AxisDivider(axis, + color: sectionDividerColor, space: sectionDividerSpace), if (showHeaderStyle) SelectHeaderStyleButton( tooltip: buttonTooltips[ToolbarButtons.headerStyle], @@ -452,7 +437,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { (isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) - _dividerOnAxis(axis), + AxisDivider(axis, + color: sectionDividerColor, space: sectionDividerSpace), if (showListNumbers) ToggleStyleButton( attribute: Attribute.ol, @@ -496,7 +482,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { if (showDividers && isButtonGroupShown[3] && (isButtonGroupShown[4] || isButtonGroupShown[5])) - _dividerOnAxis(axis), + AxisDivider(axis, + color: sectionDividerColor, space: sectionDividerSpace), if (showQuote) ToggleStyleButton( attribute: Attribute.blockQuote, @@ -528,7 +515,8 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { afterButtonPressed: afterButtonPressed, ), if (showDividers && isButtonGroupShown[4] && isButtonGroupShown[5]) - _dividerOnAxis(axis), + AxisDivider(axis, + color: sectionDividerColor, space: sectionDividerSpace), if (showLink) LinkStyleButton( tooltip: buttonTooltips[ToolbarButtons.link], @@ -549,7 +537,9 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { afterButtonPressed: afterButtonPressed, ), if (customButtons.isNotEmpty) - if (showDividers) _dividerOnAxis(axis), + if (showDividers) + AxisDivider(axis, + color: sectionDividerColor, space: sectionDividerSpace), for (var customButton in customButtons) QuillIconButton( highlightElevation: 0, @@ -565,22 +555,6 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { ); } - static Widget _dividerOnAxis(Axis axis) { - if (axis == Axis.horizontal) { - return const VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey, - ); - } else { - return const Divider( - indent: 12, - endIndent: 12, - color: Colors.grey, - ); - } - } - final List children; final Axis axis; final double toolbarSize; @@ -602,6 +576,15 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { /// List of custom buttons final List customButtons; + /// The color to use when painting the toolbar section divider. + /// + /// If this is null, then the [DividerThemeData.color] is used. If that is + /// also null, then [ThemeData.dividerColor] is used. + final Color? sectionDividerColor; + + /// The space occupied by toolbar section divider. + final double? sectionDividerSpace; + @override Size get preferredSize => axis == Axis.horizontal ? Size.fromHeight(toolbarSize) @@ -634,3 +617,39 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { ); } } + +class AxisDivider extends StatelessWidget { + const AxisDivider( + this.axis, { + Key? key, + this.color, + this.space, + }) : super(key: key); + + const AxisDivider.horizontal({Color? color, double? space}) + : this(Axis.horizontal, color: color, space: space); + + const AxisDivider.vertical({Color? color, double? space}) + : this(Axis.vertical, color: color, space: space); + + final Axis axis; + final Color? color; + final double? space; + + @override + Widget build(BuildContext context) { + return axis == Axis.horizontal + ? Divider( + height: space, + color: color, + indent: 12, + endIndent: 12, + ) + : VerticalDivider( + width: space, + color: color, + indent: 12, + endIndent: 12, + ); + } +} diff --git a/lib/src/widgets/toolbar/quill_font_family_button.dart b/lib/src/widgets/toolbar/quill_font_family_button.dart index bdddc832..d214bf64 100644 --- a/lib/src/widgets/toolbar/quill_font_family_button.dart +++ b/lib/src/widgets/toolbar/quill_font_family_button.dart @@ -9,11 +9,11 @@ import '../controller.dart'; class QuillFontFamilyButton extends StatefulWidget { const QuillFontFamilyButton({ - required this.items, required this.rawItemsMap, required this.attribute, required this.controller, - required this.onSelected, + @Deprecated('It is not required because of `rawItemsMap`') this.items, + this.onSelected, this.iconSize = 40, this.fillColor, this.hoverElevation = 1, @@ -21,6 +21,11 @@ class QuillFontFamilyButton extends StatefulWidget { this.iconTheme, this.afterButtonPressed, this.tooltip, + this.padding, + this.style, + this.width, + this.renderFontFamilies = true, + this.alignment, Key? key, }) : super(key: key); @@ -28,14 +33,20 @@ class QuillFontFamilyButton extends StatefulWidget { final Color? fillColor; final double hoverElevation; final double highlightElevation; - final List> items; + @Deprecated('It is not required because of `rawItemsMap`') + final List>? items; final Map rawItemsMap; - final ValueChanged onSelected; + final ValueChanged? onSelected; final QuillIconTheme? iconTheme; final Attribute attribute; final QuillController controller; final VoidCallback? afterButtonPressed; final String? tooltip; + final EdgeInsetsGeometry? padding; + final TextStyle? style; + final double? width; + final bool renderFontFamilies; + final AlignmentGeometry? alignment; @override _QuillFontFamilyButtonState createState() => _QuillFontFamilyButtonState(); @@ -90,7 +101,10 @@ class _QuillFontFamilyButtonState extends State { @override Widget build(BuildContext context) { return ConstrainedBox( - constraints: BoxConstraints.tightFor(height: widget.iconSize * 1.81), + constraints: BoxConstraints.tightFor( + height: widget.iconSize * 1.81, + width: widget.width, + ), child: UtilityWidgets.maybeTooltip( message: widget.tooltip, child: RawMaterialButton( @@ -127,7 +141,20 @@ class _QuillFontFamilyButtonState extends State { showMenu( context: context, elevation: 4, - items: widget.items, + items: [ + for (MapEntry fontFamily in widget.rawItemsMap.entries) + PopupMenuItem( + key: ValueKey(fontFamily.key), + value: fontFamily.value, + child: Text( + fontFamily.key.toString(), + style: TextStyle( + fontFamily: + widget.renderFontFamilies ? fontFamily.value : null, + color: fontFamily.value == 'Clear' ? Colors.red : null), + ), + ), + ], position: position, shape: popupMenuTheme.shape, color: popupMenuTheme.color, @@ -140,7 +167,9 @@ class _QuillFontFamilyButtonState extends State { setState(() { _currentValue = keyName ?? _defaultDisplayText; if (keyName != null) { - widget.onSelected(newValue); + widget.controller.formatSelection(Attribute.fromKeyValue( + 'font', newValue == 'Clear' ? null : newValue)); + widget.onSelected?.call(newValue); } }); }); @@ -148,16 +177,18 @@ class _QuillFontFamilyButtonState extends State { Widget _buildContent(BuildContext context) { final theme = Theme.of(context); - return Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), + return Container( + alignment: widget.alignment ?? Alignment.center, + padding: widget.padding ?? const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(_currentValue, - style: TextStyle( - fontSize: widget.iconSize / 1.15, - color: widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color)), + style: widget.style ?? + TextStyle( + fontSize: widget.iconSize / 1.15, + color: widget.iconTheme?.iconUnselectedColor ?? + theme.iconTheme.color)), const SizedBox(width: 3), Icon(Icons.arrow_drop_down, size: widget.iconSize / 1.15, diff --git a/lib/src/widgets/toolbar/quill_font_size_button.dart b/lib/src/widgets/toolbar/quill_font_size_button.dart index f82175c2..83d39747 100644 --- a/lib/src/widgets/toolbar/quill_font_size_button.dart +++ b/lib/src/widgets/toolbar/quill_font_size_button.dart @@ -10,11 +10,11 @@ import '../controller.dart'; class QuillFontSizeButton extends StatefulWidget { const QuillFontSizeButton({ - required this.items, required this.rawItemsMap, required this.attribute, required this.controller, - required this.onSelected, + this.onSelected, + @Deprecated('It is not required because of `rawItemsMap`') this.items, this.iconSize = 40, this.fillColor, this.hoverElevation = 1, @@ -22,21 +22,33 @@ class QuillFontSizeButton extends StatefulWidget { this.iconTheme, this.afterButtonPressed, this.tooltip, + this.padding, + this.style, + this.width, + this.initialValue, + this.alignment, Key? key, - }) : super(key: key); + }) : assert(rawItemsMap.length > 0), + super(key: key); final double iconSize; final Color? fillColor; final double hoverElevation; final double highlightElevation; - final List> items; + @Deprecated('It is not required because of `rawItemsMap`') + final List>? items; final Map rawItemsMap; - final ValueChanged onSelected; + final ValueChanged? onSelected; final QuillIconTheme? iconTheme; final Attribute attribute; final QuillController controller; final VoidCallback? afterButtonPressed; final String? tooltip; + final EdgeInsetsGeometry? padding; + final TextStyle? style; + final double? width; + final String? initialValue; + final AlignmentGeometry? alignment; @override _QuillFontSizeButtonState createState() => _QuillFontSizeButtonState(); @@ -50,7 +62,7 @@ class _QuillFontSizeButtonState extends State { @override void initState() { super.initState(); - _currentValue = _defaultDisplayText = 'Size'.i18n; + _currentValue = _defaultDisplayText = widget.initialValue ?? 'Size'.i18n; widget.controller.addListener(_didChangeEditingValue); } @@ -91,7 +103,10 @@ class _QuillFontSizeButtonState extends State { @override Widget build(BuildContext context) { return ConstrainedBox( - constraints: BoxConstraints.tightFor(height: widget.iconSize * 1.81), + constraints: BoxConstraints.tightFor( + height: widget.iconSize * 1.81, + width: widget.width, + ), child: UtilityWidgets.maybeTooltip( message: widget.tooltip, child: RawMaterialButton( @@ -128,7 +143,18 @@ class _QuillFontSizeButtonState extends State { showMenu( context: context, elevation: 4, - items: widget.items, + items: [ + for (MapEntry fontSize in widget.rawItemsMap.entries) + PopupMenuItem( + key: ValueKey(fontSize.key), + value: fontSize.value, + child: Text( + fontSize.key.toString(), + style: + TextStyle(color: fontSize.value == '0' ? Colors.red : null), + ), + ), + ], position: position, shape: popupMenuTheme.shape, color: popupMenuTheme.color, @@ -141,7 +167,9 @@ class _QuillFontSizeButtonState extends State { setState(() { _currentValue = keyName ?? _defaultDisplayText; if (keyName != null) { - widget.onSelected(newValue); + widget.controller.formatSelection(Attribute.fromKeyValue( + 'size', newValue == '0' ? null : getFontSize(newValue))); + widget.onSelected?.call(newValue); } }); }); @@ -149,16 +177,18 @@ class _QuillFontSizeButtonState extends State { Widget _buildContent(BuildContext context) { final theme = Theme.of(context); - return Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), + return Container( + alignment: widget.alignment ?? Alignment.center, + padding: widget.padding ?? const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(_currentValue, - style: TextStyle( - fontSize: widget.iconSize / 1.15, - color: widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color)), + style: widget.style ?? + TextStyle( + fontSize: widget.iconSize / 1.15, + color: widget.iconTheme?.iconUnselectedColor ?? + theme.iconTheme.color)), const SizedBox(width: 3), Icon(Icons.arrow_drop_down, size: widget.iconSize / 1.15, diff --git a/lib/src/widgets/toolbar/select_alignment_button.dart b/lib/src/widgets/toolbar/select_alignment_button.dart index 4d22b2cf..922b30fc 100644 --- a/lib/src/widgets/toolbar/select_alignment_button.dart +++ b/lib/src/widgets/toolbar/select_alignment_button.dart @@ -20,6 +20,7 @@ class SelectAlignmentButton extends StatefulWidget { this.showJustifyAlignment, this.afterButtonPressed, this.tooltips = const {}, + this.padding, Key? key, }) : super(key: key); @@ -33,6 +34,7 @@ class SelectAlignmentButton extends StatefulWidget { final bool? showJustifyAlignment; final VoidCallback? afterButtonPressed; final Map tooltips; + final EdgeInsetsGeometry? padding; @override _SelectAlignmentButtonState createState() => _SelectAlignmentButtonState(); @@ -100,8 +102,8 @@ class _SelectAlignmentButtonState extends State { mainAxisSize: MainAxisSize.min, children: List.generate(buttonCount, (index) { return Padding( - // ignore: prefer_const_constructors - padding: EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), + padding: widget.padding ?? + const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), child: ConstrainedBox( constraints: BoxConstraints.tightFor( width: widget.iconSize * kIconButtonFactor, From 4617d79b64059002b0a2d130d1a0fc3e5e275002 Mon Sep 17 00:00:00 2001 From: Cheryl Date: Mon, 17 Apr 2023 09:23:24 -0700 Subject: [PATCH 24/27] Upgrade to 7.1.7 --- CHANGELOG.md | 3 +++ flutter_quill_extensions/pubspec.yaml | 2 +- pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf092c36..4a4dc3a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.7] +* Toolbar tweaks. + # [7.1.6] * Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets. diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index ba650ad9..cd2b2ee8 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter: sdk: flutter - flutter_quill: ^7.1.6 + flutter_quill: ^7.1.7 image_picker: ^0.8.5+3 photo_view: ^0.14.0 diff --git a/pubspec.yaml b/pubspec.yaml index 208761ac..94ff3915 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.1.6 +version: 7.1.7 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill From 752e1a746bccadbb9f16b09f54e145612791e588 Mon Sep 17 00:00:00 2001 From: BambinoUA <45417992+bambinoua@users.noreply.github.com> Date: Tue, 18 Apr 2023 11:46:15 +0300 Subject: [PATCH 25/27] Update pubspec.yaml (#1181) --- flutter_quill_extensions/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index cd2b2ee8..2bb092ce 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.2.0 +version: 0.2.1 homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions From 6d9b43f909cc609cd98fe03db412e2c41c384d3e Mon Sep 17 00:00:00 2001 From: BambinoUA <45417992+bambinoua@users.noreply.github.com> Date: Tue, 18 Apr 2023 19:04:19 +0300 Subject: [PATCH 26/27] Dropdown tweaks (#1182) --- lib/src/utils/widgets.dart | 10 +++ .../toolbar/quill_font_family_button.dart | 63 ++++++++++++++----- .../toolbar/quill_font_size_button.dart | 42 +++++++++---- 3 files changed, 88 insertions(+), 27 deletions(-) diff --git a/lib/src/utils/widgets.dart b/lib/src/utils/widgets.dart index 1b560e84..c27777f0 100644 --- a/lib/src/utils/widgets.dart +++ b/lib/src/utils/widgets.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +typedef WidgetWrapper = Widget Function(Widget child); + /// Provides utiulity widgets. abstract class UtilityWidgets { /// Conditionally wraps the [child] with [Tooltip] widget if [message] @@ -8,4 +10,12 @@ abstract class UtilityWidgets { (message ?? '').isNotEmpty ? Tooltip(message: message!, child: child) : child; + + /// Conditionally wraps the [child] with [wrapper] widget if [enabled] + /// is true. + static Widget maybeWidget( + {required WidgetWrapper wrapper, + required Widget child, + bool enabled = false}) => + enabled ? wrapper(child) : child; } diff --git a/lib/src/widgets/toolbar/quill_font_family_button.dart b/lib/src/widgets/toolbar/quill_font_family_button.dart index d214bf64..8108c200 100644 --- a/lib/src/widgets/toolbar/quill_font_family_button.dart +++ b/lib/src/widgets/toolbar/quill_font_family_button.dart @@ -25,9 +25,16 @@ class QuillFontFamilyButton extends StatefulWidget { this.style, this.width, this.renderFontFamilies = true, - this.alignment, + this.initialValue, + this.labelOverflow = TextOverflow.visible, + this.overrideTooltipByFontFamily = false, + this.itemHeight, + this.itemPadding, + this.defaultItemColor = Colors.red, Key? key, - }) : super(key: key); + }) : assert(rawItemsMap.length > 0), + assert(initialValue == null || initialValue.length > 0), + super(key: key); final double iconSize; final Color? fillColor; @@ -46,7 +53,12 @@ class QuillFontFamilyButton extends StatefulWidget { final TextStyle? style; final double? width; final bool renderFontFamilies; - final AlignmentGeometry? alignment; + final String? initialValue; + final TextOverflow labelOverflow; + final bool overrideTooltipByFontFamily; + final double? itemHeight; + final EdgeInsets? itemPadding; + final Color? defaultItemColor; @override _QuillFontFamilyButtonState createState() => _QuillFontFamilyButtonState(); @@ -60,7 +72,7 @@ class _QuillFontFamilyButtonState extends State { @override void initState() { super.initState(); - _currentValue = _defaultDisplayText = 'Font'.i18n; + _currentValue = _defaultDisplayText = widget.initialValue ?? 'Font'.i18n; widget.controller.addListener(_didChangeEditingValue); } @@ -105,8 +117,18 @@ class _QuillFontFamilyButtonState extends State { height: widget.iconSize * 1.81, width: widget.width, ), - child: UtilityWidgets.maybeTooltip( - message: widget.tooltip, + child: UtilityWidgets.maybeWidget( + enabled: (widget.tooltip ?? '').isNotEmpty || + widget.overrideTooltipByFontFamily, + wrapper: (child) { + var effectiveTooltip = widget.tooltip ?? ''; + if (widget.overrideTooltipByFontFamily) { + effectiveTooltip = effectiveTooltip.isNotEmpty + ? '$effectiveTooltip: $_currentValue' + : '${'Font'.i18n}: $_currentValue'; + } + return Tooltip(message: effectiveTooltip, child: child); + }, child: RawMaterialButton( visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( @@ -146,12 +168,16 @@ class _QuillFontFamilyButtonState extends State { PopupMenuItem( key: ValueKey(fontFamily.key), value: fontFamily.value, + height: widget.itemHeight ?? kMinInteractiveDimension, + padding: widget.itemPadding, child: Text( fontFamily.key.toString(), style: TextStyle( - fontFamily: - widget.renderFontFamilies ? fontFamily.value : null, - color: fontFamily.value == 'Clear' ? Colors.red : null), + fontFamily: widget.renderFontFamilies ? fontFamily.value : null, + color: fontFamily.value == 'Clear' + ? widget.defaultItemColor + : null, + ), ), ), ], @@ -177,18 +203,27 @@ class _QuillFontFamilyButtonState extends State { Widget _buildContent(BuildContext context) { final theme = Theme.of(context); - return Container( - alignment: widget.alignment ?? Alignment.center, + final hasFinalWidth = widget.width != null; + return Padding( padding: widget.padding ?? const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: !hasFinalWidth ? MainAxisSize.min : MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(_currentValue, + UtilityWidgets.maybeWidget( + enabled: hasFinalWidth, + wrapper: (child) => Expanded(child: child), + child: Text( + _currentValue, + maxLines: 1, + overflow: widget.labelOverflow, style: widget.style ?? TextStyle( fontSize: widget.iconSize / 1.15, color: widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color)), + theme.iconTheme.color), + ), + ), const SizedBox(width: 3), Icon(Icons.arrow_drop_down, size: widget.iconSize / 1.15, diff --git a/lib/src/widgets/toolbar/quill_font_size_button.dart b/lib/src/widgets/toolbar/quill_font_size_button.dart index 83d39747..e445c0ae 100644 --- a/lib/src/widgets/toolbar/quill_font_size_button.dart +++ b/lib/src/widgets/toolbar/quill_font_size_button.dart @@ -26,9 +26,13 @@ class QuillFontSizeButton extends StatefulWidget { this.style, this.width, this.initialValue, - this.alignment, + this.labelOverflow = TextOverflow.visible, + this.itemHeight, + this.itemPadding, + this.defaultItemColor = Colors.red, Key? key, }) : assert(rawItemsMap.length > 0), + assert(initialValue == null || initialValue.length > 0), super(key: key); final double iconSize; @@ -48,7 +52,10 @@ class QuillFontSizeButton extends StatefulWidget { final TextStyle? style; final double? width; final String? initialValue; - final AlignmentGeometry? alignment; + final TextOverflow labelOverflow; + final double? itemHeight; + final EdgeInsets? itemPadding; + final Color? defaultItemColor; @override _QuillFontSizeButtonState createState() => _QuillFontSizeButtonState(); @@ -148,10 +155,13 @@ class _QuillFontSizeButtonState extends State { PopupMenuItem( key: ValueKey(fontSize.key), value: fontSize.value, + height: widget.itemHeight ?? kMinInteractiveDimension, + padding: widget.itemPadding, child: Text( fontSize.key.toString(), - style: - TextStyle(color: fontSize.value == '0' ? Colors.red : null), + style: TextStyle( + color: fontSize.value == '0' ? widget.defaultItemColor : null, + ), ), ), ], @@ -177,18 +187,24 @@ class _QuillFontSizeButtonState extends State { Widget _buildContent(BuildContext context) { final theme = Theme.of(context); - return Container( - alignment: widget.alignment ?? Alignment.center, + final hasFinalWidth = widget.width != null; + return Padding( padding: widget.padding ?? const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: !hasFinalWidth ? MainAxisSize.min : MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(_currentValue, - style: widget.style ?? - TextStyle( - fontSize: widget.iconSize / 1.15, - color: widget.iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color)), + UtilityWidgets.maybeWidget( + enabled: hasFinalWidth, + wrapper: (child) => Expanded(child: child), + child: Text(_currentValue, + overflow: widget.labelOverflow, + style: widget.style ?? + TextStyle( + fontSize: widget.iconSize / 1.15, + color: widget.iconTheme?.iconUnselectedColor ?? + theme.iconTheme.color)), + ), const SizedBox(width: 3), Icon(Icons.arrow_drop_down, size: widget.iconSize / 1.15, From f2a1a1f45ac90d539cbf84f87a33a13d5085f155 Mon Sep 17 00:00:00 2001 From: Cheryl Date: Tue, 18 Apr 2023 09:10:18 -0700 Subject: [PATCH 27/27] Upgrade to 7.1.8 --- CHANGELOG.md | 3 +++ pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4dc3a8..a745d7b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# [7.1.8] +* Dropdown tweaks. + # [7.1.7] * Toolbar tweaks. diff --git a/pubspec.yaml b/pubspec.yaml index 94ff3915..38a3d504 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.1.7 +version: 7.1.8 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill