-
Notifications
You must be signed in to change notification settings - Fork 95
[iOS & Android] Enable borderRadius in all mention types #720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 23 commits
0986812
195ad24
1b26ad4
521e78c
9d04008
5a7c73c
ddeba51
ac410ee
870b5da
99cf1fb
8b416a5
28b6a13
98dafda
a105c10
db307b2
8d5e9b2
bd8aaf3
2a5069e
cbab252
f1b8c74
46ad23f
fe24512
39e0318
0da8207
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The amount of different start and end variables is a bit overwhelming there 😓. Maybe let's add some general comments or something? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package com.expensify.livemarkdown.spans; | ||
|
|
||
| import android.graphics.Canvas; | ||
| import android.graphics.Paint; | ||
| import android.graphics.Path; | ||
| import android.graphics.RectF; | ||
| import android.text.StaticLayout; | ||
| import android.text.TextPaint; | ||
| import android.text.style.LineBackgroundSpan; | ||
|
|
||
| import androidx.annotation.ColorInt; | ||
| import androidx.annotation.NonNull; | ||
|
|
||
| public class MarkdownBackgroundSpan implements MarkdownSpan, LineBackgroundSpan { | ||
|
|
||
| private final int backgroundColor; | ||
| private final int mentionStart; | ||
| private final int mentionEnd; | ||
| private final float borderRadius; | ||
|
|
||
| private StaticLayout layout; | ||
| private Path backgroundPath; | ||
|
|
||
| public MarkdownBackgroundSpan(@ColorInt int backgroundColor, float borderRadius, int mentionStart, int mentionEnd) { | ||
| this.backgroundColor = backgroundColor; | ||
| this.borderRadius = borderRadius; | ||
| this.mentionStart = mentionStart; | ||
| this.mentionEnd = mentionEnd; | ||
| this.backgroundPath = new Path(); | ||
| } | ||
|
|
||
| @Override | ||
| public void drawBackground( | ||
| @NonNull Canvas canvas, | ||
| @NonNull Paint paint, | ||
| int left, | ||
| int right, | ||
| int top, | ||
| int baseline, | ||
| int bottom, | ||
| @NonNull CharSequence text, | ||
| int start, | ||
| int end, | ||
| int lnum | ||
| ) { | ||
| int lineStart = 0; | ||
| int lineEnd = end - start; | ||
| CharSequence lineText = text.subSequence(start, end); | ||
| if (layout == null || layout.getText() != lineText || layout.getWidth() != right || layout.getLineEnd(0) != lineEnd) { | ||
| // Create layout for the current line only | ||
| layout = StaticLayout.Builder.obtain(lineText, lineStart, lineEnd, (TextPaint) paint, right).build(); | ||
|
|
||
| int relativeMentionStart = mentionStart - start; | ||
| int relativeMentionEnd = mentionEnd - start; | ||
|
|
||
| boolean mentionStarts = lineStart <= relativeMentionStart; | ||
| boolean mentionEnds = lineEnd >= relativeMentionEnd; | ||
|
Comment on lines
+56
to
+57
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about renaming these variables to something for example like: |
||
|
|
||
| float startX = layout.getPrimaryHorizontal(mentionStarts ? relativeMentionStart: lineStart); | ||
| float endX = layout.getPrimaryHorizontal(mentionEnds ? relativeMentionEnd : lineEnd); | ||
|
|
||
| Paint.FontMetrics fm = paint.getFontMetrics(); | ||
| float startY = baseline + fm.ascent; | ||
| float endY = baseline + fm.descent; | ||
|
|
||
| RectF lineRect = new RectF(startX, startY, endX, endY); | ||
| backgroundPath.reset(); | ||
| backgroundPath.addRoundRect(lineRect, createRadii(mentionStarts, mentionEnds), Path.Direction.CW); | ||
| } | ||
|
|
||
| int originalColor = paint.getColor(); | ||
| paint.setColor(backgroundColor); | ||
|
|
||
| canvas.drawPath(backgroundPath, paint); | ||
|
|
||
| paint.setColor(originalColor); | ||
| } | ||
|
|
||
| private float[] createRadii(boolean roundedLeft, boolean roundedRight) { | ||
| float[] radii = new float[8]; | ||
|
|
||
| if (roundedLeft) { | ||
| radii[0] = radii[1] = borderRadius; // top-left | ||
| radii[6] = radii[7] = borderRadius; // bottom-left | ||
| } | ||
|
|
||
| if (roundedRight) { | ||
| radii[2] = radii[3] = borderRadius; // top-right | ||
| radii[4] = radii[5] = borderRadius; // bottom-right | ||
| } | ||
|
|
||
| return radii; | ||
| } | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| #import "MarkdownFormatter.h" | ||
| #import <React/RCTFont.h> | ||
| #import <RNLiveMarkdown/RCTMarkdownTextBackgroundUtils.h> | ||
|
|
||
| @implementation MarkdownFormatter | ||
|
|
||
|
|
@@ -91,14 +92,44 @@ - (void)applyRangeToAttributedString:(NSMutableAttributedString *)attributedStri | |
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.codeBackgroundColor range:range]; | ||
| } else if (type == "mention-here") { | ||
| [attributedString addAttribute:NSForegroundColorAttributeName value:markdownStyle.mentionHereColor range:range]; | ||
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionHereBackgroundColor range:range]; | ||
| if (@available(iOS 16.0, *)) { | ||
| RCTMarkdownTextBackground *textBackground = [[RCTMarkdownTextBackground alloc] init]; | ||
| textBackground.color = markdownStyle.mentionHereBackgroundColor; | ||
| textBackground.borderRadius = markdownStyle.mentionHereBorderRadius; | ||
|
|
||
| [attributedString addAttribute:RCTLiveMarkdownTextBackgroundAttributeName | ||
| value:textBackground | ||
| range:range]; | ||
| } else { | ||
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionHereBackgroundColor range:range]; | ||
| } | ||
|
Comment on lines
+95
to
+105
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that there is a lot of similar code. How about moving it to a separate function with textColor, backgroundColor, and borderRadius as props (etc)
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Helper function for applying mention-related attributes sounds good! |
||
| } else if (type == "mention-user") { | ||
| // TODO: change mention color when it mentions current user | ||
| [attributedString addAttribute:NSForegroundColorAttributeName value:markdownStyle.mentionUserColor range:range]; | ||
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionUserBackgroundColor range:range]; | ||
| if (@available(iOS 16.0, *)) { | ||
| RCTMarkdownTextBackground *textBackground = [[RCTMarkdownTextBackground alloc] init]; | ||
| textBackground.color = markdownStyle.mentionUserBackgroundColor; | ||
| textBackground.borderRadius = markdownStyle.mentionUserBorderRadius; | ||
|
|
||
| [attributedString addAttribute:RCTLiveMarkdownTextBackgroundAttributeName | ||
| value:textBackground | ||
| range:range]; | ||
| } else { | ||
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionUserBackgroundColor range:range]; | ||
| } | ||
| } else if (type == "mention-report") { | ||
| [attributedString addAttribute:NSForegroundColorAttributeName value:markdownStyle.mentionReportColor range:range]; | ||
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionReportBackgroundColor range:range]; | ||
| if (@available(iOS 16.0, *)) { | ||
| RCTMarkdownTextBackground *textBackground = [[RCTMarkdownTextBackground alloc] init]; | ||
| textBackground.color = markdownStyle.mentionReportBackgroundColor; | ||
| textBackground.borderRadius = markdownStyle.mentionReportBorderRadius; | ||
|
|
||
| [attributedString addAttribute:RCTLiveMarkdownTextBackgroundAttributeName | ||
| value:textBackground | ||
| range:range]; | ||
| } else { | ||
| [attributedString addAttribute:NSBackgroundColorAttributeName value:markdownStyle.mentionReportBackgroundColor range:range]; | ||
| } | ||
| } else if (type == "link") { | ||
| [attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range]; | ||
| [attributedString addAttribute:NSForegroundColorAttributeName value:markdownStyle.linkColor range:range]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,7 +77,7 @@ - (void)addTextInputObservers | |
| react_native_assert([childView isKindOfClass:[RCTTextInputComponentView class]] && "Child component of MarkdownTextInputDecoratorComponentView is not an instance of RCTTextInputComponentView."); | ||
| RCTTextInputComponentView *textInputComponentView = (RCTTextInputComponentView *)childView; | ||
| UIView<RCTBackedTextInputViewProtocol> *backedTextInputView = [textInputComponentView valueForKey:@"_backedTextInputView"]; | ||
|
|
||
| _observersAdded = true; | ||
|
|
||
| if ([backedTextInputView isKindOfClass:[RCTUITextField class]]) { | ||
|
|
@@ -100,6 +100,17 @@ - (void)addTextInputObservers | |
| // format initial value | ||
| [_markdownTextFieldObserver textFieldDidChange:_textField]; | ||
|
|
||
| if (@available(iOS 16.0, *)) { | ||
| NSTextStorage *textStorage = [_textField valueForKey:@"_textStorage"]; | ||
| NSTextContainer *textContainer = [_textField valueForKey:@"_textContainer"]; | ||
| NSTextLayoutManager *textLayoutManager = [textContainer valueForKey:@"_textLayoutManager"]; | ||
|
Comment on lines
+104
to
+106
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if accessing UITextField's private fields could cause some rejections on app store. We should probably verify that cc @tomekzaw any ideas on how to do that?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to obfuscate all the selectors here, e.g. |
||
|
|
||
| _markdownTextLayoutManagerDelegate = [[MarkdownTextLayoutManagerDelegate alloc] init]; | ||
| _markdownTextLayoutManagerDelegate.textStorage = textStorage; | ||
| _markdownTextLayoutManagerDelegate.markdownUtils = _markdownUtils; | ||
| textLayoutManager.delegate = _markdownTextLayoutManagerDelegate; | ||
| } | ||
|
|
||
| // TODO: register blockquotes layout manager | ||
| // https://github.com/Expensify/react-native-live-markdown/issues/87 | ||
| } else if ([backedTextInputView isKindOfClass:[RCTUITextView class]]) { | ||
|
|
@@ -229,7 +240,7 @@ - (void)prepareForRecycle | |
| { | ||
| react_native_assert(!_observersAdded && "MarkdownTextInputDecoratorComponentView was being recycled with TextInput observers still attached"); | ||
| [super prepareForRecycle]; | ||
|
|
||
| static const auto defaultProps = std::make_shared<const MarkdownTextInputDecoratorViewProps>(); | ||
| _props = defaultProps; | ||
| _markdownUtils = [[RCTMarkdownUtils alloc] init]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,18 @@ | ||
| #import <RNLiveMarkdown/RCTMarkdownUtils.h> | ||
| #import <RNLiveMarkdown/RCTMarkdownTextBackgroundUtils.h> | ||
| #import <UIKit/UIKit.h> | ||
|
|
||
| NS_ASSUME_NONNULL_BEGIN | ||
|
|
||
| API_AVAILABLE(ios(15.0)) | ||
| @interface BlockquoteTextLayoutFragment : NSTextLayoutFragment | ||
| @interface MarkdownTextLayoutFragment : NSTextLayoutFragment | ||
|
|
||
| @property (nonnull, atomic) RCTMarkdownUtils *markdownUtils; | ||
|
|
||
| @property NSUInteger depth; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At some point we might wanna rename |
||
|
|
||
| @property NSMutableArray<RCTMarkdownTextBackgroundWithRange *> *mentions; | ||
|
|
||
| @end | ||
|
|
||
| NS_ASSUME_NONNULL_END | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather keep the original value of
borderRadiusinMarkdownStyleand multiply it byscreenDensitynear the drawing logic.