From 2488c1047b32b25dbe69d5b93ebd94850e04034a Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Thu, 7 Nov 2019 17:30:20 +0300 Subject: [PATCH] Add editor documentation page --- docs/README.md | 2 +- docs/docs/v4/editor/README.md | 112 +++++++++++++++++- .../markwon/sample/editor/EditorActivity.java | 97 +++++++++------ 3 files changed, 169 insertions(+), 42 deletions(-) diff --git a/docs/README.md b/docs/README.md index 300075e8..9f53dca3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,7 +22,7 @@ listed in are supported (including support for * **markdown tables**, **images** and **syntax highlight**). Since version **Markwon** comes with an [editor] to _highlight_ markdown input -as user types for example in **EditText**. +as user types (for example in **EditText**). [editor]: /docs/v4/editor/ diff --git a/docs/docs/v4/editor/README.md b/docs/docs/v4/editor/README.md index 9af9bf6c..6477643b 100644 --- a/docs/docs/v4/editor/README.md +++ b/docs/docs/v4/editor/README.md @@ -18,8 +18,116 @@ video { ## Getting started with editor +```java +// obtain Markwon instance +final Markwon markwon = Markwon.create(this); + +// create editor +final MarkwonEditor editor = MarkwonEditor.create(markwon); + +// set edit listener +editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor)); +``` + +The code above _highlights_ in-place which is OK for relatively small markdown inputs. +If you wish to offload main thread and highlight in background use `withPreRender` +`MarkwonEditorTextWatcher`: + +```java +editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender( + editor, + Executors.newCachedThreadPool(), + editText)); +``` + +`MarkwonEditorTextWatcher` automatically triggers markdown highlight when text in `EditText` changes. +But you still can invoke `MarkwonEditor` manually: + +```java +editor.process(editText.getText()); + +// please note that MarkwonEditor operates on caller thread, +// if you wish to execute this operation in background - this method +// must be called from background thread +editor.preRender(editText.getText(), new MarkwonEditor.PreRenderResultListener() { + @Override + public void onPreRenderResult(@NonNull MarkwonEditor.PreRenderResult result) { + // it's wise to check if rendered result is for the same input, + // for example by matching raw input + if (editText.getText().toString().equals(result.resultEditable().toString())) { + + // if you are in background thread do not forget + // to execute dispatch in main thread + result.dispatchTo(editText.getText()); + } + } +}); +``` + :::warning Implementation Detail It must be mentioned that highlight is implemented via text diff. Everything -that is present in raw markdown input and missing from rendered result is considered +that is present in raw markdown input but missing from rendered result is considered to be _punctuation_. -::: \ No newline at end of file +::: + +:::danger Tables and LaTeX +Tables and LaTeX nodes won't be rendered correctly. They will be treated as _punctuation_ +as whole. This comes from their implementation - they are _mocked_ and do not present +in final result as text and thus cannot be _diffed_. +::: + +## Custom punctuation span + +By default `MarkwonEditor` uses lighter text color of widget to customize punctuation. +If you wish to use a different span you can use `withPunctuationSpan` configuration step: + +```java +final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this)) + .withPunctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new) + .build(); +``` + +```java +public class CustomPunctuationSpan extends ForegroundColorSpan { + CustomPunctuationSpan() { + super(0xFFFF0000); // RED + } +} +``` + +## Additional handling + +In order to additionally highlight portions of markdown input (for example make text wrapped with `**` +symbols **bold**) `EditSpanHandler` can be used: + +```java +final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this)) + // This is required for edit-span cache + // We could use Markwon `StrongEmphasisSpan` here, but I use a different + // one to indicate that those are completely unrelated spans and must be + // treated differently. + .includeEditSpan(Bold.class, Bold::new) + .withEditSpanHandler(new MarkwonEditor.EditSpanHandler() { + @Override + public void handle( + @NonNull MarkwonEditor.EditSpanStore store, + @NonNull Editable editable, + @NonNull String input, + @NonNull Object span, + int spanStart, + int spanTextLength) { + if (span instanceof StrongEmphasisSpan) { + editable.setSpan( + // `includeEditSpan(Bold.class, Bold::new)` ensured that we have + // a span here to use (either reuse existing or create a new one) + store.get(Bold.class), + spanStart, + // we know that strong emphasis is delimited with 2 characters on both sides + spanStart + spanTextLength + 4, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ); + } + } + }) + .build(); +``` diff --git a/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java b/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java index 271b6b69..33c79ad8 100644 --- a/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java @@ -64,13 +64,13 @@ public class EditorActivity extends Activity { private void simple_process() { // Process highlight in-place (right after text has changed) - // obtain Markwon instance +// obtain Markwon instance final Markwon markwon = Markwon.create(this); - // create editor +// create editor final MarkwonEditor editor = MarkwonEditor.create(markwon); - // set edit listener +// set edit listener editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor)); } @@ -80,6 +80,25 @@ public class EditorActivity extends Activity { final Markwon markwon = Markwon.create(this); final MarkwonEditor editor = MarkwonEditor.create(markwon); +editor.process(editText.getText()); + +// please note that MarkwonEditor operates on caller thread, +// fi you wish to execute this operation in background - this method +// must be called from background thread +editor.preRender(editText.getText(), new MarkwonEditor.PreRenderResultListener() { + @Override + public void onPreRenderResult(@NonNull MarkwonEditor.PreRenderResult result) { + // it's wise to check if rendered result is for the same input, + // for example by matching raw input + if (editText.getText().toString().equals(result.resultEditable().toString())) { + + // if you are in background thread do not forget + // to execute dispatch in main thread + result.dispatchTo(editText.getText()); + } + } +}); + editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender( editor, Executors.newCachedThreadPool(), @@ -89,9 +108,9 @@ public class EditorActivity extends Activity { private void custom_punctuation_span() { // Use own punctuation span - final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this)) - .withPunctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new) - .build(); +final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this)) + .withPunctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new) + .build(); editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor)); } @@ -99,35 +118,35 @@ public class EditorActivity extends Activity { private void additional_edit_span() { // An additional span is used to highlight strong-emphasis - final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this)) - // This is required for edit-span cache - // We could use Markwon `StrongEmphasisSpan` here, but I use a different - // one to indicate that those are completely unrelated spans and must be - // treated differently. - .includeEditSpan(Bold.class, Bold::new) - .withEditSpanHandler(new MarkwonEditor.EditSpanHandler() { - @Override - public void handle( - @NonNull MarkwonEditor.EditSpanStore store, - @NonNull Editable editable, - @NonNull String input, - @NonNull Object span, - int spanStart, - int spanTextLength) { - if (span instanceof StrongEmphasisSpan) { - editable.setSpan( - // `includeEditSpan(Bold.class, Bold::new)` ensured that we have - // a span here to use (either reuse existing or create a new one) - store.get(Bold.class), - spanStart, - // we know that strong emphasis is delimited with 2 characters on both sides - spanStart + spanTextLength + 4, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE - ); - } - } - }) - .build(); +final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this)) + // This is required for edit-span cache + // We could use Markwon `StrongEmphasisSpan` here, but I use a different + // one to indicate that those are completely unrelated spans and must be + // treated differently. + .includeEditSpan(Bold.class, Bold::new) + .withEditSpanHandler(new MarkwonEditor.EditSpanHandler() { + @Override + public void handle( + @NonNull MarkwonEditor.EditSpanStore store, + @NonNull Editable editable, + @NonNull String input, + @NonNull Object span, + int spanStart, + int spanTextLength) { + if (span instanceof StrongEmphasisSpan) { + editable.setSpan( + // `includeEditSpan(Bold.class, Bold::new)` ensured that we have + // a span here to use (either reuse existing or create a new one) + store.get(Bold.class), + spanStart, + // we know that strong emphasis is delimited with 2 characters on both sides + spanStart + spanTextLength + 4, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ); + } + } + }) + .build(); editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor)); } @@ -316,11 +335,11 @@ public class EditorActivity extends Activity { } } - private static class CustomPunctuationSpan extends ForegroundColorSpan { - CustomPunctuationSpan() { - super(0xFFFF0000); // RED - } +private static class CustomPunctuationSpan extends ForegroundColorSpan { + CustomPunctuationSpan() { + super(0xFFFF0000); // RED } +} private static class Bold extends MetricAffectingSpan { public Bold() {