react-native-marcus 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/README.md +220 -63
- package/RNMarcus.podspec +6 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
- package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
- package/android/src/main/new_arch/CMakeLists.txt +17 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
- package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
- package/android/src/test/resources/render-model.txt +221 -16
- package/apple/MarcusSwizzle.h +25 -0
- package/apple/MarcusTextDecoratorComponentView.h +22 -0
- package/apple/MarcusTextDecoratorComponentView.mm +72 -0
- package/apple/MarcusTextDecoratorShadowNode.h +62 -0
- package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
- package/apple/MarcusTextDecoratorViewManager.h +4 -0
- package/apple/MarcusTextDecoratorViewManager.mm +11 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
- package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
- package/apple/MarcusTextLayoutManager.h +40 -0
- package/apple/MarcusTextLayoutManager.mm +173 -0
- package/apple/MarkdownAttributes.h +23 -2
- package/apple/MarkdownAttributes.m +6 -0
- package/apple/MarkdownCopy.swift +63 -0
- package/apple/MarkdownDecorator.swift +2 -2
- package/apple/MarkdownFormatter+React.swift +23 -0
- package/apple/MarkdownFormatter.swift +869 -148
- package/apple/MarkdownParagraphLayoutManager.swift +455 -0
- package/apple/MarkdownSwiftInterop.h +0 -1
- package/apple/MarkdownTextBackground.swift +59 -1
- package/apple/MarkdownTextFieldObserver.swift +2 -2
- package/apple/MarkdownTextLayoutFragment.swift +159 -33
- package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
- package/apple/MarkdownTextStorageDelegate.swift +2 -2
- package/apple/MarkdownTextViewObserver.swift +2 -2
- package/apple/MarkdownUtils.swift +38 -0
- package/apple/MarkdownWritingDirection.swift +132 -0
- package/apple/RCTMarcusStyle+Codegen.h +115 -9
- package/apple/RCTMarcusStyle.h +128 -31
- package/apple/RCTMarcusStyle.m +48 -0
- package/apple/RCTParagraphComponentView+Marcus.h +17 -0
- package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/commonjs/MarkdownText.js +330 -0
- package/lib/commonjs/MarkdownText.js.map +1 -0
- package/lib/commonjs/MarkdownText.web.js +529 -0
- package/lib/commonjs/MarkdownText.web.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.js +22 -27
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/commonjs/MarkdownTextInput.web.js +12 -7
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
- package/lib/commonjs/atomUtils.js +112 -0
- package/lib/commonjs/atomUtils.js.map +1 -0
- package/lib/commonjs/childrenUtils.js +36 -0
- package/lib/commonjs/childrenUtils.js.map +1 -0
- package/lib/commonjs/index.js +8 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/math/environment.js +69 -0
- package/lib/commonjs/math/environment.js.map +1 -0
- package/lib/commonjs/math/index.js +61 -0
- package/lib/commonjs/math/index.js.map +1 -0
- package/lib/commonjs/math/mathjax.js +548 -0
- package/lib/commonjs/math/mathjax.js.map +1 -0
- package/lib/commonjs/metro.js +172 -0
- package/lib/commonjs/metro.js.map +1 -0
- package/lib/commonjs/parser/index.d.ts +3 -0
- package/lib/commonjs/parser/index.js +450 -42
- package/lib/commonjs/parser/index.js.map +4 -4
- package/lib/commonjs/stripUtils.js +743 -0
- package/lib/commonjs/stripUtils.js.map +1 -0
- package/lib/commonjs/styleUtils.js +194 -23
- package/lib/commonjs/styleUtils.js.map +1 -1
- package/lib/commonjs/web/MarkdownElements.css +67 -0
- package/lib/commonjs/web/MarkdownTextInput.css +9 -7
- package/lib/commonjs/web/utils/blockLayout.js +95 -7
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
- package/lib/commonjs/web/utils/blockUtils.js +327 -54
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
- package/lib/commonjs/web/utils/elementUtils.js +80 -0
- package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +84 -28
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
- package/lib/commonjs/web/utils/treeUtils.js +6 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
- package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
- package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/module/MarkdownText.js +325 -0
- package/lib/module/MarkdownText.js.map +1 -0
- package/lib/module/MarkdownText.web.js +524 -0
- package/lib/module/MarkdownText.web.js.map +1 -0
- package/lib/module/MarkdownTextInput.js +24 -29
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/module/MarkdownTextInput.web.js +12 -7
- package/lib/module/MarkdownTextInput.web.js.map +1 -1
- package/lib/module/atomUtils.js +106 -0
- package/lib/module/atomUtils.js.map +1 -0
- package/lib/module/childrenUtils.js +33 -0
- package/lib/module/childrenUtils.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/math/environment.js +67 -0
- package/lib/module/math/environment.js.map +1 -0
- package/lib/module/math/index.js +56 -0
- package/lib/module/math/index.js.map +1 -0
- package/lib/module/math/mathjax.js +541 -0
- package/lib/module/math/mathjax.js.map +1 -0
- package/lib/module/metro.js +169 -0
- package/lib/module/metro.js.map +1 -0
- package/lib/module/parser/index.d.ts +3 -0
- package/lib/module/parser/index.js +450 -42
- package/lib/module/parser/index.js.map +4 -4
- package/lib/module/stripUtils.js +739 -0
- package/lib/module/stripUtils.js.map +1 -0
- package/lib/module/styleUtils.js +196 -25
- package/lib/module/styleUtils.js.map +1 -1
- package/lib/module/web/MarkdownElements.css +67 -0
- package/lib/module/web/MarkdownTextInput.css +9 -7
- package/lib/module/web/utils/blockLayout.js +94 -8
- package/lib/module/web/utils/blockLayout.js.map +1 -1
- package/lib/module/web/utils/blockUtils.js +328 -56
- package/lib/module/web/utils/blockUtils.js.map +1 -1
- package/lib/module/web/utils/elementUtils.js +75 -0
- package/lib/module/web/utils/elementUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +86 -30
- package/lib/module/web/utils/parserUtils.js.map +1 -1
- package/lib/module/web/utils/treeUtils.js +6 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -1
- package/lib/module/web/utils/webStyleUtils.js +25 -5
- package/lib/module/web/utils/webStyleUtils.js.map +1 -1
- package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
- package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
- package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
- package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/metro.d.ts +59 -0
- package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
- package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/lib/typescript/module/e2e/links.test.d.ts +2 -0
- package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/module/src/atomUtils.d.ts +58 -0
- package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +39 -3
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/math/environment.d.ts +7 -0
- package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/module/src/math/index.d.ts +64 -0
- package/lib/typescript/module/src/math/index.d.ts.map +1 -0
- package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/module/src/metro.d.ts +59 -0
- package/lib/typescript/module/src/metro.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +3 -3
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/module/src/stripUtils.d.ts +108 -0
- package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +62 -4
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/package.json +29 -2
- package/react-native.config.js +8 -1
- package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/src/MarkdownText.tsx +472 -0
- package/src/MarkdownText.web.tsx +668 -0
- package/src/MarkdownTextInput.tsx +42 -38
- package/src/MarkdownTextInput.web.tsx +36 -13
- package/src/atomUtils.ts +112 -0
- package/src/childrenUtils.ts +39 -0
- package/src/commonTypes.ts +69 -9
- package/src/index.tsx +2 -1
- package/src/math/environment.ts +68 -0
- package/src/math/index.ts +78 -0
- package/src/math/mathjax.ts +604 -0
- package/src/metro.ts +238 -0
- package/src/parser/index.ts +175 -10
- package/src/parser/micromark-extension-mentions.ts +104 -11
- package/src/stripUtils.ts +919 -0
- package/src/styleUtils.ts +290 -26
- package/src/web/MarkdownElements.css +67 -0
- package/src/web/MarkdownTextInput.css +9 -7
- package/src/web/utils/blockLayout.ts +124 -14
- package/src/web/utils/blockUtils.ts +365 -59
- package/src/web/utils/elementUtils.ts +87 -0
- package/src/web/utils/parserUtils.ts +119 -31
- package/src/web/utils/treeUtils.ts +6 -1
- package/src/web/utils/webStyleUtils.ts +32 -8
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
- package/apple/RCTMarcusStyle+Codegen.mm +0 -64
- package/apple/RCTMarcusUtils.h +0 -26
- package/apple/RCTMarcusUtils.mm +0 -26
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Draws the parts of markdown rendering that attributed-string attributes
|
|
4
|
+
/// cannot express -- the box behind a code block, blockquote ribbons, rounded
|
|
5
|
+
/// mention pills and a list's bullets -- for a `Text` rather than a `TextInput`.
|
|
6
|
+
///
|
|
7
|
+
/// The input draws these in TextKit 2, from `MarkdownTextLayoutFragment`, which
|
|
8
|
+
/// is only reachable through a text view's own `NSTextLayoutManager`. A
|
|
9
|
+
/// paragraph is laid out and drawn by `RCTTextLayoutManager`, which is TextKit
|
|
10
|
+
/// 1, so the same shapes are drawn here instead -- during the background pass,
|
|
11
|
+
/// which is exactly where something belongs that has to sit under the glyphs and
|
|
12
|
+
/// over nothing.
|
|
13
|
+
///
|
|
14
|
+
/// The geometry is the same in both, and deliberately expressed the same way:
|
|
15
|
+
/// a code block's box spans the lines it covers, a ribbon sits at a fixed
|
|
16
|
+
/// offset from the edge the line begins at, and a pill is a font-height box
|
|
17
|
+
/// around the run's baseline. The bullets are the one shape with no counterpart
|
|
18
|
+
/// in the input, which shows the marker that was typed rather than drawing one.
|
|
19
|
+
@objc public final class MarkdownParagraphLayoutManager: NSLayoutManager {
|
|
20
|
+
|
|
21
|
+
@objc public var markdownStyle: RCTMarcusStyle?
|
|
22
|
+
|
|
23
|
+
public override func drawBackground(
|
|
24
|
+
forGlyphRange glyphsToShow: NSRange,
|
|
25
|
+
at origin: CGPoint
|
|
26
|
+
) {
|
|
27
|
+
super.drawBackground(forGlyphRange: glyphsToShow, at: origin)
|
|
28
|
+
|
|
29
|
+
guard markdownStyle != nil, let textStorage else { return }
|
|
30
|
+
|
|
31
|
+
let characterRange = self.characterRange(
|
|
32
|
+
forGlyphRange: glyphsToShow,
|
|
33
|
+
actualGlyphRange: nil
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Before the ribbons: a quoted block's box would otherwise cover the bar
|
|
37
|
+
// drawn beside it.
|
|
38
|
+
drawCodeBlocks(in: textStorage, characterRange, at: origin)
|
|
39
|
+
drawBlockquoteRibbons(in: textStorage, glyphsToShow, at: origin)
|
|
40
|
+
drawMentions(in: textStorage, characterRange, at: origin)
|
|
41
|
+
drawListBullets(in: textStorage, characterRange, at: origin)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// MARK: - Code blocks
|
|
45
|
+
|
|
46
|
+
/// One box per fenced or indented block, drawn behind the whole of it.
|
|
47
|
+
///
|
|
48
|
+
/// The whole block at once rather than a rect per line: the fills of two
|
|
49
|
+
/// abutting lines each antialias their shared edge, and the pair of
|
|
50
|
+
/// half-covered rows reads as a seam across the box. It is the same reason
|
|
51
|
+
/// the ribbons below are drawn per run of lines rather than per line.
|
|
52
|
+
private func drawCodeBlocks(
|
|
53
|
+
in textStorage: NSTextStorage,
|
|
54
|
+
_ characterRange: NSRange,
|
|
55
|
+
at origin: CGPoint
|
|
56
|
+
) {
|
|
57
|
+
textStorage.enumerateAttribute(
|
|
58
|
+
.marcusCodeBlock,
|
|
59
|
+
in: characterRange,
|
|
60
|
+
options: []
|
|
61
|
+
) { value, range, _ in
|
|
62
|
+
guard let block = value as? MarkdownCodeBlock else { return }
|
|
63
|
+
|
|
64
|
+
// The visible range can start mid-block while scrolling, and a box drawn
|
|
65
|
+
// to that would round its corners in the middle of the code. Ask the
|
|
66
|
+
// storage for the block's whole extent instead.
|
|
67
|
+
var blockRange = range
|
|
68
|
+
_ = textStorage.attribute(
|
|
69
|
+
.marcusCodeBlock,
|
|
70
|
+
at: range.location,
|
|
71
|
+
longestEffectiveRange: &blockRange,
|
|
72
|
+
in: NSRange(location: 0, length: textStorage.length)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
drawCodeBlock(block, blockRange, at: origin)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private func drawCodeBlock(
|
|
80
|
+
_ block: MarkdownCodeBlock,
|
|
81
|
+
_ range: NSRange,
|
|
82
|
+
at origin: CGPoint
|
|
83
|
+
) {
|
|
84
|
+
let blockGlyphRange = glyphRange(
|
|
85
|
+
forCharacterRange: range,
|
|
86
|
+
actualCharacterRange: nil
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// Line fragment rects rather than the text inside them: a fragment spans
|
|
90
|
+
// the width the line was laid out in and carries the paragraph spacing that
|
|
91
|
+
// holds the box's own padding open, which is exactly the box.
|
|
92
|
+
var bounds = CGRect.null
|
|
93
|
+
var glyphIndex = blockGlyphRange.location
|
|
94
|
+
|
|
95
|
+
while glyphIndex < NSMaxRange(blockGlyphRange) {
|
|
96
|
+
var lineGlyphRange = NSRange()
|
|
97
|
+
let lineRect = lineFragmentRect(
|
|
98
|
+
forGlyphAt: glyphIndex,
|
|
99
|
+
effectiveRange: &lineGlyphRange
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// An empty effective range would spin here rather than crash, which is
|
|
103
|
+
// worse; bail instead.
|
|
104
|
+
guard lineGlyphRange.length > 0 else { break }
|
|
105
|
+
|
|
106
|
+
bounds = bounds.isNull ? lineRect : bounds.union(lineRect)
|
|
107
|
+
glyphIndex = NSMaxRange(lineGlyphRange)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
guard !bounds.isNull else { return }
|
|
111
|
+
|
|
112
|
+
// The insets are measured from the edge the block's lines begin at, so in a
|
|
113
|
+
// right-to-left block they swap sides: the padded one is the right, and the
|
|
114
|
+
// bare margin is all that is left on the far side.
|
|
115
|
+
let near = block.rightToLeft ? block.marginHorizontal : block.leadingInset
|
|
116
|
+
let far = block.rightToLeft ? block.leadingInset : block.marginHorizontal
|
|
117
|
+
|
|
118
|
+
let rect = CGRect(
|
|
119
|
+
x: origin.x + bounds.minX + near,
|
|
120
|
+
y: origin.y + bounds.minY + block.marginVertical,
|
|
121
|
+
width: bounds.width - near - far,
|
|
122
|
+
height: bounds.height - 2 * block.marginVertical
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
guard rect.width > 0, rect.height > 0 else { return }
|
|
126
|
+
|
|
127
|
+
let path = UIBezierPath(roundedRect: rect, cornerRadius: block.borderRadius)
|
|
128
|
+
block.color.setFill()
|
|
129
|
+
path.fill()
|
|
130
|
+
|
|
131
|
+
stroke(path, color: block.borderColor, width: block.borderWidth)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Draws a box's border on the inside of the box.
|
|
135
|
+
///
|
|
136
|
+
/// Clipped to the path and stroked at twice the width, so the outer half of
|
|
137
|
+
/// the stroke is trimmed away: a bordered box then covers exactly the room an
|
|
138
|
+
/// unbordered one does, which is what a border on an element does on the web
|
|
139
|
+
/// and what the Android spans draw.
|
|
140
|
+
private func stroke(_ path: UIBezierPath, color: UIColor?, width: CGFloat) {
|
|
141
|
+
guard let color, width > 0, let context = UIGraphicsGetCurrentContext() else {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
context.saveGState()
|
|
146
|
+
path.addClip()
|
|
147
|
+
color.setStroke()
|
|
148
|
+
path.lineWidth = width * 2
|
|
149
|
+
path.stroke()
|
|
150
|
+
context.restoreGState()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// MARK: - Blockquote ribbons
|
|
154
|
+
|
|
155
|
+
/// One ribbon per run of consecutive lines at the same nesting.
|
|
156
|
+
///
|
|
157
|
+
/// Walked a line fragment at a time rather than per attribute run, because
|
|
158
|
+
/// the line that opens a quote steps over a marker and the ones continuing it
|
|
159
|
+
/// do not, so consecutive lines share the attribute but not their indent.
|
|
160
|
+
/// Lines that do agree are then filled as a single rect: two abutting fills
|
|
161
|
+
/// at a fractional boundary each antialias their shared edge, and the pair of
|
|
162
|
+
/// half-covered rows reads as a seam across the bar.
|
|
163
|
+
private func drawBlockquoteRibbons(
|
|
164
|
+
in textStorage: NSTextStorage,
|
|
165
|
+
_ glyphsToShow: NSRange,
|
|
166
|
+
at origin: CGPoint
|
|
167
|
+
) {
|
|
168
|
+
guard let style = markdownStyle else { return }
|
|
169
|
+
|
|
170
|
+
var pending: (depth: Int, indent: CGFloat, rightToLeft: Bool, rect: CGRect)?
|
|
171
|
+
var didSetFill = false
|
|
172
|
+
var glyphIndex = glyphsToShow.location
|
|
173
|
+
|
|
174
|
+
func flush() {
|
|
175
|
+
guard let run = pending else { return }
|
|
176
|
+
pending = nil
|
|
177
|
+
|
|
178
|
+
if !didSetFill {
|
|
179
|
+
(style.blockquote.borderColor ?? .clear).setFill()
|
|
180
|
+
didSetFill = true
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Measured from the edge the line begins at, never from the text: a list
|
|
184
|
+
// nested inside the quote adds indent and a marker drawn in front of the
|
|
185
|
+
// ribbon takes some away, and either would split the bar. The line
|
|
186
|
+
// fragment spans the width the line was laid out in, so its two sides are
|
|
187
|
+
// the two candidate edges and the line's own direction picks one.
|
|
188
|
+
//
|
|
189
|
+
// Everything past that point is then a signed distance, the way Android
|
|
190
|
+
// writes the same arithmetic in `MarkdownBlockIndentSpan`: `step` carries
|
|
191
|
+
// the direction, and each bar is normalised into a rect at the end.
|
|
192
|
+
let step: CGFloat = run.rightToLeft ? -1 : 1
|
|
193
|
+
let edge = run.rightToLeft ? run.rect.maxX : run.rect.minX
|
|
194
|
+
let first =
|
|
195
|
+
origin.x + edge + step * (run.indent + style.blockquote.marginStart)
|
|
196
|
+
let radius = style.blockquote.borderRadius
|
|
197
|
+
|
|
198
|
+
for level in 0..<run.depth {
|
|
199
|
+
let near = first + step * CGFloat(level) * style.blockquoteShift
|
|
200
|
+
let far = near + step * style.blockquote.borderWidth
|
|
201
|
+
let bar = CGRect(
|
|
202
|
+
x: min(near, far),
|
|
203
|
+
y: origin.y + run.rect.minY,
|
|
204
|
+
width: style.blockquote.borderWidth,
|
|
205
|
+
height: run.rect.height
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if radius > 0 {
|
|
209
|
+
UIBezierPath(roundedRect: bar, cornerRadius: radius).fill()
|
|
210
|
+
} else {
|
|
211
|
+
UIRectFill(bar)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
while glyphIndex < NSMaxRange(glyphsToShow) {
|
|
217
|
+
var lineGlyphRange = NSRange()
|
|
218
|
+
let lineRect = lineFragmentRect(
|
|
219
|
+
forGlyphAt: glyphIndex,
|
|
220
|
+
effectiveRange: &lineGlyphRange
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
// An empty effective range would spin here rather than crash, which is
|
|
224
|
+
// worse; bail instead.
|
|
225
|
+
guard lineGlyphRange.length > 0 else { break }
|
|
226
|
+
|
|
227
|
+
let line = characterRange(
|
|
228
|
+
forGlyphRange: lineGlyphRange,
|
|
229
|
+
actualGlyphRange: nil
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if line.location < textStorage.length,
|
|
233
|
+
let depth = textStorage.attribute(
|
|
234
|
+
.marcusBlockquoteDepth,
|
|
235
|
+
at: line.location,
|
|
236
|
+
effectiveRange: nil
|
|
237
|
+
) as? NSNumber,
|
|
238
|
+
depth.intValue > 0
|
|
239
|
+
{
|
|
240
|
+
let indent = CGFloat(
|
|
241
|
+
(textStorage.attribute(
|
|
242
|
+
.marcusBlockquoteIndent,
|
|
243
|
+
at: line.location,
|
|
244
|
+
effectiveRange: nil
|
|
245
|
+
) as? NSNumber)?.doubleValue ?? 0
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
let rightToLeft =
|
|
249
|
+
textStorage.attribute(
|
|
250
|
+
.marcusRightToLeft,
|
|
251
|
+
at: line.location,
|
|
252
|
+
effectiveRange: nil
|
|
253
|
+
) != nil
|
|
254
|
+
|
|
255
|
+
// A line that turns round starts a new run even at the same depth and
|
|
256
|
+
// indent: its bars hang from the other side, so there is no single rect
|
|
257
|
+
// the two could share.
|
|
258
|
+
if let run = pending, run.depth == depth.intValue, run.indent == indent,
|
|
259
|
+
run.rightToLeft == rightToLeft
|
|
260
|
+
{
|
|
261
|
+
pending?.rect = run.rect.union(lineRect)
|
|
262
|
+
} else {
|
|
263
|
+
flush()
|
|
264
|
+
pending = (depth.intValue, indent, rightToLeft, lineRect)
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
flush()
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
glyphIndex = NSMaxRange(lineGlyphRange)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
flush()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// MARK: - Mention pills
|
|
277
|
+
|
|
278
|
+
private func drawMentions(
|
|
279
|
+
in textStorage: NSTextStorage,
|
|
280
|
+
_ characterRange: NSRange,
|
|
281
|
+
at origin: CGPoint
|
|
282
|
+
) {
|
|
283
|
+
guard let textContainer = textContainers.first else { return }
|
|
284
|
+
|
|
285
|
+
textStorage.enumerateAttribute(
|
|
286
|
+
.marcusTextBackground,
|
|
287
|
+
in: characterRange,
|
|
288
|
+
options: []
|
|
289
|
+
) { value, mentionRange, _ in
|
|
290
|
+
guard let background = value as? MarkdownTextBackground else { return }
|
|
291
|
+
|
|
292
|
+
drawMention(
|
|
293
|
+
background,
|
|
294
|
+
in: textStorage,
|
|
295
|
+
mentionRange,
|
|
296
|
+
textContainer: textContainer,
|
|
297
|
+
at: origin
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/// Fills one mention, a line fragment at a time.
|
|
303
|
+
///
|
|
304
|
+
/// Soft wrapping can split a mention, so each piece is filled on its own and
|
|
305
|
+
/// only the sides that actually terminate the mention are rounded -- the
|
|
306
|
+
/// break itself stays square, so the two halves read as one run.
|
|
307
|
+
private func drawMention(
|
|
308
|
+
_ background: MarkdownTextBackground,
|
|
309
|
+
in textStorage: NSTextStorage,
|
|
310
|
+
_ mentionRange: NSRange,
|
|
311
|
+
textContainer: NSTextContainer,
|
|
312
|
+
at origin: CGPoint
|
|
313
|
+
) {
|
|
314
|
+
let mentionGlyphRange = glyphRange(
|
|
315
|
+
forCharacterRange: mentionRange,
|
|
316
|
+
actualCharacterRange: nil
|
|
317
|
+
)
|
|
318
|
+
let radius = background.borderRadius
|
|
319
|
+
background.color.setFill()
|
|
320
|
+
|
|
321
|
+
var glyphIndex = mentionGlyphRange.location
|
|
322
|
+
|
|
323
|
+
while glyphIndex < NSMaxRange(mentionGlyphRange) {
|
|
324
|
+
var lineGlyphRange = NSRange()
|
|
325
|
+
let lineRect = lineFragmentRect(
|
|
326
|
+
forGlyphAt: glyphIndex,
|
|
327
|
+
effectiveRange: &lineGlyphRange
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
guard lineGlyphRange.length > 0 else { return }
|
|
331
|
+
|
|
332
|
+
let segment = NSIntersectionRange(lineGlyphRange, mentionGlyphRange)
|
|
333
|
+
|
|
334
|
+
guard segment.length > 0 else {
|
|
335
|
+
glyphIndex = NSMaxRange(lineGlyphRange)
|
|
336
|
+
continue
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let box = boundingRect(forGlyphRange: segment, in: textContainer)
|
|
340
|
+
let segmentCharacters = characterRange(
|
|
341
|
+
forGlyphRange: segment,
|
|
342
|
+
actualGlyphRange: nil
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
// The pill is a font-height box around the baseline, not the line
|
|
346
|
+
// fragment: an explicit `lineHeight` makes the fragment taller than the
|
|
347
|
+
// text, and a pill drawn to that height would swallow the gap between
|
|
348
|
+
// two lines.
|
|
349
|
+
if box.width > 0,
|
|
350
|
+
segmentCharacters.location < textStorage.length,
|
|
351
|
+
let font = textStorage.attribute(
|
|
352
|
+
.font,
|
|
353
|
+
at: segmentCharacters.location,
|
|
354
|
+
effectiveRange: nil
|
|
355
|
+
) as? UIFont
|
|
356
|
+
{
|
|
357
|
+
// Where the run's own ends fall on this line, which is what the kerning
|
|
358
|
+
// reserved space against and what the corners are rounded on.
|
|
359
|
+
let opens = segment.location == mentionGlyphRange.location
|
|
360
|
+
let closes = NSMaxRange(segment) == NSMaxRange(mentionGlyphRange)
|
|
361
|
+
let baseline = lineRect.minY + location(forGlyphAt: segment.location).y
|
|
362
|
+
let left = box.minX - (opens ? background.paddingHorizontal : 0)
|
|
363
|
+
let right = box.maxX - (closes ? background.marginHorizontal : 0)
|
|
364
|
+
let rect = CGRect(
|
|
365
|
+
x: origin.x + left,
|
|
366
|
+
y: origin.y + baseline - font.ascender - background.paddingVertical,
|
|
367
|
+
width: right - left,
|
|
368
|
+
height: font.lineHeight + 2 * background.paddingVertical
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
var corners: UIRectCorner = []
|
|
372
|
+
if opens {
|
|
373
|
+
corners.insert([.topLeft, .bottomLeft])
|
|
374
|
+
}
|
|
375
|
+
if closes {
|
|
376
|
+
corners.insert([.topRight, .bottomRight])
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
let path =
|
|
380
|
+
corners.isEmpty || radius <= 0
|
|
381
|
+
? UIBezierPath(rect: rect)
|
|
382
|
+
: UIBezierPath(
|
|
383
|
+
roundedRect: rect,
|
|
384
|
+
byRoundingCorners: corners,
|
|
385
|
+
cornerRadii: CGSize(width: radius, height: radius)
|
|
386
|
+
)
|
|
387
|
+
if rect.width > 0 {
|
|
388
|
+
path.fill()
|
|
389
|
+
stroke(path, color: background.borderColor, width: background.borderWidth)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
glyphIndex = NSMaxRange(lineGlyphRange)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// MARK: - List bullets
|
|
397
|
+
|
|
398
|
+
/// One circle per unordered list item, in the advance its marker character
|
|
399
|
+
/// was left holding.
|
|
400
|
+
///
|
|
401
|
+
/// `MarkdownFormatter` kerned that character to exactly the circle's width and
|
|
402
|
+
/// left its glyph undrawn, so the shape is the marker: it starts where the
|
|
403
|
+
/// glyph run starts and needs no measuring of its own. Only the height is a
|
|
404
|
+
/// question, and the answer is the line's -- a bullet marks the line rather
|
|
405
|
+
/// than sitting on its baseline, so it is centred on the line's own box the
|
|
406
|
+
/// way it is on every other platform.
|
|
407
|
+
private func drawListBullets(
|
|
408
|
+
in textStorage: NSTextStorage,
|
|
409
|
+
_ characterRange: NSRange,
|
|
410
|
+
at origin: CGPoint
|
|
411
|
+
) {
|
|
412
|
+
guard let textContainer = textContainers.first else { return }
|
|
413
|
+
|
|
414
|
+
textStorage.enumerateAttribute(
|
|
415
|
+
.marcusListBullet,
|
|
416
|
+
in: characterRange,
|
|
417
|
+
options: []
|
|
418
|
+
) { value, markerRange, _ in
|
|
419
|
+
guard let bullet = value as? MarkdownListBullet, bullet.diameter > 0 else {
|
|
420
|
+
return
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let markerGlyphRange = glyphRange(
|
|
424
|
+
forCharacterRange: markerRange,
|
|
425
|
+
actualCharacterRange: nil
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
guard markerGlyphRange.length > 0 else { return }
|
|
429
|
+
|
|
430
|
+
let box = boundingRect(forGlyphRange: markerGlyphRange, in: textContainer)
|
|
431
|
+
let line = lineFragmentUsedRect(
|
|
432
|
+
forGlyphAt: markerGlyphRange.location,
|
|
433
|
+
effectiveRange: nil
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
let rect = CGRect(
|
|
437
|
+
x: origin.x + box.minX,
|
|
438
|
+
y: origin.y + line.midY - bullet.diameter / 2,
|
|
439
|
+
width: bullet.diameter,
|
|
440
|
+
height: bullet.diameter
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
bullet.color.setFill()
|
|
444
|
+
UIBezierPath(ovalIn: rect).fill()
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
extension RCTMarcusStyle {
|
|
450
|
+
/// Width one level of blockquote nesting takes up: its own margin, the ribbon
|
|
451
|
+
/// itself, and the padding between the ribbon and the text.
|
|
452
|
+
var blockquoteShift: CGFloat {
|
|
453
|
+
blockquote.marginStart + blockquote.borderWidth + blockquote.paddingStart
|
|
454
|
+
}
|
|
455
|
+
}
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import UIKit
|
|
2
2
|
|
|
3
|
-
/// The rounded
|
|
3
|
+
/// The rounded box drawn behind a run of text: a mention's pill, or an inline
|
|
4
|
+
/// run of code.
|
|
4
5
|
///
|
|
5
6
|
/// Stored as an attributed-string attribute value under
|
|
6
7
|
/// `.marcusTextBackground`, so it stays an object rather than a struct.
|
|
8
|
+
///
|
|
9
|
+
/// The padding is the room between the glyphs and the box, the margin the room
|
|
10
|
+
/// between the box and the text either side of it. Horizontally both are held
|
|
11
|
+
/// open by the kerning `MarkdownFormatter` puts on the characters the run starts
|
|
12
|
+
/// and ends with, so all that is left when drawing is to place the edges inside
|
|
13
|
+
/// the space they reserved. Vertically only the padding is used, and the box
|
|
14
|
+
/// grows out of the font's own box into the line's spacing: a line cannot be
|
|
15
|
+
/// pushed apart for one run on it, so there is nothing a vertical margin could
|
|
16
|
+
/// hold open.
|
|
7
17
|
final class MarkdownTextBackground: NSObject {
|
|
8
18
|
var color: UIColor = .clear
|
|
19
|
+
var borderColor: UIColor?
|
|
20
|
+
var borderWidth: CGFloat = 0
|
|
9
21
|
var borderRadius: CGFloat = 0
|
|
22
|
+
var paddingHorizontal: CGFloat = 0
|
|
23
|
+
var paddingVertical: CGFloat = 0
|
|
24
|
+
var marginHorizontal: CGFloat = 0
|
|
10
25
|
}
|
|
11
26
|
|
|
12
27
|
/// A `MarkdownTextBackground` paired with the range it applies to. Only ever a
|
|
@@ -15,3 +30,46 @@ struct MarkdownTextBackgroundWithRange {
|
|
|
15
30
|
let textBackground: MarkdownTextBackground
|
|
16
31
|
let range: NSRange
|
|
17
32
|
}
|
|
33
|
+
|
|
34
|
+
/// The box drawn behind a fenced or indented code block.
|
|
35
|
+
///
|
|
36
|
+
/// Stored under `.marcusCodeBlock` over the block's own text, and read by both
|
|
37
|
+
/// drawing paths -- `MarkdownTextLayoutFragment` in an input,
|
|
38
|
+
/// `MarkdownParagraphLayoutManager` in a paragraph -- so that the two draw the
|
|
39
|
+
/// same box from the same numbers.
|
|
40
|
+
///
|
|
41
|
+
/// `leadingInset` is where the box's near edge sits, measured from the edge the
|
|
42
|
+
/// block's lines begin at: the indent they already carried, plus the horizontal
|
|
43
|
+
/// margin. It is worked out while formatting, where the indents are, rather
|
|
44
|
+
/// than rediscovered from a paragraph style at every frame.
|
|
45
|
+
///
|
|
46
|
+
/// Which edge that is comes from `rightToLeft`, taken from the block's first
|
|
47
|
+
/// line. One answer for the whole box rather than one per line, because the box
|
|
48
|
+
/// is one rect over all of them -- the same reason there is a single inset for
|
|
49
|
+
/// lines that could each have been indented differently.
|
|
50
|
+
final class MarkdownCodeBlock: NSObject {
|
|
51
|
+
var color: UIColor = .clear
|
|
52
|
+
var borderColor: UIColor?
|
|
53
|
+
var borderWidth: CGFloat = 0
|
|
54
|
+
var borderRadius: CGFloat = 0
|
|
55
|
+
var marginHorizontal: CGFloat = 0
|
|
56
|
+
var marginVertical: CGFloat = 0
|
|
57
|
+
var leadingInset: CGFloat = 0
|
|
58
|
+
var rightToLeft = false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// The bullet drawn in place of an unordered item's marker.
|
|
62
|
+
///
|
|
63
|
+
/// Stored under `.marcusListBullet` over the marker character itself, whose own
|
|
64
|
+
/// glyph is left undrawn: the character stays in the string -- it is what a
|
|
65
|
+
/// reader copies and what VoiceOver announces -- and the circle is drawn in the
|
|
66
|
+
/// advance it would have taken.
|
|
67
|
+
///
|
|
68
|
+
/// `diameter` comes from the base font size rather than from a length of its
|
|
69
|
+
/// own, so a list marks itself in proportion to the prose it marks. The marker's
|
|
70
|
+
/// advance is exactly this wide, which is what lets the circle be drawn from the
|
|
71
|
+
/// left edge of the glyph run and nothing else.
|
|
72
|
+
final class MarkdownListBullet: NSObject {
|
|
73
|
+
var color: UIColor = .clear
|
|
74
|
+
var diameter: CGFloat = 0
|
|
75
|
+
}
|
|
@@ -8,12 +8,12 @@ import UIKit
|
|
|
8
8
|
|
|
9
9
|
// Strong on purpose -- see MarkdownTextStorageDelegate.
|
|
10
10
|
private let textField: RCTUITextField
|
|
11
|
-
private let markdownUtils:
|
|
11
|
+
private let markdownUtils: MarkdownUtils
|
|
12
12
|
|
|
13
13
|
/// Guards against the KVO observer re-entering while we assign `attributedText`.
|
|
14
14
|
private var active = true
|
|
15
15
|
|
|
16
|
-
@objc public init(textField: RCTUITextField, markdownUtils:
|
|
16
|
+
@objc public init(textField: RCTUITextField, markdownUtils: MarkdownUtils)
|
|
17
17
|
{
|
|
18
18
|
self.textField = textField
|
|
19
19
|
self.markdownUtils = markdownUtils
|