V1.1.1 (#60)
* Fix OrderedListItemSpan text position (baseline) (#55) * Add softBreakAddsNewLine option for SpannableConfiguration (#54) * Paragraph text can now explicitly be spanned (#58) Thanks to @c-b-h! * Fix table border color if odd background is specified (#56) * Add table customizations (even and header rows)
This commit is contained in:
		
							parent
							
								
									7c7b1f59a8
								
							
						
					
					
						commit
						20159bef7f
					
				| @ -37,7 +37,7 @@ allprojects { | |||||||
| and then in your module `build.gradle`: | and then in your module `build.gradle`: | ||||||
| 
 | 
 | ||||||
| ```groovy | ```groovy | ||||||
| implementation 'ru.noties:markwon:1.1.0-SNAPSHOT' | implementation 'ru.noties:markwon:1.1.1-SNAPSHOT' | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Please note that `markwon-image-loader`, `markwon-syntax` and `markwon-view` are also present in `SNAPSHOT` repository and share the same version as main `markwon` artifact. | Please note that `markwon-image-loader`, `markwon-syntax` and `markwon-view` are also present in `SNAPSHOT` repository and share the same version as main `markwon` artifact. | ||||||
|  | |||||||
| @ -29,6 +29,14 @@ task wrapper(type: Wrapper) { | |||||||
|     distributionType 'all' |     distributionType 'all' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | if (hasProperty('local')) { | ||||||
|  |     if (!hasProperty('LOCAL_MAVEN_URL')) { | ||||||
|  |         throw new RuntimeException('Cannot publish to local maven as no such property exists: LOCAL_MAVEN_URL') | ||||||
|  |     } | ||||||
|  |     ext.RELEASE_REPOSITORY_URL = LOCAL_MAVEN_URL | ||||||
|  |     ext.SNAPSHOT_REPOSITORY_URL = LOCAL_MAVEN_URL | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ext { | ext { | ||||||
| 
 | 
 | ||||||
|     // Config |     // Config | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ org.gradle.configureondemand=true | |||||||
| android.enableBuildCache=true | android.enableBuildCache=true | ||||||
| android.buildCacheDir=build/pre-dex-cache | android.buildCacheDir=build/pre-dex-cache | ||||||
| 
 | 
 | ||||||
| VERSION_NAME=1.1.0 | VERSION_NAME=1.1.1-SNAPSHOT | ||||||
| 
 | 
 | ||||||
| GROUP=ru.noties | GROUP=ru.noties | ||||||
| POM_DESCRIPTION=Markwon | POM_DESCRIPTION=Markwon | ||||||
|  | |||||||
| @ -30,9 +30,5 @@ afterEvaluate { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (hasProperty('release')) { | if (hasProperty('release')) { | ||||||
|     if (hasProperty('local')) { |  | ||||||
|         ext.RELEASE_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|         ext.SNAPSHOT_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|     } |  | ||||||
|     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' |     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' | ||||||
| } | } | ||||||
|  | |||||||
| @ -24,9 +24,5 @@ afterEvaluate { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (hasProperty('release')) { | if (hasProperty('release')) { | ||||||
|     if (hasProperty('local')) { |  | ||||||
|         ext.RELEASE_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|         ext.SNAPSHOT_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|     } |  | ||||||
|     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' |     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,9 +23,5 @@ afterEvaluate { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (hasProperty('release')) { | if (hasProperty('release')) { | ||||||
|     if (hasProperty('local')) { |  | ||||||
|         ext.RELEASE_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|         ext.SNAPSHOT_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|     } |  | ||||||
|     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' |     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,9 +25,5 @@ afterEvaluate { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (hasProperty('release')) { | if (hasProperty('release')) { | ||||||
|     if (hasProperty('local')) { |  | ||||||
|         ext.RELEASE_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|         ext.SNAPSHOT_REPOSITORY_URL = LOCAL_MAVEN_URL |  | ||||||
|     } |  | ||||||
|     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' |     apply from: 'https://raw.githubusercontent.com/noties/gradle-mvn-push/master/gradle-mvn-push-aar.gradle' | ||||||
| } | } | ||||||
|  | |||||||
| @ -32,6 +32,7 @@ public class SpannableConfiguration { | |||||||
|     private final SpannableHtmlParser htmlParser; |     private final SpannableHtmlParser htmlParser; | ||||||
|     private final ImageSizeResolver imageSizeResolver; |     private final ImageSizeResolver imageSizeResolver; | ||||||
|     private final SpannableFactory factory; // @since 1.1.0 |     private final SpannableFactory factory; // @since 1.1.0 | ||||||
|  |     private final boolean softBreakAddsNewLine; // @since 1.1.1 | ||||||
| 
 | 
 | ||||||
|     private SpannableConfiguration(@NonNull Builder builder) { |     private SpannableConfiguration(@NonNull Builder builder) { | ||||||
|         this.theme = builder.theme; |         this.theme = builder.theme; | ||||||
| @ -42,6 +43,7 @@ public class SpannableConfiguration { | |||||||
|         this.htmlParser = builder.htmlParser; |         this.htmlParser = builder.htmlParser; | ||||||
|         this.imageSizeResolver = builder.imageSizeResolver; |         this.imageSizeResolver = builder.imageSizeResolver; | ||||||
|         this.factory = builder.factory; |         this.factory = builder.factory; | ||||||
|  |         this.softBreakAddsNewLine = builder.softBreakAddsNewLine; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
| @ -84,6 +86,15 @@ public class SpannableConfiguration { | |||||||
|         return factory; |         return factory; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @return a flag indicating if soft break should be treated as a hard | ||||||
|  |      * break and thus adding a new line instead of adding a white space | ||||||
|  |      * @since 1.1.1 | ||||||
|  |      */ | ||||||
|  |     public boolean softBreakAddsNewLine() { | ||||||
|  |         return softBreakAddsNewLine; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
|     public static class Builder { |     public static class Builder { | ||||||
| 
 | 
 | ||||||
| @ -95,7 +106,8 @@ public class SpannableConfiguration { | |||||||
|         private UrlProcessor urlProcessor; |         private UrlProcessor urlProcessor; | ||||||
|         private SpannableHtmlParser htmlParser; |         private SpannableHtmlParser htmlParser; | ||||||
|         private ImageSizeResolver imageSizeResolver; |         private ImageSizeResolver imageSizeResolver; | ||||||
|         private SpannableFactory factory; |         private SpannableFactory factory; // @since 1.1.0 | ||||||
|  |         private boolean softBreakAddsNewLine; // @since 1.1.1 | ||||||
| 
 | 
 | ||||||
|         Builder(@NonNull Context context) { |         Builder(@NonNull Context context) { | ||||||
|             this.context = context; |             this.context = context; | ||||||
| @ -155,6 +167,19 @@ public class SpannableConfiguration { | |||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /** | ||||||
|  |          * @param softBreakAddsNewLine a flag indicating if soft break should be treated as a hard | ||||||
|  |          *                             break and thus adding a new line instead of adding a white space | ||||||
|  |          * @return self | ||||||
|  |          * @see <a href="https://spec.commonmark.org/0.28/#soft-line-breaks">spec</a> | ||||||
|  |          * @since 1.1.1 | ||||||
|  |          */ | ||||||
|  |         @NonNull | ||||||
|  |         public Builder softBreakAddsNewLine(boolean softBreakAddsNewLine) { | ||||||
|  |             this.softBreakAddsNewLine = softBreakAddsNewLine; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         public SpannableConfiguration build() { |         public SpannableConfiguration build() { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -56,6 +56,12 @@ public interface SpannableFactory { | |||||||
|             boolean isHeader, |             boolean isHeader, | ||||||
|             boolean isOdd); |             boolean isOdd); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 1.1.1 | ||||||
|  |      */ | ||||||
|  |     @Nullable | ||||||
|  |     Object paragraph(boolean inTightList); | ||||||
|  | 
 | ||||||
|     @Nullable |     @Nullable | ||||||
|     Object image( |     Object image( | ||||||
|             @NonNull SpannableTheme theme, |             @NonNull SpannableTheme theme, | ||||||
|  | |||||||
| @ -103,6 +103,15 @@ public class SpannableFactoryDef implements SpannableFactory { | |||||||
|         return new TableRowSpan(theme, cells, isHeader, isOdd); |         return new TableRowSpan(theme, cells, isHeader, isOdd); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 1.1.1 | ||||||
|  |      */ | ||||||
|  |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     public Object paragraph(boolean inTightList) { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Nullable |     @Nullable | ||||||
|     @Override |     @Override | ||||||
|     public Object image(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull AsyncDrawable.Loader loader, @NonNull ImageSizeResolver imageSizeResolver, @Nullable ImageSize imageSize, boolean replacementTextIsLink) { |     public Object image(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull AsyncDrawable.Loader loader, @NonNull ImageSizeResolver imageSizeResolver, @Nullable ImageSize imageSize, boolean replacementTextIsLink) { | ||||||
|  | |||||||
| @ -256,8 +256,12 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void visit(SoftLineBreak softLineBreak) { |     public void visit(SoftLineBreak softLineBreak) { | ||||||
|         // at first here was a new line, but here should be a space char |         // @since 1.1.1 there is an option to treat soft break as a hard break (thus adding new line) | ||||||
|         builder.append(' '); |         if (configuration.softBreakAddsNewLine()) { | ||||||
|  |             newLine(); | ||||||
|  |         } else { | ||||||
|  |             builder.append(' '); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @ -376,15 +380,18 @@ public class SpannableMarkdownVisitor extends AbstractVisitor { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void visit(Paragraph paragraph) { |     public void visit(Paragraph paragraph) { | ||||||
| 
 |  | ||||||
|         final boolean inTightList = isInTightList(paragraph); |         final boolean inTightList = isInTightList(paragraph); | ||||||
| 
 | 
 | ||||||
|         if (!inTightList) { |         if (!inTightList) { | ||||||
|             newLine(); |             newLine(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         final int length = builder.length(); | ||||||
|         visitChildren(paragraph); |         visitChildren(paragraph); | ||||||
| 
 | 
 | ||||||
|  |         // @since 1.1.1 apply paragraph span | ||||||
|  |         setSpan(length, factory.paragraph(inTightList)); | ||||||
|  | 
 | ||||||
|         if (!inTightList) { |         if (!inTightList) { | ||||||
|             newLine(); |             newLine(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,7 +6,8 @@ import android.support.annotation.NonNull; | |||||||
| abstract class CanvasUtils { | abstract class CanvasUtils { | ||||||
| 
 | 
 | ||||||
|     static float textCenterY(int top, int bottom, @NonNull Paint paint) { |     static float textCenterY(int top, int bottom, @NonNull Paint paint) { | ||||||
|         return (int) (bottom - ((bottom - top) / 2) - ((paint.descent() + paint.ascent()) / 2.F + .5F)); |         // @since 1.1.1 it's `top +` and not `bottom -` | ||||||
|  |         return (int) (top + ((bottom - top) / 2) - ((paint.descent() + paint.ascent()) / 2.F + .5F)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private CanvasUtils() { |     private CanvasUtils() { | ||||||
|  | |||||||
| @ -59,8 +59,7 @@ public class OrderedListItemSpan implements LeadingMarginSpan { | |||||||
|             left = x + (width * dir) + (width - numberWidth); |             left = x + (width * dir) + (width - numberWidth); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         final float numberY = CanvasUtils.textCenterY(top, bottom, p); |         // @since 1.1.1 we are using `baseline` argument to position text | ||||||
| 
 |         c.drawText(number, left, baseline, p); | ||||||
|         c.drawText(number, left, numberY, p); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -206,6 +206,14 @@ public class SpannableTheme { | |||||||
|     // by default paint.color * TABLE_ODD_ROW_DEF_ALPHA |     // by default paint.color * TABLE_ODD_ROW_DEF_ALPHA | ||||||
|     protected final int tableOddRowBackgroundColor; |     protected final int tableOddRowBackgroundColor; | ||||||
| 
 | 
 | ||||||
|  |     // @since 1.1.1 | ||||||
|  |     // by default no background | ||||||
|  |     protected final int tableEventRowBackgroundColor; | ||||||
|  | 
 | ||||||
|  |     // @since 1.1.1 | ||||||
|  |     // by default no background | ||||||
|  |     protected final int tableHeaderRowBackgroundColor; | ||||||
|  | 
 | ||||||
|     // drawable that will be used to render checkbox (should be stateful) |     // drawable that will be used to render checkbox (should be stateful) | ||||||
|     // TaskListDrawable can be used |     // TaskListDrawable can be used | ||||||
|     protected final Drawable taskListDrawable; |     protected final Drawable taskListDrawable; | ||||||
| @ -236,6 +244,8 @@ public class SpannableTheme { | |||||||
|         this.tableBorderColor = builder.tableBorderColor; |         this.tableBorderColor = builder.tableBorderColor; | ||||||
|         this.tableBorderWidth = builder.tableBorderWidth; |         this.tableBorderWidth = builder.tableBorderWidth; | ||||||
|         this.tableOddRowBackgroundColor = builder.tableOddRowBackgroundColor; |         this.tableOddRowBackgroundColor = builder.tableOddRowBackgroundColor; | ||||||
|  |         this.tableEventRowBackgroundColor = builder.tableEvenRowBackgroundColor; | ||||||
|  |         this.tableHeaderRowBackgroundColor = builder.tableHeaderRowBackgroundColor; | ||||||
|         this.taskListDrawable = builder.taskListDrawable; |         this.taskListDrawable = builder.taskListDrawable; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -493,6 +503,23 @@ public class SpannableTheme { | |||||||
|         paint.setStyle(Paint.Style.FILL); |         paint.setStyle(Paint.Style.FILL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 1.1.1 | ||||||
|  |      */ | ||||||
|  |     public void applyTableEvenRowStyle(@NonNull Paint paint) { | ||||||
|  |         // by default to background to even row | ||||||
|  |         paint.setColor(tableEventRowBackgroundColor); | ||||||
|  |         paint.setStyle(Paint.Style.FILL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @since 1.1.1 | ||||||
|  |      */ | ||||||
|  |     public void applyTableHeaderRowStyle(@NonNull Paint paint) { | ||||||
|  |         paint.setColor(tableHeaderRowBackgroundColor); | ||||||
|  |         paint.setStyle(Paint.Style.FILL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @return a Drawable to be used as a checkbox indication in task lists |      * @return a Drawable to be used as a checkbox indication in task lists | ||||||
|      * @since 1.0.1 |      * @since 1.0.1 | ||||||
| @ -530,6 +557,8 @@ public class SpannableTheme { | |||||||
|         private int tableBorderColor; |         private int tableBorderColor; | ||||||
|         private int tableBorderWidth = -1; |         private int tableBorderWidth = -1; | ||||||
|         private int tableOddRowBackgroundColor; |         private int tableOddRowBackgroundColor; | ||||||
|  |         private int tableEvenRowBackgroundColor; // @since 1.1.1 | ||||||
|  |         private int tableHeaderRowBackgroundColor; // @since 1.1.1 | ||||||
|         private Drawable taskListDrawable; |         private Drawable taskListDrawable; | ||||||
| 
 | 
 | ||||||
|         Builder() { |         Builder() { | ||||||
| @ -733,6 +762,24 @@ public class SpannableTheme { | |||||||
|             return this; |             return this; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         /** | ||||||
|  |          * @since 1.1.1 | ||||||
|  |          */ | ||||||
|  |         @NonNull | ||||||
|  |         public Builder tableEvenRowBackgroundColor(@ColorInt int tableEvenRowBackgroundColor) { | ||||||
|  |             this.tableEvenRowBackgroundColor = tableEvenRowBackgroundColor; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * @since 1.1.1 | ||||||
|  |          */ | ||||||
|  |         @NonNull | ||||||
|  |         public Builder tableHeaderRowBackgroundColor(int tableHeaderRowBackgroundColor) { | ||||||
|  |             this.tableHeaderRowBackgroundColor = tableHeaderRowBackgroundColor; | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         /** |         /** | ||||||
|          * Supplied Drawable must be stateful ({@link Drawable#isStateful()} returns true). If a task |          * Supplied Drawable must be stateful ({@link Drawable#isStateful()} returns true). If a task | ||||||
|          * is marked as done, then this drawable will be updated with an {@code int[] { android.R.attr.state_checked }} |          * is marked as done, then this drawable will be updated with an {@code int[] { android.R.attr.state_checked }} | ||||||
|  | |||||||
| @ -157,18 +157,35 @@ public class TableRowSpan extends ReplacementSpan { | |||||||
|         // feels like magic... |         // feels like magic... | ||||||
|         final int heightDiff = (bottom - top - height) / 4; |         final int heightDiff = (bottom - top - height) / 4; | ||||||
| 
 | 
 | ||||||
|         if (odd) { |         // @since 1.1.1 | ||||||
|             final int save = canvas.save(); |         // draw backgrounds | ||||||
|             try { |         { | ||||||
|                 rect.set(0, 0, width, bottom - top); |             if (header) { | ||||||
|  |                 theme.applyTableHeaderRowStyle(this.paint); | ||||||
|  |             } else if (odd) { | ||||||
|                 theme.applyTableOddRowStyle(this.paint); |                 theme.applyTableOddRowStyle(this.paint); | ||||||
|                 canvas.translate(x, top - heightDiff); |             } else { | ||||||
|                 canvas.drawRect(rect, this.paint); |                 // even | ||||||
|             } finally { |                 theme.applyTableEvenRowStyle(this.paint); | ||||||
|                 canvas.restoreToCount(save); |             } | ||||||
|  | 
 | ||||||
|  |             // if present (0 is transparent) | ||||||
|  |             if (this.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); | ||||||
|  |                 } finally { | ||||||
|  |                     canvas.restoreToCount(save); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // @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); |         theme.applyTableBorderStyle(this.paint); | ||||||
| 
 | 
 | ||||||
|         final int borderWidth = theme.tableBorderWidth(paint); |         final int borderWidth = theme.tableBorderWidth(paint); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry
						Dimitry