From b11a127432cb67a3708c2d194557efd4d557d8e5 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 2 Aug 2022 11:01:58 +0800 Subject: [PATCH] feat: implement italic, strikethrough and underline in toolbar service --- .../flowy_editor/lib/document/attributes.dart | 2 +- .../flowy_editor/lib/document/node.dart | 6 ++- .../lib/render/selection/toolbar_widget.dart | 10 ++--- .../format_rich_text_style.dart | 44 ++++++++++++++++++- ...pdate_text_style_by_command_x_handler.dart | 4 +- 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart index 6e845420ef..4e1f39775f 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart @@ -26,7 +26,7 @@ Attributes? composeAttributes(Attributes? a, Attributes? b) { a ??= {}; b ??= {}; final Attributes attributes = {}; - attributes.addAll(b); + attributes.addAll(Map.from(b)..removeWhere((_, value) => value == null)); for (final entry in a.entries) { if (!b.containsKey(entry.key)) { diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/node.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/node.dart index 69852856b0..0404be2a26 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/document/node.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/document/node.dart @@ -89,7 +89,11 @@ class Node extends ChangeNotifier with LinkedListEntry { this.attributes['subtype'] != attributes['subtype']; for (final attribute in attributes.entries) { - this.attributes[attribute.key] = attribute.value; + if (attribute.value == null) { + this.attributes.remove(attribute.key); + } else { + this.attributes[attribute.key] = attribute.value; + } } // Notify the new attributes // if attributes contains 'subtype', should notify parent to rebuild node diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/selection/toolbar_widget.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/selection/toolbar_widget.dart index 486bd97671..1a05b6ef10 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/render/selection/toolbar_widget.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/selection/toolbar_widget.dart @@ -9,12 +9,10 @@ typedef ToolbarEventHandler = void Function(EditorState editorState); typedef ToolbarEventHandlers = Map; ToolbarEventHandlers defaultToolbarEventHandlers = { - 'bold': ((editorState) { - formatRichTextStyle(editorState, {StyleKey.bold: true}); - }), - 'italic': ((editorState) {}), - 'strikethrough': ((editorState) {}), - 'underline': ((editorState) {}), + 'bold': (editorState) => formatBold(editorState), + 'italic': (editorState) => formatItalic(editorState), + 'strikethrough': (editorState) => formatStrikethrough(editorState), + 'underline': (editorState) => formatUnderline(editorState), 'quote': ((editorState) {}), 'number_list': ((editorState) {}), 'bulleted_list': ((editorState) {}), diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/default_text_operations/format_rich_text_style.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/default_text_operations/format_rich_text_style.dart index 162818ae1d..514a9d706b 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/default_text_operations/format_rich_text_style.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/default_text_operations/format_rich_text_style.dart @@ -1,9 +1,49 @@ +import 'package:flowy_editor/document/attributes.dart'; import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/editor_state.dart'; import 'package:flowy_editor/operation/transaction_builder.dart'; +import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; +import 'package:flowy_editor/extensions/text_node_extensions.dart'; -bool formatRichTextStyle( - EditorState editorState, Map attributes) { +bool formatBold(EditorState editorState) { + return formatRichText(editorState, StyleKey.bold); +} + +bool formatItalic(EditorState editorState) { + return formatRichText(editorState, StyleKey.italic); +} + +bool formatUnderline(EditorState editorState) { + return formatRichText(editorState, StyleKey.underline); +} + +bool formatStrikethrough(EditorState editorState) { + return formatRichText(editorState, StyleKey.strikethrough); +} + +bool formatRichText(EditorState editorState, String styleKey) { + final selection = editorState.service.selectionService.currentSelection; + final nodes = editorState.service.selectionService.currentSelectedNodes.value; + final textNodes = nodes.whereType().toList(growable: false); + + if (selection == null || textNodes.isEmpty) { + return false; + } + + bool value = !textNodes.allSatisfyInSelection(styleKey, selection); + Attributes attributes = { + styleKey: value, + }; + if (styleKey == StyleKey.underline && value) { + attributes[StyleKey.strikethrough] = null; + } else if (styleKey == StyleKey.strikethrough && value) { + attributes[StyleKey.underline] = null; + } + + return formatRichTextStyle(editorState, attributes); +} + +bool formatRichTextStyle(EditorState editorState, Attributes attributes) { final selection = editorState.service.selectionService.currentSelection; final nodes = editorState.service.selectionService.currentSelectedNodes.value; final textNodes = nodes.whereType().toList(); diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart index 6e4b742785..6b1fbcd9ca 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart @@ -23,9 +23,7 @@ FlowyKeyEventHandler updateTextStyleByCommandXHandler = (editorState, event) { // bold case 'B': case 'b': - formatRichTextStyle(editorState, { - StyleKey.bold: !textNodes.allSatisfyBoldInSelection(selection), - }); + formatBold(editorState); return KeyEventResult.handled; default: break;