Finished moving samples to app-sample

This commit is contained in:
Dimitry Ivanov 2020-07-02 17:12:22 +03:00
parent 0b1544feae
commit 12a73d982c
67 changed files with 2326 additions and 64 deletions

View File

@ -5,6 +5,7 @@
#### Added
* `core` - `MovementMethodPlugin.none()`, `MovementMethodPlugin.link()` factory methods
* `core` - `CorePlugin` `hasExplicitMovementMethod` configuration method to **not** add implicit `LinkMovementMethod` in `afterSetText`
* `ext-latex` - `JLatexMathTheme.Padding.of(int,int,int,int)` factory method
#### Changed
* `html` - `SimpleTagHandler` visits children tags if supplied tag is block one ([#235])

12
app-sample/README.md Normal file
View File

@ -0,0 +1,12 @@
# Markwon sample app
## Building
When adding/removing samples _most likely_ a clean build would be required.
First, for annotation processor to create `samples.json`. And secondly,
in order for Android Gradle plugin to bundle resources references via
symbolic links (the `sample.json` itself and `io.noties.markwon.app.samples.*` directory)
```gradle
./gradlew :app-s:clean :app-s:asDe
```

View File

@ -76,6 +76,7 @@ dependencies {
implementation project(':markwon-image-glide')
deps.with {
// implementation it['x-appcompat']
implementation it['x-recycler-view']
implementation it['x-cardview']
implementation it['x-fragment']

View File

@ -1,4 +1,435 @@
[
{
"javaClassName": "io.noties.markwon.app.samples.tasklist.TaskListMutateSample",
"id": "202007184140901",
"title": "GFM task list mutate",
"description": "",
"artifacts": [
"EXT_TASKLIST"
],
"tags": [
"plugin"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.tasklist.TaskListCustomDrawableSample",
"id": "202007184140749",
"title": "GFM task list custom drawable",
"description": "",
"artifacts": [
"EXT_TASKLIST"
],
"tags": [
"plugin"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.tasklist.TaskListCustomColorsSample",
"id": "202007184140536",
"title": "GFM task list custom colors",
"description": "Custom colors for task list extension",
"artifacts": [
"EXT_TASKLIST"
],
"tags": [
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.tasklist.TaskListSample",
"id": "202007184140352",
"title": "GFM task list",
"description": "Github Flavored Markdown (GFM) task list extension",
"artifacts": [
"EXT_TASKLIST"
],
"tags": [
"plugin"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.table.TableLatexSample",
"id": "202007184140041",
"title": "LaTeX inside table",
"description": "Usage of LaTeX formulas inside markdown tables",
"artifacts": [
"EXT_LATEX",
"EXT_TABLES",
"IMAGE"
],
"tags": [
"image"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.table.TableWithImagesSample",
"id": "202007184135932",
"title": "Images inside table",
"description": "Usage of images inside markdown tables",
"artifacts": [
"EXT_TABLES",
"IMAGE"
],
"tags": [
"image"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.table.TableLinkifySample",
"id": "202007184135739",
"title": "Linkify table",
"description": "Automatically linkify markdown content including content inside tables",
"artifacts": [
"EXT_TABLES",
"LINKIFY"
],
"tags": [
"links"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.table.TableCustomizeSample",
"id": "202007184135621",
"title": "Customize table theme",
"description": "",
"artifacts": [
"EXT_TABLES"
],
"tags": [
"theme"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.RecyclerSample",
"id": "202007184101750",
"title": "RecyclerView",
"description": "Usage with `RecyclerView`",
"artifacts": [
"RECYCLER",
"RECYCLER_TABLE"
],
"tags": [
"recycler-view"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.LinkRemoveUnderlineSample",
"id": "202007184101224",
"title": "Remove link underline",
"description": "",
"artifacts": [
"CORE"
],
"tags": [
"links",
"rendering",
"span"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.PrecomputedFutureSample",
"id": "202007184092446",
"title": "PrecomputedFutureTextSetterCompat",
"description": "Usage of `PrecomputedFutureTextSetterCompat` inside a `RecyclerView` with appcompat",
"artifacts": [
"RECYCLER"
],
"tags": [
"precomputed-text",
"recycler-view"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.PrecomputedSample",
"id": "202007184091654",
"title": "PrecomputedTextSetterCompat",
"description": "`TextSetter` to use `PrecomputedTextSetterCompat`",
"artifacts": [
"CORE"
],
"tags": [
"precomputed-text"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.notification.RemoteViewsSample",
"id": "202007184090140",
"title": "RemoteViews in notification",
"description": "Display markdown with platform (system) spans in notification via `RemoteViews`",
"artifacts": [
"CORE"
],
"tags": [
"hack"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.notification.NotificationSample",
"id": "202007183130729",
"title": "Markdown in Notification",
"description": "Proof of concept of using `Markwon` with `android.app.Notification`",
"artifacts": [
"CORE"
],
"tags": [
"hack"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexErrorSample",
"id": "202007183122624",
"title": "LaTeX error handling",
"description": "Log error when parsing LaTeX and display error drawable",
"artifacts": [
"EXT_LATEX"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexThemeSample",
"id": "202007183121528",
"title": "LaTeX theme",
"description": "Sample of theme customization for LaTeX",
"artifacts": [
"EXT_LATEX",
"INLINE_PARSER"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexDefaultTextColorSample",
"id": "202007183120848",
"title": "LaTeX default text color",
"description": "LaTeX will use text color of `TextView` by default",
"artifacts": [
"EXT_LATEX"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexDarkSample",
"id": "202007183094225",
"title": "LaTeX dark",
"description": "LaTeX automatically uses `TextView` text color if not configured explicitly",
"artifacts": [
"EXT_LATEX"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexDifferentTextSizesSample",
"id": "202007183093504",
"title": "LaTeX inline/block different text size",
"description": "",
"artifacts": [
"EXT_LATEX",
"INLINE_PARSER"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexOmegaSample",
"id": "202007183090618",
"title": "LaTeX omega symbol",
"description": "Bug rendering omega symbol in LaTeX",
"artifacts": [
"EXT_LATEX",
"INLINE_PARSER"
],
"tags": [
"known-bug",
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexLegacySample",
"id": "202007183090335",
"title": "LaTeX blocks in legacy mode",
"description": "Sample using _legacy_ LaTeX block parsing (pre `4.3.0` Markwon version)",
"artifacts": [
"EXT_LATEX"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexInlineSample",
"id": "202007183085820",
"title": "LaTeX inline",
"description": "Display LaTeX inline",
"artifacts": [
"EXT_LATEX",
"INLINE_PARSER"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.latex.LatexBlockSample",
"id": "202006182200257",
"title": "LaTex block",
"description": "Render LaTeX block",
"artifacts": [
"EXT_LATEX"
],
"tags": [
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.inlineparsing.InlineParsingTooltipSample",
"id": "202006182195409",
"title": "Tooltip with inline parser",
"description": "",
"artifacts": [
"INLINE_PARSER"
],
"tags": [
"parsing",
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.SimpleExtensionSample",
"id": "202006182194335",
"title": "Delimiter processor simple-ext",
"description": "Custom delimiter processor implemented with a `SimpleExtPlugin`",
"artifacts": [
"SIMPLE_EXT"
],
"tags": [
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.DelimiterProcessorSample",
"id": "202006182194017",
"title": "Custom delimiter processor",
"description": "Custom parsing delimiter processor with `?` character",
"artifacts": [
"CORE"
],
"tags": [
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.html.HtmlDisableSanitizeSample",
"id": "202006182171424",
"title": "Disable HTML",
"description": "Disable HTML via replacing special `\u003c` and `\u003e` symbols",
"artifacts": [
"CORE"
],
"tags": [
"HTML",
"parsing",
"plugin",
"rendering"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.inlineparsing.InlineParsingNoHtmlSample",
"id": "202006182171239",
"title": "Inline parsing exclude HTML",
"description": "",
"artifacts": [
"INLINE_PARSER"
],
"tags": [
"block",
"inline",
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.inlineparsing.InlineParsingNoDefaultsSample",
"id": "202006182170823",
"title": "Inline parsing no defaults",
"description": "Parsing only inline code and disable all the rest",
"artifacts": [
"INLINE_PARSER"
],
"tags": [
"inline",
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.inlineparsing.InlineParsingWithDefaultsSample",
"id": "202006182170723",
"title": "Inline parsing with defaults",
"description": "Parsing with all defaults except links",
"artifacts": [
"INLINE_PARSER"
],
"tags": [
"inline",
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.inlineparsing.InlineParsingDisableCodeSample",
"id": "202006182170607",
"title": "Disable code inline parsing",
"description": "",
"artifacts": [
"INLINE_PARSER"
],
"tags": [
"inline",
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.inlineparsing.InlineParsingLinksOnlySample",
"id": "202006182170412",
"title": "Links only inline parsing",
"description": "",
"artifacts": [
"INLINE_PARSER"
],
"tags": [
"inline",
"parsing"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.image.GlidePlaceholderImageSample",
"id": "202006182170241",
"title": "Glide image with placeholder",
"description": "",
"artifacts": [
"IMAGE_GLIDE"
],
"tags": [
"image"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.image.GlideImageSample",
"id": "202006182170112",
"title": "Glide image",
"description": "",
"artifacts": [
"IMAGE_GLIDE"
],
"tags": [
"image"
]
},
{
"javaClassName": "io.noties.markwon.app.samples.image.ErrorImageSample",
"id": "202006182165828",
@ -71,6 +502,7 @@
"IMAGE"
],
"tags": [
"HTML",
"image",
"rendering"
]
@ -85,6 +517,7 @@
"IMAGE"
],
"tags": [
"HTML",
"rendering"
]
},
@ -97,6 +530,7 @@
"HTML"
],
"tags": [
"HTML",
"rendering"
]
},
@ -110,6 +544,7 @@
"IMAGE"
],
"tags": [
"HTML",
"image",
"rendering"
]
@ -124,6 +559,7 @@
"IMAGE"
],
"tags": [
"HTML",
"image",
"rendering"
]
@ -137,6 +573,7 @@
"HTML"
],
"tags": [
"HTML",
"rendering",
"span"
]
@ -150,6 +587,7 @@
"HTML"
],
"tags": [
"HTML",
"rendering",
"span"
]
@ -163,6 +601,7 @@
"HTML"
],
"tags": [
"HTML",
"rendering",
"span"
]
@ -485,7 +924,7 @@
]
},
{
"javaClassName": "io.noties.markwon.app.samples.ImagesCustomSchemeSample",
"javaClassName": "io.noties.markwon.app.samples.image.ImagesCustomSchemeSample",
"id": "202006181124201",
"title": "Image destination custom scheme",
"description": "Example of handling custom scheme (`https`, `ftp`, `whatever`, etc.) for images destination URLs with `ImagesPlugin`",

View File

@ -31,4 +31,6 @@ object Tags {
const val gif = "GIF"
const val inline = "inline"
const val html = "HTML"
const val knownBug = "known-bug"
const val precomputedText = "precomputed-text"
}

View File

@ -0,0 +1,23 @@
package io.noties.markwon.app.sample.ui
import android.content.Context
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import io.noties.markwon.app.R
abstract class MarkwonRecyclerViewSample : MarkwonSample() {
protected lateinit var context: Context
protected lateinit var recyclerView: RecyclerView
override fun onViewCreated(view: View) {
context = view.context
recyclerView = view.findViewById(R.id.recycler_view)
render()
}
override val layoutResId: Int
get() = R.layout.sample_recycler_view
abstract fun render()
}

View File

@ -2,6 +2,7 @@ package io.noties.markwon.app.sample.ui
import android.content.Context
import android.view.View
import android.widget.ScrollView
import android.widget.TextView
import io.noties.markwon.app.R
@ -9,12 +10,14 @@ import io.noties.markwon.app.R
abstract class MarkwonTextViewSample : MarkwonSample() {
protected lateinit var context: Context
protected lateinit var scrollView: ScrollView
protected lateinit var textView: TextView
override val layoutResId: Int = R.layout.sample_text_view
override fun onViewCreated(view: View) {
context = view.context
scrollView = view.findViewById(R.id.scroll_view)
textView = view.findViewById(R.id.text_view)
render()
}

View File

@ -54,10 +54,10 @@ class SampleListFragment : Fragment() {
private val sampleManager: SampleManager
get() = App.sampleManager
override fun onAttach(context: Context?) {
override fun onAttach(context: Context) {
super.onAttach(context)
context?.also {
context.also {
markwon = markwon(it)
}
}

View File

@ -0,0 +1,83 @@
package io.noties.markwon.app.samples;
import androidx.annotation.NonNull;
import org.commonmark.node.Emphasis;
import org.commonmark.node.Node;
import org.commonmark.node.Text;
import org.commonmark.parser.Parser;
import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.parser.delimiter.DelimiterRun;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202006182194017",
title = "Custom delimiter processor",
description = "Custom parsing delimiter processor with `?` character",
artifacts = MarkwonArtifact.CORE,
tags = Tags.parsing
)
public class DelimiterProcessorSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"?hello? there!";
final Markwon markwon = Markwon.builder(context)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureParser(@NonNull Parser.Builder builder) {
builder.customDelimiterProcessor(new QuestionDelimiterProcessor());
}
})
.build();
markwon.setMarkdown(textView, md);
}
}
class QuestionDelimiterProcessor implements DelimiterProcessor {
@Override
public char getOpeningCharacter() {
return '?';
}
@Override
public char getClosingCharacter() {
return '?';
}
@Override
public int getMinLength() {
return 1;
}
@Override
public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) {
if (opener.length() >= 1 && closer.length() >= 1) {
return 1;
}
return 0;
}
@Override
public void process(Text opener, Text closer, int delimiterUse) {
final Node node = new Emphasis();
Node tmp = opener.getNext();
while (tmp != null && tmp != closer) {
Node next = tmp.getNext();
node.appendChild(tmp);
tmp = next;
}
opener.insertAfter(node);
}
}

View File

@ -21,7 +21,7 @@ import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
artifacts = MarkwonArtifact.CORE,
tags = {Tags.spacing, Tags.padding, Tags.spacing, Tags.rendering}
)
class HeadingNoSpaceSample extends MarkwonTextViewSample {
public class HeadingNoSpaceSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +

View File

@ -0,0 +1,49 @@
package io.noties.markwon.app.samples;
import android.text.TextPaint;
import android.text.style.CharacterStyle;
import android.text.style.UpdateAppearance;
import androidx.annotation.NonNull;
import org.commonmark.node.Link;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184101224",
title = "Remove link underline",
artifacts = MarkwonArtifact.CORE,
tags = {Tags.links, Tags.rendering, Tags.span}
)
public class LinkRemoveUnderlineSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"There are a lot of [links](#) [here](#)";
final Markwon markwon = Markwon.builder(context)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder.appendFactory(Link.class, (configuration, props) -> new RemoveUnderlineSpan());
}
})
.build();
markwon.setMarkdown(textView, md);
}
}
class RemoveUnderlineSpan extends CharacterStyle implements UpdateAppearance {
@Override
public void updateDrawState(TextPaint tp) {
tp.setUnderlineText(false);
}
}

View File

@ -0,0 +1,75 @@
package io.noties.markwon.app.samples;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonConfiguration;
import io.noties.markwon.PrecomputedFutureTextSetterCompat;
import io.noties.markwon.app.R;
import io.noties.markwon.app.readme.GithubImageDestinationProcessor;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonRecyclerViewSample;
import io.noties.markwon.app.utils.SampleUtilsKtKt;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.image.ImagesPlugin;
import io.noties.markwon.recycler.MarkwonAdapter;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184092446",
title = "PrecomputedFutureTextSetterCompat",
description = "Usage of `PrecomputedFutureTextSetterCompat` " +
"inside a `RecyclerView` with appcompat",
artifacts = {MarkwonArtifact.RECYCLER},
tags = {Tags.recyclerView, Tags.precomputedText}
)
public class PrecomputedFutureSample extends MarkwonRecyclerViewSample {
@Override
public void render() {
if (!hasAppCompat()) {
/*
PLEASE COMPILE WITH `APPCOMPAT` dependency
*/
return;
}
final String md = SampleUtilsKtKt.loadReadMe(context);
final Markwon markwon = Markwon.builder(context)
.textSetter(PrecomputedFutureTextSetterCompat.create())
.usePlugin(ImagesPlugin.create())
.usePlugin(TablePlugin.create(context))
.usePlugin(TaskListPlugin.create(context))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.imageDestinationProcessor(new GithubImageDestinationProcessor());
}
})
.build();
final MarkwonAdapter adapter = MarkwonAdapter
.createTextViewIsRoot(R.layout.adapter_appcompat_default_entry);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(adapter);
adapter.setMarkdown(markwon, md);
adapter.notifyDataSetChanged();
}
private static boolean hasAppCompat() {
try {
Class.forName("androidx.appcompat.widget.AppCompatTextView");
return true;
} catch (Throwable t) {
return false;
}
}
}

View File

@ -0,0 +1,32 @@
package io.noties.markwon.app.samples;
import java.util.concurrent.Executors;
import io.noties.markwon.Markwon;
import io.noties.markwon.PrecomputedTextSetterCompat;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184091654",
title = "PrecomputedTextSetterCompat",
description = "`TextSetter` to use `PrecomputedTextSetterCompat`",
artifacts = MarkwonArtifact.CORE,
tags = Tags.precomputedText
)
public class PrecomputedSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# Heading\n" +
"**bold** some precomputed spans via `PrecomputedTextSetterCompat`";
final Markwon markwon = Markwon.builder(context)
.textSetter(PrecomputedTextSetterCompat.create(Executors.newCachedThreadPool()))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,84 @@
package io.noties.markwon.app.samples;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.node.FencedCodeBlock;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonConfiguration;
import io.noties.markwon.MarkwonVisitor;
import io.noties.markwon.app.R;
import io.noties.markwon.app.readme.GithubImageDestinationProcessor;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonRecyclerViewSample;
import io.noties.markwon.app.utils.SampleUtilsKtKt;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.html.HtmlPlugin;
import io.noties.markwon.image.ImagesPlugin;
import io.noties.markwon.recycler.MarkwonAdapter;
import io.noties.markwon.recycler.SimpleEntry;
import io.noties.markwon.recycler.table.TableEntry;
import io.noties.markwon.recycler.table.TableEntryPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184101750",
title = "RecyclerView",
description = "Usage with `RecyclerView`",
artifacts = {MarkwonArtifact.RECYCLER, MarkwonArtifact.RECYCLER_TABLE},
tags = Tags.recyclerView
)
public class RecyclerSample extends MarkwonRecyclerViewSample {
@Override
public void render() {
final String md = SampleUtilsKtKt.loadReadMe(context);
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create())
.usePlugin(TableEntryPlugin.create(context))
.usePlugin(HtmlPlugin.create())
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TaskListPlugin.create(context))
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.imageDestinationProcessor(new GithubImageDestinationProcessor());
}
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.on(FencedCodeBlock.class, (visitor, fencedCodeBlock) -> {
// we actually won't be applying code spans here, as our custom view will
// draw background and apply mono typeface
//
// NB the `trim` operation on literal (as code will have a new line at the end)
final CharSequence code = visitor.configuration()
.syntaxHighlight()
.highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim());
visitor.builder().append(code);
});
}
})
.build();
final MarkwonAdapter adapter = MarkwonAdapter.builderTextViewIsRoot(R.layout.adapter_node)
.include(FencedCodeBlock.class, SimpleEntry.create(R.layout.adapter_node_code_block, R.id.text_view))
.include(TableBlock.class, TableEntry.create(builder -> {
builder
.tableLayout(R.layout.adapter_node_table_block, R.id.table_layout)
.textLayoutIsRoot(R.layout.view_table_entry_cell);
}))
.build();
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(adapter);
adapter.setMarkdown(markwon, md);
adapter.notifyDataSetChanged();
}
}

View File

@ -0,0 +1,47 @@
package io.noties.markwon.app.samples;
import android.graphics.Color;
import android.text.style.ForegroundColorSpan;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.core.spans.EmphasisSpan;
import io.noties.markwon.core.spans.StrongEmphasisSpan;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import io.noties.markwon.simple.ext.SimpleExtPlugin;
@MarkwonSampleInfo(
id = "202006182194335",
title = "Delimiter processor simple-ext",
description = "Custom delimiter processor implemented with a `SimpleExtPlugin`",
artifacts = MarkwonArtifact.SIMPLE_EXT,
tags = Tags.parsing
)
public class SimpleExtensionSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# SimpleExt\n" +
"\n" +
"+let's start with `+`, ??then we can use this, and finally @@this$$??+";
;
final Markwon markwon = Markwon.builder(context)
.usePlugin(SimpleExtPlugin.create(plugin -> {
plugin
.addExtension(1, '+', (configuration, props) -> new EmphasisSpan())
.addExtension(2, '?', (configuration, props) -> new StrongEmphasisSpan())
.addExtension(
2,
'@',
'?',
(configuration, props) -> new ForegroundColorSpan(Color.RED)
);
}))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -14,7 +14,7 @@ import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
artifacts = MarkwonArtifact.CORE,
tags = {Tags.newLine, Tags.softBreak}
)
class SoftBreakAddsNewLineSample extends MarkwonTextViewSample {
public class SoftBreakAddsNewLineSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +

View File

@ -9,7 +9,7 @@ import androidx.annotation.NonNull;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.core.spans.StrongEmphasisSpan;
import io.noties.markwon.editor.AbstractEditHandler;
import io.noties.markwon.editor.MarkwonEditor;

View File

@ -2,7 +2,7 @@ package io.noties.markwon.app.samples.editor;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.editor.MarkwonEditor;
import io.noties.markwon.editor.MarkwonEditorTextWatcher;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;

View File

@ -4,7 +4,7 @@ import android.text.style.ForegroundColorSpan;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.editor.MarkwonEditor;
import io.noties.markwon.editor.MarkwonEditorTextWatcher;
import io.noties.markwon.sample.annotations.MarkwonArtifact;

View File

@ -4,7 +4,7 @@ import java.util.concurrent.Executors;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.HeadingEditHandler;
import io.noties.markwon.editor.MarkwonEditor;
import io.noties.markwon.editor.MarkwonEditorTextWatcher;

View File

@ -13,7 +13,7 @@ import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.SoftBreakAddsNewLinePlugin;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.BlockQuoteEditHandler;
import io.noties.markwon.app.samples.editor.shared.CodeEditHandler;
import io.noties.markwon.app.samples.editor.shared.LinkEditHandler;

View File

@ -12,7 +12,7 @@ import java.util.regex.Pattern;
import io.noties.debug.Debug;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.editor.MarkwonEditor;
import io.noties.markwon.editor.MarkwonEditorTextWatcher;
import io.noties.markwon.sample.annotations.MarkwonArtifact;

View File

@ -4,7 +4,7 @@ import java.util.concurrent.Executors;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.editor.MarkwonEditor;
import io.noties.markwon.editor.MarkwonEditorTextWatcher;
import io.noties.markwon.sample.annotations.MarkwonArtifact;

View File

@ -2,7 +2,7 @@ package io.noties.markwon.app.samples.editor;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonEditTextSample;
import io.noties.markwon.app.samples.editor.shared.MarkwonEditTextSample;
import io.noties.markwon.editor.MarkwonEditor;
import io.noties.markwon.editor.MarkwonEditorTextWatcher;
import io.noties.markwon.sample.annotations.MarkwonArtifact;

View File

@ -1,4 +1,4 @@
package io.noties.markwon.app.sample.ui
package io.noties.markwon.app.samples.editor.shared
import android.content.Context
import android.text.SpannableStringBuilder
@ -9,6 +9,7 @@ import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import io.noties.markwon.app.R
import io.noties.markwon.app.sample.ui.MarkwonSample
import io.noties.markwon.core.spans.EmphasisSpan
import io.noties.markwon.core.spans.StrongEmphasisSpan
import java.util.ArrayList
@ -19,7 +20,7 @@ abstract class MarkwonEditTextSample : MarkwonSample() {
protected lateinit var editText: EditText
override val layoutResId: Int
get() = R.layout.activity_edit_text
get() = R.layout.sample_edit_text
override fun onViewCreated(view: View) {
context = view.context

View File

@ -28,14 +28,15 @@ public class ErrorImageSample extends MarkwonTextViewSample {
final Markwon markwon = Markwon.builder(context)
// error handler additionally allows to log/inspect errors during image loading
.usePlugin(ImagesPlugin.create(plugin ->
.usePlugin(ImagesPlugin.create(plugin -> {
plugin.errorHandler(new ImagesPlugin.ErrorHandler() {
@Nullable
@Override
public Drawable handleError(@NonNull String url, @NonNull Throwable throwable) {
return ContextCompat.getDrawable(context, R.drawable.ic_home_black_36dp);
}
})))
});
}))
.build();
markwon.setMarkdown(textView, md);

View File

@ -23,8 +23,9 @@ public class GifImageSample extends MarkwonTextViewSample {
final Markwon markwon = Markwon.builder(context)
// GIF is handled by default if library is used in the app
// .usePlugin(ImagesPlugin.create())
.usePlugin(ImagesPlugin.create(plugin ->
plugin.addMediaDecoder(GifMediaDecoder.create())))
.usePlugin(ImagesPlugin.create(plugin -> {
plugin.addMediaDecoder(GifMediaDecoder.create());
}))
.build();
markwon.setMarkdown(textView, md);

View File

@ -1,4 +1,4 @@
package io.noties.markwon.app.samples;
package io.noties.markwon.app.samples.image;
import android.net.Uri;

View File

@ -28,7 +28,7 @@ public class PlaceholderImageSample extends MarkwonTextViewSample {
"![image](https://github.com/dcurtis/markdown-mark/raw/master/png/1664x1024-solid.png)";
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(plugin ->
.usePlugin(ImagesPlugin.create(plugin -> {
plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() {
@Nullable
@Override
@ -37,7 +37,8 @@ public class PlaceholderImageSample extends MarkwonTextViewSample {
// otherwise bounds can be applied explicitly
return ContextCompat.getDrawable(context, R.drawable.ic_android_black_24dp);
}
})))
});
}))
.build();
markwon.setMarkdown(textView, md);

View File

@ -25,11 +25,12 @@ public class SvgImageSample extends MarkwonTextViewSample {
// libraries are in path (specified in dependencies block)
// .usePlugin(ImagesPlugin.create())
// let's make it implicit
.usePlugin(ImagesPlugin.create(plugin ->
.usePlugin(ImagesPlugin.create(plugin -> {
// there 2 svg media decoders:
// - regular `SvgMediaDecoder`
// - special one when SVG doesn't have width and height specified - `SvgPictureMediaDecoder`
plugin.addMediaDecoder(SvgPictureMediaDecoder.create())))
plugin.addMediaDecoder(SvgPictureMediaDecoder.create());
}))
.build();
markwon.setMarkdown(textView, md);

View File

@ -3,15 +3,11 @@ package io.noties.markwon.app.samples.inlineparsing;
import androidx.annotation.NonNull;
import org.commonmark.node.Block;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.Heading;
import org.commonmark.node.HtmlBlock;
import org.commonmark.node.ListBlock;
import org.commonmark.node.ThematicBreak;
import org.commonmark.node.FencedCodeBlock;
import org.commonmark.node.IndentedCodeBlock;
import org.commonmark.parser.InlineParserFactory;
import org.commonmark.parser.Parser;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@ -19,6 +15,7 @@ import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.core.CorePlugin;
import io.noties.markwon.inlineparser.BackticksInlineProcessor;
import io.noties.markwon.inlineparser.MarkwonInlineParser;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
@ -49,16 +46,12 @@ public class InlineParsingDisableCodeSample extends MarkwonTextViewSample {
.excludeInlineProcessor(BackticksInlineProcessor.class)
.build();
// unfortunately there is no _exclude_ method for parser-builder
final Set<Class<? extends Block>> enabledBlocks = new HashSet<Class<? extends Block>>() {{
// IndentedCodeBlock.class and FencedCodeBlock.class are missing
// this is full list (including above) that can be passed to `enabledBlockTypes` method
addAll(Arrays.asList(
BlockQuote.class,
Heading.class,
HtmlBlock.class,
ThematicBreak.class,
ListBlock.class));
addAll(CorePlugin.enabledBlockTypes());
remove(FencedCodeBlock.class);
remove(IndentedCodeBlock.class);
}};
final Markwon markwon = Markwon.builder(context)

View File

@ -14,7 +14,8 @@ import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202006182170823",
title = "Inline parsing with defaults",
title = "Inline parsing no defaults",
description = "Parsing only inline code and disable all the rest",
artifacts = MarkwonArtifact.INLINE_PARSER,
tags = {Tags.inline, Tags.parsing}
)

View File

@ -0,0 +1,202 @@
package io.noties.markwon.app.samples.inlineparsing;
import android.app.Activity;
import android.graphics.Point;
import android.text.Layout;
import android.text.Spannable;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.commonmark.node.CustomNode;
import org.commonmark.node.Node;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonVisitor;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.inlineparser.InlineProcessor;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202006182195409",
title = "Tooltip with inline parser",
artifacts = MarkwonArtifact.INLINE_PARSER,
tags = {Tags.parsing, Tags.rendering}
)
public class InlineParsingTooltipSample extends MarkwonTextViewSample {
@Override
public void render() {
// NB! tooltip contents cannot have new lines
final String md = "" +
"\n" +
"\n" +
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vitae enim ut sem aliquet ultrices. Nunc a accumsan orci. Suspendisse tortor ante, lacinia ac scelerisque sed, dictum eget metus. Morbi ante augue, tristique eget quam in, vestibulum rutrum lacus. Nulla aliquam auctor cursus. Nulla at lacus condimentum, viverra lacus eget, sollicitudin ex. Cras efficitur leo dui, sit amet rutrum tellus venenatis et. Sed in facilisis libero. Etiam ultricies, nulla ut venenatis tincidunt, tortor erat tristique ante, non aliquet massa arcu eget nisl. Etiam gravida erat ante, sit amet lobortis mauris commodo nec. Praesent vitae sodales quam. Vivamus condimentum porta suscipit. Donec posuere id felis ac scelerisque. Vestibulum lacinia et leo id lobortis. Sed vitae dolor nec ligula dapibus finibus vel eu libero. Nam tincidunt maximus elit, sit amet tincidunt lacus laoreet malesuada.\n" +
"\n" +
"Aenean at urna leo. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla facilisi. Integer lectus elit, congue a orci sed, dignissim sagittis sem. Aenean et pretium magna, nec ornare justo. Sed quis nunc blandit, luctus justo eget, pellentesque arcu. Pellentesque porta semper tristique. Donec et odio arcu. Nullam ultrices gravida congue. Praesent vel leo sed orci tempor luctus. Vivamus eget tortor arcu. Nullam sapien nulla, iaculis sit amet semper in, mattis nec metus. In porttitor augue id elit euismod mattis. Ut est justo, dapibus suscipit erat eu, pellentesque porttitor magna.\n" +
"\n" +
"Nunc porta orci eget dictum malesuada. Donec vehicula felis sit amet leo tincidunt placerat. Cras quis elit faucibus, porta elit at, sodales tortor. Donec elit mi, eleifend et maximus vitae, pretium varius velit. Integer maximus egestas urna, at semper augue egestas vitae. Phasellus arcu tellus, tincidunt eget tellus nec, hendrerit mollis mauris. Pellentesque commodo urna quis nisi ultrices, quis vehicula felis ultricies. Vivamus eu feugiat leo.\n" +
"\n" +
"Etiam sit amet lorem et eros suscipit rhoncus a a tellus. Sed pharetra dui purus, quis molestie leo congue nec. Suspendisse sed scelerisque quam. Vestibulum non laoreet felis. Fusce interdum euismod purus at scelerisque. Vivamus tempus varius nibh, sed accumsan nisl interdum non. Pellentesque rutrum egestas eros sit amet sollicitudin. Vivamus ultrices est erat. Curabitur gravida justo non felis euismod mollis. Ut porta finibus nulla, sed pellentesque purus euismod ac.\n" +
"\n" +
"Aliquam erat volutpat. Nullam suscipit sit amet tortor vel fringilla. Nulla facilisi. Nullam lacinia ex lacus, sit amet scelerisque justo semper a. Nullam ullamcorper, erat ac malesuada porta, augue erat sagittis mi, in auctor turpis mauris nec orci. Nunc sit amet felis placerat, pharetra diam nec, dapibus metus. Proin nulla orci, iaculis vitae vulputate vel, placerat ac erat. Morbi sit amet blandit velit. Cras consectetur vehicula lacus vel sagittis. Nunc tincidunt lacus in blandit faucibus. Curabitur vestibulum auctor vehicula. Sed quis ligula sit amet quam venenatis venenatis eget id felis. Maecenas feugiat nisl elit, facilisis tempus risus malesuada quis. " +
"# Hello tooltip!\n\n" +
"This is the !{tooltip label}(and actual content comes here)\n\n" +
"what if it is !{here}(The contents can be blocks, limited though) instead?\n\n" +
"![image](#) anyway";
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create(factoryBuilder ->
factoryBuilder.addInlineProcessor(new TooltipInlineProcessor())))
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) {
builder.on(TooltipNode.class, (visitor, tooltipNode) -> {
final int start = visitor.length();
visitor.builder().append(tooltipNode.label);
visitor.setSpans(start, new TooltipSpan(tooltipNode.contents));
});
}
})
.build();
markwon.setMarkdown(textView, md);
}
}
class TooltipInlineProcessor extends InlineProcessor {
// NB! without bang
// `\\{` is required (although marked as redundant), without it - runtime crash
@SuppressWarnings("RegExpRedundantEscape")
private static final Pattern RE = Pattern.compile("\\{(.+?)\\}\\((.+?)\\)");
@Override
public char specialCharacter() {
return '!';
}
@Nullable
@Override
protected Node parse() {
final String match = match(RE);
if (match == null) {
return null;
}
final Matcher matcher = RE.matcher(match);
if (matcher.matches()) {
final String label = matcher.group(1);
final String contents = matcher.group(2);
return new TooltipNode(label, contents);
}
return null;
}
}
class TooltipNode extends CustomNode {
final String label;
final String contents;
TooltipNode(@NonNull String label, @NonNull String contents) {
this.label = label;
this.contents = contents;
}
}
class TooltipSpan extends ClickableSpan {
final String contents;
TooltipSpan(@NonNull String contents) {
this.contents = contents;
}
@Override
public void onClick(@NonNull View widget) {
// just to be safe
if (!(widget instanceof TextView)) {
return;
}
final TextView textView = (TextView) widget;
final Spannable spannable = (Spannable) textView.getText();
// find self ending position (can also obtain start)
// final int start = spannable.getSpanStart(this);
final int end = spannable.getSpanEnd(this);
// weird, didn't find self
if (/*start < 0 ||*/ end < 0) {
return;
}
final Layout layout = textView.getLayout();
if (layout == null) {
// also weird
return;
}
final int line = layout.getLineForOffset(end);
// position inside TextView, these values must also be adjusted to parent widget
// also note that container can
final int y = layout.getLineBottom(line);
final int x = (int) (layout.getPrimaryHorizontal(end) + 0.5F);
final Window window = ((Activity) widget.getContext()).getWindow();
final View decor = window.getDecorView();
final Point point = relativeTo(decor, widget);
// new Tooltip.Builder(widget.getContext())
// .anchor(x + point.x, y + point.y)
// .text(contents)
// .create()
// .show(widget, Tooltip.Gravity.TOP, false);
// Toast is not reliable when tried to position on the screen
// but anyway, this is to showcase only
final Toast toast = Toast.makeText(widget.getContext(), contents, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP | Gravity.START, x + point.x, y + point.y);
toast.show();
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
// can customize appearance here as spans will be rendered as links
super.updateDrawState(ds);
}
@NonNull
private static Point relativeTo(@NonNull View parent, @NonNull View who) {
return relativeTo(parent, who, new Point());
}
@NonNull
private static Point relativeTo(@NonNull View parent, @NonNull View who, @NonNull Point point) {
// NB! the scroll adjustments (we are interested in screen position,
// not real position inside parent)
point.x += who.getLeft();
point.y += who.getTop();
point.x -= who.getScrollX();
point.y -= who.getScrollY();
if (who != parent
&& who.getParent() instanceof View) {
relativeTo(parent, (View) who.getParent(), point);
}
return point;
}
}

View File

@ -14,6 +14,7 @@ import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202006182170723",
title = "Inline parsing with defaults",
description = "Parsing with all defaults except links",
artifacts = MarkwonArtifact.INLINE_PARSER,
tags = {Tags.inline, Tags.parsing}
)

View File

@ -0,0 +1,33 @@
package io.noties.markwon.app.samples.latex;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.latex.shared.LatexHolder;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202006182200257",
title = "LaTex block",
description = "Render LaTeX block",
artifacts = MarkwonArtifact.EXT_LATEX,
tags = {Tags.rendering}
)
public class LatexBlockSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# LaTeX\n" +
"$$\n" +
"" + LatexHolder.LATEX_ARRAY + "\n" +
"$$";
final Markwon markwon = Markwon.builder(context)
.usePlugin(JLatexMathPlugin.create(textView.getTextSize()))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,37 @@
package io.noties.markwon.app.samples.latex;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183094225",
title = "LaTeX dark",
description = "LaTeX automatically uses `TextView` text color " +
"if not configured explicitly",
artifacts = MarkwonArtifact.EXT_LATEX,
tags = Tags.rendering
)
public class LatexDarkSample extends MarkwonTextViewSample {
@Override
public void render() {
scrollView.setBackgroundColor(0xFF000000);
textView.setTextColor(0xFFffffff);
final String md = "" +
"# LaTeX\n" +
"$$\n" +
"\\int \\frac{1}{x} dx = \\ln \\left| x \\right| + C\n" +
"$$\n" +
"text color is taken from text";
final Markwon markwon = Markwon.builder(context)
.usePlugin(JLatexMathPlugin.create(textView.getTextSize()))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,40 @@
package io.noties.markwon.app.samples.latex;
import android.graphics.Color;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.latex.shared.LatexHolder;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183120848",
title = "LaTeX default text color",
description = "LaTeX will use text color of `TextView` by default",
artifacts = MarkwonArtifact.EXT_LATEX,
tags = Tags.rendering
)
public class LatexDefaultTextColorSample extends MarkwonTextViewSample {
@Override
public void render() {
// @since 4.3.0 text color is automatically taken from textView
// (if it's not specified explicitly via configuration)
textView.setTextColor(Color.RED);
final String md = "" +
"# LaTeX default text color\n" +
"$$\n" +
"" + LatexHolder.LATEX_LONG_DIVISION + "\n" +
"$$\n" +
"";
final Markwon markwon = Markwon.builder(context)
.usePlugin(JLatexMathPlugin.create(textView.getTextSize()))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,42 @@
package io.noties.markwon.app.samples.latex;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.latex.shared.LatexHolder;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183093504",
title = "LaTeX inline/block different text size",
artifacts = {MarkwonArtifact.EXT_LATEX, MarkwonArtifact.INLINE_PARSER},
tags = {Tags.rendering}
)
public class LatexDifferentTextSizesSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# LaTeX different text sizes\n" +
"inline: " + LatexHolder.LATEX_BANGLE + ", okay and block:\n" +
"$$\n" +
"" + LatexHolder.LATEX_BANGLE + "\n" +
"$$\n" +
"that's it";
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(JLatexMathPlugin.create(
textView.getTextSize() * 0.75F,
textView.getTextSize() * 1.50F,
builder -> {
builder.inlinesEnabled(true);
}
))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,54 @@
package io.noties.markwon.app.samples.latex;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import io.noties.debug.Debug;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.R;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183122624",
title = "LaTeX error handling",
description = "Log error when parsing LaTeX and display error drawable",
artifacts = MarkwonArtifact.EXT_LATEX,
tags = Tags.rendering
)
public class LatexErrorSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# LaTeX with error\n" +
"$$\n" +
"\\sum_{i=0}^\\infty x \\cdot 0 \\rightarrow \\iMightNotExist{0}\n" +
"$$\n\n" +
"must **not** be rendered";
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
builder.inlinesEnabled(true);
//noinspection Convert2Lambda
builder.errorHandler(new JLatexMathPlugin.ErrorHandler() {
@Nullable
@Override
public Drawable handleError(@Nullable String latex, @NonNull Throwable error) {
Debug.e(error, latex);
return ContextCompat.getDrawable(context, R.drawable.ic_android_black_24dp);
}
});
}))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,37 @@
package io.noties.markwon.app.samples.latex;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.latex.shared.LatexHolder;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183085820",
title = "LaTeX inline",
description = "Display LaTeX inline",
artifacts = {MarkwonArtifact.EXT_LATEX, MarkwonArtifact.INLINE_PARSER},
tags = Tags.rendering
)
public class LatexInlineSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# LaTeX inline\n" +
"hey = $$" + LatexHolder.LATEX_BANGLE + "$$,\n" +
"that's it!";
// inlines must be explicitly enabled and require `MarkwonInlineParserPlugin`
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
builder.inlinesEnabled(true);
}))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,37 @@
package io.noties.markwon.app.samples.latex;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.latex.shared.LatexHolder;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183090335",
title = "LaTeX blocks in legacy mode",
description = "Sample using _legacy_ LaTeX block parsing (pre `4.3.0` Markwon version)",
artifacts = MarkwonArtifact.EXT_LATEX,
tags = Tags.rendering
)
public class LatexLegacySample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# LaTeX legacy\n" +
"There are no inlines in previous versions, only blocks:\n" +
"$$\n" +
"" + LatexHolder.LATEX_BOXES + "\n" +
"$$\n" +
"yeah";
final Markwon markwon = Markwon.builder(context)
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
builder.blocksLegacy(true);
}))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,38 @@
package io.noties.markwon.app.samples.latex;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183090618",
title = "LaTeX omega symbol",
description = "Bug rendering omega symbol in LaTeX",
artifacts = {MarkwonArtifact.EXT_LATEX, MarkwonArtifact.INLINE_PARSER},
tags = {Tags.rendering, Tags.knownBug}
)
public class LatexOmegaSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# Block\n\n" +
"$$\n" +
"\\Omega\n" +
"$$\n\n" +
"# Inline\n\n" +
"$$\\Omega$$";
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
builder.inlinesEnabled(true);
}))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,53 @@
package io.noties.markwon.app.samples.latex;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.latex.shared.LatexHolder;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.ext.latex.JLatexMathTheme;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183121528",
title = "LaTeX theme",
description = "Sample of theme customization for LaTeX",
artifacts = {MarkwonArtifact.EXT_LATEX, MarkwonArtifact.INLINE_PARSER},
tags = Tags.rendering
)
public class LatexThemeSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# LaTeX theme\n" +
"Hello there $$" + LatexHolder.LATEX_BANGLE + "$$, how was it?" +
"Now, what about a _different_ approach and block:\n\n" +
"$$\n" +
"" + LatexHolder.LATEX_LONG_DIVISION + "\n" +
"$$\n\n" +
"Seems **fine**";
final int blockPadding = (int) (16 * context.getResources().getDisplayMetrics().density + 0.5F);
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> {
builder.inlinesEnabled(true);
builder.theme()
.inlineBackgroundProvider(() -> new ColorDrawable(0x200000ff))
.inlineTextColor(Color.GREEN)
.blockBackgroundProvider(() -> new ColorDrawable(0x2000ff00))
.blockPadding(JLatexMathTheme.Padding.all(blockPadding))
.blockTextColor(Color.RED)
;
}))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,35 @@
package io.noties.markwon.app.samples.latex.shared;
public abstract class LatexHolder {
public static final String LATEX_ARRAY;
public static final String LATEX_LONG_DIVISION = "\\text{A long division \\longdiv{12345}{13}";
public static final String LATEX_BANGLE = "{a \\bangle b} {c \\brace d} {e \\brack f} {g \\choose h}";
public static final String LATEX_BOXES;
static {
String latex = "\\begin{array}{cc}";
latex += "\\fbox{\\text{A framed box with \\textdbend}}&\\shadowbox{\\text{A shadowed box}}\\cr";
latex += "\\doublebox{\\text{A double framed box}}&\\ovalbox{\\text{An oval framed box}}\\cr";
latex += "\\end{array}";
LATEX_BOXES = latex;
}
static {
String latex = "\\begin{array}{l}";
latex += "\\forall\\varepsilon\\in\\mathbb{R}_+^*\\ \\exists\\eta>0\\ |x-x_0|\\leq\\eta\\Longrightarrow|f(x)-f(x_0)|\\leq\\varepsilon\\\\";
latex += "\\det\\begin{bmatrix}a_{11}&a_{12}&\\cdots&a_{1n}\\\\a_{21}&\\ddots&&\\vdots\\\\\\vdots&&\\ddots&\\vdots\\\\a_{n1}&\\cdots&\\cdots&a_{nn}\\end{bmatrix}\\overset{\\mathrm{def}}{=}\\sum_{\\sigma\\in\\mathfrak{S}_n}\\varepsilon(\\sigma)\\prod_{k=1}^n a_{k\\sigma(k)}\\\\";
latex += "\\sideset{_\\alpha^\\beta}{_\\gamma^\\delta}{\\begin{pmatrix}a&b\\\\c&d\\end{pmatrix}}\\\\";
latex += "\\int_0^\\infty{x^{2n} e^{-a x^2}\\,dx} = \\frac{2n-1}{2a} \\int_0^\\infty{x^{2(n-1)} e^{-a x^2}\\,dx} = \\frac{(2n-1)!!}{2^{n+1}} \\sqrt{\\frac{\\pi}{a^{2n+1}}}\\\\";
latex += "\\int_a^b{f(x)\\,dx} = (b - a) \\sum\\limits_{n = 1}^\\infty {\\sum\\limits_{m = 1}^{2^n - 1} {\\left( { - 1} \\right)^{m + 1} } } 2^{ - n} f(a + m\\left( {b - a} \\right)2^{-n} )\\\\";
latex += "\\int_{-\\pi}^{\\pi} \\sin(\\alpha x) \\sin^n(\\beta x) dx = \\textstyle{\\left \\{ \\begin{array}{cc} (-1)^{(n+1)/2} (-1)^m \\frac{2 \\pi}{2^n} \\binom{n}{m} & n \\mbox{ odd},\\ \\alpha = \\beta (2m-n) \\\\ 0 & \\mbox{otherwise} \\\\ \\end{array} \\right .}\\\\";
latex += "L = \\int_a^b \\sqrt{ \\left|\\sum_{i,j=1}^ng_{ij}(\\gamma(t))\\left(\\frac{d}{dt}x^i\\circ\\gamma(t)\\right)\\left(\\frac{d}{dt}x^j\\circ\\gamma(t)\\right)\\right|}\\,dt\\\\";
latex += "\\begin{array}{rl} s &= \\int_a^b\\left\\|\\frac{d}{dt}\\vec{r}\\,(u(t),v(t))\\right\\|\\,dt \\\\ &= \\int_a^b \\sqrt{u'(t)^2\\,\\vec{r}_u\\cdot\\vec{r}_u + 2u'(t)v'(t)\\, \\vec{r}_u\\cdot\\vec{r}_v+ v'(t)^2\\,\\vec{r}_v\\cdot\\vec{r}_v}\\,\\,\\, dt. \\end{array}\\\\";
latex += "\\end{array}";
LATEX_ARRAY = latex;
}
private LatexHolder() {
}
}

View File

@ -0,0 +1,84 @@
package io.noties.markwon.app.samples.notification;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.QuoteSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import androidx.annotation.NonNull;
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.Code;
import org.commonmark.node.Emphasis;
import org.commonmark.node.ListItem;
import org.commonmark.node.StrongEmphasis;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.notification.shared.NotificationUtils;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007183130729",
title = "Markdown in Notification",
description = "Proof of concept of using `Markwon` with `android.app.Notification`",
artifacts = MarkwonArtifact.CORE,
tags = Tags.hack
)
public class NotificationSample extends MarkwonTextViewSample {
@Override
public void render() {
// supports:
// * bold -> StyleSpan(BOLD)
// * italic -> StyleSpan(ITALIC)
// * quote -> QuoteSpan()
// * strikethrough -> StrikethroughSpan()
// * bullet list -> BulletSpan()
// * link -> is styled but not clickable
// * code -> typeface monospace works, background is not
final String md = "" +
"**bold _bold-italic_ bold** ~~strike~~ `code` [link](#)\n\n" +
"* bullet-one\n" +
"* * bullet-two\n" +
" * bullet-three\n\n" +
"> a quote\n\n" +
"";
final Markwon markwon = Markwon.builder(context)
.usePlugin(StrikethroughPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder
.setFactory(Emphasis.class, (configuration, props) -> new StyleSpan(Typeface.ITALIC))
.setFactory(StrongEmphasis.class, (configuration, props) -> new StyleSpan(Typeface.BOLD))
.setFactory(BlockQuote.class, (configuration, props) -> new QuoteSpan())
.setFactory(Strikethrough.class, (configuration, props) -> new StrikethroughSpan())
// NB! notification does not handle background color
.setFactory(Code.class, (configuration, props) -> new Object[]{
new BackgroundColorSpan(Color.GRAY),
new TypefaceSpan("monospace")
})
// NB! both ordered and bullet list items
.setFactory(ListItem.class, (configuration, props) -> new BulletSpan());
}
})
.build();
markwon.setMarkdown(textView, md);
NotificationUtils.display(context, markwon.toMarkdown(md));
}
}

View File

@ -0,0 +1,93 @@
package io.noties.markwon.app.samples.notification;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.Code;
import org.commonmark.node.Emphasis;
import org.commonmark.node.Heading;
import org.commonmark.node.ListItem;
import org.commonmark.node.StrongEmphasis;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.app.R;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.app.samples.notification.shared.NotificationUtils;
import io.noties.markwon.core.CoreProps;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184090140",
title = "RemoteViews in notification",
description = "Display markdown with platform (system) spans in notification via `RemoteViews`",
artifacts = MarkwonArtifact.CORE,
tags = Tags.hack
)
public class RemoteViewsSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"# Heading 1\n" +
// "## Heading 2\n" +
// "### Heading 3\n" +
// "#### Heading 4\n" +
// "##### Heading 5\n" +
// "###### Heading 6\n" +
"**bold _italic_ bold** `code` [link](#) ~~strike~~\n" +
"* Bullet 1\n" +
"* * Bullet 2\n" +
" * Bullet 3\n" +
"> A quote **here**";
final float[] headingSizes = {
2.F, 1.5F, 1.17F, 1.F, .83F, .67F,
};
final int bulletGapWidth = (int) (8 * context.getResources().getDisplayMetrics().density + 0.5F);
final Markwon markwon = Markwon.builder(context)
.usePlugin(StrikethroughPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
builder
.setFactory(Heading.class, (configuration, props) -> new Object[]{
new StyleSpan(Typeface.BOLD),
new RelativeSizeSpan(headingSizes[CoreProps.HEADING_LEVEL.require(props) - 1])
})
.setFactory(StrongEmphasis.class, (configuration, props) -> new StyleSpan(Typeface.BOLD))
.setFactory(Emphasis.class, (configuration, props) -> new StyleSpan(Typeface.ITALIC))
.setFactory(Code.class, (configuration, props) -> new Object[]{
new BackgroundColorSpan(Color.GRAY),
new TypefaceSpan("monospace")
})
.setFactory(Strikethrough.class, (configuration, props) -> new StrikethroughSpan())
.setFactory(ListItem.class, (configuration, props) -> new BulletSpan(bulletGapWidth))
.setFactory(BlockQuote.class, (configuration, props) -> new QuoteSpan());
}
})
.build();
final RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.sample_remote_view);
remoteViews.setTextViewText(R.id.text_view, markwon.toMarkdown(md));
NotificationUtils.display(context, remoteViews);
}
}

View File

@ -0,0 +1,85 @@
package io.noties.markwon.app.samples.notification.shared;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import io.noties.markwon.app.R;
public abstract class NotificationUtils {
private static final int ID = 2;
private static final String CHANNEL_ID = "2";
public static void display(@NonNull Context context, @NonNull CharSequence cs) {
final NotificationManager manager = context.getSystemService(NotificationManager.class);
if (manager == null) {
return;
}
ensureChannel(manager, CHANNEL_ID);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(cs)
.setStyle(new Notification.BigTextStyle().bigText(cs));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
manager.notify(ID, builder.build());
}
public static void display(@NonNull Context context, @NonNull RemoteViews remoteViews) {
final NotificationManager manager = context.getSystemService(NotificationManager.class);
if (manager == null) {
return;
}
ensureChannel(manager, CHANNEL_ID);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(context.getString(R.string.app_name));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder
.setCustomContentView(remoteViews)
.setCustomBigContentView(remoteViews);
} else {
builder.setContent(remoteViews);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
manager.notify(ID, builder.build());
}
@SuppressWarnings("SameParameterValue")
private static void ensureChannel(@NonNull NotificationManager manager, @NonNull String channelId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
final NotificationChannel channel = manager.getNotificationChannel(channelId);
if (channel == null) {
manager.createNotificationChannel(new NotificationChannel(
channelId,
channelId,
NotificationManager.IMPORTANCE_DEFAULT
));
}
}
private NotificationUtils() {
}
}

View File

@ -1,10 +1,5 @@
package io.noties.markwon.app.samples.plugins;
import android.view.View;
import android.widget.ScrollView;
import org.jetbrains.annotations.NotNull;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.R;
import io.noties.markwon.app.sample.Tags;
@ -22,14 +17,6 @@ import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
)
public class AnchorSample extends MarkwonTextViewSample {
private ScrollView scrollView;
@Override
public void onViewCreated(@NotNull View view) {
scrollView = view.findViewById(R.id.scroll_view);
super.onViewCreated(view);
}
@Override
public void render() {

View File

@ -1,8 +1,5 @@
package io.noties.markwon.app.samples.plugins;
import android.view.View;
import android.widget.ScrollView;
import androidx.annotation.NonNull;
import org.commonmark.node.AbstractVisitor;
@ -13,7 +10,6 @@ import org.commonmark.node.Link;
import org.commonmark.node.ListItem;
import org.commonmark.node.Node;
import org.commonmark.node.Text;
import org.jetbrains.annotations.NotNull;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
@ -35,14 +31,6 @@ import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
)
public class TableOfContentsSample extends MarkwonTextViewSample {
private ScrollView scrollView;
@Override
public void onViewCreated(@NotNull View view) {
scrollView = view.findViewById(R.id.scroll_view);
super.onViewCreated(view);
}
@Override
public void render() {
final String lorem = context.getString(R.string.lorem);

View File

@ -0,0 +1,47 @@
package io.noties.markwon.app.samples.table;
import android.graphics.Color;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import io.noties.markwon.utils.ColorUtils;
import io.noties.markwon.utils.Dip;
@MarkwonSampleInfo(
id = "202007184135621",
title = "Customize table theme",
artifacts = MarkwonArtifact.EXT_TABLES,
tags = {Tags.theme}
)
public class TableCustomizeSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"| HEADER | HEADER | HEADER |\n" +
"|:----:|:----:|:----:|\n" +
"| 测试 | 测试 | 测试 |\n" +
"| 测试 | 测试 | 测测测12345试测试测试 |\n" +
"| 测试 | 测试 | 123445 |\n" +
"| 测试 | 测试 | (650) 555-1212 |\n" +
"| 测试 | 测试 | [link](#) |\n";
final Markwon markwon = Markwon.builder(context)
.usePlugin(TablePlugin.create(builder -> {
final Dip dip = Dip.create(context);
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);
}
}

View File

@ -0,0 +1,45 @@
package io.noties.markwon.app.samples.table;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.latex.JLatexMathPlugin;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.image.ImagesPlugin;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184140041",
title = "LaTeX inside table",
description = "Usage of LaTeX formulas inside markdown tables",
artifacts = {MarkwonArtifact.EXT_LATEX, MarkwonArtifact.EXT_TABLES, MarkwonArtifact.IMAGE},
tags = {Tags.image}
)
public class TableLatexSample extends MarkwonTextViewSample {
@Override
public void render() {
String latex = "\\begin{array}{cc}";
latex += "\\fbox{\\text{A framed box with \\textdbend}}&\\shadowbox{\\text{A shadowed box}}\\cr";
latex += "\\doublebox{\\text{A double framed box}}&\\ovalbox{\\text{An oval framed box}}\\cr";
latex += "\\end{array}";
final String md = "" +
"| HEADER | HEADER |\n" +
"|:----:|:----:|\n" +
"| ![Build](https://github.com/noties/Markwon/workflows/Build/badge.svg) | Build |\n" +
"| Stable | ![stable](https://img.shields.io/maven-central/v/io.noties.markwon/core.svg?label=stable) |\n" +
"| BIG | $$" + latex + "$$ |\n" +
"\n";
final Markwon markwon = Markwon.builder(context)
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(ImagesPlugin.create())
.usePlugin(JLatexMathPlugin.create(textView.getTextSize(), builder -> builder.inlinesEnabled(true)))
.usePlugin(TablePlugin.create(context))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,42 @@
package io.noties.markwon.app.samples.table;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.linkify.LinkifyPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184135739",
title = "Linkify table",
description = "Automatically linkify markdown content " +
"including content inside tables",
artifacts = {MarkwonArtifact.EXT_TABLES, MarkwonArtifact.LINKIFY},
tags = {Tags.links}
)
public class TableLinkifySample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"| HEADER | HEADER | HEADER |\n" +
"|:----:|:----:|:----:|\n" +
"| 测试 | 测试 | 测试 |\n" +
"| 测试 | 测试 | 测测测12345试测试测试 |\n" +
"| 测试 | 测试 | 123445 |\n" +
"| 测试 | 测试 | (650) 555-1212 |\n" +
"| 测试 | 测试 | [link](#) |\n" +
"\n" +
"测试\n" +
"\n" +
"[link link](https://link.link)";
final Markwon markwon = Markwon.builder(context)
.usePlugin(LinkifyPlugin.create())
.usePlugin(TablePlugin.create(context))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,36 @@
package io.noties.markwon.app.samples.table;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.image.ImagesPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
@MarkwonSampleInfo(
id = "202007184135932",
title = "Images inside table",
description = "Usage of images inside markdown tables",
artifacts = {MarkwonArtifact.EXT_TABLES, MarkwonArtifact.IMAGE},
tags = Tags.image
)
public class TableWithImagesSample extends MarkwonTextViewSample {
@Override
public void render() {
final String md = "" +
"| HEADER | HEADER |\n" +
"|:----:|:----:|\n" +
"| ![Build](https://github.com/noties/Markwon/workflows/Build/badge.svg) | Build |\n" +
"| Stable | ![stable](https://img.shields.io/maven-central/v/io.noties.markwon/core.svg?label=stable) |\n" +
"| BIG | ![image](https://images.pexels.com/photos/41171/brussels-sprouts-sprouts-cabbage-grocery-41171.jpeg) |\n" +
"\n";
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create())
.usePlugin(TablePlugin.create(context))
.build();
markwon.setMarkdown(textView, md);
}
}

View File

@ -0,0 +1,34 @@
package io.noties.markwon.app.samples.tasklist;
import android.graphics.Color;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import static io.noties.markwon.app.samples.tasklist.shared.TaskListHolder.MD;
@MarkwonSampleInfo(
id = "202007184140536",
title = "GFM task list custom colors",
description = "Custom colors for task list extension",
artifacts = MarkwonArtifact.EXT_TASKLIST,
tags = Tags.parsing
)
public class TaskListCustomColorsSample extends MarkwonTextViewSample {
@Override
public void render() {
final int checkedFillColor = Color.RED;
final int normalOutlineColor = Color.GREEN;
final int checkMarkColor = Color.BLUE;
final Markwon markwon = Markwon.builder(context)
.usePlugin(TaskListPlugin.create(checkedFillColor, normalOutlineColor, checkMarkColor))
.build();
markwon.setMarkdown(textView, MD);
}
}

View File

@ -0,0 +1,37 @@
package io.noties.markwon.app.samples.tasklist;
import android.graphics.drawable.Drawable;
import androidx.core.content.ContextCompat;
import java.util.Objects;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.R;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import static io.noties.markwon.app.samples.tasklist.shared.TaskListHolder.MD;
@MarkwonSampleInfo(
id = "202007184140749",
title = "GFM task list custom drawable",
artifacts = MarkwonArtifact.EXT_TASKLIST,
tags = Tags.plugin
)
public class TaskListCustomDrawableSample extends MarkwonTextViewSample {
@Override
public void render() {
final Drawable drawable = Objects.requireNonNull(
ContextCompat.getDrawable(context, R.drawable.custom_task_list));
final Markwon markwon = Markwon.builder(context)
.usePlugin(TaskListPlugin.create(drawable))
.build();
markwon.setMarkdown(textView, MD);
}
}

View File

@ -0,0 +1,101 @@
package io.noties.markwon.app.samples.tasklist;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import io.noties.debug.Debug;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonSpansFactory;
import io.noties.markwon.SpanFactory;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tasklist.TaskListItem;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.ext.tasklist.TaskListSpan;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import static io.noties.markwon.app.samples.tasklist.shared.TaskListHolder.MD;
@MarkwonSampleInfo(
id = "202007184140901",
title = "GFM task list mutate",
artifacts = MarkwonArtifact.EXT_TASKLIST,
tags = Tags.plugin
)
public class TaskListMutateSample extends MarkwonTextViewSample {
@Override
public void render() {
final Markwon markwon = Markwon.builder(context)
.usePlugin(TaskListPlugin.create(context))
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) {
// obtain origin task-list-factory
final SpanFactory origin = builder.getFactory(TaskListItem.class);
if (origin == null) {
return;
}
builder.setFactory(TaskListItem.class, (configuration, props) -> {
// maybe it's better to validate the actual type here also
// and not force cast to task-list-span
final TaskListSpan span = (TaskListSpan) origin.getSpans(configuration, props);
if (span == null) {
return null;
}
// NB, toggle click will intercept possible links inside task-list-item
return new Object[]{
span,
new TaskListToggleSpan(span)
};
});
}
})
.build();
markwon.setMarkdown(textView, MD);
}
}
class TaskListToggleSpan extends ClickableSpan {
private final TaskListSpan span;
TaskListToggleSpan(@NonNull TaskListSpan span) {
this.span = span;
}
@Override
public void onClick(@NonNull View widget) {
// toggle span (this is a mere visual change)
span.setDone(!span.isDone());
// request visual update
widget.invalidate();
// it must be a TextView
final TextView textView = (TextView) widget;
// it must be spanned
final Spanned spanned = (Spanned) textView.getText();
// actual text of the span (this can be used along with the `span`)
final CharSequence task = spanned.subSequence(
spanned.getSpanStart(this),
spanned.getSpanEnd(this)
);
Debug.i("task done: %s, '%s'", span.isDone(), task);
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
// no op, so text is not rendered as a link
}
}

View File

@ -0,0 +1,28 @@
package io.noties.markwon.app.samples.tasklist;
import io.noties.markwon.Markwon;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import static io.noties.markwon.app.samples.tasklist.shared.TaskListHolder.MD;
@MarkwonSampleInfo(
id = "202007184140352",
title = "GFM task list",
description = "Github Flavored Markdown (GFM) task list extension",
artifacts = MarkwonArtifact.EXT_TASKLIST,
tags = Tags.plugin
)
public class TaskListSample extends MarkwonTextViewSample {
@Override
public void render() {
final Markwon markwon = Markwon.builder(context)
.usePlugin(TaskListPlugin.create(context))
.build();
markwon.setMarkdown(textView, MD);
}
}

View File

@ -0,0 +1,17 @@
package io.noties.markwon.app.samples.tasklist.shared;
public abstract class TaskListHolder {
public static final String MD = "" +
"- [ ] Not done here!\n" +
"- [x] and done\n" +
"- [X] and again!\n" +
"* [ ] **and** syntax _included_ `code`\n" +
"- [ ] [link](#)\n" +
"- [ ] [a check box](https://examp.le)\n" +
"- [x] [test]()\n" +
"- [List](https://examp.le) 3";
private TaskListHolder() {
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/ic_android_black_24dp" />
<item android:drawable="@drawable/ic_home_black_36dp" />
</selector>

View File

@ -0,0 +1,31 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<group android:name="group_3">
<group android:name="group">
<path
android:name="path"
android:pathData="M 296.339 368.334 L 296.339 351.583 L 299.412 351.583 Q 304.33 351.583 308.018 350.969 Q 311.86 350.354 314.318 348.51 Q 316.777 346.666 318.007 343.285 Q 319.236 339.751 319.236 334.065 L 319.236 174.708 L 255.77 368.334 L 234.409 368.334 L 167.87 174.708 L 167.87 331.145 Q 167.87 337.907 169.406 341.902 Q 171.097 345.898 174.016 348.049 Q 177.09 350.2 181.393 350.969 Q 185.696 351.583 191.228 351.583 L 193.072 351.583 L 193.072 368.334 L 115.007 368.334 L 115.007 351.583 L 128.222 351.583 Q 131.603 351.583 134.523 350.969 Q 137.443 350.354 139.594 348.51 Q 141.899 346.512 143.129 342.824 Q 144.358 338.982 144.358 332.836 L 144.358 179.472 Q 144.358 173.171 143.129 169.483 Q 141.899 165.641 139.594 163.643 Q 137.443 161.646 134.523 161.031 Q 131.603 160.263 128.222 160.263 L 115.007 160.263 L 115.007 143.666 L 205.673 143.666 L 257.921 295.647 L 308.018 143.666 L 396.994 143.666 L 396.994 160.263 L 383.778 160.263 Q 380.397 160.263 377.477 161.031 Q 374.558 161.646 372.406 163.797 Q 370.255 165.948 369.025 169.944 Q 367.796 173.939 367.796 180.701 L 367.796 331.145 Q 367.796 337.907 369.025 341.902 Q 370.255 345.898 372.406 348.049 Q 374.558 350.2 377.477 350.969 Q 380.397 351.583 383.778 351.583 L 396.994 351.583 L 396.994 368.334 Z"
android:fillColor="#ffffff"
android:strokeWidth="1"/>
</group>
<group android:name="group_1">
<path
android:name="path_1"
android:pathData="M 122.034 58.217 L 132.361 34.981 L 177.542 63.525 L 169.94 16.909 L 195.471 16.909 L 187.296 63.238 L 232.764 35.699 L 243.091 58.504 L 193.463 72.991 L 243.091 87.334 L 232.764 109.997 L 187.296 82.601 L 195.471 129.073 L 169.94 129.073 L 177.256 82.601 L 132.361 110.57 L 122.034 87.621 L 171.375 72.991 Z M 268.909 58.217 L 279.236 34.981 L 324.417 63.525 L 316.815 16.909 L 342.346 16.909 L 334.171 63.238 L 379.639 35.699 L 389.966 58.504 L 340.338 72.991 L 389.966 87.334 L 379.639 109.997 L 334.171 82.601 L 342.346 129.073 L 316.815 129.073 L 324.13 82.601 L 279.236 110.57 L 268.909 87.621 L 318.25 72.991 Z"
android:fillColor="#ffffff"
android:strokeWidth="1"/>
</group>
<group android:name="group_2">
<path
android:name="path_2"
android:pathData="M 122.034 428.561 L 132.361 405.325 L 177.542 433.868 L 169.94 387.252 L 195.471 387.252 L 187.296 433.581 L 232.764 406.042 L 243.091 428.848 L 193.463 443.334 L 243.091 457.678 L 232.764 480.34 L 187.296 452.944 L 195.471 499.417 L 169.94 499.417 L 177.256 452.944 L 132.361 480.914 L 122.034 457.964 L 171.375 443.334 Z M 268.909 428.561 L 279.236 405.325 L 324.417 433.868 L 316.815 387.252 L 342.346 387.252 L 334.171 433.581 L 379.639 406.042 L 389.966 428.848 L 340.338 443.334 L 389.966 457.678 L 379.639 480.34 L 334.171 452.944 L 342.346 499.417 L 316.815 499.417 L 324.13 452.944 L 279.236 480.914 L 268.909 457.964 L 318.25 443.334 Z"
android:fillColor="#ffffff"
android:strokeWidth="1"/>
</group>
</group>
</vector>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.AppCompatTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:lineSpacingExtra="2dip"
android:paddingTop="8dip"
android:paddingBottom="8dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#000"
tools:text="Hello" />

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
tools:text="Hello" />
</LinearLayout>

View File

@ -13,6 +13,7 @@
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="2dip"
android:padding="@dimen/content_padding_double"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="markwon-icon-foreground-stroked.svg"
inkscape:export-ydpi="89.93"
inkscape:export-xdpi="89.93"
inkscape:export-filename="/Users/di/text4169.png"
inkscape:version="1.0beta2 (2b71d25, 2019-12-03)"
version="1.1"
id="svg2"
viewBox="0 0 512.00001 512.00001"
height="512"
width="512">
<defs
id="defs4" />
<sodipodi:namedview
inkscape:document-rotation="0"
inkscape:window-maximized="0"
inkscape:window-y="23"
inkscape:window-x="0"
inkscape:window-height="788"
inkscape:window-width="1440"
units="px"
showgrid="false"
inkscape:current-layer="layer1"
inkscape:document-units="px"
inkscape:cy="237.21893"
inkscape:cx="151.88384"
inkscape:zoom="0.92578125"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-540.36216)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<g
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4136"
aria-label="M">
<path
id="path26"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:314.719px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Bold'"
d="m 296.33892,908.6958 v -16.75018 h 3.07342 q 4.91749,0 8.6056,-0.61469 3.84179,-0.61468 6.30053,-2.45874 2.45874,-1.84405 3.68811,-5.22482 1.22937,-3.53445 1.22937,-9.22029 V 715.06986 L 255.76967,908.6958 H 234.40935 L 167.86964,715.06986 v 156.43747 q 0,6.76154 1.53671,10.75699 1.69039,3.99546 4.61014,6.14686 3.07343,2.1514 7.37623,2.91975 4.3028,0.61469 9.83497,0.61469 h 1.84406 v 16.75018 h -78.06507 v -16.75018 h 13.21574 q 3.38077,0 6.30053,-0.61469 2.91975,-0.61468 5.07115,-2.45874 2.30507,-1.99773 3.53445,-5.68584 1.22937,-3.84178 1.22937,-9.98864 V 719.83367 q 0,-6.30053 -1.22937,-9.98864 -1.22938,-3.84178 -3.53445,-5.83951 -2.1514,-1.99773 -5.07115,-2.61242 -2.91976,-0.76835 -6.30053,-0.76835 h -13.21574 v -16.59651 h 90.66612 l 52.24827,151.981 50.09687,-151.981 h 88.97573 v 16.59651 h -13.21574 q -3.38077,0 -6.30052,0.76835 -2.91976,0.61469 -5.07116,2.76609 -2.1514,2.1514 -3.38077,6.14685 -1.22937,3.99546 -1.22937,10.757 v 150.44429 q 0,6.76154 1.22937,10.75699 1.22937,3.99546 3.38077,6.14686 2.1514,2.1514 5.07116,2.91975 2.91975,0.61469 6.30052,0.61469 h 13.21574 v 16.75018 z" />
</g>
<g
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#666666;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4140"
aria-label="**">
<path
id="path21"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:293.75px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';fill:#666666"
d="m 122.03394,598.57965 10.32715,-23.23608 45.18128,28.54309 -7.60193,-46.6156 h 25.531 l -8.17566,46.32873 45.46814,-27.53906 10.32715,22.80579 -49.62768,14.48669 49.62768,14.34326 -10.32715,22.66236 -45.46814,-27.39563 8.17566,46.47216 h -25.531 l 7.31506,-46.47216 -44.89441,27.96936 -10.32715,-22.94922 49.34082,-14.63013 z" />
<path
id="path23"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:293.75px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';fill:#666666"
d="m 268.90894,598.57965 10.32715,-23.23608 45.18128,28.54309 -7.60193,-46.6156 h 25.531 l -8.17566,46.32873 45.46814,-27.53906 10.32715,22.80579 -49.62768,14.48669 49.62768,14.34326 -10.32715,22.66236 -45.46814,-27.39563 8.17566,46.47216 h -25.531 l 7.31506,-46.47216 -44.89441,27.96936 -10.32715,-22.94922 49.34082,-14.63013 z" />
</g>
<g
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#666666;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4169"
aria-label="**">
<path
id="path29"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:293.75px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';fill:#666666"
d="m 122.03394,968.92297 10.32715,-23.23608 45.18128,28.54309 -7.60193,-46.6156 h 25.531 l -8.17566,46.32874 45.46814,-27.53907 10.32715,22.80579 -49.62768,14.48669 49.62768,14.34326 -10.32715,22.66231 -45.46814,-27.39558 8.17566,46.47218 h -25.531 l 7.31506,-46.47218 -44.89441,27.96938 -10.32715,-22.94924 49.34082,-14.63013 z" />
<path
id="path31"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:293.75px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';fill:#666666"
d="m 268.90894,968.92297 10.32715,-23.23608 45.18128,28.54309 -7.60193,-46.6156 h 25.531 l -8.17566,46.32874 45.46814,-27.53907 10.32715,22.80579 -49.62768,14.48669 49.62768,14.34326 -10.32715,22.66231 -45.46814,-27.39558 8.17566,46.47218 h -25.531 l 7.31506,-46.47218 -44.89441,27.96938 -10.32715,-22.94924 49.34082,-14.63013 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -81,6 +81,14 @@ public abstract class JLatexMathTheme {
public static Padding symmetric(int vertical, int horizontal) {
return new Padding(horizontal, vertical, horizontal, vertical);
}
/**
* @since $nap;
*/
@NonNull
public static Padding of(int left, int top, int right, int bottom) {
return new Padding(left, top, right, bottom);
}
}
/**
@ -175,6 +183,10 @@ public abstract class JLatexMathTheme {
return this;
}
/**
* Configure if `LaTeX` formula should take all available widget width.
* By default - `true`
*/
@NonNull
public Builder blockFitCanvas(boolean blockFitCanvas) {
this.blockFitCanvas = blockFitCanvas;