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.
- package/README.md +220 -63
- package/RNMarcus.podspec +6 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
- package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
- package/android/src/main/new_arch/CMakeLists.txt +17 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
- package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
- package/android/src/test/resources/render-model.txt +221 -16
- package/apple/MarcusSwizzle.h +25 -0
- package/apple/MarcusTextDecoratorComponentView.h +22 -0
- package/apple/MarcusTextDecoratorComponentView.mm +72 -0
- package/apple/MarcusTextDecoratorShadowNode.h +62 -0
- package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
- package/apple/MarcusTextDecoratorViewManager.h +4 -0
- package/apple/MarcusTextDecoratorViewManager.mm +11 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
- package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
- package/apple/MarcusTextLayoutManager.h +40 -0
- package/apple/MarcusTextLayoutManager.mm +173 -0
- package/apple/MarkdownAttributes.h +23 -2
- package/apple/MarkdownAttributes.m +6 -0
- package/apple/MarkdownCopy.swift +63 -0
- package/apple/MarkdownDecorator.swift +2 -2
- package/apple/MarkdownFormatter+React.swift +23 -0
- package/apple/MarkdownFormatter.swift +869 -148
- package/apple/MarkdownParagraphLayoutManager.swift +455 -0
- package/apple/MarkdownSwiftInterop.h +0 -1
- package/apple/MarkdownTextBackground.swift +59 -1
- package/apple/MarkdownTextFieldObserver.swift +2 -2
- package/apple/MarkdownTextLayoutFragment.swift +159 -33
- package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
- package/apple/MarkdownTextStorageDelegate.swift +2 -2
- package/apple/MarkdownTextViewObserver.swift +2 -2
- package/apple/MarkdownUtils.swift +38 -0
- package/apple/MarkdownWritingDirection.swift +132 -0
- package/apple/RCTMarcusStyle+Codegen.h +115 -9
- package/apple/RCTMarcusStyle.h +128 -31
- package/apple/RCTMarcusStyle.m +48 -0
- package/apple/RCTParagraphComponentView+Marcus.h +17 -0
- package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/commonjs/MarkdownText.js +330 -0
- package/lib/commonjs/MarkdownText.js.map +1 -0
- package/lib/commonjs/MarkdownText.web.js +529 -0
- package/lib/commonjs/MarkdownText.web.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.js +22 -27
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/commonjs/MarkdownTextInput.web.js +12 -7
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
- package/lib/commonjs/atomUtils.js +112 -0
- package/lib/commonjs/atomUtils.js.map +1 -0
- package/lib/commonjs/childrenUtils.js +36 -0
- package/lib/commonjs/childrenUtils.js.map +1 -0
- package/lib/commonjs/index.js +8 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/math/environment.js +69 -0
- package/lib/commonjs/math/environment.js.map +1 -0
- package/lib/commonjs/math/index.js +61 -0
- package/lib/commonjs/math/index.js.map +1 -0
- package/lib/commonjs/math/mathjax.js +548 -0
- package/lib/commonjs/math/mathjax.js.map +1 -0
- package/lib/commonjs/metro.js +172 -0
- package/lib/commonjs/metro.js.map +1 -0
- package/lib/commonjs/parser/index.d.ts +3 -0
- package/lib/commonjs/parser/index.js +450 -42
- package/lib/commonjs/parser/index.js.map +4 -4
- package/lib/commonjs/stripUtils.js +743 -0
- package/lib/commonjs/stripUtils.js.map +1 -0
- package/lib/commonjs/styleUtils.js +194 -23
- package/lib/commonjs/styleUtils.js.map +1 -1
- package/lib/commonjs/web/MarkdownElements.css +67 -0
- package/lib/commonjs/web/MarkdownTextInput.css +9 -7
- package/lib/commonjs/web/utils/blockLayout.js +95 -7
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
- package/lib/commonjs/web/utils/blockUtils.js +327 -54
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
- package/lib/commonjs/web/utils/elementUtils.js +80 -0
- package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +84 -28
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
- package/lib/commonjs/web/utils/treeUtils.js +6 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
- package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
- package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/module/MarkdownText.js +325 -0
- package/lib/module/MarkdownText.js.map +1 -0
- package/lib/module/MarkdownText.web.js +524 -0
- package/lib/module/MarkdownText.web.js.map +1 -0
- package/lib/module/MarkdownTextInput.js +24 -29
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/module/MarkdownTextInput.web.js +12 -7
- package/lib/module/MarkdownTextInput.web.js.map +1 -1
- package/lib/module/atomUtils.js +106 -0
- package/lib/module/atomUtils.js.map +1 -0
- package/lib/module/childrenUtils.js +33 -0
- package/lib/module/childrenUtils.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/math/environment.js +67 -0
- package/lib/module/math/environment.js.map +1 -0
- package/lib/module/math/index.js +56 -0
- package/lib/module/math/index.js.map +1 -0
- package/lib/module/math/mathjax.js +541 -0
- package/lib/module/math/mathjax.js.map +1 -0
- package/lib/module/metro.js +169 -0
- package/lib/module/metro.js.map +1 -0
- package/lib/module/parser/index.d.ts +3 -0
- package/lib/module/parser/index.js +450 -42
- package/lib/module/parser/index.js.map +4 -4
- package/lib/module/stripUtils.js +739 -0
- package/lib/module/stripUtils.js.map +1 -0
- package/lib/module/styleUtils.js +196 -25
- package/lib/module/styleUtils.js.map +1 -1
- package/lib/module/web/MarkdownElements.css +67 -0
- package/lib/module/web/MarkdownTextInput.css +9 -7
- package/lib/module/web/utils/blockLayout.js +94 -8
- package/lib/module/web/utils/blockLayout.js.map +1 -1
- package/lib/module/web/utils/blockUtils.js +328 -56
- package/lib/module/web/utils/blockUtils.js.map +1 -1
- package/lib/module/web/utils/elementUtils.js +75 -0
- package/lib/module/web/utils/elementUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +86 -30
- package/lib/module/web/utils/parserUtils.js.map +1 -1
- package/lib/module/web/utils/treeUtils.js +6 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -1
- package/lib/module/web/utils/webStyleUtils.js +25 -5
- package/lib/module/web/utils/webStyleUtils.js.map +1 -1
- package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
- package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
- package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
- package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/metro.d.ts +59 -0
- package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
- package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/lib/typescript/module/e2e/links.test.d.ts +2 -0
- package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/module/src/atomUtils.d.ts +58 -0
- package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +39 -3
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/math/environment.d.ts +7 -0
- package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/module/src/math/index.d.ts +64 -0
- package/lib/typescript/module/src/math/index.d.ts.map +1 -0
- package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/module/src/metro.d.ts +59 -0
- package/lib/typescript/module/src/metro.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +3 -3
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/module/src/stripUtils.d.ts +108 -0
- package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +62 -4
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/package.json +29 -2
- package/react-native.config.js +8 -1
- package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/src/MarkdownText.tsx +472 -0
- package/src/MarkdownText.web.tsx +668 -0
- package/src/MarkdownTextInput.tsx +42 -38
- package/src/MarkdownTextInput.web.tsx +36 -13
- package/src/atomUtils.ts +112 -0
- package/src/childrenUtils.ts +39 -0
- package/src/commonTypes.ts +69 -9
- package/src/index.tsx +2 -1
- package/src/math/environment.ts +68 -0
- package/src/math/index.ts +78 -0
- package/src/math/mathjax.ts +604 -0
- package/src/metro.ts +238 -0
- package/src/parser/index.ts +175 -10
- package/src/parser/micromark-extension-mentions.ts +104 -11
- package/src/stripUtils.ts +919 -0
- package/src/styleUtils.ts +290 -26
- package/src/web/MarkdownElements.css +67 -0
- package/src/web/MarkdownTextInput.css +9 -7
- package/src/web/utils/blockLayout.ts +124 -14
- package/src/web/utils/blockUtils.ts +365 -59
- package/src/web/utils/elementUtils.ts +87 -0
- package/src/web/utils/parserUtils.ts +119 -31
- package/src/web/utils/treeUtils.ts +6 -1
- package/src/web/utils/webStyleUtils.ts +32 -8
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
- package/apple/RCTMarcusStyle+Codegen.mm +0 -64
- package/apple/RCTMarcusUtils.h +0 -26
- package/apple/RCTMarcusUtils.mm +0 -26
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
package app.getbullet.marcus
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableArray
|
|
4
|
+
import com.facebook.react.bridge.ReadableType
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Reads the `ranges` prop into what the formatter wants.
|
|
8
|
+
*
|
|
9
|
+
* The JavaScript side has already stripped the syntax and remapped every offset onto the text the
|
|
10
|
+
* child `Text` is rendering, so nothing here validates or reorders: emission order is the contract
|
|
11
|
+
* the formatter walks, and reordering it would break the pairing of a block prefix with the
|
|
12
|
+
* container that follows it.
|
|
13
|
+
*
|
|
14
|
+
* An object with `@JvmStatic` rather than a top-level function, because `CustomFabricUIManager` is
|
|
15
|
+
* Java and Kotlin mangles the names of top-level internal ones.
|
|
16
|
+
*/
|
|
17
|
+
object MarkdownRanges {
|
|
18
|
+
|
|
19
|
+
@JvmStatic
|
|
20
|
+
fun read(array: ReadableArray?): List<MarkdownRange> {
|
|
21
|
+
if (array == null || array.size() == 0) {
|
|
22
|
+
return emptyList()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
val ranges = ArrayList<MarkdownRange>(array.size())
|
|
26
|
+
|
|
27
|
+
for (i in 0 until array.size()) {
|
|
28
|
+
if (array.getType(i) != ReadableType.Map) {
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
val map = array.getMap(i) ?: continue
|
|
33
|
+
val type = map.getString("type") ?: continue
|
|
34
|
+
|
|
35
|
+
ranges.add(
|
|
36
|
+
MarkdownRange(
|
|
37
|
+
type = type,
|
|
38
|
+
start = map.getInt("start"),
|
|
39
|
+
length = map.getInt("length"),
|
|
40
|
+
depth = map.getInt("depth")
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return ranges
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -7,75 +7,154 @@ import com.facebook.react.bridge.JSApplicationCausedNativeException
|
|
|
7
7
|
import com.facebook.react.bridge.ReadableMap
|
|
8
8
|
import com.facebook.react.bridge.ReadableType
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
val
|
|
10
|
+
/**
|
|
11
|
+
* The text attributes one styled run can carry: the subset of React Native's
|
|
12
|
+
* `TextStyle` that survives as a span.
|
|
13
|
+
*
|
|
14
|
+
* Everything is optional and everything unset is inherited, which is what a run
|
|
15
|
+
* of markdown wants -- a mention that only names a colour keeps the face, the
|
|
16
|
+
* size and the weight of the prose it sits in. "Unset" is null for a colour or a
|
|
17
|
+
* name and zero for a length, which costs nothing: a zero-length `fontSize`,
|
|
18
|
+
* `letterSpacing` or `lineHeight` would mean the same as leaving it out.
|
|
19
|
+
*
|
|
20
|
+
* `textDecorationColor` and `textDecorationStyle` are not read. Android draws an
|
|
21
|
+
* underline and a strikethrough in the text's own colour as a solid line and
|
|
22
|
+
* offers no way to say otherwise, so the two are honoured on iOS and the web and
|
|
23
|
+
* documented as such rather than parsed and dropped here.
|
|
24
|
+
*/
|
|
25
|
+
open class MarkdownTextStyle(map: ReadableMap?, context: Context) {
|
|
26
|
+
@ColorInt val color = parseColor(map, "color", context)
|
|
27
|
+
val fontFamily = parseString(map, "fontFamily")
|
|
28
|
+
val fontSize = parseFloat(map, "fontSize")
|
|
29
|
+
val fontStyle = parseString(map, "fontStyle")
|
|
30
|
+
val fontWeight = parseString(map, "fontWeight")
|
|
31
|
+
val letterSpacing = parseFloat(map, "letterSpacing")
|
|
32
|
+
val lineHeight = parseFloat(map, "lineHeight")
|
|
33
|
+
val textDecorationLine = parseString(map, "textDecorationLine")
|
|
34
|
+
@ColorInt val textShadowColor = parseColor(map, "textShadowColor", context)
|
|
35
|
+
val textShadowOffsetX = parseFloat(map?.let { style(it, "textShadowOffset") }, "width")
|
|
36
|
+
val textShadowOffsetY = parseFloat(map?.let { style(it, "textShadowOffset") }, "height")
|
|
37
|
+
val textShadowRadius = parseFloat(map, "textShadowRadius")
|
|
38
|
+
}
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
/**
|
|
41
|
+
* A run drawn as a box rather than as a background behind the glyphs: an inline
|
|
42
|
+
* or fenced run of code, or a mention's pill.
|
|
43
|
+
*
|
|
44
|
+
* Only the per-axis lengths are here. A style may be written with a single
|
|
45
|
+
* `padding` or `margin` covering both axes; `mergeMarkdownStyleWithDefault`
|
|
46
|
+
* resolves that into the two before the style is ever sent, so a length of zero
|
|
47
|
+
* here means zero rather than "unset".
|
|
48
|
+
*
|
|
49
|
+
* The lengths are in dp, as they are written in a style. What draws them
|
|
50
|
+
* converts, so a value means the same thing wherever it is read.
|
|
51
|
+
*/
|
|
52
|
+
class MarkdownBoxStyle(map: ReadableMap?, context: Context) : MarkdownTextStyle(map, context) {
|
|
53
|
+
@ColorInt val backgroundColor = parseColor(map, "backgroundColor", context)
|
|
54
|
+
@ColorInt val borderColor = parseColor(map, "borderColor", context)
|
|
55
|
+
val borderWidth = parseFloat(map, "borderWidth")
|
|
56
|
+
val borderRadius = parseFloat(map, "borderRadius")
|
|
57
|
+
val paddingHorizontal = parseFloat(map, "paddingHorizontal")
|
|
58
|
+
val paddingVertical = parseFloat(map, "paddingVertical")
|
|
59
|
+
val marginHorizontal = parseFloat(map, "marginHorizontal")
|
|
60
|
+
val marginVertical = parseFloat(map, "marginVertical")
|
|
61
|
+
}
|
|
30
62
|
|
|
31
|
-
|
|
32
|
-
|
|
63
|
+
class MarkdownHeadingStyle(map: ReadableMap?, context: Context) : MarkdownTextStyle(map, context) {
|
|
64
|
+
/**
|
|
65
|
+
* Each level down multiplies the one above, so level N is
|
|
66
|
+
* `fontSize * scale^(N - 1)`. Six levels, matching HTML.
|
|
67
|
+
*/
|
|
68
|
+
val scale = parseFloat(map, "scale")
|
|
69
|
+
}
|
|
33
70
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The bar beside a quoted block, the indent it opens, and the text inside it.
|
|
73
|
+
*
|
|
74
|
+
* `marginStart` and `paddingStart` rather than left and right: the gutter opens
|
|
75
|
+
* on the line's leading edge, which is what a `LeadingMarginSpan` has always
|
|
76
|
+
* meant, so the names now say what was already drawn.
|
|
77
|
+
*/
|
|
78
|
+
class MarkdownBlockquoteStyle(map: ReadableMap?, context: Context) :
|
|
79
|
+
MarkdownTextStyle(map, context) {
|
|
80
|
+
@ColorInt val borderColor = parseColor(map, "borderColor", context)
|
|
81
|
+
val borderWidth = parseFloat(map, "borderWidth")
|
|
82
|
+
/** Rounds the bar's ends. A bar whose radius is half its width is a capsule. */
|
|
83
|
+
val borderRadius = parseFloat(map, "borderRadius")
|
|
84
|
+
val marginStart = parseFloat(map, "marginStart")
|
|
85
|
+
val paddingStart = parseFloat(map, "paddingStart")
|
|
86
|
+
}
|
|
38
87
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Indent, and the marker that sits in the gutter it opens.
|
|
90
|
+
*
|
|
91
|
+
* The marker is sized from the base font -- the size the wrapped `Text` renders
|
|
92
|
+
* at -- rather than from a size of its own, so a list marks itself in proportion
|
|
93
|
+
* to the prose it marks however that prose is sized. [marker] is what it is
|
|
94
|
+
* drawn with, already resolved against `syntax` on the JavaScript side, so
|
|
95
|
+
* nothing here has to know where an unset value came from.
|
|
96
|
+
*/
|
|
97
|
+
class MarkdownListStyle(map: ReadableMap?, context: Context) {
|
|
98
|
+
val marginStart = parseFloat(map, "marginStart")
|
|
99
|
+
val paddingStart = parseFloat(map, "paddingStart")
|
|
100
|
+
val markerScale = parseFloat(map, "markerScale")
|
|
101
|
+
val markerPadding = parseFloat(map, "markerPadding")
|
|
102
|
+
val marker = MarkdownTextStyle(map?.let { style(it, "marker") }, context)
|
|
103
|
+
}
|
|
43
104
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
val
|
|
105
|
+
class MarkdownEmojiStyle(map: ReadableMap?, context: Context) {
|
|
106
|
+
val fontSize = parseFloat(map, "fontSize")
|
|
107
|
+
val fontFamily = parseString(map, "fontFamily")
|
|
108
|
+
}
|
|
47
109
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
val
|
|
110
|
+
class MarkdownStyle(map: ReadableMap, context: Context) {
|
|
111
|
+
val syntax = MarkdownTextStyle(style(map, "syntax"), context)
|
|
112
|
+
val link = MarkdownTextStyle(style(map, "link"), context)
|
|
113
|
+
val strong = MarkdownTextStyle(style(map, "strong"), context)
|
|
114
|
+
val emphasis = MarkdownTextStyle(style(map, "emphasis"), context)
|
|
115
|
+
val strikethrough = MarkdownTextStyle(style(map, "strikethrough"), context)
|
|
116
|
+
val heading = MarkdownHeadingStyle(style(map, "heading"), context)
|
|
117
|
+
val emoji = MarkdownEmojiStyle(style(map, "emoji"), context)
|
|
118
|
+
val blockquote = MarkdownBlockquoteStyle(style(map, "blockquote"), context)
|
|
119
|
+
val orderedList = MarkdownListStyle(style(map, "orderedList"), context)
|
|
120
|
+
val unorderedList = MarkdownListStyle(style(map, "unorderedList"), context)
|
|
121
|
+
val code = MarkdownBoxStyle(style(map, "code"), context)
|
|
122
|
+
val pre = MarkdownBoxStyle(style(map, "pre"), context)
|
|
123
|
+
val mention = MarkdownBoxStyle(style(map, "mention"), context)
|
|
124
|
+
}
|
|
51
125
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
126
|
+
/** The nested map under [key], or null where the style says nothing about it. */
|
|
127
|
+
internal fun style(map: ReadableMap, key: String): ReadableMap? =
|
|
128
|
+
if (map.hasKey(key)) map.getMap(key) else null
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Reads a property, or the value that stands for "unset": null for a colour and
|
|
132
|
+
* a name, zero for a length.
|
|
133
|
+
*
|
|
134
|
+
* A missing entry and a missing property are the same thing, which is what lets
|
|
135
|
+
* a caller pass a map it did not have to check for.
|
|
136
|
+
*/
|
|
137
|
+
@ColorInt
|
|
138
|
+
internal fun parseColor(map: ReadableMap?, prop: String, context: Context): Int? {
|
|
139
|
+
if (map == null || !map.hasKey(prop) || map.isNull(prop)) {
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
55
142
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
143
|
+
val value = map.getDynamic(prop)
|
|
144
|
+
val color = when (value.type) {
|
|
145
|
+
ReadableType.Number -> ColorPropConverter.getColor(value.asDouble(), context)
|
|
146
|
+
ReadableType.Map -> ColorPropConverter.getColor(value.asMap(), context)
|
|
147
|
+
else -> throw JSApplicationCausedNativeException(
|
|
148
|
+
"ColorValue: the value must be a number or Object."
|
|
149
|
+
)
|
|
150
|
+
}
|
|
59
151
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
val value = style(map, key).getDynamic(prop)
|
|
63
|
-
val color = when (value.type) {
|
|
64
|
-
ReadableType.Number -> ColorPropConverter.getColor(value.asDouble(), context)
|
|
65
|
-
ReadableType.Map -> ColorPropConverter.getColor(value.asMap(), context)
|
|
66
|
-
else -> throw JSApplicationCausedNativeException("ColorValue: the value must be a number or Object.")
|
|
67
|
-
}
|
|
68
|
-
return requireNotNull(color) {
|
|
69
|
-
"[react-native-marcus] markdownStyle.$key.$prop could not be resolved to a color"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
152
|
+
return color
|
|
153
|
+
}
|
|
72
154
|
|
|
73
|
-
|
|
74
|
-
|
|
155
|
+
internal fun parseFloat(map: ReadableMap?, prop: String): Float =
|
|
156
|
+
if (map == null || !map.hasKey(prop) || map.isNull(prop)) 0f
|
|
157
|
+
else map.getDouble(prop).toFloat()
|
|
75
158
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"[react-native-marcus] markdownStyle.$key.$prop is missing"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
159
|
+
internal fun parseString(map: ReadableMap?, prop: String): String? =
|
|
160
|
+
if (map == null || !map.hasKey(prop)) null else map.getString(prop)
|
|
@@ -14,12 +14,18 @@ import com.facebook.react.util.RNLog
|
|
|
14
14
|
* nested inside them start, and has to see the size the text is drawn at.
|
|
15
15
|
* Null on the measure path, which has no view yet and carries the size on the
|
|
16
16
|
* spannable instead.
|
|
17
|
+
* @param backgroundPass set for an input, where the view paints the markdown
|
|
18
|
+
* backgrounds itself so that the caret stays visible. Null everywhere else.
|
|
19
|
+
* @param display set for a `Text`, whose list markers are rendered rather than
|
|
20
|
+
* shown; see [MarkdownFormatter].
|
|
17
21
|
*/
|
|
18
22
|
class MarkdownUtils @JvmOverloads constructor(
|
|
19
23
|
private val reactContext: ReactContext,
|
|
20
|
-
private val textPaint: TextPaint? = null
|
|
24
|
+
private val textPaint: TextPaint? = null,
|
|
25
|
+
backgroundPass: MarkdownBackgroundPass? = null,
|
|
26
|
+
display: Boolean = false
|
|
21
27
|
) {
|
|
22
|
-
private val markdownFormatter = MarkdownFormatter(reactContext.assets)
|
|
28
|
+
private val markdownFormatter = MarkdownFormatter(reactContext.assets, backgroundPass, display)
|
|
23
29
|
|
|
24
30
|
// Hoisted rather than built per call: applyMarkdownFormatting runs several times per
|
|
25
31
|
// keystroke, and this is only ever needed for a warning that normally never fires.
|
|
@@ -30,6 +36,15 @@ class MarkdownUtils @JvmOverloads constructor(
|
|
|
30
36
|
var markdownStyle: MarkdownStyle? = null
|
|
31
37
|
var parserId: Int = 0
|
|
32
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Ranges to format with, for a caller that already has them.
|
|
41
|
+
*
|
|
42
|
+
* A `Text` is parsed in JavaScript -- its content only ever changes by a re-render, so there is
|
|
43
|
+
* nothing for a worklet to react to -- and the result arrives as a prop. A `TextInput` leaves
|
|
44
|
+
* this null and the parser above is used instead.
|
|
45
|
+
*/
|
|
46
|
+
var ranges: List<MarkdownRange>? = null
|
|
47
|
+
|
|
33
48
|
/**
|
|
34
49
|
* Records what the markdown spans currently on a spannable were built from.
|
|
35
50
|
*
|
|
@@ -38,9 +53,15 @@ class MarkdownUtils @JvmOverloads constructor(
|
|
|
38
53
|
* this along with the markdown spans, and the next pass rebuilds both. A field would
|
|
39
54
|
* survive that and we would skip a pass that was actually needed.
|
|
40
55
|
*/
|
|
41
|
-
private class Fingerprint(
|
|
56
|
+
private class Fingerprint(
|
|
57
|
+
val text: String,
|
|
58
|
+
val parserId: Int,
|
|
59
|
+
val style: MarkdownStyle,
|
|
60
|
+
val ranges: List<MarkdownRange>?
|
|
61
|
+
)
|
|
42
62
|
|
|
43
|
-
|
|
63
|
+
/** Whether anything was reformatted, which is what tells a caller to push the result on. */
|
|
64
|
+
fun applyMarkdownFormatting(ssb: SpannableStringBuilder): Boolean {
|
|
44
65
|
try {
|
|
45
66
|
Trace.beginSection("applyMarkdownFormatting")
|
|
46
67
|
val style = checkNotNull(markdownStyle) { "[react-native-marcus] markdownStyle is null" }
|
|
@@ -49,13 +70,14 @@ class MarkdownUtils @JvmOverloads constructor(
|
|
|
49
70
|
// Native's state round-trip. They all ask for the same text, so only the first has any
|
|
50
71
|
// work to do.
|
|
51
72
|
if (isUpToDate(ssb, style)) {
|
|
52
|
-
return
|
|
73
|
+
return false
|
|
53
74
|
}
|
|
54
75
|
|
|
55
76
|
val text = ssb.toString()
|
|
56
|
-
val markdownRanges = MarkdownParser.shared.parse(text, parserId, onSchemaError)
|
|
77
|
+
val markdownRanges = ranges ?: MarkdownParser.shared.parse(text, parserId, onSchemaError)
|
|
57
78
|
markdownFormatter.format(ssb, markdownRanges, style, textPaint)
|
|
58
79
|
stamp(ssb, text, style)
|
|
80
|
+
return true
|
|
59
81
|
} finally {
|
|
60
82
|
Trace.endSection()
|
|
61
83
|
}
|
|
@@ -68,6 +90,9 @@ class MarkdownUtils @JvmOverloads constructor(
|
|
|
68
90
|
// Identity, not equality: MarkdownStyle is rebuilt whenever the prop arrives, so a
|
|
69
91
|
// different instance means the style may have changed and we re-apply to be safe.
|
|
70
92
|
fingerprint.style === style &&
|
|
93
|
+
// Same reasoning for the ranges, which are rebuilt from the prop on every commit that
|
|
94
|
+
// carries them.
|
|
95
|
+
fingerprint.ranges === ranges &&
|
|
71
96
|
// Compares the CharSequence in place. ssb.toString() would copy the whole document on
|
|
72
97
|
// a path whose entire purpose is to do no work.
|
|
73
98
|
TextUtils.equals(ssb, fingerprint.text)
|
|
@@ -79,6 +104,11 @@ class MarkdownUtils @JvmOverloads constructor(
|
|
|
79
104
|
for (stale in ssb.getSpans(0, ssb.length, Fingerprint::class.java)) {
|
|
80
105
|
ssb.removeSpan(stale)
|
|
81
106
|
}
|
|
82
|
-
ssb.setSpan(
|
|
107
|
+
ssb.setSpan(
|
|
108
|
+
Fingerprint(text, parserId, style, ranges),
|
|
109
|
+
0,
|
|
110
|
+
ssb.length,
|
|
111
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
112
|
+
)
|
|
83
113
|
}
|
|
84
114
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package app.getbullet.marcus.spans
|
|
2
|
+
|
|
3
|
+
import android.graphics.Paint
|
|
4
|
+
import android.text.style.LineHeightSpan
|
|
5
|
+
import com.facebook.react.uimanager.PixelUtil
|
|
6
|
+
import kotlin.math.roundToInt
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Draws the lines a run touches at an exact height.
|
|
10
|
+
*
|
|
11
|
+
* A line height is a property of a line rather than of a run, so a run that asks
|
|
12
|
+
* for one raises the whole line it sits on. iOS puts the same value on the
|
|
13
|
+
* paragraph style for the same reason, and the public type says as much.
|
|
14
|
+
*
|
|
15
|
+
* The difference is taken off the ascent and the descent in the proportion they
|
|
16
|
+
* already stood in, which is what React Native's own line-height span does and
|
|
17
|
+
* what keeps the text centred in the taller line rather than sitting on its
|
|
18
|
+
* floor.
|
|
19
|
+
*
|
|
20
|
+
* @param lineHeight in dp, as it is written in a style
|
|
21
|
+
*/
|
|
22
|
+
class MarkdownAbsoluteLineHeightSpan(lineHeight: Float) : MarkdownSpan, LineHeightSpan {
|
|
23
|
+
|
|
24
|
+
private val height = PixelUtil.toPixelFromDIP(lineHeight).roundToInt()
|
|
25
|
+
|
|
26
|
+
override fun chooseHeight(
|
|
27
|
+
text: CharSequence?,
|
|
28
|
+
start: Int,
|
|
29
|
+
end: Int,
|
|
30
|
+
spanstartv: Int,
|
|
31
|
+
lineHeight: Int,
|
|
32
|
+
fm: Paint.FontMetricsInt
|
|
33
|
+
) {
|
|
34
|
+
if (height <= 0) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
val current = fm.descent - fm.ascent
|
|
39
|
+
if (current <= 0) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fm.descent = (fm.descent.toFloat() * height / current).roundToInt()
|
|
44
|
+
fm.ascent = fm.descent - height
|
|
45
|
+
fm.top = fm.ascent
|
|
46
|
+
fm.bottom = fm.descent
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -8,16 +8,50 @@ import android.text.StaticLayout
|
|
|
8
8
|
import android.text.TextPaint
|
|
9
9
|
import android.text.style.LineBackgroundSpan
|
|
10
10
|
import androidx.annotation.ColorInt
|
|
11
|
-
|
|
11
|
+
import app.getbullet.marcus.MarkdownBackgroundPass
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The rounded box behind a run of text: a mention's pill, or an inline run of
|
|
15
|
+
* code.
|
|
16
|
+
*
|
|
17
|
+
* Drawn a line at a time, because soft wrapping can split a run, and only the
|
|
18
|
+
* sides that actually terminate it are rounded -- the break itself stays square,
|
|
19
|
+
* so the two halves read as one run. iOS draws the same shape the same way, in
|
|
20
|
+
* `MarkdownTextLayoutFragment` and `MarkdownParagraphLayoutManager`.
|
|
21
|
+
*
|
|
22
|
+
* The padding is the room between the glyphs and the box, the margin the room
|
|
23
|
+
* between the box and the text either side of it. Horizontally both are held
|
|
24
|
+
* open by the `MarkdownGapSpan`s the formatter puts on the run's first and last
|
|
25
|
+
* characters, so what is left to do here is place the box's edges inside the
|
|
26
|
+
* space those reserved. Vertically only the padding is used, and the box grows
|
|
27
|
+
* out of the font's own box into the line's spacing, which is what an inline box
|
|
28
|
+
* does everywhere else: a line cannot be pushed apart for one run on it, so
|
|
29
|
+
* there is nothing a vertical margin could hold open.
|
|
30
|
+
*
|
|
31
|
+
* Only the vertical padding is a parameter: the horizontal room was already
|
|
32
|
+
* taken by the gap spans, so all this has to know is how far inside it the box's
|
|
33
|
+
* edge sits, which is the margin.
|
|
34
|
+
*
|
|
35
|
+
* @param runStart first character of the run, for the corners and the insets
|
|
36
|
+
* @param runEnd one past its last character
|
|
37
|
+
* @param pass set in an input, where the view paints the backgrounds itself so
|
|
38
|
+
* that the caret stays visible; null in a `Text`, which has no caret and lets
|
|
39
|
+
* the layout paint them
|
|
40
|
+
*/
|
|
12
41
|
class MarkdownBackgroundSpan(
|
|
13
42
|
@ColorInt private val backgroundColor: Int,
|
|
14
43
|
private val borderRadius: Float,
|
|
15
|
-
private val
|
|
16
|
-
private val
|
|
44
|
+
private val runStart: Int,
|
|
45
|
+
private val runEnd: Int,
|
|
46
|
+
private val paddingVertical: Float = 0f,
|
|
47
|
+
private val marginHorizontal: Float = 0f,
|
|
48
|
+
@ColorInt private val borderColor: Int? = null,
|
|
49
|
+
private val borderWidth: Float = 0f,
|
|
50
|
+
private val pass: MarkdownBackgroundPass? = null
|
|
17
51
|
) : MarkdownSpan, LineBackgroundSpan {
|
|
18
52
|
|
|
19
|
-
private
|
|
20
|
-
private val
|
|
53
|
+
private val rect = RectF()
|
|
54
|
+
private val path = Path()
|
|
21
55
|
|
|
22
56
|
override fun drawBackground(
|
|
23
57
|
canvas: Canvas,
|
|
@@ -32,58 +66,78 @@ class MarkdownBackgroundSpan(
|
|
|
32
66
|
end: Int,
|
|
33
67
|
lnum: Int
|
|
34
68
|
) {
|
|
69
|
+
if (pass != null && !pass.painting) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// The line on its own, measured as it was laid out. `drawBackground` is
|
|
74
|
+
// handed the line's characters and its paint but not the layout that
|
|
75
|
+
// positioned them, so where the run sits on it has to be measured again.
|
|
35
76
|
val lineText = text.subSequence(start, end)
|
|
36
|
-
val
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
val startX = newLayout.getPrimaryHorizontal(
|
|
59
|
-
if (mentionStartsInCurrentLine) relativeMentionStart else currentLineStart
|
|
60
|
-
)
|
|
61
|
-
val endX = newLayout.getPrimaryHorizontal(
|
|
62
|
-
if (mentionEndsInCurrentLine) relativeMentionEnd else currentLineEnd
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
val fm = paint.getFontMetrics()
|
|
66
|
-
val startY = baseline + fm.ascent
|
|
67
|
-
val endY = baseline + fm.descent
|
|
68
|
-
|
|
69
|
-
val lineRect = RectF(startX, startY, endX, endY)
|
|
70
|
-
backgroundPath.reset()
|
|
71
|
-
backgroundPath.addRoundRect(
|
|
72
|
-
lineRect,
|
|
73
|
-
createRadii(mentionStartsInCurrentLine, mentionEndsInCurrentLine),
|
|
74
|
-
Path.Direction.CW
|
|
75
|
-
)
|
|
77
|
+
val layout = StaticLayout.Builder
|
|
78
|
+
.obtain(lineText, 0, lineText.length, paint as TextPaint, right)
|
|
79
|
+
.build()
|
|
80
|
+
|
|
81
|
+
val opens = runStart >= start
|
|
82
|
+
val closes = runEnd <= end
|
|
83
|
+
|
|
84
|
+
val startX = layout.getPrimaryHorizontal(if (opens) runStart - start else 0)
|
|
85
|
+
val endX = layout.getPrimaryHorizontal(if (closes) runEnd - start else lineText.length)
|
|
86
|
+
|
|
87
|
+
// The gap spans reserved padding and margin inside the first and last
|
|
88
|
+
// characters, so the box's edge is the margin's width inside what they took.
|
|
89
|
+
val metrics = paint.fontMetrics
|
|
90
|
+
rect.set(
|
|
91
|
+
startX + if (opens) marginHorizontal else 0f,
|
|
92
|
+
baseline + metrics.ascent - paddingVertical,
|
|
93
|
+
endX - if (closes) marginHorizontal else 0f,
|
|
94
|
+
baseline + metrics.descent + paddingVertical,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if (rect.width() <= 0f || rect.height() <= 0f) {
|
|
98
|
+
return
|
|
76
99
|
}
|
|
77
100
|
|
|
101
|
+
path.reset()
|
|
102
|
+
path.addRoundRect(rect, radii(opens, closes), Path.Direction.CW)
|
|
103
|
+
|
|
104
|
+
fill(canvas, paint, path)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Fills the box and, where the style asked for one, draws its border.
|
|
109
|
+
*
|
|
110
|
+
* The border is stroked at twice its width and clipped to the box, so the
|
|
111
|
+
* outer half is trimmed away and a bordered run covers exactly the room an
|
|
112
|
+
* unbordered one does. `MarkdownFormatter.swift` draws its border the same
|
|
113
|
+
* way, and it is what a border on an element does on the web.
|
|
114
|
+
*/
|
|
115
|
+
private fun fill(canvas: Canvas, paint: Paint, path: Path) {
|
|
78
116
|
val originalColor = paint.color
|
|
79
|
-
|
|
117
|
+
val originalStyle = paint.style
|
|
118
|
+
val originalWidth = paint.strokeWidth
|
|
80
119
|
|
|
81
|
-
|
|
120
|
+
paint.style = Paint.Style.FILL
|
|
121
|
+
paint.color = backgroundColor
|
|
122
|
+
canvas.drawPath(path, paint)
|
|
123
|
+
|
|
124
|
+
if (borderColor != null && borderWidth > 0f) {
|
|
125
|
+
canvas.save()
|
|
126
|
+
canvas.clipPath(path)
|
|
127
|
+
paint.style = Paint.Style.STROKE
|
|
128
|
+
paint.strokeWidth = borderWidth * 2f
|
|
129
|
+
paint.color = borderColor
|
|
130
|
+
canvas.drawPath(path, paint)
|
|
131
|
+
canvas.restore()
|
|
132
|
+
}
|
|
82
133
|
|
|
83
134
|
paint.color = originalColor
|
|
135
|
+
paint.style = originalStyle
|
|
136
|
+
paint.strokeWidth = originalWidth
|
|
84
137
|
}
|
|
85
138
|
|
|
86
|
-
|
|
139
|
+
/** Corners of one line's rect: only the ends of the run are rounded. */
|
|
140
|
+
private fun radii(roundedLeft: Boolean, roundedRight: Boolean): FloatArray {
|
|
87
141
|
val radii = FloatArray(8)
|
|
88
142
|
|
|
89
143
|
if (roundedLeft) {
|
|
@@ -13,11 +13,15 @@ import androidx.annotation.ColorInt
|
|
|
13
13
|
* One span per line rather than one per container: where a container's gutter
|
|
14
14
|
* goes depends on the markers of the containers around it, so the offsets are
|
|
15
15
|
* worked out together by the formatter and handed over already in pixels.
|
|
16
|
+
*
|
|
17
|
+
* The ribbon's own three values default to nothing, for a line that is only
|
|
18
|
+
* indented -- a code block's padding, where no quote is involved.
|
|
16
19
|
*/
|
|
17
20
|
class MarkdownBlockIndentSpan(
|
|
18
|
-
@ColorInt private val borderColor: Int,
|
|
19
|
-
private val borderWidth: Float,
|
|
20
|
-
private val borderSpacing: Float
|
|
21
|
+
@ColorInt private val borderColor: Int = 0,
|
|
22
|
+
private val borderWidth: Float = 0f,
|
|
23
|
+
private val borderSpacing: Float = 0f,
|
|
24
|
+
private val borderRadius: Float = 0f
|
|
21
25
|
) : MarkdownSpan, LeadingMarginSpan {
|
|
22
26
|
|
|
23
27
|
/** Where the line's text starts, and where it resumes when it wraps. */
|
|
@@ -58,7 +62,21 @@ class MarkdownBlockIndentSpan(
|
|
|
58
62
|
|
|
59
63
|
for (level in 0 until depth) {
|
|
60
64
|
val left = x + dir * (ribbonOffset + borderSpacing * level)
|
|
61
|
-
|
|
65
|
+
val right = left + dir * borderWidth
|
|
66
|
+
|
|
67
|
+
if (borderRadius > 0f) {
|
|
68
|
+
c.drawRoundRect(
|
|
69
|
+
minOf(left, right),
|
|
70
|
+
top.toFloat(),
|
|
71
|
+
maxOf(left, right),
|
|
72
|
+
bottom.toFloat(),
|
|
73
|
+
borderRadius,
|
|
74
|
+
borderRadius,
|
|
75
|
+
p
|
|
76
|
+
)
|
|
77
|
+
} else {
|
|
78
|
+
c.drawRect(left, top.toFloat(), right, bottom.toFloat(), p)
|
|
79
|
+
}
|
|
62
80
|
}
|
|
63
81
|
|
|
64
82
|
p.style = originalStyle
|