From 23f6cfcb2709df594b4f0a18356569978436570a Mon Sep 17 00:00:00 2001 From: Angelo Silvestre Date: Sat, 18 Jan 2025 12:46:16 -0300 Subject: [PATCH 1/3] [SuperEditor][SuperTextField] Improve RTL support (Resolves #2472) --- .../lib/src/default_editor/list_items.dart | 138 +++++++++++------- .../lib/src/default_editor/paragraph.dart | 1 + .../lib/src/default_editor/tasks.dart | 71 +++++---- .../android/android_textfield.dart | 34 ++++- .../desktop/desktop_textfield.dart | 39 ++++- .../super_textfield/ios/ios_textfield.dart | 35 ++++- .../src/super_textfield/super_textfield.dart | 7 +- .../super_editor/supereditor_test_tools.dart | 1 + ...or-rtl-caret-ordered-list-item-android.png | Bin 0 -> 3637 bytes ...editor-rtl-caret-ordered-list-item-iOS.png | Bin 0 -> 3211 bytes ...itor-rtl-caret-ordered-list-item-linux.png | Bin 0 -> 3598 bytes ...itor-rtl-caret-ordered-list-item-macOS.png | Bin 0 -> 3156 bytes ...or-rtl-caret-ordered-list-item-windows.png | Bin 0 -> 3156 bytes ...per-editor-rtl-caret-paragraph-android.png | Bin 0 -> 3137 bytes .../super-editor-rtl-caret-paragraph-iOS.png | Bin 0 -> 3137 bytes ...super-editor-rtl-caret-paragraph-linux.png | Bin 0 -> 3130 bytes ...super-editor-rtl-caret-paragraph-macOS.png | Bin 0 -> 3130 bytes ...per-editor-rtl-caret-paragraph-windows.png | Bin 0 -> 3130 bytes .../super-editor-rtl-caret-task-android.png | Bin 0 -> 3614 bytes .../super-editor-rtl-caret-task-iOS.png | Bin 0 -> 3614 bytes .../super-editor-rtl-caret-task-linux.png | Bin 0 -> 3550 bytes .../super-editor-rtl-caret-task-macOS.png | Bin 0 -> 3550 bytes .../super-editor-rtl-caret-task-windows.png | Bin 0 -> 3550 bytes ...-rtl-caret-unordered-list-item-android.png | Bin 0 -> 3550 bytes ...itor-rtl-caret-unordered-list-item-iOS.png | Bin 0 -> 3550 bytes ...or-rtl-caret-unordered-list-item-linux.png | Bin 0 -> 3508 bytes ...or-rtl-caret-unordered-list-item-macOS.png | Bin 0 -> 3508 bytes ...-rtl-caret-unordered-list-item-windows.png | Bin 0 -> 3508 bytes .../editor/supereditor_rtl_test.dart | 126 ++++++++++++++++ .../super-text-field_rtl-caret-android.png | Bin 0 -> 3096 bytes .../super-text-field_rtl-caret-iOS.png | Bin 0 -> 3102 bytes .../super-text-field_rtl-caret-linux.png | Bin 0 -> 3109 bytes .../super-text-field_rtl-caret-macOS.png | Bin 0 -> 3109 bytes .../super-text-field_rtl-caret-windows.png | Bin 0 -> 3109 bytes .../super_textfield_rtl_test.dart | 61 ++++++++ super_text_layout/lib/src/super_text.dart | 3 + 36 files changed, 418 insertions(+), 98 deletions(-) create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-windows.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-windows.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-windows.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-windows.png create mode 100644 super_editor/test_goldens/editor/supereditor_rtl_test.dart create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-android.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-iOS.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-linux.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-macOS.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-windows.png create mode 100644 super_editor/test_goldens/super_textfield/super_textfield_rtl_test.dart diff --git a/super_editor/lib/src/default_editor/list_items.dart b/super_editor/lib/src/default_editor/list_items.dart index f663143c21..f701d6b55f 100644 --- a/super_editor/lib/src/default_editor/list_items.dart +++ b/super_editor/lib/src/default_editor/list_items.dart @@ -9,6 +9,7 @@ import 'package:super_editor/src/core/editor.dart'; import 'package:super_editor/src/core/styles.dart'; import 'package:super_editor/src/default_editor/attributions.dart'; import 'package:super_editor/src/default_editor/blocks/indentation.dart'; +import 'package:super_editor/src/default_editor/text_tools.dart'; import 'package:super_editor/src/infrastructure/_logging.dart'; import 'package:super_editor/src/infrastructure/attributed_text_styles.dart'; import 'package:super_editor/src/infrastructure/keyboard.dart'; @@ -161,11 +162,16 @@ class ListItemComponentBuilder implements ComponentBuilder { ordinalValue = computeListItemOrdinalValue(node, document); } + final textDirection = getParagraphDirection(node.text.toPlainText()); + final textAlignment = textDirection == TextDirection.ltr ? TextAlign.left : TextAlign.right; + return switch (node.type) { ListItemType.unordered => UnorderedListItemComponentViewModel( nodeId: node.id, indent: node.indent, text: node.text, + textDirection: textDirection, + textAlignment: textAlignment, textStyleBuilder: noStyleBuilder, selectionColor: const Color(0x00000000), ), @@ -174,6 +180,8 @@ class ListItemComponentBuilder implements ComponentBuilder { indent: node.indent, ordinalValue: ordinalValue, text: node.text, + textDirection: textDirection, + textAlignment: textAlignment, textStyleBuilder: noStyleBuilder, selectionColor: const Color(0x00000000), ), @@ -196,6 +204,8 @@ class ListItemComponentBuilder implements ComponentBuilder { indent: componentViewModel.indent, dotStyle: componentViewModel.dotStyle, textSelection: componentViewModel.selection, + textDirection: componentViewModel.textDirection, + textAlignment: componentViewModel.textAlignment, selectionColor: componentViewModel.selectionColor, highlightWhenEmpty: componentViewModel.highlightWhenEmpty, underlines: componentViewModel.createUnderlines(), @@ -206,6 +216,8 @@ class ListItemComponentBuilder implements ComponentBuilder { indent: componentViewModel.indent, listIndex: componentViewModel.ordinalValue!, text: componentViewModel.text, + textDirection: componentViewModel.textDirection, + textAlignment: componentViewModel.textAlignment, styleBuilder: componentViewModel.textStyleBuilder, numeralStyle: componentViewModel.numeralStyle, textSelection: componentViewModel.selection, @@ -281,6 +293,7 @@ abstract class ListItemComponentViewModel extends SingleColumnLayoutComponentVie indent == other.indent && text == other.text && textDirection == other.textDirection && + textAlignment == other.textAlignment && selection == other.selection && selectionColor == other.selectionColor && highlightWhenEmpty == other.highlightWhenEmpty && @@ -298,6 +311,7 @@ abstract class ListItemComponentViewModel extends SingleColumnLayoutComponentVie indent.hashCode ^ text.hashCode ^ textDirection.hashCode ^ + textAlignment.hashCode ^ selection.hashCode ^ selectionColor.hashCode ^ highlightWhenEmpty.hashCode ^ @@ -355,6 +369,7 @@ class UnorderedListItemComponentViewModel extends ListItemComponentViewModel { textStyleBuilder: textStyleBuilder, dotStyle: dotStyle, textDirection: textDirection, + textAlignment: textAlignment, selection: selection, selectionColor: selectionColor, composingRegion: composingRegion, @@ -423,6 +438,7 @@ class OrderedListItemComponentViewModel extends ListItemComponentViewModel { text: text, textStyleBuilder: textStyleBuilder, textDirection: textDirection, + textAlignment: textAlignment, selection: selection, selectionColor: selectionColor, composingRegion: composingRegion, @@ -493,6 +509,8 @@ class UnorderedListItemComponent extends StatefulWidget { Key? key, required this.componentKey, required this.text, + this.textDirection = TextDirection.ltr, + this.textAlignment = TextAlign.left, required this.styleBuilder, this.inlineWidgetBuilders = const [], this.dotBuilder = _defaultUnorderedListItemDotBuilder, @@ -510,6 +528,8 @@ class UnorderedListItemComponent extends StatefulWidget { final GlobalKey componentKey; final AttributedText text; + final TextDirection textDirection; + final TextAlign textAlignment; final AttributionStyleBuilder styleBuilder; final InlineWidgetBuilderChain inlineWidgetBuilders; final UnorderedListItemDotBuilder dotBuilder; @@ -558,34 +578,39 @@ class _UnorderedListItemComponentState extends State return ProxyTextDocumentComponent( key: widget.componentKey, textComponentKey: _innerTextComponentKey, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: indentSpace, - decoration: BoxDecoration( - border: widget.showDebugPaint ? Border.all(width: 1, color: Colors.grey) : null, - ), - child: SizedBox( - height: lineHeight, - child: widget.dotBuilder(context, widget), + child: Directionality( + textDirection: widget.textDirection, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: indentSpace, + decoration: BoxDecoration( + border: widget.showDebugPaint ? Border.all(width: 1, color: Colors.grey) : null, + ), + child: SizedBox( + height: lineHeight, + child: widget.dotBuilder(context, widget), + ), ), - ), - Expanded( - child: TextComponent( - key: _innerTextComponentKey, - text: widget.text, - textStyleBuilder: widget.styleBuilder, - inlineWidgetBuilders: widget.inlineWidgetBuilders, - textSelection: widget.textSelection, - textScaler: textScaler, - selectionColor: widget.selectionColor, - highlightWhenEmpty: widget.highlightWhenEmpty, - underlines: widget.underlines, - showDebugPaint: widget.showDebugPaint, + Expanded( + child: TextComponent( + key: _innerTextComponentKey, + text: widget.text, + textDirection: widget.textDirection, + textAlign: widget.textAlignment, + textStyleBuilder: widget.styleBuilder, + inlineWidgetBuilders: widget.inlineWidgetBuilders, + textSelection: widget.textSelection, + textScaler: textScaler, + selectionColor: widget.selectionColor, + highlightWhenEmpty: widget.highlightWhenEmpty, + underlines: widget.underlines, + showDebugPaint: widget.showDebugPaint, + ), ), - ), - ], + ], + ), ), ); } @@ -659,6 +684,8 @@ class OrderedListItemComponent extends StatefulWidget { required this.componentKey, required this.listIndex, required this.text, + this.textDirection = TextDirection.ltr, + this.textAlignment = TextAlign.left, required this.styleBuilder, this.inlineWidgetBuilders = const [], this.numeralBuilder = _defaultOrderedListItemNumeralBuilder, @@ -677,6 +704,8 @@ class OrderedListItemComponent extends StatefulWidget { final GlobalKey componentKey; final int listIndex; final AttributedText text; + final TextDirection textDirection; + final TextAlign textAlignment; final AttributionStyleBuilder styleBuilder; final InlineWidgetBuilderChain inlineWidgetBuilders; final OrderedListItemNumeralBuilder numeralBuilder; @@ -725,35 +754,40 @@ class _OrderedListItemComponentState extends State { return ProxyTextDocumentComponent( key: widget.componentKey, textComponentKey: _innerTextComponentKey, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: indentSpace, - height: lineHeight, - decoration: BoxDecoration( - border: widget.showDebugPaint ? Border.all(width: 1, color: Colors.grey) : null, - ), - child: SizedBox( + child: Directionality( + textDirection: widget.textDirection, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: indentSpace, height: lineHeight, - child: widget.numeralBuilder(context, widget), + decoration: BoxDecoration( + border: widget.showDebugPaint ? Border.all(width: 1, color: Colors.grey) : null, + ), + child: SizedBox( + height: lineHeight, + child: widget.numeralBuilder(context, widget), + ), ), - ), - Expanded( - child: TextComponent( - key: _innerTextComponentKey, - text: widget.text, - textStyleBuilder: widget.styleBuilder, - inlineWidgetBuilders: widget.inlineWidgetBuilders, - textSelection: widget.textSelection, - textScaler: textScaler, - selectionColor: widget.selectionColor, - highlightWhenEmpty: widget.highlightWhenEmpty, - underlines: widget.underlines, - showDebugPaint: widget.showDebugPaint, + Expanded( + child: TextComponent( + key: _innerTextComponentKey, + text: widget.text, + textDirection: widget.textDirection, + textAlign: widget.textAlignment, + textStyleBuilder: widget.styleBuilder, + inlineWidgetBuilders: widget.inlineWidgetBuilders, + textSelection: widget.textSelection, + textScaler: textScaler, + selectionColor: widget.selectionColor, + highlightWhenEmpty: widget.highlightWhenEmpty, + underlines: widget.underlines, + showDebugPaint: widget.showDebugPaint, + ), ), - ), - ], + ], + ), ), ); } diff --git a/super_editor/lib/src/default_editor/paragraph.dart b/super_editor/lib/src/default_editor/paragraph.dart index dfadf61136..b953121059 100644 --- a/super_editor/lib/src/default_editor/paragraph.dart +++ b/super_editor/lib/src/default_editor/paragraph.dart @@ -346,6 +346,7 @@ class _ParagraphComponentState extends State child: TextComponent( key: _textKey, text: widget.viewModel.text, + textDirection: widget.viewModel.textDirection, textAlign: widget.viewModel.textAlignment, textScaler: widget.viewModel.textScaler, textStyleBuilder: widget.viewModel.textStyleBuilder, diff --git a/super_editor/lib/src/default_editor/tasks.dart b/super_editor/lib/src/default_editor/tasks.dart index daf3116b1b..180bd84f37 100644 --- a/super_editor/lib/src/default_editor/tasks.dart +++ b/super_editor/lib/src/default_editor/tasks.dart @@ -13,6 +13,7 @@ import 'package:super_editor/src/default_editor/blocks/indentation.dart'; import 'package:super_editor/src/default_editor/multi_node_editing.dart'; import 'package:super_editor/src/default_editor/paragraph.dart'; import 'package:super_editor/src/default_editor/text.dart'; +import 'package:super_editor/src/default_editor/text_tools.dart'; import 'package:super_editor/src/infrastructure/_logging.dart'; import 'package:super_editor/src/infrastructure/attributed_text_styles.dart'; import 'package:super_editor/src/infrastructure/composable_text.dart'; @@ -163,6 +164,8 @@ class TaskComponentBuilder implements ComponentBuilder { return null; } + final textDirection = getParagraphDirection(node.text.toPlainText()); + return TaskComponentViewModel( nodeId: node.id, padding: EdgeInsets.zero, @@ -177,6 +180,8 @@ class TaskComponentBuilder implements ComponentBuilder { ]); }, text: node.text, + textDirection: textDirection, + textAlignment: textDirection == TextDirection.ltr ? TextAlign.left : TextAlign.right, textStyleBuilder: noStyleBuilder, selectionColor: const Color(0x00000000), ); @@ -273,6 +278,7 @@ class TaskComponentViewModel extends SingleColumnLayoutComponentViewModel with T textStyleBuilder: textStyleBuilder, inlineWidgetBuilders: inlineWidgetBuilders, textDirection: textDirection, + textAlignment: textAlignment, selection: selection, selectionColor: selectionColor, highlightWhenEmpty: highlightWhenEmpty, @@ -376,39 +382,44 @@ class _TaskComponentState extends State with ProxyDocumentCompone @override Widget build(BuildContext context) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: widget.viewModel.indentCalculator( - widget.viewModel.textStyleBuilder({}), - widget.viewModel.indent, + return Directionality( + textDirection: widget.viewModel.textDirection, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: widget.viewModel.indentCalculator( + widget.viewModel.textStyleBuilder({}), + widget.viewModel.indent, + ), ), - ), - Padding( - padding: const EdgeInsets.only(left: 16, right: 4), - child: Checkbox( - visualDensity: Theme.of(context).visualDensity, - value: widget.viewModel.isComplete, - onChanged: (newValue) { - widget.viewModel.setComplete(newValue!); - }, + Padding( + padding: const EdgeInsets.only(left: 16, right: 4), + child: Checkbox( + visualDensity: Theme.of(context).visualDensity, + value: widget.viewModel.isComplete, + onChanged: (newValue) { + widget.viewModel.setComplete(newValue!); + }, + ), ), - ), - Expanded( - child: TextComponent( - key: _textKey, - text: widget.viewModel.text, - textStyleBuilder: _computeStyles, - inlineWidgetBuilders: widget.viewModel.inlineWidgetBuilders, - textSelection: widget.viewModel.selection, - selectionColor: widget.viewModel.selectionColor, - highlightWhenEmpty: widget.viewModel.highlightWhenEmpty, - underlines: widget.viewModel.createUnderlines(), - showDebugPaint: widget.showDebugPaint, + Expanded( + child: TextComponent( + key: _textKey, + text: widget.viewModel.text, + textDirection: widget.viewModel.textDirection, + textAlign: widget.viewModel.textAlignment, + textStyleBuilder: _computeStyles, + inlineWidgetBuilders: widget.viewModel.inlineWidgetBuilders, + textSelection: widget.viewModel.selection, + selectionColor: widget.viewModel.selectionColor, + highlightWhenEmpty: widget.viewModel.highlightWhenEmpty, + underlines: widget.viewModel.createUnderlines(), + showDebugPaint: widget.showDebugPaint, + ), ), - ), - ], + ], + ), ); } } diff --git a/super_editor/lib/src/super_textfield/android/android_textfield.dart b/super_editor/lib/src/super_textfield/android/android_textfield.dart index e4aefd1128..8b254d2e64 100644 --- a/super_editor/lib/src/super_textfield/android/android_textfield.dart +++ b/super_editor/lib/src/super_textfield/android/android_textfield.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:follow_the_leader/follow_the_leader.dart'; +import 'package:super_editor/src/default_editor/text_tools.dart'; import 'package:super_editor/src/infrastructure/attributed_text_styles.dart'; import 'package:super_editor/src/infrastructure/flutter/build_context.dart'; import 'package:super_editor/src/infrastructure/flutter/flutter_scheduler.dart'; @@ -32,7 +33,7 @@ class SuperAndroidTextField extends StatefulWidget { this.focusNode, this.tapRegionGroupId, this.textController, - this.textAlign = TextAlign.left, + this.textAlign, this.textStyleBuilder = defaultTextFieldStyleBuilder, this.hintBehavior = HintBehavior.displayHintUntilFocus, this.hintBuilder, @@ -63,7 +64,10 @@ class SuperAndroidTextField extends StatefulWidget { final ImeAttributedTextEditingController? textController; /// The alignment to use for text in this text field. - final TextAlign textAlign; + /// + /// If `null`, the text alignment is determined by the text direction + /// of the content. + final TextAlign? textAlign; /// Text style factory that creates styles for the content in /// [textController] based on the attributions in that content. @@ -175,6 +179,21 @@ class SuperAndroidTextFieldState extends State late ImeAttributedTextEditingController _textEditingController; + /// The text direction of the first character in the text. + /// + /// Used to align and position the caret depending on whether + /// the text is RTL or LTR. + TextDirection? _contentTextDirection; + + /// The text direction applied to the inner text. + TextDirection get _textDirection => _contentTextDirection ?? TextDirection.ltr; + + TextAlign get _textAlign => + widget.textAlign ?? + ((_textDirection == TextDirection.ltr) // + ? TextAlign.left + : TextAlign.right); + final _magnifierLayerLink = LeaderLink(); late AndroidEditingOverlayController _editingOverlayController; @@ -217,6 +236,8 @@ class SuperAndroidTextFieldState extends State magnifierFocalPoint: _magnifierLayerLink, ); + _contentTextDirection = getParagraphDirection(_textEditingController.text.toPlainText()); + WidgetsBinding.instance.addObserver(this); if (_focusNode.hasFocus) { @@ -432,6 +453,10 @@ class SuperAndroidTextFieldState extends State if (_textEditingController.selection.isCollapsed) { _editingOverlayController.hideToolbar(); } + + setState(() { + _contentTextDirection = getParagraphDirection(_textEditingController.text.toPlainText()); + }); } void _onTextScrollChange() { @@ -576,7 +601,7 @@ class SuperAndroidTextFieldState extends State textScrollController: _textScrollController, textKey: _textContentKey, textEditingController: _textEditingController, - textAlign: widget.textAlign, + textAlign: _textAlign, minLines: widget.minLines, maxLines: widget.maxLines, lineHeight: widget.lineHeight, @@ -612,7 +637,8 @@ class SuperAndroidTextFieldState extends State return SuperText( key: _textContentKey, richText: textSpan, - textAlign: widget.textAlign, + textAlign: _textAlign, + textDirection: _textDirection, textScaler: MediaQuery.textScalerOf(context), layerBeneathBuilder: (context, textLayout) { final isTextEmpty = _textEditingController.text.isEmpty; diff --git a/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart b/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart index 1dc301d3dc..0f124ecbf2 100644 --- a/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart +++ b/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart @@ -1,13 +1,13 @@ import 'dart:math'; import 'dart:ui' as ui; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' hide SelectableText; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:super_editor/src/core/document_layout.dart'; +import 'package:super_editor/src/default_editor/text_tools.dart'; import 'package:super_editor/src/infrastructure/_logging.dart'; import 'package:super_editor/src/infrastructure/actions.dart'; import 'package:super_editor/src/infrastructure/attributed_text_styles.dart'; @@ -23,7 +23,6 @@ import 'package:super_editor/src/infrastructure/multi_tap_gesture.dart'; import 'package:super_editor/src/infrastructure/platforms/mac/mac_ime.dart'; import 'package:super_editor/src/infrastructure/platforms/platform.dart'; import 'package:super_editor/src/infrastructure/text_input.dart'; -import 'package:super_editor/src/super_textfield/infrastructure/text_field_gestures_interaction_overrides.dart'; import 'package:super_editor/src/super_textfield/infrastructure/text_field_scroller.dart'; import 'package:super_editor/src/super_textfield/super_textfield.dart'; import 'package:super_text_layout/super_text_layout.dart'; @@ -52,7 +51,7 @@ class SuperDesktopTextField extends StatefulWidget { this.tapRegionGroupId, this.textController, this.textStyleBuilder = defaultTextFieldStyleBuilder, - this.textAlign = TextAlign.left, + this.textAlign, this.hintBehavior = HintBehavior.displayHintUntilFocus, this.hintBuilder, this.selectionHighlightStyle = const SelectionHighlightStyle( @@ -102,7 +101,10 @@ class SuperDesktopTextField extends StatefulWidget { final WidgetBuilder? hintBuilder; /// The alignment to use for text in this text field. - final TextAlign textAlign; + /// + /// If `null`, the text alignment is determined by the text direction + /// of the content. + final TextAlign? textAlign; /// The visual representation of the user's selection highlight. final SelectionHighlightStyle selectionHighlightStyle; @@ -170,6 +172,22 @@ class SuperDesktopTextFieldState extends State implements late SuperTextFieldContext _textFieldContext; late ImeAttributedTextEditingController _controller; + + /// The text direction of the first character in the text. + /// + /// Used to align and position the caret depending on whether + /// the text is RTL or LTR. + TextDirection? _contentTextDirection; + + /// The text direction applied to the inner text. + TextDirection get _textDirection => _contentTextDirection ?? TextDirection.ltr; + + TextAlign get _textAlign => + widget.textAlign ?? + ((_textDirection == TextDirection.ltr) // + ? TextAlign.left + : TextAlign.right); + late ScrollController _scrollController; late TextFieldScroller _textFieldScroller; @@ -198,6 +216,8 @@ class SuperDesktopTextFieldState extends State implements // Check if we need to update the selection. _updateSelectionAndComposingRegionOnFocusChange(); + + _contentTextDirection = getParagraphDirection(_controller.text.toPlainText()); } @override @@ -316,6 +336,12 @@ class SuperDesktopTextFieldState extends State implements // so that any pending visual content changes can happen before // attempting to calculate the visual position of the selection extent. onNextFrame((_) => _updateViewportHeight()); + + // Even though we calling `onNextFrame`, it doesn't necessarily mean + // a new frame will be scheduled. Call setState to ensure the text direction is updated. + setState(() { + _contentTextDirection = getParagraphDirection(_controller.text.toPlainText()); + }); } /// Returns true if the viewport height changed, false otherwise. @@ -433,7 +459,7 @@ class SuperDesktopTextFieldState extends State implements key: _textScrollKey, textKey: _textKey, textController: _controller, - textAlign: widget.textAlign, + textAlign: _textAlign, scrollController: _scrollController, viewportHeight: _viewportHeight, estimatedLineHeight: _getEstimatedLineHeight(), @@ -498,7 +524,8 @@ class SuperDesktopTextFieldState extends State implements return SuperText( key: _textKey, richText: _controller.text.computeTextSpan(widget.textStyleBuilder), - textAlign: widget.textAlign, + textAlign: _textAlign, + textDirection: _textDirection, textScaler: _textScaler, layerBeneathBuilder: (context, textLayout) { final isTextEmpty = _controller.text.isEmpty; diff --git a/super_editor/lib/src/super_textfield/ios/ios_textfield.dart b/super_editor/lib/src/super_textfield/ios/ios_textfield.dart index 6cdd9baaef..78475c9166 100644 --- a/super_editor/lib/src/super_textfield/ios/ios_textfield.dart +++ b/super_editor/lib/src/super_textfield/ios/ios_textfield.dart @@ -2,6 +2,7 @@ import 'package:attributed_text/attributed_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:follow_the_leader/follow_the_leader.dart'; +import 'package:super_editor/src/default_editor/text_tools.dart'; import 'package:super_editor/src/infrastructure/_logging.dart'; import 'package:super_editor/src/infrastructure/attributed_text_styles.dart'; import 'package:super_editor/src/infrastructure/flutter/build_context.dart'; @@ -41,7 +42,7 @@ class SuperIOSTextField extends StatefulWidget { this.tapHandlers = const [], this.textController, this.textStyleBuilder = defaultTextFieldStyleBuilder, - this.textAlign = TextAlign.left, + this.textAlign, this.padding, this.hintBehavior = HintBehavior.displayHintUntilFocus, this.hintBuilder, @@ -73,7 +74,10 @@ class SuperIOSTextField extends StatefulWidget { final ImeAttributedTextEditingController? textController; /// The alignment to use for text in this text field. - final TextAlign textAlign; + /// + /// If `null`, the text alignment is determined by the text direction + /// of the content. + final TextAlign? textAlign; /// Text style factory that creates styles for the content in /// [textController] based on the attributions in that content. @@ -181,6 +185,22 @@ class SuperIOSTextFieldState extends State late FocusNode _focusNode; late ImeAttributedTextEditingController _textEditingController; + + /// The text direction of the first character in the text. + /// + /// Used to align and position the caret depending on whether + /// the text is RTL or LTR. + TextDirection? _contentTextDirection; + + /// The text direction applied to the inner text. + TextDirection get _textDirection => _contentTextDirection ?? TextDirection.ltr; + + TextAlign get _textAlign => + widget.textAlign ?? + ((_textDirection == TextDirection.ltr) // + ? TextAlign.left + : TextAlign.right); + late FloatingCursorController _floatingCursorController; final _toolbarLeaderLink = LeaderLink(); @@ -237,6 +257,8 @@ class SuperIOSTextFieldState extends State overlayController: _overlayController, ); + _contentTextDirection = getParagraphDirection(_textEditingController.text.toPlainText()); + WidgetsBinding.instance.addObserver(this); if (_focusNode.hasFocus) { @@ -451,6 +473,10 @@ class SuperIOSTextFieldState extends State if (_textEditingController.selection.isCollapsed) { _editingOverlayController.hideToolbar(); } + + setState(() { + _contentTextDirection = getParagraphDirection(_textEditingController.text.toPlainText()); + }); } void _onTextScrollChange() { @@ -575,7 +601,7 @@ class SuperIOSTextFieldState extends State textScrollController: _textScrollController, textKey: _textContentKey, textEditingController: _textEditingController, - textAlign: widget.textAlign, + textAlign: _textAlign, minLines: widget.minLines, maxLines: widget.maxLines, lineHeight: widget.lineHeight, @@ -618,7 +644,8 @@ class SuperIOSTextFieldState extends State return SuperText( key: _textContentKey, richText: textSpan, - textAlign: widget.textAlign, + textAlign: _textAlign, + textDirection: _textDirection, textScaler: MediaQuery.textScalerOf(context), layerBeneathBuilder: (context, textLayout) { final isTextEmpty = _textEditingController.text.isEmpty; diff --git a/super_editor/lib/src/super_textfield/super_textfield.dart b/super_editor/lib/src/super_textfield/super_textfield.dart index 2e1c172176..37ff87fa89 100644 --- a/super_editor/lib/src/super_textfield/super_textfield.dart +++ b/super_editor/lib/src/super_textfield/super_textfield.dart @@ -60,7 +60,7 @@ class SuperTextField extends StatefulWidget { this.tapRegionGroupId, this.configuration, this.textController, - this.textAlign = TextAlign.left, + this.textAlign, this.textStyleBuilder = defaultTextFieldStyleBuilder, this.hintBehavior = HintBehavior.displayHintUntilFocus, this.hintBuilder, @@ -98,7 +98,10 @@ class SuperTextField extends StatefulWidget { final AttributedTextEditingController? textController; /// The alignment of the text in this text field. - final TextAlign textAlign; + /// + /// If `null`, the text alignment is determined by the text direction + /// of the content. + final TextAlign? textAlign; /// Text style factory that creates styles for the content in /// [textController] based on the attributions in that content. diff --git a/super_editor/test/super_editor/supereditor_test_tools.dart b/super_editor/test/super_editor/supereditor_test_tools.dart index 9399e5d83a..e33d1fcfd6 100644 --- a/super_editor/test/super_editor/supereditor_test_tools.dart +++ b/super_editor/test/super_editor/supereditor_test_tools.dart @@ -683,6 +683,7 @@ class _TestSuperEditorState extends State<_TestSuperEditor> { componentBuilders: [ ...widget.testConfiguration.addedComponents, ...(widget.testConfiguration.componentBuilders ?? defaultComponentBuilders), + if (widget.testConfiguration.componentBuilders == null) TaskComponentBuilder(widget.testDocumentContext.editor) ], scrollController: widget.testConfiguration.scrollController, documentOverlayBuilders: _createOverlayBuilders(), diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-android.png new file mode 100644 index 0000000000000000000000000000000000000000..c9c526bbbd4f59cff5ce207ccc557a8a18f57e31 GIT binary patch literal 3637 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 pjj9|nk44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOzPZ!6KiaBrZ8ukUJOSA>XpY9cB7VYkIk<9E+Fb+OrrQx6{6fjB2 z@l?`^1jBc4M5YLO9QpRSx^BMm&VBb+Yj?SaOHKRxt{{RDX!f-k;k8kGwA-4}rHC7|&A!xRkzabdt zLSP8B#>O;(yw`tv&F%-YelX$mA1E(K)Uq%fIFt7Gb?kh*x;Wd~Zz*3S)ysc>D*O8E z>+Or_KBvWr$rbt~FmLNUJ@MbW>vwE^%~f_b>)Q8JV3g0z z7yr;oAbaPy$sdsTeOIyiTGj59^P65*JY5%g_Se2QR`X(y&n^{@5sr5lB!$H6i|hu+ zs&@C-J=A&gNazgEja4yknDnOVD_}{<;5bOT`JS)+qB}5+Uq47x)KXA1R<^|L*%i5V z`|0|+_jUE=zf2eyeyAaG7Io5u&y(fzhmu zcbf4p28*R%%Oqfzl87%I?uR kd9*z;+CUp^oPO4Ki@cYdkzaTXSa&gay85}Sb4q9e0M$-w4gdfE literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..67045eb87506f0a02fd00f3f7d438be64c440a66 GIT binary patch literal 3156 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOzPZ!6KiaBrZ8ukUJOSA>XpY9cB7VYkIk<9E+Fb+OrrQx6{6fjB2 z@l?`^1jBc4M5YLO9QpRSx^BMm&VBb+Yj?SaOHKRxt{{RDX!f-k;k8kGwA-4}rHC7|&A!xRkzabdt zLSP8B#>O;(yw`tv&F%-YelX$mA1E(K)Uq%fIFt7Gb?kh*x;Wd~Zz*3S)ysc>D*O8E z>+Or_KBvWr$rbt~FmLNUJ@MbW>vwE^%~f_b>)Q8JV3g0z z7yr;oAbaPy$sdsTeOIyiTGj59^P65*JY5%g_Se2QR`X(y&n^{@5sr5lB!$H6i|hu+ zs&@C-J=A&gNazgEja4yknDnOVD_}{<;5bOT`JS)+qB}5+Uq47x)KXA1R<^|L*%i5V z`|0|+_jUE=zf2eyeyAaG7Io5u&y(fzhmu zcbf4p28*R%%Oqfzl87%I?uR kd9*z;+CUp^oPO4Ki@cYdkzaTXSa&gay85}Sb4q9e0M$-w4gdfE literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-android.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9587c6169a6b676d63c114de4e131b5d4a00e0 GIT binary patch literal 3137 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WPKPZ!6KiaBrZ*k(mXGPFHhy&&yc(#@&n!fKj9X|9Juw3ZmW)j2X% zcfIS}cOsgHSdu5y-^$Gdl-Oh zBPJFGh7%kDKuP6M!J!l&uP?GQSX_Vj(ctc#xPH51o8P^cTXy#9vo+~&q@Jtpo|d)p z{poduZyu%R5scP|DR;~4=k^tUx|#dE>}F};v|sz)YL^#wD-`{#uG{&&Nb*qY~TgfjF4r?=aFzbu(E z`F!5)?WfmuZvuwg^w_eGVgnGdSp5u(?e_#@n~;xxi0S|fw#Nj6grd@Lvs^91gRlGQ z{(O7#z5eg#^g1zyhUYsNsalqR?f6^sBiZy9$ND0xbN+Sp6*fNR+pqtuJXcu%-5__J zS-Nd9F#YS^54t}+_VJJ3RhER)xz4G)-Shp9TmF2rt$O#HT`Qou=>68$lLkwTaHa}a zW8J&-Tz~$u^YM1QcE@IKe*JyZ6^Kj4_iN$yBrFxJ|NQmwcJ=-Lf8W3VJKV|7{l5+4 zflE}09#9E<@BB-Pemf?H1OKrac?MYX{?!i!Cc=lbO@yPBHv?5O+-P`?h9{wll%iS# dYXbW$zrB!CnPpbWTwrU9!PC{xWt~$(697g*X7~UA literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..ca9587c6169a6b676d63c114de4e131b5d4a00e0 GIT binary patch literal 3137 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WPKPZ!6KiaBrZ*k(mXGPFHhy&&yc(#@&n!fKj9X|9Juw3ZmW)j2X% zcfIS}cOsgHSdu5y-^$Gdl-Oh zBPJFGh7%kDKuP6M!J!l&uP?GQSX_Vj(ctc#xPH51o8P^cTXy#9vo+~&q@Jtpo|d)p z{poduZyu%R5scP|DR;~4=k^tUx|#dE>}F};v|sz)YL^#wD-`{#uG{&&Nb*qY~TgfjF4r?=aFzbu(E z`F!5)?WfmuZvuwg^w_eGVgnGdSp5u(?e_#@n~;xxi0S|fw#Nj6grd@Lvs^91gRlGQ z{(O7#z5eg#^g1zyhUYsNsalqR?f6^sBiZy9$ND0xbN+Sp6*fNR+pqtuJXcu%-5__J zS-Nd9F#YS^54t}+_VJJ3RhER)xz4G)-Shp9TmF2rt$O#HT`Qou=>68$lLkwTaHa}a zW8J&-Tz~$u^YM1QcE@IKe*JyZ6^Kj4_iN$yBrFxJ|NQmwcJ=-Lf8W3VJKV|7{l5+4 zflE}09#9E<@BB-Pemf?H1OKrac?MYX{?!i!Cc=lbO@yPBHv?5O+-P`?h9{wll%iS# dYXbW$zrB!CnPpbWTwrU9!PC{xWt~$(697g*X7~UA literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..5b016a5c602cf323a3b08c31cc33ee299e0105dd GIT binary patch literal 3130 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOHPZ!6KiaBrZ9L!RRWN5o+lrlNJbJwTZ*crzQMbB>Du{rHxciS@c zo=G+3LGzgNm{`9r_jvGFDE#_q8-Jj2jms~x11Y(721W)3BPJFGh7%kD3=9g&4h#$p zJ)?p{DL`!c?HCTMJtzM>bNB5YyJO4m^)Ju-yKCP|$=%0xre6=z{aSm|v@iD0NnQ>H zh6#cS3=9r#4ZygUWMpDsNMS)1>@K>o-}n0IIW>I0&3C`4((ta0FT44*{>E1i6lGZT z(nm0@bnltZFPs_fJo-~OZ`vK8gzep?Bg3p=ikBM zNuWdTH?ab}jz+Sx*HCT#+%|ORh zeo<_AU--|$|MuOqFNSts>uh{qU;pZR{)Xu}+uhT$R=z*IuJFyT+?Bh39L1f!Kk7yR zQ~fdA3h?F%Sl|=L13;%THvG2PyXN1o&#zO@oo8bB5Wka=s>SPPn_mnEu2)q>_Szkq zzBBIG{(pbK*}%7MUi2P$|I5EhYcCs@^(J5c1j+RG*Z(H$f`mZ6-`ihS&76Eb@qgO% zSpKb$bo487w_N97sR-7rn_ekb%kiMhUw`#Q_a9}~Pv8Gu<1FirZBIDJ|Mdi@9QYHH=RkR#2P~AB8RB18&2*P% zWO#rh-s_#(V1l literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-macOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-macOS.png new file mode 100644 index 0000000000000000000000000000000000000000..5b016a5c602cf323a3b08c31cc33ee299e0105dd GIT binary patch literal 3130 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOHPZ!6KiaBrZ9L!RRWN5o+lrlNJbJwTZ*crzQMbB>Du{rHxciS@c zo=G+3LGzgNm{`9r_jvGFDE#_q8-Jj2jms~x11Y(721W)3BPJFGh7%kD3=9g&4h#$p zJ)?p{DL`!c?HCTMJtzM>bNB5YyJO4m^)Ju-yKCP|$=%0xre6=z{aSm|v@iD0NnQ>H zh6#cS3=9r#4ZygUWMpDsNMS)1>@K>o-}n0IIW>I0&3C`4((ta0FT44*{>E1i6lGZT z(nm0@bnltZFPs_fJo-~OZ`vK8gzep?Bg3p=ikBM zNuWdTH?ab}jz+Sx*HCT#+%|ORh zeo<_AU--|$|MuOqFNSts>uh{qU;pZR{)Xu}+uhT$R=z*IuJFyT+?Bh39L1f!Kk7yR zQ~fdA3h?F%Sl|=L13;%THvG2PyXN1o&#zO@oo8bB5Wka=s>SPPn_mnEu2)q>_Szkq zzBBIG{(pbK*}%7MUi2P$|I5EhYcCs@^(J5c1j+RG*Z(H$f`mZ6-`ihS&76Eb@qgO% zSpKb$bo487w_N97sR-7rn_ekb%kiMhUw`#Q_a9}~Pv8Gu<1FirZBIDJ|Mdi@9QYHH=RkR#2P~AB8RB18&2*P% zWO#rh-s_#(V1l literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..5b016a5c602cf323a3b08c31cc33ee299e0105dd GIT binary patch literal 3130 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOHPZ!6KiaBrZ9L!RRWN5o+lrlNJbJwTZ*crzQMbB>Du{rHxciS@c zo=G+3LGzgNm{`9r_jvGFDE#_q8-Jj2jms~x11Y(721W)3BPJFGh7%kD3=9g&4h#$p zJ)?p{DL`!c?HCTMJtzM>bNB5YyJO4m^)Ju-yKCP|$=%0xre6=z{aSm|v@iD0NnQ>H zh6#cS3=9r#4ZygUWMpDsNMS)1>@K>o-}n0IIW>I0&3C`4((ta0FT44*{>E1i6lGZT z(nm0@bnltZFPs_fJo-~OZ`vK8gzep?Bg3p=ikBM zNuWdTH?ab}jz+Sx*HCT#+%|ORh zeo<_AU--|$|MuOqFNSts>uh{qU;pZR{)Xu}+uhT$R=z*IuJFyT+?Bh39L1f!Kk7yR zQ~fdA3h?F%Sl|=L13;%THvG2PyXN1o&#zO@oo8bB5Wka=s>SPPn_mnEu2)q>_Szkq zzBBIG{(pbK*}%7MUi2P$|I5EhYcCs@^(J5c1j+RG*Z(H$f`mZ6-`ihS&76Eb@qgO% zSpKb$bo487w_N97sR-7rn_ekb%kiMhUw`#Q_a9}~Pv8Gu<1FirZBIDJ|Mdi@9QYHH=RkR#2P~AB8RB18&2*P% zWO#rh-s_#(V1l literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-android.png new file mode 100644 index 0000000000000000000000000000000000000000..471708079f0204ddeabd5a7ea43d0acaa98a2df8 GIT binary patch literal 3614 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 BUjqOD literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..82d3db2fd076664bddac95d7ab19eab84456864c GIT binary patch literal 3550 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`P*aDei(^Q|oVRz+cF#?hJn-4muw zTuyJA^P%edHYpz;OB!uHdalS4=nSUME7^fmA0HzV149Z62LrwZ4dsh+;~+xk1%v1?yf-pv00hP6f{4^2H* zLw;B*Ffhniz0cWiyzbxIS33$nAG7%V;b!5pg~pG6e0&_-e^d{okFWP_`S#V7@)HUHZmuUv0unr2+@QZ)6as15@|*uA8U68bSUi8Zel`ri|iPwx46 zuljv&`o4c(e*6))H{W*u?z|MYGWP}sh7F(WemrRY_*uN(Ebs2F((BLTe!tFs{M9`E z{If@kg@MjmvuAq!_fHj{<@4XYy7|85`)zhR{%UoguOce*_iX$f-Mjr>UG|>;|9-Pu z!NkMY#oqk7x%+5R%f8EoQh5wJgyMJ|a^!fVF2c9Rs2W2(w_xpaolivU9==$RulaIgqb?0$>RgKN} zFU2=wYD$3d)ZqLOX#C^1_5Z4EzO^3D`+c)hSp8VPUYyka%9qp}7Z_y>3~#Qj zoV@&)eBJ-!8~?uhwxj6nDa>F$4hibwcd<({b&mcfDhE@07-tbig`|qu=OE59S9ZG8tZ?(h8D&h*UUmf+@YZu@?5v zW^bPQHz(F2f4%aC>${?^f8I2G@0-$sYDRe>U=qSpFwX2<$(|sEBl~Qvf4xgwMs?5x zi*zvm0TZBCSGrpWd`l8t1I*;F3pR>xSaqiXn4qv0{4;MRrSF_m1x&XOG76Y!T3y_n zQN{4>`KSK%ef#%R{oVF{{*PNbKb}~;Tzw`d1H-!`yi_igi%!J;KmYar@4uVR+gk$z z#rgNnKbM|bzuQy#@XwRaci(OX((}$;iu)GTy7$`EUGt{z<+~ntpq)T%nU?&D@j+bF z8=cMNXU)@Z?tZhi{@WuzP;tDq{$K6EgES69LHwx|DT+a%y<<8oF<8Z1D_vi@b=~t+ z{e;k+j5wN%f`ZNT{Rf$j?sw2(x4oAiS|rAV^5TImc`GKyTCGo!PC{xWt~$(698R1 BUjqOD literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..5c14f06d05492a7063b658245f0165932a5c1926 GIT binary patch literal 3508 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 gLy5Fdn)#nm)k|+jSWWB{VCXV RTL mode >', () { + testGoldensOnAllPlatforms( + 'inserts text and paints caret on the left side of paragraph', + (tester) async { + await tester // + .createDocument() + .withSingleEmptyParagraph() + .withInputSource(TextInputSource.ime) + .pump(); + + // Place the caret at the beginning of the paragraph. + await tester.placeCaretInParagraph('1', 0); + + // Type the text "Example of text containing multiple lines.". + await tester.ime.typeText( + 'مثال لنص يحتوي على عدة أسطر.', + getter: imeClientGetter, + ); + + await screenMatchesGolden(tester, 'super-editor-rtl-caret-paragraph-${defaultTargetPlatform.name}'); + }, + windowSize: const Size(800, 500), + ); + + testGoldensOnAllPlatforms( + 'inserts text and paints caret on the left side of unordered list item', + (tester) async { + await tester // + .createDocument() + .withCustomContent( + MutableDocument( + nodes: [ + ListItemNode.unordered(id: '1', text: AttributedText()), + ], + ), + ) + .withInputSource(TextInputSource.ime) + .pump(); + + // Place the caret at the beginning of the list item. + await tester.placeCaretInParagraph('1', 0); + + // Type the text "Example of text containing multiple lines.". + await tester.ime.typeText( + 'مثال لنص يحتوي على عدة أسطر.', + getter: imeClientGetter, + ); + + await screenMatchesGolden(tester, 'super-editor-rtl-caret-unordered-list-item-${defaultTargetPlatform.name}'); + }, + windowSize: const Size(800, 500), + ); + + testGoldensOnAllPlatforms( + 'inserts text and paints caret on the left side of ordered list item', + (tester) async { + await tester // + .createDocument() + .withCustomContent( + MutableDocument( + nodes: [ + ListItemNode.ordered(id: '1', text: AttributedText()), + ], + ), + ) + .withInputSource(TextInputSource.ime) + .pump(); + + // Place the caret at the beginning of the list item. + await tester.placeCaretInParagraph('1', 0); + + // Type the text "Example of text containing multiple lines.". + await tester.ime.typeText( + 'مثال لنص يحتوي على عدة أسطر.', + getter: imeClientGetter, + ); + + await screenMatchesGolden(tester, 'super-editor-rtl-caret-ordered-list-item-${defaultTargetPlatform.name}'); + }, + windowSize: const Size(800, 500), + ); + + testGoldensOnAllPlatforms( + 'inserts text and paints caret on the left side of task', + (tester) async { + await tester // + .createDocument() + .withCustomContent( + MutableDocument( + nodes: [ + TaskNode(id: '1', text: AttributedText(), isComplete: false), + ], + ), + ) + .withInputSource(TextInputSource.ime) + .pump(); + + // Place the caret at the beginning of the task. + await tester.placeCaretInParagraph('1', 0); + + // Type the text "Example of text containing multiple lines.". + await tester.ime.typeText( + 'مثال لنص يحتوي على عدة أسطر.', + getter: imeClientGetter, + ); + + await screenMatchesGolden(tester, 'super-editor-rtl-caret-task-${defaultTargetPlatform.name}'); + }, + windowSize: const Size(800, 500), + ); + }); +} diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-android.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-android.png new file mode 100644 index 0000000000000000000000000000000000000000..862b982fded652ccf6d326bc3ff5568c6c1344c6 GIT binary patch literal 3096 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^sKIG}*7*a9k?cIa9QjQF57js;=r##T|P+xa`X+W#@1F4hdX6`8`)s$*d z%BFws>o~mLGD2Rt*JiH$v!Cn?3^V6129i0w2bdT-ICU5rW{fgM17S2JjF?<-Bk$MW z>@RE;R+s--R2@?P{IcNk_qX|Xch$eWR`>7Q_HDcF^NKMz_#`kg9AQCbl9`Bn?XU1wv62unH#z@>t$>o>U}Hj-#UN) z-(=m~{nA)Vu^T4y`98l8WMGJX#73&iwn^JE{5!hEzHi=#lQmX%zSRDX-u^CrH^@TW z-^YLD;jnT>cuUTy#qpDEe|_!xdtLYK*9(`{{QdKG|G9s!r)|R#te62!p$I6RrY!qg z_@!m{jq|pkIPFiZKaC?!XZg2Re&Jwf&@F6cWe^Ys)ibC-bFlZ)#&%KiOy?BDA*_qTyU z_xfF2$&bQN!PE{;&xUXN7pvbn`fua4ebDrO2P5HwwJ!TvGQ+*ULASEnzW(RW*WLGx z8O|SyuOYGKxKZ+N#$SCWhK32))9h$nj5RBd2Fqx$45!BG4f$#Jw+K(K*Z%@+-7|Q) L`njxgN@xNAr&q6? literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-iOS.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..76fcc4c3e2ebbd2b62b075373d80f00bc228d2a0 GIT binary patch literal 3102 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^sKH=%&7*a9k?cIaE(vA#m7jvG7-C@YJV76%>K*_W3+l$TwfUipua!Qt8S7mN%H(aIYb7?PN{85Gn;8KZ$Pni57# zF4#7^?!VMt#(i@$>t$>o&iz@k<3;uF=;`m`cW<4)|L@wh@5-B583crZ#gbtoGUIly z9K)WSU$Zww@87ra|DL6L>!N0XbUd%!{x^?Kp||@YbA$Qie->4TLf<@>%C@ineLU(` zojHbEL9XAJX(#*aqA~-+wj;b^3=TdCj0{Iuh-BoLS26tQ#~MJozmNaQ*)Ofb&@kfw z6GI0l3M0HF=hWKx$#;K!?fUy%_wCmUm)89K^Va_Szt7XQ-6z=ayy>;Nf8Y93>rZ31JZF}Fd*v4nh6de2?2(R{4B#T5^blSA{l}Mw@6Yppum3;q z+j0GvFZrKe7F_)PZTr64|F%Z|{(1X8zQEd`u)VIg^!0V~Z}ZdtZ(9ors`r-$Br(Zn zzx{nZ@9pz^0wHx{((dES`RnWJcKrXO%h2GZx^prwfgF}%C(jTL zAgJL;>0n^sKJV$`7*a9k?QO$8VMhj6NB&QuGKXBxY*ky9PVPGL<*ho{x?S451n`n@`^OEeh&j8vk z|N7#$-3OT%IyiM08fF|oX6U~DxuZ{xkzs>s9?8Yr?~BY2;&s|Dzu?qg^Zaq>|J(O> zmHmH{W?%n1`dk0kTXp8x{kopx=9KFFYP-L`7X7VXz2&-df7-Xt+y5JZIDbiZ1$k)_ z==KF$Wc(JZ-#Jp3XZZW)ZTo3|b+J2mW2&wE^^3|34BL)ik95pr02cwJhn$Po-`khp zulsZBZQ1U-)BWF<$J^Iee5|#cbBw3@Xq8>u*ZqIjZr@g4ws}5~aUEAoRl0KUKe@Ml zBP3GZx^prwfgF}%C(jTL zAgJL;>0n^sKJV$`7*a9k?QO$8VMhj6NB&QuGKXBxY*ky9PVPGL<*ho{x?S451n`n@`^OEeh&j8vk z|N7#$-3OT%IyiM08fF|oX6U~DxuZ{xkzs>s9?8Yr?~BY2;&s|Dzu?qg^Zaq>|J(O> zmHmH{W?%n1`dk0kTXp8x{kopx=9KFFYP-L`7X7VXz2&-df7-Xt+y5JZIDbiZ1$k)_ z==KF$Wc(JZ-#Jp3XZZW)ZTo3|b+J2mW2&wE^^3|34BL)ik95pr02cwJhn$Po-`khp zulsZBZQ1U-)BWF<$J^Iee5|#cbBw3@Xq8>u*ZqIjZr@g4ws}5~aUEAoRl0KUKe@Ml zBP3GZx^prwfgF}%C(jTL zAgJL;>0n^sKJV$`7*a9k?QO$8VMhj6NB&QuGKXBxY*ky9PVPGL<*ho{x?S451n`n@`^OEeh&j8vk z|N7#$-3OT%IyiM08fF|oX6U~DxuZ{xkzs>s9?8Yr?~BY2;&s|Dzu?qg^Zaq>|J(O> zmHmH{W?%n1`dk0kTXp8x{kopx=9KFFYP-L`7X7VXz2&-df7-Xt+y5JZIDbiZ1$k)_ z==KF$Wc(JZ-#Jp3XZZW)ZTo3|b+J2mW2&wE^^3|34BL)ik95pr02cwJhn$Po-`khp zulsZBZQ1U-)BWF<$J^Iee5|#cbBw3@Xq8>u*ZqIjZr@g4ws}5~aUEAoRl0KUKe@Ml zBP3 RTL mode >', () { + testGoldensOnAllPlatforms( + 'inserts text and paints caret on the left side', + (tester) async { + await _pumpTestApp(tester); + + // Place the caret at the beginning of the text field. + await tester.placeCaretInSuperTextField(0); + + // Type the text "Example of text containing multiple lines.". + await tester.ime.typeText( + 'مثال لنص يحتوي على عدة أسطر', + getter: imeClientGetter, + ); + await tester.pumpAndSettle(); + + await screenMatchesGolden(tester, 'super-text-field_rtl-caret-${defaultTargetPlatform.name}'); + }, + windowSize: const Size(600, 600), + ); + }); +} + +/// Pump a widget tree with a centered multiline textfield with +/// a yellow background, so we can clearly see the bounds of the textfield. +Future _pumpTestApp(WidgetTester tester) async { + final controller = ImeAttributedTextEditingController(); + + await tester.pumpWidget( + MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + body: Center( + child: SizedBox( + width: 300, + child: ColoredBox( + color: Colors.yellow, + child: SuperTextField( + textController: controller, + maxLines: 10, + lineHeight: 16, + ), + ), + ), + ), + ), + ), + ); +} diff --git a/super_text_layout/lib/src/super_text.dart b/super_text_layout/lib/src/super_text.dart index 34c4ba9285..936ff8b0a5 100644 --- a/super_text_layout/lib/src/super_text.dart +++ b/super_text_layout/lib/src/super_text.dart @@ -90,6 +90,7 @@ class SuperTextState extends ProseTextState with ProseTextBlock { text: LayoutAwareRichText( text: widget.richText, textAlign: widget.textAlign, + textDirection: widget.textDirection, textScaler: widget.textScaler ?? MediaQuery.textScalerOf(context), onMarkNeedsLayout: _invalidateParagraph, ), @@ -295,12 +296,14 @@ class LayoutAwareRichText extends RichText { Key? key, required InlineSpan text, TextAlign textAlign = TextAlign.left, + TextDirection textDirection = TextDirection.ltr, TextScaler textScaler = TextScaler.noScaling, required this.onMarkNeedsLayout, }) : super( key: key, text: text, textAlign: textAlign, + textDirection: textDirection, textScaler: textScaler, ); From 74e19f1a180db6325faf7c49d4f4769490f2522a Mon Sep 17 00:00:00 2001 From: Angelo Silvestre Date: Sun, 19 Jan 2025 17:04:59 -0300 Subject: [PATCH 2/3] Wrap text with Directionality --- .../lib/src/default_editor/paragraph.dart | 65 ++++----- .../android/android_textfield.dart | 109 +++++++-------- .../desktop/desktop_textfield.dart | 107 +++++++-------- .../super_textfield/ios/ios_textfield.dart | 125 +++++++++--------- 4 files changed, 209 insertions(+), 197 deletions(-) diff --git a/super_editor/lib/src/default_editor/paragraph.dart b/super_editor/lib/src/default_editor/paragraph.dart index b953121059..c4b3db7eff 100644 --- a/super_editor/lib/src/default_editor/paragraph.dart +++ b/super_editor/lib/src/default_editor/paragraph.dart @@ -331,39 +331,42 @@ class _ParagraphComponentState extends State @override Widget build(BuildContext context) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Indent spacing on left. - SizedBox( - width: widget.viewModel.indentCalculator( - widget.viewModel.textStyleBuilder({}), - widget.viewModel.indent, + return Directionality( + textDirection: widget.viewModel.textDirection, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Indent spacing on left. + SizedBox( + width: widget.viewModel.indentCalculator( + widget.viewModel.textStyleBuilder({}), + widget.viewModel.indent, + ), ), - ), - // The actual paragraph UI. - Expanded( - child: TextComponent( - key: _textKey, - text: widget.viewModel.text, - textDirection: widget.viewModel.textDirection, - textAlign: widget.viewModel.textAlignment, - textScaler: widget.viewModel.textScaler, - textStyleBuilder: widget.viewModel.textStyleBuilder, - inlineWidgetBuilders: widget.viewModel.inlineWidgetBuilders, - metadata: widget.viewModel.blockType != null - ? { - 'blockType': widget.viewModel.blockType, - } - : {}, - textSelection: widget.viewModel.selection, - selectionColor: widget.viewModel.selectionColor, - highlightWhenEmpty: widget.viewModel.highlightWhenEmpty, - underlines: widget.viewModel.createUnderlines(), - showDebugPaint: widget.showDebugPaint, + // The actual paragraph UI. + Expanded( + child: TextComponent( + key: _textKey, + text: widget.viewModel.text, + textDirection: widget.viewModel.textDirection, + textAlign: widget.viewModel.textAlignment, + textScaler: widget.viewModel.textScaler, + textStyleBuilder: widget.viewModel.textStyleBuilder, + inlineWidgetBuilders: widget.viewModel.inlineWidgetBuilders, + metadata: widget.viewModel.blockType != null + ? { + 'blockType': widget.viewModel.blockType, + } + : {}, + textSelection: widget.viewModel.selection, + selectionColor: widget.viewModel.selectionColor, + highlightWhenEmpty: widget.viewModel.highlightWhenEmpty, + underlines: widget.viewModel.createUnderlines(), + showDebugPaint: widget.showDebugPaint, + ), ), - ), - ], + ], + ), ); } } diff --git a/super_editor/lib/src/super_textfield/android/android_textfield.dart b/super_editor/lib/src/super_textfield/android/android_textfield.dart index 8b254d2e64..6275f1fa80 100644 --- a/super_editor/lib/src/super_textfield/android/android_textfield.dart +++ b/super_editor/lib/src/super_textfield/android/android_textfield.dart @@ -634,62 +634,65 @@ class SuperAndroidTextFieldState extends State ? _textEditingController.text.computeTextSpan(widget.textStyleBuilder) : TextSpan(text: "", style: widget.textStyleBuilder({})); - return SuperText( - key: _textContentKey, - richText: textSpan, - textAlign: _textAlign, + return Directionality( textDirection: _textDirection, - textScaler: MediaQuery.textScalerOf(context), - layerBeneathBuilder: (context, textLayout) { - final isTextEmpty = _textEditingController.text.isEmpty; - final showHint = widget.hintBuilder != null && - ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || - (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); - - return Stack( - children: [ - if (widget.textController?.selection.isValid == true) - // Selection highlight beneath the text. - TextLayoutSelectionHighlight( - textLayout: textLayout, - style: SelectionHighlightStyle( - color: widget.selectionColor, - ), - selection: widget.textController?.selection, - ), - // Underline beneath the composing region. - if (widget.textController?.composingRegion.isValid == true && widget.showComposingUnderline) - TextUnderlineLayer( - textLayout: textLayout, - style: StraightUnderlineStyle( - color: widget.textStyleBuilder({}).color ?? // - (Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), + child: SuperText( + key: _textContentKey, + richText: textSpan, + textAlign: _textAlign, + textDirection: _textDirection, + textScaler: MediaQuery.textScalerOf(context), + layerBeneathBuilder: (context, textLayout) { + final isTextEmpty = _textEditingController.text.isEmpty; + final showHint = widget.hintBuilder != null && + ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || + (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); + + return Stack( + children: [ + if (widget.textController?.selection.isValid == true) + // Selection highlight beneath the text. + TextLayoutSelectionHighlight( + textLayout: textLayout, + style: SelectionHighlightStyle( + color: widget.selectionColor, + ), + selection: widget.textController?.selection, ), - underlines: [ - TextLayoutUnderline( - range: widget.textController!.composingRegion, + // Underline beneath the composing region. + if (widget.textController?.composingRegion.isValid == true && widget.showComposingUnderline) + TextUnderlineLayer( + textLayout: textLayout, + style: StraightUnderlineStyle( + color: widget.textStyleBuilder({}).color ?? // + (Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), ), - ], - ), - if (showHint) // - widget.hintBuilder!(context), - ], - ); - }, - layerAboveBuilder: (context, textLayout) { - if (!_focusNode.hasFocus) { - return const SizedBox(); - } - - return TextLayoutCaret( - textLayout: textLayout, - style: widget.caretStyle, - position: _textEditingController.selection.isCollapsed // - ? _textEditingController.selection.extent - : null, - blinkController: _caretBlinkController, - ); - }, + underlines: [ + TextLayoutUnderline( + range: widget.textController!.composingRegion, + ), + ], + ), + if (showHint) // + widget.hintBuilder!(context), + ], + ); + }, + layerAboveBuilder: (context, textLayout) { + if (!_focusNode.hasFocus) { + return const SizedBox(); + } + + return TextLayoutCaret( + textLayout: textLayout, + style: widget.caretStyle, + position: _textEditingController.selection.isCollapsed // + ? _textEditingController.selection.extent + : null, + blinkController: _caretBlinkController, + ); + }, + ), ); } diff --git a/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart b/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart index 0f124ecbf2..c649818cb6 100644 --- a/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart +++ b/super_editor/lib/src/super_textfield/desktop/desktop_textfield.dart @@ -521,61 +521,64 @@ class SuperDesktopTextFieldState extends State implements } Widget _buildSelectableText() { - return SuperText( - key: _textKey, - richText: _controller.text.computeTextSpan(widget.textStyleBuilder), - textAlign: _textAlign, + return Directionality( textDirection: _textDirection, - textScaler: _textScaler, - layerBeneathBuilder: (context, textLayout) { - final isTextEmpty = _controller.text.isEmpty; - final showHint = widget.hintBuilder != null && - ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || - (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); - - return Stack( - children: [ - if (widget.textController?.selection.isValid == true) - // Selection highlight beneath the text. - TextLayoutSelectionHighlight( - textLayout: textLayout, - style: widget.selectionHighlightStyle, - selection: widget.textController?.selection, - ), - // Underline beneath the composing region. - if (widget.textController?.composingRegion.isValid == true && _shouldShowComposingUnderline) - TextUnderlineLayer( - textLayout: textLayout, - style: StraightUnderlineStyle( - color: widget.textStyleBuilder({}).color ?? // - (Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), + child: SuperText( + key: _textKey, + richText: _controller.text.computeTextSpan(widget.textStyleBuilder), + textAlign: _textAlign, + textDirection: _textDirection, + textScaler: _textScaler, + layerBeneathBuilder: (context, textLayout) { + final isTextEmpty = _controller.text.isEmpty; + final showHint = widget.hintBuilder != null && + ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || + (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); + + return Stack( + children: [ + if (widget.textController?.selection.isValid == true) + // Selection highlight beneath the text. + TextLayoutSelectionHighlight( + textLayout: textLayout, + style: widget.selectionHighlightStyle, + selection: widget.textController?.selection, ), - underlines: [ - TextLayoutUnderline( - range: widget.textController!.composingRegion, + // Underline beneath the composing region. + if (widget.textController?.composingRegion.isValid == true && _shouldShowComposingUnderline) + TextUnderlineLayer( + textLayout: textLayout, + style: StraightUnderlineStyle( + color: widget.textStyleBuilder({}).color ?? // + (Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), ), - ], - ), - if (showHint) // - Align( - alignment: Alignment.centerLeft, - child: widget.hintBuilder!(context), - ), - ], - ); - }, - layerAboveBuilder: (context, textLayout) { - if (!_focusNode.hasFocus) { - return const SizedBox(); - } - - return TextLayoutCaret( - textLayout: textLayout, - style: widget.caretStyle, - position: _controller.selection.extent, - blinkTimingMode: widget.blinkTimingMode, - ); - }, + underlines: [ + TextLayoutUnderline( + range: widget.textController!.composingRegion, + ), + ], + ), + if (showHint) // + Align( + alignment: Alignment.centerLeft, + child: widget.hintBuilder!(context), + ), + ], + ); + }, + layerAboveBuilder: (context, textLayout) { + if (!_focusNode.hasFocus) { + return const SizedBox(); + } + + return TextLayoutCaret( + textLayout: textLayout, + style: widget.caretStyle, + position: _controller.selection.extent, + blinkTimingMode: widget.blinkTimingMode, + ); + }, + ), ); } } diff --git a/super_editor/lib/src/super_textfield/ios/ios_textfield.dart b/super_editor/lib/src/super_textfield/ios/ios_textfield.dart index 78475c9166..be00a479a0 100644 --- a/super_editor/lib/src/super_textfield/ios/ios_textfield.dart +++ b/super_editor/lib/src/super_textfield/ios/ios_textfield.dart @@ -641,71 +641,74 @@ class SuperIOSTextFieldState extends State caretStyle = caretStyle.copyWith(color: caretColorOverride); } - return SuperText( - key: _textContentKey, - richText: textSpan, - textAlign: _textAlign, + return Directionality( textDirection: _textDirection, - textScaler: MediaQuery.textScalerOf(context), - layerBeneathBuilder: (context, textLayout) { - final isTextEmpty = _textEditingController.text.isEmpty; - final showHint = widget.hintBuilder != null && - ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || - (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); - - return Stack( - clipBehavior: Clip.none, - children: [ - if (_textEditingController.selection.isValid == true) - // Selection highlight beneath the text. - TextLayoutSelectionHighlight( - textLayout: textLayout, - style: SelectionHighlightStyle( - color: widget.selectionColor, - ), - selection: _textEditingController.selection, - ), - // Underline beneath the composing region. - if (_textEditingController.composingRegion.isValid == true && widget.showComposingUnderline) - TextUnderlineLayer( - textLayout: textLayout, - style: StraightUnderlineStyle( - color: widget.textStyleBuilder({}).color ?? // - (Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), + child: SuperText( + key: _textContentKey, + richText: textSpan, + textAlign: _textAlign, + textDirection: _textDirection, + textScaler: MediaQuery.textScalerOf(context), + layerBeneathBuilder: (context, textLayout) { + final isTextEmpty = _textEditingController.text.isEmpty; + final showHint = widget.hintBuilder != null && + ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || + (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); + + return Stack( + clipBehavior: Clip.none, + children: [ + if (_textEditingController.selection.isValid == true) + // Selection highlight beneath the text. + TextLayoutSelectionHighlight( + textLayout: textLayout, + style: SelectionHighlightStyle( + color: widget.selectionColor, + ), + selection: _textEditingController.selection, ), - underlines: [ - TextLayoutUnderline( - range: _textEditingController.composingRegion, + // Underline beneath the composing region. + if (_textEditingController.composingRegion.isValid == true && widget.showComposingUnderline) + TextUnderlineLayer( + textLayout: textLayout, + style: StraightUnderlineStyle( + color: widget.textStyleBuilder({}).color ?? // + (Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white), ), - ], + underlines: [ + TextLayoutUnderline( + range: _textEditingController.composingRegion, + ), + ], + ), + if (showHint) // + widget.hintBuilder!(context), + ], + ); + }, + layerAboveBuilder: (context, textLayout) { + if (!_focusNode.hasFocus) { + return const SizedBox(); + } + + return Stack( + clipBehavior: Clip.none, + children: [ + TextLayoutCaret( + textLayout: textLayout, + style: widget.caretStyle, + position: _textEditingController.selection.isCollapsed // + ? _textEditingController.selection.extent + : null, + blinkController: _caretBlinkController, ), - if (showHint) // - widget.hintBuilder!(context), - ], - ); - }, - layerAboveBuilder: (context, textLayout) { - if (!_focusNode.hasFocus) { - return const SizedBox(); - } - - return Stack( - clipBehavior: Clip.none, - children: [ - TextLayoutCaret( - textLayout: textLayout, - style: widget.caretStyle, - position: _textEditingController.selection.isCollapsed // - ? _textEditingController.selection.extent - : null, - blinkController: _caretBlinkController, - ), - IOSFloatingCursor( - controller: _floatingCursorController, - ), - ], - ); - }, + IOSFloatingCursor( + controller: _floatingCursorController, + ), + ], + ); + }, + ), ); } From ac690d0a43347980a2d343c0946b1b8976ef456d Mon Sep 17 00:00:00 2001 From: Angelo Silvestre Date: Wed, 22 Jan 2025 20:54:59 -0300 Subject: [PATCH 3/3] Update goldens --- ...st-character-ordered-list-item-android.png | Bin 0 -> 3946 bytes ...ftmost-character-ordered-list-item-iOS.png | Bin 0 -> 2128 bytes ...most-character-ordered-list-item-linux.png | Bin 0 -> 2573 bytes ...most-character-ordered-list-item-macOS.png | Bin 0 -> 2117 bytes ...st-character-ordered-list-item-windows.png | Bin 0 -> 2117 bytes ...t-leftmost-character-paragraph-android.png | Bin 0 -> 3427 bytes ...et-at-leftmost-character-paragraph-iOS.png | Bin 0 -> 2054 bytes ...-at-leftmost-character-paragraph-linux.png | Bin 0 -> 2051 bytes ...-at-leftmost-character-paragraph-macOS.png | Bin 0 -> 2051 bytes ...t-leftmost-character-paragraph-windows.png | Bin 0 -> 2051 bytes ...ret-at-leftmost-character-task-android.png | Bin 0 -> 3780 bytes ...l-caret-at-leftmost-character-task-iOS.png | Bin 0 -> 2419 bytes ...caret-at-leftmost-character-task-linux.png | Bin 0 -> 2422 bytes ...caret-at-leftmost-character-task-macOS.png | Bin 0 -> 2422 bytes ...ret-at-leftmost-character-task-windows.png | Bin 0 -> 2422 bytes ...-character-unordered-list-item-android.png | Bin 0 -> 3828 bytes ...most-character-unordered-list-item-iOS.png | Bin 0 -> 2441 bytes ...st-character-unordered-list-item-linux.png | Bin 0 -> 2429 bytes ...st-character-unordered-list-item-macOS.png | Bin 0 -> 2429 bytes ...-character-unordered-list-item-windows.png | Bin 0 -> 2429 bytes ...or-rtl-caret-ordered-list-item-android.png | Bin 3637 -> 0 bytes ...editor-rtl-caret-ordered-list-item-iOS.png | Bin 3211 -> 0 bytes ...itor-rtl-caret-ordered-list-item-linux.png | Bin 3598 -> 0 bytes ...itor-rtl-caret-ordered-list-item-macOS.png | Bin 3156 -> 0 bytes ...or-rtl-caret-ordered-list-item-windows.png | Bin 3156 -> 0 bytes ...per-editor-rtl-caret-paragraph-android.png | Bin 3137 -> 0 bytes .../super-editor-rtl-caret-paragraph-iOS.png | Bin 3137 -> 0 bytes ...super-editor-rtl-caret-paragraph-linux.png | Bin 3130 -> 0 bytes ...super-editor-rtl-caret-paragraph-macOS.png | Bin 3130 -> 0 bytes ...per-editor-rtl-caret-paragraph-windows.png | Bin 3130 -> 0 bytes .../super-editor-rtl-caret-task-android.png | Bin 3614 -> 0 bytes .../super-editor-rtl-caret-task-iOS.png | Bin 3614 -> 0 bytes .../super-editor-rtl-caret-task-linux.png | Bin 3550 -> 0 bytes .../super-editor-rtl-caret-task-macOS.png | Bin 3550 -> 0 bytes .../super-editor-rtl-caret-task-windows.png | Bin 3550 -> 0 bytes ...-rtl-caret-unordered-list-item-android.png | Bin 3550 -> 0 bytes ...itor-rtl-caret-unordered-list-item-iOS.png | Bin 3550 -> 0 bytes ...or-rtl-caret-unordered-list-item-linux.png | Bin 3508 -> 0 bytes ...or-rtl-caret-unordered-list-item-macOS.png | Bin 3508 -> 0 bytes ...-rtl-caret-unordered-list-item-windows.png | Bin 3508 -> 0 bytes .../editor/supereditor_rtl_test.dart | 46 +++++++++--------- .../super-text-field_rtl-caret-android.png | Bin 3096 -> 0 bytes ...tl-caret-at-leftmost-character-android.png | Bin 0 -> 2921 bytes ...ld_rtl-caret-at-leftmost-character-iOS.png | Bin 0 -> 2925 bytes ..._rtl-caret-at-leftmost-character-linux.png | Bin 0 -> 2929 bytes ..._rtl-caret-at-leftmost-character-macOS.png | Bin 0 -> 2929 bytes ...tl-caret-at-leftmost-character-windows.png | Bin 0 -> 2929 bytes .../super-text-field_rtl-caret-iOS.png | Bin 3102 -> 0 bytes .../super-text-field_rtl-caret-linux.png | Bin 3109 -> 0 bytes .../super-text-field_rtl-caret-macOS.png | Bin 3109 -> 0 bytes .../super-text-field_rtl-caret-windows.png | Bin 3109 -> 0 bytes .../super_textfield_rtl_test.dart | 9 ++-- 52 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-windows.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-windows.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-windows.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-android.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-iOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-linux.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-macOS.png create mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-windows.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-android.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-iOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-linux.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-macOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-windows.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-android.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-iOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-linux.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-macOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-windows.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-android.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-iOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-linux.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-macOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-windows.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-android.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-iOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-linux.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-macOS.png delete mode 100644 super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-windows.png delete mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-android.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-android.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-iOS.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-linux.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-macOS.png create mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-windows.png delete mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-iOS.png delete mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-linux.png delete mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-macOS.png delete mode 100644 super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-windows.png diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-android.png new file mode 100644 index 0000000000000000000000000000000000000000..fb114e12cdca6cd2fcd1def7226d779ab94c3372 GIT binary patch literal 3946 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 literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..9429d84e92b9f405a5bdd25ade6722eb9b479dd7 GIT binary patch literal 2128 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WanPZ!6KiaBrZD*CAeia1<+#n)Dw!0y^~a3haS*AmAKd@KSb# zlbNjCUkO{cywZ)c^77xg^&3X}ZD3$XV&Y~{P(x*O=Rbe-t0?N7{=PTGrL~#*)x7op z%Jd(IbCni*KYg&Ui(zNPU3pp|8sE%<`eBuY9K zALd#dWMb&x)M03taR8a2YxeqW*%yI;>K*m-=E>c+-(zoWKhMvPzwHc5vHA6S`+fWN z__n*-yY)|uj$O0t_Uzpklg~^O#xMrO;2ZZ|Ffz<&KU=l$y4JZ$^RmO7Vhj#G35*O! zScqVV&Hj4r?|%jcMfK5KGg@?v7Mr8FfG)Yk44ofy`glX(f`uoOFa zhHwBu4M$1`0|V!FPZ!6KiaBp@-(P*dl=1k-@0JxCiw)f#>1|swGay9k^aQ=sqkkNa z%sRHI`#^Vs8*?kmdahr}J*Q^I=6K%DSd*&Fox|}c!+5d5nY=mcq&IipeRII_%^W=* zZMQ|Yi=ThK`>o=@^G7v#cI&_2oqYah-Lo*U@NjkphM7NC0?C+M7BL0~p9DsRBclw` z10lS({o5P4&)mi0G5&vEbgo>^SkNm7jDEvLRt5oK3K&OCpHFm`2bpYKZTsoP;d4)8 zCdZ3c>phTV;$~1#+rYq(1Z1pBSU=# z?Ty91KOYN!oH&1M-}3K!tN+iwf5Z5t-aqFA#si`Wm6erqYF<9E__jK}Prd%nlZw~! z`M$QD-xW46B(&}Qw*C6C&HHQ1-qcs!xA=MK^OCP=xcYc&i=OMXX{SW>S@KX zR?m}k_x?S<{dn>5`O@tCvJ&m}brr8HV_1OxH-2(;|2`|bzc<$=XJ%%;(u`q>7i2oX z6B!ltYky(s-}ZI#{k5+*e%yUuM&ABzeZtx8AOBT01GT+7xZc*H?A?z1oZZE@Z)`|B zERo!^_`J;ADzW#DK!@|}dcLmu81wFZR@>_9e((5r*xb*yo! zm7bn4`?KJosa|05io?e?er)RvKL0@I=R|8-rT2BSs~FBq0tU@W^%${prKvlk&a++@ z0*1u&gP%TqviN)KW#PX+KQ}&Ajy|@l^!2iNN2EUKJ27<3{rP)l;r;NkzW)2aHhw(0 zcD_V;ZQ}2Wr~2!CY`aZWHZUlB+xy<$uJ7-!8@ucGe|W_G*!uq8Z%bxJL<3Vml6qbJ zqo!*2y26w>*5!GVY}eQVE!;TUzUnjU_q_X6zat_dW~A_E=jWvsmX^K}45-LB|8C>= zm3zhS{6F~EUS{|HebtkE4{UvTMc%Jh-@f2~#pg4#lY0!Wt&g|odt-0$Ztw4VZzSF4 zTUwR9`*rF69qSL1o0G5q-MuNj;A4#|2g62dyW-;&AAbrzermtZCM+!MSnl?FW_h=Y zf`9eM#@{gv6WgmiGZg4hz2>vC&H91Z-?q8)2QQTh9dKc`T#k{U!1N;k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WbePZ!6KiaBrZ8v02Gia1=9>^|15C~Do6qB3D&f~?2_5l)RrK~bG! zbLT4gs;p8Gx09T&{P$Sz{nzXEnHYbrE4T1rU`PpD%+A0NA=SvrARr91-f)ybav&T{ zniHS5>}pkAsoXyM9y#6yW#tVF3`tDf3<_!l80x^#Pmm0IUHOG0|IyEvslQ6ftg3qM zd`q&vd-q{35*O!Si~3{e9#!~&woA3idy@ezj|H%x;LroA3o3j&QvFeI|@H< zO#gAVuDoLV|ERr>+Y8@*egB5_52rb94YW%R){?diH7cvW+ElfeC$9g0I_P^-@*_5^ z!DaToy#4SC#RUa*J7>z(+wA-G_x1Gr%g;Wio$x8}nfB|;xxBCJB5=qK?U@z(eQBFR@f$Q-HzK)z4*}Q$iB}SMT0^ literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-ordered-list-item-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..b4042d8bf491ab81ebb11d85069bb6dfe261d56f GIT binary patch literal 2117 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WbePZ!6KiaBrZ8v02Gia1=9>^|15C~Do6qB3D&f~?2_5l)RrK~bG! zbLT4gs;p8Gx09T&{P$Sz{nzXEnHYbrE4T1rU`PpD%+A0NA=SvrARr91-f)ybav&T{ zniHS5>}pkAsoXyM9y#6yW#tVF3`tDf3<_!l80x^#Pmm0IUHOG0|IyEvslQ6ftg3qM zd`q&vd-q{35*O!Si~3{e9#!~&woA3idy@ezj|H%x;LroA3o3j&QvFeI|@H< zO#gAVuDoLV|ERr>+Y8@*egB5_52rb94YW%R){?diH7cvW+ElfeC$9g0I_P^-@*_5^ z!DaToy#4SC#RUa*J7>z(+wA-G_x1Gr%g;Wio$x8}nfB|;xxBCJB5=qK?U@z(eQBFR@f$Q-HzK)z4*}Q$iB}SMT0^ literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-android.png new file mode 100644 index 0000000000000000000000000000000000000000..d2e21accd45851c8adeed0f47040baeb7c809c8c GIT binary patch literal 3427 zcmeHKYdDl?AAe@Th@CB|HY7x(134|xMCGt522BlyMmbE*nQ>}{kV?CuB1D;N)**+X ziCOG8ZSANTH4|Z3Mwu9tnM#eu@x4dAA9t_!!~0(Em!2=rbKlSZ{QLdy>wo{~=IVr0 zQdfc?2#&T8I`A?SCx^^o0-HaMiH3X(VhSBA6? z`@x{|D|bN2yS@tjQwhSzQ!r@geJbbd9Bt|Bbog*Yg7Pj;pZ5PoElc}0LA*L7I(#b! zyR#A{Sn~cgDAB7aVB*9=CaFi@!_40Pk8rj9HAtWBHQl|p=TYCz%E%Zap?Wz9x#s@` z=yE#xSf4Xz8b{ciQ0^rXjo(!xRM(^Z4cE{r@D+rKtdc-tLWLyVN;tW5fo#MdQTorw z%FAOh#Hc%Bn4aQ}B5`{7FpkzLNtu~n{Mffwo1RQt<7tPVc-X_Jrtqf;jPN1d&wImG z*Bd5Rd)Zm8+)-q_(bXsB(vBjW+MOUz<$Lxm5Y>HF-TC zZLmd9aUcL@2n2QyeclD0%c9L>dT;ju34*x_R=H2CIn6mtGQV@9ymFbv7nkgnrE9>&Xi1w29EIida3BLK;2 zU=s#xl4iou*H<8Bg(e79RRp^^F1?out4nT!SHuT*XTHDE+TuoW9Q0phi)VxlxKk9| z^1-hLp=6QSWoBVx}>FmGa(;F|NSfGN+b>S^XuN4sUM+a|`OdMYxe*|Na(f^?@M(}SpsIfVUE@@}0h9wM;%?Ky3Pqpl z%rrsAr0v@Rl#w3M-*_B`BUC-@iWLm46(*|M+Cg09nk&|7rbZuo;RgVF&Zz!Pru7s? zwpnWhPH%M!%*7D&sdS-V`wKws;xR4TGRb*XXQAb961gJK_U0j1@A!+NQb}J7+>=>b zoBi2zBMD((?bCj)ecAu>byYCy?We)XXFmDrh_H{!yOE7vI6bw}Zi~#dLGCTjAa1@p z#j)>60jl_xcko7I9`Ps^{t+NGxC0@k}JB5wuKMr)F$^fiABg!zKzo%+~PqbGKctbi|!}~kMjnd zPXuVzKeM>WsfmdHh1nRH)T3!ea52XEk|MmCPVVc`w2K7!z0L^#3nyw0^_9*&;O0Dj z9JCaGT(v6a{@$dJ#ur)FOyl@(9Pt|CzTiAls|oA(5;Z2)?U8a_GMM}cf)S`Z<4(e> zhyLnkP(?|g{KBw#E`uocea$|CZh4-wl=S$~IpWI_UDUf^$D%^O^ZTqB*Xy9sjEY0Q z&na`l8_i|D-CHe#1D%mjmtN(K$-N!rk&hxcCT!p32^1`QtA{+ONs@XxsUyC>ema9r zm}#Mt!kBN!3mK;7=9tXgu|fGZk+J!t51qu$L9al5)P0U&nI=BB+Q*Y(%_A&NKn9H~ qz79QK=f1xgN50O-|Env)o7M_reu1i0$_8*zf}9;(?P>e{&ixC{Z@-)X literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f985c152ca30326c3802d10b72f8c41b1ec12d GIT binary patch literal 2054 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|Wa8PZ!6KiaBp@8)iudia1;hypU(&IrXeWbnEFgCzf(srx>rxN!5L2 zqIpR|n5j{^HoBP4$JpRQTzRFz?8}9bY77iU^WFFv7z#33#26fW5*Qhdj50_Kgl&u2 z8Ggj?i>>|t`1fx4>*@1ZBgDmZ7#e0AU}EUtB%5)g>`>Q?{?ph*}%Y%#Kg^@ zphhLeHcQ#*sb2&b7^WX#rJ}Laa^9#pqme@01V5Uh21kn8&(LtUUSjtNZzEt`&fw|l K=d#Wzp$P!%+_Jd< literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..38c198d71204d71c0753baae565a6d1dc95e553c GIT binary patch literal 2051 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|Wb7PZ!6KiaBp@8+tJZia1>K^mrWAv+~aRR=wk=RI+F09Saf^WWHWw z@Z4cmW8?Q(1(gP~Q{%34GBlhi`vNrXfA0Y%h7L|0hK3oV3=#w3MjkK(m%hFG?Dq51 zd*W)v9+d4&U}QMLBF5n0Lq4NyF+0PKZI^$V$jZsP^?zKv{rclA`|rnp>Dzm3qj)@i z8)`oN`C_E|xoYbD&zHORz9|i^zAMzo${-*NOjCw*WNbWZwEp!AMFy?gyU%VvKmF(9 z_3ujTeA|z=Yx|Y%B+5fIt{i`~_y6n?c(5M7m&pk49P=uMhD%247rQeQ9AsixF`D{E li-6JMZ!{OsJQv)se!ZnPfNkO~ePGSY;OXk;vd$@?2>^KVSup?r literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-macOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-macOS.png new file mode 100644 index 0000000000000000000000000000000000000000..38c198d71204d71c0753baae565a6d1dc95e553c GIT binary patch literal 2051 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|Wb7PZ!6KiaBp@8+tJZia1>K^mrWAv+~aRR=wk=RI+F09Saf^WWHWw z@Z4cmW8?Q(1(gP~Q{%34GBlhi`vNrXfA0Y%h7L|0hK3oV3=#w3MjkK(m%hFG?Dq51 zd*W)v9+d4&U}QMLBF5n0Lq4NyF+0PKZI^$V$jZsP^?zKv{rclA`|rnp>Dzm3qj)@i z8)`oN`C_E|xoYbD&zHORz9|i^zAMzo${-*NOjCw*WNbWZwEp!AMFy?gyU%VvKmF(9 z_3ujTeA|z=Yx|Y%B+5fIt{i`~_y6n?c(5M7m&pk49P=uMhD%247rQeQ9AsixF`D{E li-6JMZ!{OsJQv)se!ZnPfNkO~ePGSY;OXk;vd$@?2>^KVSup?r literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-paragraph-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..38c198d71204d71c0753baae565a6d1dc95e553c GIT binary patch literal 2051 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|Wb7PZ!6KiaBp@8+tJZia1>K^mrWAv+~aRR=wk=RI+F09Saf^WWHWw z@Z4cmW8?Q(1(gP~Q{%34GBlhi`vNrXfA0Y%h7L|0hK3oV3=#w3MjkK(m%hFG?Dq51 zd*W)v9+d4&U}QMLBF5n0Lq4NyF+0PKZI^$V$jZsP^?zKv{rclA`|rnp>Dzm3qj)@i z8)`oN`C_E|xoYbD&zHORz9|i^zAMzo${-*NOjCw*WNbWZwEp!AMFy?gyU%VvKmF(9 z_3ujTeA|z=Yx|Y%B+5fIt{i`~_y6n?c(5M7m&pk49P=uMhD%247rQeQ9AsixF`D{E li-6JMZ!{OsJQv)se!ZnPfNkO~ePGSY;OXk;vd$@?2>^KVSup?r literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-android.png new file mode 100644 index 0000000000000000000000000000000000000000..b250555fc2cae82add436c53279438fb2a1d9538 GIT binary patch literal 3780 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 literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-task-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..2d6219ccbf46dde0940dd0c49e028f4a9d207dc7 GIT binary patch literal 2422 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 Sv8&*lE7<4e?MijVUi>GQXyEMt literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-iOS.png new file mode 100644 index 0000000000000000000000000000000000000000..ec432fbebfbd1ce901d2786455e739fc64380773 GIT binary patch literal 2441 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|TeAr;B4q#hkZycX!96$}~L87x#<^dQ(5aR3Pw!P@uDvqhglk4Xq}D z{1p-hgAQJD;^yX7?G;}n*0rFw>xP8lMI~|F5S=?Fx0ZQaYKi2QR#lk%_3jzZPc{X! zjOEX~e;v0k@m%Drx%Y2KFfgP{6SrkxIB?Z1fsx?|ix`80&nSb$KzOsum7gK{;fICE zk58EIk1hE>@%Z=N-QVBGo$z6Zv7SSf@Vq{!S>MinTKF`zYT25PmA^NwZ;hXQSNwx{ zBP)Y|Ffer)VlXzo7PDpWIr-+#vDDw|o=#nQ`djR`um5j~7ibc4k_|gIgW9X_U+*8^ zDBss>|L;%oo4v*Fcf9V~eeB!Y+v2uj#R3rw28OSfUq2pv-p+F0uV1U{@5aZ*%C6u0 z>zBoc8;u*cJW<>j{7l`6VMfQz9J6~BpL+{W%bLfFgeUhDK07n>;}c{5p6d7e)7Q+8 zi$BqG#yx@YfJ@Adg2X+)|5-nNb93|mo$qeH-zR(AUv7U+ap6l~y7W1+e15H!-#jbJ z>$j%IS7x4mp7E{iVOhErX zy4Wr+yL;Vz)9bgI&+pyv6e^r_yifLV^LIO$+uL$~pD*RN{c)kdI;$J71$``F)AKUj1=EIj;pbpAeDyMHexx1KrnXO9*;gIZed^IJb2ebztz>*d$N zFTWSxbFE+%0)}W{w)OR6x6W;K?LVmYqf>xEL2UyALlV$22*$JcmJj@=wY|IL61{I+ zpZYZGu6T?$j`*CG_T1b|%$DT}pS+b>U(M&+uHWS=Ywqu;{{OD^49kJ7hdHU8-C;HY u%abEiH+-~k8ZDtmYp2m%fHfEVV+wse)7+`58Q6ScVDNPHb6Mw<&;$U;(!O2* literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..3e2afda0c436379805d27427aa47eeca530a5f3c GIT binary patch literal 2429 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|TeJr;B4q#hkZy_jbpG$}~JQ?pD#!{APbbDnR5Vw<$eC6GyT}9S2>%me-u7{V|uOb-&^nYHSFimVQ83ffQg}l6N7Pg z%Nxc7FJSbpySXeYS^9Ce{{A1wpJ!)hKVB*xFXt|2 zZDlL>6;GdNKR#FeeuvV!V-k=5{QR8!c%}L=X8ybOX6JMDQr*Oq zH!vh9>BaA}xmWrAZQ;X1t@W|ruU?PueSf~D{_L|yuYg{g(GfgrT z&YRtp_nor)?f<^%I}a7Mt^U^Y{r=yCH@CLFj=wHj_vAyxr%v_tH)CRKftg0Q=*5GD zA5Zu1?`xN@`O)?M+O=!PR;$NL%h&$+7?hS=_)m+SK`pKJ{jVQyE}uXCZ0Fg+U$-y+ z>;6#h0dn=)_p$P?tFKSDWxM@AP!U`7oLSHDFdrC~r{xmeZ(RR>`s*(J9TP1MVvYW@ zpo~4umPG`VB=h%tJtjJTf7Pe08w*pCHH{eyl8c(CoXI!d{B!BrwN32|3_IrGa1&-C t9TmYEd85HH8Z5ZV_0hC}o;GgGKb<+P>OoWYd|(rb!PC{xWt~$(697M~3u6EP literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-macOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-macOS.png new file mode 100644 index 0000000000000000000000000000000000000000..3e2afda0c436379805d27427aa47eeca530a5f3c GIT binary patch literal 2429 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|TeJr;B4q#hkZy_jbpG$}~JQ?pD#!{APbbDnR5Vw<$eC6GyT}9S2>%me-u7{V|uOb-&^nYHSFimVQ83ffQg}l6N7Pg z%Nxc7FJSbpySXeYS^9Ce{{A1wpJ!)hKVB*xFXt|2 zZDlL>6;GdNKR#FeeuvV!V-k=5{QR8!c%}L=X8ybOX6JMDQr*Oq zH!vh9>BaA}xmWrAZQ;X1t@W|ruU?PueSf~D{_L|yuYg{g(GfgrT z&YRtp_nor)?f<^%I}a7Mt^U^Y{r=yCH@CLFj=wHj_vAyxr%v_tH)CRKftg0Q=*5GD zA5Zu1?`xN@`O)?M+O=!PR;$NL%h&$+7?hS=_)m+SK`pKJ{jVQyE}uXCZ0Fg+U$-y+ z>;6#h0dn=)_p$P?tFKSDWxM@AP!U`7oLSHDFdrC~r{xmeZ(RR>`s*(J9TP1MVvYW@ zpo~4umPG`VB=h%tJtjJTf7Pe08w*pCHH{eyl8c(CoXI!d{B!BrwN32|3_IrGa1&-C t9TmYEd85HH8Z5ZV_0hC}o;GgGKb<+P>OoWYd|(rb!PC{xWt~$(697M~3u6EP literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-at-leftmost-character-unordered-list-item-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..3e2afda0c436379805d27427aa47eeca530a5f3c GIT binary patch literal 2429 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1V4T3g1{5hWm74*i7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|TeJr;B4q#hkZy_jbpG$}~JQ?pD#!{APbbDnR5Vw<$eC6GyT}9S2>%me-u7{V|uOb-&^nYHSFimVQ83ffQg}l6N7Pg z%Nxc7FJSbpySXeYS^9Ce{{A1wpJ!)hKVB*xFXt|2 zZDlL>6;GdNKR#FeeuvV!V-k=5{QR8!c%}L=X8ybOX6JMDQr*Oq zH!vh9>BaA}xmWrAZQ;X1t@W|ruU?PueSf~D{_L|yuYg{g(GfgrT z&YRtp_nor)?f<^%I}a7Mt^U^Y{r=yCH@CLFj=wHj_vAyxr%v_tH)CRKftg0Q=*5GD zA5Zu1?`xN@`O)?M+O=!PR;$NL%h&$+7?hS=_)m+SK`pKJ{jVQyE}uXCZ0Fg+U$-y+ z>;6#h0dn=)_p$P?tFKSDWxM@AP!U`7oLSHDFdrC~r{xmeZ(RR>`s*(J9TP1MVvYW@ zpo~4umPG`VB=h%tJtjJTf7Pe08w*pCHH{eyl8c(CoXI!d{B!BrwN32|3_IrGa1&-C t9TmYEd85HH8Z5ZV_0hC}o;GgGKb<+P>OoWYd|(rb!PC{xWt~$(697M~3u6EP literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-android.png deleted file mode 100644 index c9c526bbbd4f59cff5ce207ccc557a8a18f57e31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3637 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 pjj9|nk44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOzPZ!6KiaBrZ8ukUJOSA>XpY9cB7VYkIk<9E+Fb+OrrQx6{6fjB2 z@l?`^1jBc4M5YLO9QpRSx^BMm&VBb+Yj?SaOHKRxt{{RDX!f-k;k8kGwA-4}rHC7|&A!xRkzabdt zLSP8B#>O;(yw`tv&F%-YelX$mA1E(K)Uq%fIFt7Gb?kh*x;Wd~Zz*3S)ysc>D*O8E z>+Or_KBvWr$rbt~FmLNUJ@MbW>vwE^%~f_b>)Q8JV3g0z z7yr;oAbaPy$sdsTeOIyiTGj59^P65*JY5%g_Se2QR`X(y&n^{@5sr5lB!$H6i|hu+ zs&@C-J=A&gNazgEja4yknDnOVD_}{<;5bOT`JS)+qB}5+Uq47x)KXA1R<^|L*%i5V z`|0|+_jUE=zf2eyeyAaG7Io5u&y(fzhmu zcbf4p28*R%%Oqfzl87%I?uR kd9*z;+CUp^oPO4Ki@cYdkzaTXSa&gay85}Sb4q9e0M$-w4gdfE diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-ordered-list-item-windows.png deleted file mode 100644 index 67045eb87506f0a02fd00f3f7d438be64c440a66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3156 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOzPZ!6KiaBrZ8ukUJOSA>XpY9cB7VYkIk<9E+Fb+OrrQx6{6fjB2 z@l?`^1jBc4M5YLO9QpRSx^BMm&VBb+Yj?SaOHKRxt{{RDX!f-k;k8kGwA-4}rHC7|&A!xRkzabdt zLSP8B#>O;(yw`tv&F%-YelX$mA1E(K)Uq%fIFt7Gb?kh*x;Wd~Zz*3S)ysc>D*O8E z>+Or_KBvWr$rbt~FmLNUJ@MbW>vwE^%~f_b>)Q8JV3g0z z7yr;oAbaPy$sdsTeOIyiTGj59^P65*JY5%g_Se2QR`X(y&n^{@5sr5lB!$H6i|hu+ zs&@C-J=A&gNazgEja4yknDnOVD_}{<;5bOT`JS)+qB}5+Uq47x)KXA1R<^|L*%i5V z`|0|+_jUE=zf2eyeyAaG7Io5u&y(fzhmu zcbf4p28*R%%Oqfzl87%I?uR kd9*z;+CUp^oPO4Ki@cYdkzaTXSa&gay85}Sb4q9e0M$-w4gdfE diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-android.png deleted file mode 100644 index ca9587c6169a6b676d63c114de4e131b5d4a00e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3137 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WPKPZ!6KiaBrZ*k(mXGPFHhy&&yc(#@&n!fKj9X|9Juw3ZmW)j2X% zcfIS}cOsgHSdu5y-^$Gdl-Oh zBPJFGh7%kDKuP6M!J!l&uP?GQSX_Vj(ctc#xPH51o8P^cTXy#9vo+~&q@Jtpo|d)p z{poduZyu%R5scP|DR;~4=k^tUx|#dE>}F};v|sz)YL^#wD-`{#uG{&&Nb*qY~TgfjF4r?=aFzbu(E z`F!5)?WfmuZvuwg^w_eGVgnGdSp5u(?e_#@n~;xxi0S|fw#Nj6grd@Lvs^91gRlGQ z{(O7#z5eg#^g1zyhUYsNsalqR?f6^sBiZy9$ND0xbN+Sp6*fNR+pqtuJXcu%-5__J zS-Nd9F#YS^54t}+_VJJ3RhER)xz4G)-Shp9TmF2rt$O#HT`Qou=>68$lLkwTaHa}a zW8J&-Tz~$u^YM1QcE@IKe*JyZ6^Kj4_iN$yBrFxJ|NQmwcJ=-Lf8W3VJKV|7{l5+4 zflE}09#9E<@BB-Pemf?H1OKrac?MYX{?!i!Cc=lbO@yPBHv?5O+-P`?h9{wll%iS# dYXbW$zrB!CnPpbWTwrU9!PC{xWt~$(697g*X7~UA diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-iOS.png deleted file mode 100644 index ca9587c6169a6b676d63c114de4e131b5d4a00e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3137 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WPKPZ!6KiaBrZ*k(mXGPFHhy&&yc(#@&n!fKj9X|9Juw3ZmW)j2X% zcfIS}cOsgHSdu5y-^$Gdl-Oh zBPJFGh7%kDKuP6M!J!l&uP?GQSX_Vj(ctc#xPH51o8P^cTXy#9vo+~&q@Jtpo|d)p z{poduZyu%R5scP|DR;~4=k^tUx|#dE>}F};v|sz)YL^#wD-`{#uG{&&Nb*qY~TgfjF4r?=aFzbu(E z`F!5)?WfmuZvuwg^w_eGVgnGdSp5u(?e_#@n~;xxi0S|fw#Nj6grd@Lvs^91gRlGQ z{(O7#z5eg#^g1zyhUYsNsalqR?f6^sBiZy9$ND0xbN+Sp6*fNR+pqtuJXcu%-5__J zS-Nd9F#YS^54t}+_VJJ3RhER)xz4G)-Shp9TmF2rt$O#HT`Qou=>68$lLkwTaHa}a zW8J&-Tz~$u^YM1QcE@IKe*JyZ6^Kj4_iN$yBrFxJ|NQmwcJ=-Lf8W3VJKV|7{l5+4 zflE}09#9E<@BB-Pemf?H1OKrac?MYX{?!i!Cc=lbO@yPBHv?5O+-P`?h9{wll%iS# dYXbW$zrB!CnPpbWTwrU9!PC{xWt~$(697g*X7~UA diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-linux.png deleted file mode 100644 index 5b016a5c602cf323a3b08c31cc33ee299e0105dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3130 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOHPZ!6KiaBrZ9L!RRWN5o+lrlNJbJwTZ*crzQMbB>Du{rHxciS@c zo=G+3LGzgNm{`9r_jvGFDE#_q8-Jj2jms~x11Y(721W)3BPJFGh7%kD3=9g&4h#$p zJ)?p{DL`!c?HCTMJtzM>bNB5YyJO4m^)Ju-yKCP|$=%0xre6=z{aSm|v@iD0NnQ>H zh6#cS3=9r#4ZygUWMpDsNMS)1>@K>o-}n0IIW>I0&3C`4((ta0FT44*{>E1i6lGZT z(nm0@bnltZFPs_fJo-~OZ`vK8gzep?Bg3p=ikBM zNuWdTH?ab}jz+Sx*HCT#+%|ORh zeo<_AU--|$|MuOqFNSts>uh{qU;pZR{)Xu}+uhT$R=z*IuJFyT+?Bh39L1f!Kk7yR zQ~fdA3h?F%Sl|=L13;%THvG2PyXN1o&#zO@oo8bB5Wka=s>SPPn_mnEu2)q>_Szkq zzBBIG{(pbK*}%7MUi2P$|I5EhYcCs@^(J5c1j+RG*Z(H$f`mZ6-`ihS&76Eb@qgO% zSpKb$bo487w_N97sR-7rn_ekb%kiMhUw`#Q_a9}~Pv8Gu<1FirZBIDJ|Mdi@9QYHH=RkR#2P~AB8RB18&2*P% zWO#rh-s_#(V1l diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-macOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-macOS.png deleted file mode 100644 index 5b016a5c602cf323a3b08c31cc33ee299e0105dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3130 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOHPZ!6KiaBrZ9L!RRWN5o+lrlNJbJwTZ*crzQMbB>Du{rHxciS@c zo=G+3LGzgNm{`9r_jvGFDE#_q8-Jj2jms~x11Y(721W)3BPJFGh7%kD3=9g&4h#$p zJ)?p{DL`!c?HCTMJtzM>bNB5YyJO4m^)Ju-yKCP|$=%0xre6=z{aSm|v@iD0NnQ>H zh6#cS3=9r#4ZygUWMpDsNMS)1>@K>o-}n0IIW>I0&3C`4((ta0FT44*{>E1i6lGZT z(nm0@bnltZFPs_fJo-~OZ`vK8gzep?Bg3p=ikBM zNuWdTH?ab}jz+Sx*HCT#+%|ORh zeo<_AU--|$|MuOqFNSts>uh{qU;pZR{)Xu}+uhT$R=z*IuJFyT+?Bh39L1f!Kk7yR zQ~fdA3h?F%Sl|=L13;%THvG2PyXN1o&#zO@oo8bB5Wka=s>SPPn_mnEu2)q>_Szkq zzBBIG{(pbK*}%7MUi2P$|I5EhYcCs@^(J5c1j+RG*Z(H$f`mZ6-`ihS&76Eb@qgO% zSpKb$bo487w_N97sR-7rn_ekb%kiMhUw`#Q_a9}~Pv8Gu<1FirZBIDJ|Mdi@9QYHH=RkR#2P~AB8RB18&2*P% zWO#rh-s_#(V1l diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-windows.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-paragraph-windows.png deleted file mode 100644 index 5b016a5c602cf323a3b08c31cc33ee299e0105dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3130 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`0|WOHPZ!6KiaBrZ9L!RRWN5o+lrlNJbJwTZ*crzQMbB>Du{rHxciS@c zo=G+3LGzgNm{`9r_jvGFDE#_q8-Jj2jms~x11Y(721W)3BPJFGh7%kD3=9g&4h#$p zJ)?p{DL`!c?HCTMJtzM>bNB5YyJO4m^)Ju-yKCP|$=%0xre6=z{aSm|v@iD0NnQ>H zh6#cS3=9r#4ZygUWMpDsNMS)1>@K>o-}n0IIW>I0&3C`4((ta0FT44*{>E1i6lGZT z(nm0@bnltZFPs_fJo-~OZ`vK8gzep?Bg3p=ikBM zNuWdTH?ab}jz+Sx*HCT#+%|ORh zeo<_AU--|$|MuOqFNSts>uh{qU;pZR{)Xu}+uhT$R=z*IuJFyT+?Bh39L1f!Kk7yR zQ~fdA3h?F%Sl|=L13;%THvG2PyXN1o&#zO@oo8bB5Wka=s>SPPn_mnEu2)q>_Szkq zzBBIG{(pbK*}%7MUi2P$|I5EhYcCs@^(J5c1j+RG*Z(H$f`mZ6-`ihS&76Eb@qgO% zSpKb$bo487w_N97sR-7rn_ekb%kiMhUw`#Q_a9}~Pv8Gu<1FirZBIDJ|Mdi@9QYHH=RkR#2P~AB8RB18&2*P% zWO#rh-s_#(V1l diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-android.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-task-android.png deleted file mode 100644 index 471708079f0204ddeabd5a7ea43d0acaa98a2df8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3614 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 BUjqOD diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-iOS.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-iOS.png deleted file mode 100644 index 82d3db2fd076664bddac95d7ab19eab84456864c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3550 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`P*aDei(^Q|oVRz+cF#?hJn-4muw zTuyJA^P%edHYpz;OB!uHdalS4=nSUME7^fmA0HzV149Z62LrwZ4dsh+;~+xk1%v1?yf-pv00hP6f{4^2H* zLw;B*Ffhniz0cWiyzbxIS33$nAG7%V;b!5pg~pG6e0&_-e^d{okFWP_`S#V7@)HUHZmuUv0unr2+@QZ)6as15@|*uA8U68bSUi8Zel`ri|iPwx46 zuljv&`o4c(e*6))H{W*u?z|MYGWP}sh7F(WemrRY_*uN(Ebs2F((BLTe!tFs{M9`E z{If@kg@MjmvuAq!_fHj{<@4XYy7|85`)zhR{%UoguOce*_iX$f-Mjr>UG|>;|9-Pu z!NkMY#oqk7x%+5R%f8EoQh5wJgyMJ|a^!fVF2c9Rs2W2(w_xpaolivU9==$RulaIgqb?0$>RgKN} zFU2=wYD$3d)ZqLOX#C^1_5Z4EzO^3D`+c)hSp8VPUYyka%9qp}7Z_y>3~#Qj zoV@&)eBJ-!8~?uhwxj6nDa>F$4hibwcd<({b&mcfDhE@07-tbig`|qu=OE59S9ZG8tZ?(h8D&h*UUmf+@YZu@?5v zW^bPQHz(F2f4%aC>${?^f8I2G@0-$sYDRe>U=qSpFwX2<$(|sEBl~Qvf4xgwMs?5x zi*zvm0TZBCSGrpWd`l8t1I*;F3pR>xSaqiXn4qv0{4;MRrSF_m1x&XOG76Y!T3y_n zQN{4>`KSK%ef#%R{oVF{{*PNbKb}~;Tzw`d1H-!`yi_igi%!J;KmYar@4uVR+gk$z z#rgNnKbM|bzuQy#@XwRaci(OX((}$;iu)GTy7$`EUGt{z<+~ntpq)T%nU?&D@j+bF z8=cMNXU)@Z?tZhi{@WuzP;tDq{$K6EgES69LHwx|DT+a%y<<8oF<8Z1D_vi@b=~t+ z{e;k+j5wN%f`ZNT{Rf$j?sw2(x4oAiS|rAV^5TImc`GKyTCGo!PC{xWt~$(698R1 BUjqOD diff --git a/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-linux.png b/super_editor/test_goldens/editor/goldens/super-editor-rtl-caret-unordered-list-item-linux.png deleted file mode 100644 index 5c14f06d05492a7063b658245f0165932a5c1926..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3508 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYVEn?t1{68!;8_8r7>k44ofy`glX(f`uoOFa zhHwBu4M$1`P*a?zi(^Q|oVR!Pubx{jd*I`H%NQX($xresVme|XQ_U0?^W6%3p*F2a zU=~YoQELxZAD>mpiwp8UHiubju*PoLA*$Hx6dfD2{GQ&+TMKu3TO3!M+|n|;y7>P2 zozM5heb70{eE#|2={))Srr)_)RK8UT=!C|fSD1m+@pcAA1_mQ076yhB90Cjs3d#-) z3=KV_f=4=mnt zCpPu|Ed8{b+uxKHJdK;36@3159DBO{gTx(-L?n^N-y0Yh3ZB&L`TVis`~3HP(ev*~ zJ)du7f6lftZ;nl6(W{L&m>HQE4ouwm`Q*7BHD51P{C=PR|Ni{>cC}T1-YmD57vEoB zcqXl`^NH1G

6bHG7{HS;p18Jo<5}`u-aKnjXVr>GNyN^!`5h`SEOiee#={O~xmC z-t+=fYQo#QJB!o*Jo_5of8PH8pT_rlKlhg(tCrt0{p``h!U`a>w*UKc?Z<=r@^4<= zmH+?5z1@!gw>mJPM^wMxzyEu5FTee-f;;xj^*jef7y7HYdeY5}X zs$B7TLp?BuG;IBTWcs`tpLc)$SrcPX{}7=%JO1yldG~AobocikO)5kvzw>>bbbsx` zjkbGt|2@RdUn}>`9~ep-zWL3yGOhc6+rCFw-OuL3-t>K+&+Pd0XKId~+&rLv9(e31 z_;={X3;X}F{`Ie4R{wkV<>lqaPp8L8*4KRYpM2)ny|8t8;r|z5KYkO$n2JzPkpd{dadf7YH{<8CTXwgvufO?P;?35TW?%QbN!72Gy%%>NmO$CK+4L)ePx8*) zr}mk~pV^du^Yw#M(9FkoE{=Ws%IbrKkJtuDX##O8TKsnJZ76+;+l6PQ?OuKLiZd{E z9B;;*2&hwSST^a!?==BNh3)y(`zuOcU1VT*(1@N!u_jy+1UEUZ+fz_^MtpN`#l4Ep zy}#e>to?iI$!FUaH}}=$#y?NjuU>l?TKu_Z>p##VVD!ydRSXqVfPwTT`twnp-_!O2 zqy5f#P~ppWZoj^T%%I5<(ZT!-{g-MCXIHg2C$3+d8vOlM$n2`PZ%o&w;*M8n=013M zC#cANJAJ*c?XiVbf5Z1npRfCVbLDYh1PK;1V=GDs1RGMJ16H|hnOuYWb5J2ST`z7{ z6%PZ$4t;Y4c)^4)ZKiH{`0ADI!1UP1j#UA=$<$6iRPgMmld#3?Xv8%DQ#JOA8Ik@+ gLy5Fdn)#nm)k|+jSWWB{VCXVk44ofy`glX(f`uoOFa zhHwBu4M$1`P*a?zi(^Q|oVR!Pubx{jd*I`H%NQX($xresVme|XQ_U0?^W6%3p*F2a zU=~YoQELxZAD>mpiwp8UHiubju*PoLA*$Hx6dfD2{GQ&+TMKu3TO3!M+|n|;y7>P2 zozM5heb70{eE#|2={))Srr)_)RK8UT=!C|fSD1m+@pcAA1_mQ076yhB90Cjs3d#-) z3=KV_f=4=mnt zCpPu|Ed8{b+uxKHJdK;36@3159DBO{gTx(-L?n^N-y0Yh3ZB&L`TVis`~3HP(ev*~ zJ)du7f6lftZ;nl6(W{L&m>HQE4ouwm`Q*7BHD51P{C=PR|Ni{>cC}T1-YmD57vEoB zcqXl`^NH1G

6bHG7{HS;p18Jo<5}`u-aKnjXVr>GNyN^!`5h`SEOiee#={O~xmC z-t+=fYQo#QJB!o*Jo_5of8PH8pT_rlKlhg(tCrt0{p``h!U`a>w*UKc?Z<=r@^4<= zmH+?5z1@!gw>mJPM^wMxzyEu5FTee-f;;xj^*jef7y7HYdeY5}X zs$B7TLp?BuG;IBTWcs`tpLc)$SrcPX{}7=%JO1yldG~AobocikO)5kvzw>>bbbsx` zjkbGt|2@RdUn}>`9~ep-zWL3yGOhc6+rCFw-OuL3-t>K+&+Pd0XKId~+&rLv9(e31 z_;={X3;X}F{`Ie4R{wkV<>lqaPp8L8*4KRYpM2)ny|8t8;r|z5KYkO$n2JzPkpd{dadf7YH{<8CTXwgvufO?P;?35TW?%QbN!72Gy%%>NmO$CK+4L)ePx8*) zr}mk~pV^du^Yw#M(9FkoE{=Ws%IbrKkJtuDX##O8TKsnJZ76+;+l6PQ?OuKLiZd{E z9B;;*2&hwSST^a!?==BNh3)y(`zuOcU1VT*(1@N!u_jy+1UEUZ+fz_^MtpN`#l4Ep zy}#e>to?iI$!FUaH}}=$#y?NjuU>l?TKu_Z>p##VVD!ydRSXqVfPwTT`twnp-_!O2 zqy5f#P~ppWZoj^T%%I5<(ZT!-{g-MCXIHg2C$3+d8vOlM$n2`PZ%o&w;*M8n=013M zC#cANJAJ*c?XiVbf5Z1npRfCVbLDYh1PK;1V=GDs1RGMJ16H|hnOuYWb5J2ST`z7{ z6%PZ$4t;Y4c)^4)ZKiH{`0ADI!1UP1j#UA=$<$6iRPgMmld#3?Xv8%DQ#JOA8Ik@+ gLy5Fdn)#nm)k|+jSWWB{VCXVk44ofy`glX(f`uoOFa zhHwBu4M$1`P*a?zi(^Q|oVR!Pubx{jd*I`H%NQX($xresVme|XQ_U0?^W6%3p*F2a zU=~YoQELxZAD>mpiwp8UHiubju*PoLA*$Hx6dfD2{GQ&+TMKu3TO3!M+|n|;y7>P2 zozM5heb70{eE#|2={))Srr)_)RK8UT=!C|fSD1m+@pcAA1_mQ076yhB90Cjs3d#-) z3=KV_f=4=mnt zCpPu|Ed8{b+uxKHJdK;36@3159DBO{gTx(-L?n^N-y0Yh3ZB&L`TVis`~3HP(ev*~ zJ)du7f6lftZ;nl6(W{L&m>HQE4ouwm`Q*7BHD51P{C=PR|Ni{>cC}T1-YmD57vEoB zcqXl`^NH1G

6bHG7{HS;p18Jo<5}`u-aKnjXVr>GNyN^!`5h`SEOiee#={O~xmC z-t+=fYQo#QJB!o*Jo_5of8PH8pT_rlKlhg(tCrt0{p``h!U`a>w*UKc?Z<=r@^4<= zmH+?5z1@!gw>mJPM^wMxzyEu5FTee-f;;xj^*jef7y7HYdeY5}X zs$B7TLp?BuG;IBTWcs`tpLc)$SrcPX{}7=%JO1yldG~AobocikO)5kvzw>>bbbsx` zjkbGt|2@RdUn}>`9~ep-zWL3yGOhc6+rCFw-OuL3-t>K+&+Pd0XKId~+&rLv9(e31 z_;={X3;X}F{`Ie4R{wkV<>lqaPp8L8*4KRYpM2)ny|8t8;r|z5KYkO$n2JzPkpd{dadf7YH{<8CTXwgvufO?P;?35TW?%QbN!72Gy%%>NmO$CK+4L)ePx8*) zr}mk~pV^du^Yw#M(9FkoE{=Ws%IbrKkJtuDX##O8TKsnJZ76+;+l6PQ?OuKLiZd{E z9B;;*2&hwSST^a!?==BNh3)y(`zuOcU1VT*(1@N!u_jy+1UEUZ+fz_^MtpN`#l4Ep zy}#e>to?iI$!FUaH}}=$#y?NjuU>l?TKu_Z>p##VVD!ydRSXqVfPwTT`twnp-_!O2 zqy5f#P~ppWZoj^T%%I5<(ZT!-{g-MCXIHg2C$3+d8vOlM$n2`PZ%o&w;*M8n=013M zC#cANJAJ*c?XiVbf5Z1npRfCVbLDYh1PK;1V=GDs1RGMJ16H|hnOuYWb5J2ST`z7{ z6%PZ$4t;Y4c)^4)ZKiH{`0ADI!1UP1j#UA=$<$6iRPgMmld#3?Xv8%DQ#JOA8Ik@+ gLy5Fdn)#nm)k|+jSWWB{VCXV RTL mode >', () { testGoldensOnAllPlatforms( - 'inserts text and paints caret on the left side of paragraph', + 'inserts text and paints caret on the left side of paragraph for downstream position', (tester) async { await tester // .createDocument() @@ -25,19 +23,20 @@ void main() { // Place the caret at the beginning of the paragraph. await tester.placeCaretInParagraph('1', 0); - // Type the text "Example of text containing multiple lines.". + // Type the text "Example". await tester.ime.typeText( - 'مثال لنص يحتوي على عدة أسطر.', + 'مثال', getter: imeClientGetter, ); - await screenMatchesGolden(tester, 'super-editor-rtl-caret-paragraph-${defaultTargetPlatform.name}'); + await screenMatchesGolden( + tester, 'super-editor-rtl-caret-at-leftmost-character-paragraph-${defaultTargetPlatform.name}'); }, - windowSize: const Size(800, 500), + windowSize: goldenSizeSmall, ); testGoldensOnAllPlatforms( - 'inserts text and paints caret on the left side of unordered list item', + 'inserts text and paints caret on the left side of unordered list item for downstream position', (tester) async { await tester // .createDocument() @@ -54,19 +53,20 @@ void main() { // Place the caret at the beginning of the list item. await tester.placeCaretInParagraph('1', 0); - // Type the text "Example of text containing multiple lines.". + // Type the text "Example". await tester.ime.typeText( - 'مثال لنص يحتوي على عدة أسطر.', + 'مثال', getter: imeClientGetter, ); - await screenMatchesGolden(tester, 'super-editor-rtl-caret-unordered-list-item-${defaultTargetPlatform.name}'); + await screenMatchesGolden( + tester, 'super-editor-rtl-caret-at-leftmost-character-unordered-list-item-${defaultTargetPlatform.name}'); }, - windowSize: const Size(800, 500), + windowSize: goldenSizeSmall, ); testGoldensOnAllPlatforms( - 'inserts text and paints caret on the left side of ordered list item', + 'inserts text and paints caret on the left side of ordered list item for downstream position', (tester) async { await tester // .createDocument() @@ -83,19 +83,20 @@ void main() { // Place the caret at the beginning of the list item. await tester.placeCaretInParagraph('1', 0); - // Type the text "Example of text containing multiple lines.". + // Type the text "Example". await tester.ime.typeText( - 'مثال لنص يحتوي على عدة أسطر.', + 'مثال', getter: imeClientGetter, ); - await screenMatchesGolden(tester, 'super-editor-rtl-caret-ordered-list-item-${defaultTargetPlatform.name}'); + await screenMatchesGolden( + tester, 'super-editor-rtl-caret-at-leftmost-character-ordered-list-item-${defaultTargetPlatform.name}'); }, - windowSize: const Size(800, 500), + windowSize: goldenSizeSmall, ); testGoldensOnAllPlatforms( - 'inserts text and paints caret on the left side of task', + 'inserts text and paints caret on the left side of task for downstream position', (tester) async { await tester // .createDocument() @@ -112,15 +113,16 @@ void main() { // Place the caret at the beginning of the task. await tester.placeCaretInParagraph('1', 0); - // Type the text "Example of text containing multiple lines.". + // Type the text "Example". await tester.ime.typeText( - 'مثال لنص يحتوي على عدة أسطر.', + 'مثال', getter: imeClientGetter, ); - await screenMatchesGolden(tester, 'super-editor-rtl-caret-task-${defaultTargetPlatform.name}'); + await screenMatchesGolden( + tester, 'super-editor-rtl-caret-at-leftmost-character-task-${defaultTargetPlatform.name}'); }, - windowSize: const Size(800, 500), + windowSize: goldenSizeSmall, ); }); } diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-android.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-android.png deleted file mode 100644 index 862b982fded652ccf6d326bc3ff5568c6c1344c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3096 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^sKIG}*7*a9k?cIa9QjQF57js;=r##T|P+xa`X+W#@1F4hdX6`8`)s$*d z%BFws>o~mLGD2Rt*JiH$v!Cn?3^V6129i0w2bdT-ICU5rW{fgM17S2JjF?<-Bk$MW z>@RE;R+s--R2@?P{IcNk_qX|Xch$eWR`>7Q_HDcF^NKMz_#`kg9AQCbl9`Bn?XU1wv62unH#z@>t$>o>U}Hj-#UN) z-(=m~{nA)Vu^T4y`98l8WMGJX#73&iwn^JE{5!hEzHi=#lQmX%zSRDX-u^CrH^@TW z-^YLD;jnT>cuUTy#qpDEe|_!xdtLYK*9(`{{QdKG|G9s!r)|R#te62!p$I6RrY!qg z_@!m{jq|pkIPFiZKaC?!XZg2Re&Jwf&@F6cWe^Ys)ibC-bFlZ)#&%KiOy?BDA*_qTyU z_xfF2$&bQN!PE{;&xUXN7pvbn`fua4ebDrO2P5HwwJ!TvGQ+*ULASEnzW(RW*WLGx z8O|SyuOYGKxKZ+N#$SCWhK32))9h$nj5RBd2Fqx$45!BG4f$#Jw+K(K*Z%@+-7|Q) L`njxgN@xNAr&q6? diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-android.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-android.png new file mode 100644 index 0000000000000000000000000000000000000000..ece7a4aa642ff468fe2b7148fbe6bce26f573202 GIT binary patch literal 2921 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^s7WQ;;45^s&_Vz~K!vPX*fzqk1JqJV*g`Xu)@j1wmIq}lXDJC2uMJJh` z&$;CFajNvg*%-AMv%fPTq zevjJjEBX8FHviB5z72;;OuZC}K&;N3_3UrqmzLEx?61Y&e&58(ARr8^3=A8Q8K1iZ z8fHiNw_g-qm-Ij4f9ig2Qp@g~xO1GZx^prwfgF}%C(jTL zAgJL;>0n^s7WZ^<45^s&_Vz(vra%d|i;~XVCJE9J4ZIOcd2cY}-e8CpD(kThcWaKR zw3@eBDMjr5r)U26YV34RvoJV3%lpE>(6ChyXr*BzD}#XWC}T7bMpMFQE*Rpu;P#p0 z?H8pPB07IRpU(fkzP767zis|^^O!GNpKjRwqWX7q_TBeQSj)THJ#q{Tm(AlQ$G%z~ zKmX?co&UGpXXR#4P}{)3ki}9-p%;a2-*PDD`lbONO)z4*}Q$iB}5$};? literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-linux.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..59f46310cc725af514a453afcba3475f11744298 GIT binary patch literal 2929 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^smiBaU45^s&_O_uBOQ3|?#p_wy6jGFvqnEgy+LhB%5EN>pD;8iHBlmdD z7V95}qU*0l$IZK5%D^zea;7ZxY17S2JjOKzNo(poPE$_Z5 z&0wLl`*wR_@pp@35|4j<%6YTy-2Iw(zxU61s*mrwFZX5t-?hni-#4)`2nYkqTf;_V z#_cn53=EHj*PrD3dUyT#H~(+c-^QU5Q!j-gV5^gRHqN{Lo7 z7=wck3S*BF%LDhdzu&&$j8FO>Q=ht@8$$`ECKQpJ%F4LwHV@IV( tjfl|@8x1jRS$s5GVJ-QH4VE{ea=XmWeGTam0XCZ%JYD@<);T3K0RYOk>0$r? literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-macOS.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-macOS.png new file mode 100644 index 0000000000000000000000000000000000000000..59f46310cc725af514a453afcba3475f11744298 GIT binary patch literal 2929 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^smiBaU45^s&_O_uBOQ3|?#p_wy6jGFvqnEgy+LhB%5EN>pD;8iHBlmdD z7V95}qU*0l$IZK5%D^zea;7ZxY17S2JjOKzNo(poPE$_Z5 z&0wLl`*wR_@pp@35|4j<%6YTy-2Iw(zxU61s*mrwFZX5t-?hni-#4)`2nYkqTf;_V z#_cn53=EHj*PrD3dUyT#H~(+c-^QU5Q!j-gV5^gRHqN{Lo7 z7=wck3S*BF%LDhdzu&&$j8FO>Q=ht@8$$`ECKQpJ%F4LwHV@IV( tjfl|@8x1jRS$s5GVJ-QH4VE{ea=XmWeGTam0XCZ%JYD@<);T3K0RYOk>0$r? literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-windows.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-at-leftmost-character-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..59f46310cc725af514a453afcba3475f11744298 GIT binary patch literal 2929 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^smiBaU45^s&_O_uBOQ3|?#p_wy6jGFvqnEgy+LhB%5EN>pD;8iHBlmdD z7V95}qU*0l$IZK5%D^zea;7ZxY17S2JjOKzNo(poPE$_Z5 z&0wLl`*wR_@pp@35|4j<%6YTy-2Iw(zxU61s*mrwFZX5t-?hni-#4)`2nYkqTf;_V z#_cn53=EHj*PrD3dUyT#H~(+c-^QU5Q!j-gV5^gRHqN{Lo7 z7=wck3S*BF%LDhdzu&&$j8FO>Q=ht@8$$`ECKQpJ%F4LwHV@IV( tjfl|@8x1jRS$s5GVJ-QH4VE{ea=XmWeGTam0XCZ%JYD@<);T3K0RYOk>0$r? literal 0 HcmV?d00001 diff --git a/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-iOS.png b/super_editor/test_goldens/super_textfield/goldens/super-text-field_rtl-caret-iOS.png deleted file mode 100644 index 76fcc4c3e2ebbd2b62b075373d80f00bc228d2a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3102 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4mP03zO)&4fD~hKkh>GZx^prwfgF}%C(jTL zAgJL;>0n^sKH=%&7*a9k?cIaE(vA#m7jvG7-C@YJV76%>K*_W3+l$TwfUipua!Qt8S7mN%H(aIYb7?PN{85Gn;8KZ$Pni57# zF4#7^?!VMt#(i@$>t$>o&iz@k<3;uF=;`m`cW<4)|L@wh@5-B583crZ#gbtoGUIly z9K)WSU$Zww@87ra|DL6L>!N0XbUd%!{x^?Kp||@YbA$Qie->4TLf<@>%C@ineLU(` zojHbEL9XAJX(#*aqA~-+wj;b^3=TdCj0{Iuh-BoLS26tQ#~MJozmNaQ*)Ofb&@kfw z6GI0l3M0HF=hWKx$#;K!?fUy%_wCmUm)89K^Va_Szt7XQ-6z=ayy>;Nf8Y93>rZ31JZF}Fd*v4nh6de2?2(R{4B#T5^blSA{l}Mw@6Yppum3;q z+j0GvFZrKe7F_)PZTr64|F%Z|{(1X8zQEd`u)VIg^!0V~Z}ZdtZ(9ors`r-$Br(Zn zzx{nZ@9pz^0wHx{((dES`RnWJcKrXO%h2GZx^prwfgF}%C(jTL zAgJL;>0n^sKJV$`7*a9k?QO$8VMhj6NB&QuGKXBxY*ky9PVPGL<*ho{x?S451n`n@`^OEeh&j8vk z|N7#$-3OT%IyiM08fF|oX6U~DxuZ{xkzs>s9?8Yr?~BY2;&s|Dzu?qg^Zaq>|J(O> zmHmH{W?%n1`dk0kTXp8x{kopx=9KFFYP-L`7X7VXz2&-df7-Xt+y5JZIDbiZ1$k)_ z==KF$Wc(JZ-#Jp3XZZW)ZTo3|b+J2mW2&wE^^3|34BL)ik95pr02cwJhn$Po-`khp zulsZBZQ1U-)BWF<$J^Iee5|#cbBw3@Xq8>u*ZqIjZr@g4ws}5~aUEAoRl0KUKe@Ml zBP3GZx^prwfgF}%C(jTL zAgJL;>0n^sKJV$`7*a9k?QO$8VMhj6NB&QuGKXBxY*ky9PVPGL<*ho{x?S451n`n@`^OEeh&j8vk z|N7#$-3OT%IyiM08fF|oX6U~DxuZ{xkzs>s9?8Yr?~BY2;&s|Dzu?qg^Zaq>|J(O> zmHmH{W?%n1`dk0kTXp8x{kopx=9KFFYP-L`7X7VXz2&-df7-Xt+y5JZIDbiZ1$k)_ z==KF$Wc(JZ-#Jp3XZZW)ZTo3|b+J2mW2&wE^^3|34BL)ik95pr02cwJhn$Po-`khp zulsZBZQ1U-)BWF<$J^Iee5|#cbBw3@Xq8>u*ZqIjZr@g4ws}5~aUEAoRl0KUKe@Ml zBP3GZx^prwfgF}%C(jTL zAgJL;>0n^sKJV$`7*a9k?QO$8VMhj6NB&QuGKXBxY*ky9PVPGL<*ho{x?S451n`n@`^OEeh&j8vk z|N7#$-3OT%IyiM08fF|oX6U~DxuZ{xkzs>s9?8Yr?~BY2;&s|Dzu?qg^Zaq>|J(O> zmHmH{W?%n1`dk0kTXp8x{kopx=9KFFYP-L`7X7VXz2&-df7-Xt+y5JZIDbiZ1$k)_ z==KF$Wc(JZ-#Jp3XZZW)ZTo3|b+J2mW2&wE^^3|34BL)ik95pr02cwJhn$Po-`khp zulsZBZQ1U-)BWF<$J^Iee5|#cbBw3@Xq8>u*ZqIjZr@g4ws}5~aUEAoRl0KUKe@Ml zBP3 RTL mode >', () { testGoldensOnAllPlatforms( - 'inserts text and paints caret on the left side', + 'inserts text and paints caret on the left side for downstream position', (tester) async { await _pumpTestApp(tester); // Place the caret at the beginning of the text field. await tester.placeCaretInSuperTextField(0); - // Type the text "Example of text containing multiple lines.". + // Type the text "Example". await tester.ime.typeText( - 'مثال لنص يحتوي على عدة أسطر', + 'مثال', getter: imeClientGetter, ); await tester.pumpAndSettle(); - await screenMatchesGolden(tester, 'super-text-field_rtl-caret-${defaultTargetPlatform.name}'); + await screenMatchesGolden( + tester, 'super-text-field_rtl-caret-at-leftmost-character-${defaultTargetPlatform.name}'); }, windowSize: const Size(600, 600), );