@fendent/react-native-enriched 0.5.2-fork.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +343 -0
- package/ReactNativeEnriched.podspec +31 -0
- package/android/build.gradle +106 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +197 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +72 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +434 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +391 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +173 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +833 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/lint.gradle +70 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/swmansion/enriched/ReactNativeEnrichedPackage.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/AsyncDrawable.kt +126 -0
- package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedConstants.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/common/ForceRedrawSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/common/GumboNormalizer.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +7 -0
- package/android/src/main/java/com/swmansion/enriched/common/ResourceManager.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +956 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +79 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBlockQuoteSpan.kt +53 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +92 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCodeBlockSpan.kt +81 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedImageSpan.kt +184 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedInlineCodeSpan.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedLinkSpan.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedMentionSpan.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt +79 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnorderedListSpan.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedBlockSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedHeadingSpan.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedParagraphSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputConnectionWrapper.kt +140 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +83 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +1120 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewLayoutManager.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewManager.kt +478 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +225 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +55 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeHtmlEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeSelectionEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeStateEvent.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeTextEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnContextMenuItemPressEvent.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputBlurEvent.kt +25 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputFocusEvent.kt +25 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputKeyPressEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnLinkDetectedEvent.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionDetectedEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnRequestHtmlResultEvent.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnSubmitEditingEvent.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedLineHeightSpan.kt +44 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +241 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt +372 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +164 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +263 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +434 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +394 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedEditableFactory.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +320 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +310 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +106 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +106 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt +107 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt +74 -0
- package/android/src/main/new_arch/CMakeLists.txt +62 -0
- package/android/src/main/new_arch/GumboNormalizerJni.cpp +14 -0
- package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
- package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +15 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputComponentDescriptor.h +35 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.cpp +53 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.h +25 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.cpp +35 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.h +53 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.cpp +9 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.h +24 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +27 -0
- package/android/src/main/res/drawable/broken_image.xml +10 -0
- package/cpp/CMakeLists.txt +50 -0
- package/cpp/GumboParser/GumboParser.h +34043 -0
- package/cpp/README.md +59 -0
- package/cpp/parser/GumboNormalizer.c +915 -0
- package/cpp/parser/GumboParser.cpp +16 -0
- package/cpp/parser/GumboParser.hpp +23 -0
- package/cpp/tests/GumboParserTest.cpp +457 -0
- package/ios/EnrichedTextInputView.h +53 -0
- package/ios/EnrichedTextInputView.mm +2360 -0
- package/ios/EnrichedTextInputViewManager.mm +13 -0
- package/ios/attributesManager/AttributesManager.h +17 -0
- package/ios/attributesManager/AttributesManager.mm +195 -0
- package/ios/config/InputConfig.h +104 -0
- package/ios/config/InputConfig.mm +664 -0
- package/ios/extensions/ColorExtension.h +7 -0
- package/ios/extensions/ColorExtension.mm +38 -0
- package/ios/extensions/FontExtension.h +11 -0
- package/ios/extensions/FontExtension.mm +72 -0
- package/ios/extensions/ImageExtension.h +34 -0
- package/ios/extensions/ImageExtension.mm +165 -0
- package/ios/extensions/LayoutManagerExtension.h +6 -0
- package/ios/extensions/LayoutManagerExtension.mm +443 -0
- package/ios/extensions/StringExtension.h +15 -0
- package/ios/extensions/StringExtension.mm +69 -0
- package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +434 -0
- package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +391 -0
- package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +173 -0
- package/ios/generated/ReactNativeEnrichedSpec/Props.h +833 -0
- package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +582 -0
- package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
- package/ios/generated/ReactNativeEnrichedSpec/States.cpp +16 -0
- package/ios/generated/ReactNativeEnrichedSpec/States.h +20 -0
- package/ios/inputParser/InputParser.h +11 -0
- package/ios/inputParser/InputParser.mm +1463 -0
- package/ios/inputTextView/InputTextView.h +6 -0
- package/ios/inputTextView/InputTextView.mm +285 -0
- package/ios/interfaces/AttributeEntry.h +9 -0
- package/ios/interfaces/AttributeEntry.mm +4 -0
- package/ios/interfaces/BaseStyleProtocol.h +17 -0
- package/ios/interfaces/ImageAttachment.h +11 -0
- package/ios/interfaces/ImageAttachment.mm +107 -0
- package/ios/interfaces/ImageData.h +10 -0
- package/ios/interfaces/ImageData.mm +4 -0
- package/ios/interfaces/LinkData.h +11 -0
- package/ios/interfaces/LinkData.mm +29 -0
- package/ios/interfaces/LinkRegexConfig.h +19 -0
- package/ios/interfaces/LinkRegexConfig.mm +37 -0
- package/ios/interfaces/MediaAttachment.h +23 -0
- package/ios/interfaces/MediaAttachment.mm +31 -0
- package/ios/interfaces/MentionParams.h +8 -0
- package/ios/interfaces/MentionParams.mm +4 -0
- package/ios/interfaces/MentionStyleProps.h +13 -0
- package/ios/interfaces/MentionStyleProps.mm +63 -0
- package/ios/interfaces/StyleBase.h +36 -0
- package/ios/interfaces/StyleBase.mm +256 -0
- package/ios/interfaces/StyleHeaders.h +102 -0
- package/ios/interfaces/StylePair.h +9 -0
- package/ios/interfaces/StylePair.mm +4 -0
- package/ios/interfaces/StyleTypeEnum.h +26 -0
- package/ios/interfaces/TextDecorationLineEnum.h +6 -0
- package/ios/interfaces/TextDecorationLineEnum.mm +4 -0
- package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.h +44 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +103 -0
- package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
- package/ios/internals/EnrichedTextInputViewState.h +22 -0
- package/ios/styles/BlockQuoteStyle.mm +55 -0
- package/ios/styles/BoldStyle.mm +37 -0
- package/ios/styles/CheckboxListStyle.mm +153 -0
- package/ios/styles/CodeBlockStyle.mm +49 -0
- package/ios/styles/H1Style.mm +20 -0
- package/ios/styles/H2Style.mm +20 -0
- package/ios/styles/H3Style.mm +20 -0
- package/ios/styles/H4Style.mm +20 -0
- package/ios/styles/H5Style.mm +20 -0
- package/ios/styles/H6Style.mm +20 -0
- package/ios/styles/HeadingStyleBase.mm +65 -0
- package/ios/styles/ImageStyle.mm +146 -0
- package/ios/styles/InlineCodeStyle.mm +65 -0
- package/ios/styles/ItalicStyle.mm +37 -0
- package/ios/styles/LinkStyle.mm +532 -0
- package/ios/styles/MentionStyle.mm +538 -0
- package/ios/styles/OrderedListStyle.mm +86 -0
- package/ios/styles/StrikethroughStyle.mm +25 -0
- package/ios/styles/UnderlineStyle.mm +24 -0
- package/ios/styles/UnorderedListStyle.mm +86 -0
- package/ios/utils/CheckboxHitTestUtils.h +10 -0
- package/ios/utils/CheckboxHitTestUtils.mm +122 -0
- package/ios/utils/DotReplacementUtils.h +10 -0
- package/ios/utils/DotReplacementUtils.mm +68 -0
- package/ios/utils/KeyboardUtils.h +7 -0
- package/ios/utils/KeyboardUtils.mm +31 -0
- package/ios/utils/OccurenceUtils.h +44 -0
- package/ios/utils/OccurenceUtils.mm +179 -0
- package/ios/utils/ParagraphAttributesUtils.h +15 -0
- package/ios/utils/ParagraphAttributesUtils.mm +257 -0
- package/ios/utils/RangeUtils.h +12 -0
- package/ios/utils/RangeUtils.mm +183 -0
- package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
- package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
- package/ios/utils/TextInsertionUtils.h +17 -0
- package/ios/utils/TextInsertionUtils.mm +64 -0
- package/ios/utils/WordsUtils.h +7 -0
- package/ios/utils/WordsUtils.mm +98 -0
- package/ios/utils/ZeroWidthSpaceUtils.h +9 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +270 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native/EnrichedTextInput.js +304 -0
- package/lib/module/native/EnrichedTextInput.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/spec/EnrichedTextInputNativeComponent.ts +517 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/EnrichedTextInputDefaultProps.js +12 -0
- package/lib/module/utils/EnrichedTextInputDefaultProps.js.map +1 -0
- package/lib/module/utils/normalizeHtmlStyle.js +155 -0
- package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
- package/lib/module/utils/nullthrows.js +9 -0
- package/lib/module/utils/nullthrows.js.map +1 -0
- package/lib/module/utils/regexParser.js +46 -0
- package/lib/module/utils/regexParser.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/native/EnrichedTextInput.d.ts +3 -0
- package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -0
- package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts +397 -0
- package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +447 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts +10 -0
- package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts.map +1 -0
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
- package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
- package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
- package/lib/typescript/src/utils/regexParser.d.ts +3 -0
- package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
- package/package.json +226 -0
- package/react-native.config.js +13 -0
- package/src/index.tsx +20 -0
- package/src/native/EnrichedTextInput.tsx +370 -0
- package/src/spec/EnrichedTextInputNativeComponent.ts +517 -0
- package/src/types.ts +499 -0
- package/src/utils/EnrichedTextInputDefaultProps.ts +9 -0
- package/src/utils/normalizeHtmlStyle.ts +199 -0
- package/src/utils/nullthrows.ts +7 -0
- package/src/utils/regexParser.ts +56 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#import "ParagraphAttributesUtils.h"
|
|
2
|
+
#import "EnrichedTextInputView.h"
|
|
3
|
+
#import "RangeUtils.h"
|
|
4
|
+
#import "StyleHeaders.h"
|
|
5
|
+
#import "TextInsertionUtils.h"
|
|
6
|
+
|
|
7
|
+
@implementation ParagraphAttributesUtils
|
|
8
|
+
|
|
9
|
+
// if the user backspaces the last character in a line, the iOS applies typing
|
|
10
|
+
// attributes from the previous line in the case of some paragraph styles it
|
|
11
|
+
// works especially bad when a list point just appears this method handles that
|
|
12
|
+
// case differently with or without present paragraph styles
|
|
13
|
+
+ (BOOL)handleBackspaceInRange:(NSRange)range
|
|
14
|
+
replacementText:(NSString *)text
|
|
15
|
+
input:(id)input {
|
|
16
|
+
EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
|
|
17
|
+
if (typedInput == nullptr) {
|
|
18
|
+
return NO;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// we make sure it was a backspace (text with 0 length) and it deleted
|
|
22
|
+
// something (range longer than 0)
|
|
23
|
+
if (text.length > 0 || range.length == 0) {
|
|
24
|
+
return NO;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// find a non-newline range of the paragraph
|
|
28
|
+
NSRange paragraphRange =
|
|
29
|
+
[typedInput->textView.textStorage.string paragraphRangeForRange:range];
|
|
30
|
+
|
|
31
|
+
NSArray *paragraphs = [RangeUtils getNonNewlineRangesIn:typedInput->textView
|
|
32
|
+
range:paragraphRange];
|
|
33
|
+
if (paragraphs.count == 0) {
|
|
34
|
+
return NO;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
NSRange nonNewlineRange = [(NSValue *)paragraphs.firstObject rangeValue];
|
|
38
|
+
CheckboxListStyle *cbLStyle =
|
|
39
|
+
typedInput->stylesDict[@([CheckboxListStyle getType])];
|
|
40
|
+
|
|
41
|
+
// the backspace removes the whole content of a paragraph (possibly more but
|
|
42
|
+
// has to start where the paragraph starts)
|
|
43
|
+
if (range.location == nonNewlineRange.location &&
|
|
44
|
+
range.length >= nonNewlineRange.length) {
|
|
45
|
+
|
|
46
|
+
// for styles that need ZWS (lists, quotes, etc.) we do the following:
|
|
47
|
+
// - manually do the removing
|
|
48
|
+
// - reset typing attributes so that the previous line styles don't get
|
|
49
|
+
// applied
|
|
50
|
+
// - reapply the paragraph style that was present so that a zero width space
|
|
51
|
+
// appears here
|
|
52
|
+
for (NSNumber *type in typedInput->stylesDict) {
|
|
53
|
+
StyleBase *style = typedInput->stylesDict[type];
|
|
54
|
+
if ([style needsZWS] && [style detect:nonNewlineRange]) {
|
|
55
|
+
BOOL isCurrentlyChecked = [cbLStyle getCheckboxStateAt:range.location];
|
|
56
|
+
[TextInsertionUtils replaceText:text
|
|
57
|
+
at:range
|
|
58
|
+
additionalAttributes:nullptr
|
|
59
|
+
input:typedInput
|
|
60
|
+
withSelection:YES];
|
|
61
|
+
typedInput->textView.typingAttributes =
|
|
62
|
+
typedInput->defaultTypingAttributes;
|
|
63
|
+
|
|
64
|
+
if (style == cbLStyle) {
|
|
65
|
+
[cbLStyle addWithChecked:isCurrentlyChecked
|
|
66
|
+
range:NSMakeRange(range.location, 0)
|
|
67
|
+
withTyping:YES
|
|
68
|
+
withDirtyRange:YES];
|
|
69
|
+
} else {
|
|
70
|
+
[style add:NSMakeRange(range.location, 0)
|
|
71
|
+
withTyping:YES
|
|
72
|
+
withDirtyRange:YES];
|
|
73
|
+
}
|
|
74
|
+
return YES;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// otherwise (no paragraph styles present), we just do the replacement
|
|
79
|
+
// manually and reset typing attribtues
|
|
80
|
+
[TextInsertionUtils replaceText:text
|
|
81
|
+
at:range
|
|
82
|
+
additionalAttributes:nullptr
|
|
83
|
+
input:typedInput
|
|
84
|
+
withSelection:YES];
|
|
85
|
+
typedInput->textView.typingAttributes = typedInput->defaultTypingAttributes;
|
|
86
|
+
return YES;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return NO;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Handles the specific case of backspacing a newline character, which results
|
|
94
|
+
* in merging two paragraphs.
|
|
95
|
+
*
|
|
96
|
+
* THE PROBLEM:
|
|
97
|
+
* When merging a bottom paragraph (Source) into a top paragraph (Destination),
|
|
98
|
+
* the bottom paragraph normally brings all its attributes with it. If the top
|
|
99
|
+
* paragraph is a restrictive style (like a CodeBlock), and the bottom paragraph
|
|
100
|
+
* contains a conflicting style (like an H1 Header), a standard merge would
|
|
101
|
+
* create an invalid state (e.g., a CodeBlock that is also a Header).
|
|
102
|
+
*
|
|
103
|
+
* THE SOLUTION:
|
|
104
|
+
* 1. Identifies the dominant style of the paragraph ABOVE the deleted newline
|
|
105
|
+
* (`leftParagraphStyle`).
|
|
106
|
+
* 2. Checks the paragraph BELOW the newline (`rightRange`) for any styles that
|
|
107
|
+
* conflict with or are blocked by the top style.
|
|
108
|
+
* 3. Explicitly removes those forbidden styles from the bottom paragraph
|
|
109
|
+
* *before* the merge occurs.
|
|
110
|
+
* 4. Performs the merge (deletes the newline).
|
|
111
|
+
*
|
|
112
|
+
* @return YES if the newline backspace was handled and sanitized; NO otherwise.
|
|
113
|
+
*/
|
|
114
|
+
+ (BOOL)handleParagraphStylesMergeOnBackspace:(NSRange)range
|
|
115
|
+
replacementText:(NSString *)text
|
|
116
|
+
input:(id)input {
|
|
117
|
+
EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
|
|
118
|
+
if (typedInput == nullptr) {
|
|
119
|
+
return NO;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Must be a backspace.
|
|
123
|
+
if (text.length > 0) {
|
|
124
|
+
return NO;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Backspace must have removed a newline character.
|
|
128
|
+
NSString *removedString =
|
|
129
|
+
[typedInput->textView.textStorage.string substringWithRange:range];
|
|
130
|
+
if ([removedString
|
|
131
|
+
rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]]
|
|
132
|
+
.location == NSNotFound) {
|
|
133
|
+
return NO;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
NSRange leftRange = [typedInput->textView.textStorage.string
|
|
137
|
+
paragraphRangeForRange:NSMakeRange(range.location, 0)];
|
|
138
|
+
|
|
139
|
+
StyleBase *leftParagraphStyle = nullptr;
|
|
140
|
+
for (NSNumber *key in typedInput->stylesDict) {
|
|
141
|
+
StyleBase *style = typedInput->stylesDict[key];
|
|
142
|
+
if ([style isParagraph] && [style detect:leftRange]) {
|
|
143
|
+
leftParagraphStyle = style;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (leftParagraphStyle == nullptr) {
|
|
148
|
+
return NO;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// index out of bounds
|
|
152
|
+
NSUInteger rightRangeStart = range.location + range.length;
|
|
153
|
+
if (rightRangeStart >= typedInput->textView.textStorage.string.length) {
|
|
154
|
+
return NO;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
NSRange rightRange = [typedInput->textView.textStorage.string
|
|
158
|
+
paragraphRangeForRange:NSMakeRange(rightRangeStart, 1)];
|
|
159
|
+
|
|
160
|
+
StyleType type = [[leftParagraphStyle class] getType];
|
|
161
|
+
|
|
162
|
+
NSArray *conflictingStyles = [typedInput
|
|
163
|
+
getPresentStyleTypesFrom:typedInput->conflictingStyles[@(type)]
|
|
164
|
+
range:rightRange];
|
|
165
|
+
NSArray *blockingStyles =
|
|
166
|
+
[typedInput getPresentStyleTypesFrom:typedInput->blockingStyles[@(type)]
|
|
167
|
+
range:rightRange];
|
|
168
|
+
NSArray *allToBeRemoved =
|
|
169
|
+
[conflictingStyles arrayByAddingObjectsFromArray:blockingStyles];
|
|
170
|
+
|
|
171
|
+
for (NSNumber *style in allToBeRemoved) {
|
|
172
|
+
StyleBase *styleToRemove = typedInput->stylesDict[style];
|
|
173
|
+
|
|
174
|
+
// for ranges, we need to remove each occurence
|
|
175
|
+
NSArray<StylePair *> *allOccurences = [styleToRemove all:rightRange];
|
|
176
|
+
|
|
177
|
+
for (StylePair *pair in allOccurences) {
|
|
178
|
+
[styleToRemove remove:[pair.rangeValue rangeValue] withDirtyRange:YES];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
[TextInsertionUtils replaceText:text
|
|
183
|
+
at:range
|
|
184
|
+
additionalAttributes:nullptr
|
|
185
|
+
input:typedInput
|
|
186
|
+
withSelection:YES];
|
|
187
|
+
return YES;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Resets typing attributes to defaults when the cursor lands on an empty line
|
|
192
|
+
* after a deletion.
|
|
193
|
+
*
|
|
194
|
+
* This override is necessary because `UITextView` automatically inherits
|
|
195
|
+
* attributes from the preceding newline. This prevents scenarios where a
|
|
196
|
+
* restrictive style (like CodeBlock) "leaks" into the next empty paragraph.
|
|
197
|
+
*/
|
|
198
|
+
+ (BOOL)handleResetTypingAttributesOnBackspace:(NSRange)range
|
|
199
|
+
replacementText:(NSString *)text
|
|
200
|
+
input:(id)input {
|
|
201
|
+
EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
|
|
202
|
+
if (typedInput == nullptr) {
|
|
203
|
+
return NO;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
NSString *storageString = typedInput->textView.textStorage.string;
|
|
207
|
+
|
|
208
|
+
if (text.length > 0 || range.location >= storageString.length) {
|
|
209
|
+
return NO;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
unichar firstCharToDelete = [storageString characterAtIndex:range.location];
|
|
213
|
+
if (![[NSCharacterSet newlineCharacterSet]
|
|
214
|
+
characterIsMember:firstCharToDelete]) {
|
|
215
|
+
return NO;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
NSRange leftParagraphRange =
|
|
219
|
+
[storageString paragraphRangeForRange:NSMakeRange(range.location, 0)];
|
|
220
|
+
BOOL isLeftLineEmpty = [self isParagraphEmpty:leftParagraphRange
|
|
221
|
+
inString:storageString];
|
|
222
|
+
|
|
223
|
+
BOOL isRightLineEmpty = YES;
|
|
224
|
+
NSUInteger rightRangeStart = range.location + range.length;
|
|
225
|
+
|
|
226
|
+
if (rightRangeStart < storageString.length) {
|
|
227
|
+
NSRange rightParagraphRange =
|
|
228
|
+
[storageString paragraphRangeForRange:NSMakeRange(rightRangeStart, 0)];
|
|
229
|
+
isRightLineEmpty = [self isParagraphEmpty:rightParagraphRange
|
|
230
|
+
inString:storageString];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (isLeftLineEmpty && isRightLineEmpty) {
|
|
234
|
+
[TextInsertionUtils replaceText:text
|
|
235
|
+
at:range
|
|
236
|
+
additionalAttributes:nullptr
|
|
237
|
+
input:typedInput
|
|
238
|
+
withSelection:YES];
|
|
239
|
+
|
|
240
|
+
typedInput->textView.typingAttributes = typedInput->defaultTypingAttributes;
|
|
241
|
+
return YES;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return NO;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
+ (BOOL)isParagraphEmpty:(NSRange)range inString:(NSString *)string {
|
|
248
|
+
if (range.length == 0)
|
|
249
|
+
return YES;
|
|
250
|
+
|
|
251
|
+
NSString *paragraphText = [string substringWithRange:range];
|
|
252
|
+
NSString *trimmed = [paragraphText
|
|
253
|
+
stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
|
254
|
+
return trimmed.length == 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import <UIKit/UIKit.h>
|
|
3
|
+
|
|
4
|
+
@interface RangeUtils : NSObject
|
|
5
|
+
+ (NSArray *)getSeparateParagraphsRangesIn:(UITextView *)textView
|
|
6
|
+
range:(NSRange)range;
|
|
7
|
+
+ (NSArray *)getNonNewlineRangesIn:(UITextView *)textView range:(NSRange)range;
|
|
8
|
+
+ (NSArray *)connectAndDedupeRanges:(NSArray *)ranges;
|
|
9
|
+
+ (NSArray *)shiftRanges:(NSArray *)ranges
|
|
10
|
+
withEditedRange:(NSRange)editedRange
|
|
11
|
+
changeInLength:(NSInteger)delta;
|
|
12
|
+
@end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#import "RangeUtils.h"
|
|
2
|
+
|
|
3
|
+
@implementation RangeUtils
|
|
4
|
+
|
|
5
|
+
+ (NSArray *)getSeparateParagraphsRangesIn:(UITextView *)textView
|
|
6
|
+
range:(NSRange)range {
|
|
7
|
+
// just in case, get full paragraphs range
|
|
8
|
+
NSRange fullRange =
|
|
9
|
+
[textView.textStorage.string paragraphRangeForRange:range];
|
|
10
|
+
|
|
11
|
+
// we are in an empty paragraph
|
|
12
|
+
if (fullRange.length == 0) {
|
|
13
|
+
return @[ [NSValue valueWithRange:fullRange] ];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
NSMutableArray *results = [[NSMutableArray alloc] init];
|
|
17
|
+
|
|
18
|
+
NSInteger lastStart = fullRange.location;
|
|
19
|
+
for (int i = int(fullRange.location);
|
|
20
|
+
i < fullRange.location + fullRange.length; i++) {
|
|
21
|
+
unichar currentChar = [textView.textStorage.string characterAtIndex:i];
|
|
22
|
+
if ([[NSCharacterSet newlineCharacterSet] characterIsMember:currentChar]) {
|
|
23
|
+
NSRange paragraphRange = [textView.textStorage.string
|
|
24
|
+
paragraphRangeForRange:NSMakeRange(lastStart, i - lastStart)];
|
|
25
|
+
[results addObject:[NSValue valueWithRange:paragraphRange]];
|
|
26
|
+
lastStart = i + 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (lastStart < fullRange.location + fullRange.length) {
|
|
31
|
+
NSRange paragraphRange = [textView.textStorage.string
|
|
32
|
+
paragraphRangeForRange:NSMakeRange(lastStart, fullRange.location +
|
|
33
|
+
fullRange.length -
|
|
34
|
+
lastStart)];
|
|
35
|
+
[results addObject:[NSValue valueWithRange:paragraphRange]];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
+ (NSArray *)getNonNewlineRangesIn:(UITextView *)textView range:(NSRange)range {
|
|
42
|
+
NSMutableArray *nonNewlineRanges = [[NSMutableArray alloc] init];
|
|
43
|
+
int lastRangeLocation = int(range.location);
|
|
44
|
+
|
|
45
|
+
for (int i = int(range.location); i < range.location + range.length; i++) {
|
|
46
|
+
unichar currentChar = [textView.textStorage.string characterAtIndex:i];
|
|
47
|
+
if ([[NSCharacterSet newlineCharacterSet] characterIsMember:currentChar]) {
|
|
48
|
+
if (i - lastRangeLocation > 0) {
|
|
49
|
+
[nonNewlineRanges
|
|
50
|
+
addObject:[NSValue
|
|
51
|
+
valueWithRange:NSMakeRange(lastRangeLocation,
|
|
52
|
+
i - lastRangeLocation)]];
|
|
53
|
+
}
|
|
54
|
+
lastRangeLocation = i + 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (lastRangeLocation < range.location + range.length) {
|
|
58
|
+
[nonNewlineRanges
|
|
59
|
+
addObject:[NSValue
|
|
60
|
+
valueWithRange:NSMakeRange(lastRangeLocation,
|
|
61
|
+
range.location + range.length -
|
|
62
|
+
lastRangeLocation)]];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return nonNewlineRanges;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Condenses an array of NSRange to make sure the overlapping ones are connected
|
|
69
|
+
// + sorted based on NSRange.location
|
|
70
|
+
+ (NSArray *)connectAndDedupeRanges:(NSArray *)ranges {
|
|
71
|
+
if (ranges.count == 0) {
|
|
72
|
+
return @[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// We sort primarily by location. If locations match, shorter length goes
|
|
76
|
+
// first
|
|
77
|
+
NSArray<NSValue *> *sortedRanges =
|
|
78
|
+
[ranges sortedArrayUsingComparator:^NSComparisonResult(NSValue *obj1,
|
|
79
|
+
NSValue *obj2) {
|
|
80
|
+
NSRange range1 = obj1.rangeValue;
|
|
81
|
+
NSRange range2 = obj2.rangeValue;
|
|
82
|
+
|
|
83
|
+
if (range1.location < range2.location)
|
|
84
|
+
return NSOrderedAscending;
|
|
85
|
+
if (range1.location > range2.location)
|
|
86
|
+
return NSOrderedDescending;
|
|
87
|
+
|
|
88
|
+
if (range1.length < range2.length)
|
|
89
|
+
return NSOrderedAscending;
|
|
90
|
+
if (range1.length > range2.length)
|
|
91
|
+
return NSOrderedDescending;
|
|
92
|
+
|
|
93
|
+
return NSOrderedSame;
|
|
94
|
+
}];
|
|
95
|
+
|
|
96
|
+
NSMutableArray<NSValue *> *mergedRanges = [[NSMutableArray alloc] init];
|
|
97
|
+
|
|
98
|
+
// We work by comparing each two ranges.
|
|
99
|
+
// If we connected some ranges, the newly created one is still compared with
|
|
100
|
+
// the next ranges from the sorted list.
|
|
101
|
+
NSRange currentRange = sortedRanges[0].rangeValue;
|
|
102
|
+
|
|
103
|
+
for (NSUInteger i = 1; i < sortedRanges.count; i++) {
|
|
104
|
+
NSRange nextRange = sortedRanges[i].rangeValue;
|
|
105
|
+
|
|
106
|
+
// Calculate the end points
|
|
107
|
+
NSUInteger currentMax = currentRange.location + currentRange.length;
|
|
108
|
+
NSUInteger nextMax = nextRange.location + nextRange.length;
|
|
109
|
+
|
|
110
|
+
// If next range starts before (or exactly when) the current one ends.
|
|
111
|
+
if (nextRange.location <= currentMax) {
|
|
112
|
+
// Merge them; the new end is the maximum of the two ends
|
|
113
|
+
NSUInteger newMax = MAX(currentMax, nextMax);
|
|
114
|
+
currentRange.length = newMax - currentRange.location;
|
|
115
|
+
} else {
|
|
116
|
+
// No overlap; push the current range and start a new one
|
|
117
|
+
[mergedRanges addObject:[NSValue valueWithRange:currentRange]];
|
|
118
|
+
currentRange = nextRange;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Add the final range
|
|
123
|
+
[mergedRanges addObject:[NSValue valueWithRange:currentRange]];
|
|
124
|
+
|
|
125
|
+
return [mergedRanges copy];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Updates a list of NSRanges based on a text change, so that they are still
|
|
129
|
+
// pointing to the same characters. editedRange is the post-change range of the
|
|
130
|
+
// edited fragment. delta tells what is the length change of the edited
|
|
131
|
+
// fragment. While the ranges outside of the change are being just shifted, the
|
|
132
|
+
// ones intersecting with it are just merging with the change.
|
|
133
|
+
+ (NSArray *)shiftRanges:(NSArray *)ranges
|
|
134
|
+
withEditedRange:(NSRange)editedRange
|
|
135
|
+
changeInLength:(NSInteger)delta {
|
|
136
|
+
NSMutableArray *result = [[NSMutableArray alloc] init];
|
|
137
|
+
|
|
138
|
+
// Calculate what the changed range was like before being edited
|
|
139
|
+
NSUInteger oldEditLength = editedRange.length - delta;
|
|
140
|
+
NSUInteger oldEditEnd = editedRange.location + oldEditLength;
|
|
141
|
+
|
|
142
|
+
NSUInteger newEditEnd = editedRange.location + editedRange.length;
|
|
143
|
+
|
|
144
|
+
for (NSValue *value in ranges) {
|
|
145
|
+
NSRange range = [value rangeValue];
|
|
146
|
+
NSUInteger rangeEnd = range.location + range.length;
|
|
147
|
+
|
|
148
|
+
if (rangeEnd <= editedRange.location) {
|
|
149
|
+
// Range was strictly before the old edit range.
|
|
150
|
+
// Do nothing.
|
|
151
|
+
[result addObject:value];
|
|
152
|
+
} else if (range.location >= oldEditEnd) {
|
|
153
|
+
// Range was strictly after the old edit range.
|
|
154
|
+
// Shift it by the delta.
|
|
155
|
+
[result
|
|
156
|
+
addObject:[NSValue valueWithRange:NSMakeRange(range.location + delta,
|
|
157
|
+
range.length)]];
|
|
158
|
+
} else {
|
|
159
|
+
// Range overlaps the old edit range in some way.
|
|
160
|
+
// Our best bet is to merge it with the edit range.
|
|
161
|
+
|
|
162
|
+
NSUInteger newStart = MIN(range.location, editedRange.location);
|
|
163
|
+
|
|
164
|
+
NSUInteger newEnd;
|
|
165
|
+
if (rangeEnd <= oldEditEnd) {
|
|
166
|
+
// The range was inside the editedRange before.
|
|
167
|
+
// So we use the newer editRange as the end here.
|
|
168
|
+
newEnd = newEditEnd;
|
|
169
|
+
} else {
|
|
170
|
+
// The range sticked outside of the editedRange before.
|
|
171
|
+
// It is safe to shift its end and use it.
|
|
172
|
+
newEnd = rangeEnd + delta;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
NSRange adjustedRange = NSMakeRange(newStart, newEnd - newStart);
|
|
176
|
+
[result addObject:[NSValue valueWithRange:adjustedRange]];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
typedef NS_ENUM(NSInteger, TextBlockTapKind) {
|
|
2
|
+
TextBlockTapKindNone = 0,
|
|
3
|
+
TextBlockTapKindCheckbox,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
@class EnrichedTextInputView;
|
|
7
|
+
|
|
8
|
+
@interface TextBlockTapGestureRecognizer : UITapGestureRecognizer
|
|
9
|
+
- (instancetype _Nonnull)initWithInput:(id _Nonnull)input
|
|
10
|
+
action:(SEL _Nonnull)action;
|
|
11
|
+
|
|
12
|
+
@property(nonatomic, weak) EnrichedTextInputView *input;
|
|
13
|
+
|
|
14
|
+
@property(nonatomic, assign, readonly) TextBlockTapKind tapKind;
|
|
15
|
+
@property(nonatomic, assign, readonly) NSInteger characterIndex;
|
|
16
|
+
|
|
17
|
+
@end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#import "TextBlockTapGestureRecognizer.h"
|
|
2
|
+
#import "CheckboxHitTestUtils.h"
|
|
3
|
+
#import "EnrichedTextInputView.h"
|
|
4
|
+
|
|
5
|
+
@implementation TextBlockTapGestureRecognizer {
|
|
6
|
+
TextBlockTapKind _tapKind;
|
|
7
|
+
NSInteger _characterIndex;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
- (instancetype)initWithInput:(id)input action:(SEL)action {
|
|
11
|
+
self = [super initWithTarget:input action:action];
|
|
12
|
+
_input = input;
|
|
13
|
+
|
|
14
|
+
self.cancelsTouchesInView = YES;
|
|
15
|
+
self.delaysTouchesBegan = YES;
|
|
16
|
+
self.delaysTouchesEnded = YES;
|
|
17
|
+
|
|
18
|
+
for (UIGestureRecognizer *gr in _input->textView.gestureRecognizers) {
|
|
19
|
+
[gr requireGestureRecognizerToFail:self];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return self;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
- (TextBlockTapKind)tapKind {
|
|
26
|
+
return _tapKind;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
- (NSInteger)characterIndex {
|
|
30
|
+
return _characterIndex;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
|
34
|
+
_tapKind = TextBlockTapKindNone;
|
|
35
|
+
_characterIndex = NSNotFound;
|
|
36
|
+
|
|
37
|
+
if (!self.input) {
|
|
38
|
+
self.state = UIGestureRecognizerStateFailed;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
UITouch *touch = touches.anyObject;
|
|
43
|
+
CGPoint point = [touch locationInView:self.input->textView];
|
|
44
|
+
NSInteger checkboxIndex =
|
|
45
|
+
[CheckboxHitTestUtils hitTestCheckboxAtPoint:point inInput:self.input];
|
|
46
|
+
|
|
47
|
+
if (checkboxIndex >= 0) {
|
|
48
|
+
_tapKind = TextBlockTapKindCheckbox;
|
|
49
|
+
_characterIndex = checkboxIndex;
|
|
50
|
+
[super touchesBegan:touches withEvent:event];
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
self.state = UIGestureRecognizerStateFailed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
@interface TextInsertionUtils : NSObject
|
|
4
|
+
+ (void)insertText:(NSString *)text
|
|
5
|
+
at:(NSInteger)index
|
|
6
|
+
additionalAttributes:
|
|
7
|
+
(NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
|
|
8
|
+
input:(id)input
|
|
9
|
+
withSelection:(BOOL)withSelection;
|
|
10
|
+
+ (void)replaceText:(NSString *)text
|
|
11
|
+
at:(NSRange)range
|
|
12
|
+
additionalAttributes:
|
|
13
|
+
(NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
|
|
14
|
+
input:(id)input
|
|
15
|
+
withSelection:(BOOL)withSelection;
|
|
16
|
+
;
|
|
17
|
+
@end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#import "TextInsertionUtils.h"
|
|
2
|
+
#import "EnrichedTextInputView.h"
|
|
3
|
+
#import "UIView+React.h"
|
|
4
|
+
|
|
5
|
+
@implementation TextInsertionUtils
|
|
6
|
+
+ (void)insertText:(NSString *)text
|
|
7
|
+
at:(NSInteger)index
|
|
8
|
+
additionalAttributes:
|
|
9
|
+
(NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
|
|
10
|
+
input:(id)input
|
|
11
|
+
withSelection:(BOOL)withSelection {
|
|
12
|
+
EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
|
|
13
|
+
if (typedInput == nullptr) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
UITextView *textView = typedInput->textView;
|
|
18
|
+
|
|
19
|
+
NSMutableDictionary<NSAttributedStringKey, id> *copiedAttrs =
|
|
20
|
+
[textView.typingAttributes mutableCopy];
|
|
21
|
+
if (additionalAttrs != nullptr) {
|
|
22
|
+
[copiedAttrs addEntriesFromDictionary:additionalAttrs];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
NSAttributedString *newAttrStr =
|
|
26
|
+
[[NSAttributedString alloc] initWithString:text attributes:copiedAttrs];
|
|
27
|
+
[textView.textStorage insertAttributedString:newAttrStr atIndex:index];
|
|
28
|
+
|
|
29
|
+
if (withSelection) {
|
|
30
|
+
if (![textView isFirstResponder]) {
|
|
31
|
+
[textView reactFocus];
|
|
32
|
+
}
|
|
33
|
+
textView.selectedRange = NSMakeRange(index + text.length, 0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
+ (void)replaceText:(NSString *)text
|
|
38
|
+
at:(NSRange)range
|
|
39
|
+
additionalAttributes:
|
|
40
|
+
(NSDictionary<NSAttributedStringKey, id> *)additionalAttrs
|
|
41
|
+
input:(id)input
|
|
42
|
+
withSelection:(BOOL)withSelection {
|
|
43
|
+
EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input;
|
|
44
|
+
if (typedInput == nullptr) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
UITextView *textView = typedInput->textView;
|
|
49
|
+
|
|
50
|
+
[textView.textStorage replaceCharactersInRange:range withString:text];
|
|
51
|
+
if (additionalAttrs != nullptr) {
|
|
52
|
+
[textView.textStorage
|
|
53
|
+
addAttributes:additionalAttrs
|
|
54
|
+
range:NSMakeRange(range.location, [text length])];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (withSelection) {
|
|
58
|
+
if (![textView isFirstResponder]) {
|
|
59
|
+
[textView reactFocus];
|
|
60
|
+
}
|
|
61
|
+
textView.selectedRange = NSMakeRange(range.location + text.length, 0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
@end
|