react-native-enriched-markdown-scaffold 0.7.4-scaffold.9 → 1.0.2-scaffold.10
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 +54 -8
- package/ReactNativeEnrichedMarkdown.podspec +82 -73
- package/android/build.gradle +188 -1
- package/android/consumer-rules.pro +1 -1
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +27 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +7 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +24 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +8 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +27 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +7 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +223 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +183 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +65 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1086 -61
- package/android/src/main/baseline-prof.txt +1 -1
- package/android/src/main/cpp/jni-adapter.cpp +74 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +351 -13
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +85 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +160 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +10 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +126 -10
- package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyPressEvent.kt +24 -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/events/SelectionChangeEvent.kt +43 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +54 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +923 -462
- package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +108 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -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/ListContextManager.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +35 -28
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +20 -82
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
- 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/spans/ListItemSpacingSpan.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +16 -1
- 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/styles/ListStyle.kt +4 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +84 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +52 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -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/extensions/TextPaintExtensions.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/HeadlessActionMode.kt +88 -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/LinkLongPressMovementMethod.kt +58 -27
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +72 -16
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionChangeReporter.kt +232 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +64 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +414 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +61 -11
- package/android/src/main/jni/CMakeLists.txt +53 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
- package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +8 -2
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +13 -6
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathMeasureHelper.kt +2 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +26 -8
- package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
- package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
- package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
- package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
- package/cpp/highlight/HighlightGrammars.hpp +28 -0
- package/cpp/highlight/code_highlight_podspec.rb +211 -0
- package/cpp/highlight/gen-registry.mjs +155 -0
- package/cpp/highlight/grammar-versions.json +159 -0
- package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
- package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
- package/cpp/parser/MD4CParser.cpp +173 -61
- package/cpp/parser/MD4CParser.hpp +1 -0
- package/cpp/parser/MarkdownASTNode.hpp +2 -1
- package/cpp/wasm/ASTSerializer.cpp +2 -0
- package/cpp/wasm/build.sh +1 -0
- package/cpp/wasm/md4c_wasm.cpp +3 -1
- package/docs/ACCESSIBILITY.md +14 -2
- package/docs/API_REFERENCE.md +405 -12
- package/docs/BREAKING_CHANGES.md +129 -0
- package/docs/CODE_HIGHLIGHT.md +133 -0
- package/docs/COPY_OPTIONS.md +61 -3
- package/docs/ELEMENTS_STRUCTURE.md +52 -4
- package/docs/IMAGE_CACHING.md +30 -0
- package/docs/INPUT.md +110 -11
- package/docs/LATEX_MATH.md +87 -45
- package/docs/MACOS.md +1 -1
- package/docs/MARKDOWN_STREAMING.md +27 -1
- package/docs/MENTIONS.md +8 -1
- package/docs/NATIVE_ASSETS.md +127 -0
- package/docs/RTL.md +1 -1
- package/docs/STYLES.md +73 -6
- package/docs/TESTING.md +112 -0
- package/docs/TEXT.md +18 -6
- package/docs/WEB.md +4 -1
- package/ios/EnrichedMarkdown.h +7 -0
- package/ios/EnrichedMarkdown.mm +583 -76
- package/ios/EnrichedMarkdownText.h +7 -0
- package/ios/EnrichedMarkdownText.mm +299 -73
- package/ios/attachments/ENRMImageAttachment.h +9 -0
- package/ios/attachments/ENRMImageAttachment.m +143 -40
- package/ios/attachments/ENRMMathInlineAttachment+macOS.m +2 -1
- package/ios/attachments/ENRMMathInlineAttachment.h +5 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +50 -82
- package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +223 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +183 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +65 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +1086 -61
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +181 -2
- package/ios/input/ENRMAutoLinkDetector.h +2 -1
- package/ios/input/ENRMAutoLinkDetector.mm +79 -41
- package/ios/input/ENRMBlockEditCoordinator.h +71 -0
- package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
- package/ios/input/ENRMBlockRange.h +28 -0
- package/ios/input/ENRMBlockRange.mm +30 -0
- package/ios/input/ENRMBlockStore.h +56 -0
- package/ios/input/ENRMBlockStore.mm +275 -0
- package/ios/input/ENRMClipboardCoordinator.h +45 -0
- package/ios/input/ENRMClipboardCoordinator.mm +120 -0
- package/ios/input/ENRMDetectorPipeline.h +2 -1
- package/ios/input/ENRMEditPipeline.h +76 -0
- package/ios/input/ENRMEditPipeline.mm +228 -0
- package/ios/input/ENRMEditSession.h +36 -0
- package/ios/input/ENRMEditSession.mm +63 -0
- package/ios/input/ENRMFormattingStore.h +17 -0
- package/ios/input/ENRMFormattingStore.mm +95 -105
- package/ios/input/ENRMInputBlockType.h +91 -0
- package/ios/input/ENRMInputBlockType.mm +5 -0
- package/ios/input/ENRMInputDecorationDrawer.h +18 -0
- package/ios/input/ENRMInputEventEmitter.h +56 -0
- package/ios/input/ENRMInputEventEmitter.mm +308 -0
- package/ios/input/ENRMInputFormatter.h +29 -69
- package/ios/input/ENRMInputFormatter.mm +216 -124
- package/ios/input/ENRMInputFormatterStyle.h +99 -0
- package/ios/input/ENRMInputFormatterStyle.mm +188 -0
- package/ios/input/ENRMInputLayoutManager.h +9 -0
- package/ios/input/ENRMInputLayoutManager.mm +30 -0
- package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
- package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
- package/ios/input/ENRMInputParser.h +5 -6
- package/ios/input/ENRMInputParser.mm +461 -178
- package/ios/input/ENRMInputRemend.mm +2 -2
- package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
- package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
- package/ios/input/ENRMInputTextView.mm +103 -8
- package/ios/input/ENRMInputTypingAttributesController.h +42 -0
- package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
- package/ios/input/ENRMLinkCoordinator.h +41 -0
- package/ios/input/ENRMLinkCoordinator.mm +89 -0
- package/ios/input/ENRMMarkdownSerializer.h +29 -5
- package/ios/input/ENRMMarkdownSerializer.mm +170 -48
- package/ios/input/ENRMMathAtom.h +26 -0
- package/ios/input/ENRMMathAtom.mm +12 -0
- package/ios/input/ENRMMentionCoordinator.h +45 -0
- package/ios/input/ENRMMentionCoordinator.mm +167 -0
- package/ios/input/ENRMRangeEditAdjustment.h +38 -0
- package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
- package/ios/input/ENRMRangeStoreUtils.h +39 -0
- package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
- package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
- package/ios/input/EnrichedMarkdownTextInput.h +7 -1
- package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
- package/ios/input/InputStylePropsUtils.h +109 -68
- package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
- package/ios/input/styles/ENRMBlockHandler.h +46 -0
- package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
- package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
- package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
- package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
- package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
- package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
- package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
- package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
- package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
- package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
- package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
- package/ios/internals/MeasurementCache.h +41 -9
- package/ios/internals/ShadowMeasurementUtils.h +63 -58
- package/ios/math/ENRMMathChipController.h +74 -0
- package/ios/math/ENRMMathChipController.mm +476 -0
- package/ios/math/ENRMRaTeXBridge.swift +27 -6
- package/ios/parser/ENRMMarkdownParser.h +1 -8
- package/ios/parser/ENRMMarkdownParser.mm +2 -11
- package/ios/parser/MarkdownASTNode.h +2 -1
- package/ios/parser/MarkdownParserBridge.mm +4 -0
- package/ios/renderer/AttributedRenderer.m +20 -2
- package/ios/renderer/BaseRenderer.h +17 -2
- package/ios/renderer/BaseRenderer.m +30 -0
- package/ios/renderer/BlockquoteRenderer.h +1 -1
- package/ios/renderer/BlockquoteRenderer.m +144 -35
- package/ios/renderer/CodeBlockRenderer.h +1 -1
- package/ios/renderer/CodeBlockRenderer.m +27 -23
- package/ios/renderer/CodeRenderer.h +1 -1
- package/ios/renderer/CodeRenderer.m +3 -1
- package/ios/renderer/ENRMImageRenderer.h +1 -1
- package/ios/renderer/ENRMImageRenderer.m +5 -1
- package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
- package/ios/renderer/ENRMMathInlineRenderer.m +6 -1
- package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
- package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
- package/ios/renderer/EmphasisRenderer.h +1 -1
- package/ios/renderer/EmphasisRenderer.m +3 -1
- package/ios/renderer/HeadingRenderer.h +1 -1
- package/ios/renderer/HeadingRenderer.m +13 -6
- package/ios/renderer/HighlightRenderer.h +1 -1
- package/ios/renderer/HighlightRenderer.m +3 -1
- package/ios/renderer/LinkRenderer.h +1 -1
- package/ios/renderer/LinkRenderer.m +50 -16
- package/ios/renderer/ListItemRenderer.h +13 -1
- package/ios/renderer/ListItemRenderer.m +102 -44
- package/ios/renderer/ListRenderer.h +2 -2
- package/ios/renderer/ListRenderer.m +63 -25
- package/ios/renderer/ParagraphRenderer.h +1 -1
- package/ios/renderer/ParagraphRenderer.m +16 -8
- package/ios/renderer/RenderContext.h +10 -0
- package/ios/renderer/RenderContext.m +44 -0
- package/ios/renderer/RendererFactory.m +14 -0
- package/ios/renderer/StrikethroughRenderer.h +1 -1
- package/ios/renderer/StrikethroughRenderer.m +3 -1
- package/ios/renderer/StrongRenderer.h +1 -1
- package/ios/renderer/StrongRenderer.m +3 -1
- package/ios/renderer/SubscriptRenderer.h +1 -1
- package/ios/renderer/SubscriptRenderer.m +3 -1
- package/ios/renderer/SuperscriptRenderer.h +1 -1
- package/ios/renderer/SuperscriptRenderer.m +3 -1
- package/ios/renderer/TextRenderer.h +2 -2
- package/ios/renderer/TextRenderer.m +3 -1
- package/ios/renderer/ThematicBreakRenderer.h +1 -1
- package/ios/renderer/ThematicBreakRenderer.m +3 -1
- package/ios/renderer/UnderlineRenderer.h +1 -1
- package/ios/renderer/UnderlineRenderer.m +3 -1
- package/ios/styles/StyleConfig.h +42 -0
- package/ios/styles/StyleConfig.mm +216 -0
- package/ios/utils/BlockquoteBorder.h +6 -0
- package/ios/utils/BlockquoteBorder.m +249 -52
- package/ios/utils/CodeBackground.m +3 -65
- package/ios/utils/CodeBlockBackground.m +16 -2
- package/ios/utils/ENRMAtomicSize.h +65 -0
- package/ios/utils/ENRMBlockMenu.h +66 -0
- package/ios/utils/ENRMChipDrawing.h +24 -0
- package/ios/utils/ENRMChipDrawing.m +83 -0
- package/ios/utils/ENRMCodeBlockContent.h +37 -0
- package/ios/utils/ENRMCodeBlockContent.mm +79 -0
- package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
- package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
- package/ios/utils/ENRMFeatureFlags.h +7 -0
- package/ios/utils/ENRMImageDownloader.h +11 -1
- package/ios/utils/ENRMImageDownloader.m +60 -16
- package/ios/utils/ENRMLinkChip.h +70 -0
- package/ios/utils/ENRMLinkChip.m +213 -0
- package/ios/utils/ENRMLinkLayout.h +50 -0
- package/ios/utils/ENRMLinkLayout.m +45 -0
- package/ios/utils/ENRMLocalImageLoader.h +31 -0
- package/ios/utils/ENRMLocalImageLoader.m +68 -0
- package/ios/utils/ENRMMathFallback.h +26 -0
- package/ios/utils/ENRMMathFallback.m +18 -0
- package/ios/utils/ENRMSelectionChange.h +120 -0
- package/ios/utils/ENRMSelectionChange.m +282 -0
- package/ios/utils/ENRMTextViewSetup.h +35 -17
- package/ios/utils/EditMenuUtils+macOS.m +4 -1
- package/ios/utils/EditMenuUtils.h +35 -6
- package/ios/utils/EditMenuUtils.m +188 -11
- package/ios/utils/HTMLGenerator.m +2 -1
- package/ios/utils/ImageRequestHeaderUtils.h +35 -0
- package/ios/utils/LastElementUtils.h +1 -0
- package/ios/utils/ListMarkerDrawer.m +29 -42
- package/ios/utils/MarkdownASTSerializer.m +4 -0
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +3 -1
- package/ios/utils/MarkdownExtractor.m +2 -1
- package/ios/utils/ParagraphStyleUtils.h +5 -0
- package/ios/utils/ParagraphStyleUtils.m +77 -17
- package/ios/utils/RTFExportUtils.m +2 -0
- package/ios/utils/RenderedMarkdownSegment.h +13 -1
- package/ios/utils/RenderedMarkdownSegment.m +20 -0
- package/ios/utils/SegmentRenderer.m +17 -4
- package/ios/utils/StreamingMarkdownFilter.h +11 -1
- package/ios/utils/StreamingMarkdownFilter.m +98 -2
- package/ios/utils/StylePropsUtils.h +110 -1
- package/ios/utils/TaskListTapUtils.m +24 -0
- package/ios/utils/TextViewLayoutManager.mm +3 -0
- package/ios/views/ENRMCodeBlockContainerView.h +60 -0
- package/ios/views/ENRMCodeBlockContainerView.m +569 -0
- package/ios/views/ENRMMathContainerView.h +23 -0
- package/ios/views/ENRMMathContainerView.m +87 -20
- package/ios/views/TableContainerView.h +32 -0
- package/ios/views/TableContainerView.m +291 -175
- package/jest.d.ts +1 -0
- package/jest.js +5 -0
- package/lib/module/EnrichedMarkdownNativeComponent.ts +203 -1
- package/lib/module/EnrichedMarkdownTextInput.js +84 -5
- package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +204 -1
- package/lib/module/headingDefaults.js +34 -0
- package/lib/module/headingDefaults.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/jest/index.js +205 -0
- package/lib/module/jest/index.js.map +1 -0
- package/lib/module/native/EnrichedMarkdownText.js +199 -15
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +98 -23
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +73 -22
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
- package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
- package/lib/module/normalizeMenuItem.js +1 -1
- package/lib/module/utils/textInputState.js +26 -0
- package/lib/module/utils/textInputState.js.map +1 -0
- package/lib/module/web/EnrichedMarkdownText.js +15 -8
- package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/web/parseMarkdown.js +4 -3
- package/lib/module/web/parseMarkdown.js.map +1 -1
- package/lib/module/web/renderers/InlineRenderers.js +102 -3
- package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
- package/lib/module/web/renderers/ListRenderers.js +7 -2
- package/lib/module/web/renderers/ListRenderers.js.map +1 -1
- package/lib/module/web/renderers/index.js +7 -4
- package/lib/module/web/renderers/index.js.map +1 -1
- package/lib/module/web/styles.js +101 -6
- package/lib/module/web/styles.js.map +1 -1
- package/lib/module/web/utils.js +14 -0
- package/lib/module/web/utils.js.map +1 -1
- package/lib/module/web/wasm/md4c.js +0 -0
- package/lib/module/web/wasm/md4c.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +185 -2
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +185 -2
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/headingDefaults.d.ts +9 -0
- package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +2 -2
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/lib/typescript/src/jest/index.d.ts +6 -0
- package/lib/typescript/src/jest/index.d.ts.map +1 -0
- 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/normalizeMarkdownTextInputStyle.d.ts +19 -5
- package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyle.d.ts +122 -5
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +39 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +207 -10
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +60 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +85 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/lib/typescript/src/utils/textInputState.d.ts +24 -0
- package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
- 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/parseMarkdown.d.ts +1 -1
- package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/index.d.ts +2 -1
- package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts +2 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/lib/typescript/src/web/types.d.ts +10 -2
- 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 +36 -9
- package/postinstall.mjs +131 -0
- package/ratex-version.json +21 -0
- package/src/EnrichedMarkdownNativeComponent.ts +203 -1
- package/src/EnrichedMarkdownTextInput.tsx +159 -8
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
- package/src/EnrichedMarkdownTextNativeComponent.ts +204 -1
- package/src/headingDefaults.ts +21 -0
- package/src/index.tsx +15 -0
- package/src/index.web.tsx +6 -1
- package/src/jest/index.tsx +236 -0
- package/src/native/EnrichedMarkdownText.tsx +260 -11
- package/src/normalizeMarkdownStyle.ts +117 -16
- package/src/normalizeMarkdownStyle.web.ts +76 -15
- package/src/normalizeMarkdownTextInputStyle.ts +110 -35
- package/src/normalizeMenuItem.ts +1 -1
- package/src/types/MarkdownStyle.ts +125 -5
- package/src/types/MarkdownStyleInternal.ts +51 -0
- package/src/types/MarkdownTextProps.ts +215 -10
- package/src/types/MarkdownTextProps.web.ts +62 -1
- package/src/types/events.ts +90 -0
- package/src/utils/textInputState.ts +35 -0
- package/src/web/EnrichedMarkdownText.tsx +26 -5
- package/src/web/parseMarkdown.ts +6 -2
- package/src/web/renderers/InlineRenderers.tsx +116 -3
- package/src/web/renderers/ListRenderers.tsx +14 -2
- package/src/web/renderers/index.tsx +6 -2
- package/src/web/styles.ts +114 -5
- package/src/web/types.ts +10 -1
- package/src/web/utils.ts +9 -0
- package/src/web/wasm/md4c.js +0 -0
- package/vendor-grammars.mjs +511 -0
- package/vendor-ratex.mjs +173 -0
- package/app.plugin.js +0 -2
- package/plugin/build/withAndroidMath.js +0 -19
- package/plugin/build/withIosMath.js +0 -28
- package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
- package/plugin/src/withAndroidMath.ts +0 -24
- package/plugin/src/withIosMath.ts +0 -35
- package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
- package/plugin/tsconfig.build.json +0 -16
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.views
|
|
2
|
+
|
|
3
|
+
import android.content.ClipData
|
|
4
|
+
import android.content.ClipboardManager
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.graphics.Canvas
|
|
7
|
+
import android.graphics.ColorFilter
|
|
8
|
+
import android.graphics.Paint
|
|
9
|
+
import android.graphics.PixelFormat
|
|
10
|
+
import android.graphics.Typeface
|
|
11
|
+
import android.graphics.drawable.Drawable
|
|
12
|
+
import android.graphics.drawable.GradientDrawable
|
|
13
|
+
import android.os.Build
|
|
14
|
+
import android.text.Layout
|
|
15
|
+
import android.text.SpannableString
|
|
16
|
+
import android.text.StaticLayout
|
|
17
|
+
import android.text.TextPaint
|
|
18
|
+
import android.text.TextUtils
|
|
19
|
+
import android.util.TypedValue
|
|
20
|
+
import android.view.View
|
|
21
|
+
import android.widget.FrameLayout
|
|
22
|
+
import android.widget.HorizontalScrollView
|
|
23
|
+
import android.widget.ImageView
|
|
24
|
+
import androidx.appcompat.widget.AppCompatImageButton
|
|
25
|
+
import androidx.appcompat.widget.AppCompatTextView
|
|
26
|
+
import com.swmansion.enriched.markdown.parser.MarkdownASTNode
|
|
27
|
+
import com.swmansion.enriched.markdown.spans.LineHeightSpan
|
|
28
|
+
import com.swmansion.enriched.markdown.styles.CodeBlockStyle
|
|
29
|
+
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
30
|
+
import com.swmansion.enriched.markdown.utils.common.CodeBlockHighlighter
|
|
31
|
+
import com.swmansion.enriched.markdown.utils.common.CodeBlockNode
|
|
32
|
+
import com.swmansion.enriched.markdown.utils.text.extensions.applyCodeBlockTextStyle
|
|
33
|
+
import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
34
|
+
import kotlin.math.ceil
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Block segment view for fenced code blocks (see splitASTIntoSegments):
|
|
38
|
+
* a fixed header bar (language name, copy button) above a horizontally
|
|
39
|
+
* scrolling, non-wrapping code pane. Syntax coloring comes from the
|
|
40
|
+
* CodeBlockHighlighter seam and falls back to plain text. The box visuals
|
|
41
|
+
* must stay in sync with the commonmark flavor's CodeBlockSpan.
|
|
42
|
+
*
|
|
43
|
+
* measureCodeBlockNodeHeight must stay in sync with the view: it builds the
|
|
44
|
+
* same styled text, paint, and header metrics, so the height reported to
|
|
45
|
+
* Yoga matches the laid-out height. Since lines never wrap, the height is
|
|
46
|
+
* independent of the available width.
|
|
47
|
+
*/
|
|
48
|
+
class CodeBlockContainerView(
|
|
49
|
+
context: Context,
|
|
50
|
+
styleConfig: StyleConfig,
|
|
51
|
+
) : FrameLayout(context),
|
|
52
|
+
BlockSegmentView {
|
|
53
|
+
private val codeBlockStyle: CodeBlockStyle = styleConfig.codeBlockStyle
|
|
54
|
+
|
|
55
|
+
private val inset = contentInset(codeBlockStyle)
|
|
56
|
+
private val borderW = borderPx(codeBlockStyle)
|
|
57
|
+
private val headerH = headerHeight(codeBlockStyle)
|
|
58
|
+
|
|
59
|
+
override val segmentMarginTop: Int get() = codeBlockStyle.marginTop.toInt()
|
|
60
|
+
override val segmentMarginBottom: Int get() = codeBlockStyle.marginBottom.toInt()
|
|
61
|
+
|
|
62
|
+
var copyLabel: String = ""
|
|
63
|
+
set(value) {
|
|
64
|
+
field = value
|
|
65
|
+
copyButton.contentDescription = value
|
|
66
|
+
}
|
|
67
|
+
var copyAsMarkdownLabel: String = ""
|
|
68
|
+
|
|
69
|
+
// Hides the long-press copy menu only; the header copy button is a visible
|
|
70
|
+
// control, not a context menu, and stays.
|
|
71
|
+
var contextMenuHidden: Boolean = false
|
|
72
|
+
|
|
73
|
+
// Called instead of the menu on long press while contextMenuHidden is on, so
|
|
74
|
+
// the owner can report the block (onSelectionChange) and show its own UI.
|
|
75
|
+
var onMenuSuppressed: ((markdown: String, text: String) -> Unit)? = null
|
|
76
|
+
|
|
77
|
+
// Copy / Copy as Markdown visibility, Copy format and the header copy button.
|
|
78
|
+
// The header button always copies the raw code.
|
|
79
|
+
var menuOptions: BlockMenuOptions = BlockMenuOptions()
|
|
80
|
+
set(value) {
|
|
81
|
+
field = value
|
|
82
|
+
// Hiding the button leaves the header (and its height) untouched.
|
|
83
|
+
copyButton.visibility = if (value.copyButtonHidden) View.GONE else View.VISIBLE
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var onCopyPress: ((code: String, language: String) -> Unit)? = null
|
|
87
|
+
|
|
88
|
+
private var code: String = ""
|
|
89
|
+
private var language: String? = null
|
|
90
|
+
private var fenceChar: String = "`"
|
|
91
|
+
|
|
92
|
+
// True until the closing fence arrives: highlighting is deferred and copying
|
|
93
|
+
// is disabled, while the header stays visible.
|
|
94
|
+
var pending: Boolean = false
|
|
95
|
+
set(value) {
|
|
96
|
+
if (field == value) return
|
|
97
|
+
field = value
|
|
98
|
+
copyButton.isEnabled = !value
|
|
99
|
+
// The reconciler can reuse an unchanged block without re-applying the
|
|
100
|
+
// node, so re-derive here to pick up highlighting once the block closes.
|
|
101
|
+
rebuildCodeText()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private val textView =
|
|
105
|
+
AppCompatTextView(context).apply {
|
|
106
|
+
includeFontPadding = false
|
|
107
|
+
setLineSpacing(0f, 1f)
|
|
108
|
+
breakStrategy = Layout.BREAK_STRATEGY_SIMPLE
|
|
109
|
+
hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
|
|
110
|
+
layoutDirection = View.LAYOUT_DIRECTION_LTR
|
|
111
|
+
textDirection = View.TEXT_DIRECTION_LTR
|
|
112
|
+
setTextSize(TypedValue.COMPLEX_UNIT_PX, codeBlockStyle.fontSize)
|
|
113
|
+
typeface = createCodePaint(codeBlockStyle, context).typeface
|
|
114
|
+
setTextColor(codeBlockStyle.color)
|
|
115
|
+
val horizontalPad = (inset - borderW).coerceAtLeast(0)
|
|
116
|
+
setPadding(horizontalPad, inset, horizontalPad, inset)
|
|
117
|
+
setOnLongClickListener { view -> showContextMenu(view) }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private val scrollView =
|
|
121
|
+
HorizontalScrollView(context).apply {
|
|
122
|
+
isHorizontalScrollBarEnabled = true
|
|
123
|
+
overScrollMode = View.OVER_SCROLL_NEVER
|
|
124
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
125
|
+
horizontalScrollbarThumbDrawable =
|
|
126
|
+
GradientDrawable().apply {
|
|
127
|
+
setColor(secondaryColor(codeBlockStyle.color))
|
|
128
|
+
cornerRadius = context.resources.displayMetrics.density * 2
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
addView(textView, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private val languageView =
|
|
135
|
+
AppCompatTextView(context).apply {
|
|
136
|
+
includeFontPadding = false
|
|
137
|
+
maxLines = 1
|
|
138
|
+
ellipsize = TextUtils.TruncateAt.END
|
|
139
|
+
setTextSize(TypedValue.COMPLEX_UNIT_PX, codeBlockStyle.fontSize * HEADER_LABEL_SCALE)
|
|
140
|
+
typeface = headerTypeface
|
|
141
|
+
setTextColor(secondaryColor(codeBlockStyle.color))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private val copyButton =
|
|
145
|
+
AppCompatImageButton(context).apply {
|
|
146
|
+
background = null
|
|
147
|
+
scaleType = ImageView.ScaleType.CENTER
|
|
148
|
+
setImageDrawable(
|
|
149
|
+
CopyIconDrawable(
|
|
150
|
+
secondaryColor(codeBlockStyle.color),
|
|
151
|
+
ceil(codeBlockStyle.fontSize * HEADER_ICON_SCALE).toInt(),
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
setOnClickListener { copyCode() }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private val dividerPaint =
|
|
158
|
+
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
159
|
+
color = dividerColor(codeBlockStyle.color)
|
|
160
|
+
strokeWidth = context.resources.displayMetrics.density
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
init {
|
|
164
|
+
setWillNotDraw(false)
|
|
165
|
+
background =
|
|
166
|
+
GradientDrawable().apply {
|
|
167
|
+
setColor(codeBlockStyle.backgroundColor)
|
|
168
|
+
cornerRadius = codeBlockStyle.borderRadius
|
|
169
|
+
if (codeBlockStyle.borderWidth > 0f) {
|
|
170
|
+
setStroke(ceil(codeBlockStyle.borderWidth).toInt(), codeBlockStyle.borderColor)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
isLongClickable = true
|
|
174
|
+
setOnLongClickListener { view -> showContextMenu(view) }
|
|
175
|
+
addView(scrollView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
|
|
176
|
+
addView(languageView, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
|
|
177
|
+
addView(copyButton, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fun applyCodeBlockNode(node: MarkdownASTNode) {
|
|
181
|
+
code = CodeBlockNode.extractCode(node)
|
|
182
|
+
fenceChar = CodeBlockNode.fenceChar(node)
|
|
183
|
+
|
|
184
|
+
val newLanguage = CodeBlockNode.language(node)
|
|
185
|
+
if (newLanguage != language) {
|
|
186
|
+
language = newLanguage
|
|
187
|
+
languageView.text = CodeBlockNode.displayLanguageName(newLanguage)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
rebuildCodeText()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Highlighting is deferred while pending, applied once the block closes.
|
|
194
|
+
private fun rebuildCodeText() {
|
|
195
|
+
val plainCode = buildCodeText(code, codeBlockStyle)
|
|
196
|
+
if (!pending) {
|
|
197
|
+
CodeBlockHighlighter.highlight(plainCode, code, language, codeBlockStyle)
|
|
198
|
+
}
|
|
199
|
+
textView.text = plainCode
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
override fun onMeasure(
|
|
203
|
+
widthSpec: Int,
|
|
204
|
+
heightSpec: Int,
|
|
205
|
+
) {
|
|
206
|
+
val measuredWidth = MeasureSpec.getSize(widthSpec)
|
|
207
|
+
scrollView.measure(
|
|
208
|
+
MeasureSpec.makeMeasureSpec((measuredWidth - borderW * 2).coerceAtLeast(0), MeasureSpec.EXACTLY),
|
|
209
|
+
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
|
|
210
|
+
)
|
|
211
|
+
copyButton.measure(
|
|
212
|
+
MeasureSpec.makeMeasureSpec(headerH, MeasureSpec.EXACTLY),
|
|
213
|
+
MeasureSpec.makeMeasureSpec(headerH, MeasureSpec.EXACTLY),
|
|
214
|
+
)
|
|
215
|
+
val labelMaxWidth = (measuredWidth - inset * 2 - headerH).coerceAtLeast(0)
|
|
216
|
+
languageView.measure(
|
|
217
|
+
MeasureSpec.makeMeasureSpec(labelMaxWidth, MeasureSpec.AT_MOST),
|
|
218
|
+
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
|
|
219
|
+
)
|
|
220
|
+
setMeasuredDimension(measuredWidth, headerH + scrollView.measuredHeight)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
override fun onLayout(
|
|
224
|
+
changed: Boolean,
|
|
225
|
+
left: Int,
|
|
226
|
+
top: Int,
|
|
227
|
+
right: Int,
|
|
228
|
+
bottom: Int,
|
|
229
|
+
) {
|
|
230
|
+
val width = right - left
|
|
231
|
+
val labelTop = headerH - languageView.measuredHeight
|
|
232
|
+
languageView.layout(
|
|
233
|
+
inset,
|
|
234
|
+
labelTop,
|
|
235
|
+
inset + languageView.measuredWidth,
|
|
236
|
+
labelTop + languageView.measuredHeight,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
val iconWidth = copyButton.drawable?.intrinsicWidth ?: copyButton.measuredWidth
|
|
240
|
+
val iconSlack = ((copyButton.measuredWidth - iconWidth) / 2).coerceAtLeast(0)
|
|
241
|
+
val buttonLeft = (width - inset - copyButton.measuredWidth + iconSlack).coerceAtLeast(0)
|
|
242
|
+
val buttonTop = (headerH - languageView.measuredHeight / 2 - copyButton.measuredHeight / 2).coerceAtLeast(0)
|
|
243
|
+
copyButton.layout(
|
|
244
|
+
buttonLeft,
|
|
245
|
+
buttonTop,
|
|
246
|
+
buttonLeft + copyButton.measuredWidth,
|
|
247
|
+
buttonTop + copyButton.measuredHeight,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
scrollView.layout(borderW, headerH, width - borderW, bottom - top - borderW)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Divider between the header and the code, centered in the gap the code
|
|
254
|
+
// text's top inset creates, so it adds no height of its own.
|
|
255
|
+
override fun onDraw(canvas: Canvas) {
|
|
256
|
+
super.onDraw(canvas)
|
|
257
|
+
val borderWidth = borderW.toFloat()
|
|
258
|
+
val y = headerH + inset / 2f
|
|
259
|
+
canvas.drawLine(borderWidth, y, width - borderWidth, y, dividerPaint)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Returns whether a menu was shown, so the long-press listener only consumes
|
|
263
|
+
// the event when there is one (a pending block has no menu yet, and a hidden
|
|
264
|
+
// one never does).
|
|
265
|
+
private fun showContextMenu(anchor: View): Boolean {
|
|
266
|
+
if (pending) return false
|
|
267
|
+
if (contextMenuHidden) {
|
|
268
|
+
val onSuppressed = onMenuSuppressed ?: return false
|
|
269
|
+
onSuppressed(CodeBlockNode.fencedMarkdown(code, language, fenceChar), code)
|
|
270
|
+
return true
|
|
271
|
+
}
|
|
272
|
+
if (!menuOptions.hasItems) return false
|
|
273
|
+
ContextMenuPopup.show(anchor, this) {
|
|
274
|
+
blockCopyItems(
|
|
275
|
+
menuOptions,
|
|
276
|
+
copyLabel,
|
|
277
|
+
copyAsMarkdownLabel,
|
|
278
|
+
markdown = { CodeBlockNode.fencedMarkdown(code, language, fenceChar) },
|
|
279
|
+
rawText = { code },
|
|
280
|
+
copyRaw = ::copyCode,
|
|
281
|
+
copyMarkdown = ::copyFencedMarkdown,
|
|
282
|
+
)
|
|
283
|
+
}
|
|
284
|
+
return true
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private fun copyCode() {
|
|
288
|
+
if (pending || code.isEmpty()) return
|
|
289
|
+
copyToClipboard(code)
|
|
290
|
+
onCopyPress?.invoke(code, language ?: "")
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private fun copyFencedMarkdown() {
|
|
294
|
+
if (code.isEmpty()) return
|
|
295
|
+
copyToClipboard(CodeBlockNode.fencedMarkdown(code, language, fenceChar))
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private fun copyToClipboard(text: String) {
|
|
299
|
+
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
300
|
+
clipboard.setPrimaryClip(ClipData.newPlainText("Code", text))
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private class CopyIconDrawable(
|
|
304
|
+
color: Int,
|
|
305
|
+
private val size: Int,
|
|
306
|
+
) : Drawable() {
|
|
307
|
+
private val paint =
|
|
308
|
+
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
309
|
+
style = Paint.Style.STROKE
|
|
310
|
+
strokeWidth = size / 20f
|
|
311
|
+
strokeJoin = Paint.Join.ROUND
|
|
312
|
+
strokeCap = Paint.Cap.ROUND
|
|
313
|
+
this.color = color
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private val glyph = CopyGlyph()
|
|
317
|
+
|
|
318
|
+
override fun draw(canvas: Canvas) {
|
|
319
|
+
glyph.draw(canvas, bounds.width().toFloat(), paint)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
override fun getIntrinsicWidth() = size
|
|
323
|
+
|
|
324
|
+
override fun getIntrinsicHeight() = size
|
|
325
|
+
|
|
326
|
+
override fun setAlpha(alpha: Int) {
|
|
327
|
+
paint.alpha = alpha
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
override fun setColorFilter(colorFilter: ColorFilter?) {
|
|
331
|
+
paint.colorFilter = colorFilter
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@Deprecated("Deprecated in Java")
|
|
335
|
+
override fun getOpacity() = PixelFormat.TRANSLUCENT
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
companion object {
|
|
339
|
+
private const val HEADER_LABEL_SCALE = 0.85f
|
|
340
|
+
|
|
341
|
+
// Drawable box for the copy glyph, as a multiple of the code font size.
|
|
342
|
+
// iOS sizes the SF Symbol by point size (kENRMHeaderIconScale = 0.72) but
|
|
343
|
+
// the symbol renders roughly half again as tall as its point size; this box
|
|
344
|
+
// is scaled up to land the drawn glyph at the same on-screen size.
|
|
345
|
+
private const val HEADER_ICON_SCALE = 1.08f
|
|
346
|
+
|
|
347
|
+
private val headerTypeface: Typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL)
|
|
348
|
+
|
|
349
|
+
private fun secondaryColor(color: Int): Int = (color and 0x00FFFFFF) or (0x99 shl 24)
|
|
350
|
+
|
|
351
|
+
private fun dividerColor(color: Int): Int = (color and 0x00FFFFFF) or (0x33 shl 24)
|
|
352
|
+
|
|
353
|
+
private fun contentInset(style: CodeBlockStyle): Int = ceil(style.padding + style.borderWidth).toInt()
|
|
354
|
+
|
|
355
|
+
private fun borderPx(style: CodeBlockStyle): Int = ceil(style.borderWidth).toInt()
|
|
356
|
+
|
|
357
|
+
// Header is the top content inset plus one label line; the code text
|
|
358
|
+
// view's own top inset then forms the single gap below the label.
|
|
359
|
+
private fun headerHeight(style: CodeBlockStyle): Int {
|
|
360
|
+
val paint =
|
|
361
|
+
TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
362
|
+
textSize = style.fontSize * HEADER_LABEL_SCALE
|
|
363
|
+
typeface = headerTypeface
|
|
364
|
+
}
|
|
365
|
+
val metrics = paint.fontMetrics
|
|
366
|
+
return contentInset(style) + ceil(metrics.descent - metrics.ascent).toInt()
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
private fun createCodePaint(
|
|
370
|
+
style: CodeBlockStyle,
|
|
371
|
+
context: Context,
|
|
372
|
+
): TextPaint =
|
|
373
|
+
TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
374
|
+
applyCodeBlockTextStyle(style, context)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private fun buildCodeText(
|
|
378
|
+
code: String,
|
|
379
|
+
style: CodeBlockStyle,
|
|
380
|
+
): SpannableString =
|
|
381
|
+
SpannableString(code).apply {
|
|
382
|
+
if (style.lineHeight > 0f && isNotEmpty()) {
|
|
383
|
+
setSpan(LineHeightSpan(style.lineHeight), 0, length, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Lines never wrap (the code pane scrolls horizontally), so the layout is
|
|
388
|
+
// built at the text's desired width and the result is independent of the
|
|
389
|
+
// available width.
|
|
390
|
+
fun measureCodeBlockNodeHeight(
|
|
391
|
+
node: MarkdownASTNode,
|
|
392
|
+
config: StyleConfig,
|
|
393
|
+
context: Context,
|
|
394
|
+
@Suppress("unused") width: Float,
|
|
395
|
+
): Float {
|
|
396
|
+
val style = config.codeBlockStyle
|
|
397
|
+
val inset = contentInset(style)
|
|
398
|
+
val headerH = headerHeight(style)
|
|
399
|
+
val text = buildCodeText(CodeBlockNode.extractCode(node), style)
|
|
400
|
+
if (text.isEmpty()) return headerH + inset * 2f
|
|
401
|
+
val paint = createCodePaint(style, context)
|
|
402
|
+
val contentWidth = ceil(Layout.getDesiredWidth(text, paint)).toInt().coerceAtLeast(1)
|
|
403
|
+
val layout =
|
|
404
|
+
StaticLayout.Builder
|
|
405
|
+
.obtain(text, 0, text.length, paint, contentWidth)
|
|
406
|
+
.setIncludePad(false)
|
|
407
|
+
.setLineSpacing(0f, 1f)
|
|
408
|
+
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
|
409
|
+
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
|
410
|
+
.build()
|
|
411
|
+
return layout.height.toFloat() + inset * 2 + headerH
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
@@ -183,16 +183,19 @@ object ContextMenuPopup {
|
|
|
183
183
|
color = Color.parseColor("#333333")
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
private val copyGlyph = CopyGlyph()
|
|
187
|
+
|
|
188
|
+
// COPY mirrors SF Symbol doc.on.doc, DOCUMENT mirrors doc.text; the copy
|
|
189
|
+
// glyph is shared with the header button in CodeBlockContainerView.
|
|
186
190
|
override fun draw(canvas: Canvas) {
|
|
187
191
|
val u = bounds.width() / 24f
|
|
188
192
|
if (icon == Icon.COPY) {
|
|
189
|
-
|
|
190
|
-
canvas.drawRoundRect(RectF(2 * u, 8 * u, 16 * u, 22 * u), 2 * u, 2 * u, paint)
|
|
193
|
+
copyGlyph.draw(canvas, bounds.width().toFloat(), paint)
|
|
191
194
|
} else {
|
|
192
|
-
canvas.drawRoundRect(RectF(
|
|
193
|
-
canvas.drawLine(8 * u,
|
|
194
|
-
canvas.drawLine(8 * u, 12 * u,
|
|
195
|
-
canvas.drawLine(8 * u, 16 * u, 13 * u, 16 * u, paint)
|
|
195
|
+
canvas.drawRoundRect(RectF(5 * u, 3 * u, 19 * u, 21 * u), 2.5f * u, 2.5f * u, paint)
|
|
196
|
+
canvas.drawLine(8.5f * u, 9 * u, 15.5f * u, 9 * u, paint)
|
|
197
|
+
canvas.drawLine(8.5f * u, 12.5f * u, 15.5f * u, 12.5f * u, paint)
|
|
198
|
+
canvas.drawLine(8.5f * u, 16 * u, 13 * u, 16 * u, paint)
|
|
196
199
|
}
|
|
197
200
|
}
|
|
198
201
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.views
|
|
2
|
+
|
|
3
|
+
import android.graphics.Canvas
|
|
4
|
+
import android.graphics.Paint
|
|
5
|
+
import android.graphics.Path
|
|
6
|
+
import android.graphics.PorterDuff
|
|
7
|
+
import android.graphics.PorterDuffXfermode
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Draws the copy glyph so Android matches the SF Symbol doc.on.doc rendered on
|
|
11
|
+
* iOS: two portrait document pages with a folded (dog-eared) top-right corner,
|
|
12
|
+
* a front page in the lower-left laid over a back page in the upper-right.
|
|
13
|
+
*
|
|
14
|
+
* The back page is punched out where the front page (plus a hairline of
|
|
15
|
+
* clearance) covers it, so the front reads as sitting on top exactly as the SF
|
|
16
|
+
* Symbol does, instead of both outlines crossing. The clear is done inside an
|
|
17
|
+
* offscreen layer so the punched region reveals whatever background the drawable
|
|
18
|
+
* sits on, without the glyph needing to know that color.
|
|
19
|
+
*
|
|
20
|
+
* One instance is held per drawable rather than shared: the paths and clear
|
|
21
|
+
* paint are reused across draws to keep draw() allocation-free, and two drawables
|
|
22
|
+
* of different sizes must not share those buffers. All coordinates use a 24-unit
|
|
23
|
+
* grid scaled to the drawable width, matching the grid the iOS symbol is authored
|
|
24
|
+
* on.
|
|
25
|
+
*/
|
|
26
|
+
internal class CopyGlyph {
|
|
27
|
+
private val back = Path()
|
|
28
|
+
private val front = Path()
|
|
29
|
+
private val backFold = Path()
|
|
30
|
+
private val frontFold = Path()
|
|
31
|
+
|
|
32
|
+
// Color and colorFilter are irrelevant under PorterDuff.CLEAR; only the
|
|
33
|
+
// widened stroke (set per draw) matters, so this is configured once.
|
|
34
|
+
private val clearPaint =
|
|
35
|
+
Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
36
|
+
style = Paint.Style.FILL_AND_STROKE
|
|
37
|
+
strokeJoin = Paint.Join.ROUND
|
|
38
|
+
strokeCap = Paint.Cap.ROUND
|
|
39
|
+
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fun draw(
|
|
43
|
+
canvas: Canvas,
|
|
44
|
+
width: Float,
|
|
45
|
+
stroke: Paint,
|
|
46
|
+
) {
|
|
47
|
+
val u = width / 24f
|
|
48
|
+
val fold = 4f * u
|
|
49
|
+
val corner = 2f * u
|
|
50
|
+
|
|
51
|
+
buildPage(back, 9 * u, 1 * u, 20 * u, 17 * u, fold, corner)
|
|
52
|
+
buildPage(front, 3 * u, 7 * u, 14 * u, 23 * u, fold, corner)
|
|
53
|
+
buildFold(backFold, 20 * u, 1 * u, fold)
|
|
54
|
+
buildFold(frontFold, 14 * u, 7 * u, fold)
|
|
55
|
+
clearPaint.strokeWidth = stroke.strokeWidth * 3f
|
|
56
|
+
|
|
57
|
+
val saved = canvas.saveLayer(0f, 0f, width, width, null)
|
|
58
|
+
canvas.drawPath(back, stroke)
|
|
59
|
+
canvas.drawPath(backFold, stroke)
|
|
60
|
+
canvas.drawPath(front, clearPaint)
|
|
61
|
+
canvas.drawPath(front, stroke)
|
|
62
|
+
canvas.drawPath(frontFold, stroke)
|
|
63
|
+
canvas.restoreToCount(saved)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A page outline whose top-right corner is cut back by fold, with the other
|
|
67
|
+
// three corners rounded by corner.
|
|
68
|
+
private fun buildPage(
|
|
69
|
+
path: Path,
|
|
70
|
+
l: Float,
|
|
71
|
+
t: Float,
|
|
72
|
+
r: Float,
|
|
73
|
+
b: Float,
|
|
74
|
+
fold: Float,
|
|
75
|
+
corner: Float,
|
|
76
|
+
) {
|
|
77
|
+
path.rewind()
|
|
78
|
+
path.moveTo(l, t + corner)
|
|
79
|
+
path.quadTo(l, t, l + corner, t)
|
|
80
|
+
path.lineTo(r - fold, t)
|
|
81
|
+
path.lineTo(r, t + fold)
|
|
82
|
+
path.lineTo(r, b - corner)
|
|
83
|
+
path.quadTo(r, b, r - corner, b)
|
|
84
|
+
path.lineTo(l + corner, b)
|
|
85
|
+
path.quadTo(l, b, l, b - corner)
|
|
86
|
+
path.close()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The two inner edges of the folded-over corner, at the page's top-right.
|
|
90
|
+
private fun buildFold(
|
|
91
|
+
path: Path,
|
|
92
|
+
r: Float,
|
|
93
|
+
t: Float,
|
|
94
|
+
fold: Float,
|
|
95
|
+
) {
|
|
96
|
+
path.rewind()
|
|
97
|
+
path.moveTo(r - fold, t)
|
|
98
|
+
path.lineTo(r - fold, t + fold)
|
|
99
|
+
path.lineTo(r, t + fold)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -24,6 +24,7 @@ import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
|
|
|
24
24
|
import com.swmansion.enriched.markdown.parser.MarkdownASTNode
|
|
25
25
|
import com.swmansion.enriched.markdown.parser.MarkdownASTNode.NodeType
|
|
26
26
|
import com.swmansion.enriched.markdown.renderer.Renderer
|
|
27
|
+
import com.swmansion.enriched.markdown.spans.LinkChipBackgroundSpan
|
|
27
28
|
import com.swmansion.enriched.markdown.styles.StyleConfig
|
|
28
29
|
import com.swmansion.enriched.markdown.styles.TableStyle
|
|
29
30
|
import com.swmansion.enriched.markdown.utils.common.layout.isLayoutRTL
|
|
@@ -43,7 +44,7 @@ class TableContainerView(
|
|
|
43
44
|
private val styleConfig: StyleConfig,
|
|
44
45
|
) : FrameLayout(context),
|
|
45
46
|
BlockSegmentView {
|
|
46
|
-
|
|
47
|
+
internal val tableStyle: TableStyle = styleConfig.tableStyle
|
|
47
48
|
|
|
48
49
|
override val segmentMarginTop: Int get() = tableStyle.marginTop.toInt()
|
|
49
50
|
override val segmentMarginBottom: Int get() = tableStyle.marginBottom.toInt()
|
|
@@ -63,6 +64,12 @@ class TableContainerView(
|
|
|
63
64
|
|
|
64
65
|
var copyLabel: String = ""
|
|
65
66
|
var copyAsMarkdownLabel: String = ""
|
|
67
|
+
var contextMenuHidden: Boolean = false
|
|
68
|
+
var menuOptions: BlockMenuOptions = BlockMenuOptions()
|
|
69
|
+
|
|
70
|
+
// Called instead of the menu on long press while contextMenuHidden is on, so
|
|
71
|
+
// the owner can report the block (onSelectionChange) and show its own UI.
|
|
72
|
+
var onMenuSuppressed: ((markdown: String, text: String) -> Unit)? = null
|
|
66
73
|
|
|
67
74
|
private val scrollView =
|
|
68
75
|
HorizontalScrollView(context).apply {
|
|
@@ -79,6 +86,19 @@ class TableContainerView(
|
|
|
79
86
|
|
|
80
87
|
val rowCount: Int get() = rows.size
|
|
81
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Number of links across all cells. Tables are not reported by `onLinkLayout`, but their links
|
|
91
|
+
* still count so later link indices stay in document order.
|
|
92
|
+
*/
|
|
93
|
+
val linkCount: Int
|
|
94
|
+
get() =
|
|
95
|
+
rows.sumOf { row ->
|
|
96
|
+
row.sumOf { cell ->
|
|
97
|
+
// LinkRenderer attaches one LinkChipBackgroundSpan per link.
|
|
98
|
+
cell.attributedText.getSpans(0, cell.attributedText.length, LinkChipBackgroundSpan::class.java).size
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
82
102
|
fun animateNewRows(
|
|
83
103
|
previousRowCount: Int,
|
|
84
104
|
durationMs: Long,
|
|
@@ -327,20 +347,49 @@ class TableContainerView(
|
|
|
327
347
|
right: Int,
|
|
328
348
|
bottom: Int,
|
|
329
349
|
) {
|
|
330
|
-
scrollView.layout(0, 0, right - left, bottom - top)
|
|
331
350
|
val viewWidth = right - left
|
|
332
|
-
|
|
351
|
+
val overhang = max(ceil(tableStyle.horizontalOverflow.toDouble()).toInt(), 0)
|
|
352
|
+
val contentWidth = viewWidth - overhang * 2
|
|
353
|
+
val tableOverflows = totalTableWidth > contentWidth
|
|
354
|
+
|
|
355
|
+
scrollView.setPadding(overhang, 0, overhang, 0)
|
|
356
|
+
scrollView.clipToPadding = !tableOverflows
|
|
357
|
+
scrollView.isHorizontalScrollBarEnabled = tableOverflows
|
|
358
|
+
|
|
359
|
+
scrollView.layout(0, 0, viewWidth, bottom - top)
|
|
360
|
+
|
|
361
|
+
gridContainer.translationX =
|
|
362
|
+
if (tableStyle.align.isNotEmpty() && !tableOverflows) {
|
|
363
|
+
val freeSpace = max(contentWidth - totalTableWidth, 0f)
|
|
364
|
+
val desiredLeft =
|
|
365
|
+
overhang +
|
|
366
|
+
when (tableStyle.align) {
|
|
367
|
+
"center" -> freeSpace / 2f
|
|
368
|
+
"right" -> freeSpace
|
|
369
|
+
else -> 0f
|
|
370
|
+
}
|
|
371
|
+
desiredLeft - gridContainer.left
|
|
372
|
+
} else {
|
|
373
|
+
0f
|
|
374
|
+
}
|
|
333
375
|
|
|
334
|
-
if (isRtl
|
|
335
|
-
|
|
376
|
+
if (isRtl) {
|
|
377
|
+
val effectiveWidth = if (tableOverflows) contentWidth.toFloat() else viewWidth.toFloat()
|
|
378
|
+
if (totalTableWidth > effectiveWidth) {
|
|
379
|
+
scrollView.scrollTo((totalTableWidth - effectiveWidth).toInt(), 0)
|
|
380
|
+
}
|
|
336
381
|
}
|
|
337
382
|
}
|
|
338
383
|
|
|
339
384
|
private fun showContextMenu(anchor: View) {
|
|
385
|
+
if (contextMenuHidden) {
|
|
386
|
+
onMenuSuppressed?.invoke(tableMarkdown, buildPlainText())
|
|
387
|
+
return
|
|
388
|
+
}
|
|
340
389
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
341
390
|
ContextMenuPopup.show(anchor, this) {
|
|
342
|
-
|
|
343
|
-
val plainText =
|
|
391
|
+
blockCopyItems(menuOptions, copyLabel, copyAsMarkdownLabel, markdown = { tableMarkdown }, rawText = ::buildPlainText, copyRaw = {
|
|
392
|
+
val plainText = buildPlainText()
|
|
344
393
|
if (plainText.isNotEmpty()) {
|
|
345
394
|
val displayMetrics = context.resources.displayMetrics
|
|
346
395
|
val tableRows =
|
|
@@ -350,13 +399,14 @@ class TableContainerView(
|
|
|
350
399
|
val html = HTMLGenerator.generateTableHTML(tableRows, styleConfig, displayMetrics.scaledDensity, displayMetrics.density)
|
|
351
400
|
clipboard.setPrimaryClip(ClipData.newHtmlText("Table", plainText, html))
|
|
352
401
|
}
|
|
353
|
-
}
|
|
354
|
-
item(ContextMenuPopup.Icon.DOCUMENT, copyAsMarkdownLabel) {
|
|
402
|
+
}, copyMarkdown = {
|
|
355
403
|
if (tableMarkdown.isNotEmpty()) clipboard.setPrimaryClip(ClipData.newPlainText("Table", tableMarkdown))
|
|
356
|
-
}
|
|
404
|
+
})
|
|
357
405
|
}
|
|
358
406
|
}
|
|
359
407
|
|
|
408
|
+
private fun buildPlainText(): String = rows.joinToString("\n") { row -> row.joinToString("\t") { it.plainText } }
|
|
409
|
+
|
|
360
410
|
private fun buildMarkdownFromRows(): String =
|
|
361
411
|
rows.joinToString("") { row ->
|
|
362
412
|
val line = "| ${row.joinToString(" | ") { it.markdownText }} |\n"
|
|
@@ -456,7 +506,7 @@ class TableContainerView(
|
|
|
456
506
|
.apply { configure(config, context) }
|
|
457
507
|
.renderDocument(MarkdownASTNode(NodeType.Document, children = listOf(paragraph)), null, null)
|
|
458
508
|
}
|
|
459
|
-
styledText.replaceMathSpansWithPlaceholders(context, config.inlineMathStyle.
|
|
509
|
+
styledText.replaceMathSpansWithPlaceholders(context, config.inlineMathStyle.boxPaddingVertical)
|
|
460
510
|
if (isHeader && styledText.isNotEmpty()) {
|
|
461
511
|
styledText.setSpan(HeaderTypefaceSpan(headerTypeface), 0, styledText.length, 33)
|
|
462
512
|
}
|