diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0f5148..31fc43d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Changelog -# 4.3.1-SNAPSHOT +# $nap; * Fix DexGuard optimization issue ([#216])
Thanks [@francescocervone] * module `images`: `GifSupport` and `SvgSupport` use `Class.forName` instead access to full qualified class name +* `ext-table`: fix links in tables ([#224]) +* `ext-table`: proper borders (equal for all sides) [#216]: https://github.com/noties/Markwon/pull/216 +[#224]: https://github.com/noties/Markwon/issues/224 [@francescocervone]: https://github.com/francescocervone + # 4.3.0 * add `MarkwonInlineParserPlugin` in `inline-parser` module * `JLatexMathPlugin` now supports inline LaTeX structures via `MarkwonInlineParserPlugin` diff --git a/app/src/debug/java/io/noties/markwon/debug/ColorBlendView.java b/app/src/debug/java/io/noties/markwon/debug/ColorBlendView.java new file mode 100644 index 00000000..baf5e92b --- /dev/null +++ b/app/src/debug/java/io/noties/markwon/debug/ColorBlendView.java @@ -0,0 +1,59 @@ +package io.noties.markwon.debug; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.Nullable; + +import io.noties.markwon.app.R; +import io.noties.markwon.utils.ColorUtils; + +public class ColorBlendView extends View { + + private final Rect rect = new Rect(); + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private int background; + private int foreground; + + public ColorBlendView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + + if (attrs != null) { + final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ColorBlendView); + try { + background = array.getColor(R.styleable.ColorBlendView_cbv_background, 0); + foreground = array.getColor(R.styleable.ColorBlendView_cbv_foreground, 0); + } finally { + array.recycle(); + } + } + + paint.setStyle(Paint.Style.FILL); + + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + final int side = getWidth() / 11; + + rect.set(0, 0, side, getHeight()); + + canvas.translate(getPaddingLeft(), 0F); + + for (int i = 0; i < 11; i++) { + final float alpha = i / 10F; + paint.setColor(ColorUtils.blend(foreground, background, alpha)); + canvas.drawRect(rect, paint); + canvas.translate(side, 0F); + } + } +} diff --git a/app/src/debug/res/layout/debug_color_blend.xml b/app/src/debug/res/layout/debug_color_blend.xml new file mode 100644 index 00000000..cacc73fa --- /dev/null +++ b/app/src/debug/res/layout/debug_color_blend.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/debug/res/values/attrs.xml b/app/src/debug/res/values/attrs.xml index 161f7806..0636e107 100644 --- a/app/src/debug/res/values/attrs.xml +++ b/app/src/debug/res/values/attrs.xml @@ -8,4 +8,9 @@ + + + + + \ No newline at end of file diff --git a/markwon-core/src/main/java/io/noties/markwon/utils/ColorUtils.java b/markwon-core/src/main/java/io/noties/markwon/utils/ColorUtils.java index d2a8bc6e..d5d9703d 100644 --- a/markwon-core/src/main/java/io/noties/markwon/utils/ColorUtils.java +++ b/markwon-core/src/main/java/io/noties/markwon/utils/ColorUtils.java @@ -1,11 +1,33 @@ package io.noties.markwon.utils; +import android.graphics.Color; + +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; +import androidx.annotation.IntRange; + public abstract class ColorUtils { - public static int applyAlpha(int color, int alpha) { + @ColorInt + public static int applyAlpha( + @ColorInt int color, + @IntRange(from = 0, to = 255) int alpha) { return (color & 0x00FFFFFF) | (alpha << 24); } + // blend two colors w/ specified ratio, resulting color won't have alpha channel + @ColorInt + public static int blend( + @ColorInt int foreground, + @ColorInt int background, + @FloatRange(from = 0.0F, to = 1.0F) float ratio) { + return Color.rgb( + (int) (((1F - ratio) * Color.red(foreground)) + (ratio * Color.red(background))), + (int) (((1F - ratio) * Color.green(foreground)) + (ratio * Color.green(background))), + (int) (((1F - ratio) * Color.blue(foreground)) + (ratio * Color.blue(background))) + ); + } + private ColorUtils() { } } diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java index 93dccd83..8e624e01 100644 --- a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TablePlugin.java @@ -123,8 +123,13 @@ public class TablePlugin extends AbstractMarkwonPlugin { visitor.blockStart(tableBlock); + final int length = visitor.length(); + visitor.visitChildren(tableBlock); + // @since $nap; apply table span for the full table + visitor.setSpans(length, new TableSpan()); + visitor.blockEnd(tableBlock); } }) diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableRowSpan.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableRowSpan.java index a90818fb..f6653cf8 100644 --- a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableRowSpan.java +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableRowSpan.java @@ -5,6 +5,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.text.Layout; +import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; import android.text.style.ReplacementSpan; @@ -19,6 +20,8 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import io.noties.markwon.utils.LeadingMarginUtils; + public class TableRowSpan extends ReplacementSpan { public static final int ALIGN_LEFT = 0; @@ -139,16 +142,16 @@ public class TableRowSpan extends ReplacementSpan { int top, int y, int bottom, - @NonNull Paint paint) { + @NonNull Paint p) { if (recreateLayouts(canvas.getWidth())) { width = canvas.getWidth(); // @since $nap; it's important to cast to TextPaint in order to display links, etc - if (paint instanceof TextPaint) { + if (p instanceof TextPaint) { // there must be a reason why this method receives Paint instead of TextPaint... - textPaint.set((TextPaint) paint); + textPaint.set((TextPaint) p); } else { - textPaint.set(paint); + textPaint.set(p); } makeNewLayouts(); } @@ -161,28 +164,25 @@ public class TableRowSpan extends ReplacementSpan { final int w = width / size; - // feels like magic... - final int heightDiff = (bottom - top - height) / 4; - // @since 1.1.1 // draw backgrounds { if (header) { - theme.applyTableHeaderRowStyle(this.paint); + theme.applyTableHeaderRowStyle(paint); } else if (odd) { - theme.applyTableOddRowStyle(this.paint); + theme.applyTableOddRowStyle(paint); } else { // even - theme.applyTableEvenRowStyle(this.paint); + theme.applyTableEvenRowStyle(paint); } // if present (0 is transparent) - if (this.paint.getColor() != 0) { + if (paint.getColor() != 0) { final int save = canvas.save(); try { rect.set(0, 0, width, bottom - top); - canvas.translate(x, top - heightDiff); - canvas.drawRect(rect, this.paint); + canvas.translate(x, top); + canvas.drawRect(rect, paint); } finally { canvas.restoreToCount(save); } @@ -192,25 +192,73 @@ public class TableRowSpan extends ReplacementSpan { // @since 1.1.1 reset after applying background color // as background changes color attribute and if not specific tableBorderColor // is specified then after this row all borders will have color of this row (plus alpha) - this.paint.set(paint); - theme.applyTableBorderStyle(this.paint); + paint.set(p); + theme.applyTableBorderStyle(paint); final int borderWidth = theme.tableBorderWidth(paint); final boolean drawBorder = borderWidth > 0; + + // why divided by 4 gives a more or less good result is still not clear (shouldn't it be 2?) + final int heightDiff = (bottom - top - height) / 4; + + // required for borderTop calculation + final boolean isFirstTableRow; + + // @since $nap; if (drawBorder) { - rect.set(0, 0, w, bottom - top); + boolean first = false; + // only if first draw the line + { + final Spanned spanned = (Spanned) text; + final TableSpan[] spans = spanned.getSpans(start, end, TableSpan.class); + if (spans != null && spans.length > 0) { + final TableSpan span = spans[0]; + if (LeadingMarginUtils.selfStart(start, text, span)) { + first = true; + rect.set((int) x, top, width, top + borderWidth); + canvas.drawRect(rect, paint); + } + } + } + + // draw the line at the bottom + rect.set((int) x, bottom - borderWidth, width, bottom); + canvas.drawRect(rect, paint); + + isFirstTableRow = first; + } else { + isFirstTableRow = false; } + final int borderWidthHalf = borderWidth / 2; + + // to NOT overlap borders inset top and bottom + final int borderTop = isFirstTableRow ? borderWidth : 0; + final int borderBottom = bottom - top - borderWidth; + StaticLayout layout; for (int i = 0; i < size; i++) { layout = layouts.get(i); final int save = canvas.save(); try { - canvas.translate(x + (i * w), top - heightDiff); + canvas.translate(x + (i * w), top); + // @since $nap; if (drawBorder) { - canvas.drawRect(rect, this.paint); + // first vertical border will have full width (it cannot exceed canvas) + if (i == 0) { + rect.set(0, borderTop, borderWidth, borderBottom); + } else { + rect.set(-borderWidthHalf, borderTop, borderWidthHalf, borderBottom); + } + + canvas.drawRect(rect, paint); + + if (i == (size - 1)) { + rect.set(w - borderWidth, borderTop, w, borderBottom); + canvas.drawRect(rect, paint); + } } canvas.translate(padding, padding + heightDiff); diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableSpan.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableSpan.java new file mode 100644 index 00000000..bba40d76 --- /dev/null +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableSpan.java @@ -0,0 +1,7 @@ +package io.noties.markwon.ext.tables; + +/** + * @since $nap; + */ +public class TableSpan { +} diff --git a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableTheme.java b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableTheme.java index f4a1c5cc..48655220 100644 --- a/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableTheme.java +++ b/markwon-ext-tables/src/main/java/io/noties/markwon/ext/tables/TableTheme.java @@ -10,6 +10,7 @@ import androidx.annotation.Px; import io.noties.markwon.utils.ColorUtils; import io.noties.markwon.utils.Dip; +@SuppressWarnings("WeakerAccess") public class TableTheme { @NonNull @@ -101,7 +102,8 @@ public class TableTheme { } paint.setColor(color); - paint.setStyle(Paint.Style.STROKE); + // @since $nap; before it was STROKE... change to FILL as we draw border differently + paint.setStyle(Paint.Style.FILL); } public void applyTableOddRowStyle(@NonNull Paint paint) { diff --git a/sample/src/main/java/io/noties/markwon/sample/table/TableActivity.java b/sample/src/main/java/io/noties/markwon/sample/table/TableActivity.java index a47a13e2..d7153ec6 100644 --- a/sample/src/main/java/io/noties/markwon/sample/table/TableActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/table/TableActivity.java @@ -1,17 +1,22 @@ package io.noties.markwon.sample.table; +import android.graphics.Color; import android.os.Bundle; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import io.noties.debug.Debug; import io.noties.markwon.Markwon; import io.noties.markwon.ext.tables.TablePlugin; +import io.noties.markwon.ext.tables.TableTheme; import io.noties.markwon.linkify.LinkifyPlugin; import io.noties.markwon.sample.ActivityWithMenuOptions; import io.noties.markwon.sample.MenuOptions; import io.noties.markwon.sample.R; +import io.noties.markwon.utils.ColorUtils; +import io.noties.markwon.utils.Dip; public class TableActivity extends ActivityWithMenuOptions { @@ -19,6 +24,7 @@ public class TableActivity extends ActivityWithMenuOptions { @Override public MenuOptions menuOptions() { return MenuOptions.create() + .add("customize", this::customize) .add("tableAndLinkify", this::tableAndLinkify); } @@ -33,6 +39,32 @@ public class TableActivity extends ActivityWithMenuOptions { tableAndLinkify(); } + private void customize() { + final String md = "" + + "| HEADER | HEADER | HEADER |\n" + + "|:----:|:----:|:----:|\n" + + "| 测试 | 测试 | 测试 |\n" + + "| 测试 | 测试 | 测测测12345试测试测试 |\n" + + "| 测试 | 测试 | 123445 |\n" + + "| 测试 | 测试 | (650) 555-1212 |\n" + + "| 测试 | 测试 | [link](#) |\n"; + + final Markwon markwon = Markwon.builder(this) + .usePlugin(TablePlugin.create(builder -> { + final Dip dip = Dip.create(this); + builder + .tableBorderWidth(dip.toPx(2)) + .tableBorderColor(Color.YELLOW) + .tableCellPadding(dip.toPx(4)) + .tableHeaderRowBackgroundColor(ColorUtils.applyAlpha(Color.RED, 80)) + .tableEvenRowBackgroundColor(ColorUtils.applyAlpha(Color.GREEN, 80)) + .tableOddRowBackgroundColor(ColorUtils.applyAlpha(Color.BLUE, 80)); + })) + .build(); + + markwon.setMarkdown(textView, md); + } + private void tableAndLinkify() { final String md = "" + "| HEADER | HEADER | HEADER |\n" + @@ -45,7 +77,7 @@ public class TableActivity extends ActivityWithMenuOptions { "\n" + "测试\n" + "\n" + - "[https://www.baidu.com](https://www.baidu.com)"; + "[link link](https://link.link)"; final Markwon markwon = Markwon.builder(this) .usePlugin(LinkifyPlugin.create())