diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-12-02 11:53:13 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2020-12-02 11:53:13 +0300 |
commit | cbd46ad5e693bdeff7e2cbd49ea0cf75f10a8056 (patch) | |
tree | 34175fd6241bfbcc54de80720ee50e9878461399 | |
parent | 3cc27df32e010aaf68be8e9e135f3ed25baa9f36 (diff) |
Move list auto continuation to own class
Signed-off-by: Stefan Niedermann <info@niedermann.it>
2 files changed, 120 insertions, 76 deletions
diff --git a/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownEditor.java b/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownEditor.java index 887b35620..1e4a1fba7 100644 --- a/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownEditor.java +++ b/markdown/src/main/java/it/niedermann/android/markdown/markwon/MarkwonMarkdownEditor.java @@ -1,8 +1,6 @@ package it.niedermann.android.markdown.markwon; import android.content.Context; -import android.text.Editable; -import android.text.TextWatcher; import android.util.AttributeSet; import androidx.annotation.NonNull; @@ -11,11 +9,8 @@ import androidx.appcompat.widget.AppCompatEditText; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import java.util.concurrent.Executors; - import io.noties.markwon.Markwon; import io.noties.markwon.editor.MarkwonEditor; -import io.noties.markwon.editor.MarkwonEditorTextWatcher; import io.noties.markwon.editor.handler.EmphasisEditHandler; import io.noties.markwon.editor.handler.StrongEmphasisEditHandler; import it.niedermann.android.markdown.MarkdownEditor; @@ -24,18 +19,10 @@ import it.niedermann.android.markdown.markwon.handler.CodeBlockEditHandler; import it.niedermann.android.markdown.markwon.handler.CodeEditHandler; import it.niedermann.android.markdown.markwon.handler.HeadingEditHandler; import it.niedermann.android.markdown.markwon.handler.StrikethroughEditHandler; +import it.niedermann.android.markdown.markwon.textwatcher.AutoContinuationTextWatcher; public class MarkwonMarkdownEditor extends AppCompatEditText implements MarkdownEditor { - private static final String CHECKBOX_UNCHECKED_MINUS = "- [ ]"; - private static final String CHECKBOX_UNCHECKED_MINUS_TRAILING_SPACE = CHECKBOX_UNCHECKED_MINUS + " "; - private static final String CHECKBOX_UNCHECKED_STAR = "* [ ]"; - private static final String CHECKBOX_UNCHECKED_STAR_TRAILING_SPACE = CHECKBOX_UNCHECKED_STAR + " "; - private static final String CHECKBOX_CHECKED_MINUS = "- [x]"; - private static final String CHECKBOX_CHECKED_STAR = "* [x]"; - - private static final int lengthCheckbox = 6; - private final MutableLiveData<CharSequence> unrenderedText$ = new MutableLiveData<>(); public MarkwonMarkdownEditor(@NonNull Context context) { @@ -59,29 +46,8 @@ public class MarkwonMarkdownEditor extends AppCompatEditText implements Markdown .useEditHandler(new HeadingEditHandler()) .build(); - MarkwonEditorTextWatcher originalWatcher = MarkwonEditorTextWatcher.withPreRender(editor, Executors.newSingleThreadExecutor(), this); - - // intercept behavior of the original MarkwonEditorTextWatcher. - addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - originalWatcher.beforeTextChanged(s, start, count, after); - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - if (count == 1 && s.charAt(start) == '\n') { - autoContinueCheckboxListsOnEnter(s, start, count); - } - originalWatcher.onTextChanged(s, start, before, count); -// unrenderedText$.setValue(s.toString()); - } - - @Override - public void afterTextChanged(Editable s) { - originalWatcher.afterTextChanged(s); - } - }); + // addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(editor, Executors.newSingleThreadExecutor(), this)); + addTextChangedListener(new AutoContinuationTextWatcher(editor, this)); } @Override @@ -94,43 +60,4 @@ public class MarkwonMarkdownEditor extends AppCompatEditText implements Markdown public LiveData<CharSequence> getMarkdownString() { return unrenderedText$; } - - private boolean autoContinueCheckboxListsOnEnter(@NonNull CharSequence originalSequence, int start, int count) { - final CharSequence s = originalSequence.subSequence(0, originalSequence.length()); - final int startOfLine = getStartOfLine(s, start); - final String line = s.subSequence(startOfLine, start).toString(); - - if (line.equals(CHECKBOX_UNCHECKED_MINUS_TRAILING_SPACE) || line.equals(CHECKBOX_UNCHECKED_STAR_TRAILING_SPACE)) { - setSelection(startOfLine + 1); - setNewText(new StringBuilder(s).replace(startOfLine, startOfLine + lengthCheckbox + 1, "\n"), startOfLine + 1); - return true; - } else if (lineStartsWithCheckbox(line, false)) { - setNewText(new StringBuilder(s).insert(start + count, CHECKBOX_UNCHECKED_MINUS_TRAILING_SPACE), start + lengthCheckbox + 1); - return true; - } else if (lineStartsWithCheckbox(line, true)) { - setNewText(new StringBuilder(s).insert(start + count, CHECKBOX_UNCHECKED_STAR_TRAILING_SPACE), start + lengthCheckbox + 1); - return true; - } - return false; - } - - private void setNewText(@NonNull StringBuilder newText, int selection) { - setMarkdownString(newText.toString()); - unrenderedText$.setValue(newText.toString()); - setSelection(selection); - } - - private static int getStartOfLine(@NonNull CharSequence s, int cursorPosition) { - int startOfLine = cursorPosition; - while (startOfLine > 0 && s.charAt(startOfLine - 1) != '\n') { - startOfLine--; - } - return startOfLine; - } - - private static boolean lineStartsWithCheckbox(@NonNull String line, boolean starAsLeadingCharacter) { - return starAsLeadingCharacter - ? line.startsWith(CHECKBOX_UNCHECKED_STAR) || line.startsWith(CHECKBOX_CHECKED_STAR) - : line.startsWith(CHECKBOX_UNCHECKED_MINUS) || line.startsWith(CHECKBOX_CHECKED_MINUS); - } } diff --git a/markdown/src/main/java/it/niedermann/android/markdown/markwon/textwatcher/AutoContinuationTextWatcher.java b/markdown/src/main/java/it/niedermann/android/markdown/markwon/textwatcher/AutoContinuationTextWatcher.java new file mode 100644 index 000000000..55a446f3f --- /dev/null +++ b/markdown/src/main/java/it/niedermann/android/markdown/markwon/textwatcher/AutoContinuationTextWatcher.java @@ -0,0 +1,117 @@ +package it.niedermann.android.markdown.markwon.textwatcher; + +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; + +import androidx.annotation.NonNull; + +import java.util.concurrent.Executors; + +import io.noties.markwon.editor.MarkwonEditor; +import io.noties.markwon.editor.MarkwonEditorTextWatcher; +import it.niedermann.android.markdown.markwon.MarkwonMarkdownEditor; + +/** + * Automatically continues lists and checkbox lists when pressing enter + */ +public class AutoContinuationTextWatcher implements TextWatcher { + + private static final String TAG = AutoContinuationTextWatcher.class.getSimpleName(); + + private static final String CHECKBOX_CHECKED_DASH = "- [x]"; + private static final String CHECKBOX_CHECKED_STAR = "* [x]"; + private static final String CHECKBOX_CHECKED_PLUS = "+ [x]"; + private static final String CHECKBOX_UNCHECKED_DASH = "- [ ]"; + private static final String CHECKBOX_UNCHECKED_STAR = "* [ ]"; + private static final String CHECKBOX_UNCHECKED_PLUS = "+ [ ]"; + private static final String CHECKBOX_UNCHECKED_DASH_TRAILING_SPACE = CHECKBOX_UNCHECKED_DASH + " "; + private static final String CHECKBOX_UNCHECKED_STAR_TRAILING_SPACE = CHECKBOX_UNCHECKED_STAR + " "; + private static final String CHECKBOX_UNCHECKED_PLUS_TRAILING_SPACE = CHECKBOX_UNCHECKED_PLUS + " "; + + private static final int CHECKBOX_TRAILING_SPACE_LENGTH = 6; + + private final MarkwonEditorTextWatcher originalWatcher; + private final MarkwonMarkdownEditor editText; + + public AutoContinuationTextWatcher(@NonNull MarkwonEditor editor, @NonNull MarkwonMarkdownEditor editText) { + this.editText = editText; + originalWatcher = MarkwonEditorTextWatcher.withPreRender(editor, Executors.newSingleThreadExecutor(), editText); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + originalWatcher.beforeTextChanged(s, start, count, after); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (count == 1 && s.charAt(start) == '\n') { + autoContinueCheckboxListsOnEnter(s, start, count); + } + originalWatcher.onTextChanged(s, start, before, count); + } + + @Override + public void afterTextChanged(Editable s) { + originalWatcher.afterTextChanged(s); + } + + /** + * @return added characters in front of the current line (can be negative) + */ + private int autoContinueCheckboxListsOnEnter(@NonNull CharSequence originalSequence, int start, int count) { + final CharSequence s = originalSequence.subSequence(0, originalSequence.length()); + final int startOfLine = getStartOfLine(s, start); + final String line = s.subSequence(startOfLine, start).toString(); + + if (line.equals(CHECKBOX_UNCHECKED_DASH_TRAILING_SPACE) || line.equals(CHECKBOX_UNCHECKED_STAR_TRAILING_SPACE)) { + editText.setSelection(startOfLine + 1); + setNewText(new StringBuilder(s).replace(startOfLine, startOfLine + CHECKBOX_TRAILING_SPACE_LENGTH + 1, "\n"), startOfLine + 1); + return CHECKBOX_TRAILING_SPACE_LENGTH * -1; + } else if (lineStartsWithCheckbox(line, EListType.DASH)) { + setNewText(new StringBuilder(s).insert(start + count, CHECKBOX_UNCHECKED_DASH_TRAILING_SPACE), start + CHECKBOX_TRAILING_SPACE_LENGTH + 1); + return CHECKBOX_TRAILING_SPACE_LENGTH; + } else if (lineStartsWithCheckbox(line, EListType.STAR)) { + setNewText(new StringBuilder(s).insert(start + count, CHECKBOX_UNCHECKED_STAR_TRAILING_SPACE), start + CHECKBOX_TRAILING_SPACE_LENGTH + 1); + return CHECKBOX_TRAILING_SPACE_LENGTH; + } else if (lineStartsWithCheckbox(line, EListType.PLUS)) { + setNewText(new StringBuilder(s).insert(start + count, CHECKBOX_UNCHECKED_PLUS_TRAILING_SPACE), start + CHECKBOX_TRAILING_SPACE_LENGTH + 1); + return CHECKBOX_TRAILING_SPACE_LENGTH; + } + return 0; + } + + private void setNewText(@NonNull StringBuilder newText, int selection) { + editText.setMarkdownString(newText.toString()); + editText.setSelection(selection); + } + + private static int getStartOfLine(@NonNull CharSequence s, int cursorPosition) { + int startOfLine = cursorPosition; + while (startOfLine > 0 && s.charAt(startOfLine - 1) != '\n') { + startOfLine--; + } + return startOfLine; + } + + private static boolean lineStartsWithCheckbox(@NonNull String line, @NonNull EListType listType) { + switch (listType) { + case STAR: + return line.startsWith(CHECKBOX_UNCHECKED_STAR) || line.startsWith(CHECKBOX_CHECKED_STAR); + case DASH: + return line.startsWith(CHECKBOX_UNCHECKED_DASH) || line.startsWith(CHECKBOX_CHECKED_DASH); + case PLUS: + return line.startsWith(CHECKBOX_UNCHECKED_PLUS) || line.startsWith(CHECKBOX_CHECKED_PLUS); + default: + Log.w(TAG, "List type " + listType + " is not supported."); + return false; + } + } + + private enum EListType { + STAR, + DASH, + PLUS + } +} |