Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,12 @@ export type NativeProps = $ReadOnly<{|
fontWeight?: ?string,
fontFamily?: ?string,

/**
* Taskade Specific Changes
*/
textDecorationColor?: ?ColorValue,
taskadeEditorInput?: ?boolean,

/**
* I cannot find where these are defined but JS complains without them.
*/
Expand Down Expand Up @@ -709,6 +715,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
borderLeftColor: {process: require('../../StyleSheet/processColor')},
borderTopLeftRadius: true,
borderTopColor: {process: require('../../StyleSheet/processColor')},
textDecorationColor: {process: require('../../StyleSheet/processColor')},
},
};

Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/TextInput/RCTTextInputViewConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const RCTTextInputViewConfig = {
editable: true,
inputAccessoryViewID: true,
caretHidden: true,
taskadeEditorInput: true,
enablesReturnKeyAutomatically: true,
placeholderTextColor: {process: require('../../StyleSheet/processColor')},
clearButtonMode: true,
Expand Down
5 changes: 5 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,11 @@ export type Props = $ReadOnly<{|
*/
caretHidden?: ?boolean,

/*
* If `true`, taskadeEditorInput is activated. The default value is `false`.
*/
taskadeEditorInput?: ?boolean,

/*
* If `true`, contextMenuHidden is hidden. The default value is `false`.
*/
Expand Down
2 changes: 1 addition & 1 deletion Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ export type ____TextStyle_InternalCore = $ReadOnly<{
| 'underline'
| 'line-through'
| 'underline line-through',
textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed',
textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed' | 'taskade',
textDecorationColor?: ____ColorValue_Internal,
textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase',
writingDirection?: 'auto' | 'ltr' | 'rtl',
Expand Down
3 changes: 2 additions & 1 deletion Libraries/Text/Text/RCTTextShadowView.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import <React/RCTBridge.h>
#import <React/RCTShadowView+Layout.h>
#import <React/RCTUIManager.h>
#import <React/TaskadeLayoutManager.h>
#import <yoga/Yoga.h>

#import "NSTextStorage+FontScaling.h"
Expand Down Expand Up @@ -232,7 +233,7 @@ - (NSTextStorage *)textStorageAndLayoutManagerThatFitsSize:(CGSize)size
_maximumNumberOfLines > 0 ? _lineBreakMode : NSLineBreakByClipping;
textContainer.maximumNumberOfLines = _maximumNumberOfLines;

NSLayoutManager *layoutManager = [NSLayoutManager new];
TaskadeLayoutManager *layoutManager = [TaskadeLayoutManager new];
layoutManager.usesFontLeading = NO;
[layoutManager addTextContainer:textContainer];

Expand Down
18 changes: 18 additions & 0 deletions Libraries/Text/Text/TaskadeLayoutManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright (c) Taskade
* A custom layout manager to support gap-less underlined text
*/

#import <UIKit/UIKit.h>

@interface TaskadeLayoutManager : NSLayoutManager

- (void)drawUnderlineForRect:(CGRect)rect;

- (void)drawUnderlineForGlyphRange:(NSRange)glyphRange
underlineType:(NSUnderlineStyle)underlineVal
baselineOffset:(CGFloat)baselineOffset
lineFragmentRect:(CGRect)lineRect
lineFragmentGlyphRange:(NSRange)lineGlyphRange
containerOrigin:(CGPoint)containerOrigin;
@end
37 changes: 37 additions & 0 deletions Libraries/Text/Text/TaskadeLayoutManager.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) Taskade
*/

#import "TaskadeLayoutManager.h"

@implementation TaskadeLayoutManager

- (void)drawUnderlineForRect:(CGRect)rect
{
UIBezierPath *path = [UIBezierPath new];
path.lineWidth = 3.5;
[path moveToPoint: CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect))];
[path addLineToPoint: CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect))];
[path stroke];
}

- (void)drawUnderlineForGlyphRange:(NSRange)glyphRange
underlineType:(NSUnderlineStyle)underlineVal
baselineOffset:(CGFloat)baselineOffset
lineFragmentRect:(CGRect)lineRect
lineFragmentGlyphRange:(NSRange)lineGlyphRange
containerOrigin:(CGPoint)containerOrigin
{
NSTextContainer *textContainer = [self textContainerForGlyphAtIndex:glyphRange.location effectiveRange: nil];
CGRect boundingRect = [self boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
CGRect offsetRect = CGRectOffset(boundingRect, containerOrigin.x, containerOrigin.y );
UIColor *color = [self.textStorage attribute:NSUnderlineColorAttributeName atIndex:glyphRange.location effectiveRange: nil];

if (color) {
[color setStroke];
}

[self drawUnderlineForRect:offsetRect];
}

@end
3 changes: 3 additions & 0 deletions Libraries/Text/TextInput/Multiline/RCTUITextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, assign) BOOL caretHidden;

//@Taskadev1 Editor input prop declaration
@property (nonatomic, assign) BOOL taskadeEditorInput;

@property (nonatomic, strong, nullable) NSString *inputAccessoryViewID;

@end
Expand Down
5 changes: 5 additions & 0 deletions Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRang
NSString *newText =
[_backedTextInputView.textInputDelegate textInputShouldChangeText:text inRange:range];

//@Taskadev1 Prevent enter for editor input
if (_backedTextInputView.taskadeEditorInput && [text isEqualToString:@"\n"]) {
return NO;
}

if (newText == nil) {
return NO;
}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) BOOL contextMenuHidden;
@property (nonatomic, assign, getter=isEditable) BOOL editable;
@property (nonatomic, assign) BOOL caretHidden;
@property (nonatomic, assign) BOOL taskadeEditorInput; //@Taskdev1 Editor input prop declaration
@property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
@property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/RCTBaseTextInputView.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) BOOL selectTextOnFocus;
@property (nonatomic, assign) BOOL clearTextOnFocus;
@property (nonatomic, assign) BOOL secureTextEntry;
@property (nonatomic, assign) BOOL taskadeEditorInput; //@Taskdev1 Editor input prop declaration
@property (nonatomic, copy) RCTTextSelection *selection;
@property (nonatomic, strong, nullable) NSNumber *maxLength;
@property (nonatomic, copy, nullable) NSAttributedString *attributedText;
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/RCTBaseTextInputViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ @implementation RCTBaseTextInputViewManager
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, backedTextInputView.scrollEnabled, BOOL)
RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL)
RCT_REMAP_VIEW_PROPERTY(taskadeEditorInput, backedTextInputView.taskadeEditorInput, BOOL) //@Taskdev1 Editor
RCT_EXPORT_VIEW_PROPERTY(autoFocus, BOOL)
RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL)
RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
Expand Down
1 change: 1 addition & 0 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ + (NSLocale *)NSLocale:(id)json
@"double" : @(NSUnderlineStyleDouble),
@"dotted" : @(NSUnderlinePatternDot | NSUnderlineStyleSingle),
@"dashed" : @(NSUnderlinePatternDash | NSUnderlineStyleSingle),
@"taskade": @(NSUnderlineStyleThick + NSUnderlineStyleSingle),
}),
NSUnderlineStyleSingle,
integerValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public class ViewProps {
public static final String TEXT_ALIGN = "textAlign";
public static final String TEXT_ALIGN_VERTICAL = "textAlignVertical";
public static final String TEXT_DECORATION_LINE = "textDecorationLine";
public static final String TEXT_DECORATION_COLOR = "textDecorationColor";
public static final String TEXT_BREAK_STRATEGY = "textBreakStrategy";
public static final String OPACITY = "opacity";
public static final String OVERFLOW = "overflow";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ private static void buildSpannedFromShadowNode(
textShadowNode.getThemedContext().getAssets())));
}
if (textShadowNode.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan(textShadowNode.mTextDecorationColor)));
}
if (textShadowNode.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactStrikethroughSpan()));
Expand Down Expand Up @@ -345,6 +345,7 @@ protected Spannable spannedFromShadowNode(
protected boolean mIncludeFontPadding = true;
protected boolean mAdjustsFontSizeToFit = false;
protected float mMinimumFontScale = 0;
protected @Nullable Integer mTextDecorationColor;

/**
* mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. mFontWeight can be {@link
Expand Down Expand Up @@ -496,6 +497,12 @@ public void setBackgroundColor(@Nullable Integer color) {
}
}

@ReactProp(name = ViewProps.TEXT_DECORATION_COLOR, customType = "Color")
public void setTextDecorationColor(@Nullable Integer color) {
mTextDecorationColor = color;
markUpdated();
}

@ReactProp(name = ViewProps.ACCESSIBILITY_ROLE)
public void setIsAccessibilityLink(@Nullable String accessibilityRole) {
if (isVirtual()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,53 @@

package com.facebook.react.views.text;

import android.os.Build;
import android.util.Log;
import android.text.TextPaint;
import android.text.style.UnderlineSpan;
import androidx.annotation.Nullable;

import java.lang.reflect.Method;

/*
* Wraps {@link UnderlineSpan} as a {@link ReactSpan}.
*/
public class ReactUnderlineSpan extends UnderlineSpan implements ReactSpan {}
public class ReactUnderlineSpan extends UnderlineSpan implements ReactSpan {
private final @Nullable Integer mUnderlineColor;

public ReactUnderlineSpan(@Nullable Integer color) {
mUnderlineColor = color;
}

@Override
public void updateDrawState(TextPaint textPaint) {
if (mUnderlineColor == null) {
super.updateDrawState(textPaint);
} else {
float mTaskadeUnderlineThickness = 8.0f;

/**
* Enables underline color support for TextPaint. The method is publicly available starting from API 29
* and was hidden before. We will have to resort to reflection techniques to ensure we can use this API.
* The API is defined here (as of this diff):
* https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android13-gsi/core/java/android/text/TextPaint.java
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
textPaint.underlineColor = mUnderlineColor;
textPaint.underlineThickness = mTaskadeUnderlineThickness;
} else {
try {
final Method method = TextPaint.class.getMethod("setUnderlineText", Integer.TYPE, Float.TYPE);
method.invoke(textPaint, mUnderlineColor, mTaskadeUnderlineThickness);
} catch (final Exception e) {
// Render default underline
textPaint.setUnderlineText(true);
}
}
}
}

public int getUnderlineColor() {
return mUnderlineColor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public class TextAttributeProps {
protected int mColor;
protected boolean mIsBackgroundColorSet = false;
protected int mBackgroundColor;
protected @Nullable Integer mTextDecorationColor = null;

protected int mNumberOfLines = UNSET;
protected int mFontSize = UNSET;
Expand Down Expand Up @@ -185,6 +186,7 @@ public static TextAttributeProps fromMapBuffer(MapBuffer props) {
case TA_KEY_BEST_WRITING_DIRECTION:
break;
case TA_KEY_TEXT_DECORATION_COLOR:
result.setTextDecorationColor(entry.getIntValue());
break;
case TA_KEY_TEXT_DECORATION_LINE:
result.setTextDecorationLine(entry.getStringValue());
Expand Down Expand Up @@ -241,6 +243,10 @@ public static TextAttributeProps fromReadableMap(ReactStylesDiffMap props) {
result.setTextDecorationLine(getStringProp(props, ViewProps.TEXT_DECORATION_LINE));
result.setTextShadowOffset(
props.hasKey(PROP_SHADOW_OFFSET) ? props.getMap(PROP_SHADOW_OFFSET) : null);
result.setTextDecorationColor(
props.hasKey(ViewProps.TEXT_DECORATION_COLOR)
? props.getInt(ViewProps.TEXT_DECORATION_COLOR, 0)
: null);
result.setTextShadowRadius(getFloatProp(props, PROP_SHADOW_RADIUS, 1));
result.setTextShadowColor(getIntProp(props, PROP_SHADOW_COLOR, DEFAULT_TEXT_SHADOW_COLOR));
result.setTextTransform(getStringProp(props, PROP_TEXT_TRANSFORM));
Expand Down Expand Up @@ -476,6 +482,10 @@ private void setTextDecorationLine(@Nullable String textDecorationLineString) {
}
}

private void setTextDecorationColor(@Nullable Integer color) {
mTextDecorationColor = color;
}

private void setTextShadowOffset(ReadableMap offsetMap) {
mTextShadowOffsetDx = 0;
mTextShadowOffsetDy = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ private static void buildSpannableFromFragment(
context.getAssets())));
}
if (textAttributes.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan(textAttributes.mTextDecorationColor)));
}
if (textAttributes.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactStrikethroughSpan()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private static void buildSpannableFromFragment(
context.getAssets())));
}
if (textAttributes.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan(textAttributes.mTextDecorationColor)));
}
if (textAttributes.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactStrikethroughSpan()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,16 @@ public class ReactEditText extends AppCompatEditText
private int mFontStyle = UNSET;
private boolean mAutoFocus = false;
private boolean mDidAttachToWindow = false;
private @Nullable Integer mTextDecorationColor = null;

private ReactViewBackgroundManager mReactBackgroundManager;

private final @Nullable FabricViewStateManager mFabricViewStateManager =
new FabricViewStateManager();
protected boolean mDisableTextDiffing = false;

// @Taskadev1 Editor input prop declaration
protected boolean mIsTaskadeEditor = false;
protected boolean mIsSettingTextFromState = false;

private static final KeyListener sKeyListener = QwertyKeyListener.getInstanceForFullKeyboard();
Expand Down Expand Up @@ -394,6 +397,11 @@ public void setOnKeyPress(boolean onKeyPress) {
mOnKeyPress = onKeyPress;
}

// @Taskadev1 Set editor input prop
public void setTaskadeEditorInput(boolean isTaskadeEditor) {
mIsTaskadeEditor = isTaskadeEditor;
}

public boolean getBlurOnSubmit() {
if (mBlurOnSubmit == null) {
// Default blurOnSubmit
Expand Down Expand Up @@ -780,7 +788,12 @@ private void restoreStyleEquivalentSpans(SpannableStringBuilder workingText) {
}

if ((getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0) {
workingText.setSpan(new ReactUnderlineSpan(), 0, workingText.length(), spanFlags);
workingText.setSpan(
new ReactUnderlineSpan(mTextDecorationColor),
0,
workingText.length(),
spanFlags
);
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Expand Down Expand Up @@ -1147,6 +1160,10 @@ public void setAutoFocus(boolean autoFocus) {
mAutoFocus = autoFocus;
}

public void setTextDecorationColor(@Nullable Integer color) {
mTextDecorationColor = color;
}

protected void applyTextAttributes() {
// In general, the `getEffective*` functions return `Float.NaN` if the
// property hasn't been set.
Expand Down
Loading