Skip to content

Conversation

@iamAbhi-916
Copy link
Contributor

@iamAbhi-916 iamAbhi-916 commented Dec 11, 2025

Description

Implements selectable prop for Text component for windows

Includes :

  • Basic text selection - Click and drag to select
  • Selection highlight - default windows blue accent background on selected text
  • Ctrl+C - Copy to clipboard
  • Ctrl+A - Select all text
  • Double-click - Select word
  • Right-click context menu - Copy / Select All options
  • Clear selection on click outside - Deselects when clicking elsewhere
  • Implements I-beam cursor for selectable text
  • Selection continues extending even when cursor is outside the selection range
  • Solves the CJK problem while text selection

Outstanding tasks for the feature are tracked ref: #15481

Type of Change

  • New feature (non-breaking change which adds functionality)

Why

Parity with RN Android/IOS.

Resolves #13112

What

Made upstream changes to fix the issue where the selectable prop wasn’t being passed to native due to a macro conversion problem.
Ref: facebook/react-native#52599

Also updated logic in the Paragraph component view and composition event handler to correctly handle all selection-related scenarios, including text selection, pointer events, copy-to-clipboard, and other related behaviors.

Screenshots

selectasble_all_tests.mp4

Testing

Tested in playground

Changelog

Should this change be included in the release notes: _indicate yes

Add a brief summary of the change to use in the release notes for the next release.
Adds text Component selection support for Fabric

Microsoft Reviewers: Open in CodeFlow

@iamAbhi-916 iamAbhi-916 changed the title Fabric : Implements selectable prop for Text component for windows Fabric : Implements selectable prop for text component for windows Dec 11, 2025
@vineethkuttan
Copy link
Contributor

Awesome work ! ,
Is the SelectionHighlightColor customizable through JavaScript?

@iamAbhi-916 iamAbhi-916 marked this pull request as ready for review December 12, 2025 05:29
@iamAbhi-916 iamAbhi-916 requested a review from a team as a code owner December 12, 2025 05:29
@iamAbhi-916
Copy link
Contributor Author

Awesome work ! , Is the SelectionHighlightColor customizable through JavaScript?

thank you!, for now default color is only supported (windows blue accent color) but will be taken up as soon this is merged(small change !).

Copy link
Contributor

@vineethkuttan vineethkuttan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@sundaramramaswamy
Copy link
Contributor

Please create a task to notify the app of selection change (JS).

@sundaramramaswamy
Copy link
Contributor

sundaramramaswamy commented Dec 12, 2025

I remember you talking about SHIFT + click selection. Please create a task for this too to avoid it slipping through the cracks.

@sundaramramaswamy
Copy link
Contributor

sundaramramaswamy commented Dec 12, 2025

Resolves [Add Relevant Issue Here]
#13112

Resolves #13112.

Drop template strings; they're noise in the actual PR message.

@sundaramramaswamy
Copy link
Contributor

sundaramramaswamy commented Dec 12, 2025

[Nit]

Implement selectable prop for <Text>

reads much nicer than

Implements selectable prop for Text component for windows

This is RNW so Windows is implicitly understood. Keep it minimal without data loss.

@sundaramramaswamy
Copy link
Contributor

sundaramramaswamy commented Dec 12, 2025

Please fix formatting issues in What and Changelog sections.

@iamAbhi-916 iamAbhi-916 changed the title Fabric : Implements selectable prop for text component for windows Fabric : Implements selectable prop for <Text> Dec 12, 2025
@sundaramramaswamy
Copy link
Contributor

sundaramramaswamy commented Dec 12, 2025

Just pointing out that this change is only about selecting text within a <Text> component and the user won't be able to select text spanning across two components, even if they're both selectable.

<View style={styles.selectionTestContainer}>
  <Text selectable={true} style={styles.sectionTitle}>Text Selection Test</Text>
  <Text selectable={true} style={styles.selectableText}>
    This text is SELECTABLE. Try clicking and dragging to select it.
  </Text>
</View>

@iamAbhi-916
Copy link
Contributor Author

iamAbhi-916 commented Dec 12, 2025

I know this may be beyond the scope of this PR but just pointing it out that this change is only about selecting text within a <Text> component and the user won't be able to select text spanning across two components.

<View style={styles.selectionTestContainer}>
  <Text selectable={true} style={styles.sectionTitle}>Text Selection Test</Text>
  <Text selectable={true} style={styles.selectableText}>
    This text is SELECTABLE. Try clicking and dragging to select it.
  </Text>
</View>

Good point!, this matches iOS and Android.

The React Native docs say selectable (ref: https://reactnative.dev/docs/text#selectable ) enables "native copy and paste functionality" - and native text views (UITextView on iOS, TextView on Android) don't support cross-view selection either. Each text view manages its own selection state independently.

@iamAbhi-916
Copy link
Contributor Author

iamAbhi-916 commented Dec 16, 2025

Please create a task to notify the app of selection change (JS).

tracking in ref: #15481

@iamAbhi-916
Copy link
Contributor Author

I remember you talking about SHIFT + click selection. Please create a task for this too to avoid it slipping through the cracks.

tracking in ref: #15481

Copy link
Contributor

@sundaramramaswamy sundaramramaswamy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted comments. Please address them.

Copy link
Contributor

@sundaramramaswamy sundaramramaswamy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with comments.

@iamAbhi-916 iamAbhi-916 requested a review from a team as a code owner December 23, 2025 08:34
Copy link
Contributor

@sundaramramaswamy sundaramramaswamy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but just take care of the comments I've given.

Comment on lines +24 to +27
// ICU utilities wrapped in a namespace to avoid UChar naming conflicts with Folly's FBString.
// Folly has a template parameter named 'UChar' which conflicts with ICU's global UChar typedef.
namespace IcuUtils {
#include <icu.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job adding comments to let readers know why this was done (deviating from other inclusions which are namespace wrapper-free.

Comment on lines +39 to +43
~WordBreakIterator() noexcept {
if (m_breakIterator) {
ubrk_close(m_breakIterator);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please drop this and make m_breakIterator a smart pointer.

Remember the Rule of Zero when you write in C++.

See Also: https://www.fluentcpp.com/2019/04/23/the-rule-of-zero-zero-constructor-zero-calorie/

Copy link
Contributor Author

@iamAbhi-916 iamAbhi-916 Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, I thought about this but UBreakIterator is an ICU opaque type that must be freed with ubrk_close so went with this manual destructor approach as anyways we would use ubrk_close to clean up memory, also WordBreakIterator is non-copyable and non-movable anyway.

}

private:
UBreakIterator *m_breakIterator = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
UBreakIterator *m_breakIterator = nullptr;
struct UBreakIterDeleter {
void operator()(UBreakIterator* ptr ) { if (ptr) ubrk_close(ptr); }
};
std::unique_ptr<UBreakIterator, UBreakIterDeleter> m_breakIterator {nullptr};

Almost all C APIs which create and destroy a resource has duality in functions e.g. ubrk_open + ubrk_close, fopen + fclose, SDL_CreateWindow + SDL_DestroyWindow, etc. And std::unique_ptr is a very good tool to ensure the freeing function is auto-called.

Comment on lines +45 to +46
WordBreakIterator(const WordBreakIterator &) = delete;
WordBreakIterator &operator=(const WordBreakIterator &) = delete;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be dropped once you use unique_ptr as a member variable. Why? Compiler won't supply default implementation of these functions since std::unique_ptr itself is non-copyable. So only when all members support copy would the compiler supply default impls.

return u_isalnum(codePoint) != 0;
}

inline UChar32 GetCodePointAt(const wchar_t *str, int32_t length, int32_t pos) noexcept {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inline UChar32 GetCodePointAt(const wchar_t *str, int32_t length, int32_t pos) noexcept {
UChar32 GetCodePointAt(const wchar_t *str, int32_t length, int32_t pos) noexcept {

return 0;
}
UChar32 cp;
U16_GET(str, 0, pos, length, cp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this (and others like U16_BACK_1, U16_FWD_1, etc.) function handle when str is nullptr? Check its documentation. If it'll choke on invalid inputs, we'd have to check for nullptr and feed to it only valid pointers. Ignore this comment if it'll handle gracefully.

namespace IcuUtils {
#include <icu.h>

class WordBreakIterator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These utilities within IcuUtils may be useful beyond this one source file. Consider giving this namespace its own .h and .cpp, house them somewhere like //vnext/Microsoft.ReactNative/Utils and add them to the rightful .vcxprojs.


void ParagraphComponentView::SelectWordAtPosition(int32_t charPosition) noexcept {
const std::wstring utf16Text{facebook::react::WindowsTextLayoutManager::GetTransformedText(m_attributedStringBox)};
const int32_t textLength = static_cast<int32_t>(utf16Text.length());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a reminder: this won't be the grapheme cluster count but the codepoint count.

wordEnd = charPosition;

while (wordStart > 0) {
int32_t prevPos = IcuUtils::MoveToPreviousCodePoint(utf16Text.c_str(), wordStart);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we move by grapheme clusters if you want to select characters and not partial characters?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement selectable property for Text for fabric

4 participants