react-native-marcus 0.1.1 → 0.2.0

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 (294) hide show
  1. package/README.md +220 -63
  2. package/RNMarcus.podspec +6 -0
  3. package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
  4. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
  5. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
  6. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
  7. package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
  8. package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
  9. package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
  10. package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
  11. package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
  12. package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
  13. package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
  14. package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
  15. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
  16. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
  17. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
  18. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
  19. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
  20. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
  21. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
  22. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
  23. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
  24. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
  25. package/android/src/main/new_arch/CMakeLists.txt +17 -0
  26. package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
  27. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
  28. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
  29. package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
  30. package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
  31. package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
  32. package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
  33. package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
  34. package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
  35. package/android/src/test/resources/render-model.txt +221 -16
  36. package/apple/MarcusSwizzle.h +25 -0
  37. package/apple/MarcusTextDecoratorComponentView.h +22 -0
  38. package/apple/MarcusTextDecoratorComponentView.mm +72 -0
  39. package/apple/MarcusTextDecoratorShadowNode.h +62 -0
  40. package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
  41. package/apple/MarcusTextDecoratorViewManager.h +4 -0
  42. package/apple/MarcusTextDecoratorViewManager.mm +11 -0
  43. package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
  44. package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
  45. package/apple/MarcusTextLayoutManager.h +40 -0
  46. package/apple/MarcusTextLayoutManager.mm +173 -0
  47. package/apple/MarkdownAttributes.h +23 -2
  48. package/apple/MarkdownAttributes.m +6 -0
  49. package/apple/MarkdownCopy.swift +63 -0
  50. package/apple/MarkdownDecorator.swift +2 -2
  51. package/apple/MarkdownFormatter+React.swift +23 -0
  52. package/apple/MarkdownFormatter.swift +869 -148
  53. package/apple/MarkdownParagraphLayoutManager.swift +455 -0
  54. package/apple/MarkdownSwiftInterop.h +0 -1
  55. package/apple/MarkdownTextBackground.swift +59 -1
  56. package/apple/MarkdownTextFieldObserver.swift +2 -2
  57. package/apple/MarkdownTextLayoutFragment.swift +159 -33
  58. package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
  59. package/apple/MarkdownTextStorageDelegate.swift +2 -2
  60. package/apple/MarkdownTextViewObserver.swift +2 -2
  61. package/apple/MarkdownUtils.swift +38 -0
  62. package/apple/MarkdownWritingDirection.swift +132 -0
  63. package/apple/RCTMarcusStyle+Codegen.h +115 -9
  64. package/apple/RCTMarcusStyle.h +128 -31
  65. package/apple/RCTMarcusStyle.m +48 -0
  66. package/apple/RCTParagraphComponentView+Marcus.h +17 -0
  67. package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
  68. package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
  69. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
  70. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
  71. package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  72. package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  73. package/lib/commonjs/MarkdownText.js +330 -0
  74. package/lib/commonjs/MarkdownText.js.map +1 -0
  75. package/lib/commonjs/MarkdownText.web.js +529 -0
  76. package/lib/commonjs/MarkdownText.web.js.map +1 -0
  77. package/lib/commonjs/MarkdownTextInput.js +22 -27
  78. package/lib/commonjs/MarkdownTextInput.js.map +1 -1
  79. package/lib/commonjs/MarkdownTextInput.web.js +12 -7
  80. package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
  81. package/lib/commonjs/atomUtils.js +112 -0
  82. package/lib/commonjs/atomUtils.js.map +1 -0
  83. package/lib/commonjs/childrenUtils.js +36 -0
  84. package/lib/commonjs/childrenUtils.js.map +1 -0
  85. package/lib/commonjs/index.js +8 -7
  86. package/lib/commonjs/index.js.map +1 -1
  87. package/lib/commonjs/math/environment.js +69 -0
  88. package/lib/commonjs/math/environment.js.map +1 -0
  89. package/lib/commonjs/math/index.js +61 -0
  90. package/lib/commonjs/math/index.js.map +1 -0
  91. package/lib/commonjs/math/mathjax.js +548 -0
  92. package/lib/commonjs/math/mathjax.js.map +1 -0
  93. package/lib/commonjs/metro.js +172 -0
  94. package/lib/commonjs/metro.js.map +1 -0
  95. package/lib/commonjs/parser/index.d.ts +3 -0
  96. package/lib/commonjs/parser/index.js +450 -42
  97. package/lib/commonjs/parser/index.js.map +4 -4
  98. package/lib/commonjs/stripUtils.js +743 -0
  99. package/lib/commonjs/stripUtils.js.map +1 -0
  100. package/lib/commonjs/styleUtils.js +194 -23
  101. package/lib/commonjs/styleUtils.js.map +1 -1
  102. package/lib/commonjs/web/MarkdownElements.css +67 -0
  103. package/lib/commonjs/web/MarkdownTextInput.css +9 -7
  104. package/lib/commonjs/web/utils/blockLayout.js +95 -7
  105. package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
  106. package/lib/commonjs/web/utils/blockUtils.js +327 -54
  107. package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
  108. package/lib/commonjs/web/utils/elementUtils.js +80 -0
  109. package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
  110. package/lib/commonjs/web/utils/parserUtils.js +84 -28
  111. package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
  112. package/lib/commonjs/web/utils/treeUtils.js +6 -0
  113. package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
  114. package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
  115. package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
  116. package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  117. package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  118. package/lib/module/MarkdownText.js +325 -0
  119. package/lib/module/MarkdownText.js.map +1 -0
  120. package/lib/module/MarkdownText.web.js +524 -0
  121. package/lib/module/MarkdownText.web.js.map +1 -0
  122. package/lib/module/MarkdownTextInput.js +24 -29
  123. package/lib/module/MarkdownTextInput.js.map +1 -1
  124. package/lib/module/MarkdownTextInput.web.js +12 -7
  125. package/lib/module/MarkdownTextInput.web.js.map +1 -1
  126. package/lib/module/atomUtils.js +106 -0
  127. package/lib/module/atomUtils.js.map +1 -0
  128. package/lib/module/childrenUtils.js +33 -0
  129. package/lib/module/childrenUtils.js.map +1 -0
  130. package/lib/module/index.js +1 -1
  131. package/lib/module/index.js.map +1 -1
  132. package/lib/module/math/environment.js +67 -0
  133. package/lib/module/math/environment.js.map +1 -0
  134. package/lib/module/math/index.js +56 -0
  135. package/lib/module/math/index.js.map +1 -0
  136. package/lib/module/math/mathjax.js +541 -0
  137. package/lib/module/math/mathjax.js.map +1 -0
  138. package/lib/module/metro.js +169 -0
  139. package/lib/module/metro.js.map +1 -0
  140. package/lib/module/parser/index.d.ts +3 -0
  141. package/lib/module/parser/index.js +450 -42
  142. package/lib/module/parser/index.js.map +4 -4
  143. package/lib/module/stripUtils.js +739 -0
  144. package/lib/module/stripUtils.js.map +1 -0
  145. package/lib/module/styleUtils.js +196 -25
  146. package/lib/module/styleUtils.js.map +1 -1
  147. package/lib/module/web/MarkdownElements.css +67 -0
  148. package/lib/module/web/MarkdownTextInput.css +9 -7
  149. package/lib/module/web/utils/blockLayout.js +94 -8
  150. package/lib/module/web/utils/blockLayout.js.map +1 -1
  151. package/lib/module/web/utils/blockUtils.js +328 -56
  152. package/lib/module/web/utils/blockUtils.js.map +1 -1
  153. package/lib/module/web/utils/elementUtils.js +75 -0
  154. package/lib/module/web/utils/elementUtils.js.map +1 -0
  155. package/lib/module/web/utils/parserUtils.js +86 -30
  156. package/lib/module/web/utils/parserUtils.js.map +1 -1
  157. package/lib/module/web/utils/treeUtils.js +6 -0
  158. package/lib/module/web/utils/treeUtils.js.map +1 -1
  159. package/lib/module/web/utils/webStyleUtils.js +25 -5
  160. package/lib/module/web/utils/webStyleUtils.js.map +1 -1
  161. package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
  162. package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
  163. package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
  164. package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
  166. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  167. package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
  168. package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
  169. package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
  170. package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
  171. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
  172. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
  174. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
  176. package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
  177. package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
  178. package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
  179. package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
  180. package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
  181. package/lib/typescript/commonjs/src/index.d.ts +2 -1
  182. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
  184. package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
  186. package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
  188. package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/src/metro.d.ts +59 -0
  190. package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
  191. package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
  192. package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
  193. package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
  195. package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
  196. package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
  198. package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
  199. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
  200. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
  202. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
  203. package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
  204. package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
  206. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
  208. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
  210. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
  211. package/lib/typescript/module/e2e/links.test.d.ts +2 -0
  212. package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
  213. package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
  214. package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
  215. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
  216. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  217. package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
  218. package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
  219. package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
  220. package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
  221. package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
  222. package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
  223. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
  224. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
  225. package/lib/typescript/module/src/atomUtils.d.ts +58 -0
  226. package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
  227. package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
  228. package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
  229. package/lib/typescript/module/src/commonTypes.d.ts +39 -3
  230. package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
  231. package/lib/typescript/module/src/index.d.ts +2 -1
  232. package/lib/typescript/module/src/index.d.ts.map +1 -1
  233. package/lib/typescript/module/src/math/environment.d.ts +7 -0
  234. package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
  235. package/lib/typescript/module/src/math/index.d.ts +64 -0
  236. package/lib/typescript/module/src/math/index.d.ts.map +1 -0
  237. package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
  238. package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
  239. package/lib/typescript/module/src/metro.d.ts +59 -0
  240. package/lib/typescript/module/src/metro.d.ts.map +1 -0
  241. package/lib/typescript/module/src/parser/index.d.ts +3 -3
  242. package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
  243. package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
  244. package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
  245. package/lib/typescript/module/src/stripUtils.d.ts +108 -0
  246. package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
  247. package/lib/typescript/module/src/styleUtils.d.ts +62 -4
  248. package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
  249. package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
  250. package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
  251. package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
  252. package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
  253. package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
  254. package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
  255. package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
  256. package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
  257. package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
  258. package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
  259. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
  260. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
  261. package/package.json +29 -2
  262. package/react-native.config.js +8 -1
  263. package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  264. package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  265. package/src/MarkdownText.tsx +472 -0
  266. package/src/MarkdownText.web.tsx +668 -0
  267. package/src/MarkdownTextInput.tsx +42 -38
  268. package/src/MarkdownTextInput.web.tsx +36 -13
  269. package/src/atomUtils.ts +112 -0
  270. package/src/childrenUtils.ts +39 -0
  271. package/src/commonTypes.ts +69 -9
  272. package/src/index.tsx +2 -1
  273. package/src/math/environment.ts +68 -0
  274. package/src/math/index.ts +78 -0
  275. package/src/math/mathjax.ts +604 -0
  276. package/src/metro.ts +238 -0
  277. package/src/parser/index.ts +175 -10
  278. package/src/parser/micromark-extension-mentions.ts +104 -11
  279. package/src/stripUtils.ts +919 -0
  280. package/src/styleUtils.ts +290 -26
  281. package/src/web/MarkdownElements.css +67 -0
  282. package/src/web/MarkdownTextInput.css +9 -7
  283. package/src/web/utils/blockLayout.ts +124 -14
  284. package/src/web/utils/blockUtils.ts +365 -59
  285. package/src/web/utils/elementUtils.ts +87 -0
  286. package/src/web/utils/parserUtils.ts +119 -31
  287. package/src/web/utils/treeUtils.ts +6 -1
  288. package/src/web/utils/webStyleUtils.ts +32 -8
  289. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
  290. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
  291. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
  292. package/apple/RCTMarcusStyle+Codegen.mm +0 -64
  293. package/apple/RCTMarcusUtils.h +0 -26
  294. package/apple/RCTMarcusUtils.mm +0 -26
@@ -0,0 +1,78 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.Paint
5
+ import android.text.style.ReplacementSpan
6
+ import androidx.annotation.ColorInt
7
+
8
+ /**
9
+ * The bullet drawn in place of an unordered item's marker.
10
+ *
11
+ * The marker character stays in the string -- it is what a reader copies and
12
+ * what TalkBack announces -- and this takes over drawing it, which is what a
13
+ * `ReplacementSpan` is for: the circle is drawn in the advance the glyph would
14
+ * have taken, and the glyph itself never reaches the canvas.
15
+ *
16
+ * [diameter] comes from the base font size rather than from a length of its own,
17
+ * so a list marks itself in proportion to the prose it marks. The circle is
18
+ * centred on the line's own height rather than sat on the baseline, because a
19
+ * bullet marks the line rather than being a letter on it -- iOS draws it from
20
+ * the same two numbers in `MarkdownParagraphLayoutManager`.
21
+ *
22
+ * @param box the advance to report, which is the diameter as a whole pixel: the
23
+ * formatter indents the text past exactly this, so the two have to agree.
24
+ */
25
+ class MarkdownBulletSpan(
26
+ @ColorInt private val color: Int,
27
+ private val diameter: Float,
28
+ private val box: Int
29
+ ) : ReplacementSpan(), MarkdownSpan {
30
+
31
+ /**
32
+ * Room held open after the bullet, for the padding around a marker and for a
33
+ * nested container's gutter.
34
+ *
35
+ * Carried here rather than by a `MarkdownGapSpan` of its own because two
36
+ * replacement spans cannot share a character: an item written with nothing
37
+ * after its marker has no other character to put one on.
38
+ */
39
+ var trailing = 0f
40
+
41
+ override fun getSize(
42
+ paint: Paint,
43
+ text: CharSequence,
44
+ start: Int,
45
+ end: Int,
46
+ fm: Paint.FontMetricsInt?
47
+ ): Int {
48
+ // The line keeps the height the text gives it: the circle is drawn inside
49
+ // what the line already is, never by pushing it open.
50
+ if (fm != null) {
51
+ val metrics = paint.fontMetricsInt
52
+ fm.top = metrics.top
53
+ fm.ascent = metrics.ascent
54
+ fm.descent = metrics.descent
55
+ fm.bottom = metrics.bottom
56
+ fm.leading = metrics.leading
57
+ }
58
+
59
+ return box + trailing.toInt()
60
+ }
61
+
62
+ override fun draw(
63
+ canvas: Canvas,
64
+ text: CharSequence,
65
+ start: Int,
66
+ end: Int,
67
+ x: Float,
68
+ top: Int,
69
+ y: Int,
70
+ bottom: Int,
71
+ paint: Paint
72
+ ) {
73
+ val originalColor = paint.color
74
+ paint.color = color
75
+ canvas.drawCircle(x + box / 2f, (top + bottom) / 2f, diameter / 2f, paint)
76
+ paint.color = originalColor
77
+ }
78
+ }
@@ -0,0 +1,177 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.Paint
5
+ import android.graphics.Path
6
+ import android.graphics.RectF
7
+ import android.text.style.LineBackgroundSpan
8
+ import android.text.style.LineHeightSpan
9
+ import androidx.annotation.ColorInt
10
+ import app.getbullet.marcus.MarkdownBackgroundPass
11
+
12
+ /**
13
+ * The box behind a fenced or indented code block: one fill for the whole block,
14
+ * from its first line to its last.
15
+ *
16
+ * A background colour would follow the glyphs -- leaving the line spacing above
17
+ * and below each line unpainted, and stopping wherever the code happens to end
18
+ * -- so the fill is drawn per line here instead, across the width the line was
19
+ * laid out in. Two lines' rects abut on whole pixels, so what they read as is
20
+ * one box. iOS draws the same shape from the same numbers in
21
+ * `MarkdownTextLayoutFragment` and `MarkdownParagraphLayoutManager`.
22
+ *
23
+ * The vertical padding and margin are held open by the same span: [chooseHeight]
24
+ * grows the first line upwards and the last line downwards, so the box has room
25
+ * to breathe without overlapping the lines around it. The horizontal ones are
26
+ * indent, which is `MarkdownBlockIndentSpan`'s job -- one authority for how far
27
+ * a line starts in. Nothing is reserved on the far side: a paragraph cannot be
28
+ * given a trailing indent, so a line long enough to wrap reaches the padding
29
+ * there.
30
+ *
31
+ * @param leadingInset where the box's near edge sits, measured from the edge the
32
+ * block's lines begin at
33
+ * @param rightToLeft which edge that is, and the one value here left readable:
34
+ * nothing else about the box records the decision, so this is where a test
35
+ * asks what the formatter settled on. A `LineBackgroundSpan` is handed the
36
+ * line's two sides and nothing to tell them apart -- unlike a
37
+ * `LeadingMarginSpan`, which the framework hands the direction outright -- so
38
+ * this is settled while formatting and carried in, the way iOS carries the
39
+ * same answer on `MarkdownCodeBlock.rightToLeft`.
40
+ * @param blockStart first character of the block, for the corners and the space
41
+ * @param blockEnd one past its last character
42
+ * @param pass set in an input, where the view paints the backgrounds itself so
43
+ * that the caret stays visible; null in a `Text`, which has no caret and lets
44
+ * the layout paint them
45
+ */
46
+ class MarkdownCodeBlockSpan(
47
+ @ColorInt private val backgroundColor: Int,
48
+ private val borderRadius: Float,
49
+ private val paddingVertical: Float,
50
+ private val marginHorizontal: Float,
51
+ private val marginVertical: Float,
52
+ private val leadingInset: Float,
53
+ val rightToLeft: Boolean,
54
+ private val blockStart: Int,
55
+ private val blockEnd: Int,
56
+ @ColorInt private val borderColor: Int? = null,
57
+ private val borderWidth: Float = 0f,
58
+ private val pass: MarkdownBackgroundPass? = null
59
+ ) : MarkdownSpan, LineBackgroundSpan, LineHeightSpan {
60
+
61
+ private val rect = RectF()
62
+ private val path = Path()
63
+
64
+ override fun drawBackground(
65
+ canvas: Canvas,
66
+ paint: Paint,
67
+ left: Int,
68
+ right: Int,
69
+ top: Int,
70
+ baseline: Int,
71
+ bottom: Int,
72
+ text: CharSequence,
73
+ start: Int,
74
+ end: Int,
75
+ lnum: Int
76
+ ) {
77
+ if (pass != null && !pass.painting) {
78
+ return
79
+ }
80
+
81
+ val opens = start <= blockStart
82
+ val closes = end >= blockEnd
83
+
84
+ // The insets are measured from the edge the block's lines begin at, so in a
85
+ // right-to-left block they swap sides: the padded one is the right, and the
86
+ // bare margin is all that is left on the far side.
87
+ val near = if (rightToLeft) marginHorizontal else leadingInset
88
+ val far = if (rightToLeft) leadingInset else marginHorizontal
89
+
90
+ rect.set(
91
+ left + near,
92
+ top + if (opens) marginVertical else 0f,
93
+ right - far,
94
+ bottom - if (closes) marginVertical else 0f,
95
+ )
96
+
97
+ if (rect.width() <= 0f || rect.height() <= 0f) {
98
+ return
99
+ }
100
+
101
+ path.reset()
102
+ path.addRoundRect(rect, radii(opens, closes), Path.Direction.CW)
103
+
104
+ val originalColor = paint.color
105
+ val originalStyle = paint.style
106
+ val originalWidth = paint.strokeWidth
107
+
108
+ paint.style = Paint.Style.FILL
109
+ paint.color = backgroundColor
110
+ canvas.drawPath(path, paint)
111
+
112
+ // Stroked at twice the width and clipped to the box, so the outer half is
113
+ // trimmed away and a bordered block covers exactly the room an unbordered
114
+ // one does. `MarkdownFormatter.swift` draws its border the same way.
115
+ if (borderColor != null && borderWidth > 0f) {
116
+ canvas.save()
117
+ canvas.clipPath(path)
118
+ paint.style = Paint.Style.STROKE
119
+ paint.strokeWidth = borderWidth * 2f
120
+ paint.color = borderColor
121
+ canvas.drawPath(path, paint)
122
+ canvas.restore()
123
+ }
124
+
125
+ paint.color = originalColor
126
+ paint.style = originalStyle
127
+ paint.strokeWidth = originalWidth
128
+ }
129
+
130
+ /**
131
+ * Room above the block's first line and below its last, for the padding
132
+ * inside the box and the margin outside it.
133
+ *
134
+ * Called for every line the span covers, so the two ends are picked out by
135
+ * where the line falls in the block rather than by the span's own extent.
136
+ */
137
+ override fun chooseHeight(
138
+ text: CharSequence?,
139
+ start: Int,
140
+ end: Int,
141
+ spanstartv: Int,
142
+ lineHeight: Int,
143
+ fm: Paint.FontMetricsInt
144
+ ) {
145
+ val inset = (paddingVertical + marginVertical).toInt()
146
+
147
+ if (start <= blockStart) {
148
+ fm.top -= inset
149
+ fm.ascent -= inset
150
+ }
151
+ if (end >= blockEnd) {
152
+ fm.descent += inset
153
+ fm.bottom += inset
154
+ }
155
+ }
156
+
157
+ /** Corners of one line's rect: only the ends of the block are rounded. */
158
+ private fun radii(roundedTop: Boolean, roundedBottom: Boolean): FloatArray {
159
+ val radii = FloatArray(8)
160
+
161
+ if (roundedTop) {
162
+ radii[1] = borderRadius // top-left
163
+ radii[0] = radii[1]
164
+ radii[3] = borderRadius // top-right
165
+ radii[2] = radii[3]
166
+ }
167
+
168
+ if (roundedBottom) {
169
+ radii[5] = borderRadius // bottom-right
170
+ radii[4] = radii[5]
171
+ radii[7] = borderRadius // bottom-left
172
+ radii[6] = radii[7]
173
+ }
174
+
175
+ return radii
176
+ }
177
+ }
@@ -0,0 +1,32 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.graphics.Typeface
4
+ import android.text.TextPaint
5
+ import android.text.style.MetricAffectingSpan
6
+ import com.facebook.react.common.assets.ReactFontManager
7
+ import com.facebook.react.views.text.ReactTypefaceUtils
8
+
9
+ /**
10
+ * Draws a run upright or slanted, keeping the face and the weight it inherited.
11
+ *
12
+ * The weight has to be carried over by hand rather than left alone, because the
13
+ * only way to change one of the two is to ask for both: a typeface is created
14
+ * from a weight and a slant together.
15
+ */
16
+ class MarkdownFontStyleSpan(fontStyle: String) : MetricAffectingSpan(), MarkdownSpan {
17
+
18
+ val italic: Boolean = ReactTypefaceUtils.parseFontStyle(fontStyle) == Typeface.ITALIC
19
+
20
+ override fun updateMeasureState(textPaint: TextPaint) = apply(textPaint)
21
+
22
+ override fun updateDrawState(tp: TextPaint) = apply(tp)
23
+
24
+ private fun apply(textPaint: TextPaint) {
25
+ val current = textPaint.typeface
26
+ val bold = ((current?.style ?: Typeface.NORMAL) and Typeface.BOLD) != 0
27
+ val weight =
28
+ if (bold) ReactFontManager.TypefaceStyle.BOLD else ReactFontManager.TypefaceStyle.NORMAL
29
+
30
+ textPaint.typeface = ReactFontManager.TypefaceStyle(weight, italic).apply(current)
31
+ }
32
+ }
@@ -0,0 +1,34 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.graphics.Typeface
4
+ import android.text.TextPaint
5
+ import android.text.style.MetricAffectingSpan
6
+ import com.facebook.react.common.assets.ReactFontManager
7
+ import com.facebook.react.views.text.ReactTypefaceUtils
8
+
9
+ /**
10
+ * Draws a run at a given weight, keeping the face and the slant it inherited.
11
+ *
12
+ * React Native's own spelling of a weight -- `bold`, `600`, `semibold` -- is
13
+ * resolved by React Native, so a numeric weight lands on the nearest variant a
14
+ * font actually has rather than on a synthesised bold.
15
+ *
16
+ * [weight] is readable so the render model can name what this draws without
17
+ * reaching into the span: a weight of bold or heavier is the `bold` the shared
18
+ * model already had a word for.
19
+ */
20
+ class MarkdownFontWeightSpan(fontWeight: String) : MetricAffectingSpan(), MarkdownSpan {
21
+
22
+ val weight: Int = ReactTypefaceUtils.parseFontWeight(fontWeight)
23
+
24
+ override fun updateMeasureState(textPaint: TextPaint) = apply(textPaint)
25
+
26
+ override fun updateDrawState(tp: TextPaint) = apply(tp)
27
+
28
+ private fun apply(textPaint: TextPaint) {
29
+ val current = textPaint.typeface
30
+ val italic = ((current?.style ?: Typeface.NORMAL) and Typeface.ITALIC) != 0
31
+
32
+ textPaint.typeface = ReactFontManager.TypefaceStyle(weight, italic).apply(current)
33
+ }
34
+ }
@@ -6,14 +6,30 @@ import android.text.style.ReplacementSpan
6
6
  import androidx.annotation.ColorInt
7
7
 
8
8
  /**
9
- * Widens the character it covers by [gap] pixels, leaving the glyph where it is.
9
+ * Widens the character it covers, leaving the glyph where it belongs.
10
10
  *
11
- * Applied to the last character of a container's marker so that the container
12
- * nested inside it can put its gutter between that marker and the next one.
11
+ * Two things need this. A container's marker holds [trailing] space after its
12
+ * last character, so the container nested inside it can put its gutter between
13
+ * that marker and the next one. And an inline run of code holds space on both
14
+ * sides -- [leading] on the character it starts with, [trailing] on the one it
15
+ * ends with -- so the box drawn behind it has room to breathe without sitting
16
+ * over the words either side of it.
17
+ *
18
+ * The character is drawn here rather than by the layout, because a replacement
19
+ * takes over its own drawing: metric spans still reach the paint, so whatever
20
+ * font and size the character had are already in it, but its colour has to be
21
+ * put back by hand. [color] is the colour that character should have -- a
22
+ * marker's syntax colour, or the colour of the code it belongs to.
23
+ *
24
+ * [trailing] is a `var` because two things can want space after the same
25
+ * character: the padding around a list's marker, and then the gutter of the
26
+ * container nested inside it. Only one replacement span on a character is ever
27
+ * asked for a width, so the second has to add to the first rather than join it.
13
28
  */
14
29
  class MarkdownGapSpan(
15
- private val gap: Float,
16
- @ColorInt private val syntaxColor: Int
30
+ var trailing: Float = 0f,
31
+ private val leading: Float = 0f,
32
+ @ColorInt private val color: Int
17
33
  ) : ReplacementSpan(), MarkdownSpan {
18
34
 
19
35
  override fun getSize(
@@ -32,7 +48,7 @@ class MarkdownGapSpan(
32
48
  fm.leading = metrics.leading
33
49
  }
34
50
 
35
- return (paint.measureText(text, start, end) + gap).toInt()
51
+ return (leading + paint.measureText(text, start, end) + trailing).toInt()
36
52
  }
37
53
 
38
54
  override fun draw(
@@ -46,12 +62,9 @@ class MarkdownGapSpan(
46
62
  bottom: Int,
47
63
  paint: Paint
48
64
  ) {
49
- // Spans inside a replacement are not applied, so the one colour a marker's
50
- // last character can have is restored by hand. It is whitespace whenever the
51
- // marker is followed by any, and a syntax character otherwise.
52
65
  val originalColor = paint.color
53
- paint.color = syntaxColor
54
- canvas.drawText(text, start, end, x, y.toFloat(), paint)
66
+ paint.color = color
67
+ canvas.drawText(text, start, end, x + leading, y.toFloat(), paint)
55
68
  paint.color = originalColor
56
69
  }
57
70
  }
@@ -0,0 +1,32 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.text.TextPaint
4
+ import android.text.style.MetricAffectingSpan
5
+ import com.facebook.react.uimanager.PixelUtil
6
+
7
+ /**
8
+ * Opens the same amount of space after every character of a run.
9
+ *
10
+ * The style names a length, as it does everywhere else, and `Paint` wants a
11
+ * multiple of the text size, so the conversion happens against whatever size the
12
+ * run ended up being drawn at. A run given a larger font therefore keeps the
13
+ * spacing it asked for rather than a proportionally larger one, which is what
14
+ * `letterSpacing` means on the other two platforms.
15
+ *
16
+ * @param letterSpacing in dp, as it is written in a style
17
+ */
18
+ class MarkdownLetterSpacingSpan(private val letterSpacing: Float) :
19
+ MetricAffectingSpan(), MarkdownSpan {
20
+
21
+ override fun updateMeasureState(textPaint: TextPaint) = apply(textPaint)
22
+
23
+ override fun updateDrawState(tp: TextPaint) = apply(tp)
24
+
25
+ private fun apply(textPaint: TextPaint) {
26
+ if (textPaint.textSize <= 0f) {
27
+ return
28
+ }
29
+
30
+ textPaint.letterSpacing = PixelUtil.toPixelFromDIP(letterSpacing) / textPaint.textSize
31
+ }
32
+ }
@@ -0,0 +1,32 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.text.TextPaint
4
+ import android.text.style.CharacterStyle
5
+ import androidx.annotation.ColorInt
6
+ import com.facebook.react.uimanager.PixelUtil
7
+
8
+ /**
9
+ * The shadow behind a run's glyphs.
10
+ *
11
+ * Not metric affecting: a shadow is painted outside the advance it belongs to
12
+ * and changes nothing about where the text sits, which is also how iOS treats
13
+ * `NSShadow` and how the browser treats `text-shadow`.
14
+ *
15
+ * The offset and the radius arrive in dp, as they are written in a style.
16
+ */
17
+ class MarkdownTextShadowSpan(
18
+ @ColorInt private val color: Int,
19
+ private val offsetX: Float,
20
+ private val offsetY: Float,
21
+ private val radius: Float
22
+ ) : CharacterStyle(), MarkdownSpan {
23
+
24
+ override fun updateDrawState(tp: TextPaint) {
25
+ tp.setShadowLayer(
26
+ PixelUtil.toPixelFromDIP(radius),
27
+ PixelUtil.toPixelFromDIP(offsetX),
28
+ PixelUtil.toPixelFromDIP(offsetY),
29
+ color
30
+ )
31
+ }
32
+ }
@@ -43,6 +43,23 @@ target_link_libraries(
43
43
  ReactAndroid::reactnative
44
44
  )
45
45
 
46
+ # `ParagraphProps.h`, which the paragraph shadow node pulls in, includes
47
+ # `HostPlatformParagraphProps.h` -- a header React Native ships once per
48
+ # platform, under an include root the prefab module does not put on the search
49
+ # path. Add Android's, or including the paragraph at all fails to compile.
50
+ get_target_property(
51
+ REACTNATIVE_INCLUDE_DIRS ReactAndroid::reactnative INTERFACE_INCLUDE_DIRECTORIES
52
+ )
53
+
54
+ foreach(include_dir ${REACTNATIVE_INCLUDE_DIRS})
55
+ set(HOST_PLATFORM_TEXT_DIR
56
+ "${include_dir}/react/renderer/components/text/platform/android"
57
+ )
58
+ if(EXISTS "${HOST_PLATFORM_TEXT_DIR}")
59
+ target_include_directories(${LIB_TARGET_NAME} PRIVATE "${HOST_PLATFORM_TEXT_DIR}")
60
+ endif()
61
+ endforeach()
62
+
46
63
  if (ReactAndroid_VERSION_MINOR LESS_EQUAL 80)
47
64
  target_compile_options(
48
65
  ${LIB_TARGET_NAME}
@@ -16,6 +16,7 @@
16
16
  #include <jsi/jsi.h>
17
17
 
18
18
  #include <react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorViewComponentDescriptor.h>
19
+ #include <react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h>
19
20
 
20
21
  namespace facebook {
21
22
  namespace react {