Browse Source

Merge pull request #780 from LucasXu0/feat/fix_focus_error

customize flowy_rich_text style
Lucas.Xu 2 years ago
parent
commit
4a00f3c2ca

+ 8 - 6
frontend/app_flowy/packages/flowy_editor/lib/infra/flowy_svg.dart

@@ -18,12 +18,14 @@ class FlowySvg extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     if (name != null) {
-      return SvgPicture.asset(
-        'assets/images/$name.svg',
-        color: color,
-        package: 'flowy_editor',
-        width: size.width,
-        height: size.width,
+      return SizedBox.fromSize(
+        size: size,
+        child: SvgPicture.asset(
+          'assets/images/$name.svg',
+          color: color,
+          package: 'flowy_editor',
+          fit: BoxFit.fill,
+        ),
       );
     } else if (number != null) {
       final numberText =

+ 7 - 4
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/bulleted_list_text.dart

@@ -62,10 +62,13 @@ class _BulletedListTextNodeWidgetState extends State<BulletedListTextNodeWidget>
           size: Size.square(leftPadding),
           name: 'point',
         ),
-        FlowyRichText(
-          key: _richTextKey,
-          textNode: widget.textNode,
-          editorState: widget.editorState,
+        Expanded(
+          child: FlowyRichText(
+            key: _richTextKey,
+            placeholderText: 'List',
+            textNode: widget.textNode,
+            editorState: widget.editorState,
+          ),
         ),
       ],
     );

+ 29 - 5
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/checkbox_text.dart

@@ -82,11 +82,15 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
               ..commit();
           },
         ),
-        FlowyRichText(
-          key: _richTextKey,
-          textNode: widget.textNode,
-          editorState: widget.editorState,
-        )
+        Expanded(
+          child: FlowyRichText(
+            key: _richTextKey,
+            placeholderText: 'To-do',
+            textNode: widget.textNode,
+            textSpanDecorator: _textSpanDecorator,
+            editorState: widget.editorState,
+          ),
+        ),
       ],
     );
   }
@@ -120,4 +124,24 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
       ],
     );
   }
+
+  TextSpan _textSpanDecorator(TextSpan textSpan) {
+    return TextSpan(
+      children: textSpan.children
+          ?.whereType<TextSpan>()
+          .map(
+            (span) => TextSpan(
+              text: span.text,
+              style: widget.textNode.attributes.check
+                  ? span.style?.copyWith(
+                      color: Colors.grey,
+                      decoration: TextDecoration.lineThrough,
+                    )
+                  : span.style,
+              recognizer: span.recognizer,
+            ),
+          )
+          .toList(),
+    );
+  }
 }

+ 38 - 5
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart

@@ -35,15 +35,19 @@ class FlowyRichText extends StatefulWidget {
     this.cursorHeight,
     this.cursorWidth = 2.0,
     this.textSpanDecorator,
+    this.placeholderText = ' ',
+    this.placeholderTextSpanDecorator,
     required this.textNode,
     required this.editorState,
   }) : super(key: key);
 
-  final double? cursorHeight;
-  final double cursorWidth;
   final TextNode textNode;
   final EditorState editorState;
+  final double? cursorHeight;
+  final double cursorWidth;
   final FlowyTextSpanDecorator? textSpanDecorator;
+  final String placeholderText;
+  final FlowyTextSpanDecorator? placeholderTextSpanDecorator;
 
   @override
   State<FlowyRichText> createState() => _FlowyRichTextState();
@@ -51,10 +55,14 @@ class FlowyRichText extends StatefulWidget {
 
 class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
   final _textKey = GlobalKey();
+  final _placeholderTextKey = GlobalKey();
 
   RenderParagraph get _renderParagraph =>
       _textKey.currentContext?.findRenderObject() as RenderParagraph;
 
+  RenderParagraph get _placeholderRenderParagraph =>
+      _placeholderTextKey.currentContext?.findRenderObject() as RenderParagraph;
+
   @override
   Widget build(BuildContext context) {
     return _buildRichText(context);
@@ -74,6 +82,7 @@ class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
         _renderParagraph.getOffsetForCaret(textPosition, Rect.zero);
     final cursorHeight = widget.cursorHeight ??
         _renderParagraph.getFullHeightForCaret(textPosition) ??
+        _placeholderRenderParagraph.getFullHeightForCaret(textPosition) ??
         18.0; // default height
     return Rect.fromLTWH(
       cursorOffset.dx - (widget.cursorWidth / 2),
@@ -129,9 +138,33 @@ class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
   }
 
   Widget _buildRichText(BuildContext context) {
-    return Align(
-      alignment: Alignment.centerLeft,
-      child: _buildSingleRichText(context),
+    return Stack(
+      children: [
+        _buildPlaceholderText(context),
+        _buildSingleRichText(context),
+      ],
+    );
+  }
+
+  Widget _buildPlaceholderText(BuildContext context) {
+    final textSpan = TextSpan(
+      children: [
+        TextSpan(
+          text: widget.placeholderText,
+          style: TextStyle(
+            color: widget.textNode.toRawString().isNotEmpty
+                ? Colors.transparent
+                : Colors.grey,
+            fontSize: baseFontSize,
+          ),
+        ),
+      ],
+    );
+    return RichText(
+      key: _placeholderTextKey,
+      text: widget.placeholderTextSpanDecorator != null
+          ? widget.placeholderTextSpanDecorator!(textSpan)
+          : textSpan,
     );
   }
 

+ 26 - 5
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/heading_text.dart

@@ -63,11 +63,15 @@ class _HeadingTextNodeWidgetState extends State<HeadingTextNodeWidget>
             top: topPadding,
             bottom: bottomPadding,
           ),
-          child: FlowyRichText(
-            key: _richTextKey,
-            textSpanDecorator: _textSpanDecorator,
-            textNode: widget.textNode,
-            editorState: widget.editorState,
+          child: Expanded(
+            child: FlowyRichText(
+              key: _richTextKey,
+              placeholderText: 'Heading',
+              placeholderTextSpanDecorator: _placeholderTextSpanDecorator,
+              textSpanDecorator: _textSpanDecorator,
+              textNode: widget.textNode,
+              editorState: widget.editorState,
+            ),
           ),
         )
       ],
@@ -90,4 +94,21 @@ class _HeadingTextNodeWidgetState extends State<HeadingTextNodeWidget>
           .toList(),
     );
   }
+
+  TextSpan _placeholderTextSpanDecorator(TextSpan textSpan) {
+    return TextSpan(
+      children: textSpan.children
+          ?.whereType<TextSpan>()
+          .map(
+            (span) => TextSpan(
+              text: span.text,
+              style: span.style?.copyWith(
+                fontSize: widget.textNode.attributes.fontSize,
+              ),
+              recognizer: span.recognizer,
+            ),
+          )
+          .toList(),
+    );
+  }
 }

+ 7 - 4
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/number_list_text.dart

@@ -63,10 +63,13 @@ class _NumberListTextNodeWidgetState extends State<NumberListTextNodeWidget>
           size: Size.square(leftPadding),
           number: widget.textNode.attributes.number,
         ),
-        FlowyRichText(
-          key: _richTextKey,
-          textNode: widget.textNode,
-          editorState: widget.editorState,
+        Expanded(
+          child: FlowyRichText(
+            key: _richTextKey,
+            placeholderText: 'List',
+            textNode: widget.textNode,
+            editorState: widget.editorState,
+          ),
         ),
       ],
     );

+ 17 - 5
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/quoted_text.dart

@@ -59,15 +59,27 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
     return Row(
       children: [
         FlowySvg(
-          size: Size.square(leftPadding),
+          size: Size(
+            leftPadding,
+            _quoteHeight,
+          ),
           name: 'quote',
         ),
-        FlowyRichText(
-          key: _richTextKey,
-          textNode: widget.textNode,
-          editorState: widget.editorState,
+        Expanded(
+          child: FlowyRichText(
+            key: _richTextKey,
+            placeholderText: 'Quote',
+            textNode: widget.textNode,
+            editorState: widget.editorState,
+          ),
         ),
       ],
     );
   }
+
+  double get _quoteHeight {
+    final lines =
+        widget.textNode.toRawString().characters.where((c) => c == '\n').length;
+    return (lines + 1) * leftPadding;
+  }
 }

+ 8 - 1
frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/slash_handler.dart

@@ -208,7 +208,14 @@ class _PopupListWidgetState extends State<PopupListWidget> {
     }
 
     if (event.logicalKey == LogicalKeyboardKey.enter) {
-      widget.items[selectedIndex].handler(widget.editorState);
+      if (0 <= selectedIndex && selectedIndex < widget.items.length) {
+        widget.items[selectedIndex].handler(widget.editorState);
+        return KeyEventResult.handled;
+      }
+    }
+
+    if (event.logicalKey == LogicalKeyboardKey.escape) {
+      clearPopupListOverlay();
       return KeyEventResult.handled;
     }
 

+ 10 - 6
frontend/app_flowy/packages/flowy_editor/lib/service/keyboard_service.dart

@@ -32,23 +32,23 @@ class FlowyKeyboard extends StatefulWidget {
 
 class _FlowyKeyboardState extends State<FlowyKeyboard>
     with FlowyKeyboardService {
-  final FocusNode focusNode = FocusNode(debugLabel: 'flowy_keyboard_service');
+  final FocusNode _focusNode = FocusNode(debugLabel: 'flowy_keyboard_service');
 
   bool isFocus = true;
 
   @override
   Widget build(BuildContext context) {
     return Focus(
-      focusNode: focusNode,
-      autofocus: true,
+      focusNode: _focusNode,
       onKey: _onKey,
+      onFocusChange: _onFocusChange,
       child: widget.child,
     );
   }
 
   @override
   void dispose() {
-    focusNode.dispose();
+    _focusNode.dispose();
 
     super.dispose();
   }
@@ -56,13 +56,17 @@ class _FlowyKeyboardState extends State<FlowyKeyboard>
   @override
   void enable() {
     isFocus = true;
-    focusNode.requestFocus();
+    _focusNode.requestFocus();
   }
 
   @override
   void disable() {
     isFocus = false;
-    focusNode.unfocus();
+    _focusNode.unfocus();
+  }
+
+  void _onFocusChange(bool value) {
+    debugPrint('[KeyBoard Service] focus change $value');
   }
 
   KeyEventResult _onKey(FocusNode node, RawKeyEvent event) {

+ 2 - 0
frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart

@@ -265,6 +265,8 @@ class _FlowySelectionState extends State<FlowySelection>
     }
     final selection = Selection.collapsed(position);
     editorState.updateCursorSelection(selection);
+
+    editorState.service.keyboardService?.enable();
   }
 
   @override