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": "[email protected]",
"dependentChangeType": "patch"
}
50 changes: 50 additions & 0 deletions packages/playground/Samples/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ 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">
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">
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">
This text uses natural direction (defaults to LTR)
</Text>
</View>
</View>
);
}
Expand All @@ -64,6 +84,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 @@ -158,6 +158,28 @@ 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;
switch (*outerFragment.textAttributes.baseWritingDirection) {
case facebook::react::WritingDirection::LeftToRight:
readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
break;
case facebook::react::WritingDirection::RightToLeft:
readingDirection = DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
break;
case facebook::react::WritingDirection::Natural:
// For Natural, default to left-to-right. In a more complete implementation,
// this could be determined based on the script/locale context.
readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
break;
default:
readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
break;
}
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
112 changes: 56 additions & 56 deletions vnext/codegen/rnwcoreJSI-generated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -952,41 +952,6 @@ NativeAnimatedTurboModuleCxxSpecJSI::NativeAnimatedTurboModuleCxxSpecJSI(std::sh
methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleCxxSpecJSI_removeListeners};
methodMap_["queueAndExecuteBatchedOperations"] = MethodMetadata {1, __hostFunction_NativeAnimatedTurboModuleCxxSpecJSI_queueAndExecuteBatchedOperations};
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_getColorScheme(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
auto result = static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->getColorScheme(
rt
);
return result ? jsi::Value(std::move(*result)) : jsi::Value::null();
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_setColorScheme(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->setColorScheme(
rt,
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
);
return jsi::Value::undefined();
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_addListener(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->addListener(
rt,
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
);
return jsi::Value::undefined();
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_removeListeners(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->removeListeners(
rt,
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
);
return jsi::Value::undefined();
}

NativeAppearanceCxxSpecJSI::NativeAppearanceCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("Appearance", jsInvoker) {
methodMap_["getColorScheme"] = MethodMetadata {0, __hostFunction_NativeAppearanceCxxSpecJSI_getColorScheme};
methodMap_["setColorScheme"] = MethodMetadata {1, __hostFunction_NativeAppearanceCxxSpecJSI_setColorScheme};
methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAppearanceCxxSpecJSI_addListener};
methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAppearanceCxxSpecJSI_removeListeners};
}
static jsi::Value __hostFunction_NativeAppStateCxxSpecJSI_getConstants(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<NativeAppStateCxxSpecJSI *>(&turboModule)->getConstants(
rt
Expand Down Expand Up @@ -1032,6 +997,41 @@ NativeAppThemeCxxSpecJSI::NativeAppThemeCxxSpecJSI(std::shared_ptr<CallInvoker>
: TurboModule("AppTheme", jsInvoker) {
methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeAppThemeCxxSpecJSI_getConstants};
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_getColorScheme(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
auto result = static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->getColorScheme(
rt
);
return result ? jsi::Value(std::move(*result)) : jsi::Value::null();
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_setColorScheme(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->setColorScheme(
rt,
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
);
return jsi::Value::undefined();
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_addListener(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->addListener(
rt,
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
);
return jsi::Value::undefined();
}
static jsi::Value __hostFunction_NativeAppearanceCxxSpecJSI_removeListeners(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeAppearanceCxxSpecJSI *>(&turboModule)->removeListeners(
rt,
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
);
return jsi::Value::undefined();
}

NativeAppearanceCxxSpecJSI::NativeAppearanceCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("Appearance", jsInvoker) {
methodMap_["getColorScheme"] = MethodMetadata {0, __hostFunction_NativeAppearanceCxxSpecJSI_getColorScheme};
methodMap_["setColorScheme"] = MethodMetadata {1, __hostFunction_NativeAppearanceCxxSpecJSI_setColorScheme};
methodMap_["addListener"] = MethodMetadata {1, __hostFunction_NativeAppearanceCxxSpecJSI_addListener};
methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeAppearanceCxxSpecJSI_removeListeners};
}
static jsi::Value __hostFunction_NativeBlobModuleCxxSpecJSI_getConstants(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<NativeBlobModuleCxxSpecJSI *>(&turboModule)->getConstants(
rt
Expand Down Expand Up @@ -1135,27 +1135,6 @@ NativeClipboardCxxSpecJSI::NativeClipboardCxxSpecJSI(std::shared_ptr<CallInvoker
methodMap_["getString"] = MethodMetadata {0, __hostFunction_NativeClipboardCxxSpecJSI_getString};
methodMap_["setString"] = MethodMetadata {1, __hostFunction_NativeClipboardCxxSpecJSI_setString};
}
static jsi::Value __hostFunction_NativeDeviceEventManagerCxxSpecJSI_invokeDefaultBackPressHandler(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeDeviceEventManagerCxxSpecJSI *>(&turboModule)->invokeDefaultBackPressHandler(
rt
);
return jsi::Value::undefined();
}

NativeDeviceEventManagerCxxSpecJSI::NativeDeviceEventManagerCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("DeviceEventManager", jsInvoker) {
methodMap_["invokeDefaultBackPressHandler"] = MethodMetadata {0, __hostFunction_NativeDeviceEventManagerCxxSpecJSI_invokeDefaultBackPressHandler};
}
static jsi::Value __hostFunction_NativeDeviceInfoCxxSpecJSI_getConstants(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<NativeDeviceInfoCxxSpecJSI *>(&turboModule)->getConstants(
rt
);
}

NativeDeviceInfoCxxSpecJSI::NativeDeviceInfoCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("DeviceInfo", jsInvoker) {
methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeDeviceInfoCxxSpecJSI_getConstants};
}
static jsi::Value __hostFunction_NativeDevLoadingViewCxxSpecJSI_showMessage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeDevLoadingViewCxxSpecJSI *>(&turboModule)->showMessage(
rt,
Expand Down Expand Up @@ -1299,6 +1278,27 @@ NativeDevSettingsCxxSpecJSI::NativeDevSettingsCxxSpecJSI(std::shared_ptr<CallInv
methodMap_["removeListeners"] = MethodMetadata {1, __hostFunction_NativeDevSettingsCxxSpecJSI_removeListeners};
methodMap_["setIsShakeToShowDevMenuEnabled"] = MethodMetadata {1, __hostFunction_NativeDevSettingsCxxSpecJSI_setIsShakeToShowDevMenuEnabled};
}
static jsi::Value __hostFunction_NativeDeviceEventManagerCxxSpecJSI_invokeDefaultBackPressHandler(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeDeviceEventManagerCxxSpecJSI *>(&turboModule)->invokeDefaultBackPressHandler(
rt
);
return jsi::Value::undefined();
}

NativeDeviceEventManagerCxxSpecJSI::NativeDeviceEventManagerCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("DeviceEventManager", jsInvoker) {
methodMap_["invokeDefaultBackPressHandler"] = MethodMetadata {0, __hostFunction_NativeDeviceEventManagerCxxSpecJSI_invokeDefaultBackPressHandler};
}
static jsi::Value __hostFunction_NativeDeviceInfoCxxSpecJSI_getConstants(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<NativeDeviceInfoCxxSpecJSI *>(&turboModule)->getConstants(
rt
);
}

NativeDeviceInfoCxxSpecJSI::NativeDeviceInfoCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("DeviceInfo", jsInvoker) {
methodMap_["getConstants"] = MethodMetadata {0, __hostFunction_NativeDeviceInfoCxxSpecJSI_getConstants};
}
static jsi::Value __hostFunction_NativeDialogManagerAndroidCxxSpecJSI_getConstants(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<NativeDialogManagerAndroidCxxSpecJSI *>(&turboModule)->getConstants(
rt
Expand Down
Loading