diff --git a/app/assets/sample_data.json b/app/assets/sample_data.json index c2cac340..687e834d 100644 --- a/app/assets/sample_data.json +++ b/app/assets/sample_data.json @@ -199,5 +199,32 @@ }, { "insert":"\n" + }, + { + "insert":"\nCenter Align" + }, + { + "attributes":{ + "align":"center" + }, + "insert":"\n" + }, + { + "insert":"Right Align" + }, + { + "attributes":{ + "align":"right" + }, + "insert":"\n" + }, + { + "insert":"Justify Align" + }, + { + "attributes":{ + "align":"justify" + }, + "insert":"\n" } ] \ No newline at end of file diff --git a/lib/models/documents/attribute.dart b/lib/models/documents/attribute.dart index f2776a55..24212229 100644 --- a/lib/models/documents/attribute.dart +++ b/lib/models/documents/attribute.dart @@ -23,6 +23,7 @@ class Attribute { Attribute.background.key: Attribute.background, Attribute.header.key: Attribute.header, Attribute.indent.key: Attribute.indent, + Attribute.align.key: Attribute.align, Attribute.list.key: Attribute.list, Attribute.codeBlock.key: Attribute.codeBlock, Attribute.blockQuote.key: Attribute.blockQuote, @@ -46,6 +47,8 @@ class Attribute { static final IndentAttribute indent = IndentAttribute(); + static final AlignAttribute align = AlignAttribute(null); + static final ListAttribute list = ListAttribute(null); static final CodeBlockAttribute codeBlock = CodeBlockAttribute(); @@ -65,6 +68,7 @@ class Attribute { static final Set blockKeys = { Attribute.header.key, Attribute.indent.key, + Attribute.align.key, Attribute.list.key, Attribute.codeBlock.key, Attribute.blockQuote.key, @@ -73,6 +77,7 @@ class Attribute { static final Set blockKeysExceptHeader = { Attribute.list.key, Attribute.indent.key, + Attribute.align.key, Attribute.codeBlock.key, Attribute.blockQuote.key, }; @@ -83,6 +88,18 @@ class Attribute { static Attribute get h3 => HeaderAttribute(level: 3); + // "attributes":{"align":"left"} + static Attribute get leftAlignment => AlignAttribute('left'); + + // "attributes":{"align":"center"} + static Attribute get centerAlignment => AlignAttribute('center'); + + // "attributes":{"align":"right"} + static Attribute get rightAlignment => AlignAttribute('right'); + + // "attributes":{"align":"justify"} + static Attribute get justifyAlignment => AlignAttribute('justify'); + // "attributes":{"list":"bullet"} static Attribute get ul => ListAttribute('bullet'); @@ -164,6 +181,10 @@ class IndentAttribute extends Attribute { IndentAttribute({int level}) : super('indent', AttributeScope.BLOCK, level); } +class AlignAttribute extends Attribute { + AlignAttribute(String val) : super('align', AttributeScope.BLOCK, val); +} + class ListAttribute extends Attribute { ListAttribute(String val) : super('list', AttributeScope.BLOCK, val); } diff --git a/lib/widgets/default_styles.dart b/lib/widgets/default_styles.dart index 0d393de1..35eb561c 100644 --- a/lib/widgets/default_styles.dart +++ b/lib/widgets/default_styles.dart @@ -55,6 +55,7 @@ class DefaultStyles { final DefaultTextBlockStyle quote; final DefaultTextBlockStyle code; final DefaultTextBlockStyle indent; + final DefaultTextBlockStyle align; DefaultStyles( this.h1, @@ -69,7 +70,8 @@ class DefaultStyles { this.lists, this.quote, this.code, - this.indent); + this.indent, + this.align); static DefaultStyles getInstance(BuildContext context) { ThemeData themeData = Theme.of(context); @@ -158,6 +160,7 @@ class DefaultStyles { color: Colors.grey.shade50, borderRadius: BorderRadius.circular(2), )), + DefaultTextBlockStyle(baseStyle, Tuple2(0.0, 0.0), Tuple2(0.0, 0.0), null), DefaultTextBlockStyle(baseStyle, Tuple2(0.0, 0.0), Tuple2(0.0, 0.0), null)); } @@ -175,6 +178,7 @@ class DefaultStyles { other.lists ?? this.lists, other.quote ?? this.quote, other.code ?? this.code, - other.indent ?? this.indent); + other.indent ?? this.indent, + other.align ?? this.align); } } diff --git a/lib/widgets/proxy.dart b/lib/widgets/proxy.dart index fd7793e7..b710e8d3 100644 --- a/lib/widgets/proxy.dart +++ b/lib/widgets/proxy.dart @@ -128,6 +128,7 @@ class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox { class RichTextProxy extends SingleChildRenderObjectWidget { final TextStyle textStyle; + final TextAlign textAlign; final TextDirection textDirection; final double textScaleFactor; final Locale locale; @@ -137,13 +138,22 @@ class RichTextProxy extends SingleChildRenderObjectWidget { @override RenderParagraphProxy createRenderObject(BuildContext context) { - return RenderParagraphProxy(null, textStyle, textDirection, textScaleFactor, - strutStyle, locale, textWidthBasis, textHeightBehavior); + return RenderParagraphProxy( + null, + textStyle, + textAlign, + textDirection, + textScaleFactor, + strutStyle, + locale, + textWidthBasis, + textHeightBehavior); } RichTextProxy( RichText child, this.textStyle, + this.textAlign, this.textDirection, this.textScaleFactor, this.locale, @@ -152,6 +162,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget { this.textHeightBehavior) : assert(child != null), assert(textStyle != null), + assert(textAlign != null), assert(textDirection != null), assert(locale != null), assert(strutStyle != null), @@ -161,6 +172,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget { void updateRenderObject( BuildContext context, covariant RenderParagraphProxy renderObject) { renderObject.textStyle = textStyle; + renderObject.textAlign = textAlign; renderObject.textDirection = textDirection; renderObject.textScaleFactor = textScaleFactor; renderObject.locale = locale; @@ -175,6 +187,7 @@ class RenderParagraphProxy extends RenderProxyBox RenderParagraphProxy( RenderParagraph child, TextStyle textStyle, + TextAlign textAlign, TextDirection textDirection, double textScaleFactor, StrutStyle strutStyle, @@ -183,7 +196,7 @@ class RenderParagraphProxy extends RenderProxyBox TextHeightBehavior textHeightBehavior, ) : _prototypePainter = TextPainter( text: TextSpan(text: ' ', style: textStyle), - textAlign: TextAlign.left, + textAlign: textAlign, textDirection: textDirection, textScaleFactor: textScaleFactor, strutStyle: strutStyle, @@ -203,6 +216,15 @@ class RenderParagraphProxy extends RenderProxyBox markNeedsLayout(); } + set textAlign(TextAlign value) { + assert(value != null); + if (_prototypePainter.textAlign == value) { + return; + } + _prototypePainter.textAlign = value; + markNeedsLayout(); + } + set textDirection(TextDirection value) { assert(value != null); if (_prototypePainter.textDirection == value) { diff --git a/lib/widgets/text_block.dart b/lib/widgets/text_block.dart index 0ec6efd2..e1a6143f 100644 --- a/lib/widgets/text_block.dart +++ b/lib/widgets/text_block.dart @@ -180,6 +180,8 @@ class EditableTextBlock extends StatelessWidget { lineSpacing = defaultStyles.code.lineSpacing; } else if (attrs.containsKey(Attribute.indent.key)) { lineSpacing = defaultStyles.indent.lineSpacing; + } else if (attrs.containsKey(Attribute.align.key)) { + lineSpacing = defaultStyles.align.lineSpacing; } top = lineSpacing.item1; bottom = lineSpacing.item2; diff --git a/lib/widgets/text_line.dart b/lib/widgets/text_line.dart index 6aae72e6..fc3737fc 100644 --- a/lib/widgets/text_line.dart +++ b/lib/widgets/text_line.dart @@ -42,6 +42,7 @@ class TextLine extends StatelessWidget { TextSpan textSpan = _buildTextSpan(context); StrutStyle strutStyle = StrutStyle.fromTextStyle(textSpan.style, forceStrutHeight: true); + final textAlign = _getTextAlign(); RichText child = RichText( text: _buildTextSpan(context), textDirection: textDirection, @@ -51,6 +52,7 @@ class TextLine extends StatelessWidget { return RichTextProxy( child, textSpan.style, + textAlign, textDirection, 1.0, Localizations.localeOf(context, nullOk: true), @@ -59,6 +61,20 @@ class TextLine extends StatelessWidget { null); } + TextAlign _getTextAlign() { + final alignment = line.style.attributes[Attribute.align.key]; + if (alignment == Attribute.leftAlignment) { + return TextAlign.left; + } else if (alignment == Attribute.centerAlignment) { + return TextAlign.center; + } else if (alignment == Attribute.rightAlignment) { + return TextAlign.right; + } else if (alignment == Attribute.justifyAlignment) { + return TextAlign.justify; + } + return TextAlign.start; + } + TextSpan _buildTextSpan(BuildContext context) { DefaultStyles defaultStyles = DefaultStyles.getInstance(context); List children = line.children