Split code and codeBlock spans and factories

This commit is contained in:
Dimitry Ivanov 2019-03-02 14:41:01 +03:00
parent e1d5530962
commit 0a965e4cbc
8 changed files with 191 additions and 120 deletions

View File

@ -116,7 +116,7 @@ The color of background of code block text
Leading margin for the block code content
<ThemeProperty name="codeMultilineMargin" type="@Px int" defaults="Width of the space character" />
<ThemeProperty name="codeMultilineMargin" type="@Px int" defaults="8dip" />
### Code typeface

View File

@ -120,7 +120,7 @@ The color of background of code block text
Leading margin for the block code content
<ThemeProperty name="codeMultilineMargin" type="@Px int" defaults="Width of the space character" />
<ThemeProperty name="codeMultilineMargin" type="@Px int" defaults="8dip" />
### Code typeface

View File

@ -108,7 +108,7 @@ public class MarkwonTheme {
final Dip dip = Dip.create(context);
return new Builder()
.codeMultilineMargin(dip.toPx(8))
.codeBlockMargin(dip.toPx(8))
.blockMargin(dip.toPx(24))
.blockQuoteWidth(dip.toPx(4))
.bulletListItemStrokeWidth(dip.toPx(1))
@ -165,15 +165,19 @@ public class MarkwonTheme {
// by default `width` of a space char... it's fun and games, but span doesn't have access to paint in `getLeadingMargin`
// so, we need to set this value explicitly (think of an utility method, that takes TextView/TextPaint and measures space char)
protected final int codeMultilineMargin;
protected final int codeBlockMargin;
// by default Typeface.MONOSPACE
protected final Typeface codeTypeface;
protected final Typeface codeBlockTypeface;
// by default a bit (how much?!) smaller than normal text
// applied ONLY if default typeface was used, otherwise, not applied
protected final int codeTextSize;
protected final int codeBlockTextSize;
// by default paint.getStrokeWidth
protected final int headingBreakHeight;
@ -207,9 +211,11 @@ public class MarkwonTheme {
this.codeBlockTextColor = builder.codeBlockTextColor;
this.codeBackgroundColor = builder.codeBackgroundColor;
this.codeBlockBackgroundColor = builder.codeBlockBackgroundColor;
this.codeMultilineMargin = builder.codeMultilineMargin;
this.codeBlockMargin = builder.codeBlockMargin;
this.codeTypeface = builder.codeTypeface;
this.codeBlockTypeface = builder.codeBlockTypeface;
this.codeTextSize = builder.codeTextSize;
this.codeBlockTextSize = builder.codeBlockTextSize;
this.headingBreakHeight = builder.headingBreakHeight;
this.headingBreakColor = builder.headingBreakColor;
this.headingTypeface = builder.headingTypeface;
@ -301,66 +307,117 @@ public class MarkwonTheme {
}
/**
* Modified in 1.0.5 to accept `multiline` argument
* @since 3.0.0
*/
public void applyCodeTextStyle(@NonNull Paint paint, boolean multiline) {
public void applyCodeTextStyle(@NonNull Paint paint) {
// @since 1.0.5 added handling of multiline code blocks
if (multiline
&& codeBlockTextColor != 0) {
paint.setColor(codeBlockTextColor);
} else if (codeTextColor != 0) {
if (codeTextColor != 0) {
paint.setColor(codeTextColor);
}
// custom typeface was set
if (codeTypeface != null) {
paint.setTypeface(codeTypeface);
// please note that we won't be calculating textSize
// (like we do when no Typeface is provided), if it's some specific typeface
// we would confuse users about textSize
if (codeTextSize != 0) {
if (codeTextSize > 0) {
paint.setTextSize(codeTextSize);
}
} else {
paint.setTypeface(Typeface.MONOSPACE);
final float textSize;
if (codeTextSize != 0) {
textSize = codeTextSize;
if (codeTextSize > 0) {
paint.setTextSize(codeTextSize);
} else {
textSize = paint.getTextSize() * CODE_DEF_TEXT_SIZE_RATIO;
paint.setTextSize(paint.getTextSize() * CODE_DEF_TEXT_SIZE_RATIO);
}
paint.setTextSize(textSize);
}
}
public int getCodeMultilineMargin() {
return codeMultilineMargin;
/**
* @since 3.0.0
*/
public void applyCodeBlockTextStyle(@NonNull Paint paint) {
// apply text color, first check for block specific value,
// then check for code (inline), else do nothing (keep original color of text)
final int textColor = codeBlockTextColor != 0
? codeBlockTextColor
: codeTextColor;
if (textColor != 0) {
paint.setColor(textColor);
}
final Typeface typeface = codeBlockTypeface != null
? codeBlockTypeface
: codeTypeface;
if (typeface != null) {
paint.setTypeface(typeface);
// please note that we won't be calculating textSize
// (like we do when no Typeface is provided), if it's some specific typeface
// we would confuse users about textSize
final int textSize = codeBlockTextSize > 0
? codeBlockTextSize
: codeTextSize;
if (textSize > 0) {
paint.setTextSize(textSize);
}
} else {
// by default use monospace
paint.setTypeface(Typeface.MONOSPACE);
final int textSize = codeBlockTextSize > 0
? codeBlockTextSize
: codeTextSize;
if (textSize > 0) {
paint.setTextSize(textSize);
} else {
// calculate default value
paint.setTextSize(paint.getTextSize() * CODE_DEF_TEXT_SIZE_RATIO);
}
}
}
public int getCodeBlockMargin() {
return codeBlockMargin;
}
/**
* Modified in 1.0.5 to accept `multiline` argument
* @since 3.0.0
*/
public int getCodeBackgroundColor(@NonNull Paint paint, boolean multiline) {
public int getCodeBackgroundColor(@NonNull Paint paint) {
final int color;
// @since 1.0.5 added handling of multiline code blocks
if (multiline
&& codeBlockBackgroundColor != 0) {
color = codeBlockBackgroundColor;
} else if (codeBackgroundColor != 0) {
if (codeBackgroundColor != 0) {
color = codeBackgroundColor;
} else {
color = ColorUtils.applyAlpha(paint.getColor(), CODE_DEF_BACKGROUND_COLOR_ALPHA);
}
return color;
}
/**
* @since 3.0.0
*/
public int getCodeBlockBackgroundColor(@NonNull Paint paint) {
final int color = codeBlockBackgroundColor != 0
? codeBlockBackgroundColor
: codeBackgroundColor;
return color != 0
? color
: ColorUtils.applyAlpha(paint.getColor(), CODE_DEF_BACKGROUND_COLOR_ALPHA);
}
public void applyHeadingTextStyle(@NonNull Paint paint, @IntRange(from = 1, to = 6) int level) {
if (headingTypeface == null) {
paint.setFakeBoldText(true);
@ -426,9 +483,11 @@ public class MarkwonTheme {
private int codeBlockTextColor; // @since 1.0.5
private int codeBackgroundColor;
private int codeBlockBackgroundColor; // @since 1.0.5
private int codeMultilineMargin;
private int codeBlockMargin;
private Typeface codeTypeface;
private Typeface codeBlockTypeface; // @since 3.0.0
private int codeTextSize;
private int codeBlockTextSize; // @since 3.0.0
private int headingBreakHeight = -1;
private int headingBreakColor;
private Typeface headingTypeface;
@ -451,7 +510,7 @@ public class MarkwonTheme {
this.codeBlockTextColor = theme.codeBlockTextColor;
this.codeBackgroundColor = theme.codeBackgroundColor;
this.codeBlockBackgroundColor = theme.codeBlockBackgroundColor;
this.codeMultilineMargin = theme.codeMultilineMargin;
this.codeBlockMargin = theme.codeBlockMargin;
this.codeTypeface = theme.codeTypeface;
this.codeTextSize = theme.codeTextSize;
this.headingBreakHeight = theme.headingBreakHeight;
@ -537,8 +596,8 @@ public class MarkwonTheme {
}
@NonNull
public Builder codeMultilineMargin(@Px int codeMultilineMargin) {
this.codeMultilineMargin = codeMultilineMargin;
public Builder codeBlockMargin(@Px int codeBlockMargin) {
this.codeBlockMargin = codeBlockMargin;
return this;
}
@ -548,12 +607,30 @@ public class MarkwonTheme {
return this;
}
/**
* @since 3.0.0
*/
@NonNull
public Builder codeBlockTypeface(@NonNull Typeface typeface) {
this.codeBlockTypeface = typeface;
return this;
}
@NonNull
public Builder codeTextSize(@Px int codeTextSize) {
this.codeTextSize = codeTextSize;
return this;
}
/**
* @since 3.0.0
*/
@NonNull
public Builder codeBlockTextSize(@Px int codeTextSize) {
this.codeBlockTextSize = codeTextSize;
return this;
}
@NonNull
public Builder headingBreakHeight(@Px int headingBreakHeight) {
this.headingBreakHeight = headingBreakHeight;

View File

@ -6,12 +6,12 @@ import android.support.annotation.Nullable;
import ru.noties.markwon.MarkwonConfiguration;
import ru.noties.markwon.RenderProps;
import ru.noties.markwon.SpanFactory;
import ru.noties.markwon.core.spans.CodeSpan;
import ru.noties.markwon.core.spans.CodeBlockSpan;
public class CodeBlockSpanFactory implements SpanFactory {
@Nullable
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
return new CodeSpan(configuration.theme(), true);
return new CodeBlockSpan(configuration.theme());
}
}

View File

@ -12,6 +12,6 @@ public class CodeSpanFactory implements SpanFactory {
@Nullable
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
return new CodeSpan(configuration.theme(), false);
return new CodeSpan(configuration.theme());
}
}

View File

@ -0,0 +1,66 @@
package ru.noties.markwon.core.spans;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.text.Layout;
import android.text.TextPaint;
import android.text.style.LeadingMarginSpan;
import android.text.style.MetricAffectingSpan;
import ru.noties.markwon.core.MarkwonTheme;
/**
* @since 3.0.0 split inline and block spans
*/
public class CodeBlockSpan extends MetricAffectingSpan implements LeadingMarginSpan {
private final MarkwonTheme theme;
private final Rect rect = ObjectsPool.rect();
private final Paint paint = ObjectsPool.paint();
public CodeBlockSpan(@NonNull MarkwonTheme theme) {
this.theme = theme;
}
@Override
public void updateMeasureState(TextPaint p) {
apply(p);
}
@Override
public void updateDrawState(TextPaint ds) {
apply(ds);
}
private void apply(TextPaint p) {
theme.applyCodeBlockTextStyle(p);
}
@Override
public int getLeadingMargin(boolean first) {
return theme.getCodeBlockMargin();
}
@Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
paint.setStyle(Paint.Style.FILL);
paint.setColor(theme.getCodeBlockBackgroundColor(p));
final int left;
final int right;
if (dir > 0) {
left = x;
right = c.getWidth();
} else {
left = x - c.getWidth();
right = x;
}
rect.set(left, top, right, bottom);
c.drawRect(rect, paint);
}
}

View File

@ -1,27 +1,20 @@
package ru.noties.markwon.core.spans;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.text.Layout;
import android.text.TextPaint;
import android.text.style.LeadingMarginSpan;
import android.text.style.MetricAffectingSpan;
import ru.noties.markwon.core.MarkwonTheme;
public class CodeSpan extends MetricAffectingSpan implements LeadingMarginSpan {
/**
* @since 3.0.0 split inline and block spans
*/
public class CodeSpan extends MetricAffectingSpan {
private final MarkwonTheme theme;
private final Rect rect = ObjectsPool.rect();
private final Paint paint = ObjectsPool.paint();
private final boolean multiline;
public CodeSpan(@NonNull MarkwonTheme theme, boolean multiline) {
public CodeSpan(@NonNull MarkwonTheme theme) {
this.theme = theme;
this.multiline = multiline;
}
@Override
@ -32,41 +25,10 @@ public class CodeSpan extends MetricAffectingSpan implements LeadingMarginSpan {
@Override
public void updateDrawState(TextPaint ds) {
apply(ds);
if (!multiline) {
ds.bgColor = theme.getCodeBackgroundColor(ds, false);
}
ds.bgColor = theme.getCodeBackgroundColor(ds);
}
private void apply(TextPaint p) {
theme.applyCodeTextStyle(p, multiline);
}
@Override
public int getLeadingMargin(boolean first) {
return multiline ? theme.getCodeMultilineMargin() : 0;
}
@Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
if (multiline) {
paint.setStyle(Paint.Style.FILL);
paint.setColor(theme.getCodeBackgroundColor(p, true));
final int left;
final int right;
if (dir > 0) {
left = x;
right = c.getWidth();
} else {
left = x - c.getWidth();
right = x;
}
rect.set(left, top, right, bottom);
c.drawRect(rect, paint);
}
theme.applyCodeTextStyle(p);
}
}

View File

@ -13,8 +13,6 @@ import android.text.TextUtils;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.node.FencedCodeBlock;
import org.commonmark.node.Heading;
import org.commonmark.node.SoftLineBreak;
import org.commonmark.parser.Parser;
import java.io.BufferedReader;
@ -29,9 +27,7 @@ import ru.noties.markwon.AbstractMarkwonPlugin;
import ru.noties.markwon.Markwon;
import ru.noties.markwon.MarkwonConfiguration;
import ru.noties.markwon.MarkwonVisitor;
import ru.noties.markwon.SpanFactory;
import ru.noties.markwon.core.CorePlugin;
import ru.noties.markwon.core.CoreProps;
import ru.noties.markwon.html.HtmlPlugin;
import ru.noties.markwon.image.ImagesPlugin;
import ru.noties.markwon.image.svg.SvgPlugin;
@ -52,36 +48,6 @@ public class RecyclerActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
{
final Markwon markwon = Markwon.builder(contex)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() {
@Override
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) {
// or just `visitor.length()`
final int start = visitor.builder().length();
visitor.visitChildren(heading);
// or just `visitor.setSpansForNodeOptional(heading, start)`
final SpanFactory factory = visitor.configuration().spansFactory().get(heading.getClass());
if (factory != null) {
visitor.setSpans(start, factory.getSpans(visitor.configuration(), visitor.renderProps()));
}
if (visitor.hasNext(heading)) {
visitor.ensureNewLine();
visitor.forceNewLine();
}
}
});
}
});
}
// create MarkwonAdapter and register two blocks that will be rendered differently
// * fenced code block (can also specify the same Entry for indended code block)
// * table block