diff --git a/ios/EnrichedTextInputView.mm b/ios/EnrichedTextInputView.mm index 23178d5ef..445488690 100644 --- a/ios/EnrichedTextInputView.mm +++ b/ios/EnrichedTextInputView.mm @@ -980,8 +980,8 @@ - (void)manageSelectionBasedChanges { } // typing attributes for empty lines selection reset - NSString *currentString = [[textView.textStorage.string copy] stringByReplacingOccurrencesOfString:@"\u200B" withString:@""]; - if(textView.selectedRange.length == 0 && [_recentlyEmittedString isEqualToString: currentString] ) { + NSString *currentString = [textView.textStorage.string copy]; + if(textView.selectedRange.length == 0 && [_recentlyEmittedString isEqualToString:currentString]) { // no string change means only a selection changed with no character changes NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange:textView.selectedRange]; if( @@ -1012,15 +1012,15 @@ - (void)anyTextMayHaveBeenModified { return; } + // zero width space adding or removal + [ZeroWidthSpaceUtils handleZeroWidthSpacesInInput:self]; + // emptying input typing attributes management if(textView.textStorage.string.length == 0 && _recentlyEmittedString.length > 0) { // reset typing attribtues textView.typingAttributes = defaultTypingAttributes; } - // zero width space removal - [ZeroWidthSpaceUtils handleZeroWidthSpacesInInput:self]; - // inline code on newlines fix InlineCodeStyle *codeStyle = stylesDict[@([InlineCodeStyle getStyleType])]; if(codeStyle != nullptr) { diff --git a/ios/styles/BlockQuoteStyle.mm b/ios/styles/BlockQuoteStyle.mm index 71ade37ed..4c04cbb12 100644 --- a/ios/styles/BlockQuoteStyle.mm +++ b/ios/styles/BlockQuoteStyle.mm @@ -119,30 +119,19 @@ - (void)removeTypingAttributes { } - (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text { - if( - [self detectStyle:_input->textView.selectedRange] && - NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) && - [text isEqualToString:@""] - ) { - // removing first quote line by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange - // so we try guessing that a line should be deleted here + if([self detectStyle:_input->textView.selectedRange] && text.length == 0) { + // backspace while the style is active + NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange]; - [self removeAttributes:paragraphRange]; - return YES; - } else if( - [self detectStyle:_input->textView.selectedRange] && - [text isEqualToString:@""] - ) { - // other case; make sure removing all the (non newline) text from a quto line also removes the line itself - NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range]; - NSValue *nonNewlineVal = [ParagraphsUtils getNonNewlineRangesIn:_input->textView range:paragraphRange].firstObject; - if(nonNewlineVal == nullptr) { - return NO; - } - NSRange nonNewlineRange = [nonNewlineVal rangeValue]; - if(NSEqualRanges(range, nonNewlineRange)) { - [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES]; - [self removeAttributes:NSMakeRange(range.location, 0)]; + + if(NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0))) { + // a backspace on the very first input's line quote + // it doesn't run textVieDidChange so we need to manually remove attributes + [self removeAttributes:paragraphRange]; + return YES; + } else if(range.location == paragraphRange.location - 1) { + // same case in other lines; here, the removed range location will be exactly 1 less than paragraph range location + [self removeAttributes:paragraphRange]; return YES; } } diff --git a/ios/styles/OrderedListStyle.mm b/ios/styles/OrderedListStyle.mm index e4f9a5f5f..472118dfd 100644 --- a/ios/styles/OrderedListStyle.mm +++ b/ios/styles/OrderedListStyle.mm @@ -131,30 +131,19 @@ - (void)removeTypingAttributes { } - (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text { - if( - [self detectStyle:_input->textView.selectedRange] && - NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) && - [text isEqualToString:@""] - ) { - // removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange - // so we try guessing that a point should be deleted here + if([self detectStyle:_input->textView.selectedRange] && text.length == 0) { + // backspace while the style is active + NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange]; - [self removeAttributes:paragraphRange]; - return YES; - } else if( - [self detectStyle:_input->textView.selectedRange] && - [text isEqualToString:@""] - ) { - // other case; make sure removing all the (non newline) text from a list item also removes the item itself - NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range]; - NSValue *nonNewlineVal = [ParagraphsUtils getNonNewlineRangesIn:_input->textView range:paragraphRange].firstObject; - if(nonNewlineVal == nullptr) { - return NO; - } - NSRange nonNewlineRange = [nonNewlineVal rangeValue]; - if(NSEqualRanges(range, nonNewlineRange)) { - [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES]; - [self removeAttributes:NSMakeRange(range.location, 0)]; + + if(NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0))) { + // a backspace on the very first input's line list point + // it doesn't run textVieDidChange so we need to manually remove attributes + [self removeAttributes:paragraphRange]; + return YES; + } else if(range.location == paragraphRange.location - 1) { + // same case in other lines; here, the removed range location will be exactly 1 less than paragraph range location + [self removeAttributes:paragraphRange]; return YES; } } diff --git a/ios/styles/UnorderedListStyle.mm b/ios/styles/UnorderedListStyle.mm index 95270ca51..e161fc2d6 100644 --- a/ios/styles/UnorderedListStyle.mm +++ b/ios/styles/UnorderedListStyle.mm @@ -131,30 +131,19 @@ - (void)removeTypingAttributes { } - (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text { - if( - [self detectStyle:_input->textView.selectedRange] && - NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0)) && - [text isEqualToString:@""] - ) { - // removing first list point by backspacing doesn't remove typing attributes because it doesn't run textViewDidChange - // so we try guessing that a point should be deleted here + if([self detectStyle:_input->textView.selectedRange] && text.length == 0) { + // backspace while the style is active + NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange]; - [self removeAttributes:paragraphRange]; - return YES; - } else if( - [self detectStyle:_input->textView.selectedRange] && - [text isEqualToString:@""] - ) { - // other case; make sure removing all the (non newline) text from a list item also removes the item itself - NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:range]; - NSValue *nonNewlineVal = [ParagraphsUtils getNonNewlineRangesIn:_input->textView range:paragraphRange].firstObject; - if(nonNewlineVal == nullptr) { - return NO; - } - NSRange nonNewlineRange = [nonNewlineVal rangeValue]; - if(NSEqualRanges(range, nonNewlineRange)) { - [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:_input withSelection:YES]; - [self removeAttributes:NSMakeRange(range.location, 0)]; + + if(NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0))) { + // a backspace on the very first input's line list point + // it doesn't run textVieDidChange so we need to manually remove attributes + [self removeAttributes:paragraphRange]; + return YES; + } else if(range.location == paragraphRange.location - 1) { + // same case in other lines; here, the removed range location will be exactly 1 less than paragraph range location + [self removeAttributes:paragraphRange]; return YES; } } diff --git a/ios/utils/OccurenceUtils.mm b/ios/utils/OccurenceUtils.mm index 7c5b707bb..796282eb4 100644 --- a/ios/utils/OccurenceUtils.mm +++ b/ios/utils/OccurenceUtils.mm @@ -47,7 +47,7 @@ + (BOOL)detect NSRange attrRange = NSMakeRange(0, 0); attrValue = [input->textView.textStorage attribute:key atIndex:index effectiveRange:&attrRange]; } - return condition(attrValue, NSMakeRange(index, 0)); + return condition(attrValue, detectionRange); } + (BOOL)detectMultiple diff --git a/ios/utils/ParagraphAttributesUtils.mm b/ios/utils/ParagraphAttributesUtils.mm index 38b69c825..4cf63840d 100644 --- a/ios/utils/ParagraphAttributesUtils.mm +++ b/ios/utils/ParagraphAttributesUtils.mm @@ -1,5 +1,6 @@ #import "ParagraphAttributesUtils.h" #import "EnrichedTextInputView.h" +#import "StyleHeaders.h" #import "ParagraphsUtils.h" #import "TextInsertionUtils.h" @@ -10,6 +11,10 @@ @implementation ParagraphAttributesUtils // hence the solution - reset typing attributes + (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text input:(id)input { EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input; + UnorderedListStyle *ulStyle = typedInput->stylesDict[@([UnorderedListStyle getStyleType])]; + OrderedListStyle *olStyle = typedInput->stylesDict[@([OrderedListStyle getStyleType])]; + BlockQuoteStyle *bqStyle = typedInput->stylesDict[@([BlockQuoteStyle getStyleType])]; + if(typedInput == nullptr) { return NO; } @@ -21,6 +26,7 @@ + (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text i // find a non-newline range of the paragraph NSRange paragraphRange = [typedInput->textView.textStorage.string paragraphRangeForRange:range]; + NSArray *paragraphs = [ParagraphsUtils getNonNewlineRangesIn:typedInput->textView range:paragraphRange]; if(paragraphs.count == 0) { return NO; @@ -28,8 +34,26 @@ + (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text i NSRange nonNewlineRange = [(NSValue *)paragraphs.firstObject rangeValue]; - // if the backspace removes the whole content of a paragraph - do the thing - if(NSEqualRanges(nonNewlineRange, range)) { + // if the backspace removes the whole content of a paragraph (possibly more but has to start where the paragraph starts), we remove the typing attributes + if(range.location == nonNewlineRange.location && range.length >= nonNewlineRange.length) { + // for lists and quotes we want to remove the characters but keep attribtues so that a zero width space appears here + // so we do the removing manually and reapply attributes + if([ulStyle detectStyle:nonNewlineRange]) { + [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES]; + [ulStyle addAttributes:NSMakeRange(range.location, 0)]; + return YES; + } + if([olStyle detectStyle:nonNewlineRange]) { + [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES]; + [olStyle addAttributes:NSMakeRange(range.location, 0)]; + return YES; + } + if([bqStyle detectStyle:nonNewlineRange]) { + [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES]; + [bqStyle addAttributes:NSMakeRange(range.location, 0)]; + return YES; + } + // do the replacement manually [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES]; // reset typing attribtues