-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fabric : Implements selectable prop for <Text> #15473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…ndows into text_selectable
…ndows into text_selectable
…t-native-windows into text_selectable
|
Awesome work ! , |
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 !). |
vineethkuttan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
|
Please create a task to notify the app of selection change (JS). |
|
I remember you talking about SHIFT + click selection. Please create a task for this too to avoid it slipping through the cracks. |
|
[Nit]
reads much nicer than
This is RNW so Windows is implicitly understood. Keep it minimal without data loss. |
|
Please fix formatting issues in What and Changelog sections. |
|
Just pointing out that this change is only about selecting text within a <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. |
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
…t-native-windows into text_selectable
tracking in ref: #15481 |
tracking in ref: #15481 |
There was a problem hiding this 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.
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
sundaramramaswamy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved with comments.
vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Outdated
Show resolved
Hide resolved
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Show resolved
Hide resolved
sundaramramaswamy
left a comment
There was a problem hiding this 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.
| // 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> |
There was a problem hiding this comment.
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.
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp
Show resolved
Hide resolved
| ~WordBreakIterator() noexcept { | ||
| if (m_breakIterator) { | ||
| ubrk_close(m_breakIterator); | ||
| } | ||
| } |
There was a problem hiding this comment.
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/
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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.
| WordBreakIterator(const WordBreakIterator &) = delete; | ||
| WordBreakIterator &operator=(const WordBreakIterator &) = delete; |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
Description
Implements selectable prop for Text component for windows
Includes :
Outstanding tasks for the feature are tracked ref: #15481
Type of Change
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