Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f8fad51e8b31224b04d7933da8affb3a>>
* @generated SignedSource<<b2491b9c519d6ff211a760974f72c006>>
*/

/**
Expand Down Expand Up @@ -402,6 +402,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact()

/**
* Flushes TextInput state updates synchronously on Android when a multiline input changes line count, so the new height mounts in the same frame instead of the content jumping. Pairs with enableFollowUpMountItemDispatchAndroid for updates that happen during mount dispatch.
*/
@JvmStatic
public fun fixMultilineContentJumpAndroid(): Boolean = accessor.fixMultilineContentJumpAndroid()

/**
* Re-measures text and re-sends padding and overflow insets to mounted views when the pixel density changes, so text and padded views do not keep sizes from the old density.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<846a8685f662d66dbcdaa4324c772d51>>
* @generated SignedSource<<eed52c54382753f03848f15fe822f36e>>
*/

/**
Expand Down Expand Up @@ -82,6 +82,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
private var fixBorderlessRippleAndroidCache: Boolean? = null
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
private var fixMultilineContentJumpAndroidCache: Boolean? = null
private var fixTextLayoutOnPixelDensityChangeCache: Boolean? = null
private var fixYogaFlexBasisFitContentInMainAxisCache: Boolean? = null
private var fuseboxAssertSingleHostStateCache: Boolean? = null
Expand Down Expand Up @@ -676,6 +677,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun fixMultilineContentJumpAndroid(): Boolean {
var cached = fixMultilineContentJumpAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.fixMultilineContentJumpAndroid()
fixMultilineContentJumpAndroidCache = cached
}
return cached
}

override fun fixTextLayoutOnPixelDensityChange(): Boolean {
var cached = fixTextLayoutOnPixelDensityChangeCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9ccf3958e1db2db295ba73dc371b5185>>
* @generated SignedSource<<195a56178c63d9535dea6cd541f6d059>>
*/

/**
Expand Down Expand Up @@ -152,6 +152,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean

@DoNotStrip @JvmStatic public external fun fixMultilineContentJumpAndroid(): Boolean

@DoNotStrip @JvmStatic public external fun fixTextLayoutOnPixelDensityChange(): Boolean

@DoNotStrip @JvmStatic public external fun fixYogaFlexBasisFitContentInMainAxis(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0a04c5e0a0b418f2efd4b253646ae7c1>>
* @generated SignedSource<<4a0424eab4b27bb7a79ebb130821712a>>
*/

/**
Expand Down Expand Up @@ -147,6 +147,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false

override fun fixMultilineContentJumpAndroid(): Boolean = false

override fun fixTextLayoutOnPixelDensityChange(): Boolean = false

override fun fixYogaFlexBasisFitContentInMainAxis(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<861063ef7d217992d3cecd410e25d885>>
* @generated SignedSource<<61e361921129768f95948e9b260cd69c>>
*/

/**
Expand Down Expand Up @@ -86,6 +86,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableVirtualViewContainerStateExperimentalCache: Boolean? = null
private var fixBorderlessRippleAndroidCache: Boolean? = null
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
private var fixMultilineContentJumpAndroidCache: Boolean? = null
private var fixTextLayoutOnPixelDensityChangeCache: Boolean? = null
private var fixYogaFlexBasisFitContentInMainAxisCache: Boolean? = null
private var fuseboxAssertSingleHostStateCache: Boolean? = null
Expand Down Expand Up @@ -742,6 +743,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}

override fun fixMultilineContentJumpAndroid(): Boolean {
var cached = fixMultilineContentJumpAndroidCache
if (cached == null) {
cached = currentProvider.fixMultilineContentJumpAndroid()
accessedFeatureFlags.add("fixMultilineContentJumpAndroid")
fixMultilineContentJumpAndroidCache = cached
}
return cached
}

override fun fixTextLayoutOnPixelDensityChange(): Boolean {
var cached = fixTextLayoutOnPixelDensityChangeCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<5fa21c808df68be4c007469040fbca2a>>
* @generated SignedSource<<383a53db2b32a9ac98cd7e47602eb43e>>
*/

/**
Expand Down Expand Up @@ -147,6 +147,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean

@DoNotStrip public fun fixMultilineContentJumpAndroid(): Boolean

@DoNotStrip public fun fixTextLayoutOnPixelDensityChange(): Boolean

@DoNotStrip public fun fixYogaFlexBasisFitContentInMainAxis(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,27 @@ import android.text.TextWatcher
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.uimanager.StateWrapper.UpdateMode
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.EventDispatcher

@OptIn(UnstableReactNativeAPI::class)
internal class ReactTextInputTextWatcher(
reactContext: ReactContext,
private val editText: ReactEditText,
) : TextWatcher {
private val eventDispatcher: EventDispatcher? = UIManagerHelper.getEventDispatcher(reactContext)
private val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
private var previousText: String? = null
private var previousLineCount: Int? = null
private var pendingEventCount: Int? = null

override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
// Incoming charSequence gets mutated before onTextChanged() is invoked
previousText = s.toString()
previousLineCount = editText.layout?.lineCount
}

override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
Expand All @@ -46,13 +53,13 @@ internal class ReactTextInputTextWatcher(
return
}

val stateWrapper = editText.stateWrapper

if (stateWrapper != null) {
val newStateData: WritableMap = WritableNativeMap()
newStateData.putInt("mostRecentEventCount", editText.incrementAndGetEventCounter())
newStateData.putInt("opaqueCacheId", editText.id)
stateWrapper.updateState(newStateData)
if (editText.stateWrapper != null) {
val eventCount = editText.incrementAndGetEventCounter()
if (shouldUpdateSynchronously) {
pendingEventCount = eventCount
} else {
updateState(eventCount)
}
}

// The event that contains the event counter and updates it must be sent first.
Expand All @@ -68,5 +75,31 @@ internal class ReactTextInputTextWatcher(
)
}

override fun afterTextChanged(s: Editable) = Unit
override fun afterTextChanged(s: Editable) {
// Update here rather than in on TextChanged since ReactEditText updates the cached spannable
// only after every TextWatcher has seen onTextChanged
pendingEventCount?.let { eventCount ->
// A synchronous update flushes all enqueued state updates. Mounting a queued text update
// calls maybeSetTextFromState() which re-runs this callback, so clear pendingEventCount first
pendingEventCount = null
updateState(eventCount, UpdateMode.unstable_Immediate)
}
}

private fun updateState(eventCount: Int, updateMode: UpdateMode = UpdateMode.Asynchronous) {
val stateWrapper = editText.stateWrapper ?: return
val newStateData: WritableMap = WritableNativeMap()
newStateData.putInt("mostRecentEventCount", eventCount)
newStateData.putInt("opaqueCacheId", editText.id)
stateWrapper.updateState(newStateData, updateMode)
}

private val shouldUpdateSynchronously: Boolean
get() {
if (!ReactNativeFeatureFlags.fixMultilineContentJumpAndroid()) {
return false
}
val lineCount = editText.layout?.lineCount
return editText.isMultiline && lineCount != null && lineCount != previousLineCount
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<7e430d5b575ea983d53ecde87c4dc9f0>>
* @generated SignedSource<<f4fcec6774ae07298cc5539ab1e2f5e4>>
*/

/**
Expand Down Expand Up @@ -411,6 +411,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}

bool fixMultilineContentJumpAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixMultilineContentJumpAndroid");
return method(javaProvider_);
}

bool fixTextLayoutOnPixelDensityChange() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixTextLayoutOnPixelDensityChange");
Expand Down Expand Up @@ -935,6 +941,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabri
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
}

bool JReactNativeFeatureFlagsCxxInterop::fixMultilineContentJumpAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::fixMultilineContentJumpAndroid();
}

bool JReactNativeFeatureFlagsCxxInterop::fixTextLayoutOnPixelDensityChange(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::fixTextLayoutOnPixelDensityChange();
Expand Down Expand Up @@ -1327,6 +1338,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"fixMappingOfEventPrioritiesBetweenFabricAndReact",
JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact),
makeNativeMethod(
"fixMultilineContentJumpAndroid",
JReactNativeFeatureFlagsCxxInterop::fixMultilineContentJumpAndroid),
makeNativeMethod(
"fixTextLayoutOnPixelDensityChange",
JReactNativeFeatureFlagsCxxInterop::fixTextLayoutOnPixelDensityChange),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<6b96c286c25d3c9fcb66d39d67092787>>
* @generated SignedSource<<f34849eb6e11940b3d369d8cdf129b85>>
*/

/**
Expand Down Expand Up @@ -216,6 +216,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool fixMultilineContentJumpAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool fixTextLayoutOnPixelDensityChange(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<493a51a28c2f55411c5c5aea374638d1>>
* @generated SignedSource<<11e376a5469fb31e25416f43c895b28b>>
*/

/**
Expand Down Expand Up @@ -274,6 +274,10 @@ bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact()
return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact();
}

bool ReactNativeFeatureFlags::fixMultilineContentJumpAndroid() {
return getAccessor().fixMultilineContentJumpAndroid();
}

bool ReactNativeFeatureFlags::fixTextLayoutOnPixelDensityChange() {
return getAccessor().fixTextLayoutOnPixelDensityChange();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2d7aa66f222fa01d4cc93c7d5ef2fdb7>>
* @generated SignedSource<<7b76517f0c1d334fad73a96881a2f377>>
*/

/**
Expand Down Expand Up @@ -351,6 +351,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool fixMappingOfEventPrioritiesBetweenFabricAndReact();

/**
* Flushes TextInput state updates synchronously on Android when a multiline input changes line count, so the new height mounts in the same frame instead of the content jumping. Pairs with enableFollowUpMountItemDispatchAndroid for updates that happen during mount dispatch.
*/
RN_EXPORT static bool fixMultilineContentJumpAndroid();

/**
* Re-measures text and re-sends padding and overflow insets to mounted views when the pixel density changes, so text and padded views do not keep sizes from the old density.
*/
Expand Down
Loading
Loading