Support text direction rtl

pull/681/head
X Code 3 years ago
parent a92bfe9d6d
commit 4f9ee27f7d
  1. 12
      lib/src/models/documents/attribute.dart
  2. 11
      lib/src/utils/delta.dart
  3. 7
      lib/src/widgets/raw_editor.dart
  4. 5
      lib/src/widgets/text_block.dart
  5. 11
      lib/src/widgets/toolbar.dart

@ -79,6 +79,8 @@ class Attribute<T> {
static final BlockQuoteAttribute blockQuote = BlockQuoteAttribute(); static final BlockQuoteAttribute blockQuote = BlockQuoteAttribute();
static final DirectionAttribute direction = DirectionAttribute(null);
static final WidthAttribute width = WidthAttribute(null); static final WidthAttribute width = WidthAttribute(null);
static final HeightAttribute height = HeightAttribute(null); static final HeightAttribute height = HeightAttribute(null);
@ -116,6 +118,7 @@ class Attribute<T> {
Attribute.codeBlock.key, Attribute.codeBlock.key,
Attribute.blockQuote.key, Attribute.blockQuote.key,
Attribute.indent.key, Attribute.indent.key,
Attribute.direction.key,
}); });
static final Set<String> blockKeysExceptHeader = LinkedHashSet.of({ static final Set<String> blockKeysExceptHeader = LinkedHashSet.of({
@ -124,6 +127,7 @@ class Attribute<T> {
Attribute.codeBlock.key, Attribute.codeBlock.key,
Attribute.blockQuote.key, Attribute.blockQuote.key,
Attribute.indent.key, Attribute.indent.key,
Attribute.direction.key,
}); });
static final Set<String> exclusiveBlockKeys = LinkedHashSet.of({ static final Set<String> exclusiveBlockKeys = LinkedHashSet.of({
@ -163,6 +167,9 @@ class Attribute<T> {
// "attributes":{"list":"unchecked"} // "attributes":{"list":"unchecked"}
static Attribute<String?> get unchecked => ListAttribute('unchecked'); static Attribute<String?> get unchecked => ListAttribute('unchecked');
// "attributes":{"direction":"rtl"}
static Attribute<String?> get rtl => DirectionAttribute('rtl');
// "attributes":{"indent":1"} // "attributes":{"indent":1"}
static Attribute<int?> get indentL1 => IndentAttribute(level: 1); static Attribute<int?> get indentL1 => IndentAttribute(level: 1);
@ -309,6 +316,11 @@ class BlockQuoteAttribute extends Attribute<bool> {
BlockQuoteAttribute() : super('blockquote', AttributeScope.BLOCK, true); BlockQuoteAttribute() : super('blockquote', AttributeScope.BLOCK, true);
} }
class DirectionAttribute extends Attribute<String?> {
DirectionAttribute(String? val)
: super('direction', AttributeScope.BLOCK, val);
}
class WidthAttribute extends Attribute<String?> { class WidthAttribute extends Attribute<String?> {
WidthAttribute(String? val) : super('width', AttributeScope.IGNORE, val); WidthAttribute(String? val) : super('width', AttributeScope.IGNORE, val);
} }

@ -1,5 +1,8 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui';
import '../models/documents/attribute.dart';
import '../models/documents/nodes/node.dart';
import '../models/quill_delta.dart'; import '../models/quill_delta.dart';
// Diff between two texts - old text and new text // Diff between two texts - old text and new text
@ -72,3 +75,11 @@ int getPositionDelta(Delta user, Delta actual) {
} }
return diff; return diff;
} }
TextDirection getDirectionOfNode(Node node) {
final direction = node.style.attributes[Attribute.direction.key];
if (direction == Attribute.rtl) {
return TextDirection.rtl;
}
return TextDirection.ltr;
}

@ -19,6 +19,7 @@ import '../models/documents/nodes/embeddable.dart';
import '../models/documents/nodes/line.dart'; import '../models/documents/nodes/line.dart';
import '../models/documents/nodes/node.dart'; import '../models/documents/nodes/node.dart';
import '../models/documents/style.dart'; import '../models/documents/style.dart';
import '../utils/delta.dart';
import '../utils/platform.dart'; import '../utils/platform.dart';
import 'controller.dart'; import 'controller.dart';
import 'cursor.dart'; import 'cursor.dart';
@ -436,7 +437,8 @@ class RawEditorState extends EditorState
for (final node in doc.root.children) { for (final node in doc.root.children) {
if (node is Line) { if (node is Line) {
final editableTextLine = _getEditableTextLineFromNode(node, context); final editableTextLine = _getEditableTextLineFromNode(node, context);
result.add(editableTextLine); result.add(Directionality(
textDirection: getDirectionOfNode(node), child: editableTextLine));
} else if (node is Block) { } else if (node is Block) {
final attrs = node.style.attributes; final attrs = node.style.attributes;
final editableTextBlock = EditableTextBlock( final editableTextBlock = EditableTextBlock(
@ -461,7 +463,8 @@ class RawEditorState extends EditorState
onCheckboxTap: _handleCheckboxTap, onCheckboxTap: _handleCheckboxTap,
readOnly: widget.readOnly, readOnly: widget.readOnly,
customStyleBuilder: widget.customStyleBuilder); customStyleBuilder: widget.customStyleBuilder);
result.add(editableTextBlock); result.add(Directionality(
textDirection: getDirectionOfNode(node), child: editableTextBlock));
} else { } else {
throw StateError('Unreachable.'); throw StateError('Unreachable.');
} }

@ -5,6 +5,7 @@ import 'package:tuple/tuple.dart';
import '../../flutter_quill.dart'; import '../../flutter_quill.dart';
import '../models/documents/nodes/block.dart'; import '../models/documents/nodes/block.dart';
import '../models/documents/nodes/line.dart'; import '../models/documents/nodes/line.dart';
import '../utils/delta.dart';
import 'box.dart'; import 'box.dart';
import 'cursor.dart'; import 'cursor.dart';
import 'delegate.dart'; import 'delegate.dart';
@ -146,7 +147,9 @@ class EditableTextBlock extends StatelessWidget {
hasFocus, hasFocus,
MediaQuery.of(context).devicePixelRatio, MediaQuery.of(context).devicePixelRatio,
cursorCont); cursorCont);
children.add(editableTextLine); final nodeTextDirection = getDirectionOfNode(line);
children.add(Directionality(
textDirection: nodeTextDirection, child: editableTextLine));
} }
return children.toList(growable: false); return children.toList(growable: false);
} }

@ -100,6 +100,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
bool showImageButton = true, bool showImageButton = true,
bool showVideoButton = true, bool showVideoButton = true,
bool showCameraButton = true, bool showCameraButton = true,
bool showDirection = false,
OnImagePickCallback? onImagePickCallback, OnImagePickCallback? onImagePickCallback,
OnVideoPickCallback? onVideoPickCallback, OnVideoPickCallback? onVideoPickCallback,
MediaPickSettingSelector? mediaPickSettingSelector, MediaPickSettingSelector? mediaPickSettingSelector,
@ -131,7 +132,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
showClearFormat || showClearFormat ||
onImagePickCallback != null || onImagePickCallback != null ||
onVideoPickCallback != null, onVideoPickCallback != null,
showAlignmentButtons, showAlignmentButtons || showDirection,
showLeftAlignment, showLeftAlignment,
showCenterAlignment, showCenterAlignment,
showRightAlignment, showRightAlignment,
@ -296,6 +297,14 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
showRightAlignment: showRightAlignment, showRightAlignment: showRightAlignment,
showJustifyAlignment: showJustifyAlignment, showJustifyAlignment: showJustifyAlignment,
), ),
if (showDirection)
ToggleStyleButton(
attribute: Attribute.rtl,
controller: controller,
icon: Icons.format_textdirection_r_to_l,
iconSize: toolbarIconSize,
iconTheme: iconTheme,
),
if (showDividers && if (showDividers &&
isButtonGroupShown[1] && isButtonGroupShown[1] &&
(isButtonGroupShown[2] || (isButtonGroupShown[2] ||

Loading…
Cancel
Save