소스 검색

chore: (draft) support text node widget editing

Lucas.Xu 2 년 전
부모
커밋
00c628437d

+ 141 - 35
frontend/app_flowy/packages/flowy_editor/example/lib/plugin/text_node_widget.dart

@@ -1,5 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flowy_editor/flowy_editor.dart';
 import 'package:flowy_editor/flowy_editor.dart';
+import 'package:flutter/services.dart';
+import 'package:provider/provider.dart';
 
 
 class TextNodeBuilder extends NodeWidgetBuilder {
 class TextNodeBuilder extends NodeWidgetBuilder {
   TextNodeBuilder.create({
   TextNodeBuilder.create({
@@ -11,41 +13,7 @@ class TextNodeBuilder extends NodeWidgetBuilder {
 
 
   @override
   @override
   Widget build(BuildContext buildContext) {
   Widget build(BuildContext buildContext) {
-    final richText = SelectableText.rich(
-      TextSpan(
-        text: node.attributes['content'] as String,
-        style: node.attributes.toTextStyle(),
-      ),
-    );
-
-    Widget? children;
-    if (node.children.isNotEmpty) {
-      children = Column(
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: node.children
-            .map(
-              (e) => renderPlugins.buildWidget(
-                context: NodeWidgetContext(
-                  buildContext: buildContext,
-                  node: e,
-                  editorState: editorState,
-                ),
-              ),
-            )
-            .toList(),
-      );
-    }
-
-    if (children != null) {
-      return Column(
-        children: [
-          richText,
-          children,
-        ],
-      );
-    } else {
-      return richText;
-    }
+    return _TextNodeWidget(node: node, editorState: editorState);
   }
   }
 }
 }
 
 
@@ -57,3 +25,141 @@ extension on Attributes {
     );
     );
   }
   }
 }
 }
+
+class _TextNodeWidget extends StatefulWidget {
+  final Node node;
+  final EditorState editorState;
+
+  const _TextNodeWidget({
+    Key? key,
+    required this.node,
+    required this.editorState,
+  }) : super(key: key);
+
+  @override
+  State<_TextNodeWidget> createState() => __TextNodeWidgetState();
+}
+
+class __TextNodeWidgetState extends State<_TextNodeWidget>
+    implements TextInputClient {
+  Node get node => widget.node;
+  EditorState get editorState => widget.editorState;
+  String get content => node.attributes['content'] as String;
+  TextEditingValue get textEditingValue => TextEditingValue(text: content);
+
+  TextInputConnection? _textInputConnection;
+
+  @override
+  Widget build(BuildContext context) {
+    final editableRichText = ChangeNotifierProvider.value(
+      value: node,
+      builder: (_, __) => Consumer<Node>(
+        builder: ((context, value, child) => SelectableText.rich(
+              TextSpan(
+                text: content,
+                style: node.attributes.toTextStyle(),
+              ),
+              onTap: () {
+                _textInputConnection?.close();
+                _textInputConnection = TextInput.attach(
+                  this,
+                  const TextInputConfiguration(
+                    enableDeltaModel: false,
+                    inputType: TextInputType.multiline,
+                    textCapitalization: TextCapitalization.sentences,
+                  ),
+                );
+                _textInputConnection
+                  ?..show()
+                  ..setEditingState(textEditingValue);
+              },
+            )),
+      ),
+    );
+
+    final child = Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        editableRichText,
+        if (node.children.isNotEmpty)
+          Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: node.children
+                .map(
+                  (e) => editorState.renderPlugins.buildWidget(
+                    context: NodeWidgetContext(
+                      buildContext: context,
+                      node: e,
+                      editorState: editorState,
+                    ),
+                  ),
+                )
+                .toList(),
+          ),
+      ],
+    );
+    return child;
+  }
+
+  @override
+  void connectionClosed() {
+    // TODO: implement connectionClosed
+  }
+
+  @override
+  // TODO: implement currentAutofillScope
+  AutofillScope? get currentAutofillScope => throw UnimplementedError();
+
+  @override
+  // TODO: implement currentTextEditingValue
+  TextEditingValue? get currentTextEditingValue => textEditingValue;
+
+  @override
+  void insertTextPlaceholder(Size size) {
+    // TODO: implement insertTextPlaceholder
+  }
+
+  @override
+  void performAction(TextInputAction action) {
+    // TODO: implement performAction
+  }
+
+  @override
+  void performPrivateCommand(String action, Map<String, dynamic> data) {
+    // TODO: implement performPrivateCommand
+  }
+
+  @override
+  void removeTextPlaceholder() {
+    // TODO: implement removeTextPlaceholder
+  }
+
+  @override
+  void showAutocorrectionPromptRect(int start, int end) {
+    // TODO: implement showAutocorrectionPromptRect
+  }
+
+  @override
+  void showToolbar() {
+    // TODO: implement showToolbar
+  }
+
+  @override
+  void updateEditingValue(TextEditingValue value) {
+    debugPrint(value.text);
+    editorState.update(
+      node,
+      Attributes.from(node.attributes)
+        ..addAll(
+          {
+            'content': value.text,
+          },
+        ),
+    );
+  }
+
+  @override
+  void updateFloatingCursor(RawFloatingCursorPoint point) {
+    // TODO: implement updateFloatingCursor
+  }
+}

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/lib/document/node.dart

@@ -29,6 +29,7 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
   factory Node.fromJson(Map<String, Object> json) {
   factory Node.fromJson(Map<String, Object> json) {
     assert(json['type'] is String);
     assert(json['type'] is String);
 
 
+    // TODO: check the type that not exist on plugins.
     final jType = json['type'] as String;
     final jType = json['type'] as String;
     final jChildren = json['children'] as List?;
     final jChildren = json['children'] as List?;
     final jAttributes = json['attributes'] != null
     final jAttributes = json['attributes'] != null