react-native-enriched-markdown-scaffold 1.0.2-scaffold.2 → 1.0.2-scaffold.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +1 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +1 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +72 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +50 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +10 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +134 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +62 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +13 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +42 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyRequestEvent.kt +28 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLayoutEvent.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +37 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +13 -99
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/InlineChipPainter.kt +140 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkChipSpans.kt +177 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +1 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +36 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkVariantEntry.kt +7 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/HTMLGenerator.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/MarkdownExtractor.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt +100 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +24 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +27 -8
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +9 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +18 -2
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +3 -1
- package/docs/API_REFERENCE.md +101 -0
- package/docs/COPY_OPTIONS.md +14 -0
- package/docs/MENTIONS.md +8 -1
- package/docs/STYLES.md +27 -0
- package/docs/TEXT.md +2 -0
- package/ios/EnrichedMarkdown.mm +91 -2
- package/ios/EnrichedMarkdownText.mm +64 -3
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +72 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +50 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +10 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +134 -0
- package/ios/internals/MeasurementCache.h +5 -0
- package/ios/renderer/LinkRenderer.m +47 -15
- package/ios/styles/StyleConfig.h +16 -0
- package/ios/styles/StyleConfig.mm +60 -0
- package/ios/utils/CodeBackground.m +3 -65
- package/ios/utils/ENRMBlockMenu.h +12 -3
- package/ios/utils/ENRMChipDrawing.h +24 -0
- package/ios/utils/ENRMChipDrawing.m +83 -0
- package/ios/utils/ENRMLinkChip.h +70 -0
- package/ios/utils/ENRMLinkChip.m +194 -0
- package/ios/utils/ENRMLinkLayout.h +50 -0
- package/ios/utils/ENRMLinkLayout.m +45 -0
- package/ios/utils/EditMenuUtils+macOS.m +4 -1
- package/ios/utils/EditMenuUtils.h +10 -1
- package/ios/utils/EditMenuUtils.m +22 -5
- package/ios/utils/HTMLGenerator.m +2 -1
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +4 -1
- package/ios/utils/MarkdownExtractor.m +2 -1
- package/ios/utils/RTFExportUtils.m +2 -0
- package/ios/utils/StylePropsUtils.h +37 -1
- package/ios/utils/TextViewLayoutManager.mm +3 -0
- package/ios/views/ENRMCodeBlockContainerView.h +4 -0
- package/ios/views/ENRMCodeBlockContainerView.m +8 -1
- package/ios/views/ENRMMathContainerView.h +4 -0
- package/ios/views/ENRMMathContainerView.m +8 -1
- package/ios/views/TableContainerView.h +7 -0
- package/ios/views/TableContainerView.m +30 -3
- package/lib/module/EnrichedMarkdownNativeComponent.ts +57 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +57 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +119 -9
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +13 -2
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +13 -2
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/web/EnrichedMarkdownText.js +6 -3
- package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/web/renderers/InlineRenderers.js +96 -3
- package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
- package/lib/module/web/styles.js +18 -0
- package/lib/module/web/styles.js.map +1 -1
- package/lib/module/web/utils.js +13 -0
- package/lib/module/web/utils.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +53 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +53 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +1 -1
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +5 -5
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyle.d.ts +28 -5
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +10 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +54 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +41 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +22 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/lib/typescript/src/web/types.d.ts +4 -0
- package/lib/typescript/src/web/types.d.ts.map +1 -1
- package/lib/typescript/src/web/utils.d.ts +4 -0
- package/lib/typescript/src/web/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +57 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +57 -0
- package/src/index.tsx +3 -0
- package/src/index.web.tsx +5 -1
- package/src/native/EnrichedMarkdownText.tsx +127 -7
- package/src/normalizeMarkdownStyle.ts +13 -0
- package/src/normalizeMarkdownStyle.web.ts +11 -0
- package/src/types/MarkdownStyle.ts +29 -5
- package/src/types/MarkdownStyleInternal.ts +10 -0
- package/src/types/MarkdownTextProps.ts +56 -0
- package/src/types/MarkdownTextProps.web.ts +43 -1
- package/src/types/events.ts +23 -0
- package/src/web/EnrichedMarkdownText.tsx +10 -3
- package/src/web/renderers/InlineRenderers.tsx +111 -3
- package/src/web/styles.ts +25 -0
- package/src/web/types.ts +4 -0
- package/src/web/utils.ts +8 -0
|
@@ -25,9 +25,12 @@ import com.swmansion.enriched.markdown.utils.common.RenderedSegment
|
|
|
25
25
|
import com.swmansion.enriched.markdown.utils.common.SegmentReconciler
|
|
26
26
|
import com.swmansion.enriched.markdown.utils.common.StreamingMarkdownFilter
|
|
27
27
|
import com.swmansion.enriched.markdown.utils.common.TableStreamingMode
|
|
28
|
+
import com.swmansion.enriched.markdown.utils.common.emitLinkLayout
|
|
28
29
|
import com.swmansion.enriched.markdown.utils.common.isReducedMotionEnabled
|
|
29
30
|
import com.swmansion.enriched.markdown.utils.common.splitASTIntoSegments
|
|
30
31
|
import com.swmansion.enriched.markdown.utils.text.TailFadeInAnimator
|
|
32
|
+
import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutCollector
|
|
33
|
+
import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutFragment
|
|
31
34
|
import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
|
|
32
35
|
import com.swmansion.enriched.markdown.utils.text.view.applySelectionColors
|
|
33
36
|
import com.swmansion.enriched.markdown.views.BlockMenuOptions
|
|
@@ -99,6 +102,8 @@ class EnrichedMarkdown
|
|
|
99
102
|
private var selectionMenuConfig = SelectionMenuConfig()
|
|
100
103
|
private var accessibilityLabels = AccessibilityLabels()
|
|
101
104
|
private var textBreakStrategy: String = BreakStrategyUtils.DEFAULT_STRATEGY
|
|
105
|
+
private var linkLayoutEnabled = false
|
|
106
|
+
private var lastLinkLayout: List<LinkLayoutFragment>? = null
|
|
102
107
|
|
|
103
108
|
private var onLinkPressCallback: ((String) -> Unit)? = null
|
|
104
109
|
private var onLinkLongPressCallback: ((String) -> Unit)? = null
|
|
@@ -106,6 +111,7 @@ class EnrichedMarkdown
|
|
|
106
111
|
private var onCopyPressCallback: ((String, String) -> Unit)? = null
|
|
107
112
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
108
113
|
var onContextMenuItemPressCallback: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
114
|
+
var onCopyRequestCallback: ((source: String, markdown: String, text: String, whole: Boolean) -> Unit)? = null
|
|
109
115
|
var spoilerOverlay: SpoilerOverlay = SpoilerOverlay.PARTICLES
|
|
110
116
|
set(value) {
|
|
111
117
|
if (field == value) return
|
|
@@ -318,19 +324,18 @@ class EnrichedMarkdown
|
|
|
318
324
|
private fun pushCopyLabelsToBlockSegments() {
|
|
319
325
|
val copyLabel = selectionMenuConfig.copyLabel
|
|
320
326
|
val copyAsMarkdownLabel = selectionMenuConfig.copyAsMarkdownLabel
|
|
321
|
-
val menuOptions = BlockMenuOptions.from(selectionMenuConfig)
|
|
322
327
|
segmentViews.forEach { view ->
|
|
323
328
|
when {
|
|
324
329
|
view is TableContainerView -> {
|
|
325
330
|
view.copyLabel = copyLabel
|
|
326
331
|
view.copyAsMarkdownLabel = copyAsMarkdownLabel
|
|
327
|
-
view.menuOptions =
|
|
332
|
+
view.menuOptions = blockMenuOptions("table")
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
view is CodeBlockContainerView -> {
|
|
331
336
|
view.copyLabel = copyLabel
|
|
332
337
|
view.copyAsMarkdownLabel = copyAsMarkdownLabel
|
|
333
|
-
view.menuOptions =
|
|
338
|
+
view.menuOptions = blockMenuOptions("code")
|
|
334
339
|
}
|
|
335
340
|
|
|
336
341
|
isMathContainerView(view) -> {
|
|
@@ -341,7 +346,7 @@ class EnrichedMarkdown
|
|
|
341
346
|
.invoke(view, copyAsMarkdownLabel)
|
|
342
347
|
view.javaClass
|
|
343
348
|
.getMethod("setMenuOptions", BlockMenuOptions::class.java)
|
|
344
|
-
.invoke(view,
|
|
349
|
+
.invoke(view, blockMenuOptions("math"))
|
|
345
350
|
}
|
|
346
351
|
}
|
|
347
352
|
}
|
|
@@ -368,6 +373,22 @@ class EnrichedMarkdown
|
|
|
368
373
|
}
|
|
369
374
|
}
|
|
370
375
|
|
|
376
|
+
private fun blockMenuOptions(source: String) =
|
|
377
|
+
BlockMenuOptions.from(selectionMenuConfig) { markdown, text ->
|
|
378
|
+
onCopyRequestCallback?.invoke(source, markdown, text, true)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Segments only see their own text, so a selection is the whole component
|
|
382
|
+
// only when it spans the sole segment; the markdown is then the original.
|
|
383
|
+
private fun forwardSegmentCopyRequest(
|
|
384
|
+
markdown: String,
|
|
385
|
+
text: String,
|
|
386
|
+
fullSelection: Boolean,
|
|
387
|
+
) {
|
|
388
|
+
val whole = fullSelection && segmentViews.size == 1 && currentMarkdown.isNotEmpty()
|
|
389
|
+
onCopyRequestCallback?.invoke("selection", if (whole) currentMarkdown else markdown, text, whole)
|
|
390
|
+
}
|
|
391
|
+
|
|
371
392
|
private fun forwardContextMenuItemPress(
|
|
372
393
|
itemText: String,
|
|
373
394
|
selectedText: String,
|
|
@@ -615,6 +636,7 @@ class EnrichedMarkdown
|
|
|
615
636
|
onTaskListItemPressCallback = { taskIndex, checked, itemText ->
|
|
616
637
|
this@EnrichedMarkdown.onTaskListItemPressCallback?.invoke(taskIndex, checked, itemText)
|
|
617
638
|
}
|
|
639
|
+
onCopyRequest = ::forwardSegmentCopyRequest
|
|
618
640
|
|
|
619
641
|
if (contextMenuItemTexts.isNotEmpty()) {
|
|
620
642
|
setContextMenuItems(contextMenuItemTexts, ::forwardContextMenuItemPress)
|
|
@@ -635,7 +657,7 @@ class EnrichedMarkdown
|
|
|
635
657
|
copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
|
|
636
658
|
copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
|
|
637
659
|
contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
|
|
638
|
-
menuOptions =
|
|
660
|
+
menuOptions = blockMenuOptions("table")
|
|
639
661
|
applyTableNode(segment.node)
|
|
640
662
|
}
|
|
641
663
|
|
|
@@ -646,7 +668,7 @@ class EnrichedMarkdown
|
|
|
646
668
|
copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
|
|
647
669
|
copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
|
|
648
670
|
contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
|
|
649
|
-
menuOptions =
|
|
671
|
+
menuOptions = blockMenuOptions("code")
|
|
650
672
|
onCopyPress = { code, language ->
|
|
651
673
|
this@EnrichedMarkdown.onCopyPressCallback?.invoke(code, language)
|
|
652
674
|
}
|
|
@@ -678,7 +700,7 @@ class EnrichedMarkdown
|
|
|
678
700
|
.invoke(view, selectionMenuConfig.copyAsMarkdownLabel)
|
|
679
701
|
resolvedClass
|
|
680
702
|
.getMethod("setMenuOptions", BlockMenuOptions::class.java)
|
|
681
|
-
.invoke(view,
|
|
703
|
+
.invoke(view, blockMenuOptions("math"))
|
|
682
704
|
setMathContextMenuHidden(view)
|
|
683
705
|
resolvedClass.getMethod("applyLatex", String::class.java).invoke(view, segment.latex)
|
|
684
706
|
view
|
|
@@ -765,6 +787,39 @@ class EnrichedMarkdown
|
|
|
765
787
|
currentY += segment?.segmentMarginBottom ?: 0
|
|
766
788
|
}
|
|
767
789
|
}
|
|
790
|
+
|
|
791
|
+
emitLinkLayoutIfNeeded()
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
fun setEmitLinkLayout(enabled: Boolean) {
|
|
795
|
+
if (linkLayoutEnabled == enabled) return
|
|
796
|
+
linkLayoutEnabled = enabled
|
|
797
|
+
lastLinkLayout = null
|
|
798
|
+
emitLinkLayoutIfNeeded()
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Reports link rects of every text segment (offset by the segment's position) to JS for
|
|
803
|
+
* `renderLinkDecoration`. Tables are skipped: their cells scroll horizontally, so rects would
|
|
804
|
+
* go stale. Runs after each segment layout pass; deduplicated so an unchanged layout sends nothing.
|
|
805
|
+
*/
|
|
806
|
+
private fun emitLinkLayoutIfNeeded() {
|
|
807
|
+
if (!linkLayoutEnabled || width <= 0) return
|
|
808
|
+
val fragments = mutableListOf<LinkLayoutFragment>()
|
|
809
|
+
var linkCount = 0
|
|
810
|
+
segmentViews.forEach { view ->
|
|
811
|
+
if (view is TableContainerView) {
|
|
812
|
+
linkCount += view.linkCount
|
|
813
|
+
return@forEach
|
|
814
|
+
}
|
|
815
|
+
if (view !is EnrichedMarkdownInternalText) return@forEach
|
|
816
|
+
val (segmentFragments, segmentLinks) = LinkLayoutCollector.collect(view, view.left, view.top, linkCount)
|
|
817
|
+
fragments += segmentFragments
|
|
818
|
+
linkCount += segmentLinks
|
|
819
|
+
}
|
|
820
|
+
if (fragments == lastLinkLayout) return
|
|
821
|
+
lastLinkLayout = fragments
|
|
822
|
+
emitLinkLayout(this, fragments)
|
|
768
823
|
}
|
|
769
824
|
|
|
770
825
|
private fun computeSegmentsTotalHeight(): Int {
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt
CHANGED
|
@@ -56,6 +56,7 @@ class EnrichedMarkdownInternalText
|
|
|
56
56
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
57
57
|
private var onContextMenuItemPress: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
58
58
|
var selectionMenuConfig: SelectionMenuConfig = SelectionMenuConfig()
|
|
59
|
+
var onCopyRequest: ((markdown: String, text: String, fullSelection: Boolean) -> Unit)? = null
|
|
59
60
|
private var activeActionMode: ActionMode? = null
|
|
60
61
|
var contextMenuHidden: Boolean = false
|
|
61
62
|
set(value) {
|
|
@@ -82,6 +83,9 @@ class EnrichedMarkdownInternalText
|
|
|
82
83
|
onCustomItemPress = { itemText, selectedText, start, end ->
|
|
83
84
|
onContextMenuItemPress?.invoke(itemText, selectedText, start, end)
|
|
84
85
|
},
|
|
86
|
+
onCopyRequest = { markdown, text, fullSelection ->
|
|
87
|
+
onCopyRequest?.invoke(markdown, text, fullSelection)
|
|
88
|
+
},
|
|
85
89
|
)
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -18,6 +18,7 @@ import com.swmansion.enriched.markdown.utils.common.CodeBlockStreamingMode
|
|
|
18
18
|
import com.swmansion.enriched.markdown.utils.common.TableStreamingMode
|
|
19
19
|
import com.swmansion.enriched.markdown.utils.common.emitContextMenuItemPress
|
|
20
20
|
import com.swmansion.enriched.markdown.utils.common.emitCopyPress
|
|
21
|
+
import com.swmansion.enriched.markdown.utils.common.emitCopyRequest
|
|
21
22
|
import com.swmansion.enriched.markdown.utils.common.emitLinkLongPress
|
|
22
23
|
import com.swmansion.enriched.markdown.utils.common.emitLinkPress
|
|
23
24
|
import com.swmansion.enriched.markdown.utils.common.emitTaskListItemPress
|
|
@@ -65,6 +66,10 @@ class EnrichedMarkdownManager :
|
|
|
65
66
|
emitCopyPress(view, code, language)
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
view.onCopyRequestCallback = { source, markdown, text, whole ->
|
|
70
|
+
emitCopyRequest(view, source, markdown, text, whole)
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
return view
|
|
69
74
|
}
|
|
70
75
|
|
|
@@ -172,6 +177,14 @@ class EnrichedMarkdownManager :
|
|
|
172
177
|
view?.setContextMenuHidden(contextMenuHidden)
|
|
173
178
|
}
|
|
174
179
|
|
|
180
|
+
@ReactProp(name = "emitLinkLayout", defaultBoolean = false)
|
|
181
|
+
override fun setEmitLinkLayout(
|
|
182
|
+
view: EnrichedMarkdown?,
|
|
183
|
+
emitLinkLayout: Boolean,
|
|
184
|
+
) {
|
|
185
|
+
view?.setEmitLinkLayout(emitLinkLayout)
|
|
186
|
+
}
|
|
187
|
+
|
|
175
188
|
@ReactProp(name = "enableLinkPreview", defaultBoolean = true)
|
|
176
189
|
override fun setEnableLinkPreview(
|
|
177
190
|
view: EnrichedMarkdown?,
|
|
@@ -24,8 +24,11 @@ import com.swmansion.enriched.markdown.spoiler.SpoilerOverlay
|
|
|
24
24
|
import com.swmansion.enriched.markdown.spoiler.SpoilerOverlayDrawer
|
|
25
25
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
26
26
|
import com.swmansion.enriched.markdown.utils.common.BreakStrategyUtils
|
|
27
|
+
import com.swmansion.enriched.markdown.utils.common.emitLinkLayout
|
|
27
28
|
import com.swmansion.enriched.markdown.utils.text.TailFadeInAnimator
|
|
28
29
|
import com.swmansion.enriched.markdown.utils.text.interaction.CheckboxTouchHelper
|
|
30
|
+
import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutCollector
|
|
31
|
+
import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutFragment
|
|
29
32
|
import com.swmansion.enriched.markdown.utils.text.view.LinkLongPressMovementMethod
|
|
30
33
|
import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
|
|
31
34
|
import com.swmansion.enriched.markdown.utils.text.view.applySelectableState
|
|
@@ -68,6 +71,7 @@ class EnrichedMarkdownText
|
|
|
68
71
|
|
|
69
72
|
private var contextMenuItemTexts: List<String> = emptyList()
|
|
70
73
|
var onContextMenuItemPressCallback: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
|
|
74
|
+
var onCopyRequestCallback: ((markdown: String, text: String, whole: Boolean) -> Unit)? = null
|
|
71
75
|
|
|
72
76
|
var markdownStyle: StyleConfig? = null
|
|
73
77
|
private set
|
|
@@ -101,6 +105,9 @@ class EnrichedMarkdownText
|
|
|
101
105
|
private var contextMenuHidden = false
|
|
102
106
|
private var activeActionMode: ActionMode? = null
|
|
103
107
|
|
|
108
|
+
private var linkLayoutEnabled = false
|
|
109
|
+
private var lastLinkLayout: List<LinkLayoutFragment>? = null
|
|
110
|
+
|
|
104
111
|
init {
|
|
105
112
|
setupAsMarkdownTextView()
|
|
106
113
|
customSelectionActionModeCallback =
|
|
@@ -113,6 +120,10 @@ class EnrichedMarkdownText
|
|
|
113
120
|
onCustomItemPress = { itemText, selectedText, start, end ->
|
|
114
121
|
onContextMenuItemPressCallback?.invoke(itemText, selectedText, start, end)
|
|
115
122
|
},
|
|
123
|
+
// A full selection already carries the original markdown.
|
|
124
|
+
onCopyRequest = { markdown, text, fullSelection ->
|
|
125
|
+
onCopyRequestCallback?.invoke(markdown, text, fullSelection && currentMarkdown.isNotEmpty())
|
|
126
|
+
},
|
|
116
127
|
)
|
|
117
128
|
}
|
|
118
129
|
|
|
@@ -305,6 +316,37 @@ class EnrichedMarkdownText
|
|
|
305
316
|
}
|
|
306
317
|
|
|
307
318
|
applySelectionColors(selectionColor, selectionHandleColor)
|
|
319
|
+
emitLinkLayoutIfNeeded()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
fun setEmitLinkLayout(enabled: Boolean) {
|
|
323
|
+
if (linkLayoutEnabled == enabled) return
|
|
324
|
+
linkLayoutEnabled = enabled
|
|
325
|
+
lastLinkLayout = null
|
|
326
|
+
emitLinkLayoutIfNeeded()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Reports link rects to JS (for `renderLinkDecoration`) after every render and layout;
|
|
331
|
+
* deduplicated so an unchanged layout — the common case while streaming — sends nothing.
|
|
332
|
+
*/
|
|
333
|
+
private fun emitLinkLayoutIfNeeded() {
|
|
334
|
+
if (!linkLayoutEnabled || layout == null || width <= 0) return
|
|
335
|
+
val (fragments, _) = LinkLayoutCollector.collect(this)
|
|
336
|
+
if (fragments == lastLinkLayout) return
|
|
337
|
+
lastLinkLayout = fragments
|
|
338
|
+
emitLinkLayout(this, fragments)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
override fun onLayout(
|
|
342
|
+
changed: Boolean,
|
|
343
|
+
left: Int,
|
|
344
|
+
top: Int,
|
|
345
|
+
right: Int,
|
|
346
|
+
bottom: Int,
|
|
347
|
+
) {
|
|
348
|
+
super.onLayout(changed, left, top, right, bottom)
|
|
349
|
+
emitLinkLayoutIfNeeded()
|
|
308
350
|
}
|
|
309
351
|
|
|
310
352
|
// Trailing bottom margin included in the shadow-node measurement — must be
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt
CHANGED
|
@@ -15,6 +15,7 @@ import com.facebook.react.viewmanagers.EnrichedMarkdownTextManagerInterface
|
|
|
15
15
|
import com.facebook.yoga.YogaMeasureMode
|
|
16
16
|
import com.swmansion.enriched.markdown.spoiler.SpoilerOverlay
|
|
17
17
|
import com.swmansion.enriched.markdown.utils.common.emitContextMenuItemPress
|
|
18
|
+
import com.swmansion.enriched.markdown.utils.common.emitCopyRequest
|
|
18
19
|
import com.swmansion.enriched.markdown.utils.common.emitLinkLongPress
|
|
19
20
|
import com.swmansion.enriched.markdown.utils.common.emitLinkPress
|
|
20
21
|
import com.swmansion.enriched.markdown.utils.common.emitTaskListItemPress
|
|
@@ -42,6 +43,9 @@ class EnrichedMarkdownTextManager :
|
|
|
42
43
|
view.onContextMenuItemPressCallback = { itemText, selectedText, selectionStart, selectionEnd ->
|
|
43
44
|
emitContextMenuItemPress(view, itemText, selectedText, selectionStart, selectionEnd)
|
|
44
45
|
}
|
|
46
|
+
view.onCopyRequestCallback = { markdown, text, whole ->
|
|
47
|
+
emitCopyRequest(view, "selection", markdown, text, whole)
|
|
48
|
+
}
|
|
45
49
|
|
|
46
50
|
view.setOnLinkPressCallback { url ->
|
|
47
51
|
emitLinkPress(view, url)
|
|
@@ -173,6 +177,14 @@ class EnrichedMarkdownTextManager :
|
|
|
173
177
|
view?.setContextMenuHidden(contextMenuHidden)
|
|
174
178
|
}
|
|
175
179
|
|
|
180
|
+
@ReactProp(name = "emitLinkLayout", defaultBoolean = false)
|
|
181
|
+
override fun setEmitLinkLayout(
|
|
182
|
+
view: EnrichedMarkdownText?,
|
|
183
|
+
emitLinkLayout: Boolean,
|
|
184
|
+
) {
|
|
185
|
+
view?.setEmitLinkLayout(emitLinkLayout)
|
|
186
|
+
}
|
|
187
|
+
|
|
176
188
|
@ReactProp(name = "enableLinkPreview", defaultBoolean = true)
|
|
177
189
|
override fun setEnableLinkPreview(
|
|
178
190
|
view: EnrichedMarkdownText?,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
|
|
7
|
+
class CopyRequestEvent(
|
|
8
|
+
surfaceId: Int,
|
|
9
|
+
viewId: Int,
|
|
10
|
+
private val source: String,
|
|
11
|
+
private val markdown: String,
|
|
12
|
+
private val text: String,
|
|
13
|
+
private val whole: Boolean,
|
|
14
|
+
) : Event<CopyRequestEvent>(surfaceId, viewId) {
|
|
15
|
+
override fun getEventName(): String = EVENT_NAME
|
|
16
|
+
|
|
17
|
+
override fun getEventData(): WritableMap =
|
|
18
|
+
Arguments.createMap().apply {
|
|
19
|
+
putString("source", source)
|
|
20
|
+
putString("markdown", markdown)
|
|
21
|
+
putString("text", text)
|
|
22
|
+
putBoolean("whole", whole)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
companion object {
|
|
26
|
+
const val EVENT_NAME: String = "onCopyRequest"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.events
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutFragment
|
|
7
|
+
|
|
8
|
+
class LinkLayoutEvent(
|
|
9
|
+
surfaceId: Int,
|
|
10
|
+
viewId: Int,
|
|
11
|
+
private val fragments: List<LinkLayoutFragment>,
|
|
12
|
+
) : Event<LinkLayoutEvent>(surfaceId, viewId) {
|
|
13
|
+
override fun getEventName(): String = EVENT_NAME
|
|
14
|
+
|
|
15
|
+
override fun getEventData(): WritableMap {
|
|
16
|
+
val array = Arguments.createArray()
|
|
17
|
+
fragments.forEach { fragment ->
|
|
18
|
+
array.pushMap(
|
|
19
|
+
Arguments.createMap().apply {
|
|
20
|
+
putInt("linkIndex", fragment.linkIndex)
|
|
21
|
+
putString("url", fragment.url)
|
|
22
|
+
putString("text", fragment.text)
|
|
23
|
+
putDouble("x", fragment.x.toDouble())
|
|
24
|
+
putDouble("y", fragment.y.toDouble())
|
|
25
|
+
putDouble("width", fragment.width.toDouble())
|
|
26
|
+
putDouble("height", fragment.height.toDouble())
|
|
27
|
+
},
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
return Arguments.createMap().apply { putArray("fragments", array) }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
companion object {
|
|
34
|
+
const val EVENT_NAME: String = "onLinkLayout"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -2,6 +2,9 @@ package com.swmansion.enriched.markdown.renderer
|
|
|
2
2
|
|
|
3
3
|
import android.text.SpannableStringBuilder
|
|
4
4
|
import com.swmansion.enriched.markdown.parser.MarkdownASTNode
|
|
5
|
+
import com.swmansion.enriched.markdown.spans.LinkChipBackgroundSpan
|
|
6
|
+
import com.swmansion.enriched.markdown.spans.LinkSpacerSpan
|
|
7
|
+
import com.swmansion.enriched.markdown.spans.LinkSpacers
|
|
5
8
|
import com.swmansion.enriched.markdown.spans.LinkSpan
|
|
6
9
|
import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
7
10
|
|
|
@@ -16,14 +19,41 @@ class LinkRenderer(
|
|
|
16
19
|
factory: RendererFactory,
|
|
17
20
|
) {
|
|
18
21
|
val url = node.getAttribute("url") ?: return
|
|
22
|
+
// Chip ("box") properties never fall back to the base link style.
|
|
23
|
+
val chip = factory.styleCache.linkChipForUrl(url)
|
|
24
|
+
val padding = chip.paddingHorizontal
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
val start = builder.length
|
|
27
|
+
// The leading spacer goes in before the children so ranges they register stay valid.
|
|
28
|
+
if (padding > 0f) appendSpacer(builder, padding)
|
|
29
|
+
val contentStart = builder.length
|
|
30
|
+
|
|
31
|
+
factory.renderChildren(node, builder, onLinkPress, onLinkLongPress)
|
|
32
|
+
|
|
33
|
+
if (builder.length == contentStart) {
|
|
34
|
+
if (contentStart > start) builder.delete(start, contentStart)
|
|
35
|
+
return
|
|
27
36
|
}
|
|
37
|
+
if (padding > 0f) appendSpacer(builder, padding)
|
|
38
|
+
val end = builder.length
|
|
39
|
+
|
|
40
|
+
val blockStyle = factory.blockStyleContext.requireBlockStyle()
|
|
41
|
+
builder.setSpan(
|
|
42
|
+
LinkSpan(url, onLinkPress, onLinkLongPress, factory.styleCache, blockStyle, factory.context),
|
|
43
|
+
start,
|
|
44
|
+
end,
|
|
45
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
46
|
+
)
|
|
47
|
+
// One instance per link: also identifies the link for link-layout reporting.
|
|
48
|
+
builder.setSpan(LinkChipBackgroundSpan(chip), start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private fun appendSpacer(
|
|
52
|
+
builder: SpannableStringBuilder,
|
|
53
|
+
width: Float,
|
|
54
|
+
) {
|
|
55
|
+
val position = builder.length
|
|
56
|
+
builder.append(LinkSpacers.CHAR)
|
|
57
|
+
builder.setSpan(LinkSpacerSpan(width), position, position + 1, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
28
58
|
}
|
|
29
59
|
}
|
|
@@ -4,6 +4,7 @@ import android.content.res.AssetManager
|
|
|
4
4
|
import android.graphics.Typeface
|
|
5
5
|
import com.facebook.react.common.ReactConstants
|
|
6
6
|
import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
|
|
7
|
+
import com.swmansion.enriched.markdown.styles.LinkChipStyle
|
|
7
8
|
import com.swmansion.enriched.markdown.styles.LinkVariantEntry
|
|
8
9
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
9
10
|
|
|
@@ -26,7 +27,7 @@ class SpanStyleCache(
|
|
|
26
27
|
val linkFontFamily: String = style.linkStyle.fontFamily
|
|
27
28
|
val linkColor: Int = style.linkStyle.color
|
|
28
29
|
val linkUnderline: Boolean = style.linkStyle.underline
|
|
29
|
-
val
|
|
30
|
+
val linkChip: LinkChipStyle = style.linkStyle.chip
|
|
30
31
|
val linkVariants: List<LinkVariantEntry> = style.linkVariants
|
|
31
32
|
|
|
32
33
|
private val compiledVariantPatterns: List<Pair<Regex, LinkVariantEntry>> =
|
|
@@ -100,6 +101,9 @@ class SpanStyleCache(
|
|
|
100
101
|
return compiledVariantPatterns.firstOrNull { (regex, _) -> regex.containsMatchIn(url) }?.second
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
/** Chip style for [url]: the matching variant's own, else the base link's (never merged). */
|
|
105
|
+
fun linkChipForUrl(url: String): LinkChipStyle = resolvedVariantForUrl(url)?.chip ?: linkChip
|
|
106
|
+
|
|
103
107
|
/**
|
|
104
108
|
* Cached typeface for font family + style (BOLD, ITALIC, BOLD_ITALIC).
|
|
105
109
|
*
|
|
@@ -5,9 +5,6 @@ import android.graphics.Paint
|
|
|
5
5
|
import android.graphics.Path
|
|
6
6
|
import android.graphics.RectF
|
|
7
7
|
import android.text.Spanned
|
|
8
|
-
import android.text.StaticLayout
|
|
9
|
-
import android.text.TextPaint
|
|
10
|
-
import android.text.style.LeadingMarginSpan
|
|
11
8
|
import android.text.style.LineBackgroundSpan
|
|
12
9
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
13
10
|
import kotlin.math.max
|
|
@@ -73,14 +70,6 @@ class CodeBackgroundSpan(
|
|
|
73
70
|
drawShapes(canvas, isFirst, isLast)
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
/**
|
|
77
|
-
* Returns the x position of [index] relative to the line's left edge, including any
|
|
78
|
-
* leading margin. The measuring StaticLayout is built from a subSequence that keeps
|
|
79
|
-
* all spans, so LeadingMarginSpans (lists, blockquotes) are already applied to
|
|
80
|
-
* getPrimaryHorizontal; adding the margin again on top would shift the background
|
|
81
|
-
* right by the indent. The margin is only added explicitly in the early-return case,
|
|
82
|
-
* where no layout is built.
|
|
83
|
-
*/
|
|
84
73
|
private fun getHorizontalOffset(
|
|
85
74
|
text: CharSequence,
|
|
86
75
|
lineStart: Int,
|
|
@@ -88,105 +77,30 @@ class CodeBackgroundSpan(
|
|
|
88
77
|
index: Int,
|
|
89
78
|
paint: Paint,
|
|
90
79
|
leadingMargin: Int,
|
|
91
|
-
): Float
|
|
92
|
-
if (index <= lineStart) return leadingMargin.toFloat()
|
|
93
|
-
val lineText = text.subSequence(lineStart, lineEnd)
|
|
94
|
-
val textPaint = paint as? TextPaint ?: TextPaint(paint)
|
|
95
|
-
val layout = StaticLayout.Builder.obtain(lineText, 0, lineText.length, textPaint, 10000).build()
|
|
96
|
-
return layout.getPrimaryHorizontal(index - lineStart)
|
|
97
|
-
}
|
|
80
|
+
): Float = InlineChipPainter.horizontalOffset(text, lineStart, lineEnd, index, paint, leadingMargin)
|
|
98
81
|
|
|
99
82
|
private fun drawShapes(
|
|
100
83
|
canvas: Canvas,
|
|
101
84
|
isFirst: Boolean,
|
|
102
85
|
isLast: Boolean,
|
|
103
86
|
) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
private fun drawOpenBorders(
|
|
118
|
-
canvas: Canvas,
|
|
119
|
-
isFirst: Boolean,
|
|
120
|
-
isLast: Boolean,
|
|
121
|
-
) {
|
|
122
|
-
val r = CORNER_RADIUS
|
|
123
|
-
path.reset()
|
|
124
|
-
|
|
125
|
-
if (isFirst) {
|
|
126
|
-
path.moveTo(rect.right, rect.top)
|
|
127
|
-
path.lineTo(rect.left + r, rect.top)
|
|
128
|
-
path.quadTo(rect.left, rect.top, rect.left, rect.top + r)
|
|
129
|
-
path.lineTo(rect.left, rect.bottom - r)
|
|
130
|
-
path.quadTo(rect.left, rect.bottom, rect.left + r, rect.bottom)
|
|
131
|
-
path.lineTo(rect.right, rect.bottom)
|
|
132
|
-
} else if (isLast) {
|
|
133
|
-
path.moveTo(rect.left, rect.top)
|
|
134
|
-
path.lineTo(rect.right - r, rect.top)
|
|
135
|
-
path.quadTo(rect.right, rect.top, rect.right, rect.top + r)
|
|
136
|
-
path.lineTo(rect.right, rect.bottom - r)
|
|
137
|
-
path.quadTo(rect.right, rect.bottom, rect.right - r, rect.bottom)
|
|
138
|
-
path.lineTo(rect.left, rect.bottom)
|
|
139
|
-
} else {
|
|
140
|
-
path.moveTo(rect.left, rect.top)
|
|
141
|
-
path.lineTo(rect.right, rect.top)
|
|
142
|
-
path.moveTo(rect.left, rect.bottom)
|
|
143
|
-
path.lineTo(rect.right, rect.bottom)
|
|
144
|
-
}
|
|
145
|
-
canvas.drawPath(path, sharedBorderPaint)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private fun createRadii(
|
|
149
|
-
isFirst: Boolean,
|
|
150
|
-
isLast: Boolean,
|
|
151
|
-
) = when {
|
|
152
|
-
isFirst && isLast -> {
|
|
153
|
-
floatArrayOf(
|
|
154
|
-
CORNER_RADIUS,
|
|
155
|
-
CORNER_RADIUS,
|
|
156
|
-
CORNER_RADIUS,
|
|
157
|
-
CORNER_RADIUS,
|
|
158
|
-
CORNER_RADIUS,
|
|
159
|
-
CORNER_RADIUS,
|
|
160
|
-
CORNER_RADIUS,
|
|
161
|
-
CORNER_RADIUS,
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
isFirst -> {
|
|
166
|
-
floatArrayOf(CORNER_RADIUS, CORNER_RADIUS, 0f, 0f, 0f, 0f, CORNER_RADIUS, CORNER_RADIUS)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
isLast -> {
|
|
170
|
-
floatArrayOf(0f, 0f, CORNER_RADIUS, CORNER_RADIUS, CORNER_RADIUS, CORNER_RADIUS, 0f, 0f)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
else -> {
|
|
174
|
-
floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
|
|
175
|
-
}
|
|
87
|
+
InlineChipPainter.draw(
|
|
88
|
+
canvas,
|
|
89
|
+
rect,
|
|
90
|
+
path,
|
|
91
|
+
CORNER_RADIUS,
|
|
92
|
+
sharedBackgroundPaint,
|
|
93
|
+
sharedBorderPaint,
|
|
94
|
+
isFirst,
|
|
95
|
+
isLast,
|
|
96
|
+
strokeInset = false,
|
|
97
|
+
)
|
|
176
98
|
}
|
|
177
99
|
|
|
178
100
|
private fun leadingMarginAt(
|
|
179
101
|
text: Spanned,
|
|
180
102
|
lineStart: Int,
|
|
181
|
-
): Int
|
|
182
|
-
if (lineStart >= text.length) return 0
|
|
183
|
-
val spans = text.getSpans(lineStart, lineStart + 1, LeadingMarginSpan::class.java)
|
|
184
|
-
var margin = 0
|
|
185
|
-
for (span in spans) {
|
|
186
|
-
margin += span.getLeadingMargin(false)
|
|
187
|
-
}
|
|
188
|
-
return margin
|
|
189
|
-
}
|
|
103
|
+
): Int = InlineChipPainter.leadingMarginAt(text, lineStart)
|
|
190
104
|
|
|
191
105
|
private fun adjustBottomForMargin(
|
|
192
106
|
text: Spanned,
|