@whetware/react-native-stroke-text 0.0.11 → 0.0.13

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/android/src/main/cpp/cpp-adapter.cpp +4 -1
  3. package/android/src/main/java/com/margelo/nitro/stroketext/HybridStrokeTextView.kt +5 -11
  4. package/android/src/main/java/com/margelo/nitro/stroketext/NitroStrokeTextPackage.kt +2 -2
  5. package/android/src/main/java/com/margelo/nitro/stroketext/StrokeTextView.kt +8 -17
  6. package/lib/StrokeText.js +22 -1
  7. package/nitro.json +8 -2
  8. package/nitrogen/generated/android/NitroStrokeTextOnLoad.cpp +27 -17
  9. package/nitrogen/generated/android/NitroStrokeTextOnLoad.hpp +13 -4
  10. package/nitrogen/generated/android/c++/JHybridStrokeTextViewSpec.cpp +72 -78
  11. package/nitrogen/generated/android/c++/JHybridStrokeTextViewSpec.hpp +19 -22
  12. package/nitrogen/generated/android/c++/JStrokeTextAlign.hpp +2 -2
  13. package/nitrogen/generated/android/c++/JStrokeTextAlignVertical.hpp +2 -2
  14. package/nitrogen/generated/android/c++/JStrokeTextDecorationLine.hpp +2 -2
  15. package/nitrogen/generated/android/c++/JStrokeTextEllipsizeMode.hpp +2 -2
  16. package/nitrogen/generated/android/c++/JStrokeTextFontStyle.hpp +2 -2
  17. package/nitrogen/generated/android/c++/JStrokeTextTransform.hpp +2 -2
  18. package/nitrogen/generated/android/c++/views/JHybridStrokeTextViewStateUpdater.cpp +94 -94
  19. package/nitrogen/generated/android/c++/views/JHybridStrokeTextViewStateUpdater.hpp +3 -3
  20. package/nitrogen/generated/android/kotlin/com/margelo/nitro/stroketext/HybridStrokeTextViewSpec.kt +16 -18
  21. package/nitrogen/generated/android/kotlin/com/margelo/nitro/stroketext/views/HybridStrokeTextViewManager.kt +13 -3
  22. package/nitrogen/generated/ios/NitroStrokeText+autolinking.rb +2 -0
  23. package/nitrogen/generated/ios/NitroStrokeText-Swift-Cxx-Bridge.hpp +9 -9
  24. package/nitrogen/generated/ios/c++/views/HybridStrokeTextViewComponent.mm +15 -0
  25. package/nitrogen/generated/ios/swift/HybridStrokeTextViewSpec.swift +0 -1
  26. package/nitrogen/generated/ios/swift/HybridStrokeTextViewSpec_cxx.swift +147 -46
  27. package/nitrogen/generated/shared/c++/views/HybridStrokeTextViewComponent.cpp +5 -36
  28. package/nitrogen/generated/shared/c++/views/HybridStrokeTextViewComponent.hpp +8 -5
  29. package/package.json +10 -10
  30. package/android/src/main/java/com/margelo/nitro/stroketext/views/StrokeTextViewManager.kt +0 -15
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @whetware/react-native-stroke-text
2
2
 
3
+ ## 0.0.13
4
+
5
+ ### Patch Changes
6
+
7
+ - [`f94116e`](https://github.com/whetware/react-native-stroke-text/commit/f94116e693d2ee77e643d952f6943728e9d52c25) Thanks [@evelant](https://github.com/evelant)! - Fix Android stroke-text wrapping by measuring the outline inset as part of layout, drawing the RN-measured line breaks in the native overlay, aligning native text metrics with React Native Text, and preserving separate fill and outline colors.
8
+
9
+ Regenerate Nitro native bindings with Nitro/Nitrogen 0.36.1 so the Android C++ and Kotlin code builds with React Native 0.86 / Expo 57, including fbjni's current `HybridClass` requirements.
10
+
3
11
  ## 0.0.6
4
12
 
5
13
  ### Patch Changes
@@ -1,6 +1,9 @@
1
1
  #include <jni.h>
2
+ #include <fbjni/fbjni.h>
2
3
  #include "NitroStrokeTextOnLoad.hpp"
3
4
 
4
5
  JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5
- return margelo::nitro::stroketext::initialize(vm);
6
+ return facebook::jni::initialize(vm, []() {
7
+ margelo::nitro::stroketext::registerAllNatives();
8
+ });
6
9
  }
@@ -1,8 +1,8 @@
1
1
  package com.margelo.nitro.stroketext
2
2
 
3
3
  import android.view.View
4
- import com.facebook.jni.HybridData
5
4
  import com.facebook.react.uimanager.ThemedReactContext
5
+ import com.margelo.nitro.R.id.associated_hybrid_view_tag
6
6
  import kotlin.math.ceil
7
7
 
8
8
  class HybridStrokeTextView(context: ThemedReactContext) : HybridStrokeTextViewSpec() {
@@ -43,17 +43,11 @@ class HybridStrokeTextView(context: ThemedReactContext) : HybridStrokeTextViewSp
43
43
  if (isDisposed) return
44
44
  isDisposed = true
45
45
  super.dispose()
46
+ }
46
47
 
47
- try {
48
- val hybridDataField = HybridStrokeTextViewSpec::class.java.getDeclaredField("mHybridData")
49
- hybridDataField.isAccessible = true
50
- val hybridData = hybridDataField.get(this) as? HybridData ?: return
51
-
52
- val resetNativeMethod = HybridData::class.java.getDeclaredMethod("resetNative")
53
- resetNativeMethod.isAccessible = true
54
- resetNativeMethod.invoke(hybridData)
55
- } catch (_: Throwable) {
56
- }
48
+ override fun onDropView() {
49
+ strokeTextView.setTag(associated_hybrid_view_tag, null)
50
+ dispose()
57
51
  }
58
52
 
59
53
  override fun afterUpdate() {
@@ -7,12 +7,12 @@ import com.facebook.react.BaseReactPackage
7
7
  import com.facebook.react.ViewManagerOnDemandReactPackage
8
8
  import com.facebook.react.bridge.ModuleSpec
9
9
  import com.facebook.react.uimanager.ViewManager
10
- import com.margelo.nitro.stroketext.views.StrokeTextViewManager
10
+ import com.margelo.nitro.stroketext.views.HybridStrokeTextViewManager
11
11
 
12
12
  class NitroStrokeTextPackage : BaseReactPackage(), ViewManagerOnDemandReactPackage {
13
13
  private val viewManagers: Map<String, ModuleSpec> by lazy {
14
14
  mapOf(
15
- "StrokeTextView" to ModuleSpec.viewManagerSpec { StrokeTextViewManager() }
15
+ "StrokeTextView" to ModuleSpec.viewManagerSpec { HybridStrokeTextViewManager() }
16
16
  )
17
17
  }
18
18
 
@@ -5,6 +5,7 @@ import android.graphics.Color
5
5
  import android.graphics.Paint
6
6
  import android.graphics.Paint.FontMetricsInt
7
7
  import android.graphics.Typeface
8
+ import android.content.res.ColorStateList
8
9
  import android.os.Build
9
10
  import android.text.Layout
10
11
  import android.text.TextDirectionHeuristics
@@ -71,9 +72,9 @@ internal class StrokeTextView(context: ThemedReactContext) : TextView(context) {
71
72
  }
72
73
 
73
74
  override fun onDraw(canvas: Canvas) {
74
- // Draw stroke behind fill, using TextView's layout so metrics match RN <Text/> as closely as
75
- // possible (especially for bold fonts).
76
- val layout = layout
75
+ // Draw stroke behind fill through TextView's normal drawing path. Calling super.onDraw()
76
+ // for both passes keeps Android's TextView layout, glyph shaping, and color state handling
77
+ // consistent with the fill render while still letting us switch the paint style for outline.
77
78
  if (layout != null && strokeWidthPx > 0f && strokeColor != Color.TRANSPARENT) {
78
79
  val textPaint = paint
79
80
  val prevStyle = textPaint.style
@@ -81,29 +82,23 @@ internal class StrokeTextView(context: ThemedReactContext) : TextView(context) {
81
82
  val prevStrokeJoin = textPaint.strokeJoin
82
83
  val prevStrokeCap = textPaint.strokeCap
83
84
  val prevColor = textPaint.color
85
+ val prevTextColors: ColorStateList = textColors
84
86
  val prevUnderline = textPaint.isUnderlineText
85
87
  val prevStrike = textPaint.isStrikeThruText
86
88
 
87
- val saveCount = canvas.save()
88
- val compoundPaddingLeft = compoundPaddingLeft
89
- val extendedPaddingTop = extendedPaddingTop
90
-
91
- canvas.translate(compoundPaddingLeft.toFloat(), extendedPaddingTop.toFloat())
92
- canvas.translate(-scrollX.toFloat(), -scrollY.toFloat())
93
-
94
89
  // Only stroke the glyph outlines; keep underline/strike in the fill pass.
95
90
  textPaint.isUnderlineText = false
96
91
  textPaint.isStrikeThruText = false
97
92
 
93
+ setTextColor(strokeColor)
98
94
  textPaint.style = Paint.Style.STROKE
99
95
  textPaint.strokeJoin = Paint.Join.ROUND
100
96
  textPaint.strokeCap = Paint.Cap.ROUND
101
97
  textPaint.strokeWidth = strokeWidthPx
102
98
  textPaint.color = strokeColor
103
- layout.draw(canvas)
104
-
105
- canvas.restoreToCount(saveCount)
99
+ super.onDraw(canvas)
106
100
 
101
+ setTextColor(prevTextColors)
107
102
  textPaint.style = prevStyle
108
103
  textPaint.strokeWidth = prevStrokeWidth
109
104
  textPaint.strokeJoin = prevStrokeJoin
@@ -196,10 +191,6 @@ internal class StrokeTextView(context: ThemedReactContext) : TextView(context) {
196
191
 
197
192
  // Line limits / ellipsizing
198
193
  val maxLines = if (numberOfLines > 0) numberOfLines else Int.MAX_VALUE
199
- // React Native's single-line Text does not wrap at spaces. Without horizontal scrolling,
200
- // Android TextView can create an internal second line inside a host view that was measured
201
- // as one line by the hidden RN Text used for layout.
202
- setHorizontallyScrolling(numberOfLines == 1)
203
194
  setMaxLines(maxLines)
204
195
  ellipsize =
205
196
  if (numberOfLines <= 0) {
package/lib/StrokeText.js CHANGED
@@ -114,6 +114,12 @@ export function StrokeText({ text, children, style, hybridRef, ...rest }) {
114
114
  const [measuredNativeText, setMeasuredNativeText] = React.useState(undefined);
115
115
  const nativeText = measuredNativeText ?? resolvedText;
116
116
  const nativeTextTransform = measuredNativeText == null ? effectiveTextTransform : 'none';
117
+ // RN's hidden Text owns measurement, but Android TextView gets its own layout pass.
118
+ // Compensate for the native stroke inset on every edge, then give that native
119
+ // pass extra right-side room so one-pixel/font-metric drift cannot turn an RN
120
+ // single line into a native soft wrap.
121
+ const nativeOverlayInset = Platform.OS === 'android' && strokeInset > 0 ? strokeInset : 0;
122
+ const nativeOverlayRightInset = nativeOverlayInset > 0 ? nativeOverlayInset * 2 : 0;
117
123
  const wrappedHybridRef = React.useMemo(() => (hybridRef ? callback(hybridRef) : undefined), [hybridRef]);
118
124
  React.useEffect(() => {
119
125
  setMeasuredNativeText(undefined);
@@ -141,6 +147,11 @@ export function StrokeText({ text, children, style, hybridRef, ...rest }) {
141
147
  const handleTextLayout = React.useCallback((event) => {
142
148
  if (!shouldSyncMeasuredLineBreaks)
143
149
  return;
150
+ const lineCount = event.nativeEvent.lines.length;
151
+ if (lineCount <= 1) {
152
+ setMeasuredNativeText((currentText) => currentText == null ? currentText : undefined);
153
+ return;
154
+ }
144
155
  const nextText = toNativeMeasuredText(event.nativeEvent.lines);
145
156
  if (nextText == null)
146
157
  return;
@@ -189,7 +200,17 @@ export function StrokeText({ text, children, style, hybridRef, ...rest }) {
189
200
  },
190
201
  styles.hiddenText,
191
202
  ] }, resolvedText),
192
- React.createElement(NativeStrokeTextView, { ...nativeProps, text: nativeText, color: effectiveColor, fontSize: effectiveFontSize, fontWeight: effectiveFontWeight, fontFamily: effectiveFontFamily, fontStyle: effectiveFontStyle, lineHeight: effectiveLineHeight, letterSpacing: effectiveLetterSpacing, textAlign: effectiveTextAlign, textAlignVertical: effectiveTextAlignVertical, textDecorationLine: effectiveTextDecorationLine, textTransform: nativeTextTransform, opacity: nativeProps.opacity ?? toNumber(styleOpacity), includeFontPadding: effectiveIncludeFontPadding, numberOfLines: nativeProps.numberOfLines, ellipsizeMode: effectiveEllipsizeMode, paddingTop: baseTop, paddingRight: baseRight, paddingBottom: baseBottom, paddingLeft: baseLeft, hybridRef: wrappedHybridRef, pointerEvents: "none", style: styles.overlay })));
203
+ React.createElement(NativeStrokeTextView, { ...nativeProps, text: nativeText, color: effectiveColor, fontSize: effectiveFontSize, fontWeight: effectiveFontWeight, fontFamily: effectiveFontFamily, fontStyle: effectiveFontStyle, lineHeight: effectiveLineHeight, letterSpacing: effectiveLetterSpacing, textAlign: effectiveTextAlign, textAlignVertical: effectiveTextAlignVertical, textDecorationLine: effectiveTextDecorationLine, textTransform: nativeTextTransform, opacity: nativeProps.opacity ?? toNumber(styleOpacity), includeFontPadding: effectiveIncludeFontPadding, numberOfLines: nativeProps.numberOfLines, ellipsizeMode: effectiveEllipsizeMode, paddingTop: baseTop, paddingRight: baseRight, paddingBottom: baseBottom, paddingLeft: baseLeft, hybridRef: wrappedHybridRef, pointerEvents: "none", style: [
204
+ styles.overlay,
205
+ nativeOverlayInset > 0
206
+ ? {
207
+ top: -nativeOverlayInset,
208
+ right: -nativeOverlayRightInset,
209
+ bottom: -nativeOverlayInset,
210
+ left: -nativeOverlayInset,
211
+ }
212
+ : null,
213
+ ] })));
193
214
  }
194
215
  const styles = StyleSheet.create({
195
216
  container: {},
package/nitro.json CHANGED
@@ -14,8 +14,14 @@
14
14
  },
15
15
  "autolinking": {
16
16
  "StrokeTextView": {
17
- "swift": "HybridStrokeTextView",
18
- "kotlin": "HybridStrokeTextView"
17
+ "ios": {
18
+ "language": "swift",
19
+ "implementationClassName": "HybridStrokeTextView"
20
+ },
21
+ "android": {
22
+ "language": "kotlin",
23
+ "implementationClassName": "HybridStrokeTextView"
24
+ }
19
25
  }
20
26
  },
21
27
  "ignorePaths": [
@@ -22,25 +22,35 @@
22
22
  namespace margelo::nitro::stroketext {
23
23
 
24
24
  int initialize(JavaVM* vm) {
25
+ return facebook::jni::initialize(vm, []() {
26
+ ::margelo::nitro::stroketext::registerAllNatives();
27
+ });
28
+ }
29
+
30
+ struct JHybridStrokeTextViewSpecImpl: public jni::JavaClass<JHybridStrokeTextViewSpecImpl, JHybridStrokeTextViewSpec::JavaPart> {
31
+ static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/stroketext/HybridStrokeTextView;";
32
+ static std::shared_ptr<JHybridStrokeTextViewSpec> create() {
33
+ static const auto constructorFn = javaClassStatic()->getConstructor<JHybridStrokeTextViewSpecImpl::javaobject()>();
34
+ jni::local_ref<JHybridStrokeTextViewSpec::JavaPart> javaPart = javaClassStatic()->newObject(constructorFn);
35
+ return javaPart->getJHybridStrokeTextViewSpec();
36
+ }
37
+ };
38
+
39
+ void registerAllNatives() {
25
40
  using namespace margelo::nitro;
26
41
  using namespace margelo::nitro::stroketext;
27
- using namespace facebook;
28
-
29
- return facebook::jni::initialize(vm, [] {
30
- // Register native JNI methods
31
- margelo::nitro::stroketext::JHybridStrokeTextViewSpec::registerNatives();
32
- margelo::nitro::stroketext::views::JHybridStrokeTextViewStateUpdater::registerNatives();
33
-
34
- // Register Nitro Hybrid Objects
35
- HybridObjectRegistry::registerHybridObjectConstructor(
36
- "StrokeTextView",
37
- []() -> std::shared_ptr<HybridObject> {
38
- static DefaultConstructableObject<JHybridStrokeTextViewSpec::javaobject> object("com/margelo/nitro/stroketext/HybridStrokeTextView");
39
- auto instance = object.create();
40
- return instance->cthis()->shared();
41
- }
42
- );
43
- });
42
+
43
+ // Register native JNI methods
44
+ margelo::nitro::stroketext::JHybridStrokeTextViewSpec::CxxPart::registerNatives();
45
+ margelo::nitro::stroketext::views::JHybridStrokeTextViewStateUpdater::registerNatives();
46
+
47
+ // Register Nitro Hybrid Objects
48
+ HybridObjectRegistry::registerHybridObjectConstructor(
49
+ "StrokeTextView",
50
+ []() -> std::shared_ptr<HybridObject> {
51
+ return JHybridStrokeTextViewSpecImpl::create();
52
+ }
53
+ );
44
54
  }
45
55
 
46
56
  } // namespace margelo::nitro::stroketext
@@ -6,20 +6,29 @@
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::stroketext {
12
13
 
14
+ [[deprecated("Use registerNatives() instead.")]]
15
+ int initialize(JavaVM* vm);
16
+
13
17
  /**
14
- * Initializes the native (C++) part of NitroStrokeText, and autolinks all Hybrid Objects.
15
- * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`).
18
+ * Register the native (C++) part of NitroStrokeText, 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 margelo::nitro::stroketext::initialize(vm);
24
+ * return facebook::jni::initialize(vm, []() {
25
+ * // register all NitroStrokeText HybridObjects
26
+ * margelo::nitro::stroketext::registerNatives();
27
+ * // any other custom registrations go here.
28
+ * });
20
29
  * }
21
30
  * ```
22
31
  */
23
- int initialize(JavaVM* vm);
32
+ void registerAllNatives();
24
33
 
25
34
  } // namespace margelo::nitro::stroketext