Skip to content

Commit

Permalink
Added text direction support
Browse files Browse the repository at this point in the history
  • Loading branch information
Amir-P committed Oct 21, 2021
1 parent b809a0d commit dd6e39c
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 27 deletions.
21 changes: 21 additions & 0 deletions packages/notus/lib/src/document/attributes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ abstract class NotusAttributeBuilder<T> implements NotusAttributeKey<T> {
/// * [NotusAttribute.link]
/// * [NotusAttribute.heading]
/// * [NotusAttribute.block]
/// * [NotusAttribute.direction]
class NotusAttribute<T> implements NotusAttributeBuilder<T> {
static final Map<String, NotusAttributeBuilder> _registry = {
NotusAttribute.bold.key: NotusAttribute.bold,
Expand All @@ -81,6 +82,7 @@ class NotusAttribute<T> implements NotusAttributeBuilder<T> {
NotusAttribute.link.key: NotusAttribute.link,
NotusAttribute.heading.key: NotusAttribute.heading,
NotusAttribute.block.key: NotusAttribute.block,
NotusAttribute.direction.key: NotusAttribute.direction,
};

// Inline attributes
Expand Down Expand Up @@ -132,6 +134,15 @@ class NotusAttribute<T> implements NotusAttributeBuilder<T> {
/// Alias for [NotusAttribute.block.code].
static NotusAttribute<String> get code => block.code;

/// Direction attribute
static const direction = DirectionAttributeBuilder._();

/// Alias for [NotusAttribute.direction.ltr].
static NotusAttribute<String> get rtl => direction.rtl;

/// Alias for [NotusAttribute.direction.ltr].
static NotusAttribute<String> get ltr => direction.ltr;

static NotusAttribute _fromKeyValue(String key, dynamic value) {
if (!_registry.containsKey(key)) {
throw ArgumentError.value(
Expand Down Expand Up @@ -400,3 +411,13 @@ class BlockAttributeBuilder extends NotusAttributeBuilder<String> {
NotusAttribute<String> get quote =>
NotusAttribute<String>._(key, scope, 'quote');
}

class DirectionAttributeBuilder extends NotusAttributeBuilder<String> {
static const _kDirection = 'direction';
const DirectionAttributeBuilder._()
: super._(_kDirection, NotusAttributeScope.line);

NotusAttribute<String> get rtl => NotusAttribute<String>._(key, scope, 'rtl');

NotusAttribute<String> get ltr => NotusAttribute<String>._(key, scope, 'ltr');
}
3 changes: 1 addition & 2 deletions packages/zefyr/lib/src/rendering/editable_text_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ class RenderEditableTextBlock extends RenderEditableContainerBox
required TextDirection textDirection,
required EdgeInsetsGeometry padding,
required Decoration decoration,
ImageConfiguration configuration = ImageConfiguration.empty,
EdgeInsets contentPadding = EdgeInsets.zero,
}) : _decoration = decoration,
_configuration = configuration,
_configuration = ImageConfiguration(textDirection: textDirection),
_savedPadding = padding,
_contentPadding = contentPadding,
super(
Expand Down
3 changes: 2 additions & 1 deletion packages/zefyr/lib/src/rendering/editable_text_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,8 @@ class RenderEditableTextLine extends RenderEditableBox {
maxHeight: body!.size.height);
leading!.layout(leadingConstraints, parentUsesSize: true);
final parentData = leading!.parentData as BoxParentData;
parentData.offset = Offset(0.0, _resolvedPadding!.top);
final dxOffset = textDirection == TextDirection.rtl ? body!.size.width : 0.0;
parentData.offset = Offset(dxOffset, _resolvedPadding!.top);
}

size = constraints.constrain(Size(
Expand Down
18 changes: 15 additions & 3 deletions packages/zefyr/lib/src/widgets/editable_text_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,36 @@ class EditableTextBlock extends StatelessWidget {
);
}

TextDirection getTextDirectionForNode(BuildContext context, StyledNode node) {
final preferredDirection = node.style.get(NotusAttribute.direction);
if (preferredDirection == NotusAttribute.rtl) {
return TextDirection.rtl;
} else if (preferredDirection == NotusAttribute.ltr) {
return TextDirection.ltr;
}
return Directionality.of(context);
}

List<Widget> _buildChildren(BuildContext context) {
final theme = ZefyrTheme.of(context)!;
final count = node.children.length;
final children = <Widget>[];
var index = 0;
for (final line in node.children) {
index++;
final nodeTextDirection =
getTextDirectionForNode(context, line as LineNode);
children.add(EditableTextLine(
node: line as LineNode,
textDirection: textDirection,
node: line,
textDirection: nodeTextDirection,
spacing: _getSpacingForLine(line, index, count, theme),
leading: _buildLeading(context, line, index, count),
indentWidth: _getIndentWidth(),
devicePixelRatio: MediaQuery.of(context).devicePixelRatio,
body: TextLine(
node: line,
textDirection: textDirection,
embedBuilder: embedBuilder,
textDirection: nodeTextDirection,
),
cursorController: cursorController,
selection: selection,
Expand Down
18 changes: 15 additions & 3 deletions packages/zefyr/lib/src/widgets/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1155,13 +1155,24 @@ class RawEditorState extends EditorState
);
}

TextDirection getTextDirectionForStyledNode(StyledNode node){
final preferredDirection = node.style.get(NotusAttribute.direction);
if (preferredDirection == NotusAttribute.rtl) {
return TextDirection.rtl;
} else if (preferredDirection == NotusAttribute.ltr) {
return TextDirection.ltr;
}
return _textDirection;
}

List<Widget> _buildChildren(BuildContext context) {
final result = <Widget>[];
for (final node in widget.controller.document.root.children) {
if (node is LineNode) {
final textDirection = getTextDirectionForStyledNode(node);
result.add(EditableTextLine(
node: node,
textDirection: _textDirection,
textDirection: textDirection,
indentWidth: 0,
spacing: _getSpacingForLine(node, _themeData),
cursorController: _cursorController,
Expand All @@ -1170,17 +1181,18 @@ class RawEditorState extends EditorState
enableInteractiveSelection: widget.enableInteractiveSelection,
body: TextLine(
node: node,
textDirection: _textDirection,
embedBuilder: widget.embedBuilder,
textDirection: textDirection,
),
hasFocus: _hasFocus,
devicePixelRatio: MediaQuery.of(context).devicePixelRatio,
));
} else if (node is BlockNode) {
final block = node.style.get(NotusAttribute.block);
final textDirection = getTextDirectionForStyledNode(node);
result.add(EditableTextBlock(
node: node,
textDirection: _textDirection,
textDirection: textDirection,
spacing: _getSpacingForBlock(node, _themeData),
cursorController: _cursorController,
selection: widget.controller.selection,
Expand Down
51 changes: 37 additions & 14 deletions packages/zefyr/lib/src/widgets/editor_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -386,20 +386,22 @@ class ZefyrToolbar extends StatefulWidget implements PreferredSizeWidget {

const ZefyrToolbar({Key? key, required this.children}) : super(key: key);

factory ZefyrToolbar.basic(
{Key? key,
required ZefyrController controller,
bool hideBoldButton = false,
bool hideItalicButton = false,
bool hideUnderLineButton = false,
bool hideStrikeThrough = false,
bool hideHeadingStyle = false,
bool hideListNumbers = false,
bool hideListBullets = false,
bool hideCodeBlock = false,
bool hideQuote = false,
bool hideLink = false,
bool hideHorizontalRule = false}) {
factory ZefyrToolbar.basic({
Key? key,
required ZefyrController controller,
bool hideBoldButton = false,
bool hideItalicButton = false,
bool hideUnderLineButton = false,
bool hideStrikeThrough = false,
bool hideHeadingStyle = false,
bool hideListNumbers = false,
bool hideListBullets = false,
bool hideCodeBlock = false,
bool hideQuote = false,
bool hideLink = false,
bool hideHorizontalRule = false,
bool hideDirection = false,
}) {
return ZefyrToolbar(key: key, children: [
Visibility(
visible: !hideBoldButton,
Expand Down Expand Up @@ -436,6 +438,27 @@ class ZefyrToolbar extends StatefulWidget implements PreferredSizeWidget {
controller: controller,
),
),
Visibility(
visible: !hideDirection,
child: VerticalDivider(
indent: 16, endIndent: 16, color: Colors.grey.shade400)),
Visibility(
visible: !hideDirection,
child: ToggleStyleButton(
attribute: NotusAttribute.ltr,
icon: Icons.format_textdirection_l_to_r,
controller: controller,
),
),
SizedBox(width: 1),
Visibility(
visible: !hideDirection,
child: ToggleStyleButton(
attribute: NotusAttribute.rtl,
icon: Icons.format_textdirection_r_to_l,
controller: controller,
),
),
Visibility(
visible: !hideHeadingStyle,
child: VerticalDivider(
Expand Down
4 changes: 2 additions & 2 deletions packages/zefyr/lib/src/widgets/text_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import 'theme.dart';
class TextLine extends StatelessWidget {
/// Line of text represented by this widget.
final LineNode node;
final TextDirection? textDirection;
final TextDirection textDirection;
final ZefyrEmbedBuilder embedBuilder;

const TextLine({
Key? key,
required this.node,
required this.embedBuilder,
this.textDirection,
required this.textDirection,
}) : super(key: key);

@override
Expand Down
4 changes: 2 additions & 2 deletions packages/zefyr/lib/src/widgets/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ class ZefyrThemeData {
spacing: baseSpacing,
lineSpacing: VerticalSpacing(top: 6, bottom: 2),
decoration: BoxDecoration(
border: Border(
left: BorderSide(width: 4, color: Colors.grey.shade300),
border: BorderDirectional(
start: BorderSide(width: 4, color: Colors.grey.shade300),
),
),
),
Expand Down

0 comments on commit dd6e39c

Please sign in to comment.