/
ReactTextViewManager.java
185 lines (161 loc) · 6.74 KB
/
ReactTextViewManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.text;
import android.text.Spannable;
import android.text.TextUtils;
import android.view.Gravity;
import android.widget.TextView;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.BaseViewManager;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.facebook.yoga.YogaConstants;
import javax.annotation.Nullable;
/**
* Manages instances of spannable {@link TextView}.
*
* This is a "shadowing" view manager, which means that the {@link NativeViewHierarchyManager} will
* not manage children of native {@link TextView} instances returned by this manager. Instead we use
* @{link ReactTextShadowNode} hierarchy to calculate a {@link Spannable} text representing the
* whole text subtree.
*/
@ReactModule(name = ReactTextViewManager.REACT_CLASS)
public class ReactTextViewManager extends BaseViewManager<ReactTextView, ReactTextShadowNode> {
@VisibleForTesting
public static final String REACT_CLASS = "RCTText";
private static final int[] SPACING_TYPES = {
Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM,
};
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactTextView createViewInstance(ThemedReactContext context) {
return new ReactTextView(context);
}
// maxLines can only be set in master view (block), doesn't really make sense to set in a span
@ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = ViewDefaults.NUMBER_OF_LINES)
public void setNumberOfLines(ReactTextView view, int numberOfLines) {
view.setNumberOfLines(numberOfLines);
}
@ReactProp(name = ViewProps.ELLIPSIZE_MODE)
public void setEllipsizeMode(ReactTextView view, @Nullable String ellipsizeMode) {
if (ellipsizeMode == null || ellipsizeMode.equals("tail")) {
view.setEllipsizeLocation(TextUtils.TruncateAt.END);
} else if (ellipsizeMode.equals("head")) {
view.setEllipsizeLocation(TextUtils.TruncateAt.START);
} else if (ellipsizeMode.equals("middle")) {
view.setEllipsizeLocation(TextUtils.TruncateAt.MIDDLE);
} else {
throw new JSApplicationIllegalArgumentException("Invalid ellipsizeMode: " + ellipsizeMode);
}
}
@ReactProp(name = ViewProps.TEXT_ALIGN_VERTICAL)
public void setTextAlignVertical(ReactTextView view, @Nullable String textAlignVertical) {
if (textAlignVertical == null || "auto".equals(textAlignVertical)) {
view.setGravityVertical(Gravity.NO_GRAVITY);
} else if ("top".equals(textAlignVertical)) {
view.setGravityVertical(Gravity.TOP);
} else if ("bottom".equals(textAlignVertical)) {
view.setGravityVertical(Gravity.BOTTOM);
} else if ("center".equals(textAlignVertical)) {
view.setGravityVertical(Gravity.CENTER_VERTICAL);
} else {
throw new JSApplicationIllegalArgumentException("Invalid textAlignVertical: " + textAlignVertical);
}
}
@ReactProp(name = "selectable")
public void setSelectable(ReactTextView view, boolean isSelectable) {
view.setTextIsSelectable(isSelectable);
}
@ReactProp(name = "selectionColor", customType = "Color")
public void setSelectionColor(ReactTextView view, @Nullable Integer color) {
if (color == null) {
view.setHighlightColor(DefaultStyleValuesUtil.getDefaultTextColorHighlight(view.getContext()));
} else {
view.setHighlightColor(color);
}
}
@ReactPropGroup(names = {
ViewProps.BORDER_RADIUS,
ViewProps.BORDER_TOP_LEFT_RADIUS,
ViewProps.BORDER_TOP_RIGHT_RADIUS,
ViewProps.BORDER_BOTTOM_RIGHT_RADIUS,
ViewProps.BORDER_BOTTOM_LEFT_RADIUS
}, defaultFloat = YogaConstants.UNDEFINED)
public void setBorderRadius(ReactTextView view, int index, float borderRadius) {
if (!YogaConstants.isUndefined(borderRadius)) {
borderRadius = PixelUtil.toPixelFromDIP(borderRadius);
}
if (index == 0) {
view.setBorderRadius(borderRadius);
} else {
view.setBorderRadius(borderRadius, index - 1);
}
}
@ReactProp(name = "borderStyle")
public void setBorderStyle(ReactTextView view, @Nullable String borderStyle) {
view.setBorderStyle(borderStyle);
}
@ReactPropGroup(names = {
ViewProps.BORDER_WIDTH,
ViewProps.BORDER_LEFT_WIDTH,
ViewProps.BORDER_RIGHT_WIDTH,
ViewProps.BORDER_TOP_WIDTH,
ViewProps.BORDER_BOTTOM_WIDTH,
}, defaultFloat = YogaConstants.UNDEFINED)
public void setBorderWidth(ReactTextView view, int index, float width) {
if (!YogaConstants.isUndefined(width)) {
width = PixelUtil.toPixelFromDIP(width);
}
view.setBorderWidth(SPACING_TYPES[index], width);
}
@ReactPropGroup(names = {
"borderColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor"
}, customType = "Color")
public void setBorderColor(ReactTextView view, int index, Integer color) {
float rgbComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int)color & 0x00FFFFFF);
float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int)color >>> 24);
view.setBorderColor(SPACING_TYPES[index], rgbComponent, alphaComponent);
}
@ReactProp(name = "includeFontPadding", defaultBoolean = true)
public void setIncludeFontPadding(ReactTextView view, boolean includepad) {
view.setIncludeFontPadding(includepad);
}
@Override
public void updateExtraData(ReactTextView view, Object extraData) {
ReactTextUpdate update = (ReactTextUpdate) extraData;
if (update.containsImages()) {
Spannable spannable = update.getText();
TextInlineImageSpan.possiblyUpdateInlineImageSpans(spannable, view);
}
view.setText(update);
}
@Override
public ReactTextShadowNode createShadowNodeInstance() {
return new ReactTextShadowNode();
}
@Override
public Class<ReactTextShadowNode> getShadowNodeClass() {
return ReactTextShadowNode.class;
}
@Override
protected void onAfterUpdateTransaction(ReactTextView view) {
super.onAfterUpdateTransaction(view);
view.updateView();
}
}