react-native-enriched-markdown 0.1.0 → 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/LICENSE +20 -0
- package/README.md +551 -0
- package/ReactNativeEnrichedMarkdown.podspec +27 -0
- package/android/build.gradle +101 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +54 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +26 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +33 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +31 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +82 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1388 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/baseline-prof.txt +65 -0
- package/android/src/main/cpp/jni-adapter.cpp +220 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +270 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +173 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextPackage.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +385 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +279 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +31 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +166 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +84 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +104 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +70 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +68 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +105 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +59 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +103 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +80 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +109 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +86 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrikethroughRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +45 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/UnderlineRenderer.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +136 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +135 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +180 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +196 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +321 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +87 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeStyle.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/EmphasisStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrongStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +211 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +92 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +827 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/LinkLongPressMovementMethod.kt +121 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +375 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/SelectionActionMode.kt +139 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +183 -0
- package/android/src/main/jni/CMakeLists.txt +70 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.cpp +21 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.h +25 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +20 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +37 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +22 -0
- package/cpp/md4c/md4c.c +6492 -0
- package/cpp/md4c/md4c.h +402 -0
- package/cpp/parser/MD4CParser.cpp +327 -0
- package/cpp/parser/MD4CParser.hpp +27 -0
- package/cpp/parser/MarkdownASTNode.hpp +51 -0
- package/ios/EnrichedMarkdownText.h +18 -0
- package/ios/EnrichedMarkdownText.mm +1401 -0
- package/ios/attachments/EnrichedMarkdownImageAttachment.h +23 -0
- package/ios/attachments/EnrichedMarkdownImageAttachment.m +185 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +31 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +82 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.h +1388 -0
- package/ios/generated/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +20 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.h +20 -0
- package/ios/internals/EnrichedMarkdownTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.h +43 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.mm +85 -0
- package/ios/internals/EnrichedMarkdownTextState.h +24 -0
- package/ios/parser/MarkdownASTNode.h +35 -0
- package/ios/parser/MarkdownASTNode.m +32 -0
- package/ios/parser/MarkdownParser.h +17 -0
- package/ios/parser/MarkdownParser.mm +42 -0
- package/ios/parser/MarkdownParserBridge.mm +120 -0
- package/ios/renderer/AttributedRenderer.h +11 -0
- package/ios/renderer/AttributedRenderer.m +152 -0
- package/ios/renderer/BlockquoteRenderer.h +7 -0
- package/ios/renderer/BlockquoteRenderer.m +160 -0
- package/ios/renderer/CodeBlockRenderer.h +10 -0
- package/ios/renderer/CodeBlockRenderer.m +90 -0
- package/ios/renderer/CodeRenderer.h +10 -0
- package/ios/renderer/CodeRenderer.m +60 -0
- package/ios/renderer/EmphasisRenderer.h +6 -0
- package/ios/renderer/EmphasisRenderer.m +96 -0
- package/ios/renderer/HeadingRenderer.h +7 -0
- package/ios/renderer/HeadingRenderer.m +105 -0
- package/ios/renderer/ImageRenderer.h +12 -0
- package/ios/renderer/ImageRenderer.m +83 -0
- package/ios/renderer/LinkRenderer.h +7 -0
- package/ios/renderer/LinkRenderer.m +69 -0
- package/ios/renderer/ListItemRenderer.h +16 -0
- package/ios/renderer/ListItemRenderer.m +103 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +70 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +7 -0
- package/ios/renderer/ParagraphRenderer.m +80 -0
- package/ios/renderer/RenderContext.h +105 -0
- package/ios/renderer/RenderContext.m +312 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +116 -0
- package/ios/renderer/StrikethroughRenderer.h +6 -0
- package/ios/renderer/StrikethroughRenderer.m +40 -0
- package/ios/renderer/StrongRenderer.h +6 -0
- package/ios/renderer/StrongRenderer.m +83 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +16 -0
- package/ios/renderer/ThematicBreakRenderer.h +5 -0
- package/ios/renderer/ThematicBreakRenderer.m +53 -0
- package/ios/renderer/UnderlineRenderer.h +6 -0
- package/ios/renderer/UnderlineRenderer.m +39 -0
- package/ios/styles/StyleConfig.h +274 -0
- package/ios/styles/StyleConfig.mm +1806 -0
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/BlockquoteBorder.h +20 -0
- package/ios/utils/BlockquoteBorder.m +92 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +191 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +82 -0
- package/ios/utils/EditMenuUtils.h +22 -0
- package/ios/utils/EditMenuUtils.m +118 -0
- package/ios/utils/FontUtils.h +25 -0
- package/ios/utils/FontUtils.m +27 -0
- package/ios/utils/HTMLGenerator.h +20 -0
- package/ios/utils/HTMLGenerator.m +793 -0
- package/ios/utils/LastElementUtils.h +53 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +127 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.h +45 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +323 -0
- package/ios/utils/MarkdownExtractor.h +17 -0
- package/ios/utils/MarkdownExtractor.m +308 -0
- package/ios/utils/ParagraphStyleUtils.h +21 -0
- package/ios/utils/ParagraphStyleUtils.m +111 -0
- package/ios/utils/PasteboardUtils.h +36 -0
- package/ios/utils/PasteboardUtils.m +134 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +297 -0
- package/ios/utils/RuntimeKeys.h +38 -0
- package/ios/utils/RuntimeKeys.m +11 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +113 -0
- package/lib/module/EnrichedMarkdownText.js +65 -0
- package/lib/module/EnrichedMarkdownText.js.map +1 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +210 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/normalizeMarkdownStyle.js +384 -0
- package/lib/module/normalizeMarkdownStyle.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts +183 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +185 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts +6 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -0
- package/package.json +186 -1
- package/react-native.config.js +13 -0
- package/src/EnrichedMarkdownText.tsx +280 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +210 -0
- package/src/index.tsx +10 -0
- package/src/normalizeMarkdownStyle.ts +423 -0
package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.cpp
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateShadowNodeCpp.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "ShadowNodes.h"
|
|
12
|
+
|
|
13
|
+
namespace facebook::react {
|
|
14
|
+
|
|
15
|
+
extern const char EnrichedMarkdownTextComponentName[] = "EnrichedMarkdownText";
|
|
16
|
+
|
|
17
|
+
} // namespace facebook::react
|
package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.h
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateShadowNodeH.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#pragma once
|
|
12
|
+
|
|
13
|
+
#include "EventEmitters.h"
|
|
14
|
+
#include "Props.h"
|
|
15
|
+
#include "States.h"
|
|
16
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
17
|
+
#include <jsi/jsi.h>
|
|
18
|
+
|
|
19
|
+
namespace facebook::react {
|
|
20
|
+
|
|
21
|
+
JSI_EXPORT extern const char EnrichedMarkdownTextComponentName[];
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* `ShadowNode` for <EnrichedMarkdownText> component.
|
|
25
|
+
*/
|
|
26
|
+
using EnrichedMarkdownTextShadowNode = ConcreteViewShadowNode<
|
|
27
|
+
EnrichedMarkdownTextComponentName,
|
|
28
|
+
EnrichedMarkdownTextProps,
|
|
29
|
+
EnrichedMarkdownTextEventEmitter,
|
|
30
|
+
EnrichedMarkdownTextState>;
|
|
31
|
+
|
|
32
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateStateCpp.js
|
|
9
|
+
*/
|
|
10
|
+
#include "States.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
3
|
+
*
|
|
4
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
5
|
+
* once the code is regenerated.
|
|
6
|
+
*
|
|
7
|
+
* @generated by codegen project: GenerateStateH.js
|
|
8
|
+
*/
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include <react/renderer/core/StateData.h>
|
|
12
|
+
#ifdef RN_SERIALIZABLE_STATE
|
|
13
|
+
#include <folly/dynamic.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
namespace facebook::react {
|
|
17
|
+
|
|
18
|
+
using EnrichedMarkdownTextState = StateData;
|
|
19
|
+
|
|
20
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Baseline Profile for react-native-enriched-markdown
|
|
2
|
+
# These classes will be AOT compiled during app install
|
|
3
|
+
# to improve cold start performance.
|
|
4
|
+
|
|
5
|
+
# Core View and Rendering
|
|
6
|
+
Lcom/swmansion/enriched/markdown/EnrichedMarkdownText;
|
|
7
|
+
Lcom/swmansion/enriched/markdown/EnrichedMarkdownTextManager;
|
|
8
|
+
Lcom/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager;
|
|
9
|
+
|
|
10
|
+
# Renderer Classes
|
|
11
|
+
Lcom/swmansion/enriched/markdown/renderer/Renderer;
|
|
12
|
+
Lcom/swmansion/enriched/markdown/renderer/RendererFactory;
|
|
13
|
+
Lcom/swmansion/enriched/markdown/renderer/RendererConfig;
|
|
14
|
+
Lcom/swmansion/enriched/markdown/renderer/BlockStyleContext;
|
|
15
|
+
Lcom/swmansion/enriched/markdown/renderer/BlockStyle;
|
|
16
|
+
Lcom/swmansion/enriched/markdown/renderer/SpanStyleCache;
|
|
17
|
+
Lcom/swmansion/enriched/markdown/renderer/NodeRenderer;
|
|
18
|
+
Lcom/swmansion/enriched/markdown/renderer/TextRenderer;
|
|
19
|
+
Lcom/swmansion/enriched/markdown/renderer/ParagraphRenderer;
|
|
20
|
+
Lcom/swmansion/enriched/markdown/renderer/HeadingRenderer;
|
|
21
|
+
Lcom/swmansion/enriched/markdown/renderer/StrongRenderer;
|
|
22
|
+
Lcom/swmansion/enriched/markdown/renderer/EmphasisRenderer;
|
|
23
|
+
Lcom/swmansion/enriched/markdown/renderer/LinkRenderer;
|
|
24
|
+
Lcom/swmansion/enriched/markdown/renderer/ListRenderer;
|
|
25
|
+
Lcom/swmansion/enriched/markdown/renderer/ListItemRenderer;
|
|
26
|
+
Lcom/swmansion/enriched/markdown/renderer/BlockquoteRenderer;
|
|
27
|
+
Lcom/swmansion/enriched/markdown/renderer/CodeBlockRenderer;
|
|
28
|
+
Lcom/swmansion/enriched/markdown/renderer/CodeRenderer;
|
|
29
|
+
Lcom/swmansion/enriched/markdown/renderer/ImageRenderer;
|
|
30
|
+
Lcom/swmansion/enriched/markdown/renderer/LineBreakRenderer;
|
|
31
|
+
Lcom/swmansion/enriched/markdown/renderer/DocumentRenderer;
|
|
32
|
+
|
|
33
|
+
# Spans
|
|
34
|
+
Lcom/swmansion/enriched/markdown/spans/StrongSpan;
|
|
35
|
+
Lcom/swmansion/enriched/markdown/spans/EmphasisSpan;
|
|
36
|
+
Lcom/swmansion/enriched/markdown/spans/CodeSpan;
|
|
37
|
+
Lcom/swmansion/enriched/markdown/spans/TextSpan;
|
|
38
|
+
Lcom/swmansion/enriched/markdown/spans/HeadingSpan;
|
|
39
|
+
Lcom/swmansion/enriched/markdown/spans/LinkSpan;
|
|
40
|
+
Lcom/swmansion/enriched/markdown/spans/LineHeightSpan;
|
|
41
|
+
Lcom/swmansion/enriched/markdown/spans/MarginBottomSpan;
|
|
42
|
+
Lcom/swmansion/enriched/markdown/spans/BaseListSpan;
|
|
43
|
+
Lcom/swmansion/enriched/markdown/spans/UnorderedListSpan;
|
|
44
|
+
Lcom/swmansion/enriched/markdown/spans/OrderedListSpan;
|
|
45
|
+
Lcom/swmansion/enriched/markdown/spans/BlockquoteSpan;
|
|
46
|
+
Lcom/swmansion/enriched/markdown/spans/CodeBlockSpan;
|
|
47
|
+
Lcom/swmansion/enriched/markdown/spans/CodeBackgroundSpan;
|
|
48
|
+
Lcom/swmansion/enriched/markdown/spans/ImageSpan;
|
|
49
|
+
|
|
50
|
+
# Style Configuration
|
|
51
|
+
Lcom/swmansion/enriched/markdown/styles/StyleConfig;
|
|
52
|
+
Lcom/swmansion/enriched/markdown/styles/ParagraphStyle;
|
|
53
|
+
Lcom/swmansion/enriched/markdown/styles/HeadingStyle;
|
|
54
|
+
Lcom/swmansion/enriched/markdown/styles/ListStyle;
|
|
55
|
+
Lcom/swmansion/enriched/markdown/styles/BlockquoteStyle;
|
|
56
|
+
Lcom/swmansion/enriched/markdown/styles/CodeBlockStyle;
|
|
57
|
+
Lcom/swmansion/enriched/markdown/styles/CodeStyle;
|
|
58
|
+
|
|
59
|
+
# Parser
|
|
60
|
+
Lcom/swmansion/enriched/markdown/parser/Parser;
|
|
61
|
+
Lcom/swmansion/enriched/markdown/parser/MarkdownASTNode;
|
|
62
|
+
Lcom/swmansion/enriched/markdown/parser/MarkdownASTNode$NodeType;
|
|
63
|
+
|
|
64
|
+
# Utilities
|
|
65
|
+
Lcom/swmansion/enriched/markdown/utils/UtilsKt;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#include "MD4CParser.hpp"
|
|
2
|
+
#include <android/log.h>
|
|
3
|
+
#include <jni.h>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
using namespace Markdown;
|
|
7
|
+
|
|
8
|
+
#define ENRICHEDMARKDOWN_LOG_TAG "EnrichedMarkdownJNI"
|
|
9
|
+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, ENRICHEDMARKDOWN_LOG_TAG, __VA_ARGS__)
|
|
10
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, ENRICHEDMARKDOWN_LOG_TAG, __VA_ARGS__)
|
|
11
|
+
|
|
12
|
+
// Helper function to convert C++ NodeType to Kotlin enum ordinal
|
|
13
|
+
static jint nodeTypeToJavaOrdinal(NodeType type) {
|
|
14
|
+
switch (type) {
|
|
15
|
+
case NodeType::Document:
|
|
16
|
+
return 0;
|
|
17
|
+
case NodeType::Paragraph:
|
|
18
|
+
return 1;
|
|
19
|
+
case NodeType::Text:
|
|
20
|
+
return 2;
|
|
21
|
+
case NodeType::Link:
|
|
22
|
+
return 3;
|
|
23
|
+
case NodeType::Heading:
|
|
24
|
+
return 4;
|
|
25
|
+
case NodeType::LineBreak:
|
|
26
|
+
return 5;
|
|
27
|
+
case NodeType::Strong:
|
|
28
|
+
return 6;
|
|
29
|
+
case NodeType::Emphasis:
|
|
30
|
+
return 7;
|
|
31
|
+
case NodeType::Strikethrough:
|
|
32
|
+
return 8;
|
|
33
|
+
case NodeType::Underline:
|
|
34
|
+
return 9;
|
|
35
|
+
case NodeType::Code:
|
|
36
|
+
return 10;
|
|
37
|
+
case NodeType::Image:
|
|
38
|
+
return 11;
|
|
39
|
+
case NodeType::Blockquote:
|
|
40
|
+
return 12;
|
|
41
|
+
case NodeType::UnorderedList:
|
|
42
|
+
return 13;
|
|
43
|
+
case NodeType::OrderedList:
|
|
44
|
+
return 14;
|
|
45
|
+
case NodeType::ListItem:
|
|
46
|
+
return 15;
|
|
47
|
+
case NodeType::CodeBlock:
|
|
48
|
+
return 16;
|
|
49
|
+
case NodeType::ThematicBreak:
|
|
50
|
+
return 17;
|
|
51
|
+
default:
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Helper function to create a Kotlin MarkdownASTNode object from C++ AST node
|
|
57
|
+
static jobject createJavaNode(JNIEnv *env, std::shared_ptr<MarkdownASTNode> node) {
|
|
58
|
+
if (!node) {
|
|
59
|
+
return nullptr;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Find the MarkdownASTNode class
|
|
63
|
+
jclass nodeClass = env->FindClass("com/swmansion/enriched/markdown/parser/MarkdownASTNode");
|
|
64
|
+
if (!nodeClass) {
|
|
65
|
+
LOGE("Failed to find MarkdownASTNode class");
|
|
66
|
+
return nullptr;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Find the NodeType enum class
|
|
70
|
+
jclass nodeTypeClass = env->FindClass("com/swmansion/enriched/markdown/parser/MarkdownASTNode$NodeType");
|
|
71
|
+
if (!nodeTypeClass) {
|
|
72
|
+
LOGE("Failed to find NodeType enum class");
|
|
73
|
+
return nullptr;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get the enum values array
|
|
77
|
+
jmethodID valuesMethod = env->GetStaticMethodID(
|
|
78
|
+
nodeTypeClass, "values", "()[Lcom/swmansion/enriched/markdown/parser/MarkdownASTNode$NodeType;");
|
|
79
|
+
if (!valuesMethod) {
|
|
80
|
+
LOGE("Failed to find NodeType.values() method");
|
|
81
|
+
return nullptr;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
jobjectArray enumValues = (jobjectArray)env->CallStaticObjectMethod(nodeTypeClass, valuesMethod);
|
|
85
|
+
if (!enumValues) {
|
|
86
|
+
LOGE("Failed to get NodeType enum values");
|
|
87
|
+
return nullptr;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Get the enum value for this node type
|
|
91
|
+
jint ordinal = nodeTypeToJavaOrdinal(node->type);
|
|
92
|
+
jobject nodeTypeEnum = env->GetObjectArrayElement(enumValues, ordinal);
|
|
93
|
+
if (!nodeTypeEnum) {
|
|
94
|
+
LOGE("Failed to get NodeType enum value at index %d", ordinal);
|
|
95
|
+
return nullptr;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Create content string
|
|
99
|
+
jstring contentStr = env->NewStringUTF(node->content.c_str());
|
|
100
|
+
if (!contentStr && !node->content.empty()) {
|
|
101
|
+
LOGE("Failed to create content string");
|
|
102
|
+
return nullptr;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Create attributes HashMap
|
|
106
|
+
jclass mapClass = env->FindClass("java/util/HashMap");
|
|
107
|
+
jmethodID mapInit = env->GetMethodID(mapClass, "<init>", "(I)V");
|
|
108
|
+
jmethodID mapPut = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
109
|
+
|
|
110
|
+
jobject attributesMap = env->NewObject(mapClass, mapInit, static_cast<jint>(node->attributes.size()));
|
|
111
|
+
|
|
112
|
+
for (const auto &pair : node->attributes) {
|
|
113
|
+
jstring key = env->NewStringUTF(pair.first.c_str());
|
|
114
|
+
jstring value = env->NewStringUTF(pair.second.c_str());
|
|
115
|
+
env->CallObjectMethod(attributesMap, mapPut, key, value);
|
|
116
|
+
env->DeleteLocalRef(key);
|
|
117
|
+
env->DeleteLocalRef(value);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create children ArrayList
|
|
121
|
+
jclass listClass = env->FindClass("java/util/ArrayList");
|
|
122
|
+
jmethodID listInit = env->GetMethodID(listClass, "<init>", "(I)V");
|
|
123
|
+
jmethodID listAdd = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z");
|
|
124
|
+
|
|
125
|
+
jobject childrenList = env->NewObject(listClass, listInit, static_cast<jint>(node->children.size()));
|
|
126
|
+
|
|
127
|
+
for (const auto &child : node->children) {
|
|
128
|
+
jobject childObj = createJavaNode(env, child);
|
|
129
|
+
if (childObj) {
|
|
130
|
+
env->CallBooleanMethod(childrenList, listAdd, childObj);
|
|
131
|
+
env->DeleteLocalRef(childObj);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Find the MarkdownASTNode constructor
|
|
136
|
+
// Constructor signature: (Lcom/swmansion/enriched/markdown/parser/MarkdownASTNode$NodeType;Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)V
|
|
137
|
+
jmethodID constructor = env->GetMethodID(nodeClass, "<init>",
|
|
138
|
+
"(Lcom/swmansion/enriched/markdown/parser/MarkdownASTNode$NodeType;Ljava/"
|
|
139
|
+
"lang/String;Ljava/util/Map;Ljava/util/List;)V");
|
|
140
|
+
if (!constructor) {
|
|
141
|
+
LOGE("Failed to find MarkdownASTNode constructor");
|
|
142
|
+
return nullptr;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Create the Kotlin MarkdownASTNode object
|
|
146
|
+
jobject javaNode = env->NewObject(nodeClass, constructor, nodeTypeEnum, contentStr, attributesMap, childrenList);
|
|
147
|
+
|
|
148
|
+
// Clean up local references
|
|
149
|
+
env->DeleteLocalRef(nodeTypeClass);
|
|
150
|
+
env->DeleteLocalRef(enumValues);
|
|
151
|
+
env->DeleteLocalRef(nodeTypeEnum);
|
|
152
|
+
if (contentStr)
|
|
153
|
+
env->DeleteLocalRef(contentStr);
|
|
154
|
+
env->DeleteLocalRef(attributesMap);
|
|
155
|
+
env->DeleteLocalRef(childrenList);
|
|
156
|
+
|
|
157
|
+
return javaNode;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
extern "C" {
|
|
161
|
+
|
|
162
|
+
JNIEXPORT jobject JNICALL Java_com_swmansion_enriched_markdown_parser_Parser_nativeParseMarkdown(JNIEnv *env,
|
|
163
|
+
jobject /* this */,
|
|
164
|
+
jstring markdown,
|
|
165
|
+
jobject flags) {
|
|
166
|
+
if (!markdown) {
|
|
167
|
+
LOGE("Markdown string is null");
|
|
168
|
+
return nullptr;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const char *markdownStr = env->GetStringUTFChars(markdown, nullptr);
|
|
172
|
+
if (!markdownStr) {
|
|
173
|
+
LOGE("Failed to get UTF-8 chars from markdown string");
|
|
174
|
+
return nullptr;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
// Extract flags from Kotlin Md4cFlags data class
|
|
179
|
+
Md4cFlags md4cFlags;
|
|
180
|
+
if (flags) {
|
|
181
|
+
jclass flagsClass = env->GetObjectClass(flags);
|
|
182
|
+
if (flagsClass) {
|
|
183
|
+
jfieldID underlineField = env->GetFieldID(flagsClass, "underline", "Z");
|
|
184
|
+
if (underlineField) {
|
|
185
|
+
md4cFlags.underline = env->GetBooleanField(flags, underlineField) == JNI_TRUE;
|
|
186
|
+
}
|
|
187
|
+
env->DeleteLocalRef(flagsClass);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
MD4CParser parser;
|
|
192
|
+
auto ast = parser.parse(std::string(markdownStr), md4cFlags);
|
|
193
|
+
|
|
194
|
+
env->ReleaseStringUTFChars(markdown, markdownStr);
|
|
195
|
+
|
|
196
|
+
if (!ast) {
|
|
197
|
+
LOGE("Parser returned null AST");
|
|
198
|
+
return nullptr;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Convert C++ AST to Kotlin MarkdownASTNode object
|
|
202
|
+
jobject javaNode = createJavaNode(env, ast);
|
|
203
|
+
|
|
204
|
+
if (!javaNode) {
|
|
205
|
+
LOGE("Failed to create Java node from AST");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return javaNode;
|
|
209
|
+
} catch (const std::exception &e) {
|
|
210
|
+
env->ReleaseStringUTFChars(markdown, markdownStr);
|
|
211
|
+
LOGE("Exception during parsing: %s", e.what());
|
|
212
|
+
return nullptr;
|
|
213
|
+
} catch (...) {
|
|
214
|
+
env->ReleaseStringUTFChars(markdown, markdownStr);
|
|
215
|
+
LOGE("Unknown exception during parsing");
|
|
216
|
+
return nullptr;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
} // extern "C"
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.os.Handler
|
|
8
|
+
import android.os.Looper
|
|
9
|
+
import android.text.Layout
|
|
10
|
+
import android.util.AttributeSet
|
|
11
|
+
import android.util.Log
|
|
12
|
+
import androidx.appcompat.widget.AppCompatTextView
|
|
13
|
+
import androidx.core.view.ViewCompat
|
|
14
|
+
import com.facebook.react.bridge.ReadableMap
|
|
15
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
|
+
import com.swmansion.enriched.markdown.accessibility.MarkdownAccessibilityHelper
|
|
17
|
+
import com.swmansion.enriched.markdown.events.LinkLongPressEvent
|
|
18
|
+
import com.swmansion.enriched.markdown.events.LinkPressEvent
|
|
19
|
+
import com.swmansion.enriched.markdown.parser.Md4cFlags
|
|
20
|
+
import com.swmansion.enriched.markdown.parser.Parser
|
|
21
|
+
import com.swmansion.enriched.markdown.renderer.Renderer
|
|
22
|
+
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
23
|
+
import com.swmansion.enriched.markdown.utils.LinkLongPressMovementMethod
|
|
24
|
+
import com.swmansion.enriched.markdown.utils.createSelectionActionModeCallback
|
|
25
|
+
import java.util.concurrent.Executors
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* EnrichedMarkdownText that handles Markdown parsing and rendering on a background thread.
|
|
29
|
+
* View starts invisible and becomes visible after render completes to avoid layout shift.
|
|
30
|
+
*/
|
|
31
|
+
class EnrichedMarkdownText
|
|
32
|
+
@JvmOverloads
|
|
33
|
+
constructor(
|
|
34
|
+
context: Context,
|
|
35
|
+
attrs: AttributeSet? = null,
|
|
36
|
+
defStyleAttr: Int = 0,
|
|
37
|
+
) : AppCompatTextView(context, attrs, defStyleAttr) {
|
|
38
|
+
private val parser = Parser.shared
|
|
39
|
+
private val renderer = Renderer()
|
|
40
|
+
private var onLinkPressCallback: ((String) -> Unit)? = null
|
|
41
|
+
private var onLinkLongPressCallback: ((String) -> Unit)? = null
|
|
42
|
+
|
|
43
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
44
|
+
private val executor = Executors.newSingleThreadExecutor()
|
|
45
|
+
private var currentRenderId = 0L
|
|
46
|
+
|
|
47
|
+
val layoutManager = EnrichedMarkdownTextLayoutManager(this)
|
|
48
|
+
|
|
49
|
+
// Accessibility helper for TalkBack support
|
|
50
|
+
private val accessibilityHelper = MarkdownAccessibilityHelper(this)
|
|
51
|
+
|
|
52
|
+
var markdownStyle: StyleConfig? = null
|
|
53
|
+
private set
|
|
54
|
+
|
|
55
|
+
var currentMarkdown: String = ""
|
|
56
|
+
private set
|
|
57
|
+
|
|
58
|
+
var md4cFlags: Md4cFlags = Md4cFlags.DEFAULT
|
|
59
|
+
private set
|
|
60
|
+
|
|
61
|
+
private var lastKnownFontScale: Float = context.resources.configuration.fontScale
|
|
62
|
+
private var markdownStyleMap: ReadableMap? = null
|
|
63
|
+
|
|
64
|
+
private var allowFontScaling: Boolean = true
|
|
65
|
+
private var maxFontSizeMultiplier: Float = 0f
|
|
66
|
+
private var allowTrailingMargin: Boolean = false
|
|
67
|
+
|
|
68
|
+
init {
|
|
69
|
+
setBackgroundColor(Color.TRANSPARENT)
|
|
70
|
+
includeFontPadding = false // Must match setIncludePad(false) in MeasurementStore
|
|
71
|
+
movementMethod = LinkLongPressMovementMethod.createInstance()
|
|
72
|
+
setTextIsSelectable(true)
|
|
73
|
+
customSelectionActionModeCallback = createSelectionActionModeCallback(this)
|
|
74
|
+
isVerticalScrollBarEnabled = false
|
|
75
|
+
isHorizontalScrollBarEnabled = false
|
|
76
|
+
|
|
77
|
+
// Set up accessibility for TalkBack
|
|
78
|
+
// This enables virtual view hierarchy for semantic navigation (headings, links, etc.)
|
|
79
|
+
ViewCompat.setAccessibilityDelegate(this, accessibilityHelper)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fun setMarkdownContent(markdown: String) {
|
|
83
|
+
if (currentMarkdown == markdown) return
|
|
84
|
+
currentMarkdown = markdown
|
|
85
|
+
scheduleRender()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fun setMarkdownStyle(style: ReadableMap?) {
|
|
89
|
+
markdownStyleMap = style
|
|
90
|
+
// Register font scaling settings when style is set (view should have ID by now)
|
|
91
|
+
updateMeasurementStoreFontScaling()
|
|
92
|
+
val newStyle = style?.let { StyleConfig(it, context, allowFontScaling, maxFontSizeMultiplier) }
|
|
93
|
+
if (markdownStyle == newStyle) return
|
|
94
|
+
markdownStyle = newStyle
|
|
95
|
+
updateJustificationMode(newStyle)
|
|
96
|
+
scheduleRender()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
100
|
+
super.onConfigurationChanged(newConfig)
|
|
101
|
+
|
|
102
|
+
if (!allowFontScaling) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
val newFontScale = newConfig.fontScale
|
|
107
|
+
if (newFontScale != lastKnownFontScale) {
|
|
108
|
+
lastKnownFontScale = newFontScale
|
|
109
|
+
recreateStyleConfig()
|
|
110
|
+
scheduleRenderIfNeeded()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fun setMd4cFlags(flags: Md4cFlags) {
|
|
115
|
+
if (md4cFlags == flags) return
|
|
116
|
+
md4cFlags = flags
|
|
117
|
+
scheduleRenderIfNeeded()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fun setAllowFontScaling(allow: Boolean) {
|
|
121
|
+
if (allowFontScaling == allow) return
|
|
122
|
+
allowFontScaling = allow
|
|
123
|
+
updateMeasurementStoreFontScaling()
|
|
124
|
+
recreateStyleConfig()
|
|
125
|
+
scheduleRenderIfNeeded()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fun setMaxFontSizeMultiplier(multiplier: Float) {
|
|
129
|
+
if (maxFontSizeMultiplier == multiplier) return
|
|
130
|
+
maxFontSizeMultiplier = multiplier
|
|
131
|
+
updateMeasurementStoreFontScaling()
|
|
132
|
+
recreateStyleConfig()
|
|
133
|
+
scheduleRenderIfNeeded()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fun setAllowTrailingMargin(allow: Boolean) {
|
|
137
|
+
if (allowTrailingMargin == allow) return
|
|
138
|
+
allowTrailingMargin = allow
|
|
139
|
+
scheduleRenderIfNeeded()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private fun updateMeasurementStoreFontScaling() {
|
|
143
|
+
MeasurementStore.updateFontScalingSettings(id, allowFontScaling, maxFontSizeMultiplier)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fun scheduleRenderIfNeeded() {
|
|
147
|
+
if (currentMarkdown.isNotEmpty()) {
|
|
148
|
+
scheduleRender()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private fun recreateStyleConfig() {
|
|
153
|
+
markdownStyleMap?.let { styleMap ->
|
|
154
|
+
markdownStyle = StyleConfig(styleMap, context, allowFontScaling, maxFontSizeMultiplier)
|
|
155
|
+
updateJustificationMode(markdownStyle)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private fun updateJustificationMode(style: StyleConfig?) {
|
|
160
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
161
|
+
justificationMode =
|
|
162
|
+
if (style?.needsJustify == true) {
|
|
163
|
+
Layout.JUSTIFICATION_MODE_INTER_WORD
|
|
164
|
+
} else {
|
|
165
|
+
Layout.JUSTIFICATION_MODE_NONE
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private fun scheduleRender() {
|
|
171
|
+
val style = markdownStyle ?: return
|
|
172
|
+
val markdown = currentMarkdown
|
|
173
|
+
if (markdown.isEmpty()) return
|
|
174
|
+
|
|
175
|
+
val renderId = ++currentRenderId
|
|
176
|
+
|
|
177
|
+
executor.execute {
|
|
178
|
+
try {
|
|
179
|
+
// 1. Parse Markdown → AST (C++ md4c parser)
|
|
180
|
+
val parseStart = System.currentTimeMillis()
|
|
181
|
+
val ast =
|
|
182
|
+
parser.parseMarkdown(markdown, md4cFlags) ?: run {
|
|
183
|
+
mainHandler.post { if (renderId == currentRenderId) text = "" }
|
|
184
|
+
return@execute
|
|
185
|
+
}
|
|
186
|
+
val parseTime = System.currentTimeMillis() - parseStart
|
|
187
|
+
|
|
188
|
+
// 2. Render AST → Spannable
|
|
189
|
+
val renderStart = System.currentTimeMillis()
|
|
190
|
+
renderer.configure(style, context)
|
|
191
|
+
val styledText = renderer.renderDocument(ast, onLinkPressCallback, onLinkLongPressCallback)
|
|
192
|
+
val renderTime = System.currentTimeMillis() - renderStart
|
|
193
|
+
|
|
194
|
+
Log.i(TAG, "┌──────────────────────────────────────────────")
|
|
195
|
+
Log.i(TAG, "│ 📝 Input: ${markdown.length} chars of Markdown")
|
|
196
|
+
Log.i(TAG, "│ ⚡ md4c (C++ native): ${parseTime}ms → ${ast.children.size} AST nodes")
|
|
197
|
+
Log.i(TAG, "│ 🎨 Spannable render: ${renderTime}ms → ${styledText.length} styled chars")
|
|
198
|
+
Log.i(TAG, "└──────────────────────────────────────────────")
|
|
199
|
+
|
|
200
|
+
// 3. Apply to view on main thread
|
|
201
|
+
mainHandler.post {
|
|
202
|
+
if (renderId == currentRenderId) {
|
|
203
|
+
applyRenderedText(styledText)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (e: Exception) {
|
|
207
|
+
Log.e(TAG, "❌ Render failed: ${e.message}", e)
|
|
208
|
+
mainHandler.post { if (renderId == currentRenderId) text = "" }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private fun applyRenderedText(styledText: CharSequence) {
|
|
214
|
+
text = styledText
|
|
215
|
+
|
|
216
|
+
// setText on a selectable TextView can reset movementMethod, so re-apply if needed
|
|
217
|
+
if (movementMethod !is LinkLongPressMovementMethod) {
|
|
218
|
+
movementMethod = LinkLongPressMovementMethod.createInstance()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Register ImageSpans from the collector
|
|
222
|
+
renderer.getCollectedImageSpans().forEach { span ->
|
|
223
|
+
span.registerTextView(this)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
layoutManager.invalidateLayout()
|
|
227
|
+
|
|
228
|
+
// Update accessibility items for TalkBack navigation
|
|
229
|
+
accessibilityHelper.invalidateAccessibilityItems()
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
fun setIsSelectable(selectable: Boolean) {
|
|
233
|
+
if (isTextSelectable == selectable) return
|
|
234
|
+
setTextIsSelectable(selectable)
|
|
235
|
+
movementMethod = LinkLongPressMovementMethod.createInstance()
|
|
236
|
+
if (!selectable && !isClickable) isClickable = true
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
fun emitOnLinkPress(url: String) {
|
|
240
|
+
val reactContext = context as? com.facebook.react.bridge.ReactContext ?: return
|
|
241
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
242
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
243
|
+
|
|
244
|
+
dispatcher?.dispatchEvent(
|
|
245
|
+
LinkPressEvent(surfaceId, id, url),
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fun emitOnLinkLongPress(url: String) {
|
|
250
|
+
val reactContext = context as? com.facebook.react.bridge.ReactContext ?: return
|
|
251
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
252
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
253
|
+
|
|
254
|
+
dispatcher?.dispatchEvent(
|
|
255
|
+
LinkLongPressEvent(surfaceId, id, url),
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
fun setOnLinkPressCallback(callback: (String) -> Unit) {
|
|
260
|
+
onLinkPressCallback = callback
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
fun setOnLinkLongPressCallback(callback: (String) -> Unit) {
|
|
264
|
+
onLinkLongPressCallback = callback
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
companion object {
|
|
268
|
+
private const val TAG = "EnrichedMarkdownMeasure"
|
|
269
|
+
}
|
|
270
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown
|
|
2
|
+
|
|
3
|
+
class EnrichedMarkdownTextLayoutManager(
|
|
4
|
+
private val view: EnrichedMarkdownText,
|
|
5
|
+
) {
|
|
6
|
+
fun invalidateLayout() {
|
|
7
|
+
val text = view.text
|
|
8
|
+
val paint = view.paint
|
|
9
|
+
MeasurementStore.store(view.id, text, paint)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
fun releaseMeasurementStore() {
|
|
13
|
+
MeasurementStore.release(view.id)
|
|
14
|
+
}
|
|
15
|
+
}
|