From 72be2d35cca208d0b52b3c751ac01bf7a6bb28a8 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 18 Jan 2017 12:53:58 -0800 Subject: [PATCH] Add selectionColor prop for Text on Android Summary: **Motivation** Customizing the selection color allows to use brand colors in the app. The PR implements a `selectionColor` prop for `Text` component similar to `TextInput`. **Test plan (required)** Run UIExplorer example with the changes and verify everything works fine. ![image](https://cloud.githubusercontent.com/assets/1174278/22023258/70197d84-dceb-11e6-8662-2879d78d14d4.png) cc brentvatne Closes https://github.com/facebook/react-native/pull/11947 Differential Revision: D4430265 fbshipit-source-id: 462f16548d93ab03aadb27d6f12acf90842627ab --- Examples/UIExplorer/js/TextExample.android.js | 7 ++- Libraries/Text/Text.js | 47 ++++++++++++------- .../views/text/ReactTextViewManager.java | 19 ++++++-- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/Examples/UIExplorer/js/TextExample.android.js b/Examples/UIExplorer/js/TextExample.android.js index 54ef95ac9..2ac11e035 100644 --- a/Examples/UIExplorer/js/TextExample.android.js +++ b/Examples/UIExplorer/js/TextExample.android.js @@ -386,10 +386,15 @@ class TextExample extends React.Component { - + This text is selectable if you click-and-hold, and will offer the native Android selection menus. + + + This text will have a orange highlight on selection. + + This text contains an inline image . Neat, huh? diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index cb39fc79c..104578c5d 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -11,6 +11,7 @@ */ 'use strict'; +const ColorPropType = require('ColorPropType'); const EdgeInsetsPropType = require('EdgeInsetsPropType'); const NativeMethodsMixin = require('NativeMethodsMixin'); const Platform = require('Platform'); @@ -20,9 +21,12 @@ const StyleSheetPropType = require('StyleSheetPropType'); const TextStylePropTypes = require('TextStylePropTypes'); const Touchable = require('Touchable'); +const processColor = require('processColor'); const createReactNativeComponentClass = require('createReactNativeComponentClass'); const mergeFast = require('mergeFast'); +const { PropTypes } = React; + const stylePropType = StyleSheetPropType(TextStylePropTypes); const viewConfig = { @@ -32,6 +36,7 @@ const viewConfig = { ellipsizeMode: true, allowFontScaling: true, selectable: true, + selectionColor: true, adjustsFontSizeToFit: true, minimumFontScale: true, textBreakStrategy: true, @@ -109,7 +114,7 @@ const Text = React.createClass({ * * > `clip` is working only for iOS */ - ellipsizeMode: React.PropTypes.oneOf(['head', 'middle', 'tail', 'clip']), + ellipsizeMode: PropTypes.oneOf(['head', 'middle', 'tail', 'clip']), /** * Used to truncate the text with an ellipsis after computing the text * layout, including line wrapping, such that the total number of lines @@ -117,31 +122,31 @@ const Text = React.createClass({ * * This prop is commonly used with `ellipsizeMode`. */ - numberOfLines: React.PropTypes.number, + numberOfLines: PropTypes.number, /** * Set text break strategy on Android API Level 23+, possible values are `simple`, `highQuality`, `balanced` * The default value is `highQuality`. * @platform android */ - textBreakStrategy: React.PropTypes.oneOf(['simple', 'highQuality', 'balanced']), + textBreakStrategy: PropTypes.oneOf(['simple', 'highQuality', 'balanced']), /** * Invoked on mount and layout changes with * * `{nativeEvent: {layout: {x, y, width, height}}}` */ - onLayout: React.PropTypes.func, + onLayout: PropTypes.func, /** * This function is called on press. * * e.g., `onPress={() => console.log('1st')}`` */ - onPress: React.PropTypes.func, + onPress: PropTypes.func, /** * This function is called on long press. * * e.g., `onLongPress={this.increaseSize}>`` */ - onLongPress: React.PropTypes.func, + onLongPress: PropTypes.func, /** * When the scroll view is disabled, this defines how far your touch may * move off of the button, before deactivating the button. Once deactivated, @@ -153,24 +158,28 @@ const Text = React.createClass({ /** * Lets the user select text, to use the native copy and paste functionality. */ - selectable: React.PropTypes.bool, + selectable: PropTypes.bool, + /** + * The highlight color of the text. + * @platform android + */ + selectionColor: ColorPropType, /** * When `true`, no visual change is made when text is pressed down. By * default, a gray oval highlights the text on press down. - * * @platform ios */ - suppressHighlighting: React.PropTypes.bool, + suppressHighlighting: PropTypes.bool, style: stylePropType, /** * Used to locate this view in end-to-end tests. */ - testID: React.PropTypes.string, + testID: PropTypes.string, /** * Specifies whether fonts should scale to respect Text Size accessibility setting on iOS. The * default is `true`. */ - allowFontScaling: React.PropTypes.bool, + allowFontScaling: PropTypes.bool, /** * When set to `true`, indicates that the view is an accessibility element. The default value * for a `Text` element is `true`. @@ -179,18 +188,18 @@ const Text = React.createClass({ * [Accessibility guide](/react-native/docs/accessibility.html#accessible-ios-android) * for more information. */ - accessible: React.PropTypes.bool, + accessible: PropTypes.bool, /** * Specifies whether font should be scaled down automatically to fit given style constraints. * @platform ios */ - adjustsFontSizeToFit: React.PropTypes.bool, + adjustsFontSizeToFit: PropTypes.bool, /** * Specifies smallest possible scale a font can reach when adjustsFontSizeToFit is enabled. (values 0.01-1.0). * @platform ios */ - minimumFontScale: React.PropTypes.number, + minimumFontScale: PropTypes.number, }, getDefaultProps(): Object { return { @@ -210,10 +219,10 @@ const Text = React.createClass({ return {isInAParentText: true}; }, childContextTypes: { - isInAParentText: React.PropTypes.bool + isInAParentText: PropTypes.bool }, contextTypes: { - isInAParentText: React.PropTypes.bool + isInAParentText: PropTypes.bool }, /** * Only assigned if touch is needed. @@ -317,6 +326,12 @@ const Text = React.createClass({ isHighlighted: this.state.isHighlighted, }; } + if (newProps.selectionColor != null) { + newProps = { + ...newProps, + selectionColor: processColor(newProps.selectionColor) + }; + } if (Touchable.TOUCH_TARGET_DEBUG && newProps.onPress) { newProps = { ...newProps, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index 2e49a9048..09515a8fe 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -9,25 +9,25 @@ package com.facebook.react.views.text; -import javax.annotation.Nullable; - import android.text.Spannable; import android.text.TextUtils; import android.view.Gravity; import android.widget.TextView; -import com.facebook.yoga.YogaConstants; 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.annotations.ReactProp; 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.common.annotations.VisibleForTesting; +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}. @@ -96,6 +96,15 @@ public class ReactTextViewManager extends BaseViewManager