From 897d2720b6b790483aa26fb97d6e896e838471cd Mon Sep 17 00:00:00 2001 From: Stefanos Togkoulidis Date: Wed, 1 May 2019 00:11:36 +0300 Subject: [PATCH] Set Aztec t delete the Enter for paragraph block --- gutenberg | 2 +- react-native-aztec/android/build.gradle | 4 +- .../ReactNativeAztec/ReactAztecManager.java | 6 +- .../ReactNativeAztec/ReactAztecText.java | 11 +++ .../ReactNativeAztec/EnterPressedWatcher.kt | 71 +++++++++++++++++++ react-native-aztec/src/AztecView.js | 2 + 6 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt diff --git a/gutenberg b/gutenberg index 6358de362a..1dddab40b2 160000 --- a/gutenberg +++ b/gutenberg @@ -1 +1 @@ -Subproject commit 6358de362a89ec97eda748c704edd8d2e20f8a06 +Subproject commit 1dddab40b2ede5737e615390e6a41daaa27c4461 diff --git a/react-native-aztec/android/build.gradle b/react-native-aztec/android/build.gradle index cfe91c99c3..d4832192f6 100644 --- a/react-native-aztec/android/build.gradle +++ b/react-native-aztec/android/build.gradle @@ -12,7 +12,7 @@ buildscript { wordpressUtilsVersion = '1.22' espressoVersion = '3.0.1' - aztecVersion = 'list-block-fix-try2' + aztecVersion = 'list-block-fix-try3' } repositories { @@ -27,6 +27,7 @@ buildscript { } apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' apply plugin: 'com.github.dcendents.android-maven' // import the `readReactNativeVersion()` function @@ -62,6 +63,7 @@ android { main { dirs.each { dir -> java.srcDirs "src/${dir}/java" + java.srcDirs += "src/${dir}/kotlin" res.srcDirs "src/${dir}/res" } } diff --git a/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index 2b77940a48..76cab3b786 100644 --- a/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -42,7 +42,6 @@ import org.wordpress.aztec.plugins.wpcomments.HiddenGutenbergPlugin; import org.wordpress.aztec.plugins.wpcomments.WordPressCommentsPlugin; import org.wordpress.aztec.plugins.wpcomments.toolbar.MoreToolbarButton; -import org.wordpress.aztec.watchers.EnterPressedUnderway; import java.util.Map; import java.util.ArrayList; @@ -415,6 +414,11 @@ public void setOnPasteHandling(final ReactAztecText view, boolean onPasteHandlin view.shouldHandleOnPaste = onPasteHandling; } + @ReactProp(name = "deleteEnter", defaultBoolean = false) + public void setShouldDeleteEnter(final ReactAztecText view, boolean shouldDeleteEnter) { + view.shouldDeleteEnter = shouldDeleteEnter; + } + @Override public Map getCommandsMap() { return MapBuilder.builder() diff --git a/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java b/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java index 33837be261..fec01834db 100644 --- a/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java +++ b/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java @@ -61,6 +61,8 @@ public class ReactAztecText extends AztecText { boolean shouldHandleOnSelectionChange = false; boolean shouldHandleActiveFormatsChange = false; + boolean shouldDeleteEnter = false; + // This optional variable holds the outer HTML tag that will be added to the text when the user start typing in it // This is required to keep placeholder text working, and start typing with styled text. // Ref: https://github.com/wordpress-mobile/gutenberg-mobile/issues/707 @@ -328,6 +330,11 @@ public void addTextChangedListener(TextWatcher watcher) { if (mListeners == null) { mListeners = new ArrayList<>(); super.addTextChangedListener(getTextWatcherDelegator()); + + // Keep the enter pressed watcher at the beginning of the watchers list. + // We want to intercept Enter.key as soon as possible, and before other listeners start modifying the text. + // Also note that this Watchers, when the AztecKeyListener is set, keep hold a copy of the content in the editor. + mListeners.add(new EnterPressedWatcher(this, () -> shouldDeleteEnter)); } mListeners.add(watcher); @@ -458,6 +465,10 @@ public void setActiveFormats(Iterable newFormats) { updateToolbarButtons(newStylesList); } + protected boolean isEnterPressedUnderway() { + return EnterPressedWatcher.Companion.isEnterPressedUnderway(getText()); + } + /** * This class will redirect *TextChanged calls to the listeners only in the case where the text * is changed by the user, and not explicitly set by JS. diff --git a/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt b/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt new file mode 100644 index 0000000000..bd466a3a8c --- /dev/null +++ b/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt @@ -0,0 +1,71 @@ +package org.wordpress.mobile.ReactNativeAztec + +import android.text.Editable +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.TextWatcher +import org.wordpress.aztec.AztecText +import org.wordpress.aztec.Constants +import java.lang.ref.WeakReference + +// Class to be used as a span to temporarily denote that Enter was detected +class EnterPressedUnderway + +interface EnterDeleter { + fun shouldDeleteEnter(): Boolean +} + +class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) : TextWatcher { + + private val aztecTextRef: WeakReference = WeakReference(aztecText) + private lateinit var textBefore : SpannableStringBuilder + private var start: Int = -1 + private var selStart: Int = 0 + private var selEnd: Int = 0 + var done = false + + override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) { + val aztecText = aztecTextRef.get() + if (aztecText?.getAztecKeyListener() != null && !aztecText.isTextChangedListenerDisabled()) { + // we need to make a copy to preserve the contents as they were before the change + textBefore = SpannableStringBuilder(text) + this.start = start + this.selStart = aztecText.selectionStart + this.selEnd = aztecText.selectionEnd + } + } + + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) { + val aztecText = aztecTextRef.get() + val aztecKeyListener = aztecText?.getAztecKeyListener() + if (aztecText != null && !aztecText.isTextChangedListenerDisabled() && aztecKeyListener != null) { + val newTextCopy = SpannableStringBuilder(text) + // if new text length is longer than original text by 1 + if (textBefore?.length == newTextCopy.length - 1) { + // now check that the inserted character is actually a NEWLINE + if (newTextCopy[this.start] == Constants.NEWLINE) { + done = false + aztecText.editableText.setSpan(EnterPressedUnderway(), 0, 0, Spanned.SPAN_USER) + aztecKeyListener.onEnterKey(newTextCopy.replace(this.start, this.start+1, ""), true, selStart, selEnd) + } + } + } + } + + override fun afterTextChanged(text: Editable) { + aztecTextRef.get()?.editableText?.getSpans(0, 0, EnterPressedUnderway::class.java)?.forEach { + if (!done) { + done = true + if (enterDeleter.shouldDeleteEnter()) + text.replace(start, start + 1, "") + } + aztecTextRef.get()?.editableText?.removeSpan(it) + } + } + + companion object { + fun isEnterPressedUnderway(spanned: Spanned?): Boolean { + return spanned?.getSpans(0, 0, EnterPressedUnderway::class.java)?.isNotEmpty() ?: false + } + } +} diff --git a/react-native-aztec/src/AztecView.js b/react-native-aztec/src/AztecView.js index d0cf131676..d5da16e9e4 100644 --- a/react-native-aztec/src/AztecView.js +++ b/react-native-aztec/src/AztecView.js @@ -12,6 +12,7 @@ class AztecView extends React.Component { activeFormats: PropTypes.array, isSelected: PropTypes.bool, disableGutenbergMode: PropTypes.bool, + deleteEnter: PropTypes.bool, text: PropTypes.object, placeholder: PropTypes.string, placeholderTextColor: ColorPropType, @@ -158,6 +159,7 @@ class AztecView extends React.Component { onHTMLContentWithCursor = { this._onHTMLContentWithCursor } onSelectionChange = { this._onSelectionChange } onEnter = { this.props.onEnter && this._onEnter } + deleteEnter = { this.props.deleteEnter } // IMPORTANT: the onFocus events are thrown away as these are handled by onPress() in the upper level. // It's necessary to do this otherwise onFocus may be set by `{...otherProps}` and thus the onPress + onFocus // combination generate an infinite loop as described in https://github.com/wordpress-mobile/gutenberg-mobile/issues/302