Skip to content

Commit

Permalink
feat(iOS): added lineBreakStrategy attribute to Text/TextInput (#31272)
Browse files Browse the repository at this point in the history
Summary:
iOS did not support the implementation of Korean word-wrap(line-break) before iOS14.
If the attribute applied, the word-wrap of Korean will works.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Added] -  Line break strategy for Text and TextInput components

Pull Request resolved: #31272

Test Plan:
1. Test build and run on above iOS 14.
2. Test it does not affect existing text components when set default(none) strategy.
3. Test whether word-wrap works with Korean when set hangul-word strategy.

<img src="https://user-images.githubusercontent.com/26326015/112963967-d7f70c00-9182-11eb-9a34-8c758b80c219.png" width="300" height="" style="max-width:100%;">

Reviewed By: javache

Differential Revision: D39824809

Pulled By: lunaleaps

fbshipit-source-id: 42cb0385221a38c84e80d3494d1bfc1934ecf32b
  • Loading branch information
bang9 authored and facebook-github-bot committed Oct 17, 2022
1 parent a0ee6fa commit 0481948
Show file tree
Hide file tree
Showing 19 changed files with 242 additions and 1 deletion.
1 change: 1 addition & 0 deletions Libraries/Components/TextInput/RCTTextInputViewConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const RCTTextInputViewConfig = {
clearTextOnFocus: true,
showSoftInputOnFocus: true,
autoFocus: true,
lineBreakStrategyIOS: true,
...ConditionallyIgnoredEventHandlers({
onChange: true,
onSelectionChange: true,
Expand Down
6 changes: 6 additions & 0 deletions Libraries/Components/TextInput/TextInput.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ type IOSProps = $ReadOnly<{|
* @platform ios
*/
textContentType?: ?TextContentType,

/**
* Set line break strategy on iOS.
* @platform ios
*/
lineBreakStrategyIOS?: ?('none' | 'standard' | 'hangul-word' | 'push-out'),
|}>;

type AndroidProps = $ReadOnly<{|
Expand Down
6 changes: 6 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ type IOSProps = $ReadOnly<{|
* @platform ios
*/
textContentType?: ?TextContentType,

/**
* Set line break strategy on iOS.
* @platform ios
*/
lineBreakStrategyIOS?: ?('none' | 'standard' | 'hangul-word' | 'push-out'),
|}>;

type AndroidProps = $ReadOnly<{|
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/BaseText/RCTBaseTextViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ - (RCTShadowView *)shadowView
RCT_REMAP_SHADOW_PROPERTY(lineHeight, textAttributes.lineHeight, CGFloat)
RCT_REMAP_SHADOW_PROPERTY(textAlign, textAttributes.alignment, NSTextAlignment)
RCT_REMAP_SHADOW_PROPERTY(writingDirection, textAttributes.baseWritingDirection, NSWritingDirection)
RCT_REMAP_SHADOW_PROPERTY(lineBreakStrategyIOS, textAttributes.lineBreakStrategy, NSLineBreakStrategy)
// Decoration
RCT_REMAP_SHADOW_PROPERTY(textDecorationColor, textAttributes.textDecorationColor, UIColor)
RCT_REMAP_SHADOW_PROPERTY(textDecorationStyle, textAttributes.textDecorationStyle, NSUnderlineStyle)
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/RCTTextAttributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
@property (nonatomic, assign) CGFloat lineHeight;
@property (nonatomic, assign) NSTextAlignment alignment;
@property (nonatomic, assign) NSWritingDirection baseWritingDirection;
@property (nonatomic, assign) NSLineBreakStrategy lineBreakStrategy;
// Decoration
@property (nonatomic, strong, nullable) UIColor *textDecorationColor;
@property (nonatomic, assign) NSUnderlineStyle textDecorationStyle;
Expand Down
11 changes: 10 additions & 1 deletion Libraries/Text/RCTTextAttributes.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ - (instancetype)init
_maxFontSizeMultiplier = NAN;
_alignment = NSTextAlignmentNatural;
_baseWritingDirection = NSWritingDirectionNatural;
_lineBreakStrategy = NSLineBreakStrategyNone;
_textShadowRadius = NAN;
_opacity = NAN;
_textTransform = RCTTextTransformUndefined;
Expand Down Expand Up @@ -66,6 +67,7 @@ - (void)applyTextAttributes:(RCTTextAttributes *)textAttributes
_baseWritingDirection = textAttributes->_baseWritingDirection != NSWritingDirectionNatural
? textAttributes->_baseWritingDirection
: _baseWritingDirection; // *
_lineBreakStrategy = textAttributes->_lineBreakStrategy ?: _lineBreakStrategy;

// Decoration
_textDecorationColor = textAttributes->_textDecorationColor ?: _textDecorationColor;
Expand Down Expand Up @@ -117,6 +119,13 @@ - (NSParagraphStyle *)effectiveParagraphStyle
isParagraphStyleUsed = YES;
}

if (_lineBreakStrategy != NSLineBreakStrategyNone) {
if (@available(iOS 14.0, *)) {
paragraphStyle.lineBreakStrategy = _lineBreakStrategy;
isParagraphStyleUsed = YES;
}
}

if (!isnan(_lineHeight)) {
CGFloat lineHeight = _lineHeight * self.effectiveFontSizeMultiplier;
paragraphStyle.minimumLineHeight = lineHeight;
Expand Down Expand Up @@ -318,7 +327,7 @@ - (BOOL)isEqual:(RCTTextAttributes *)textAttributes
RCTTextAttributesCompareFloats(_letterSpacing) &&
// Paragraph Styles
RCTTextAttributesCompareFloats(_lineHeight) && RCTTextAttributesCompareFloats(_alignment) &&
RCTTextAttributesCompareOthers(_baseWritingDirection) &&
RCTTextAttributesCompareOthers(_baseWritingDirection) && RCTTextAttributesCompareOthers(_lineBreakStrategy) &&
// Decoration
RCTTextAttributesCompareObjects(_textDecorationColor) && RCTTextAttributesCompareOthers(_textDecorationStyle) &&
RCTTextAttributesCompareOthers(_textDecorationLine) &&
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const textViewConfig = {
onInlineViewLayout: true,
dataDetectorType: true,
android_hyphenationFrequency: true,
lineBreakStrategyIOS: true,
},
directEventTypes: {
topTextLayout: {
Expand Down
7 changes: 7 additions & 0 deletions Libraries/Text/TextProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,11 @@ export type TextProps = $ReadOnly<{|
* See https://reactnative.dev/docs/text#supperhighlighting
*/
suppressHighlighting?: ?boolean,

/**
* Set line break strategy on iOS.
*
* See https://reactnative.dev/docs/text.html#linebreakstrategyios
*/
lineBreakStrategyIOS?: ?('none' | 'standard' | 'hangul-word' | 'push-out'),
|}>;
1 change: 1 addition & 0 deletions React/Base/RCTConvert.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ typedef NSURL RCTFileURL;
+ (NSTextAlignment)NSTextAlignment:(id)json;
+ (NSUnderlineStyle)NSUnderlineStyle:(id)json;
+ (NSWritingDirection)NSWritingDirection:(id)json;
+ (NSLineBreakStrategy)NSLineBreakStrategy:(id)json;
+ (UITextAutocapitalizationType)UITextAutocapitalizationType:(id)json;
+ (UITextFieldViewMode)UITextFieldViewMode:(id)json;
+ (UIKeyboardType)UIKeyboardType:(id)json;
Expand Down
19 changes: 19 additions & 0 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,25 @@ + (NSLocale *)NSLocale:(id)json
NSWritingDirectionNatural,
integerValue)

+ (NSLineBreakStrategy)NSLineBreakStrategy:(id)json RCT_DYNAMIC
{
if (@available(iOS 14.0, *)) {
static NSDictionary *mapping;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mapping = @{
@"none" : @(NSLineBreakStrategyNone),
@"standard" : @(NSLineBreakStrategyStandard),
@"hangul-word" : @(NSLineBreakStrategyHangulWordPriority),
@"push-out" : @(NSLineBreakStrategyPushOut)
};
});
return RCTConvertEnumValue("NSLineBreakStrategy", mapping, @(NSLineBreakStrategyNone), json).integerValue;
} else {
return NSLineBreakStrategyNone;
}
}

RCT_ENUM_CONVERTER(
UITextAutocapitalizationType,
(@{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
baseWritingDirection = textAttributes.baseWritingDirection.has_value()
? textAttributes.baseWritingDirection
: baseWritingDirection;
lineBreakStrategy = textAttributes.lineBreakStrategy.has_value()
? textAttributes.lineBreakStrategy
: lineBreakStrategy;

// Decoration
textDecorationColor = textAttributes.textDecorationColor
Expand Down Expand Up @@ -110,6 +113,7 @@ bool TextAttributes::operator==(const TextAttributes &rhs) const {
allowFontScaling,
alignment,
baseWritingDirection,
lineBreakStrategy,
textDecorationColor,
textDecorationLineType,
textDecorationStyle,
Expand All @@ -129,6 +133,7 @@ bool TextAttributes::operator==(const TextAttributes &rhs) const {
rhs.allowFontScaling,
rhs.alignment,
rhs.baseWritingDirection,
rhs.lineBreakStrategy,
rhs.textDecorationColor,
rhs.textDecorationLineType,
rhs.textDecorationStyle,
Expand Down Expand Up @@ -187,6 +192,7 @@ SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
debugStringConvertibleItem("lineHeight", lineHeight),
debugStringConvertibleItem("alignment", alignment),
debugStringConvertibleItem("baseWritingDirection", baseWritingDirection),
debugStringConvertibleItem("lineBreakStrategyIOS", lineBreakStrategy),

// Decoration
debugStringConvertibleItem("textDecorationColor", textDecorationColor),
Expand Down
2 changes: 2 additions & 0 deletions ReactCommon/react/renderer/attributedstring/TextAttributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class TextAttributes : public DebugStringConvertible {
Float lineHeight{std::numeric_limits<Float>::quiet_NaN()};
std::optional<TextAlignment> alignment{};
std::optional<WritingDirection> baseWritingDirection{};
std::optional<LineBreakStrategy> lineBreakStrategy{};

// Decoration
SharedColor textDecorationColor{};
Expand Down Expand Up @@ -121,6 +122,7 @@ struct hash<facebook::react::TextAttributes> {
textAttributes.lineHeight,
textAttributes.alignment,
textAttributes.baseWritingDirection,
textAttributes.lineBreakStrategy,
textAttributes.textDecorationColor,
textAttributes.textDecorationLineType,
textAttributes.textDecorationStyle,
Expand Down
56 changes: 56 additions & 0 deletions ReactCommon/react/renderer/attributedstring/conversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,52 @@ inline std::string toString(const WritingDirection &writingDirection) {
return "auto";
}

inline void fromRawValue(
const PropsParserContext &context,
const RawValue &value,
LineBreakStrategy &result) {
react_native_assert(value.hasType<std::string>());
if (value.hasType<std::string>()) {
auto string = (std::string)value;
if (string == "none") {
result = LineBreakStrategy::None;
} else if (string == "push-out") {
result = LineBreakStrategy::PushOut;
} else if (string == "hangul-word") {
result = LineBreakStrategy::HangulWordPriority;
} else if (string == "standard") {
result = LineBreakStrategy::Standard;
} else {
LOG(ERROR) << "Unsupported LineBreakStrategy value: " << string;
react_native_assert(false);
// sane default for prod
result = LineBreakStrategy::None;
}
return;
}

LOG(ERROR) << "Unsupported LineBreakStrategy type";
// sane default for prod
result = LineBreakStrategy::None;
}

inline std::string toString(const LineBreakStrategy &lineBreakStrategy) {
switch (lineBreakStrategy) {
case LineBreakStrategy::None:
return "none";
case LineBreakStrategy::PushOut:
return "push-out";
case LineBreakStrategy::HangulWordPriority:
return "hangul-word";
case LineBreakStrategy::Standard:
return "standard";
}

LOG(ERROR) << "Unsupported LineBreakStrategy value";
// sane default for prod
return "none";
}

inline void fromRawValue(
const PropsParserContext &context,
const RawValue &value,
Expand Down Expand Up @@ -873,6 +919,10 @@ inline folly::dynamic toDynamic(const TextAttributes &textAttributes) {
_textAttributes(
"baseWritingDirection", toString(*textAttributes.baseWritingDirection));
}
if (textAttributes.lineBreakStrategy.has_value()) {
_textAttributes(
"lineBreakStrategyIOS", toString(*textAttributes.lineBreakStrategy));
}
// Decoration
if (textAttributes.textDecorationColor) {
_textAttributes(
Expand Down Expand Up @@ -982,6 +1032,7 @@ constexpr static MapBuffer::Key TA_KEY_TEXT_SHADOW_COLOR = 19;
constexpr static MapBuffer::Key TA_KEY_IS_HIGHLIGHTED = 20;
constexpr static MapBuffer::Key TA_KEY_LAYOUT_DIRECTION = 21;
constexpr static MapBuffer::Key TA_KEY_ACCESSIBILITY_ROLE = 22;
constexpr static MapBuffer::Key TA_KEY_LINE_BREAK_STRATEGY = 23;

// constants for ParagraphAttributes serialization
constexpr static MapBuffer::Key PA_KEY_MAX_NUMBER_OF_LINES = 0;
Expand Down Expand Up @@ -1084,6 +1135,11 @@ inline MapBuffer toMapBuffer(const TextAttributes &textAttributes) {
TA_KEY_BEST_WRITING_DIRECTION,
toString(*textAttributes.baseWritingDirection));
}
if (textAttributes.lineBreakStrategy.has_value()) {
builder.putString(
TA_KEY_LINE_BREAK_STRATEGY,
toString(*textAttributes.lineBreakStrategy));
}
// Decoration
if (textAttributes.textDecorationColor) {
builder.putInt(
Expand Down
9 changes: 9 additions & 0 deletions ReactCommon/react/renderer/attributedstring/primitives.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ enum class WritingDirection {
RightToLeft // Right to left writing direction.
};

enum class LineBreakStrategy {
None, // Don't use any line break strategies
PushOut, // Use the push out line break strategy.
HangulWordPriority, // When specified, it prohibits breaking between Hangul
// characters.
Standard // Use the same configuration of line break strategies that the
// system uses for standard UI labels.
};

enum class TextDecorationLineType {
None,
Underline,
Expand Down
12 changes: 12 additions & 0 deletions ReactCommon/react/renderer/components/text/BaseTextProps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ static TextAttributes convertRawProp(
"baseWritingDirection",
sourceTextAttributes.baseWritingDirection,
defaultTextAttributes.baseWritingDirection);
textAttributes.lineBreakStrategy = convertRawProp(
context,
rawProps,
"lineBreakStrategyIOS",
sourceTextAttributes.lineBreakStrategy,
defaultTextAttributes.lineBreakStrategy);

// Decoration
textAttributes.textDecorationColor = convertRawProp(
Expand Down Expand Up @@ -243,6 +249,12 @@ void BaseTextProps::setProp(
textAttributes,
baseWritingDirection,
"baseWritingDirection");
REBUILD_FIELD_SWITCH_CASE(
defaults,
value,
textAttributes,
lineBreakStrategy,
"lineBreakStrategyIOS");
REBUILD_FIELD_SWITCH_CASE(
defaults,
value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
isParagraphStyleUsed = YES;
}

if (textAttributes.lineBreakStrategy.has_value()) {
paragraphStyle.lineBreakStrategy =
RCTNSLineBreakStrategyFromLineBreakStrategy(textAttributes.lineBreakStrategy.value());
isParagraphStyleUsed = YES;
}

if (!isnan(textAttributes.lineHeight)) {
CGFloat lineHeight = textAttributes.lineHeight * RCTEffectiveFontSizeMultiplierFromTextAttributes(textAttributes);
paragraphStyle.minimumLineHeight = lineHeight;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ inline static NSWritingDirection RCTNSWritingDirectionFromWritingDirection(Writi
}
}

inline static NSLineBreakStrategy RCTNSLineBreakStrategyFromLineBreakStrategy(LineBreakStrategy lineBreakStrategy)
{
switch (lineBreakStrategy) {
case LineBreakStrategy::None:
return NSLineBreakStrategyNone;
case LineBreakStrategy::PushOut:
return NSLineBreakStrategyPushOut;
case LineBreakStrategy::HangulWordPriority:
if (@available(iOS 14.0, *)) {
return NSLineBreakStrategyHangulWordPriority;
} else {
return NSLineBreakStrategyNone;
}
case LineBreakStrategy::Standard:
if (@available(iOS 14.0, *)) {
return NSLineBreakStrategyStandard;
} else {
return NSLineBreakStrategyNone;
}
}
}

inline static RCTFontStyle RCTFontStyleFromFontStyle(FontStyle fontStyle)
{
switch (fontStyle) {
Expand Down
Loading

0 comments on commit 0481948

Please sign in to comment.