diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart new file mode 100644 index 0000000000..30e5fcf17e --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart @@ -0,0 +1,62 @@ +import 'package:appflowy_editor/src/commands/format_text.dart'; +import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; +import 'package:appflowy_editor/src/document/node.dart'; +import 'package:appflowy_editor/src/document/path.dart'; +import 'package:appflowy_editor/src/editor_state.dart'; + +Future formatBuiltInTextAttributes( + EditorState editorState, + String key, + Attributes attributes, { + Path? path, + TextNode? textNode, +}) async { + if (BuiltInAttributeKey.globalStyleKeys.contains(key)) { + assert(!(path != null && textNode != null)); + assert(!(path == null && textNode == null)); + + TextNode formattedTextNode; + if (textNode != null) { + formattedTextNode = textNode; + } else if (path != null) { + formattedTextNode = editorState.document.nodeAtPath(path) as TextNode; + } else { + throw Exception('path and textNode cannot be null at the same time'); + } + // remove all the existing style + final newAttributes = formattedTextNode.attributes + ..removeWhere((key, value) { + if (BuiltInAttributeKey.globalStyleKeys.contains(key)) { + return true; + } + return false; + }) + ..addAll(attributes) + ..addAll({ + BuiltInAttributeKey.subtype: key, + }); + return updateTextNodeAttributes( + editorState, + newAttributes, + textNode: textNode, + ); + } +} + +Future formatTextToCheckbox( + EditorState editorState, + bool check, { + Path? path, + TextNode? textNode, +}) async { + return formatBuiltInTextAttributes( + editorState, + BuiltInAttributeKey.checkbox, + { + BuiltInAttributeKey.checkbox: check, + }, + path: path, + textNode: textNode, + ); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart new file mode 100644 index 0000000000..41eb8c16e6 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart @@ -0,0 +1,34 @@ +import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/document/node.dart'; +import 'package:appflowy_editor/src/document/path.dart'; +import 'package:appflowy_editor/src/editor_state.dart'; +import 'package:appflowy_editor/src/operation/transaction_builder.dart'; +import 'package:flutter/widgets.dart'; + +Future updateTextNodeAttributes( + EditorState editorState, + Attributes attributes, { + Path? path, + TextNode? textNode, +}) async { + assert(!(path != null && textNode != null)); + assert(!(path == null && textNode == null)); + + TextNode formattedTextNode; + if (textNode != null) { + formattedTextNode = textNode; + } else if (path != null) { + formattedTextNode = editorState.document.nodeAtPath(path) as TextNode; + } else { + throw Exception('path and textNode cannot be null at the same time'); + } + + TransactionBuilder(editorState) + ..updateNode(formattedTextNode, attributes) + ..commit(); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + print('AAAAAAAAAAAAAA'); + return; + }); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart index 2ca7531d2b..e41f3b4891 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart @@ -1,15 +1,8 @@ -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; -import 'package:appflowy_editor/src/document/node.dart'; -import 'package:appflowy_editor/src/editor_state.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/commands/format_built_in_text.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart'; import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/selection/selectable.dart'; -import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; -import 'package:appflowy_editor/src/service/render_plugin_service.dart'; -import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; import 'package:flutter/material.dart'; @@ -82,7 +75,11 @@ class _CheckboxNodeWidgetState extends State name: check ? 'check' : 'uncheck', ), onTap: () { - formatCheckbox(widget.editorState, !check); + formatTextToCheckbox( + widget.editorState, + !check, + textNode: widget.textNode, + ); }, ), Flexible( diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart index 7dba4852ed..99a6d08918 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart @@ -18,6 +18,8 @@ import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; +const _kRichTextDebugMode = false; + typedef FlowyTextSpanDecorator = TextSpan Function(TextSpan textSpan); class FlowyRichText extends StatefulWidget { @@ -261,6 +263,17 @@ class _FlowyRichTextState extends State with SelectableMixin { ), ); } + if (_kRichTextDebugMode) { + textSpans.add( + TextSpan( + text: '${widget.textNode.path}', + style: const TextStyle( + backgroundColor: Colors.red, + fontSize: 16.0, + ), + ), + ); + } return TextSpan( children: textSpans, ); diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart index 23ddc75a69..053d9e542a 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart @@ -103,13 +103,17 @@ bool formatTextNodes(EditorState editorState, Attributes attributes) { final builder = TransactionBuilder(editorState); for (final textNode in textNodes) { + var newAttributes = {...textNode.attributes}; + for (final globalStyleKey in BuiltInAttributeKey.globalStyleKeys) { + if (newAttributes.keys.contains(globalStyleKey)) { + newAttributes[globalStyleKey] = null; + } + } + newAttributes.addAll(attributes); builder ..updateNode( textNode, - Attributes.fromIterable( - BuiltInAttributeKey.globalStyleKeys, - value: (_) => null, - )..addAll(attributes), + newAttributes, ) ..afterSelection = Selection.collapsed( Position(