Skip to content
Merged
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
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "implements selectionColor",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
41 changes: 40 additions & 1 deletion packages/playground/Samples/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,42 @@ export default class Bootstrap extends React.Component {
</Text>
</View>

<View style={styles.selectionColorTestContainer}>
<Text style={styles.sectionTitle}>Selection Color Test</Text>
<Text
selectable={true}
selectionColor="red"
style={styles.selectableText}>
Red selection color - Select this text to see red highlight!
</Text>
<Text
selectable={true}
selectionColor="#00FF00"
style={styles.selectableText}>
Green selection color (#00FF00) - Select this text!
</Text>
<Text
selectable={true}
selectionColor="rgba(255, 165, 0, 0.5)"
style={styles.selectableText}>
Orange with 50% opacity - Select this text!
</Text>
<Text
selectable={true}
selectionColor="blue"
style={styles.selectableText}>
Blue selection color - Select this text!
</Text>
<Text selectable={true} style={styles.selectableText}>
Default selection color (no selectionColor prop) - Uses theme
default.
</Text>
</View>

<View
style={styles.container2}
accessible={true}
accessibilityLabel="Annotation Checkc"
accessibilityLabel="Annotation Check"
accessibilityAnnotation={{
typeID: 'Comment',
typeName: 'Check Comment',
Expand Down Expand Up @@ -88,6 +120,13 @@ const styles = StyleSheet.create({
borderRadius: 8,
width: 400,
},
selectionColorTestContainer: {
backgroundColor: '#fff0f5',
padding: 15,
marginVertical: 10,
borderRadius: 8,
width: 400,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ void ParagraphComponentView::updateProps(
m_requireRedraw = true;
}

if (oldViewProps.selectionColor != newViewProps.selectionColor) {
m_requireRedraw = true;
}

Super::updateProps(props, oldProps);
}

Expand Down Expand Up @@ -454,9 +458,14 @@ void ParagraphComponentView::DrawSelectionHighlight(
return;
}

// TODO: use prop selectionColor if provided
winrt::com_ptr<ID2D1SolidColorBrush> selectionBrush;
const D2D1_COLOR_F selectionColor = theme()->D2DPlatformColor("Highlight@40");
D2D1_COLOR_F selectionColor;
const auto &props = paragraphProps();
if (props.selectionColor) {
selectionColor = theme()->D2DColor(**props.selectionColor);
} else {
selectionColor = theme()->D2DPlatformColor("Highlight@40");
}
hr = renderTarget.CreateSolidColorBrush(selectionColor, selectionBrush.put());

if (FAILED(hr)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "BaseParagraphProps.h"

#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/attributedstring/conversions.h>
#include <react/renderer/attributedstring/primitives.h>
#include <react/renderer/core/propsConversions.h>
#include <react/renderer/debug/debugStringConvertibleUtils.h>

#include <glog/logging.h>

namespace facebook::react {

BaseParagraphProps::BaseParagraphProps(
const PropsParserContext& context,
const BaseParagraphProps& sourceProps,
const RawProps& rawProps)
: ViewProps(context, sourceProps, rawProps),
BaseTextProps(context, sourceProps, rawProps),
paragraphAttributes(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.paragraphAttributes
: convertRawProp(
context,
rawProps,
sourceProps.paragraphAttributes,
{})),
isSelectable(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.isSelectable
: convertRawProp(
context,
rawProps,
"selectable",
sourceProps.isSelectable,
false)),
onTextLayout(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.onTextLayout
: convertRawProp(
context,
rawProps,
"onTextLayout",
sourceProps.onTextLayout,
{})),
// [Windows]
selectionColor(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.selectionColor
: convertRawProp(
context,
rawProps,
"selectionColor",
sourceProps.selectionColor,
{})) {
/*
* These props are applied to `View`, therefore they must not be a part of
* base text attributes.
*/
textAttributes.opacity = std::numeric_limits<Float>::quiet_NaN();
textAttributes.backgroundColor = {};
};

void BaseParagraphProps::setProp(
const PropsParserContext& context,
RawPropsPropNameHash hash,
const char* propName,
const RawValue& value) {
// All Props structs setProp methods must always, unconditionally,
// call all super::setProp methods, since multiple structs may
// reuse the same values.
ViewProps::setProp(context, hash, propName, value);
BaseTextProps::setProp(context, hash, propName, value);

static auto defaults = BaseParagraphProps{};

// ParagraphAttributes has its own switch statement - to keep all
// of these fields together, and because there are some collisions between
// propnames parsed here and outside of ParagraphAttributes.
// This code is also duplicated in AndroidTextInput.
static auto paDefaults = ParagraphAttributes{};
switch (hash) {
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
maximumNumberOfLines,
"numberOfLines");
REBUILD_FIELD_SWITCH_CASE(
paDefaults, value, paragraphAttributes, ellipsizeMode, "ellipsizeMode");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
textBreakStrategy,
"textBreakStrategy");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
adjustsFontSizeToFit,
"adjustsFontSizeToFit");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
minimumFontScale,
"minimumFontScale");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
minimumFontSize,
"minimumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
maximumFontSize,
"maximumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
includeFontPadding,
"includeFontPadding");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
android_hyphenationFrequency,
"android_hyphenationFrequency");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
textAlignVertical,
"textAlignVertical");
}

switch (hash) {
RAW_SET_PROP_SWITCH_CASE(isSelectable, "selectable");
RAW_SET_PROP_SWITCH_CASE_BASIC(onTextLayout);
// [Windows]
RAW_SET_PROP_SWITCH_CASE_BASIC(selectionColor);
}

/*
* These props are applied to `View`, therefore they must not be a part of
* base text attributes.
*/
textAttributes.opacity = std::numeric_limits<Float>::quiet_NaN();
textAttributes.backgroundColor = {};
}

#pragma mark - DebugStringConvertible

#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList BaseParagraphProps::getDebugProps() const {
return ViewProps::getDebugProps() + BaseTextProps::getDebugProps() +
paragraphAttributes.getDebugProps() +
SharedDebugStringConvertibleList{
debugStringConvertibleItem("selectable", isSelectable),
// [Windows]
debugStringConvertibleItem("selectionColor", selectionColor)};
}
#endif
} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <limits>
#include <memory>
// [Windows]
#include <optional>

#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/components/text/BaseTextProps.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/Props.h>
#include <react/renderer/core/PropsParserContext.h>
// [Windows]
#include <react/renderer/graphics/Color.h>

namespace facebook::react {

/*
* Props of <Paragraph> component.
* Most of the props are directly stored in composed `ParagraphAttributes`
* object.
*/
class BaseParagraphProps : public ViewProps, public BaseTextProps {
public:
BaseParagraphProps() = default;
BaseParagraphProps(
const PropsParserContext &context,
const BaseParagraphProps &sourceProps,
const RawProps &rawProps);

void
setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, const RawValue &value);

#pragma mark - Props

/*
* Contains all prop values that affect visual representation of the
* paragraph.
*/
ParagraphAttributes paragraphAttributes{};

/*
* Defines can the text be selected (and copied) or not.
*/
bool isSelectable{};

bool onTextLayout{};

/*
* Defines the color of the selection highlight.
* [Windows]
*/
std::optional<SharedColor> selectionColor{};

#pragma mark - DebugStringConvertible

#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
};

} // namespace facebook::react
14 changes: 14 additions & 0 deletions vnext/overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@
"baseHash": "7fdf1967fa9c3421b11e841afcf207624df18706",
"issue": 15379
},
{
"type": "derived",
"file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/text/BaseParagraphProps.cpp",
"baseFile": "packages/react-native/ReactCommon/react/renderer/components/text/BaseParagraphProps.cpp",
"baseHash": "1aaf0a003c83195882c512a664409e429de4f892",
"issue": 15552
},
{
"type": "derived",
"file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/text/BaseParagraphProps.h",
"baseFile": "packages/react-native/ReactCommon/react/renderer/components/text/BaseParagraphProps.h",
"baseHash": "7661ba5c8392034cbda48f5d4b721a1ff30fe68d",
"issue": 15552
},
{
"type": "patch",
"file": "ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/components/text/ParagraphShadowNode.cpp",
Expand Down
Loading