Skip to content
Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "[Fabric] Implement writingDirection property for Text component",
"packageName": "react-native-windows",
"email": "54227869+anupriya13@users.noreply.github.com",
"dependentChangeType": "patch"
}
56 changes: 56 additions & 0 deletions packages/playground/Samples/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,32 @@ export default class Bootstrap extends React.Component {
aliquip ex ea commodo consequat.
</Text>
</View>

{/* Writing Direction Examples */}
<View style={styles.writingDirectionContainer}>
<Text style={styles.sectionTitle}>Writing Direction Examples</Text>

<Text style={styles.directionLabel}>Left-to-Right (LTR):</Text>
<Text
style={styles.directionExample}
{...({writingDirection: 'ltr'} as any)}>
This text flows from left to right. Numbers: 123 456 789
</Text>

<Text style={styles.directionLabel}>Right-to-Left (RTL):</Text>
<Text
style={styles.directionExample}
{...({writingDirection: 'rtl'} as any)}>
This text flows from right to left. Numbers: 123 456 789
</Text>

<Text style={styles.directionLabel}>Auto/Natural:</Text>
<Text
style={styles.directionExample}
{...({writingDirection: 'auto'} as any)}>
This text uses natural direction (defaults to LTR)
</Text>
</View>
</View>
);
}
Expand All @@ -64,6 +90,36 @@ const styles = StyleSheet.create({
textAlign: 'center',
margin: 10,
},
writingDirectionContainer: {
backgroundColor: '#E8F4FD',
padding: 15,
margin: 10,
borderRadius: 8,
width: 400,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 15,
textAlign: 'center',
color: '#2C3E50',
},
directionLabel: {
fontSize: 14,
fontWeight: '600',
marginTop: 10,
marginBottom: 5,
color: '#34495E',
},
directionExample: {
fontSize: 16,
padding: 8,
backgroundColor: '#FFFFFF',
borderRadius: 4,
borderWidth: 1,
borderColor: '#BDC3C7',
marginBottom: 8,
},
});

AppRegistry.registerComponent('Bootstrap', () => Bootstrap);
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ void ParagraphComponentView::updateProps(
updateTextAlignment(newViewProps.textAttributes.alignment);
}

if (oldViewProps.textAttributes.baseWritingDirection != newViewProps.textAttributes.baseWritingDirection) {
m_textLayout = nullptr;
}

if (oldViewProps.paragraphAttributes.ellipsizeMode != newViewProps.paragraphAttributes.ellipsizeMode) {
m_textLayout = nullptr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ void WindowsTextLayoutManager::GetTextLayout(
}
winrt::check_hresult(spTextFormat->SetTextAlignment(alignment));

// Set reading direction based on baseWritingDirection
if (outerFragment.textAttributes.baseWritingDirection) {
DWRITE_READING_DIRECTION readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;

// Handle explicit RightToLeft
if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::RightToLeft) {
readingDirection = DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
}
// For all other cases (including Natural), use context-aware detection
else {
// For Natural direction, detect from text content
readingDirection = GetNaturalReadingDirection(attributedStringBox);
}

winrt::check_hresult(spTextFormat->SetReadingDirection(readingDirection));
}

// Get text with Object Replacement Characters for attachments
auto str = GetTransformedText(attributedStringBox);
winrt::check_hresult(Microsoft::ReactNative::DWriteFactory()->CreateTextLayout(
Expand Down Expand Up @@ -547,4 +564,43 @@ winrt::hstring WindowsTextLayoutManager::GetTransformedText(const AttributedStri
return result;
}

DWRITE_READING_DIRECTION WindowsTextLayoutManager::GetNaturalReadingDirection(const AttributedStringBox &attributedStringBox) noexcept {
const auto &attributedString = attributedStringBox.getValue();

// Analyze the text content to determine the natural reading direction
// Look for the first strong directional character
for (const auto &fragment : attributedString.getFragments()) {
if (!fragment.isAttachment()) {
auto fragmentText = Microsoft::Common::Unicode::Utf8ToUtf16(fragment.string);

for (wchar_t ch : fragmentText) {
// Check for strong RTL characters (Arabic, Hebrew ranges)
if ((ch >= 0x0590 && ch <= 0x05FF) || // Hebrew
(ch >= 0x0600 && ch <= 0x06FF) || // Arabic
(ch >= 0x0750 && ch <= 0x077F) || // Arabic Supplement
(ch >= 0x08A0 && ch <= 0x08FF) || // Arabic Extended-A
(ch >= 0xFB1D && ch <= 0xFB4F) || // Hebrew Presentation Forms
(ch >= 0xFB50 && ch <= 0xFDFF) || // Arabic Presentation Forms-A
(ch >= 0xFE70 && ch <= 0xFEFF)) { // Arabic Presentation Forms-B
return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
}
// Check for strong LTR characters (Latin, Cyrillic, etc.)
else if ((ch >= 0x0041 && ch <= 0x005A) || // Latin uppercase
(ch >= 0x0061 && ch <= 0x007A) || // Latin lowercase
(ch >= 0x00C0 && ch <= 0x00D6) || // Latin extended
(ch >= 0x00D8 && ch <= 0x00F6) || // Latin extended
(ch >= 0x00F8 && ch <= 0x00FF) || // Latin extended
(ch >= 0x0100 && ch <= 0x017F) || // Latin Extended-A
(ch >= 0x0180 && ch <= 0x024F) || // Latin Extended-B
(ch >= 0x0400 && ch <= 0x04FF)) { // Cyrillic
return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
}
}
}
}

// If no strong directional characters found, use system default (LTR)
return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class WindowsTextLayoutManager : public TextLayoutManager {

private:
static winrt::hstring GetTransformedText(const AttributedStringBox &attributedStringBox);
static DWRITE_READING_DIRECTION GetNaturalReadingDirection(const AttributedStringBox &attributedStringBox) noexcept;
static void GetTextLayout(
const AttributedStringBox &attributedStringBox,
const ParagraphAttributes &paragraphAttributes,
Expand Down
Loading