@onekeyfe/react-native-auto-size-input 3.0.81 → 3.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/margelo/nitro/autosizeinput/AutoSizeInput.kt +36 -8
- package/android/src/main/java/com/margelo/nitro/autosizeinput/TextUpdateEventCounter.kt +46 -0
- package/android/src/test/java/com/margelo/nitro/autosizeinput/TextUpdateEventCounterTest.kt +147 -0
- package/ios/AutoSizeInput.swift +57 -0
- package/lib/nitrogen/generated/android/autosizeinput+autolinking.cmake +1 -1
- package/lib/nitrogen/generated/android/autosizeinput+autolinking.gradle +1 -1
- package/lib/nitrogen/generated/android/autosizeinputOnLoad.cpp +30 -20
- package/lib/nitrogen/generated/android/autosizeinputOnLoad.hpp +14 -5
- package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +3 -3
- package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +3 -3
- package/lib/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.cpp +92 -82
- package/lib/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.hpp +22 -22
- package/lib/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.cpp +108 -104
- package/lib/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.hpp +4 -4
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void.kt +1 -1
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void_std__string.kt +1 -1
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/HybridAutoSizeInputSpec.kt +23 -19
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/autosizeinputOnLoad.kt +1 -1
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputManager.kt +40 -10
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputStateUpdater.kt +1 -1
- package/lib/nitrogen/generated/ios/AutoSizeInput+autolinking.rb +2 -2
- package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.cpp +1 -1
- package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.hpp +20 -20
- package/lib/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Umbrella.hpp +1 -1
- package/lib/nitrogen/generated/ios/AutoSizeInputAutolinking.mm +1 -1
- package/lib/nitrogen/generated/ios/AutoSizeInputAutolinking.swift +9 -8
- package/lib/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.cpp +1 -1
- package/lib/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.hpp +15 -2
- package/lib/nitrogen/generated/ios/c++/views/HybridAutoSizeInputComponent.mm +32 -1
- package/lib/nitrogen/generated/ios/swift/Func_void.swift +1 -2
- package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +1 -2
- package/lib/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec.swift +4 -4
- package/lib/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec_cxx.swift +82 -7
- package/lib/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.cpp +3 -1
- package/lib/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.hpp +4 -2
- package/lib/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.cpp +21 -43
- package/lib/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.hpp +11 -7
- package/lib/nitrogen/generated/shared/json/AutoSizeInputConfig.json +1 -0
- package/lib/typescript/src/AutoSizeInput.nitro.d.ts +1 -0
- package/nitrogen/generated/android/autosizeinput+autolinking.cmake +1 -1
- package/nitrogen/generated/android/autosizeinput+autolinking.gradle +1 -1
- package/nitrogen/generated/android/autosizeinputOnLoad.cpp +30 -20
- package/nitrogen/generated/android/autosizeinputOnLoad.hpp +14 -5
- package/nitrogen/generated/android/c++/JFunc_void.hpp +3 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +3 -3
- package/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.cpp +92 -82
- package/nitrogen/generated/android/c++/JHybridAutoSizeInputSpec.hpp +22 -22
- package/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.cpp +108 -104
- package/nitrogen/generated/android/c++/views/JHybridAutoSizeInputStateUpdater.hpp +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/Func_void_std__string.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/HybridAutoSizeInputSpec.kt +23 -19
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/autosizeinputOnLoad.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputManager.kt +40 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/autosizeinput/views/HybridAutoSizeInputStateUpdater.kt +1 -1
- package/nitrogen/generated/ios/AutoSizeInput+autolinking.rb +2 -2
- package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.cpp +1 -1
- package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Bridge.hpp +20 -20
- package/nitrogen/generated/ios/AutoSizeInput-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/AutoSizeInputAutolinking.mm +1 -1
- package/nitrogen/generated/ios/AutoSizeInputAutolinking.swift +9 -8
- package/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.cpp +1 -1
- package/nitrogen/generated/ios/c++/HybridAutoSizeInputSpecSwift.hpp +15 -2
- package/nitrogen/generated/ios/c++/views/HybridAutoSizeInputComponent.mm +32 -1
- package/nitrogen/generated/ios/swift/Func_void.swift +1 -2
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +1 -2
- package/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec.swift +4 -4
- package/nitrogen/generated/ios/swift/HybridAutoSizeInputSpec_cxx.swift +82 -7
- package/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.cpp +3 -1
- package/nitrogen/generated/shared/c++/HybridAutoSizeInputSpec.hpp +4 -2
- package/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.cpp +21 -43
- package/nitrogen/generated/shared/c++/views/HybridAutoSizeInputComponent.hpp +11 -7
- package/nitrogen/generated/shared/json/AutoSizeInputConfig.json +1 -0
- package/package.json +4 -4
- package/src/AutoSizeInput.nitro.ts +1 -0
package/android/build.gradle
CHANGED
|
@@ -8,7 +8,6 @@ import android.graphics.drawable.GradientDrawable
|
|
|
8
8
|
import android.os.Looper
|
|
9
9
|
import android.text.Editable
|
|
10
10
|
import android.text.InputType
|
|
11
|
-
import android.view.inputmethod.EditorInfo
|
|
12
11
|
import android.text.StaticLayout
|
|
13
12
|
import android.text.TextPaint
|
|
14
13
|
import android.text.TextWatcher
|
|
@@ -16,6 +15,7 @@ import android.util.TypedValue
|
|
|
16
15
|
import android.view.Gravity
|
|
17
16
|
import android.view.View
|
|
18
17
|
import android.view.ViewGroup
|
|
18
|
+
import android.view.inputmethod.EditorInfo
|
|
19
19
|
import android.widget.EditText
|
|
20
20
|
import android.widget.TextView
|
|
21
21
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
@@ -36,6 +36,7 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
|
|
|
36
36
|
private var isDisposed = false
|
|
37
37
|
private var maxFontSizeProp: Double? = null
|
|
38
38
|
private var minFontSizeProp: Double? = null
|
|
39
|
+
private val textUpdateEventCounter = TextUpdateEventCounter()
|
|
39
40
|
|
|
40
41
|
// Container view
|
|
41
42
|
override val view: View = object : ViewGroup(context) {
|
|
@@ -57,25 +58,52 @@ class HybridAutoSizeInput(val context: ThemedReactContext) : HybridAutoSizeInput
|
|
|
57
58
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
|
58
59
|
override fun afterTextChanged(s: Editable?) {
|
|
59
60
|
if (isUpdatingFromJS || isDisposed) return
|
|
61
|
+
textUpdateEventCounter.recordNativeChange()
|
|
60
62
|
recalculateFontSize()
|
|
61
63
|
onChangeText?.invoke(s?.toString() ?: "")
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
// Last text JS requested (cached even when the update is rejected as
|
|
68
|
+
// stale): a sanitize-to-same-string edit is acknowledged with a count-only
|
|
69
|
+
// prop update, and the reapply below is what rolls the view back.
|
|
70
|
+
private var lastJsText: String? = null
|
|
71
|
+
|
|
65
72
|
// Props
|
|
73
|
+
override var mostRecentEventCount: Double?
|
|
74
|
+
get() = textUpdateEventCounter.mostRecentEventCount?.toDouble()
|
|
75
|
+
set(value) {
|
|
76
|
+
// null keeps the caller out of stale-update filtering (legacy callers
|
|
77
|
+
// that never send the count must not have their text updates dropped).
|
|
78
|
+
// Sanitization (finite, integral, non-negative, Int-range — identical
|
|
79
|
+
// to iOS) also degrades malformed values to uncounted.
|
|
80
|
+
val count = TextUpdateEventCounter.sanitizeAcknowledgement(value)
|
|
81
|
+
if (textUpdateEventCounter.acknowledge(count)) {
|
|
82
|
+
lastJsText?.let { applyJsText(it) }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
66
86
|
override var text: String?
|
|
67
87
|
get() = inputView.text?.toString()
|
|
68
88
|
set(value) {
|
|
69
89
|
if (isDisposed) return
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
inputView.setSelection(inputView.text?.length ?: 0)
|
|
74
|
-
inputView.addTextChangedListener(textWatcher)
|
|
75
|
-
isUpdatingFromJS = false
|
|
76
|
-
recalculateFontSize()
|
|
90
|
+
lastJsText = value ?: ""
|
|
91
|
+
if (!textUpdateEventCounter.canApplyJsUpdate()) return
|
|
92
|
+
applyJsText(value ?: "")
|
|
77
93
|
}
|
|
78
94
|
|
|
95
|
+
private fun applyJsText(nextText: String) {
|
|
96
|
+
if (isDisposed) return
|
|
97
|
+
if (inputView.text?.toString() == nextText) return
|
|
98
|
+
isUpdatingFromJS = true
|
|
99
|
+
inputView.removeTextChangedListener(textWatcher)
|
|
100
|
+
inputView.setText(nextText)
|
|
101
|
+
inputView.setSelection(inputView.text?.length ?: 0)
|
|
102
|
+
inputView.addTextChangedListener(textWatcher)
|
|
103
|
+
isUpdatingFromJS = false
|
|
104
|
+
recalculateFontSize()
|
|
105
|
+
}
|
|
106
|
+
|
|
79
107
|
override var prefix: String?
|
|
80
108
|
get() = prefixView.text?.toString()
|
|
81
109
|
set(value) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package com.margelo.nitro.autosizeinput
|
|
2
|
+
|
|
3
|
+
import kotlin.math.floor
|
|
4
|
+
|
|
5
|
+
internal class TextUpdateEventCounter {
|
|
6
|
+
companion object {
|
|
7
|
+
// Valid acknowledgements are exact, non-negative Int values; anything
|
|
8
|
+
// else from the JS Double prop (NaN, infinities, fractional, negative,
|
|
9
|
+
// beyond Int.MAX_VALUE) degrades to null = uncounted. Must stay in
|
|
10
|
+
// lockstep with the iOS validation so the same prop value can never be
|
|
11
|
+
// stale on one platform and current on the other.
|
|
12
|
+
fun sanitizeAcknowledgement(value: Double?): Int? {
|
|
13
|
+
if (value == null || !value.isFinite()) return null
|
|
14
|
+
if (value < 0 || value > Int.MAX_VALUE.toDouble()) return null
|
|
15
|
+
if (value != floor(value)) return null
|
|
16
|
+
return value.toInt()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
var nativeEventCount: Int = 0
|
|
20
|
+
private set
|
|
21
|
+
|
|
22
|
+
// null = the caller never supplied mostRecentEventCount. Such callers do not
|
|
23
|
+
// participate in stale-update filtering and their JS text updates always
|
|
24
|
+
// apply (programmatic clears, sanitization, form restores). Only callers
|
|
25
|
+
// that echo the event count back opt into the comparison.
|
|
26
|
+
var mostRecentEventCount: Int? = null
|
|
27
|
+
|
|
28
|
+
fun recordNativeChange() {
|
|
29
|
+
nativeEventCount += 1
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fun canApplyJsUpdate(): Boolean {
|
|
33
|
+
val acknowledged = mostRecentEventCount ?: return true
|
|
34
|
+
return acknowledged >= nativeEventCount
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Records the JS acknowledgement and reports whether the cached controlled
|
|
38
|
+
// text must be reapplied now: when JS sanitizes an edit back to the SAME
|
|
39
|
+
// string, React re-sends only the count (unchanged props are not re-sent),
|
|
40
|
+
// so catching up on a count-only update is the signal to roll the view back
|
|
41
|
+
// to the last requested JS text.
|
|
42
|
+
fun acknowledge(count: Int?): Boolean {
|
|
43
|
+
mostRecentEventCount = count
|
|
44
|
+
return count != null && canApplyJsUpdate()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
package com.margelo.nitro.autosizeinput
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertFalse
|
|
5
|
+
import org.junit.Assert.assertTrue
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
|
|
8
|
+
class TextUpdateEventCounterTest {
|
|
9
|
+
@Test
|
|
10
|
+
fun allowsJsUpdatesBeforeNativeInput() {
|
|
11
|
+
val counter = TextUpdateEventCounter()
|
|
12
|
+
|
|
13
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Test
|
|
17
|
+
fun allowsUncountedProgrammaticResetAfterNativeEdits() {
|
|
18
|
+
// Regression: callers that never supply mostRecentEventCount must keep
|
|
19
|
+
// full control — a native edit followed by an uncounted reset (clear,
|
|
20
|
+
// sanitization, form restore) has to apply.
|
|
21
|
+
val counter = TextUpdateEventCounter()
|
|
22
|
+
repeat(3) {
|
|
23
|
+
counter.recordNativeChange()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun sanitizesAcknowledgementsToExactNonNegativeInts() {
|
|
31
|
+
// Valid: exact non-negative integral doubles in the Int range.
|
|
32
|
+
assertEquals(0, TextUpdateEventCounter.sanitizeAcknowledgement(0.0))
|
|
33
|
+
assertEquals(3, TextUpdateEventCounter.sanitizeAcknowledgement(3.0))
|
|
34
|
+
assertEquals(
|
|
35
|
+
Int.MAX_VALUE,
|
|
36
|
+
TextUpdateEventCounter.sanitizeAcknowledgement(Int.MAX_VALUE.toDouble()),
|
|
37
|
+
)
|
|
38
|
+
// Everything else degrades to uncounted — fractional values must NOT be
|
|
39
|
+
// truncated or rounded (1.6 with a native count of 2 would otherwise be
|
|
40
|
+
// stale on Android yet current on iOS).
|
|
41
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(null))
|
|
42
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(1.6))
|
|
43
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(-1.0))
|
|
44
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(Double.NaN))
|
|
45
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(Double.POSITIVE_INFINITY))
|
|
46
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(Double.NEGATIVE_INFINITY))
|
|
47
|
+
assertEquals(null, TextUpdateEventCounter.sanitizeAcknowledgement(Int.MAX_VALUE.toDouble() + 1.0))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Test
|
|
51
|
+
fun countOnlyAcknowledgementRequestsReapplyOnceCaughtUp() {
|
|
52
|
+
// Sanitize-to-same-string edit: the user's rejected character bumped the
|
|
53
|
+
// native count, JS keeps its (unchanged) text and acknowledges via the
|
|
54
|
+
// count prop only — catching up must request a reapply of the cached
|
|
55
|
+
// JS text, a stale acknowledgement must not.
|
|
56
|
+
val counter = TextUpdateEventCounter()
|
|
57
|
+
counter.recordNativeChange()
|
|
58
|
+
counter.recordNativeChange()
|
|
59
|
+
|
|
60
|
+
assertFalse(counter.acknowledge(1))
|
|
61
|
+
assertTrue(counter.acknowledge(2))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Test
|
|
65
|
+
fun nullAcknowledgementNeverRequestsReapply() {
|
|
66
|
+
val counter = TextUpdateEventCounter()
|
|
67
|
+
counter.recordNativeChange()
|
|
68
|
+
|
|
69
|
+
assertFalse(counter.acknowledge(null))
|
|
70
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Test
|
|
74
|
+
fun optsIntoStaleFilteringOnlyOnceACountIsSupplied() {
|
|
75
|
+
val counter = TextUpdateEventCounter()
|
|
76
|
+
counter.recordNativeChange()
|
|
77
|
+
|
|
78
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
79
|
+
|
|
80
|
+
counter.mostRecentEventCount = 0
|
|
81
|
+
assertFalse(counter.canApplyJsUpdate())
|
|
82
|
+
|
|
83
|
+
counter.mostRecentEventCount = 1
|
|
84
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Test
|
|
88
|
+
fun rejectsJsUpdatesThatAreOlderThanNativeInput() {
|
|
89
|
+
val counter = TextUpdateEventCounter()
|
|
90
|
+
counter.recordNativeChange()
|
|
91
|
+
counter.recordNativeChange()
|
|
92
|
+
counter.mostRecentEventCount = 1
|
|
93
|
+
|
|
94
|
+
assertFalse(counter.canApplyJsUpdate())
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Test
|
|
98
|
+
fun allowsTheLatestAcknowledgedJsUpdate() {
|
|
99
|
+
val counter = TextUpdateEventCounter()
|
|
100
|
+
counter.recordNativeChange()
|
|
101
|
+
counter.recordNativeChange()
|
|
102
|
+
counter.mostRecentEventCount = 2
|
|
103
|
+
|
|
104
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Test
|
|
108
|
+
fun tracksEveryNativeTextChange() {
|
|
109
|
+
val counter = TextUpdateEventCounter()
|
|
110
|
+
|
|
111
|
+
repeat(10) {
|
|
112
|
+
counter.recordNativeChange()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
assertEquals(10, counter.nativeEventCount)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@Test
|
|
119
|
+
fun allowsAnAcknowledgedDeletionWithoutNewerInput() {
|
|
120
|
+
val counter = TextUpdateEventCounter()
|
|
121
|
+
repeat(4) {
|
|
122
|
+
counter.recordNativeChange()
|
|
123
|
+
}
|
|
124
|
+
counter.mostRecentEventCount = 4
|
|
125
|
+
counter.recordNativeChange()
|
|
126
|
+
counter.mostRecentEventCount = 5
|
|
127
|
+
|
|
128
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@Test
|
|
132
|
+
fun rejectsAStaleDeletionUpdateAfterTheNextDigit() {
|
|
133
|
+
val counter = TextUpdateEventCounter()
|
|
134
|
+
repeat(4) {
|
|
135
|
+
counter.recordNativeChange()
|
|
136
|
+
}
|
|
137
|
+
counter.mostRecentEventCount = 4
|
|
138
|
+
counter.recordNativeChange()
|
|
139
|
+
counter.recordNativeChange()
|
|
140
|
+
counter.mostRecentEventCount = 5
|
|
141
|
+
|
|
142
|
+
assertFalse(counter.canApplyJsUpdate())
|
|
143
|
+
|
|
144
|
+
counter.mostRecentEventCount = 6
|
|
145
|
+
assertTrue(counter.canApplyJsUpdate())
|
|
146
|
+
}
|
|
147
|
+
}
|
package/ios/AutoSizeInput.swift
CHANGED
|
@@ -20,9 +20,64 @@ class HybridAutoSizeInput: HybridAutoSizeInputSpec {
|
|
|
20
20
|
var view: UIView = UIView()
|
|
21
21
|
|
|
22
22
|
// MARK: - Props
|
|
23
|
+
// Mirrors Android's TextUpdateEventCounter: every user edit bumps
|
|
24
|
+
// nativeEventCount, and a controlled `text` update only applies once the
|
|
25
|
+
// caller's acknowledged count has caught up — otherwise a delayed JS value
|
|
26
|
+
// would overwrite newer typed input. Callers that never supply
|
|
27
|
+
// mostRecentEventCount do not participate and their updates always apply.
|
|
28
|
+
private var nativeEventCount = 0
|
|
29
|
+
|
|
30
|
+
// Last text JS requested (cached even when the update is rejected as
|
|
31
|
+
// stale): a sanitize-to-same-string edit is acknowledged with a count-only
|
|
32
|
+
// prop update, and the reapply in mostRecentEventCount.didSet is what rolls
|
|
33
|
+
// the view back.
|
|
34
|
+
private var lastJsText: String?
|
|
35
|
+
|
|
36
|
+
var mostRecentEventCount: Double? {
|
|
37
|
+
didSet {
|
|
38
|
+
// React does not re-send an unchanged text prop, so a count-only
|
|
39
|
+
// acknowledgement that catches up must reapply the cached JS text.
|
|
40
|
+
guard acknowledgedEventCount != nil, canApplyJsUpdate,
|
|
41
|
+
let pending = lastJsText, pending != currentInputText else { return }
|
|
42
|
+
text = pending
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// JS supplies the count as a Double; Int(_:) traps on NaN/infinity/
|
|
47
|
+
// out-of-range. Valid acknowledgements are exact, non-negative values in
|
|
48
|
+
// the Android Int range — identical to the Android sanitization, so the
|
|
49
|
+
// same prop value can never be stale on one platform and current on the
|
|
50
|
+
// other. Anything else degrades to uncounted (nil).
|
|
51
|
+
private var acknowledgedEventCount: Int? {
|
|
52
|
+
guard let acknowledged = mostRecentEventCount,
|
|
53
|
+
acknowledged.isFinite,
|
|
54
|
+
acknowledged >= 0,
|
|
55
|
+
acknowledged <= Double(Int32.max),
|
|
56
|
+
acknowledged.rounded(.down) == acknowledged
|
|
57
|
+
else { return nil }
|
|
58
|
+
return Int(acknowledged)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private var canApplyJsUpdate: Bool {
|
|
62
|
+
guard let acknowledged = acknowledgedEventCount else { return true }
|
|
63
|
+
return acknowledged >= nativeEventCount
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private var currentInputText: String {
|
|
67
|
+
(multiline == true ? multiLineInput.text : singleLineInput.text) ?? ""
|
|
68
|
+
}
|
|
69
|
+
|
|
23
70
|
var text: String? {
|
|
24
71
|
didSet {
|
|
25
72
|
guard !isUpdatingFromJS else { return }
|
|
73
|
+
lastJsText = text ?? ""
|
|
74
|
+
guard canApplyJsUpdate else {
|
|
75
|
+
// Stale controlled update: keep the freshly typed value and realign
|
|
76
|
+
// the stored prop with what is actually displayed. (Assigning here
|
|
77
|
+
// does not re-trigger didSet.)
|
|
78
|
+
text = currentInputText
|
|
79
|
+
return
|
|
80
|
+
}
|
|
26
81
|
isUpdatingFromJS = true
|
|
27
82
|
if multiline == true {
|
|
28
83
|
multiLineInput.text = text ?? ""
|
|
@@ -547,6 +602,7 @@ class HybridAutoSizeInput: HybridAutoSizeInputSpec {
|
|
|
547
602
|
|
|
548
603
|
fileprivate func handleTextFieldChange(_ textField: UITextField) {
|
|
549
604
|
guard !isUpdatingFromJS else { return }
|
|
605
|
+
nativeEventCount += 1
|
|
550
606
|
view.setNeedsLayout()
|
|
551
607
|
recalculateFontSize()
|
|
552
608
|
onChangeText?(textField.text ?? "")
|
|
@@ -554,6 +610,7 @@ class HybridAutoSizeInput: HybridAutoSizeInputSpec {
|
|
|
554
610
|
|
|
555
611
|
fileprivate func handleTextViewChange(_ textView: UITextView) {
|
|
556
612
|
guard !isUpdatingFromJS else { return }
|
|
613
|
+
nativeEventCount += 1
|
|
557
614
|
view.setNeedsLayout()
|
|
558
615
|
recalculateFontSize()
|
|
559
616
|
onChangeText?(textView.text ?? "")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# autosizeinput+autolinking.cmake
|
|
3
3
|
# This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
# https://github.com/mrousavy/nitro
|
|
5
|
-
# Copyright ©
|
|
5
|
+
# Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
#
|
|
7
7
|
|
|
8
8
|
# This is a CMake file that adds all files generated by Nitrogen
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// autosizeinput+autolinking.gradle
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
/// This is a Gradle file that adds all files generated by Nitrogen
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// autosizeinputOnLoad.cpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#ifndef BUILDING_AUTOSIZEINPUT_WITH_GENERATED_CMAKE_PROJECT
|
|
@@ -24,27 +24,37 @@
|
|
|
24
24
|
namespace margelo::nitro::autosizeinput {
|
|
25
25
|
|
|
26
26
|
int initialize(JavaVM* vm) {
|
|
27
|
+
return facebook::jni::initialize(vm, []() {
|
|
28
|
+
::margelo::nitro::autosizeinput::registerAllNatives();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
struct JHybridAutoSizeInputSpecImpl: public jni::JavaClass<JHybridAutoSizeInputSpecImpl, JHybridAutoSizeInputSpec::JavaPart> {
|
|
33
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/autosizeinput/HybridAutoSizeInput;";
|
|
34
|
+
static std::shared_ptr<JHybridAutoSizeInputSpec> create() {
|
|
35
|
+
static const auto constructorFn = javaClassStatic()->getConstructor<JHybridAutoSizeInputSpecImpl::javaobject()>();
|
|
36
|
+
jni::local_ref<JHybridAutoSizeInputSpec::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
|
|
37
|
+
return javaPart->getJHybridAutoSizeInputSpec();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
void registerAllNatives() {
|
|
27
42
|
using namespace margelo::nitro;
|
|
28
43
|
using namespace margelo::nitro::autosizeinput;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
auto instance = object.create();
|
|
44
|
-
return instance->cthis()->shared();
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
});
|
|
44
|
+
|
|
45
|
+
// Register native JNI methods
|
|
46
|
+
margelo::nitro::autosizeinput::JHybridAutoSizeInputSpec::CxxPart::registerNatives();
|
|
47
|
+
margelo::nitro::autosizeinput::JFunc_void_std__string_cxx::registerNatives();
|
|
48
|
+
margelo::nitro::autosizeinput::JFunc_void_cxx::registerNatives();
|
|
49
|
+
margelo::nitro::autosizeinput::views::JHybridAutoSizeInputStateUpdater::registerNatives();
|
|
50
|
+
|
|
51
|
+
// Register Nitro Hybrid Objects
|
|
52
|
+
HybridObjectRegistry::registerHybridObjectConstructor(
|
|
53
|
+
"AutoSizeInput",
|
|
54
|
+
[]() -> std::shared_ptr<HybridObject> {
|
|
55
|
+
return JHybridAutoSizeInputSpecImpl::create();
|
|
56
|
+
}
|
|
57
|
+
);
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
} // namespace margelo::nitro::autosizeinput
|
|
@@ -2,24 +2,33 @@
|
|
|
2
2
|
/// autosizeinputOnLoad.hpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#include <jni.h>
|
|
9
|
+
#include <functional>
|
|
9
10
|
#include <NitroModules/NitroDefines.hpp>
|
|
10
11
|
|
|
11
12
|
namespace margelo::nitro::autosizeinput {
|
|
12
13
|
|
|
14
|
+
[[deprecated("Use registerNatives() instead.")]]
|
|
15
|
+
int initialize(JavaVM* vm);
|
|
16
|
+
|
|
13
17
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`)
|
|
18
|
+
* Register the native (C++) part of autosizeinput, and autolinks all Hybrid Objects.
|
|
19
|
+
* Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`),
|
|
20
|
+
* inside a `facebook::jni::initialize(vm, ...)` call.
|
|
16
21
|
* Example:
|
|
17
22
|
* ```cpp (cpp-adapter.cpp)
|
|
18
23
|
* JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
19
|
-
* return
|
|
24
|
+
* return facebook::jni::initialize(vm, []() {
|
|
25
|
+
* // register all autosizeinput HybridObjects
|
|
26
|
+
* margelo::nitro::autosizeinput::registerNatives();
|
|
27
|
+
* // any other custom registrations go here.
|
|
28
|
+
* });
|
|
20
29
|
* }
|
|
21
30
|
* ```
|
|
22
31
|
*/
|
|
23
|
-
|
|
32
|
+
void registerAllNatives();
|
|
24
33
|
|
|
25
34
|
} // namespace margelo::nitro::autosizeinput
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// JFunc_void.hpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
@@ -23,7 +23,7 @@ namespace margelo::nitro::autosizeinput {
|
|
|
23
23
|
*/
|
|
24
24
|
struct JFunc_void: public jni::JavaClass<JFunc_void> {
|
|
25
25
|
public:
|
|
26
|
-
static auto
|
|
26
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/autosizeinput/Func_void;";
|
|
27
27
|
|
|
28
28
|
public:
|
|
29
29
|
/**
|
|
@@ -59,7 +59,7 @@ namespace margelo::nitro::autosizeinput {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
public:
|
|
62
|
-
static auto
|
|
62
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/autosizeinput/Func_void_cxx;";
|
|
63
63
|
static void registerNatives() {
|
|
64
64
|
registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_cxx::invoke_cxx)});
|
|
65
65
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// JFunc_void_std__string.hpp
|
|
3
3
|
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
4
|
/// https://github.com/mrousavy/nitro
|
|
5
|
-
/// Copyright ©
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
6
|
///
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
@@ -24,7 +24,7 @@ namespace margelo::nitro::autosizeinput {
|
|
|
24
24
|
*/
|
|
25
25
|
struct JFunc_void_std__string: public jni::JavaClass<JFunc_void_std__string> {
|
|
26
26
|
public:
|
|
27
|
-
static auto
|
|
27
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/autosizeinput/Func_void_std__string;";
|
|
28
28
|
|
|
29
29
|
public:
|
|
30
30
|
/**
|
|
@@ -60,7 +60,7 @@ namespace margelo::nitro::autosizeinput {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
public:
|
|
63
|
-
static auto
|
|
63
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/autosizeinput/Func_void_std__string_cxx;";
|
|
64
64
|
static void registerNatives() {
|
|
65
65
|
registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_std__string_cxx::invoke_cxx)});
|
|
66
66
|
}
|