diff --git a/change/react-native-windows-02413c33-df01-48d8-beb1-092c80f1f706.json b/change/react-native-windows-02413c33-df01-48d8-beb1-092c80f1f706.json
new file mode 100644
index 00000000000..daa55ef6122
--- /dev/null
+++ b/change/react-native-windows-02413c33-df01-48d8-beb1-092c80f1f706.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Allow text components to have children",
+ "packageName": "react-native-windows",
+ "email": "tatianakapos@microsoft.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-17b0a7f2-3a73-40cf-9fb1-2a99ef16d226.json b/change/react-native-windows-17b0a7f2-3a73-40cf-9fb1-2a99ef16d226.json
new file mode 100644
index 00000000000..f004772889d
--- /dev/null
+++ b/change/react-native-windows-17b0a7f2-3a73-40cf-9fb1-2a99ef16d226.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Add ImageRequestParams",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-1d2e90db-6fa1-47f8-aee1-959aadf54774.json b/change/react-native-windows-1d2e90db-6fa1-47f8-aee1-959aadf54774.json
new file mode 100644
index 00000000000..62dc8873ff4
--- /dev/null
+++ b/change/react-native-windows-1d2e90db-6fa1-47f8-aee1-959aadf54774.json
@@ -0,0 +1,7 @@
+{
+ "type": "none",
+ "comment": "Added override Text.d.ts to src-win/Libraries/Text/ to add tooltip prop to Text component",
+ "packageName": "react-native-windows",
+ "email": "email not defined",
+ "dependentChangeType": "none"
+}
diff --git a/change/react-native-windows-1dcd74f9-fb9a-4bc3-b8fb-fc5a53c9a661.json b/change/react-native-windows-1dcd74f9-fb9a-4bc3-b8fb-fc5a53c9a661.json
new file mode 100644
index 00000000000..b1beab691b8
--- /dev/null
+++ b/change/react-native-windows-1dcd74f9-fb9a-4bc3-b8fb-fc5a53c9a661.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Add SetProperties method to ReactNativeIsland",
+ "packageName": "react-native-windows",
+ "email": "30809111+acoates-ms@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-262555bf-8b8f-4511-b814-9ed70a0cb8b4.json b/change/react-native-windows-262555bf-8b8f-4511-b814-9ed70a0cb8b4.json
new file mode 100644
index 00000000000..30cd61c3051
--- /dev/null
+++ b/change/react-native-windows-262555bf-8b8f-4511-b814-9ed70a0cb8b4.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implementation of adjustFontSizeToFit for Text in Fabric",
+ "packageName": "react-native-windows",
+ "email": "kvineeth@microsoft.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-38ddfc46-37fc-42ff-90d8-d3f72a15f8a2.json b/change/react-native-windows-38ddfc46-37fc-42ff-90d8-d3f72a15f8a2.json
new file mode 100644
index 00000000000..cb971b96e0d
--- /dev/null
+++ b/change/react-native-windows-38ddfc46-37fc-42ff-90d8-d3f72a15f8a2.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement scrollEventThrottle for ScrollView in Fabric",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-537a723b-a3c7-48fe-aec0-c53ca7d201e3.json b/change/react-native-windows-537a723b-a3c7-48fe-aec0-c53ca7d201e3.json
new file mode 100644
index 00000000000..b72988b6450
--- /dev/null
+++ b/change/react-native-windows-537a723b-a3c7-48fe-aec0-c53ca7d201e3.json
@@ -0,0 +1,7 @@
+{
+ "type": "none",
+ "comment": "added check for double click on textInput component view connecting it to WM_LBUTTONDBLCLK",
+ "packageName": "react-native-windows",
+ "email": "email not defined",
+ "dependentChangeType": "none"
+}
diff --git a/change/react-native-windows-53cc2215-aea1-4487-be63-34056926b427.json b/change/react-native-windows-53cc2215-aea1-4487-be63-34056926b427.json
new file mode 100644
index 00000000000..b2026641510
--- /dev/null
+++ b/change/react-native-windows-53cc2215-aea1-4487-be63-34056926b427.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement decelerationRate in ScrollView",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-54a72170-7490-4502-8483-62b58bbe76fd.json b/change/react-native-windows-54a72170-7490-4502-8483-62b58bbe76fd.json
new file mode 100644
index 00000000000..c2a3d11290f
--- /dev/null
+++ b/change/react-native-windows-54a72170-7490-4502-8483-62b58bbe76fd.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": " Add Custom Font Family support in TextInput",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-62631742-8186-482d-8779-3bdc35d1a131.json b/change/react-native-windows-62631742-8186-482d-8779-3bdc35d1a131.json
new file mode 100644
index 00000000000..fefbcacdc99
--- /dev/null
+++ b/change/react-native-windows-62631742-8186-482d-8779-3bdc35d1a131.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Fix Narrator Bug",
+ "packageName": "react-native-windows",
+ "email": "34109996+chiaramooney@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-7291aa87-457c-43d8-8f82-b2fedf913592.json b/change/react-native-windows-7291aa87-457c-43d8-8f82-b2fedf913592.json
new file mode 100644
index 00000000000..00b782425c2
--- /dev/null
+++ b/change/react-native-windows-7291aa87-457c-43d8-8f82-b2fedf913592.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement zoomScale, maximumZoomScale and minimumZoomScale in ScrollView",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-7d072016-5414-4327-b40c-a149c1dc34b9.json b/change/react-native-windows-7d072016-5414-4327-b40c-a149c1dc34b9.json
new file mode 100644
index 00000000000..0f95aad40d3
--- /dev/null
+++ b/change/react-native-windows-7d072016-5414-4327-b40c-a149c1dc34b9.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Add IScrollProvider Implementation",
+ "packageName": "react-native-windows",
+ "email": "34109996+chiaramooney@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-99f88013-45df-4134-99df-b0fb86dc5e88.json b/change/react-native-windows-99f88013-45df-4134-99df-b0fb86dc5e88.json
new file mode 100644
index 00000000000..ca0285f75c4
--- /dev/null
+++ b/change/react-native-windows-99f88013-45df-4134-99df-b0fb86dc5e88.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "Implement SpellCheck and AutoCorrect for TextInput",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-9ee8d633-698c-476d-9334-170be2c5dc25.json b/change/react-native-windows-9ee8d633-698c-476d-9334-170be2c5dc25.json
new file mode 100644
index 00000000000..183d59c5fca
--- /dev/null
+++ b/change/react-native-windows-9ee8d633-698c-476d-9334-170be2c5dc25.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement letterSpacing for TextInput",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-a2bdf157-2fb8-43f8-8541-56ca50021d6c.json b/change/react-native-windows-a2bdf157-2fb8-43f8-8541-56ca50021d6c.json
new file mode 100644
index 00000000000..6d9d85d086b
--- /dev/null
+++ b/change/react-native-windows-a2bdf157-2fb8-43f8-8541-56ca50021d6c.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement showsVerticalScrollIndicatorValue and showsVerticalScrollIndicatorValue for ScrollView",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-b165413e-a3de-4ba5-8511-cc1f22727002.json b/change/react-native-windows-b165413e-a3de-4ba5-8511-cc1f22727002.json
new file mode 100644
index 00000000000..ae103eb4668
--- /dev/null
+++ b/change/react-native-windows-b165413e-a3de-4ba5-8511-cc1f22727002.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement OnScrollBeginDrag Event",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-b25b85e4-b99c-4ba7-92db-ac45afed3cc9.json b/change/react-native-windows-b25b85e4-b99c-4ba7-92db-ac45afed3cc9.json
new file mode 100644
index 00000000000..a9fee0a6049
--- /dev/null
+++ b/change/react-native-windows-b25b85e4-b99c-4ba7-92db-ac45afed3cc9.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implemented textAlign in TextInput for Fabric",
+ "packageName": "react-native-windows",
+ "email": "14967941+danielayala94@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-b86e11e6-8cbc-4602-b53a-e199ce0f6b2e.json b/change/react-native-windows-b86e11e6-8cbc-4602-b53a-e199ce0f6b2e.json
new file mode 100644
index 00000000000..9182b15023b
--- /dev/null
+++ b/change/react-native-windows-b86e11e6-8cbc-4602-b53a-e199ce0f6b2e.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Allow TextInput to scroll",
+ "packageName": "react-native-windows",
+ "email": "30809111+acoates-ms@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-dee3a16c-0e0f-4af4-b524-b216b0a19255.json b/change/react-native-windows-dee3a16c-0e0f-4af4-b524-b216b0a19255.json
new file mode 100644
index 00000000000..19c43f27d73
--- /dev/null
+++ b/change/react-native-windows-dee3a16c-0e0f-4af4-b524-b216b0a19255.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Button should pass onAccessibilityTap to native",
+ "packageName": "react-native-windows",
+ "email": "34109996+chiaramooney@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-eed51fa4-43c0-4164-8892-4c3fa0571940.json b/change/react-native-windows-eed51fa4-43c0-4164-8892-4c3fa0571940.json
new file mode 100644
index 00000000000..9d0f1b136bc
--- /dev/null
+++ b/change/react-native-windows-eed51fa4-43c0-4164-8892-4c3fa0571940.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement onProgress for Image",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-eed821fc-4d11-4ca3-a820-682d1aba1977.json b/change/react-native-windows-eed821fc-4d11-4ca3-a820-682d1aba1977.json
new file mode 100644
index 00000000000..1270f923f34
--- /dev/null
+++ b/change/react-native-windows-eed821fc-4d11-4ca3-a820-682d1aba1977.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Implement body in Image Source",
+ "packageName": "react-native-windows",
+ "email": "54227869+anupriya13@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
\ No newline at end of file
diff --git a/change/react-native-windows-f2a8e41c-6064-487c-85d9-b2151db6436c.json b/change/react-native-windows-f2a8e41c-6064-487c-85d9-b2151db6436c.json
new file mode 100644
index 00000000000..e59e2ddbe13
--- /dev/null
+++ b/change/react-native-windows-f2a8e41c-6064-487c-85d9-b2151db6436c.json
@@ -0,0 +1,7 @@
+{
+ "type": "none",
+ "comment": "implemented on onEndEditing",
+ "packageName": "react-native-windows",
+ "email": "email not defined",
+ "dependentChangeType": "none"
+}
diff --git a/packages/@react-native-windows/tester/overrides.json b/packages/@react-native-windows/tester/overrides.json
index 895dccf4135..534658d7bec 100644
--- a/packages/@react-native-windows/tester/overrides.json
+++ b/packages/@react-native-windows/tester/overrides.json
@@ -7,6 +7,12 @@
],
"baseVersion": "0.76.6",
"overrides": [
+ {
+ "type": "derived",
+ "file": "src/js/components/TextInlineView.windows.js",
+ "baseFile": "packages/rn-tester/js/components/TextInlineView.js",
+ "baseHash": "40db48c2d0556cc66f8ac6940441b1bc01ad1d48"
+ },
{
"type": "derived",
"file": "src/js/examples-win/Button/ButtonExample.windows.js",
@@ -66,6 +72,12 @@
"baseFile": "packages/rn-tester/js/examples/Text/TextExample.android.js",
"baseHash": "985cb24e1f56c30288537e72db20c602614b64c3"
},
+ {
+ "type": "derived",
+ "file": "src/js/examples/Text/TextInlineViewsExample.windows.js",
+ "baseFile": "packages/rn-tester/js/examples/Text/TextInlineViewsExample.js",
+ "baseHash": "63088038921558214c480b323dd2099698911f3f"
+ },
{
"type": "patch",
"file": "src/js/examples/TextInput/TextInputExample.windows.js",
diff --git a/packages/@react-native-windows/tester/src/js/components/TextInlineView.windows.js b/packages/@react-native-windows/tester/src/js/components/TextInlineView.windows.js
new file mode 100644
index 00000000000..208f14fa969
--- /dev/null
+++ b/packages/@react-native-windows/tester/src/js/components/TextInlineView.windows.js
@@ -0,0 +1,224 @@
+/**
+ * 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.
+ *
+ * @format
+ * @flow strict-local
+ */
+
+'use strict';
+
+import React from 'react';
+import {Image, TouchableHighlight, Text, View} from 'react-native';
+
+function Basic(): React.Node {
+ return (
+
+ This text contains an inline blue view{' '}
+ and
+ an inline image . Neat,
+ huh?
+
+ );
+}
+
+function NestedTexts(): React.Node {
+ // [Windows] accessible = {true} is needed to find Views in e2etests
+ return (
+
+ This is the first row
+
+
+ This is a nested text
+
+ with a Red View
+
+
+
+ );
+}
+
+function ClippedByText(): React.Node {
+ // [Windows] accessible = {true} is needed to find Views in e2etests
+ return (
+
+ {/*
+ * Inline View
+ **/}
+
+ The inline view below is
+ taller than its Text parent and should be clipped.
+
+
+ This is an inline view
+ {/* Render a red border around the steelblue rectangle to make it clear how the inline view is being clipped */}
+
+
+
+
+
+ {/*
+ * Inline Image
+ **/}
+
+ The inline image below is
+ taller than its Text parent and should be clipped.
+
+
+ This is an inline image
+
+
+
+ );
+}
+
+type ChangeSizeState = {|
+ width: number,
+|};
+
+class ChangeImageSize extends React.Component {
+ state: ChangeSizeState = {
+ width: 50,
+ };
+
+ render(): React.Node {
+ return (
+
+ {
+ this.setState({width: this.state.width === 50 ? 100 : 50});
+ }}>
+
+ Change Image Width (width={this.state.width})
+
+
+
+ This is an
+
+ inline image
+
+
+ );
+ }
+}
+
+class ChangeViewSize extends React.Component {
+ state: ChangeSizeState = {
+ width: 50,
+ };
+
+ render(): React.Node {
+ return (
+
+ {
+ this.setState({width: this.state.width === 50 ? 100 : 50});
+ }}>
+
+ Change View Width (width={this.state.width})
+
+
+
+ This is an
+
+ inline view
+
+
+ );
+ }
+}
+
+class ChangeInnerViewSize extends React.Component {
+ state: ChangeSizeState = {
+ width: 50,
+ };
+
+ render(): React.Node {
+ return (
+
+ {
+ this.setState({width: this.state.width === 50 ? 100 : 50});
+ }}>
+ {/* When updating `state.width`, it's important that the only thing that
+ changes is the width of the pink inline view. When we do this, we
+ demonstrate a bug in RN Android where the pink view doesn't get
+ rerendered and remains at its old size. If other things change
+ (e.g. we display `state.width` as text somewhere) it could circumvent
+ the bug and cause the pink view to be rerendered at its new size. */}
+ Change Pink View Width
+
+
+ This is an
+
+
+
+ inline view
+
+
+ );
+ }
+}
+
+module.exports = {
+ Basic,
+ NestedTexts,
+ ClippedByText,
+ ChangeImageSize,
+ ChangeViewSize,
+ ChangeInnerViewSize,
+};
diff --git a/packages/@react-native-windows/tester/src/js/examples-win/LegacyTests/TextInputTestPage.tsx b/packages/@react-native-windows/tester/src/js/examples-win/LegacyTests/TextInputTestPage.tsx
index 739f3f49458..552718b6331 100644
--- a/packages/@react-native-windows/tester/src/js/examples-win/LegacyTests/TextInputTestPage.tsx
+++ b/packages/@react-native-windows/tester/src/js/examples-win/LegacyTests/TextInputTestPage.tsx
@@ -62,6 +62,8 @@ export class TextInputTestPage extends React.Component<
style={{height: 80}}
placeholder="MultiLine"
multiline={true}
+ spellCheck={false}
+ autoCorrect={false}
/>
{'Nested s:'}
@@ -977,78 +977,95 @@ function TextBaseLineLayoutExample(props: {}): React.Node {
{texts}
{marker}
- {/* [Windows #12997 - This tests renders nested within which is not supported yet]
- {'Interleaving and :'}
+
+ ) : (
+
+ {'Nested s:'}
{marker}
-
- Some text.
-
- {marker}
- Text inside View.
- {marker}
-
-
+ {texts}
{marker}
-
- {'Multi-line interleaved and :'}
-
+ {'Array of s in :'}
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris
- venenatis,{' '}
-
- mauris eu commodo maximus
- {' '}
- , ante arcu vestibulum ligula, et scelerisque diam.
-
+ {marker}
+ {texts}
+ {marker}
-
- {'Multi-line alignment'}
-
-
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+ {'Interleaving and :'}
+
+ {marker}
+
+ Some text.
+
+ {marker}
+ Text inside View.
+ {marker}
+
+ {marker}
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+ {'Multi-line interleaved and :'}
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris
+ venenatis,{' '}
+
+ mauris eu commodo maximus
+ {' '}
+ , ante arcu vestibulum ligula, et scelerisque diam.
-
- {':'}
-
- {marker}
- {texts}
- {marker}
-
+ {'Multi-line alignment'}
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
- {':'}
-
- {marker}
-
- {texts}
-
- {marker}
+ {':'}
+
+ {marker}
+ {texts}
+ {marker}
+
+
+ {':'}
+
+ {marker}
+
+ {texts}
+
+ {marker}
+
-*/}
);
+ // Windows]
}
function TextBorderExample(props: {}): React.Node {
@@ -1457,50 +1474,55 @@ const examples = [
);
},
},
- /* [Windows #12997 - This tests renders nested within which is not supported yet]
- {
- title: 'Inline views',
- name: 'inlineViewsBasic',
- render(): React.Node {
- return ;
- },
- },
- {
- title: 'Inline views with multiple nested texts',
- name: 'inlineViewsMultiple',
- render(): React.Node {
- return ;
- },
- },
- {
- title: 'Inline image/view clipped by ',
- name: 'inlineViewsClipped',
- render(): React.Node {
- return ;
- },
- },
- {
- title: 'Relayout inline image',
- name: 'relayoutInlineImage',
- render(): React.Node {
- return ;
- },
- },
- {
- title: 'Relayout inline view',
- name: 'relayoutInlineView',
- render(): React.Node {
- return ;
- },
- },
- {
- title: 'Relayout nested inline view',
- name: 'relayoutNestedInlineView',
- render(): React.Node {
- return ;
- },
- },
-*/
+ // [Windows - Paper doesn't support Views in Text while Fabric does
+ ...(global.RN$Bridgeless === true
+ ? [
+ {
+ title: 'Inline views',
+ name: 'inlineViewsBasic',
+ render(): React.Node {
+ return ;
+ },
+ },
+ {
+ title: 'Inline views with multiple nested texts',
+ name: 'inlineViewsMultiple',
+ render(): React.Node {
+ return ;
+ },
+ },
+ {
+ title: 'Inline image/view clipped by ',
+ name: 'inlineViewsClipped',
+ render(): React.Node {
+ return ;
+ },
+ },
+ {
+ title: 'Relayout inline image',
+ name: 'relayoutInlineImage',
+ render(): React.Node {
+ return ;
+ },
+ },
+ {
+ title: 'Relayout inline view',
+ name: 'relayoutInlineView',
+ render(): React.Node {
+ return ;
+ },
+ },
+ {
+ title: 'Relayout nested inline view',
+ name: 'relayoutNestedInlineView',
+ render(): React.Node {
+ return ;
+ },
+ },
+ TextInlineViewsExample,
+ ]
+ : []),
+ // Windows]
{
title: 'Text shadow',
name: 'textShadow',
@@ -1637,8 +1659,6 @@ const examples = [
);
},
},
- // [Windows #12997]
- // TextInlineViewsExample,
{
title: 'Customized Accessibility',
name: 'textAccessibility',
@@ -1692,6 +1712,76 @@ const examples = [
);
},
},
+ {
+ title: 'AdjustFontSize according to the Width, Height and LinesCount',
+ name: 'adjustFontSizeToFit',
+ render: function (): React.Node {
+ return (
+
+
+ {`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20`}
+
+
+ {`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20`}
+
+ {[
+ {width: 500, height: 80, lineCount: 3},
+ {width: 475, height: 120, lineCount: 5},
+ {width: 450, height: 160, lineCount: 0},
+ ].map((item, index) => (
+
+
+ {`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:${item.height},width:${item.width},lineCount:${item.lineCount},fontSize:40`}
+
+
+ {`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:${item.height},width:${item.width},lineCount:${item.lineCount},fontSize:40`}
+
+
+ ))}
+
+ );
+ },
+ },
];
const styles = StyleSheet.create({
diff --git a/packages/@react-native-windows/tester/src/js/examples/Text/TextInlineViewsExample.windows.js b/packages/@react-native-windows/tester/src/js/examples/Text/TextInlineViewsExample.windows.js
new file mode 100644
index 00000000000..a60cf411f31
--- /dev/null
+++ b/packages/@react-native-windows/tester/src/js/examples/Text/TextInlineViewsExample.windows.js
@@ -0,0 +1,67 @@
+/**
+ * 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.
+ *
+ * @format
+ * @flow strict-local
+ */
+
+import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
+
+import * as React from 'react';
+import {Text, View} from 'react-native';
+
+function InlineView(props: {
+ textAlign: 'auto' | 'left' | 'right' | 'center' | 'justify',
+ long?: boolean,
+}): React.Node {
+ return (
+
+
+ Parent
+ Child
+
+ Child
+ {props !== null && props.long === true && (
+
+ aaaa a aaaa aaaaaa aaa a a a aaaaa sdsds dsdSAD asd ASDasd ASDas
+
+ )}
+
+
+ );
+}
+
+export function TextInlineViewsExample(props: {}): React.Node {
+ return (
+ <>
+
+ BoringLayout
+
+
+
+
+
+ StaticLayout
+
+
+
+
+
+ >
+ );
+}
+
+export default ({
+ title: 'TextInlineViewsExample',
+ name: 'inlineViews',
+ description:
+ ('Shows how inline views are rendered when text is subject to alignment.': string),
+ expect: 'The red box should align correctly with the rest of the text.',
+ render: (): React.Node => ,
+}: RNTesterModuleExample);
diff --git a/packages/@react-native-windows/tester/src/js/examples/TextInput/TextInputExample.windows.js b/packages/@react-native-windows/tester/src/js/examples/TextInput/TextInputExample.windows.js
index 556db2c3044..e8ebba4dd42 100644
--- a/packages/@react-native-windows/tester/src/js/examples/TextInput/TextInputExample.windows.js
+++ b/packages/@react-native-windows/tester/src/js/examples/TextInput/TextInputExample.windows.js
@@ -377,6 +377,36 @@ const examples: Array = [
);
},
},
+ {
+ title: 'Font Family',
+ render: function (): React.Node {
+ return (
+
+
+ {[
+ 'normal',
+ 'Times New Roman',
+ 'Courier New',
+ 'Arial',
+ 'Comic Sans MS',
+ 'Georgia',
+ 'Verdana',
+ ].map(fontFamily => (
+
+ ))}
+
+ );
+ },
+ },
{
title: 'Text input, themes and heights',
render: function (): React.Node {
diff --git a/packages/e2e-test-app-fabric/test/ScrollViewComponentTest.test.ts b/packages/e2e-test-app-fabric/test/ScrollViewComponentTest.test.ts
index f8a4d699e6a..fa0f7467143 100644
--- a/packages/e2e-test-app-fabric/test/ScrollViewComponentTest.test.ts
+++ b/packages/e2e-test-app-fabric/test/ScrollViewComponentTest.test.ts
@@ -49,13 +49,12 @@ describe('ScrollView Tests', () => {
const dump = await dumpVisualTree('flash_scroll_indicators_button');
expect(dump).toMatchSnapshot();
});
- // Disable tests where testID is not found.
- /*test('ScrollViews can scroll an item list horizontally', async () => {
+ test('ScrollViews can scroll an item list horizontally', async () => {
const component = await app.findElementByTestID('scroll_horizontal');
await component.waitForDisplayed({timeout: 20000});
const dump = await dumpVisualTree('scroll_horizontal');
expect(dump).toMatchSnapshot();
- });*/
+ });
test('ScrollView has scrollTo method, scroll to start button', async () => {
const component = await app.findElementByTestID('scroll_to_start_button');
await component.waitForDisplayed({timeout: 20000});
diff --git a/packages/e2e-test-app-fabric/test/TextComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TextComponentTest.test.ts
index 19539c92404..57604a1becb 100644
--- a/packages/e2e-test-app-fabric/test/TextComponentTest.test.ts
+++ b/packages/e2e-test-app-fabric/test/TextComponentTest.test.ts
@@ -111,4 +111,97 @@ describe('Text Tests', () => {
const dump = await dumpVisualTree('advanced-borders');
expect(dump).toMatchSnapshot();
});
+ test('Text can have inline views/images', async () => {
+ const component = await app.findElementByTestID('text-view');
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-view');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can have nested views', async () => {
+ const component = await app.findElementByTestID('text-nested-view');
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-nested-view');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Texts can clip inline View/Images', async () => {
+ const component = await app.findElementByTestID('text-view-images-clipped');
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-view-images-clipped');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, default a', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-default-a',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-default-a');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, default b', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-default-b',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-default-b');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, case 0 a', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-0-a',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-0-a');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, case 0 b', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-0-b',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-0-b');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, case 1 a', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-1-a',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-1-a');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, case 1 b', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-1-b',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-1-b');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, case 2 a', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-2-a',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-2-a');
+ expect(dump).toMatchSnapshot();
+ });
+ test('Text can adjust its fontsize according to its limitations, case 2 b', async () => {
+ const component = await app.findElementByTestID(
+ 'text-adjustfontsizetofit-2-b',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('text-adjustfontsizetofit-2-b');
+ expect(dump).toMatchSnapshot();
+ });
+
+ /* For some reason WebDriver can't find this view even though accessible={true}
+ test('Texts can align inline View/Images', async () => {
+ const component = await app.findElementByTestID(
+ 'view-test-inline-text-alignment',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree('view-test-inline-text-alignment');
+ expect(dump).toMatchSnapshot();
+ });
+ */
});
diff --git a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts
index 7e901d3dcb3..f47ff7b7215 100644
--- a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts
+++ b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts
@@ -236,7 +236,6 @@ describe('TextInput Tests', () => {
await component.waitForDisplayed({timeout: 5000});
const dump = await dumpVisualTree('style-fontFamily');
expect(dump).toMatchSnapshot();
- // Behavior not implemented yet
});
test('TextInputs can have a font size', async () => {
const component = await app.findElementByTestID('style-fontSize');
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/AccessibilityTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/AccessibilityTest.test.ts.snap
index 41968769ec3..cf7f392168f 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/AccessibilityTest.test.ts.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/AccessibilityTest.test.ts.snap
@@ -245,50 +245,58 @@ exports[`Accessibility Tests Selectable items must have a Selection Container. E
"SelectionPattern.IsSelectionRequired": true,
"__Children": [
{
- "AutomationId": "Selectable item 1",
- "ControlType": 50000,
- "IsKeyboardFocusable": true,
- "LocalizedControlType": "button",
- "Name": "Selectable item 1",
+ "AutomationId": "",
+ "ControlType": 50033,
+ "LocalizedControlType": "pane",
+ "Name": "List of selectable items",
"__Children": [
{
- "AutomationId": "",
- "ControlType": 50020,
- "LocalizedControlType": "text",
- "Name": "Unselected",
- "TextRangePattern.GetText": "Unselected",
+ "AutomationId": "Selectable item 1",
+ "ControlType": 50000,
+ "IsKeyboardFocusable": true,
+ "LocalizedControlType": "button",
+ "Name": "Selectable item 1",
+ "__Children": [
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Unselected",
+ "TextRangePattern.GetText": "Unselected",
+ },
+ ],
},
- ],
- },
- {
- "AutomationId": "Selectable item 2",
- "ControlType": 50000,
- "IsKeyboardFocusable": true,
- "LocalizedControlType": "button",
- "Name": "Selectable item 2",
- "__Children": [
{
- "AutomationId": "",
- "ControlType": 50020,
- "LocalizedControlType": "text",
- "Name": "Unselected",
- "TextRangePattern.GetText": "Unselected",
+ "AutomationId": "Selectable item 2",
+ "ControlType": 50000,
+ "IsKeyboardFocusable": true,
+ "LocalizedControlType": "button",
+ "Name": "Selectable item 2",
+ "__Children": [
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Unselected",
+ "TextRangePattern.GetText": "Unselected",
+ },
+ ],
},
- ],
- },
- {
- "AutomationId": "Selectable item 3",
- "ControlType": 50000,
- "IsKeyboardFocusable": true,
- "LocalizedControlType": "button",
- "Name": "Selectable item 3",
- "__Children": [
{
- "AutomationId": "",
- "ControlType": 50020,
- "LocalizedControlType": "text",
- "Name": "Unselected",
- "TextRangePattern.GetText": "Unselected",
+ "AutomationId": "Selectable item 3",
+ "ControlType": 50000,
+ "IsKeyboardFocusable": true,
+ "LocalizedControlType": "button",
+ "Name": "Selectable item 3",
+ "__Children": [
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Unselected",
+ "TextRangePattern.GetText": "Unselected",
+ },
+ ],
},
],
},
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/ImageComponentTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/ImageComponentTest.test.ts.snap
index d23347ab617..a16869bdce1 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/ImageComponentTest.test.ts.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/ImageComponentTest.test.ts.snap
@@ -1431,12 +1431,12 @@ exports[`Image Tests An Image can have a tint color 1`] = `
},
{
"Offset": "0, 29, 0",
- "Size": "916, 20",
+ "Size": "916, 19",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "916, 20",
+ "Size": "916, 19",
"Visual Type": "SpriteVisual",
},
],
@@ -1491,12 +1491,12 @@ exports[`Image Tests An Image can have a tint color 1`] = `
},
{
"Offset": "0, 82, 0",
- "Size": "916, 19",
+ "Size": "916, 20",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "916, 19",
+ "Size": "916, 20",
"Visual Type": "SpriteVisual",
},
],
@@ -2394,12 +2394,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -2460,12 +2460,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "100, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -2526,12 +2526,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "0, 78, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
},
],
@@ -2592,12 +2592,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "100, 78, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
},
],
@@ -2658,12 +2658,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "0, 155, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -2724,12 +2724,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "100, 155, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -2790,12 +2790,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "0, 233, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
},
],
@@ -2856,12 +2856,12 @@ exports[`Image Tests An Image customized how it is rendered within the frame usi
},
{
"Offset": "100, 233, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
},
],
@@ -3255,7 +3255,7 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
"Visual Tree": {
"Comment": "image-resize-mode",
"Offset": "0, 0, 0",
- "Size": "916, 310",
+ "Size": "916, 311",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3392,12 +3392,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "0, 78, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -3458,12 +3458,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "100, 78, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -3524,12 +3524,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "200, 78, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -3590,12 +3590,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "0, 155, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
},
],
@@ -3656,12 +3656,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "100, 155, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 15",
+ "Size": "90, 16",
"Visual Type": "SpriteVisual",
},
],
@@ -3722,12 +3722,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "0, 233, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -3788,12 +3788,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "100, 233, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
@@ -3854,12 +3854,12 @@ exports[`Image Tests Images have multiple resize modes 1`] = `
},
{
"Offset": "200, 233, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "90, 16",
+ "Size": "90, 15",
"Visual Type": "SpriteVisual",
},
],
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/LegacyTextInputTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/LegacyTextInputTest.test.ts.snap
index 987a4a3541b..01aee4fcbbe 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/LegacyTextInputTest.test.ts.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/LegacyTextInputTest.test.ts.snap
@@ -32,10 +32,12 @@ exports[`LegacyTextInputTest Click on multiline TextInput to move focus away fro
"AutomationId": "textinput-log",
"ControlType": 50020,
"LocalizedControlType": "text",
- "Name": "onBlur
+ "Name": "onEndEditing text:
+onBlur
onFocus
",
- "TextRangePattern.GetText": "onBlur
+ "TextRangePattern.GetText": "onEndEditing text:
+onBlur
onFocus
",
},
@@ -48,7 +50,7 @@ onFocus
"Visual Tree": {
"Comment": "textinput-log",
"Offset": "0, 0, 0",
- "Size": "998, 57",
+ "Size": "998, 76",
"Visual Type": "SpriteVisual",
},
}
@@ -66,6 +68,7 @@ onKeyPress key: a
onChange text:
onSelectionChange range: 0,0
onFocus
+onEndEditing text: abc
onBlur
onSubmitEditing text: abc
onChange text: abc
@@ -97,10 +100,10 @@ onChange text: ab
onSelectionChange range: 2,2
onKeyPress key: b
onChange text: a
-onChange text: a
onSelectionChange range: 1,1
onKeyPress key: a
onFocus
+onEndEditing text:
onBlur
onFocus
",
@@ -110,6 +113,7 @@ onKeyPress key: a
onChange text:
onSelectionChange range: 0,0
onFocus
+onEndEditing text: abc
onBlur
onSubmitEditing text: abc
onChange text: abc
@@ -141,10 +145,10 @@ onChange text: ab
onSelectionChange range: 2,2
onKeyPress key: b
onChange text: a
-onChange text: a
onSelectionChange range: 1,1
onKeyPress key: a
onFocus
+onEndEditing text:
onBlur
onFocus
",
@@ -158,7 +162,7 @@ onFocus
"Visual Tree": {
"Comment": "textinput-log",
"Offset": "0, 0, 0",
- "Size": "998, 820",
+ "Size": "998, 839",
"Visual Type": "SpriteVisual",
},
}
@@ -177,10 +181,10 @@ onChange text: ab
onSelectionChange range: 2,2
onKeyPress key: b
onChange text: a
-onChange text: a
onSelectionChange range: 1,1
onKeyPress key: a
onFocus
+onEndEditing text:
onBlur
onFocus
",
@@ -191,10 +195,10 @@ onChange text: ab
onSelectionChange range: 2,2
onKeyPress key: b
onChange text: a
-onChange text: a
onSelectionChange range: 1,1
onKeyPress key: a
onFocus
+onEndEditing text:
onBlur
onFocus
",
@@ -250,10 +254,10 @@ onChange text: ab
onSelectionChange range: 2,2
onKeyPress key: b
onChange text: a
-onChange text: a
onSelectionChange range: 1,1
onKeyPress key: a
onFocus
+onEndEditing text:
onBlur
onFocus
",
@@ -287,10 +291,10 @@ onChange text: ab
onSelectionChange range: 2,2
onKeyPress key: b
onChange text: a
-onChange text: a
onSelectionChange range: 1,1
onKeyPress key: a
onFocus
+onEndEditing text:
onBlur
onFocus
",
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/ScrollViewComponentTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/ScrollViewComponentTest.test.ts.snap
index f3bb0cfe118..efb33774859 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/ScrollViewComponentTest.test.ts.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/ScrollViewComponentTest.test.ts.snap
@@ -224,6 +224,653 @@ exports[`ScrollView Tests ScrollView has scrollTo method, scroll to top button 1
}
`;
+exports[`ScrollView Tests ScrollViews can scroll an item list horizontally 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "scroll_horizontal",
+ "ControlType": 50033,
+ "LocalizedControlType": "pane",
+ "ScrollPattern.HorizontallyScrollable": true,
+ "__Children": [
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 0",
+ "TextRangePattern.GetText": "Item 0",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 1",
+ "TextRangePattern.GetText": "Item 1",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 2",
+ "TextRangePattern.GetText": "Item 2",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 3",
+ "TextRangePattern.GetText": "Item 3",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 4",
+ "TextRangePattern.GetText": "Item 4",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 5",
+ "TextRangePattern.GetText": "Item 5",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 6",
+ "TextRangePattern.GetText": "Item 6",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 7",
+ "TextRangePattern.GetText": "Item 7",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 8",
+ "TextRangePattern.GetText": "Item 8",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 9",
+ "TextRangePattern.GetText": "Item 9",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 10",
+ "TextRangePattern.GetText": "Item 10",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Item 11",
+ "TextRangePattern.GetText": "Item 11",
+ },
+ ],
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ScrollViewComponentView",
+ "_Props": {
+ "TestId": "scroll_horizontal",
+ },
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ ],
+ },
+ ],
+ },
+ "Visual Tree": {
+ "Comment": "scroll_horizontal",
+ "Offset": "0, 0, 0",
+ "Size": "916, 106",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(238, 238, 238, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "916, 106",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(0, 0, 0, 0)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "1272, 106",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "1272, 106",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "1272, 106",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "5, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "10, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "111, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "116, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "217, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "222, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "323, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "328, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "429, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "434, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "535, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "540, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "641, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "646, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "747, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "752, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "853, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "858, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "959, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "964, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "1065, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "1070, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "1171, 5, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(204, 204, 204, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "96, 96",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "1176, 10, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "86, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ "Offset": "-13, 0, 0",
+ "Size": "12, 0",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "-12, 3, 0",
+ "Opacity": 0,
+ "Size": "12, 100",
+ "Visual Type": "Visual",
+ },
+ {
+ "Offset": "0, 4, 0",
+ "Opacity": 0,
+ "Size": "12, 12",
+ "Visual Type": "SpriteVisual",
+ },
+ {
+ "Offset": "0, -16, 0",
+ "Opacity": 0,
+ "Size": "12, 12",
+ "Visual Type": "SpriteVisual",
+ },
+ {
+ "Offset": "-5, 16, 0",
+ "Size": "6, 74",
+ "Visual Type": "Visual",
+ },
+ ],
+ },
+ {
+ "Offset": "0, -13, 0",
+ "Size": "0, 12",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "3, -12, 0",
+ "Opacity": 0,
+ "Size": "910, 12",
+ "Visual Type": "Visual",
+ },
+ {
+ "Offset": "4, 0, 0",
+ "Opacity": 0,
+ "Size": "12, 12",
+ "Visual Type": "SpriteVisual",
+ },
+ {
+ "Offset": "-16, 0, 0",
+ "Opacity": 0,
+ "Size": "12, 12",
+ "Visual Type": "SpriteVisual",
+ },
+ {
+ "Offset": "16, -5, 0",
+ "Size": "637, 6",
+ "Visual Type": "Visual",
+ },
+ ],
+ },
+ ],
+ },
+}
+`;
+
exports[`ScrollView Tests ScrollViews has flash scroll indicators 1`] = `
{
"Automation Tree": {
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/TextComponentTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/TextComponentTest.test.ts.snap
index 65351819fdb..a2f65353ca4 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/TextComponentTest.test.ts.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/TextComponentTest.test.ts.snap
@@ -24,6 +24,198 @@ exports[`Text Tests Padding can be added to Text 1`] = `
}
`;
+exports[`Text Tests Text can adjust its fontsize according to its limitations, case 0 a 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-0-a",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:80,width:500,lineCount:3,fontSize:40",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:80,width:500,lineCount:3,fontSize:40",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-0-a",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-0-a",
+ "Offset": "0, 0, 0",
+ "Size": "500, 80",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, case 0 b 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-0-b",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:80,width:500,lineCount:3,fontSize:40",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:80,width:500,lineCount:3,fontSize:40",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-0-b",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-0-b",
+ "Offset": "0, 0, 0",
+ "Size": "500, 80",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, case 1 a 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-1-a",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:120,width:475,lineCount:5,fontSize:40",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:120,width:475,lineCount:5,fontSize:40",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-1-a",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-1-a",
+ "Offset": "0, 0, 0",
+ "Size": "475, 120",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, case 1 b 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-1-b",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:120,width:475,lineCount:5,fontSize:40",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:120,width:475,lineCount:5,fontSize:40",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-1-b",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-1-b",
+ "Offset": "0, 0, 0",
+ "Size": "475, 120",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, case 2 a 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-2-a",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:160,width:450,lineCount:0,fontSize:40",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:160,width:450,lineCount:0,fontSize:40",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-2-a",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-2-a",
+ "Offset": "0, 0, 0",
+ "Size": "450, 160",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, case 2 b 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-2-b",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:160,width:450,lineCount:0,fontSize:40",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:160,width:450,lineCount:0,fontSize:40",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-2-b",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-2-b",
+ "Offset": "0, 0, 0",
+ "Size": "450, 160",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, default a 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-default-a",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-default-a",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-default-a",
+ "Offset": "0, 0, 0",
+ "Size": "500, 100",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
+exports[`Text Tests Text can adjust its fontsize according to its limitations, default b 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-adjustfontsizetofit-default-b",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20",
+ "TextRangePattern.GetText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-adjustfontsizetofit-default-b",
+ },
+ },
+ "Visual Tree": {
+ "Comment": "text-adjustfontsizetofit-default-b",
+ "Offset": "0, 0, 0",
+ "Size": "500, 100",
+ "Visual Type": "SpriteVisual",
+ },
+}
+`;
+
exports[`Text Tests Text can be restricted to one line 1`] = `
{
"Automation Tree": {
@@ -724,6 +916,177 @@ exports[`Text Tests Text can have decoration lines: Underline 1`] = `
}
`;
+exports[`Text Tests Text can have inline views/images 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-view",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "This text contains an inline blue view  and an inline image . Neat, huh?",
+ "TextRangePattern.GetText": "This text contains an inline blue view ￯﾿ᄐ and an inline image ￯﾿ᄐ. Neat, huh?",
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {
+ "TestId": "text-view",
+ },
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ImageComponentView",
+ "_Props": {
+ "Sources": [
+ {
+ "Scale": 3,
+ "Size": "33, 33",
+ "Type": "Local",
+ "Uri": "@react-native-windows/tester/js/assets/flux@3x.png",
+ },
+ ],
+ },
+ },
+ ],
+ },
+ "Visual Tree": {
+ "Comment": "text-view",
+ "Offset": "0, 0, 0",
+ "Size": "916, 26",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "229, 0, 0",
+ "Size": "26, 19",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(70, 130, 180, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "26, 19",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "384, 0, 0",
+ "Size": "34, 23",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "34, 23",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ ],
+ },
+}
+`;
+
+exports[`Text Tests Text can have nested views 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-nested-view",
+ "ControlType": 50026,
+ "LocalizedControlType": "group",
+ "__Children": [
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "This is the first row",
+ "TextRangePattern.GetText": "This is the first row",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "This is a nested text  with a Red View",
+ "TextRangePattern.GetText": "This is a nested text ￯﾿ᄐ with a Red View",
+ },
+ ],
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {
+ "TestId": "text-nested-view",
+ },
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ ],
+ },
+ ],
+ },
+ "Visual Tree": {
+ "Comment": "text-nested-view",
+ "Offset": "0, 0, 0",
+ "Size": "916, 40",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "916, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "916, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "0, 18, 0",
+ "Size": "916, 23",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "916, 23",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "125, 0, 0",
+ "Size": "21, 19",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(255, 0, 0, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "21, 19",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+}
+`;
+
exports[`Text Tests Text can have shadows 1`] = `
{
"Automation Tree": {
@@ -742,7 +1105,7 @@ exports[`Text Tests Text can have shadows 1`] = `
"Visual Tree": {
"Comment": "text-shadow",
"Offset": "0, 0, 0",
- "Size": "916, 27",
+ "Size": "916, 28",
"Visual Type": "SpriteVisual",
},
}
@@ -771,3 +1134,195 @@ exports[`Text Tests Text can wrap 1`] = `
},
}
`;
+
+exports[`Text Tests Texts can clip inline View/Images 1`] = `
+{
+ "Automation Tree": {
+ "AutomationId": "text-view-images-clipped",
+ "ControlType": 50026,
+ "LocalizedControlType": "group",
+ "__Children": [
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "The inline view below is taller than its Text parent and should be clipped.",
+ "TextRangePattern.GetText": "The inline view below is taller than its Text parent and should be clipped.",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "This is an inline view",
+ "TextRangePattern.GetText": "This is an inline view￯﾿ᄐ",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "The inline image below is taller than its Text parent and should be clipped.",
+ "TextRangePattern.GetText": "The inline image below is taller than its Text parent and should be clipped.",
+ },
+ {
+ "AutomationId": "",
+ "ControlType": 50020,
+ "LocalizedControlType": "text",
+ "Name": "This is an inline image",
+ "TextRangePattern.GetText": "This is an inline image￯﾿ᄐ",
+ },
+ ],
+ },
+ "Component Tree": {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {
+ "TestId": "text-view-images-clipped",
+ },
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ViewComponentView",
+ "_Props": {},
+ },
+ ],
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ },
+ {
+ "Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
+ "_Props": {},
+ "__Children": [
+ {
+ "Type": "Microsoft.ReactNative.Composition.ImageComponentView",
+ "_Props": {
+ "Sources": [
+ {
+ "Size": "50, 100",
+ "Type": "Remote",
+ "Uri": "https://picsum.photos/100",
+ },
+ ],
+ },
+ },
+ ],
+ },
+ ],
+ },
+ "Visual Tree": {
+ "Comment": "text-view-images-clipped",
+ "Offset": "0, 0, 0",
+ "Size": "916, 223",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "916, 19",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "916, 19",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "0, 18, 0",
+ "Size": "150, 75",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "150, 75",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 19, 0",
+ "Size": "51, 19",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(255, 0, 0, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "51, 19",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "1, 20, 0",
+ "Size": "48, 98",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Brush": {
+ "Brush Type": "ColorBrush",
+ "Color": "rgba(70, 130, 180, 255)",
+ },
+ "Offset": "0, 0, 0",
+ "Size": "48, 98",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ "Offset": "0, 103, 0",
+ "Size": "916, 20",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "916, 20",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ {
+ "Offset": "0, 122, 0",
+ "Size": "175, 100",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "175, 100",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 19, 0",
+ "Size": "51, 19",
+ "Visual Type": "SpriteVisual",
+ "__Children": [
+ {
+ "Offset": "0, 0, 0",
+ "Size": "51, 19",
+ "Visual Type": "SpriteVisual",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+}
+`;
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap
index b4f9dfedc41..f2a980160d8 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap
@@ -333,7 +333,7 @@ exports[`TextInput Tests Text have cursorColor 1`] = `
"Visual Tree": {
"Comment": "textinput-cursorColor",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -561,7 +561,7 @@ exports[`TextInput Tests TextInputs can autocomplete, address country 1`] = `
"Visual Tree": {
"Comment": "textinput-autocomplete-address-country",
"Offset": "0, 0, 0",
- "Size": "916, 28",
+ "Size": "916, 29",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -717,7 +717,7 @@ exports[`TextInput Tests TextInputs can autocomplete, one-time-code 1`] = `
"Visual Tree": {
"Comment": "textinput-autocomplete-one-time-code",
"Offset": "0, 0, 0",
- "Size": "916, 29",
+ "Size": "916, 28",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -872,7 +872,7 @@ exports[`TextInput Tests TextInputs can autogrow 1`] = `
"Visual Tree": {
"Comment": "textinput-autogrow",
"Offset": "0, 0, 0",
- "Size": "300, 131",
+ "Size": "300, 130",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1002,12 +1002,12 @@ exports[`TextInput Tests TextInputs can be defined as a set using accessibilityP
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1130,12 +1130,12 @@ exports[`TextInput Tests TextInputs can be defined as a set using accessibilityP
},
{
"Offset": "0, 62, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1527,7 +1527,7 @@ exports[`TextInput Tests TextInputs can be set to not editable 1`] = `
"Visual Tree": {
"Comment": "textinput-not-editable",
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1605,7 +1605,7 @@ exports[`TextInput Tests TextInputs can be set to not editable 2 1`] = `
"Visual Tree": {
"Comment": "textinput-not-editable2",
"Offset": "0, 0, 0",
- "Size": "916, 29",
+ "Size": "916, 28",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1680,7 +1680,7 @@ exports[`TextInput Tests TextInputs can clear on submit 1`] = `
"Visual Tree": {
"Comment": "textinput-clear-on-submit",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1830,7 +1830,7 @@ exports[`TextInput Tests TextInputs can clear on submit with custom submit key e
"Visual Tree": {
"Comment": "textinput-clear-on-submit-2",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -1905,7 +1905,7 @@ exports[`TextInput Tests TextInputs can customize its padding 1`] = `
"Visual Tree": {
"Comment": "textinput-padding",
"Offset": "0, 0, 0",
- "Size": "916, 29",
+ "Size": "916, 28",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -2601,7 +2601,7 @@ exports[`TextInput Tests TextInputs can have caretHidden 1`] = `
"Visual Tree": {
"Comment": "textinput-carethidden",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -2679,7 +2679,7 @@ exports[`TextInput Tests TextInputs can have custom return key label, Compile 1`
"Visual Tree": {
"Comment": "textinput-return-Compile",
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -2757,7 +2757,7 @@ exports[`TextInput Tests TextInputs can have custom return key label, React Nati
"Visual Tree": {
"Comment": "textinput-return-React Native",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -2913,7 +2913,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, go 1`] = `
"Visual Tree": {
"Comment": "textinput-return-go",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3069,7 +3069,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, none 1`] =
"Visual Tree": {
"Comment": "textinput-return-none",
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3147,7 +3147,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, previous 1`
"Visual Tree": {
"Comment": "textinput-return-previous",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3303,7 +3303,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, send 1`] =
"Visual Tree": {
"Comment": "textinput-return-send",
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3381,7 +3381,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=-1
"Visual Tree": {
"Comment": "textinput-letterspacing--1",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3459,7 +3459,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=0
"Visual Tree": {
"Comment": "textinput-letterspacing-0",
"Offset": "0, 0, 0",
- "Size": "916, 33",
+ "Size": "916, 32",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -3615,7 +3615,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=9
"Visual Tree": {
"Comment": "textinput-letterspacing-9",
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -4021,7 +4021,7 @@ exports[`TextInput Tests TextInputs can have inline images, drawable props not s
"Visual Tree": {
"Comment": "textinput-inline-images-3",
"Offset": "0, 0, 0",
- "Size": "916, 32",
+ "Size": "916, 33",
"Visual Type": "SpriteVisual",
"__Children": [
{
@@ -4868,7 +4868,7 @@ exports[`TextInput Tests TextInputs can set their readOnly prop to true 1`] = `
"Visual Tree": {
"Comment": "textinput-readyonly",
"Offset": "0, 0, 0",
- "Size": "916, 28",
+ "Size": "916, 29",
"Visual Type": "SpriteVisual",
"__Children": [
{
diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap
index 603a99fb4a9..a110d429ffe 100644
--- a/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap
+++ b/packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap
@@ -27878,8 +27878,10 @@ exports[`snapshotAllPages LegacyTextInputTest 1`] = `
testID="textinput-field"
/>
`;
+exports[`snapshotAllPages Text 46`] = `
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore Ut enim ad minim veniam.With AdjustFontSize width: 800, height: 100, fontSize: 20
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:80,width:500,lineCount:3,fontSize:40
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:80,width:500,lineCount:3,fontSize:40
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:120,width:475,lineCount:5,fontSize:40
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:120,width:475,lineCount:5,fontSize:40
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. With AdjustFontSize height:160,width:450,lineCount:0,fontSize:40
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. Without AdjustFontSize height:160,width:450,lineCount:0,fontSize:40
+
+
+`;
+
exports[`snapshotAllPages TextInput 1`] = `
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`snapshotAllPages TextInput 24`] = `
`;
-exports[`snapshotAllPages TextInput 24`] = `
+exports[`snapshotAllPages TextInput 25`] = `
`;
-exports[`snapshotAllPages TextInput 25`] = `
+exports[`snapshotAllPages TextInput 26`] = `
`;
-exports[`snapshotAllPages TextInput 26`] = `
+exports[`snapshotAllPages TextInput 27`] = `
`;
-exports[`snapshotAllPages TextInput 27`] = `
+exports[`snapshotAllPages TextInput 28`] = `
`;
-exports[`snapshotAllPages TextInput 28`] = `
+exports[`snapshotAllPages TextInput 29`] = `
`;
-exports[`snapshotAllPages TextInput 29`] = `
+exports[`snapshotAllPages TextInput 30`] = `
`;
-exports[`snapshotAllPages TextInput 30`] = `
+exports[`snapshotAllPages TextInput 31`] = `
`;
-exports[`snapshotAllPages TextInput 31`] = `
+exports[`snapshotAllPages TextInput 32`] = `
`;
-exports[`snapshotAllPages TextInput 32`] = `
+exports[`snapshotAllPages TextInput 33`] = `
`;
-exports[`snapshotAllPages TextInput 33`] = `
+exports[`snapshotAllPages TextInput 34`] = `
`;
-exports[`snapshotAllPages TextInput 34`] = `
+exports[`snapshotAllPages TextInput 35`] = `
`;
-exports[`snapshotAllPages TextInput 35`] = `
+exports[`snapshotAllPages TextInput 36`] = `
PressIn/PressOut message
@@ -74998,7 +75363,7 @@ exports[`snapshotAllPages TextInput 35`] = `
`;
-exports[`snapshotAllPages TextInput 36`] = `
+exports[`snapshotAllPages TextInput 37`] = `
Default submit key (Enter):
@@ -75134,7 +75499,7 @@ exports[`snapshotAllPages TextInput 36`] = `
`;
-exports[`snapshotAllPages TextInput 37`] = `
+exports[`snapshotAllPages TextInput 38`] = `
[
Spell Check Enabled:
@@ -75249,7 +75614,7 @@ exports[`snapshotAllPages TextInput 38`] = `
]
`;
-exports[`snapshotAllPages TextInput 39`] = `
+exports[`snapshotAllPages TextInput 40`] = `
CaretHidden
@@ -75281,7 +75646,7 @@ exports[`snapshotAllPages TextInput 39`] = `
`;
-exports[`snapshotAllPages TextInput 40`] = `
+exports[`snapshotAllPages TextInput 41`] = `
Cursorcolor
@@ -75313,7 +75678,7 @@ exports[`snapshotAllPages TextInput 40`] = `
`;
-exports[`snapshotAllPages TextInput 41`] = `
+exports[`snapshotAllPages TextInput 42`] = `
Shadow
@@ -75351,7 +75716,7 @@ exports[`snapshotAllPages TextInput 41`] = `
`;
-exports[`snapshotAllPages TextInput 42`] = `
+exports[`snapshotAllPages TextInput 43`] = `
`;
-exports[`snapshotAllPages TextInput 43`] = `
+exports[`snapshotAllPages TextInput 44`] = `
scrollPattern;
+ hr = pTarget->GetCurrentPattern(UIA_ScrollPatternId, reinterpret_cast(scrollPattern.put()));
+ if (SUCCEEDED(hr) && scrollPattern) {
+ hr = scrollPattern->get_HorizontallyScrollable(&horizontallyScrollable);
+ if (SUCCEEDED(hr)) {
+ InsertBooleanValueIfNotDefault(result, L"ScrollPattern.HorizontallyScrollable", horizontallyScrollable, false);
+ }
+ }
+
::SysFreeString(text);
::SysFreeString(value);
}
diff --git a/packages/playground/Samples/image.tsx b/packages/playground/Samples/image.tsx
index cc93315baa5..f495b23fb4e 100644
--- a/packages/playground/Samples/image.tsx
+++ b/packages/playground/Samples/image.tsx
@@ -15,11 +15,18 @@ import {
PlatformColor,
} from 'react-native';
+const loadingImageUri =
+ 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4gPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyOCAyOCIgZmlsbD0ibm9uZSI+PHBhdGggZD0iTTEzLjEyNSAwSDBWMTMuMTI1SDEzLjEyNVYwWiIgZmlsbD0iI0YyNTAyMiI+PC9wYXRoPjxwYXRoIGQ9Ik0yOCAwSDE0Ljg3NVYxMy4xMjVIMjhWMFoiIGZpbGw9IiM3RkJBMDAiPjwvcGF0aD48cGF0aCBkPSJNMTMuMTI1IDE0Ljg3NUgwVjI4SDEzLjEyNVYxNC44NzVaIiBmaWxsPSIjMDBBNEVGIj48L3BhdGg+PHBhdGggZD0iTTI4IDE0Ljg3NUgxNC44NzVWMjhIMjhWMTQuODc1WiIgZmlsbD0iI0ZGQjkwMCI+PC9wYXRoPjwvc3ZnPiA=';
+
const largeImageUri =
'https://cdn.freebiesupply.com/logos/large/2x/react-logo-png-transparent.png';
-const smallImageUri =
- 'https://facebook.github.io/react-native/img/header_logo.png';
+const smallImageUri = 'https://reactnative.dev/img/tiny_logo.png';
+
+const flowerImageUri =
+ 'https://cdn.pixabay.com/photo/2021/08/02/00/10/flowers-6515538_1280.jpg';
+
+const reactLogoUri = 'https://reactjs.org/logo-og.png';
const dataImageUri =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==';
@@ -42,6 +49,10 @@ export default class Bootstrap extends React.Component<
selectedSource: string;
imageUri: string;
tintColor: string;
+ modalVisible: boolean;
+ currentPicker: string | null;
+ defaultImageUri: string;
+ includedefaultSourceOnly: boolean;
}
> {
state = {
@@ -50,7 +61,11 @@ export default class Bootstrap extends React.Component<
includeBorder: false,
tintColor: 'transparent',
blurRadius: 0,
- imageUri: 'https://facebook.github.io/react-native/img/header_logo.png',
+ imageUri: smallImageUri,
+ modalVisible: false,
+ currentPicker: '',
+ defaultImageUri: reactLogoUri,
+ includedefaultSourceOnly: false,
};
switchImageUri = (value: string) => {
@@ -60,6 +75,8 @@ export default class Bootstrap extends React.Component<
if (value === 'small') {
imageUri = smallImageUri;
+ } else if (value === 'flower') {
+ imageUri = flowerImageUri;
} else if (value === 'large') {
imageUri = largeImageUri;
} else if (value === 'data-svg') {
@@ -71,6 +88,60 @@ export default class Bootstrap extends React.Component<
this.setState({imageUri});
};
+ setModalVisible = (visible: boolean, pickerName: string | null = null) => {
+ this.setState({modalVisible: visible, currentPicker: pickerName});
+ };
+
+ setSelection = (value: any) => {
+ const {currentPicker} = this.state;
+ switch (currentPicker) {
+ case 'resizeMode':
+ this.setState({selectedResizeMode: value});
+ break;
+ case 'imageSource':
+ this.switchImageUri(value);
+ break;
+ case 'blurRadius':
+ this.setState({blurRadius: value});
+ break;
+ case 'tintColor':
+ this.setState({tintColor: value});
+ break;
+ default:
+ break;
+ }
+
+ this.setModalVisible(false);
+ };
+
+ handleResizeModesSelect = (value: any) => {
+ this.setState({selectedResizeMode: value});
+ this.state.currentPicker = 'resizeMode';
+ this.setSelection(value);
+ };
+
+ handleImageSourcesSelect = (value: any) => {
+ this.setState({selectedSource: value});
+ this.state.currentPicker = 'imageSource';
+ this.setSelection(value);
+ };
+
+ handleBlurRadiusSelect = (value: any) => {
+ this.setState({blurRadius: value});
+ this.state.currentPicker = 'blurRadius';
+ this.setSelection(value);
+ };
+
+ handleTintColorSelect = (value: any) => {
+ this.setState({tintColor: value});
+ this.state.currentPicker = 'tintColor';
+ this.setSelection(value);
+ };
+
+ handleOnProgress = (event: any) => {
+ const {progress, loaded, total} = event.nativeEvent;
+ console.log(`Progress: ${progress}, Loaded = ${loaded} , Total = ${total}`);
+ };
render() {
return (
@@ -151,6 +222,49 @@ export default class Bootstrap extends React.Component<
resizeMode={this.state.selectedResizeMode}
blurRadius={this.state.blurRadius}
/>
+ Include defaultSource Only
+
+
+ {this.state.includedefaultSourceOnly ? (
+
+ ) : (
+ console.log('onLoad')}
+ onLoadStart={() => console.log('onLoadStart')}
+ onLoadEnd={() => console.log('onLoadEnd')}
+ onProgress={this.handleOnProgress}
+ />
+ )}
);
@@ -178,6 +292,12 @@ const styles = StyleSheet.create({
height: '50%',
width: '75%',
},
+ imageContainerDefault: {
+ marginTop: 5,
+ backgroundColor: 'skyblue',
+ height: '50%',
+ width: '75%',
+ },
image: {
height: '100%',
width: '100%',
@@ -191,6 +311,13 @@ const styles = StyleSheet.create({
imageWithPlatformColor: {
tintColor: PlatformColor('SystemAccentColor'),
},
+ imageWithPlatformColorPrimary: {
+ tintColor: PlatformColor('TextFillColorPrimary'),
+ },
+ loading: {
+ height: '10%',
+ width: '10%',
+ },
title: {
fontWeight: 'bold',
margin: 5,
diff --git a/packages/playground/Samples/scrollViewSnapSample.tsx b/packages/playground/Samples/scrollViewSnapSample.tsx
index e12c779f224..7683284be4b 100644
--- a/packages/playground/Samples/scrollViewSnapSample.tsx
+++ b/packages/playground/Samples/scrollViewSnapSample.tsx
@@ -33,6 +33,8 @@ export default class Bootstrap extends React.Component<{}, any> {
keyboardDismiss: false,
snapToOffsets: true,
pagingEnabled: false,
+ showsHorizontalScrollIndicatorValue: true,
+ showsVerticalScrollIndicatorValue: false,
};
toggleSwitch1 = (value: boolean) => {
@@ -67,6 +69,14 @@ export default class Bootstrap extends React.Component<{}, any> {
this.setState({keyboardDismiss: value});
};
+ toggleSwitch9 = (value: boolean) => {
+ this.setState({showsHorizontalScrollIndicatorValue: value});
+ };
+
+ toggleSwitch10 = (value: boolean) => {
+ this.setState({showsVerticalScrollIndicatorValue: value});
+ };
+
onRefresh = () => {
this.setState({refreshing: true});
void wait(2000).then(() => this.setState({refreshing: false}));
@@ -109,6 +119,42 @@ export default class Bootstrap extends React.Component<{}, any> {
value={this.state.horizontalValue}
/>
+
+
+ {' '}
+ {this.state.showsHorizontalScrollIndicatorValue
+ ? 'Show Horizontal Indicator'
+ : 'Hide Horizontal Indicator'}
+
+
+
+
+
+ {' '}
+ {this.state.showsVerticalScrollIndicatorValue
+ ? 'Show Vertical Indicator'
+ : 'Hide Vertical Indicator'}
+
+
+
{
snapToStart={this.state.snapToStartValue}
snapToEnd={this.state.snapToEndValue}
snapToAlignment={this.state.alignToStartValue ? 'start' : 'end'}
- horizontal={this.state.horizontalValue}>
+ horizontal={this.state.horizontalValue}
+ showsHorizontalScrollIndicator={
+ this.state.showsHorizontalScrollIndicatorValue
+ }
+ showsVerticalScrollIndicator={
+ this.state.showsVerticalScrollIndicatorValue
+ }
+ onScrollBeginDrag={() => {
+ console.log('onScrollBeginDrag');
+ }}
+ onScroll={() => {
+ console.log('onScroll');
+ }}
+ decelerationRate={0.95}
+ scrollEventThrottle={50}>
{this.makeItems(20, [styles.itemWrapper])}
diff --git a/packages/playground/Samples/text.tsx b/packages/playground/Samples/text.tsx
index 54ce125b651..79bae6a10da 100644
--- a/packages/playground/Samples/text.tsx
+++ b/packages/playground/Samples/text.tsx
@@ -5,13 +5,29 @@
*/
import React from 'react';
-import {AppRegistry, StyleSheet, Text, View} from 'react-native';
+import {AppRegistry, StyleSheet, View} from 'react-native';
+import {Text} from 'react-native-windows';
export default class Bootstrap extends React.Component {
render() {
return (
Welcome to React Native!
+
+ Click here : This is a text with a tooltip.
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
+ ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
+ aliquip ex ea commodo consequat.
+
+
);
}
@@ -24,6 +40,13 @@ const styles = StyleSheet.create({
alignItems: 'center',
backgroundColor: '#C5CCFF',
},
+ container2: {
+ backgroundColor: 'lightcoral',
+ padding: 10,
+ marginBottom: 10,
+ width: 500,
+ height: 100,
+ },
welcome: {
fontSize: 20,
textAlign: 'center',
diff --git a/packages/playground/Samples/textinput.tsx b/packages/playground/Samples/textinput.tsx
index dd87eb6f734..70d52a1b0f2 100644
--- a/packages/playground/Samples/textinput.tsx
+++ b/packages/playground/Samples/textinput.tsx
@@ -14,6 +14,7 @@ import {
View,
KeyboardAvoidingView,
ScrollView,
+ TouchableWithoutFeedback,
} from 'react-native';
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
@@ -49,6 +50,7 @@ export default class Bootstrap extends React.Component<{}, any> {
state = {
passwordHidden: true,
text: '',
+ endEditingText: '',
};
onPressShowPassword = () => {
@@ -56,141 +58,203 @@ export default class Bootstrap extends React.Component<{}, any> {
this.setState({passwordHidden: !previousState});
};
+ handleEndEditing = (event: any) => {
+ const text = event.nativeEvent.text;
+ this.setState({endEditingText: text});
+ console.log('Text input focus lost:', text);
+ };
+
render() {
let textInputRef: TextInput | null;
return (
-
-
-
-
-
-
-
-
-
-
- (textInputRef = ref)}
- onFocus={() => setTimeout(() => textInputRef?.blur(), 5000)}
- placeholder={'blurs after 5 seconds'}
- style={styles.input}
- />
- {
- this.setState({text});
- }}
- value={this.state.text}
- selectionColor="red"
- maxLength={10}
- keyboardType="numeric"
- />
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (textInputRef = ref)}
+ onFocus={() => setTimeout(() => textInputRef?.blur(), 5000)}
+ placeholder={'blurs after 5 seconds'}
+ style={styles.input}
+ />
+ {
+ this.setState({text});
+ }}
+ value={this.state.text}
+ selectionColor="red"
+ maxLength={10}
+ keyboardType="numeric"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
}
diff --git a/packages/playground/windows/playground-composition.Package/packages.lock.json b/packages/playground/windows/playground-composition.Package/packages.lock.json
index f03c19f9f52..99870dd293c 100644
--- a/packages/playground/windows/playground-composition.Package/packages.lock.json
+++ b/packages/playground/windows/playground-composition.Package/packages.lock.json
@@ -2,59 +2,6 @@
"version": 1,
"dependencies": {
"UAP,Version=v10.0.17763": {
- "boost": {
- "type": "Transitive",
- "resolved": "1.83.0",
- "contentHash": "cy53VNMzysEMvhBixDe8ujPk67Fcj3v6FPHQnH91NYJNLHpc6jxa2xq9ruCaaJjE4M3YrGSHDi4uUSTGBWw6EQ=="
- },
- "Microsoft.Build.Tasks.Git": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "AT3HlgTjsqHnWpBHSNeR0KxbLZD7bztlZVj7I8vgeYG9SYqbeFGh0TM/KVtC6fg53nrWHl3VfZFvb5BiQFcY6Q=="
- },
- "Microsoft.JavaScript.Hermes": {
- "type": "Transitive",
- "resolved": "0.1.23",
- "contentHash": "cA9t1GjY4Yo0JD1AfA//e1lOwk48hLANfuX6GXrikmEBNZVr2TIX5ONJt5tqCnpZyLz6xGiPDgTfFNKbSfb21g=="
- },
- "Microsoft.SourceLink.Common": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "WMcGpWKrmJmzrNeuaEb23bEMnbtR/vLmvZtkAP5qWu7vQsY59GqfRJd65sFpBszbd2k/bQ8cs8eWawQKAabkVg=="
- },
- "Microsoft.SourceLink.GitHub": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
- "dependencies": {
- "Microsoft.Build.Tasks.Git": "1.1.1",
- "Microsoft.SourceLink.Common": "1.1.1"
- }
- },
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.Windows.SDK.BuildTools": {
- "type": "Transitive",
- "resolved": "10.0.22621.756",
- "contentHash": "7ZL2sFSioYm1Ry067Kw1hg0SCcW5kuVezC2SwjGbcPE61Nn+gTbH86T73G3LcEOVj0S3IZzNuE/29gZvOLS7VA=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- },
"common": {
"type": "Project"
},
@@ -72,21 +19,14 @@
"dependencies": {
"Common": "[1.0.0, )",
"Folly": "[1.0.0, )",
- "Microsoft.JavaScript.Hermes": "[0.1.23, )",
- "Microsoft.SourceLink.GitHub": "[1.1.1, )",
- "Microsoft.WindowsAppSDK": "[1.6.240923002, )",
- "ReactCommon": "[1.0.0, )",
- "boost": "[1.83.0, )"
+ "ReactCommon": "[1.0.0, )"
}
},
"playground-composition": {
"type": "Project",
"dependencies": {
"Microsoft.ReactNative": "[1.0.0, )",
- "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )",
- "Microsoft.WindowsAppSDK": "[1.6.240923002, )",
- "SampleCustomComponent": "[1.0.0, )",
- "boost": "[1.83.0, )"
+ "SampleCustomComponent": "[1.0.0, )"
}
},
"reactcommon": {
@@ -98,159 +38,16 @@
"samplecustomcomponent": {
"type": "Project",
"dependencies": {
- "Microsoft.ReactNative": "[1.0.0, )",
- "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )",
- "Microsoft.WindowsAppSDK": "[1.6.240923002, )",
- "boost": "[1.83.0, )"
- }
- }
- },
- "UAP,Version=v10.0.17763/win10-arm": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- }
- },
- "UAP,Version=v10.0.17763/win10-arm-aot": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
+ "Microsoft.ReactNative": "[1.0.0, )"
}
}
},
- "UAP,Version=v10.0.17763/win10-arm64-aot": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- }
- },
- "UAP,Version=v10.0.17763/win10-x64": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- }
- },
- "UAP,Version=v10.0.17763/win10-x64-aot": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- }
- },
- "UAP,Version=v10.0.17763/win10-x86": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- }
- },
- "UAP,Version=v10.0.17763/win10-x86-aot": {
- "Microsoft.VCRTForwarders.140": {
- "type": "Transitive",
- "resolved": "1.0.2-rc",
- "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ=="
- },
- "Microsoft.Web.WebView2": {
- "type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
- },
- "Microsoft.WindowsAppSDK": {
- "type": "Transitive",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
- "dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
- "Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
- }
- }
- }
+ "UAP,Version=v10.0.17763/win10-arm": {},
+ "UAP,Version=v10.0.17763/win10-arm-aot": {},
+ "UAP,Version=v10.0.17763/win10-arm64-aot": {},
+ "UAP,Version=v10.0.17763/win10-x64": {},
+ "UAP,Version=v10.0.17763/win10-x64-aot": {},
+ "UAP,Version=v10.0.17763/win10-x86": {},
+ "UAP,Version=v10.0.17763/win10-x86-aot": {}
}
}
\ No newline at end of file
diff --git a/packages/playground/windows/playground-composition/CustomComponent.cpp b/packages/playground/windows/playground-composition/CustomComponent.cpp
index 80e5f9a643f..4d0e778cb4a 100644
--- a/packages/playground/windows/playground-composition/CustomComponent.cpp
+++ b/packages/playground/windows/playground-composition/CustomComponent.cpp
@@ -83,11 +83,9 @@ struct CustomComponentUserData : winrt::implements();
-#ifdef USE_EXPERIMENTAL_WINUI3
m_buttonLabelTextBlock.Text(myProps->label);
-#endif
}
void FinalizeUpdates() noexcept {
@@ -167,13 +163,11 @@ struct CustomComponentUserData : winrt::implementsInitialize(islandView, nativeLayout);
islandView.UserData(*userData);
-#ifdef USE_EXPERIMENTAL_WINUI3
islandView.Destroying([](const winrt::IInspectable &sender, const winrt::IInspectable & /*args*/) {
auto senderIslandView = sender.as();
auto userData = senderIslandView.UserData().as();
userData->m_xamlIsland.Close();
});
-#endif
});
builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &source,
@@ -231,9 +225,7 @@ struct CustomComponentUserData : winrt::implements m_eventEmitter{nullptr};
-#ifdef USE_EXPERIMENTAL_WINUI3
winrt::Microsoft::UI::Xaml::XamlIsland m_xamlIsland{nullptr};
-#endif
};
static void RegisterViewComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) {
diff --git a/packages/playground/windows/playground-composition/Playground-Composition.cpp b/packages/playground/windows/playground-composition/Playground-Composition.cpp
index bd2d52a28db..cec251bf8db 100644
--- a/packages/playground/windows/playground-composition/Playground-Composition.cpp
+++ b/packages/playground/windows/playground-composition/Playground-Composition.cpp
@@ -104,11 +104,7 @@ struct CompReactPackageProvider
: winrt::implements {
public: // IReactPackageProvider
void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
RegisterCustomComponent(packageBuilder);
-#else
- UNREFERENCED_PARAMETER(packageBuilder);
-#endif // USE_EXPERIMENTAL_WINUI3
}
};
@@ -161,44 +157,6 @@ struct WindowData {
return m_instanceSettings;
}
- winrt::Microsoft::UI::WindowId
- CreateChildWindow(winrt::Microsoft::UI::WindowId parentWindowId, LPCWSTR title, int x, int y, int w, int h) {
- LPCWSTR childWindowClassName = L"TestChildWindowClass";
-
- // Register child window class
- static std::once_flag onceFlag;
- std::call_once(onceFlag, [&childWindowClassName]() {
- WNDCLASSEX childWindowClass = {};
-
- childWindowClass.cbSize = sizeof(WNDCLASSEX);
- childWindowClass.style = CS_HREDRAW | CS_VREDRAW;
- childWindowClass.lpfnWndProc = ::DefWindowProc;
- childWindowClass.hInstance = GetModuleHandle(nullptr);
- childWindowClass.lpszClassName = childWindowClassName;
-
- RegisterClassEx(&childWindowClass);
- });
-
- HWND parentHwnd;
- parentHwnd = winrt::Microsoft::UI::GetWindowFromWindowId(parentWindowId);
-
- HWND childHwnd = ::CreateWindowEx(
- 0 /* dwExStyle */,
- childWindowClassName,
- title,
- WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
- x,
- y,
- w,
- h,
- parentHwnd /* hWndParent */,
- nullptr /* hMenu */,
- GetModuleHandle(nullptr),
- nullptr /* lpParam */);
-
- return winrt::Microsoft::UI::GetWindowIdFromWindow(childHwnd);
- }
-
void ApplyConstraintsForContentSizedWindow(winrt::Microsoft::ReactNative::LayoutConstraints &constraints) {
constraints.MinimumSize = {300, 300};
constraints.MaximumSize = {1000, 1000};
@@ -280,21 +238,28 @@ struct WindowData {
// Disable user sizing of the hwnd
::SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX);
m_compRootView.SizeChanged(
- [hwnd](auto sender, const winrt::Microsoft::ReactNative::RootViewSizeChangedEventArgs &args) {
- RECT rcClient, rcWindow;
- GetClientRect(hwnd, &rcClient);
- GetWindowRect(hwnd, &rcWindow);
-
- SetWindowPos(
- hwnd,
- nullptr,
- 0,
- 0,
- static_cast(args.Size().Width) + rcClient.left - rcClient.right + rcWindow.right -
- rcWindow.left,
- static_cast(args.Size().Height) + rcClient.top - rcClient.bottom + rcWindow.bottom -
- rcWindow.top,
- SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
+ [hwnd, props = InstanceSettings().Properties()](
+ auto sender, const winrt::Microsoft::ReactNative::RootViewSizeChangedEventArgs &args) {
+ auto compositor =
+ winrt::Microsoft::ReactNative::Composition::CompositionUIService::GetCompositor(props);
+ auto async = compositor.RequestCommitAsync();
+ async.Completed([hwnd, size = args.Size()](
+ auto asyncInfo, winrt::Windows::Foundation::AsyncStatus /*asyncStatus*/) {
+ RECT rcClient, rcWindow;
+ GetClientRect(hwnd, &rcClient);
+ GetWindowRect(hwnd, &rcWindow);
+
+ SetWindowPos(
+ hwnd,
+ nullptr,
+ 0,
+ 0,
+ static_cast(size.Width) + rcClient.left - rcClient.right + rcWindow.right -
+ rcWindow.left,
+ static_cast(size.Height) + rcClient.top - rcClient.bottom + rcWindow.bottom -
+ rcWindow.top,
+ SWP_DEFERERASE | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
+ });
});
}
m_compRootView.Arrange(constraints, {0, 0});
@@ -399,6 +364,15 @@ struct WindowData {
}
m_forceRTL = !m_forceRTL;
}
+ case IDM_SETPROPS: {
+ m_compRootView.SetProperties([](const winrt::Microsoft::ReactNative::IJSValueWriter &writer) {
+ static int value = 123;
+ writer.WriteObjectBegin();
+ winrt::Microsoft::ReactNative::WriteProperty(writer, L"testProp1", value++);
+ winrt::Microsoft::ReactNative::WriteProperty(writer, L"testProp2", L"value2");
+ writer.WriteObjectEnd();
+ });
+ }
}
return 0;
@@ -738,12 +712,10 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR
winrt::Microsoft::UI::Dispatching::DispatcherQueueController::CreateOnCurrentThread();
g_liftedCompositor = winrt::Microsoft::UI::Composition::Compositor();
-// We only want to init XAML if we are using XAML islands
-#ifdef USE_EXPERIMENTAL_WINUI3
+ // We only want to init XAML if we are using XAML islands
// Island-support: Create our custom Xaml App object. This is needed to properly use the controls and metadata
// in Microsoft.ui.xaml.controls.dll.
auto playgroundApp{winrt::make()};
-#endif
return RunPlayground(showCmd, false);
}
diff --git a/packages/playground/windows/playground-composition/Playground-Composition.rc b/packages/playground/windows/playground-composition/Playground-Composition.rc
index adbcf9460ac..9c31e7f3651 100644
--- a/packages/playground/windows/playground-composition/Playground-Composition.rc
+++ b/packages/playground/windows/playground-composition/Playground-Composition.rc
@@ -59,6 +59,7 @@ BEGIN
MENUITEM "&Refresh\tF5", IDM_REFRESH
MENUITEM "&Unload", IDM_UNLOAD
MENUITEM "Toggle Layout &Direction", IDM_TOGGLE_LAYOUT_DIRECTION
+ MENUITEM "Set Props to running Island", IDM_SETPROPS
MENUITEM SEPARATOR
MENUITEM "&Settings...\tAlt+S", IDM_SETTINGS
MENUITEM SEPARATOR
diff --git a/packages/playground/windows/playground-composition/packages.lock.json b/packages/playground/windows/playground-composition/packages.lock.json
index 2ce2f060b1f..51fc9932c39 100644
--- a/packages/playground/windows/playground-composition/packages.lock.json
+++ b/packages/playground/windows/playground-composition/packages.lock.json
@@ -28,11 +28,11 @@
},
"Microsoft.WindowsAppSDK": {
"type": "Direct",
- "requested": "[1.6.240923002, )",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
+ "requested": "[1.7.250401001, )",
+ "resolved": "1.7.250401001",
+ "contentHash": "kPsJ2LZoo3Xs/6FtIWMZRGnQ2ZMx9zDa0ZpqRGz1qwZr0gwwlXZJTmngaA1Ym2AHmIa05NtX2jEE2He8CzfhTg==",
"dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
+ "Microsoft.Web.WebView2": "1.0.2903.40",
"Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
}
},
@@ -57,8 +57,8 @@
},
"Microsoft.Web.WebView2": {
"type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
+ "resolved": "1.0.2903.40",
+ "contentHash": "THrzYAnJgE3+cNH+9Epr44XjoZoRELdVpXlWGPs6K9C9G6TqyDfVCeVAR/Er8ljLitIUX5gaSkPsy9wRhD1sgQ=="
},
"Microsoft.Windows.SDK.BuildTools": {
"type": "Transitive",
@@ -88,7 +88,7 @@
"Folly": "[1.0.0, )",
"Microsoft.JavaScript.Hermes": "[0.1.23, )",
"Microsoft.SourceLink.GitHub": "[1.1.1, )",
- "Microsoft.WindowsAppSDK": "[1.6.240923002, )",
+ "Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"ReactCommon": "[1.0.0, )",
"boost": "[1.83.0, )"
}
@@ -105,7 +105,7 @@
"dependencies": {
"Microsoft.ReactNative": "[1.0.0, )",
"Microsoft.VCRTForwarders.140": "[1.0.2-rc, )",
- "Microsoft.WindowsAppSDK": "[1.6.240923002, )",
+ "Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"boost": "[1.83.0, )"
}
}
diff --git a/packages/playground/windows/playground-composition/resource.h b/packages/playground/windows/playground-composition/resource.h
index ead109499da..2e50cb0c4a6 100644
--- a/packages/playground/windows/playground-composition/resource.h
+++ b/packages/playground/windows/playground-composition/resource.h
@@ -26,6 +26,7 @@
#define IDC_SIZETOCONTENT 112
#define IDM_UNLOAD 113
#define IDM_TOGGLE_LAYOUT_DIRECTION 114
+#define IDM_SETPROPS 115
#define IDI_ICON1 1008
// Next default values for new objects
diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.cpp b/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.cpp
index 977b221d943..4557d956634 100644
--- a/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.cpp
+++ b/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.cpp
@@ -4,7 +4,7 @@
#include "CalendarView.h"
-#if defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
+#if defined(RNW_NEW_ARCH)
#include "codegen/react/components/SampleCustomComponent/CalendarView.g.h"
@@ -71,4 +71,4 @@ void RegisterCalendarViewComponentView(winrt::Microsoft::ReactNative::IReactPack
});
}
-#endif // defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
+#endif // defined(RNW_NEW_ARCH)
diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.h b/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.h
index 2ef17343b84..da5efefed10 100644
--- a/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.h
+++ b/packages/sample-custom-component/windows/SampleCustomComponent/CalendarView.h
@@ -1,7 +1,7 @@
#pragma once
-#if defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
+#if defined(RNW_NEW_ARCH)
void RegisterCalendarViewComponentView(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder);
-#endif // defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
+#endif // defined(RNW_NEW_ARCH)
diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.cpp b/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.cpp
index 5a9c2fc7d56..795c1b33274 100644
--- a/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.cpp
+++ b/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.cpp
@@ -76,9 +76,7 @@ DrawingIsland::DrawingIsland(const winrt::Microsoft::UI::Composition::Compositor
DrawingIsland::~DrawingIsland() {
m_siteBridge = nullptr;
-#ifdef USE_EXPERIMENTAL_WINUI3
m_popupSiteBridge = nullptr;
-#endif
// m_fragmentRoot = nullptr;
m_compositor = nullptr;
}
@@ -187,9 +185,7 @@ void DrawingIsland::RightClickAndRelease(const winrt::float2 currentPoint) {
void DrawingIsland::SetHostBridge(const winrt::IContentSiteBridge &bridge) {
m_siteBridge = bridge;
-#ifdef USE_EXPERIMENTAL_WINUI3
- m_popupSiteBridge = m_siteBridge.try_as();
-#endif
+ m_popupSiteBridge = m_siteBridge.try_as();
Accessibility_Initialize();
SystemBackdrop_Initialize();
EvaluateLightDismissPopup();
@@ -470,11 +466,7 @@ void DrawingIsland::Input_Initialize() {
}
bool DrawingIsland::IsHostedByPopupWindowSiteBridge() {
-#ifdef USE_EXPERIMENTAL_WINUI3
return (m_popupSiteBridge != nullptr);
-#else
- return false;
-#endif
}
bool DrawingIsland::Input_OnKeyDown(winrt::Windows::System::VirtualKey virtualKey) {
@@ -671,17 +663,11 @@ void DrawingIsland::OnRightClick(const winrt::float2 point) {
if (selectedVisual != nullptr) {
// Right button brings up a context menu if clicked on a visual
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_siteBridge != nullptr) {
- // Create a new popup window.
- auto desktopBridge2 = m_siteBridge.try_as();
- auto popupBridge = desktopBridge2.TryCreatePopupSiteBridge();
-
// Convert the current position to screen coordinates and display.
winrt::Rect initialPosition(point.x, point.y, 200, 300);
auto convertedPosition = m_island.CoordinateConverter().ConvertLocalToScreen(initialPosition);
- popupBridge.MoveAndResize(convertedPosition);
// Put a new instance of the content and connect it with the popup bridge.
auto popupContent = winrt::make_self(m_compositor);
@@ -691,8 +677,13 @@ void DrawingIsland::OnRightClick(const winrt::float2 point) {
popupContent->LightDismissPopup(m_lightDismissPopup);
popupContent->UseSystemBackdrop(m_useSystemBackdrop);
+#ifdef USE_EXPERIMENTAL_WINUI3
// Funnel through the existing backdrop controller, we do not want to create a new one for every popup.
popupContent->SetSystemBackdropController(m_backdropController);
+#endif
+
+ auto popupBridge = winrt::Microsoft::UI::Content::DesktopPopupSiteBridge::Create(popupContent->Island());
+ popupBridge.MoveAndResize(convertedPosition);
// Connect the content and input site to the DesktopWindowDrawingIslandBridge
popupBridge.Connect(popupContent->Island());
@@ -723,7 +714,6 @@ void DrawingIsland::OnRightClick(const winrt::float2 point) {
childFragment->SetParent(parentFragment);
*/
}
-#endif
}
}
diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.h b/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.h
index 74371e90c5b..fbf951b3436 100644
--- a/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.h
+++ b/packages/sample-custom-component/windows/SampleCustomComponent/DrawingIsland.h
@@ -233,9 +233,7 @@ void Accessibility_OnAutomationProviderRequested(
// Popups
// https://task.ms/32440118: Add ContentIsland.SiteBridge to avoid this workaround
winrt::IContentSiteBridge m_siteBridge{nullptr};
-#ifdef USE_EXPERIMENTAL_WINUI3
- winrt::PopupWindowSiteBridge m_popupSiteBridge{nullptr};
-#endif
+ winrt::DesktopPopupSiteBridge m_popupSiteBridge{nullptr};
// SystemBackdrops for Popups
winrt::ISystemBackdropControllerWithTargets m_backdropController{nullptr};
diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/ReactPackageProvider.cpp b/packages/sample-custom-component/windows/SampleCustomComponent/ReactPackageProvider.cpp
index 6e8affd9f6e..113a08be86a 100644
--- a/packages/sample-custom-component/windows/SampleCustomComponent/ReactPackageProvider.cpp
+++ b/packages/sample-custom-component/windows/SampleCustomComponent/ReactPackageProvider.cpp
@@ -21,11 +21,8 @@ void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuil
#ifdef RNW_NEW_ARCH
RegisterDrawingIslandComponentView(packageBuilder);
RegisterMovingLightNativeComponent(packageBuilder);
-#endif // #ifdef RNW_NEW_ARCH
-
-#if defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
RegisterCalendarViewComponentView(packageBuilder);
-#endif // #if defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3)
+#endif // #if defined(RNW_NEW_ARCH)
}
} // namespace winrt::SampleCustomComponent::implementation
diff --git a/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json b/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json
index 72a973b25bc..5ae7458c8c1 100644
--- a/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json
+++ b/packages/sample-custom-component/windows/SampleCustomComponent/packages.lock.json
@@ -22,11 +22,11 @@
},
"Microsoft.WindowsAppSDK": {
"type": "Direct",
- "requested": "[1.6.240923002, )",
- "resolved": "1.6.240923002",
- "contentHash": "7PfOz2scXU+AAM/GYge+f6s7k3DVI+R1P8MNPZQr56GOPCGw+csvcg3S5KZg47z/o04kNvWH3GKtWT1ML9tpZw==",
+ "requested": "[1.7.250401001, )",
+ "resolved": "1.7.250401001",
+ "contentHash": "kPsJ2LZoo3Xs/6FtIWMZRGnQ2ZMx9zDa0ZpqRGz1qwZr0gwwlXZJTmngaA1Ym2AHmIa05NtX2jEE2He8CzfhTg==",
"dependencies": {
- "Microsoft.Web.WebView2": "1.0.2651.64",
+ "Microsoft.Web.WebView2": "1.0.2903.40",
"Microsoft.Windows.SDK.BuildTools": "10.0.22621.756"
}
},
@@ -56,8 +56,8 @@
},
"Microsoft.Web.WebView2": {
"type": "Transitive",
- "resolved": "1.0.2651.64",
- "contentHash": "f5sc/vcAoTCTEW7Nqzp4galAuTRguZViw8ksn+Nx2uskEBPm0/ubzy6gVjvXS/P96jLS89C8T9I0hPc417xpNg=="
+ "resolved": "1.0.2903.40",
+ "contentHash": "THrzYAnJgE3+cNH+9Epr44XjoZoRELdVpXlWGPs6K9C9G6TqyDfVCeVAR/Er8ljLitIUX5gaSkPsy9wRhD1sgQ=="
},
"Microsoft.Windows.SDK.BuildTools": {
"type": "Transitive",
@@ -87,7 +87,7 @@
"Folly": "[1.0.0, )",
"Microsoft.JavaScript.Hermes": "[0.1.23, )",
"Microsoft.SourceLink.GitHub": "[1.1.1, )",
- "Microsoft.WindowsAppSDK": "[1.6.240923002, )",
+ "Microsoft.WindowsAppSDK": "[1.7.250401001, )",
"ReactCommon": "[1.0.0, )",
"boost": "[1.83.0, )"
}
diff --git a/vnext/ExperimentalFeatures.props b/vnext/ExperimentalFeatures.props
index 98b66f6e188..5839ed5020a 100644
--- a/vnext/ExperimentalFeatures.props
+++ b/vnext/ExperimentalFeatures.props
@@ -3,10 +3,9 @@
true
true
true
- true
- false
+ false
true
diff --git a/vnext/Microsoft.ReactNative/CompositionComponentView.idl b/vnext/Microsoft.ReactNative/CompositionComponentView.idl
index 5c72cbd5067..d155d3290b6 100644
--- a/vnext/Microsoft.ReactNative/CompositionComponentView.idl
+++ b/vnext/Microsoft.ReactNative/CompositionComponentView.idl
@@ -89,11 +89,6 @@ namespace Microsoft.ReactNative.Composition
Microsoft.ReactNative.ViewProps ViewProps { get; };
};
- // Some other interfaces we could consider implementing/exposing
- // Use ifdef USE_EXPERIMENTAL_WINUI3 to add these
- // Microsoft.UI.Content.IContentLink // Use ifdef USE_EXPERIMENTAL_WINUI3
- // Microsoft.UI.Content.IContentSiteBridge
- // Microsoft.UI.Content.IContentSiteBridge2 // Use ifdef USE_EXPERIMENTAL_WINUI3
[experimental]
[webhosthidden]
runtimeclass ContentIslandComponentView : ViewComponentView {
diff --git a/vnext/Microsoft.ReactNative/CompositionSwitcher.idl b/vnext/Microsoft.ReactNative/CompositionSwitcher.idl
index a4c98ebfdbe..e864b9838d9 100644
--- a/vnext/Microsoft.ReactNative/CompositionSwitcher.idl
+++ b/vnext/Microsoft.ReactNative/CompositionSwitcher.idl
@@ -109,11 +109,15 @@ namespace Microsoft.ReactNative.Composition.Experimental
void Brush(IBrush brush);
void ScrollEnabled(Boolean isScrollEnabled);
event Windows.Foundation.EventHandler ScrollPositionChanged;
+ event Windows.Foundation.EventHandler ScrollBeginDrag;
void ContentSize(Windows.Foundation.Numerics.Vector2 size);
Windows.Foundation.Numerics.Vector3 ScrollPosition { get; };
void ScrollBy(Windows.Foundation.Numerics.Vector3 offset, Boolean animate);
void TryUpdatePosition(Windows.Foundation.Numerics.Vector3 position, Boolean animate);
void OnPointerPressed(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args);
+ void SetDecelerationRate(Windows.Foundation.Numerics.Vector3 decelerationRate);
+ void SetMaximumZoomScale(Single maximumZoomScale);
+ void SetMinimumZoomScale(Single minimumZoomScale);
Boolean Horizontal;
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp
index 7553f2cf2b2..71da7b45c01 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp
@@ -708,6 +708,8 @@ struct CompScrollerVisual : winrt::implements<
typename TTypeRedirects::InteractionTrackerInertiaStateEnteredArgs args) noexcept {
m_outer->m_custom = false;
m_outer->m_inertia = true;
+ m_outer->m_currentPosition = args.NaturalRestingPosition();
+ m_outer->FireScrollBeginDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
}
void InteractingStateEntered(
typename TTypeRedirects::InteractionTracker sender,
@@ -834,6 +836,18 @@ struct CompScrollerVisual : winrt::implements<
UpdateInteractionModes();
}
+ void SetDecelerationRate(winrt::Windows::Foundation::Numerics::float3 const &decelerationRate) noexcept {
+ m_interactionTracker.PositionInertiaDecayRate(decelerationRate);
+ }
+
+ void SetMaximumZoomScale(float maximumZoomScale) const noexcept {
+ m_interactionTracker.MaxScale(maximumZoomScale);
+ }
+
+ void SetMinimumZoomScale(float minimumZoomScale) noexcept {
+ m_interactionTracker.MinScale(minimumZoomScale);
+ }
+
void Opacity(float opacity) noexcept {
m_visual.Opacity(opacity);
}
@@ -918,10 +932,21 @@ struct CompScrollerVisual : winrt::implements<
return m_scrollPositionChangedEvent.add(handler);
}
+ winrt::event_token ScrollBeginDrag(
+ winrt::Windows::Foundation::EventHandler<
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs> const
+ &handler) noexcept {
+ return m_scrollBeginDragEvent.add(handler);
+ }
+
void ScrollPositionChanged(winrt::event_token const &token) noexcept {
m_scrollPositionChangedEvent.remove(token);
}
+ void ScrollBeginDrag(winrt::event_token const &token) noexcept {
+ m_scrollBeginDragEvent.remove(token);
+ }
+
void ContentSize(winrt::Windows::Foundation::Numerics::float2 const &size) noexcept {
m_contentSize = size;
m_contentVisual.Size(size);
@@ -992,6 +1017,10 @@ struct CompScrollerVisual : winrt::implements<
m_scrollPositionChangedEvent(*this, winrt::make(position));
}
+ void FireScrollBeginDrag(winrt::Windows::Foundation::Numerics::float2 position) noexcept {
+ m_scrollBeginDragEvent(*this, winrt::make(position));
+ }
+
void UpdateMaxPosition() noexcept {
m_interactionTracker.MaxPosition(
{std::max(m_contentSize.x - m_visualSize.x, 0),
@@ -1010,6 +1039,9 @@ struct CompScrollerVisual : winrt::implements<
winrt::event>
m_scrollPositionChangedEvent;
+ winrt::event>
+ m_scrollBeginDragEvent;
typename TTypeRedirects::SpriteVisual m_visual{nullptr};
typename TTypeRedirects::SpriteVisual m_contentVisual{nullptr};
typename TTypeRedirects::InteractionTracker m_interactionTracker{nullptr};
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
index 2947b5acf2f..ad5e31cff02 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -37,12 +38,10 @@ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
}
}
-#ifdef USE_EXPERIMENTAL_WINUI3
CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView,
const winrt::Microsoft::UI::Content::ChildSiteLink &childSiteLink) noexcept
: m_view{componentView}, m_childSiteLink{childSiteLink} {}
-#endif // USE_EXPERIMENTAL_WINUI3
HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
NavigateDirection direction,
@@ -50,7 +49,6 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
if (pRetVal == nullptr)
return E_POINTER;
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_childSiteLink) {
if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) {
auto fragment = m_childSiteLink.AutomationProvider().try_as();
@@ -58,7 +56,6 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
return S_OK;
}
}
-#endif // USE_EXPERIMENTAL_WINUI3
return UiaNavigateHelper(m_view.view(), direction, *pRetVal);
}
@@ -231,6 +228,12 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
AddRef();
}
+ if (patternId == UIA_ScrollPatternId &&
+ strongView.try_as()) {
+ *pRetVal = static_cast(this);
+ AddRef();
+ }
+
if (patternId == UIA_ValuePatternId &&
((strongView
.try_as() &&
@@ -341,6 +344,8 @@ long GetControlTypeFromString(const std::string &role) noexcept {
return UIA_TreeControlTypeId;
} else if (role == "treeitem") {
return UIA_TreeItemControlTypeId;
+ } else if (role == "pane") {
+ return UIA_PaneControlTypeId;
}
assert(false);
return UIA_GroupControlTypeId;
@@ -606,6 +611,156 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::ScrollIntoView() {
return S_OK;
}
+HRESULT __stdcall CompositionDynamicAutomationProvider::get_HorizontalScrollPercent(double *pRetVal) {
+ BOOL horizontallyScrollable;
+ auto hr = get_HorizontallyScrollable(&horizontallyScrollable);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ if (!horizontallyScrollable) {
+ *pRetVal = UIA_ScrollPatternNoScroll;
+ } else {
+ auto strongView = m_view.view();
+ auto scrollComponentView =
+ strongView.try_as();
+ *pRetVal = scrollComponentView->getScrollPositionX();
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::get_VerticalScrollPercent(double *pRetVal) {
+ BOOL verticallyScrollable;
+ auto hr = get_VerticallyScrollable(&verticallyScrollable);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ if (!verticallyScrollable) {
+ *pRetVal = UIA_ScrollPatternNoScroll;
+ } else {
+ auto strongView = m_view.view();
+ auto scrollComponentView =
+ strongView.try_as();
+ *pRetVal = scrollComponentView->getScrollPositionY();
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::get_HorizontalViewSize(double *pRetVal) {
+ BOOL horizontallyScrollable;
+ auto hr = get_HorizontallyScrollable(&horizontallyScrollable);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ if (!horizontallyScrollable) {
+ *pRetVal = 100;
+ } else {
+ auto strongView = m_view.view();
+ auto scrollComponentView =
+ strongView.try_as();
+ *pRetVal = scrollComponentView->getHorizontalSize();
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::get_VerticalViewSize(double *pRetVal) {
+ BOOL verticallyScrollable;
+ auto hr = get_VerticallyScrollable(&verticallyScrollable);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ if (!verticallyScrollable) {
+ *pRetVal = 100;
+ } else {
+ auto strongView = m_view.view();
+ auto scrollComponentView =
+ strongView.try_as();
+ *pRetVal = scrollComponentView->getVerticalSize();
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::get_HorizontallyScrollable(BOOL *pRetVal) {
+ if (pRetVal == nullptr)
+ return E_POINTER;
+ auto strongView = m_view.view();
+
+ if (!strongView)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ auto props = std::static_pointer_cast(
+ winrt::get_self(strongView)->props());
+ if (props == nullptr)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ *pRetVal = (props->horizontal && props->scrollEnabled);
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::get_VerticallyScrollable(BOOL *pRetVal) {
+ if (pRetVal == nullptr)
+ return E_POINTER;
+ auto strongView = m_view.view();
+
+ if (!strongView)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ auto props = std::static_pointer_cast(
+ winrt::get_self(strongView)->props());
+ if (props == nullptr)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ *pRetVal = (!props->horizontal && props->scrollEnabled);
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::Scroll(
+ ScrollAmount horizontalAmount,
+ ScrollAmount verticalAmount) {
+ DispatchAccessibilityAction(m_view, "scroll");
+ auto strongView = m_view.view();
+ auto scrollComponentView =
+ strongView.try_as();
+ BOOL verticallyScrollable;
+ BOOL horizontallyScrollable;
+ float vertical = 0.0f;
+ float horizontal = 0.0f;
+ auto hr = get_VerticallyScrollable(&verticallyScrollable);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ if (verticallyScrollable) {
+ if (verticalAmount == ScrollAmount_LargeIncrement) {
+ scrollComponentView->pageDown(true);
+ } else if (verticalAmount == ScrollAmount_LargeDecrement) {
+ scrollComponentView->pageUp(true);
+ } else if (verticalAmount == ScrollAmount_SmallIncrement) {
+ scrollComponentView->lineDown(true);
+ } else if (verticalAmount == ScrollAmount_SmallDecrement) {
+ scrollComponentView->lineUp(true);
+ }
+ }
+ hr = get_HorizontallyScrollable(&horizontallyScrollable);
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ if (horizontallyScrollable) {
+ if (horizontalAmount == ScrollAmount_LargeIncrement) {
+ scrollComponentView->pageDown(true);
+ } else if (horizontalAmount == ScrollAmount_LargeDecrement) {
+ scrollComponentView->pageUp(true);
+ } else if (horizontalAmount == ScrollAmount_SmallIncrement) {
+ scrollComponentView->lineRight(true);
+ } else if (horizontalAmount == ScrollAmount_SmallDecrement) {
+ scrollComponentView->lineLeft(true);
+ }
+ }
+ return S_OK;
+}
+
+HRESULT __stdcall CompositionDynamicAutomationProvider::SetScrollPercent(
+ double horiztonalPercent,
+ double verticalPercent) {
+ return S_OK;
+}
+
BSTR StringToBSTR(const std::string &str) {
// Calculate the required BSTR size in bytes
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h
index d97550b9b72..0dfa9e92978 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h
@@ -15,6 +15,7 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
IRawElementProviderSimple,
IInvokeProvider,
IScrollItemProvider,
+ IScrollProvider,
IValueProvider,
IRangeValueProvider,
IToggleProvider,
@@ -25,11 +26,9 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
CompositionDynamicAutomationProvider(
const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView) noexcept;
-#ifdef USE_EXPERIMENTAL_WINUI3
CompositionDynamicAutomationProvider(
const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView,
const winrt::Microsoft::UI::Content::ChildSiteLink &childContentLink) noexcept;
-#endif // USE_EXPERIMENTAL_WINUI3
// inherited via IRawElementProviderFragment
virtual HRESULT __stdcall Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal) override;
@@ -52,6 +51,16 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
// inherited via IScrollItemProvider
HRESULT __stdcall ScrollIntoView() override;
+ // inherited via IScrollProvider
+ HRESULT __stdcall get_HorizontalScrollPercent(double *pRetVal) override;
+ HRESULT __stdcall get_VerticalScrollPercent(double *pRetVal) override;
+ HRESULT __stdcall get_HorizontalViewSize(double *pRetVal) override;
+ HRESULT __stdcall get_VerticalViewSize(double *pRetVal) override;
+ HRESULT __stdcall get_HorizontallyScrollable(BOOL *pRetVal) override;
+ HRESULT __stdcall get_VerticallyScrollable(BOOL *pRetVal) override;
+ HRESULT __stdcall Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) override;
+ HRESULT __stdcall SetScrollPercent(double horiztonalPercent, double verticalPercent) override;
+
// inherited via IValueProvider
virtual HRESULT __stdcall SetValue(LPCWSTR val) override;
virtual HRESULT __stdcall get_Value(BSTR *pRetVal) override;
@@ -93,10 +102,8 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
::Microsoft::ReactNative::ReactTaggedView m_view;
winrt::com_ptr m_textProvider;
std::vector> m_selectionItems;
-#ifdef USE_EXPERIMENTAL_WINUI3
// Non-null when this UIA node is the peer of a ContentIslandComponentView.
winrt::Microsoft::UI::Content::ChildSiteLink m_childSiteLink{nullptr};
-#endif
};
} // namespace winrt::Microsoft::ReactNative::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
index b5c27775ce3..6ead642c857 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
@@ -174,7 +174,6 @@ HRESULT __stdcall CompositionRootAutomationProvider::get_FragmentRoot(IRawElemen
*pRetVal = nullptr;
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_island) {
auto parentRoot = m_island.FragmentRootAutomationProvider();
auto spFragment = parentRoot.try_as();
@@ -183,7 +182,6 @@ HRESULT __stdcall CompositionRootAutomationProvider::get_FragmentRoot(IRawElemen
return S_OK;
}
}
-#endif
return S_OK;
}
@@ -288,7 +286,6 @@ HRESULT __stdcall CompositionRootAutomationProvider::Navigate(
}
}
} else if (direction == NavigateDirection_Parent) {
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_island) {
auto parent = m_island.ParentAutomationProvider();
auto spFragment = parent.try_as();
@@ -297,7 +294,6 @@ HRESULT __stdcall CompositionRootAutomationProvider::Navigate(
return S_OK;
}
}
-#endif
}
*pRetVal = nullptr;
return S_OK;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
index 4485ec08998..38af8995408 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -331,9 +332,9 @@ void ComponentView::onLostFocus(
m_componentHostingFocusVisual->hostFocusVisual(false, get_strong());
}
- if (m_uiaProvider) {
+ if (UiaClientsAreListening()) {
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider, UIA_HasKeyboardFocusPropertyId, true, false);
+ EnsureUiaProvider(), UIA_HasKeyboardFocusPropertyId, true, false);
}
}
base_type::onLostFocus(args);
@@ -381,8 +382,8 @@ void ComponentView::onGotFocus(
focusRect.size.height += (FOCUS_VISUAL_WIDTH * 2);
focusVisualRoot(focusRect)->hostFocusVisual(true, get_strong());
}
- if (m_uiaProvider) {
- auto spProviderSimple = m_uiaProvider.try_as();
+ if (UiaClientsAreListening()) {
+ auto spProviderSimple = EnsureUiaProvider().try_as();
if (spProviderSimple != nullptr) {
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
m_uiaProvider, UIA_HasKeyboardFocusPropertyId, false, true);
@@ -743,59 +744,62 @@ void ComponentView::updateTransformProps(
void ComponentView::updateAccessibilityProps(
const facebook::react::ViewProps &oldViewProps,
const facebook::react::ViewProps &newViewProps) noexcept {
- if (!m_uiaProvider)
+ if (!UiaClientsAreListening())
return;
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider, UIA_IsKeyboardFocusablePropertyId, oldViewProps.focusable, newViewProps.focusable);
+ EnsureUiaProvider(), UIA_IsKeyboardFocusablePropertyId, oldViewProps.focusable, newViewProps.focusable);
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_NamePropertyId,
oldViewProps.accessibilityLabel,
newViewProps.accessibilityLabel.empty() ? DefaultAccessibleName() : newViewProps.accessibilityLabel);
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_IsContentElementPropertyId,
(oldViewProps.accessible && oldViewProps.accessibilityRole != "none"),
(newViewProps.accessible && newViewProps.accessibilityRole != "none"));
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_IsControlElementPropertyId,
(oldViewProps.accessible && oldViewProps.accessibilityRole != "none"),
(newViewProps.accessible && newViewProps.accessibilityRole != "none"));
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_IsEnabledPropertyId,
!(oldViewProps.accessibilityState && oldViewProps.accessibilityState->disabled),
!(newViewProps.accessibilityState && newViewProps.accessibilityState->disabled));
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_IsEnabledPropertyId,
!(oldViewProps.accessibilityState && oldViewProps.accessibilityState->busy),
!(newViewProps.accessibilityState && newViewProps.accessibilityState->busy));
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider, UIA_ControlTypePropertyId, oldViewProps.accessibilityRole, newViewProps.accessibilityRole);
+ EnsureUiaProvider(), UIA_ControlTypePropertyId, oldViewProps.accessibilityRole, newViewProps.accessibilityRole);
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider, UIA_HelpTextPropertyId, oldViewProps.accessibilityHint, newViewProps.accessibilityHint);
+ EnsureUiaProvider(), UIA_HelpTextPropertyId, oldViewProps.accessibilityHint, newViewProps.accessibilityHint);
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_PositionInSetPropertyId,
oldViewProps.accessibilityPosInSet,
newViewProps.accessibilityPosInSet);
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider, UIA_SizeOfSetPropertyId, oldViewProps.accessibilitySetSize, newViewProps.accessibilitySetSize);
+ EnsureUiaProvider(),
+ UIA_SizeOfSetPropertyId,
+ oldViewProps.accessibilitySetSize,
+ newViewProps.accessibilitySetSize);
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
- m_uiaProvider,
+ EnsureUiaProvider(),
UIA_LiveSettingPropertyId,
oldViewProps.accessibilityLiveRegion,
newViewProps.accessibilityLiveRegion);
@@ -803,7 +807,8 @@ void ComponentView::updateAccessibilityProps(
if ((oldViewProps.accessibilityState.has_value() && oldViewProps.accessibilityState->selected.has_value()) !=
((newViewProps.accessibilityState.has_value() && newViewProps.accessibilityState->selected.has_value()))) {
auto compProvider =
- m_uiaProvider.try_as();
+ EnsureUiaProvider()
+ .try_as();
if (compProvider) {
if ((newViewProps.accessibilityState.has_value() && newViewProps.accessibilityState->selected.has_value())) {
winrt::Microsoft::ReactNative::implementation::AddSelectionItemsToContainer(compProvider.get());
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
index 482ac71be5f..57aa7d14656 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp
@@ -44,7 +44,6 @@ ContentIslandComponentView::ContentIslandComponentView(
}
void ContentIslandComponentView::OnMounted() noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
m_childSiteLink = winrt::Microsoft::UI::Content::ChildSiteLink::Create(
rootComponentView()->parentContentIsland(),
winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(Visual())
@@ -84,21 +83,17 @@ void ContentIslandComponentView::OnMounted() noexcept {
}));
view = view.Parent();
}
-#endif
}
void ContentIslandComponentView::OnUnmounted() noexcept {
m_layoutMetricChangedRevokers.clear();
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken);
m_navigationHostDepartFocusRequestedToken = {};
}
-#endif
}
void ContentIslandComponentView::ParentLayoutChanged() noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_layoutChangePosted)
return;
@@ -114,30 +109,21 @@ void ContentIslandComponentView::ParentLayoutChanged() noexcept {
strongThis->m_layoutChangePosted = false;
}
});
-#endif
}
winrt::IInspectable ContentIslandComponentView::EnsureUiaProvider() noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_uiaProvider == nullptr) {
m_uiaProvider = winrt::make(
*get_strong(), m_childSiteLink);
}
return m_uiaProvider;
-#else
- return Super::EnsureUiaProvider();
-#endif
}
bool ContentIslandComponentView::focusable() const noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
// We don't have a way to check to see if the ContentIsland has focusable content,
// so we'll always return true. We'll have to handle the case where the content doesn't have
// focusable content in the OnGotFocus handler.
return true;
-#else
- return Super::focusable();
-#endif
}
// Helper to convert a FocusNavigationDirection to a FocusNavigationReason.
@@ -156,17 +142,12 @@ winrt::Microsoft::UI::Input::FocusNavigationReason GetFocusNavigationReason(
void ContentIslandComponentView::onGotFocus(
const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
auto gotFocusEventArgs = args.as();
const auto navigationReason = GetFocusNavigationReason(gotFocusEventArgs->Direction());
m_navigationHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(navigationReason));
-#else
- return Super::onGotFocus(args);
-#endif // USE_EXPERIMENTAL_WINUI3
}
ContentIslandComponentView::~ContentIslandComponentView() noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken);
m_navigationHostDepartFocusRequestedToken = {};
@@ -189,7 +170,6 @@ ContentIslandComponentView::~ContentIslandComponentView() noexcept {
m_previousSiblingAutomationProviderRequestedToken = {};
}
}
-#endif // USE_EXPERIMENTAL_WINUI3
if (m_islandToConnect) {
m_islandToConnect.Close();
}
@@ -212,36 +192,31 @@ void ContentIslandComponentView::UnmountChildComponentView(
void ContentIslandComponentView::updateLayoutMetrics(
facebook::react::LayoutMetrics const &layoutMetrics,
facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_childSiteLink) {
m_childSiteLink.ActualSize({layoutMetrics.frame.size.width, layoutMetrics.frame.size.height});
ParentLayoutChanged();
}
-#endif
base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
}
void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::ContentIsland &contentIsland) noexcept {
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_childSiteLink) {
m_islandToConnect = nullptr;
m_childSiteLink.Connect(contentIsland);
} else {
m_islandToConnect = contentIsland;
}
-#endif // USE_EXPERIMENTAL_WINUI3
}
void ContentIslandComponentView::prepareForRecycle() noexcept {
Super::prepareForRecycle();
}
-#ifdef USE_EXPERIMENTAL_WINUI3
void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
// This automation mode must be set before connecting the child ContentIsland.
// It puts the child content into a mode where it won't own its own framework root. Instead, the child island's
// automation peers will use the same framework root as the automation peer of this ContentIslandComponentView.
- m_childSiteLink.AutomationTreeOption(winrt::Microsoft::UI::Content::AutomationTreeOptions::FragmentBased);
+ m_childSiteLink.AutomationOption(winrt::Microsoft::UI::Content::ContentAutomationOptions::FragmentBased);
// These events are raised in response to the child ContentIsland asking for providers.
// For example, the ContentIsland.FragmentRootAutomationProvider property will return
@@ -288,6 +263,5 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
args.Handled(true);
});
}
-#endif // USE_EXPERIMENTAL_WINUI3
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h
index 88c2499df86..0b60d15653d 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h
@@ -61,7 +61,6 @@ struct ContentIslandComponentView : ContentIslandComponentViewT m_layoutMetricChangedRevokers;
-#ifdef USE_EXPERIMENTAL_WINUI3
winrt::Microsoft::UI::Content::ChildSiteLink m_childSiteLink{nullptr};
winrt::Microsoft::UI::Input::InputFocusNavigationHost m_navigationHost{nullptr};
winrt::event_token m_navigationHostDepartFocusRequestedToken{};
@@ -72,7 +71,6 @@ struct ContentIslandComponentView : ContentIslandComponentViewT wkImage)
- : m_wkImage(std::move(wkImage)) {}
+ : m_reactContext(reactContext), m_wkImage(std::move(wkImage)) {}
void ImageComponentView::WindowsImageResponseObserver::didReceiveProgress(float progress, int64_t loaded, int64_t total)
const {
- // TODO progress?
+ int loadedInt = static_cast(loaded);
+ int totalInt = static_cast(total);
+ m_reactContext.UIDispatcher().Post([progress, wkImage = m_wkImage, loadedInt, totalInt]() {
+ if (auto image = wkImage.get()) {
+ image->didReceiveProgress(progress, loadedInt, totalInt);
+ }
+ });
}
void ImageComponentView::WindowsImageResponseObserver::didReceiveImage(
facebook::react::ImageResponse const &imageResponse) const {
- if (auto imgComponentView{m_wkImage.get()}) {
- auto imageResponseImage = std::static_pointer_cast(imageResponse.getImage());
- imgComponentView->m_reactContext.UIDispatcher().Post([imageResponseImage, wkImage = m_wkImage]() {
- if (auto image{wkImage.get()}) {
- image->didReceiveImage(imageResponseImage);
- }
- });
- }
+ auto imageResponseImage = std::static_pointer_cast(imageResponse.getImage());
+ m_reactContext.UIDispatcher().Post([imageResponseImage, wkImage = m_wkImage]() {
+ if (auto image{wkImage.get()}) {
+ image->didReceiveImage(imageResponseImage);
+ }
+ });
}
void ImageComponentView::WindowsImageResponseObserver::didReceiveFailure(
@@ -95,6 +100,21 @@ void ImageComponentView::ImageLoadStart() noexcept {
}
}
+void ImageComponentView::ImageLoaded() noexcept {
+ auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter);
+ if (imageEventEmitter) {
+ imageEventEmitter->onLoadEnd();
+ }
+}
+
+void ImageComponentView::didReceiveProgress(float progress, int loaded, int total) noexcept {
+ auto imageEventEmitter = std::static_pointer_cast(m_eventEmitter);
+ if (imageEventEmitter) {
+ imageEventEmitter->onProgress(progress, loaded, total);
+ }
+ ensureDrawingSurface();
+}
+
void ImageComponentView::didReceiveImage(const std::shared_ptr &imageResponseImage) noexcept {
// TODO check for recycled?
@@ -152,7 +172,7 @@ void ImageComponentView::updateState(
auto newImageState = std::static_pointer_cast(state);
if (!m_imageResponseObserver) {
- m_imageResponseObserver = std::make_shared(get_weak());
+ m_imageResponseObserver = std::make_shared(this->m_reactContext, get_weak());
}
setStateAndResubscribeImageResponseObserver(newImageState);
@@ -281,6 +301,11 @@ void ImageComponentView::ensureDrawingSurface() noexcept {
: winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::Uniform);
break;
}
+ /*
+ case facebook::react::ImageResizeMode::None:
+ m_drawingSurface.Stretch(winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::None);
+ break;
+ */
default:
assert(false);
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h
index 829aca4f383..83c804b5cb7 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h
@@ -71,6 +71,7 @@ struct ImageComponentView : ImageComponentViewT wkImage);
void didReceiveProgress(float progress, int64_t loaded, int64_t total) const override;
void didReceiveImage(const facebook::react::ImageResponse &imageResponse) const override;
@@ -78,6 +79,7 @@ struct ImageComponentView : ImageComponentViewT m_wkImage;
+ winrt::Microsoft::ReactNative::ReactContext m_reactContext;
};
void ensureDrawingSurface() noexcept;
@@ -85,6 +87,7 @@ struct ImageComponentView : ImageComponentViewT &wicbmp) noexcept;
void didReceiveFailureFromObserver(const facebook::react::ImageLoadError &error) noexcept;
void setStateAndResubscribeImageResponseObserver(
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
index 7fc57f309b0..f17f5b198a3 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
@@ -35,33 +35,23 @@ struct ModalHostState
struct ModalHostView : public winrt::implements,
::Microsoft::ReactNativeSpecs::BaseModalHostView {
~ModalHostView() {
- if (m_window && m_window.IsVisible()) {
- CloseWindow();
- }
-
if (m_reactNativeIsland) {
m_reactNativeIsland.Island().Close();
}
- if (m_bridge) {
- if (m_departFocusToken && !m_bridge.IsClosed()) {
- auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge);
+ if (m_popUp) {
+ if (m_departFocusToken && !m_popUp.IsClosed()) {
+ // WASDK BUG: InputFocusNavigationHost::GetForSiteBridge fails on a DesktopPopupSiteBridge
+ // https://github.com/microsoft/react-native-windows/issues/14604
+ /*
+ auto navHost =
+ winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_popUp.as());
navHost.DepartFocusRequested(m_departFocusToken);
+ */
}
- m_bridge.Close();
- }
-
- if (m_window) {
- m_window.Destroy();
- m_window = nullptr;
- }
-
-#ifdef USE_EXPERIMENTAL_WINUI3
- if (m_popUp) {
m_popUp.Close();
m_popUp = nullptr;
}
-#endif
}
void InitializePortalViewComponent(
@@ -80,16 +70,37 @@ struct ModalHostView : public winrt::implements &oldProps) noexcept override {
if (!oldProps || newProps->visible != oldProps->visible) {
if (newProps->visible.value_or(true)) {
+ m_visible = true;
// We do not immediately show the window, since we want to resize/position
// the window based on the layout metrics before we show it
- m_showQueued = true;
+ QueueShow(view);
} else {
+ m_visible = false;
CloseWindow();
}
}
::Microsoft::ReactNativeSpecs::BaseModalHostView::UpdateProps(view, newProps, oldProps);
}
+ void QueueShow(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ if (m_showQueued)
+ return;
+ m_showQueued = true;
+
+ m_reactContext.UIDispatcher().Post([wkThis = get_weak(), wkView = winrt::weak_ref(view)]() {
+ if (auto strongThis = wkThis.get()) {
+ strongThis->m_showQueued = false;
+
+ if (!strongThis->m_visible) {
+ return;
+ }
+ if (auto v = wkView.get()) {
+ strongThis->ShowOnUIThread(v);
+ }
+ }
+ });
+ }
+
void UpdateState(
const winrt::Microsoft::ReactNative::ComponentView & /*view*/,
const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept override {
@@ -118,19 +129,11 @@ struct ModalHostView : public winrt::implements(
(parentRC.top + parentRC.bottom - layoutMetrics.Frame.Height * layoutMetrics.PointScaleFactor) / 2);
-#ifdef USE_EXPERIMENTAL_WINUI3
winrt::Windows::Graphics::RectInt32 rect2{
(int)xCor,
(int)yCor,
static_cast(layoutMetrics.Frame.Width * (layoutMetrics.PointScaleFactor)),
static_cast(layoutMetrics.Frame.Height * (layoutMetrics.PointScaleFactor))};
m_popUp.MoveAndResize(rect2);
-#else
- // Fix for https://github.com/microsoft/microsoft-ui-xaml/issues/9529
- auto titleBarHeight = m_window.TitleBar().Height();
-
- // Adjust window position and size
- m_window.ResizeClient(
- {static_cast(layoutMetrics.Frame.Width * (layoutMetrics.PointScaleFactor)),
- static_cast(layoutMetrics.Frame.Height * (layoutMetrics.PointScaleFactor)) - titleBarHeight});
- m_window.Move({xCor, yCor});
-#endif
};
void ShowOnUIThread(const winrt::Microsoft::ReactNative::ComponentView &view) {
if (!m_mounted)
return;
- m_showQueued = false;
EnsureModalCreated(view);
-#ifdef USE_EXPERIMENTAL_WINUI3
if (m_popUp) {
- m_bridge.Enable();
m_popUp.Show();
+ // WASDK BUG: InputFocusNavigationHost::GetForSiteBridge fails on a DesktopPopupSiteBridge
+ // https://github.com/microsoft/react-native-windows/issues/14604
+ /*
auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(
m_popUp.as());
auto result = navHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(
winrt::Microsoft::UI::Input::FocusNavigationReason::First));
-
- // dispatch onShow event
- if (auto eventEmitter = EventEmitter()) {
- ::Microsoft::ReactNativeSpecs::ModalHostViewEventEmitter::OnShow eventArgs;
- eventEmitter->onShow(eventArgs);
- }
- }
-#endif
-
- if (m_window && !m_window.IsVisible()) {
- m_bridge.Enable();
- m_window.Show(true);
-
- auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge);
- auto result = navHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(
- winrt::Microsoft::UI::Input::FocusNavigationReason::First));
+ */
// dispatch onShow event
if (auto eventEmitter = EventEmitter()) {
@@ -222,16 +195,9 @@ struct ModalHostView : public winrt::implements()->GetHwndForParenting();
auto portal = view.as();
-
-#ifdef USE_EXPERIMENTAL_WINUI3
- m_bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
- view.Parent().as().Compositor(),
- winrt::Microsoft::UI::GetWindowIdFromWindow(m_parentHwnd));
m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal(portal);
auto contentIsland = m_reactNativeIsland.Island();
- m_popUp = m_bridge.TryCreatePopupSiteBridge();
+ m_popUp = winrt::Microsoft::UI::Content::DesktopPopupSiteBridge::Create(
+ portal.Parent()
+ .Parent()
+ .as()
+ .Root()
+ .ReactNativeIsland()
+ .Island());
m_popUp.Connect(contentIsland);
// set the top-level windows as the new hwnd
@@ -284,6 +243,9 @@ struct ModalHostView : public winrt::implements(winrt::Microsoft::UI::GetWindowFromWindowId(m_popUp.WindowId())));
+ // WASDK BUG: InputFocusNavigationHost::GetForSiteBridge fails on a DesktopPopupSiteBridge
+ // https://github.com/microsoft/react-native-windows/issues/14604
+ /*
auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(
m_popUp.as());
m_departFocusToken = navHost.DepartFocusRequested(
@@ -293,39 +255,7 @@ struct ModalHostView : public winrt::implements(winrt::Microsoft::UI::GetWindowFromWindowId(m_window.Id())));
-
- // create a react native island - code taken from CompositionHwndHost
- m_bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
- view.Parent().as().Compositor(), m_window.Id());
- m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal(portal);
- auto contentIsland = m_reactNativeIsland.Island();
-
- auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge);
- m_departFocusToken = navHost.DepartFocusRequested(
- [wkView = winrt::make_weak(view)](
- const auto &sender, const winrt::Microsoft::UI::Input::FocusNavigationRequestEventArgs &args) {
- if (auto strongView = wkView.get()) {
- TrySetFocus(strongView.Parent());
- }
- });
- m_bridge.Connect(contentIsland);
-
-#endif
-
- m_bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow);
+ */
m_islandStateChangedToken =
contentIsland.StateChanged([weakThis = get_weak()](
@@ -343,12 +273,11 @@ struct ModalHostView : public winrt::implements(state);
m_attributedStringBox = facebook::react::AttributedStringBox(newState.getData().attributedString);
- m_paragraphAttributes = {}; // TODO
+ m_paragraphAttributes = facebook::react::ParagraphAttributes(newState.getData().paragraphAttributes);
m_textLayout = nullptr;
}
@@ -168,7 +169,9 @@ void ParagraphComponentView::updateVisualBrush() noexcept {
constraints.maximumSize.height =
m_layoutMetrics.frame.size.height - m_layoutMetrics.contentInsets.top - m_layoutMetrics.contentInsets.bottom;
- facebook::react::TextLayoutManager::GetTextLayout(m_attributedStringBox, {} /*TODO*/, constraints, m_textLayout);
+ facebook::react::TextLayoutManager::GetTextLayout(
+ m_attributedStringBox, m_paragraphAttributes, constraints, m_textLayout);
+
requireNewBrush = true;
}
@@ -199,6 +202,7 @@ void ParagraphComponentView::updateVisualBrush() noexcept {
// The surfaceBrush's size is based on the size the text takes up, which maybe smaller than the total visual
// So we need to align the brush within the visual to match the text alignment.
float horizAlignment{0.f};
+
/*
const auto &props = paragraphProps()
if (props.textAttributes.alignment) {
@@ -227,7 +231,6 @@ void ParagraphComponentView::updateVisualBrush() noexcept {
// TODO Using brush alignment to align the text makes it blurry...
if (m_drawingSurface) {
m_drawingSurface.HorizontalAlignmentRatio(horizAlignment);
- m_drawingSurface.VerticalAlignmentRatio(0.f);
m_drawingSurface.Stretch(winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::None);
}
Visual().as().Brush(m_drawingSurface);
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp
index 7ab746e8c5f..dd8422a540d 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp
@@ -195,6 +195,8 @@ void ReactNativeIsland::ReactViewHost(winrt::Microsoft::ReactNative::IReactViewH
return;
}
+ m_props = nullptr;
+
if (m_reactViewHost) {
UninitRootView();
m_reactViewHost.DetachViewInstance();
@@ -374,7 +376,14 @@ winrt::IInspectable ReactNativeIsland::GetUiaProvider() noexcept {
if (m_uiaProvider == nullptr) {
m_uiaProvider =
winrt::make(*this);
- if (m_hwnd && !m_island) {
+ if (m_island) {
+ auto pRootProvider =
+ static_cast(
+ m_uiaProvider.as().get());
+ if (pRootProvider != nullptr) {
+ pRootProvider->SetIsland(m_island);
+ }
+ } else if (m_hwnd) {
auto pRootProvider =
static_cast(
m_uiaProvider.as().get());
@@ -577,7 +586,9 @@ void ReactNativeIsland::ShowInstanceLoaded() noexcept {
winrt::Microsoft::ReactNative::ReactPropertyBag(m_context.Properties()));
m_rootTag = ::Microsoft::ReactNative::getNextRootViewTag();
- auto initProps = DynamicWriter::ToDynamic(Mso::Copy(m_reactViewOptions.InitialProps()));
+
+ auto initProps =
+ m_props.isNull() ? m_props : DynamicWriter::ToDynamic(Mso::Copy(m_reactViewOptions.InitialProps()));
if (initProps.isNull()) {
initProps = folly::dynamic::object();
}
@@ -900,16 +911,6 @@ winrt::Microsoft::UI::Content::ContentIsland ReactNativeIsland::Island() {
if (args.DidLayoutDirectionChange()) {
pThis->Arrange(pThis->m_layoutConstraints, pThis->m_viewportOffset);
}
-#ifndef USE_EXPERIMENTAL_WINUI3 // Use this in place of Connected/Disconnected events for now. -- Its not quite what we
- // want, but it will do for now.
- if (args.DidSiteVisibleChange()) {
- if (island.IsSiteVisible()) {
- pThis->OnMounted();
- } else {
- pThis->OnUnmounted();
- }
- }
-#endif
}
});
#ifdef USE_EXPERIMENTAL_WINUI3
@@ -965,6 +966,24 @@ void ReactNativeIsland::OnUnmounted() noexcept {
}
}
+void ReactNativeIsland::SetProperties(winrt::Microsoft::ReactNative::JSValueArgWriter props) noexcept {
+ auto initProps = DynamicWriter::ToDynamic(props);
+ if (initProps.isNull()) {
+ initProps = folly::dynamic::object();
+ }
+
+ if (m_isJSViewAttached) {
+ if (auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
+ winrt::Microsoft::ReactNative::ReactPropertyBag(m_context.Properties()))) {
+ initProps["concurrentRoot"] = true;
+ fabricuiManager->setProps(static_cast(m_rootTag), initProps);
+ return;
+ }
+ }
+
+ m_props = initProps;
+}
+
winrt::com_ptr
ReactNativeIsland::GetComponentView() noexcept {
if (auto portal = m_portal.get()) {
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h
index 8d8ed359be8..0a306125890 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h
@@ -76,6 +76,8 @@ struct ReactNativeIsland
float ScaleFactor() noexcept;
void ScaleFactor(float value) noexcept;
+ void SetProperties(winrt::Microsoft::ReactNative::JSValueArgWriter props) noexcept;
+
float FontSizeMultiplier() const noexcept;
winrt::event_token SizeChanged(
@@ -154,11 +156,14 @@ struct ReactNativeIsland
bool m_isJSViewAttached{false};
bool m_hasRenderedVisual{false};
bool m_showingLoadingUI{false};
- bool m_mounted{false};
+ bool m_mounted{true};
winrt::weak_ref m_portal{nullptr};
IReactDispatcher m_uiDispatcher{nullptr};
winrt::IInspectable m_uiaProvider{nullptr};
+ // If SetProps is called before the surface is loaded, store it locally to use on start
+ folly::dynamic m_props;
+
// This is the surfaceId that this island belongs to.
// In the case of portal content root, this will be the surfaceId that contains the portal.
int64_t m_rootTag{-1};
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
index 3c0bc5c42ba..2777b76799b 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
@@ -76,6 +76,7 @@ void RootComponentView::updateLayoutMetrics(
winrt::Microsoft::ReactNative::ComponentView RootComponentView::GetFocusedComponent() noexcept {
return m_focusedComponent;
}
+
void RootComponentView::SetFocusedComponent(
const winrt::Microsoft::ReactNative::ComponentView &value,
winrt::Microsoft::ReactNative::FocusNavigationDirection direction) noexcept {
@@ -92,11 +93,10 @@ void RootComponentView::SetFocusedComponent(
if (auto rootView = m_wkRootView.get()) {
winrt::get_self(rootView)->TrySetFocus();
}
+ m_focusedComponent = value;
auto args = winrt::make(value, direction);
winrt::get_self(value)->onGotFocus(args);
}
-
- m_focusedComponent = value;
}
bool RootComponentView::NavigateFocus(const winrt::Microsoft::ReactNative::FocusNavigationRequest &request) noexcept {
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp
index 2643827a3cc..a71e7279b85 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp
@@ -81,9 +81,12 @@ struct ScrollBarComponent {
}
void ContentSize(winrt::Windows::Foundation::Size contentSize) noexcept {
+ if (m_contentSize == contentSize) {
+ return;
+ }
m_contentSize = contentSize;
updateThumb();
- updateVisibility();
+ updateVisibility(m_visible);
}
void updateTrack() noexcept {
@@ -108,10 +111,22 @@ struct ScrollBarComponent {
updateTrack();
updateThumb();
- updateVisibility();
+ updateVisibility(m_visible);
}
- void updateVisibility() noexcept {
+ void updateVisibility(bool visible) noexcept {
+ if ((m_size.Width <= 0.0f && m_size.Height <= 0.0f) ||
+ (m_contentSize.Width <= 0.0f && m_contentSize.Height <= 0.0f)) {
+ m_rootVisual.IsVisible(false);
+ return;
+ }
+
+ if (!visible) {
+ m_visible = false;
+ m_rootVisual.IsVisible(visible);
+ return;
+ }
+
bool newVisibility = false;
if (m_vertical) {
newVisibility = (m_contentSize.Height > m_size.Height);
@@ -119,10 +134,8 @@ struct ScrollBarComponent {
newVisibility = (m_contentSize.Width > m_size.Width);
}
- if (newVisibility != m_visible) {
- m_visible = newVisibility;
- m_rootVisual.IsVisible(m_visible);
- }
+ m_visible = newVisibility;
+ m_rootVisual.IsVisible(m_visible);
}
void updateRootAndArrowVisualOffsets() noexcept {
@@ -561,7 +574,7 @@ struct ScrollBarComponent {
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext;
winrt::Microsoft::ReactNative::ReactContext m_reactContext;
const bool m_vertical;
- bool m_visible{false};
+ bool m_visible{true};
bool m_shy{false};
int m_thumbSize{0};
float m_arrowSize{0};
@@ -753,6 +766,44 @@ void ScrollViewComponentView::updateProps(
if (!oldProps || oldViewProps.horizontal != newViewProps.horizontal) {
m_scrollVisual.Horizontal(newViewProps.horizontal);
}
+
+ if (!oldProps || oldViewProps.showsHorizontalScrollIndicator != newViewProps.showsHorizontalScrollIndicator) {
+ updateShowsHorizontalScrollIndicator(newViewProps.showsHorizontalScrollIndicator);
+ }
+
+ if (!oldProps || oldViewProps.showsVerticalScrollIndicator != newViewProps.showsVerticalScrollIndicator) {
+ updateShowsVerticalScrollIndicator(newViewProps.showsVerticalScrollIndicator);
+ }
+
+ if (!oldProps || oldViewProps.decelerationRate != newViewProps.decelerationRate) {
+ updateDecelerationRate(newViewProps.decelerationRate);
+ }
+
+ if (!oldProps || oldViewProps.scrollEventThrottle != newViewProps.scrollEventThrottle) {
+ // Zero means "send value only once per significant logical event".
+ // Prop value is in milliseconds.
+ auto throttleInSeconds = newViewProps.scrollEventThrottle / 1000.0;
+ auto msPerFrame = 1.0 / 60.0;
+ if (throttleInSeconds < 0) {
+ m_scrollEventThrottle = INFINITY;
+ } else if (throttleInSeconds <= msPerFrame) {
+ m_scrollEventThrottle = 0;
+ } else {
+ m_scrollEventThrottle = throttleInSeconds;
+ }
+ }
+
+ if (oldViewProps.maximumZoomScale != newViewProps.maximumZoomScale) {
+ m_scrollVisual.SetMaximumZoomScale(newViewProps.maximumZoomScale);
+ }
+
+ if (oldViewProps.minimumZoomScale != newViewProps.minimumZoomScale) {
+ m_scrollVisual.SetMinimumZoomScale(newViewProps.minimumZoomScale);
+ }
+
+ if (oldViewProps.zoomScale != newViewProps.zoomScale) {
+ m_scrollVisual.Scale({newViewProps.zoomScale, newViewProps.zoomScale, newViewProps.zoomScale});
+ }
}
void ScrollViewComponentView::updateState(
@@ -784,14 +835,15 @@ void ScrollViewComponentView::updateLayoutMetrics(
facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
// Set Position & Size Properties
ensureVisual();
-
- m_verticalScrollbarComponent->updateLayoutMetrics(layoutMetrics);
- m_horizontalScrollbarComponent->updateLayoutMetrics(layoutMetrics);
- base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
- m_scrollVisual.Size(
- {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
- layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
- updateContentVisualSize();
+ if (oldLayoutMetrics != layoutMetrics) {
+ m_verticalScrollbarComponent->updateLayoutMetrics(layoutMetrics);
+ m_horizontalScrollbarComponent->updateLayoutMetrics(layoutMetrics);
+ base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
+ m_scrollVisual.Size(
+ {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
+ layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
+ updateContentVisualSize();
+ }
}
void ScrollViewComponentView::updateContentVisualSize() noexcept {
@@ -962,6 +1014,8 @@ bool ScrollViewComponentView::scrollToEnd(bool animate) noexcept {
auto x = (m_contentSize.width - m_layoutMetrics.frame.size.width) * m_layoutMetrics.pointScaleFactor;
auto y = (m_contentSize.height - m_layoutMetrics.frame.size.height) * m_layoutMetrics.pointScaleFactor;
+ // Ensure at least one scroll event will fire
+ m_allowNextScrollNoMatterWhat = true;
m_scrollVisual.TryUpdatePosition({static_cast(x), static_cast(y), 0.0f}, animate);
return true;
}
@@ -976,10 +1030,16 @@ bool ScrollViewComponentView::scrollToStart(bool animate) noexcept {
}
bool ScrollViewComponentView::pageUp(bool animate) noexcept {
+ if (std::static_pointer_cast(viewProps())->horizontal) {
+ return scrollLeft(m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor, animate);
+ }
return scrollUp(m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor, animate);
}
bool ScrollViewComponentView::pageDown(bool animate) noexcept {
+ if (std::static_pointer_cast(viewProps())->horizontal) {
+ return scrollRight(m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor, animate);
+ }
return scrollDown(m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor, animate);
}
@@ -1036,7 +1096,7 @@ bool ScrollViewComponentView::scrollLeft(float delta, bool animate) noexcept {
return false;
}
- m_scrollVisual.ScrollBy({delta, 0, 0}, animate);
+ m_scrollVisual.ScrollBy({-delta, 0, 0}, animate);
return true;
}
@@ -1196,6 +1256,36 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
[this](
winrt::IInspectable const & /*sender*/,
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
+ auto now = std::chrono::steady_clock::now();
+ auto elapsed = std::chrono::duration_cast>(now - m_lastScrollEventTime).count();
+
+ if (m_allowNextScrollNoMatterWhat ||
+ (m_scrollEventThrottle < std::max(std::chrono::duration(0.017).count(), elapsed))) {
+ updateStateWithContentOffset();
+ auto eventEmitter = GetEventEmitter();
+ if (eventEmitter) {
+ facebook::react::ScrollViewEventEmitter::Metrics scrollMetrics;
+ scrollMetrics.containerSize.height = m_layoutMetrics.frame.size.height;
+ scrollMetrics.containerSize.width = m_layoutMetrics.frame.size.width;
+ scrollMetrics.contentOffset.x = args.Position().x / m_layoutMetrics.pointScaleFactor;
+ scrollMetrics.contentOffset.y = args.Position().y / m_layoutMetrics.pointScaleFactor;
+ scrollMetrics.zoomScale = 1.0;
+ scrollMetrics.contentSize.height = std::max(m_contentSize.height, m_layoutMetrics.frame.size.height);
+ scrollMetrics.contentSize.width = std::max(m_contentSize.width, m_layoutMetrics.frame.size.width);
+ std::static_pointer_cast(eventEmitter)
+ ->onScroll(scrollMetrics);
+ m_lastScrollEventTime = now;
+ m_allowNextScrollNoMatterWhat = false;
+ }
+ }
+ });
+
+ m_scrollBeginDragRevoker = m_scrollVisual.ScrollBeginDrag(
+ winrt::auto_revoke,
+ [this](
+ winrt::IInspectable const & /*sender*/,
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
+ m_allowNextScrollNoMatterWhat = true; // Ensure next scroll event is recorded, regardless of throttle
updateStateWithContentOffset();
auto eventEmitter = GetEventEmitter();
if (eventEmitter) {
@@ -1208,9 +1298,10 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
scrollMetrics.contentSize.height = std::max(m_contentSize.height, m_layoutMetrics.frame.size.height);
scrollMetrics.contentSize.width = std::max(m_contentSize.width, m_layoutMetrics.frame.size.width);
std::static_pointer_cast(eventEmitter)
- ->onScroll(scrollMetrics);
+ ->onScrollBeginDrag(scrollMetrics);
}
});
+
return visual;
}
@@ -1259,7 +1350,7 @@ facebook::react::Point ScrollViewComponentView::getClientOffset() const noexcept
}
std::string ScrollViewComponentView::DefaultControlType() const noexcept {
- return "scrollbar";
+ return "pane";
}
winrt::com_ptr ScrollViewComponentView::focusVisualRoot(
@@ -1272,4 +1363,39 @@ ScrollViewComponentView::visualToHostFocus() noexcept {
return m_scrollVisual;
}
+int ScrollViewComponentView::getScrollPositionX() noexcept {
+ return int((m_scrollVisual.ScrollPosition().x / m_horizontalScrollbarComponent->getScrollRange()) * 100);
+}
+
+int ScrollViewComponentView::getScrollPositionY() noexcept {
+ return int((m_scrollVisual.ScrollPosition().y / m_verticalScrollbarComponent->getScrollRange()) * 100);
+}
+
+double ScrollViewComponentView::getVerticalSize() noexcept {
+ return std::min((m_layoutMetrics.frame.size.height / m_contentSize.height * 100.0), 100.0);
+}
+
+double ScrollViewComponentView::getHorizontalSize() noexcept {
+ return std::min((m_layoutMetrics.frame.size.width / m_contentSize.width * 100.0), 100.0);
+}
+
+void ScrollViewComponentView::updateShowsHorizontalScrollIndicator(bool value) noexcept {
+ if (value) {
+ m_horizontalScrollbarComponent->updateVisibility(true);
+ } else {
+ m_horizontalScrollbarComponent->updateVisibility(false);
+ }
+}
+
+void ScrollViewComponentView::updateShowsVerticalScrollIndicator(bool value) noexcept {
+ if (value) {
+ m_verticalScrollbarComponent->updateVisibility(true);
+ } else {
+ m_verticalScrollbarComponent->updateVisibility(false);
+ }
+}
+
+void ScrollViewComponentView::updateDecelerationRate(float value) noexcept {
+ m_scrollVisual.SetDecelerationRate({value, value, value});
+}
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h
index ebabbffeec9..c1834beb343 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h
@@ -113,7 +113,13 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
winrt::Microsoft::ReactNative::Composition::Experimental::IVisual visualToHostFocus() noexcept override;
winrt::com_ptr focusVisualRoot(const facebook::react::Rect &focusRect) noexcept override;
+ int getScrollPositionX() noexcept;
+ int getScrollPositionY() noexcept;
+ double getVerticalSize() noexcept;
+ double getHorizontalSize() noexcept;
+
private:
+ void updateDecelerationRate(float value) noexcept;
void updateContentVisualSize() noexcept;
bool scrollToEnd(bool animate) noexcept;
bool scrollToStart(bool animate) noexcept;
@@ -123,6 +129,8 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
bool scrollRight(float delta, bool animate) noexcept;
void updateBackgroundColor(const facebook::react::SharedColor &color) noexcept;
void updateStateWithContentOffset() noexcept;
+ void updateShowsHorizontalScrollIndicator(bool value) noexcept;
+ void updateShowsVerticalScrollIndicator(bool value) noexcept;
facebook::react::Size m_contentSize;
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual m_scrollVisual{nullptr};
@@ -130,6 +138,8 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
std::shared_ptr m_verticalScrollbarComponent{nullptr};
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual::ScrollPositionChanged_revoker
m_scrollPositionChangedRevoker{};
+ winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual::ScrollBeginDrag_revoker
+ m_scrollBeginDragRevoker{};
float m_zoomFactor{1.0f};
bool m_isScrollingFromInertia = false;
@@ -137,6 +147,9 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
bool m_isHorizontal = false;
bool m_changeViewAfterLoaded = false;
bool m_dismissKeyboardOnDrag = false;
+ double m_scrollEventThrottle{0.0};
+ bool m_allowNextScrollNoMatterWhat{false};
+ std::chrono::steady_clock::time_point m_lastScrollEventTime{};
std::shared_ptr m_state;
};
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentDescriptor.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentDescriptor.h
index f84e0cd304b..14831f4c55a 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentDescriptor.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentDescriptor.h
@@ -75,8 +75,6 @@ virtual State::Shared createInitialState(
// and communicate text rendering metrics to mounting layer.
textInputShadowNode.setTextLayoutManager(m_textLayoutManager);
- textInputShadowNode.setContextContainer(const_cast(getContextContainer().get()));
-
/*
int surfaceId = textInputShadowNode.getSurfaceId();
if (surfaceIdToThemePaddingMap_.find(surfaceId) !=
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp
index af203ad8f78..05c0ac99030 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp
@@ -116,25 +116,25 @@ struct CompTextHost : public winrt::implements {
//@cmember Show the scroll bar
BOOL TxShowScrollBar(INT fnBar, BOOL fShow) override {
- assert(false);
+ // assert(false);
return {};
}
//@cmember Enable the scroll bar
BOOL TxEnableScrollBar(INT fuSBFlags, INT fuArrowflags) override {
- assert(false);
+ // assert(false);
return {};
}
//@cmember Set the scroll range
BOOL TxSetScrollRange(INT fnBar, LONG nMinPos, INT nMaxPos, BOOL fRedraw) override {
- assert(false);
+ // assert(false);
return {};
}
//@cmember Set the scroll position
BOOL TxSetScrollPos(INT fnBar, INT nPos, BOOL fRedraw) override {
- assert(false);
+ // assert(false);
return {};
}
@@ -377,8 +377,11 @@ struct CompTextHost : public winrt::implements {
//@cmember Get the bits representing requested scroll bars for the window
HRESULT TxGetScrollBars(DWORD *pdwScrollBar) override {
- // TODO support scrolling
- *pdwScrollBar = 0;
+ if (m_outer->m_multiline) {
+ *pdwScrollBar = WS_VSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | ES_AUTOHSCROLL;
+ } else {
+ *pdwScrollBar = WS_HSCROLL | ES_AUTOHSCROLL;
+ }
return S_OK;
}
@@ -460,6 +463,13 @@ struct CompTextHost : public winrt::implements {
WindowsTextInputComponentView *m_outer;
};
+int WINAPI
+AutoCorrectOffCallback(LANGID langid, const WCHAR *pszBefore, WCHAR *pszAfter, LONG cchAfter, LONG *pcchReplaced) {
+ wcsncpy_s(pszAfter, cchAfter, pszBefore, _TRUNCATE);
+ *pcchReplaced = static_cast(wcslen(pszAfter));
+ return ATP_CHANGE;
+}
+
facebook::react::AttributedString WindowsTextInputComponentView::getAttributedString() const {
// Use BaseTextShadowNode to get attributed string from children
@@ -611,6 +621,19 @@ WPARAM PointerRoutedEventArgsToMouseWParam(
return wParam;
}
+bool WindowsTextInputComponentView::IsDoubleClick() {
+ using namespace std::chrono;
+
+ auto now = steady_clock::now();
+ auto duration = duration_cast(now - m_lastClickTime).count();
+
+ const int DOUBLE_CLICK_TIME_MS = ::GetDoubleClickTime();
+
+ m_lastClickTime = now;
+
+ return (duration < DOUBLE_CLICK_TIME_MS);
+}
+
void WindowsTextInputComponentView::OnPointerPressed(
const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {
UINT msg = 0;
@@ -627,7 +650,11 @@ void WindowsTextInputComponentView::OnPointerPressed(
if (pp.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) {
switch (pp.Properties().PointerUpdateKind()) {
case winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::LeftButtonPressed:
- msg = WM_LBUTTONDOWN;
+ if (IsDoubleClick()) {
+ msg = WM_LBUTTONDBLCLK;
+ } else {
+ msg = WM_LBUTTONDOWN;
+ }
break;
case winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::MiddleButtonPressed:
msg = WM_MBUTTONDOWN;
@@ -930,6 +957,19 @@ void WindowsTextInputComponentView::onLostFocus(
m_textServices->TxSendMessage(WM_KILLFOCUS, 0, 0, &lresult);
}
m_caretVisual.IsVisible(false);
+
+ // Call onEndEditing when focus is lost
+ if (m_eventEmitter && !m_comingFromJS) {
+ auto emitter = std::static_pointer_cast(m_eventEmitter);
+ facebook::react::WindowsTextInputEventEmitter::OnEndEditing onEndEditingArgs;
+
+ // Set event arguments
+ onEndEditingArgs.eventCount = ++m_nativeEventCount;
+ onEndEditingArgs.text = GetTextFromRichEdit();
+
+ // Emit the event
+ emitter->onEndEditing(onEndEditingArgs);
+ }
}
void WindowsTextInputComponentView::onGotFocus(
@@ -983,7 +1023,10 @@ void WindowsTextInputComponentView::updateProps(
if (!facebook::react::floatEquality(
oldTextInputProps.textAttributes.fontSize, newTextInputProps.textAttributes.fontSize) ||
(oldTextInputProps.textAttributes.allowFontScaling != newTextInputProps.textAttributes.allowFontScaling) ||
- oldTextInputProps.textAttributes.fontWeight != newTextInputProps.textAttributes.fontWeight) {
+ oldTextInputProps.textAttributes.fontWeight != newTextInputProps.textAttributes.fontWeight ||
+ !facebook::react::floatEquality(
+ oldTextInputProps.textAttributes.letterSpacing, newTextInputProps.textAttributes.letterSpacing) ||
+ oldTextInputProps.textAttributes.fontFamily != newTextInputProps.textAttributes.fontFamily) {
m_propBitsMask |= TXTBIT_CHARFORMATCHANGE;
m_propBits |= TXTBIT_CHARFORMATCHANGE;
}
@@ -1042,6 +1085,26 @@ void WindowsTextInputComponentView::updateProps(
autoCapitalizeOnUpdateProps(oldTextInputProps.autoCapitalize, newTextInputProps.autoCapitalize);
}
+ if (oldTextInputProps.textAlign != newTextInputProps.textAlign) {
+ // Let UpdateParaFormat() to refresh the text field with the new text alignment.
+ m_propBitsMask |= TXTBIT_PARAFORMATCHANGE;
+ m_propBits |= TXTBIT_PARAFORMATCHANGE;
+ }
+
+ // Please note: spellcheck performs both red lines and autocorrect as per windows behaviour
+ bool shouldUpdateSpellCheck =
+ (!oldProps || (oldTextInputProps.spellCheck != newTextInputProps.spellCheck) ||
+ (oldTextInputProps.autoCorrect != newTextInputProps.autoCorrect));
+
+ if (shouldUpdateSpellCheck) {
+ bool effectiveSpellCheck = newTextInputProps.spellCheck || newTextInputProps.autoCorrect;
+ updateSpellCheck(effectiveSpellCheck);
+ }
+
+ if (!oldProps || oldTextInputProps.autoCorrect != newTextInputProps.autoCorrect) {
+ updateAutoCorrect(newTextInputProps.autoCorrect);
+ }
+
UpdatePropertyBits();
}
@@ -1070,7 +1133,7 @@ void WindowsTextInputComponentView::updateState(
if (m_mostRecentEventCount == m_state->getData().mostRecentEventCount) {
m_comingFromState = true;
- auto &fragments = m_state->getData().attributedString.getFragments();
+ auto &fragments = m_state->getData().attributedStringBox.getValue().getFragments();
UpdateText(fragments.size() ? fragments[0].string : "");
m_comingFromState = false;
@@ -1133,7 +1196,7 @@ void WindowsTextInputComponentView::OnTextUpdated() noexcept {
// auto newAttributedString = getAttributedString();
// if (data.attributedString == newAttributedString)
// return;
- data.attributedString = getAttributedString();
+ data.attributedStringBox = facebook::react::AttributedStringBox(getAttributedString());
data.mostRecentEventCount = m_nativeEventCount;
m_state->updateState(std::move(data));
@@ -1288,10 +1351,24 @@ void WindowsTextInputComponentView::UpdateCharFormat() noexcept {
// cfNew.dwEffects |= CFE_UNDERLINE;
// }
+ // set font family
+ if (!props.textAttributes.fontFamily.empty()) {
+ cfNew.dwMask |= CFM_FACE;
+ std::wstring fontFamily =
+ std::wstring(props.textAttributes.fontFamily.begin(), props.textAttributes.fontFamily.end());
+ wcsncpy_s(cfNew.szFaceName, fontFamily.c_str(), LF_FACESIZE);
+ }
+
// set char offset
cfNew.dwMask |= CFM_OFFSET;
cfNew.yOffset = 0;
+ // set letter spacing
+ float letterSpacing = props.textAttributes.letterSpacing;
+ if (!std::isnan(letterSpacing)) {
+ updateLetterSpacing(letterSpacing);
+ }
+
// set charset
cfNew.dwMask |= CFM_CHARSET;
cfNew.bCharSet = DEFAULT_CHARSET;
@@ -1305,7 +1382,15 @@ void WindowsTextInputComponentView::UpdateParaFormat() noexcept {
m_pf.cbSize = sizeof(PARAFORMAT2);
m_pf.dwMask = PFM_ALL;
- m_pf.wAlignment = PFA_LEFT;
+ auto &textAlign = windowsTextInputProps().textAlign;
+
+ if (textAlign == facebook::react::TextAlignment::Center) {
+ m_pf.wAlignment = PFA_CENTER;
+ } else if (textAlign == facebook::react::TextAlignment::Right) {
+ m_pf.wAlignment = PFA_RIGHT;
+ } else {
+ m_pf.wAlignment = PFA_LEFT;
+ }
m_pf.cTabCount = 1;
m_pf.rgxTabs[0] = lDefaultTab;
@@ -1475,6 +1560,9 @@ WindowsTextInputComponentView::createVisual() noexcept {
winrt::check_hresult(g_pfnCreateTextServices(nullptr, m_textHost.get(), spUnk.put()));
spUnk.as(m_textServices);
+ LRESULT res;
+ winrt::check_hresult(m_textServices->TxSendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0, &res));
+
m_caretVisual = m_compContext.CreateCaretVisual();
visual.InsertAt(m_caretVisual.InnerVisual(), 0);
m_caretVisual.IsVisible(false);
@@ -1525,4 +1613,39 @@ void WindowsTextInputComponentView::autoCapitalizeOnUpdateProps(
}
}
+void WindowsTextInputComponentView::updateLetterSpacing(float letterSpacing) noexcept {
+ CHARFORMAT2W cf = {};
+ cf.cbSize = sizeof(CHARFORMAT2W);
+ cf.dwMask = CFM_SPACING;
+ cf.sSpacing = static_cast(letterSpacing * 20); // Convert to TWIPS
+
+ LRESULT res;
+
+ // Apply to all existing text like placeholder
+ winrt::check_hresult(m_textServices->TxSendMessage(EM_SETCHARFORMAT, SCF_ALL, reinterpret_cast(&cf), &res));
+
+ // Apply to future text input
+ winrt::check_hresult(
+ m_textServices->TxSendMessage(EM_SETCHARFORMAT, SCF_SELECTION, reinterpret_cast(&cf), &res));
+}
+
+void WindowsTextInputComponentView::updateAutoCorrect(bool enable) noexcept {
+ LRESULT lresult;
+ winrt::check_hresult(m_textServices->TxSendMessage(
+ EM_SETAUTOCORRECTPROC, enable ? 0 : reinterpret_cast(AutoCorrectOffCallback), 0, &lresult));
+}
+
+void WindowsTextInputComponentView::updateSpellCheck(bool enable) noexcept {
+ LRESULT currentLangOptions;
+ winrt::check_hresult(m_textServices->TxSendMessage(EM_GETLANGOPTIONS, 0, 0, ¤tLangOptions));
+
+ DWORD newLangOptions = static_cast(currentLangOptions);
+ if (enable) {
+ newLangOptions |= IMF_SPELLCHECKING;
+ }
+
+ LRESULT lresult;
+ winrt::check_hresult(
+ m_textServices->TxSendMessage(EM_SETLANGOPTIONS, IMF_SPELLCHECKING, enable ? newLangOptions : 0, &lresult));
+}
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h
index 155fe5e6492..72c38144398 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h
@@ -70,6 +70,7 @@ struct WindowsTextInputComponentView
std::optional getAccessiblityValue() noexcept override;
void setAcccessiblityValue(std::string &&value) noexcept override;
bool getAcccessiblityIsReadOnly() noexcept override;
+ bool IsDoubleClick();
WindowsTextInputComponentView(
const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
@@ -110,6 +111,10 @@ struct WindowsTextInputComponentView
const std::string &previousCapitalizationType,
const std::string &newcapitalizationType) noexcept;
+ void updateLetterSpacing(float letterSpacing) noexcept;
+ void updateAutoCorrect(bool value) noexcept;
+ void updateSpellCheck(bool value) noexcept;
+
winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr};
winrt::Microsoft::ReactNative::Composition::Experimental::ICaretVisual m_caretVisual{nullptr};
winrt::Microsoft::ReactNative::Composition::Experimental::IDrawingSurfaceBrush m_drawingSurface{nullptr};
@@ -136,6 +141,7 @@ struct WindowsTextInputComponentView
DWORD m_propBitsMask{0};
DWORD m_propBits{0};
HCURSOR m_hcursor{nullptr};
+ std::chrono::steady_clock::time_point m_lastClickTime{};
std::vector m_submitKeyEvents;
};
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.cpp
index edd0d55ab0b..97f673de294 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.cpp
@@ -5,6 +5,8 @@
#include "WindowsTextInputEventEmitter.h"
+#include
+
namespace facebook::react {
void WindowsTextInputEventEmitter::onChange(OnChange event) const {
@@ -46,4 +48,33 @@ void WindowsTextInputEventEmitter::onKeyPress(OnKeyPress event) const {
});
}
+static jsi::Value textInputMetricsContentSizePayload(
+ jsi::Runtime &runtime,
+ const WindowsTextInputEventEmitter::OnContentSizeChange &event) {
+ auto payload = jsi::Object(runtime);
+ {
+ auto contentSize = jsi::Object(runtime);
+ contentSize.setProperty(runtime, "width", event.contentSize.width);
+ contentSize.setProperty(runtime, "height", event.contentSize.height);
+ payload.setProperty(runtime, "contentSize", contentSize);
+ }
+ return payload;
+};
+
+void WindowsTextInputEventEmitter::onContentSizeChange(OnContentSizeChange event) const {
+ dispatchEvent("textInputContentSizeChange", [event = std::move(event)](jsi::Runtime &runtime) {
+ return textInputMetricsContentSizePayload(runtime, event);
+ });
+}
+
+void WindowsTextInputEventEmitter::onEndEditing(OnEndEditing event) const {
+ dispatchEvent("textInputEndEditing", [event = std::move(event)](jsi::Runtime &runtime) {
+ auto payload = jsi::Object(runtime);
+ payload.setProperty(runtime, "eventCount", event.eventCount);
+ payload.setProperty(runtime, "target", event.target);
+ payload.setProperty(runtime, "text", event.text);
+ return payload;
+ });
+}
+
} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.h
index 900fe45dd3f..d57ac61698f 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputEventEmitter.h
@@ -36,10 +36,23 @@ class WindowsTextInputEventEmitter : public ViewEventEmitter {
std::string key;
};
+ struct OnContentSizeChange {
+ int target;
+ facebook::react::Size contentSize;
+ };
+
+ struct OnEndEditing {
+ int eventCount;
+ int target;
+ std::string text;
+ };
+
void onChange(OnChange value) const;
void onSelectionChange(const OnSelectionChange &value) const;
void onSubmitEditing(OnSubmitEditing value) const;
void onKeyPress(OnKeyPress value) const;
+ void onContentSizeChange(OnContentSizeChange value) const;
+ void onEndEditing(OnEndEditing value) const;
};
-} // namespace facebook::react
+} // namespace facebook::react
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.cpp
index 54cf1e1b40c..c3b13344ec8 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.cpp
@@ -24,6 +24,7 @@ WindowsTextInputProps::WindowsTextInputProps(
*/
allowFontScaling(convertRawProp(context, rawProps, "allowFontScaling", sourceProps.allowFontScaling, {true})),
+ autoCorrect(convertRawProp(context, rawProps, "autoCorrect", sourceProps.autoCorrect, {true})),
clearTextOnFocus(convertRawProp(context, rawProps, "clearTextOnFocus", sourceProps.clearTextOnFocus, {false})),
editable(convertRawProp(context, rawProps, "editable", sourceProps.editable, {true})),
maxLength(convertRawProp(context, rawProps, "maxLength", sourceProps.maxLength, {0})),
@@ -36,7 +37,7 @@ WindowsTextInputProps::WindowsTextInputProps(
selection(convertRawProp(context, rawProps, "selection", sourceProps.selection, {})),
selectionColor(convertRawProp(context, rawProps, "selectionColor", sourceProps.selectionColor, {})),
selectTextOnFocus(convertRawProp(context, rawProps, "selectTextOnFocus", sourceProps.selectTextOnFocus, {false})),
- spellCheck(convertRawProp(context, rawProps, "spellCheck", sourceProps.spellCheck, {false})),
+ spellCheck(convertRawProp(context, rawProps, "spellCheck", sourceProps.spellCheck, {true})),
text(convertRawProp(context, rawProps, "text", sourceProps.text, {})),
mostRecentEventCount(
convertRawProp(context, rawProps, "mostRecentEventCount", sourceProps.mostRecentEventCount, {0})),
@@ -47,7 +48,10 @@ WindowsTextInputProps::WindowsTextInputProps(
autoCapitalize(convertRawProp(context, rawProps, "autoCapitalize", sourceProps.autoCapitalize, {})),
clearTextOnSubmit(convertRawProp(context, rawProps, "clearTextOnSubmit", sourceProps.clearTextOnSubmit, {false})),
submitKeyEvents(convertRawProp(context, rawProps, "submitKeyEvents", sourceProps.submitKeyEvents, {})),
- autoFocus(convertRawProp(context, rawProps, "autoFocus", sourceProps.autoFocus, {false})) {}
+ autoFocus(convertRawProp(context, rawProps, "autoFocus", sourceProps.autoFocus, {false})),
+ textAlign(
+ convertRawProp(context, rawProps, "textAlign", sourceProps.textAlign, facebook::react::TextAlignment::Left)) {
+}
void WindowsTextInputProps::setProp(
const PropsParserContext &context,
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.h
index 99580c66e3b..2004eaca55a 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputProps.h
@@ -4,6 +4,7 @@
#pragma once
#include
+#include
#include
#include
@@ -96,6 +97,7 @@ class WindowsTextInputProps final : public ViewProps, public BaseTextProps {
setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, RawValue const &value);
bool allowFontScaling{true};
+ bool autoCorrect{true};
bool clearTextOnFocus{false};
bool editable{true};
int maxLength{0};
@@ -107,7 +109,7 @@ class WindowsTextInputProps final : public ViewProps, public BaseTextProps {
CompWindowsTextInputSelectionStruct selection{};
SharedColor selectionColor{};
bool selectTextOnFocus{false};
- bool spellCheck{false};
+ bool spellCheck{true};
std::string text{};
int mostRecentEventCount{0};
bool secureTextEntry{false};
@@ -118,6 +120,7 @@ class WindowsTextInputProps final : public ViewProps, public BaseTextProps {
bool clearTextOnSubmit{false};
std::vector submitKeyEvents{};
bool autoFocus{false};
+ facebook::react::TextAlignment textAlign{};
};
} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp
index ddaf498cb69..2db128c80a9 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp
@@ -3,7 +3,7 @@
#include "WindowsTextInputShadowNode.h"
-#include
+#include
#include
#include
#include
@@ -12,38 +12,33 @@
#include
#include
-#include
-
namespace facebook::react {
extern const char WindowsTextInputComponentName[] = "WindowsTextInput";
-void WindowsTextInputShadowNode::setContextContainer(ContextContainer *contextContainer) {
+void WindowsTextInputShadowNode::setTextLayoutManager(std::shared_ptr textLayoutManager) {
ensureUnsealed();
- m_contextContainer = contextContainer;
+ textLayoutManager_ = std::move(textLayoutManager);
}
-AttributedString WindowsTextInputShadowNode::getAttributedString(const LayoutContext &layoutContext) const {
- // Use BaseTextShadowNode to get attributed string from children
-
- auto childTextAttributes = TextAttributes::defaultTextAttributes();
- childTextAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
-
- childTextAttributes.apply(getConcreteProps().textAttributes);
- // Don't propagate the background color of the TextInput onto the attributed
- // string. Android tries to render shadow of the background alongside the
- // shadow of the text which results in weird artifacts.
- childTextAttributes.backgroundColor = HostPlatformColor::UndefinedColor;
+Size WindowsTextInputShadowNode::measureContent(
+ const LayoutContext &layoutContext,
+ const LayoutConstraints &layoutConstraints) const {
+ // Layout is called right after measure.
+ // Measure is marked as `const`, and `layout` is not; so State can be updated
+ // during layout, but not during `measure`. If State is out-of-date in layout,
+ // it's too late: measure will have already operated on old State. Thus, we
+ // use the same value here that we *will* use in layout to update the state.
+ AttributedString attributedString = getMostRecentAttributedString(layoutContext);
- auto attributedString = AttributedString{};
- auto attachments = BaseTextShadowNode::Attachments{};
- BaseTextShadowNode::buildAttributedString(childTextAttributes, *this, attributedString, attachments);
+ if (attributedString.isEmpty()) {
+ attributedString = getPlaceholderAttributedString(layoutContext);
+ }
// BaseTextShadowNode only gets children. We must detect and prepend text
// value attributes manually.
if (!getConcreteProps().text.empty()) {
auto textAttributes = TextAttributes::defaultTextAttributes();
- textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
textAttributes.apply(getConcreteProps().textAttributes);
auto fragment = AttributedString::Fragment{};
fragment.string = getConcreteProps().text;
@@ -53,59 +48,42 @@ AttributedString WindowsTextInputShadowNode::getAttributedString(const LayoutCon
// that effect.
fragment.textAttributes.backgroundColor = clearColor();
fragment.parentShadowView = ShadowView(*this);
- attributedString.prependFragment(fragment);
+ attributedString.prependFragment(std::move(fragment));
}
- return attributedString;
-}
-
-// For measurement purposes, we want to make sure that there's at least a
-// single character in the string so that the measured height is greater
-// than zero. Otherwise, empty TextInputs with no placeholder don't
-// display at all.
-// TODO T67606511: We will redefine the measurement of empty strings as part
-// of T67606511
-AttributedString WindowsTextInputShadowNode::getPlaceholderAttributedString(const LayoutContext &layoutContext) const {
- // Return placeholder text, since text and children are empty.
- auto textAttributedString = AttributedString{};
- auto fragment = AttributedString::Fragment{};
- fragment.string = getConcreteProps().placeholder;
-
- if (fragment.string.empty()) {
- fragment.string = BaseTextShadowNode::getEmptyPlaceholder();
- }
-
- auto textAttributes = TextAttributes::defaultTextAttributes();
- textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
- textAttributes.apply(getConcreteProps().textAttributes);
-
- // If there's no text, it's possible that this Fragment isn't actually
- // appended to the AttributedString (see implementation of appendFragment)
- fragment.textAttributes = textAttributes;
- fragment.parentShadowView = ShadowView(*this);
- textAttributedString.appendFragment(fragment);
+ TextLayoutContext textLayoutContext;
+ textLayoutContext.pointScaleFactor = layoutContext.pointScaleFactor;
- return textAttributedString;
+ facebook::react::ParagraphAttributes paragraphAttributes{};
+ paragraphAttributes.maximumNumberOfLines = getConcreteProps().multiline ? 0 : 1;
+ return textLayoutManager_
+ ->measure(AttributedStringBox{attributedString}, paragraphAttributes, textLayoutContext, layoutConstraints)
+ .size;
}
-void WindowsTextInputShadowNode::setTextLayoutManager(SharedTextLayoutManager textLayoutManager) {
- ensureUnsealed();
- m_textLayoutManager = std::move(textLayoutManager);
+void WindowsTextInputShadowNode::layout(LayoutContext layoutContext) {
+ updateStateIfNeeded(layoutContext);
+ ConcreteViewShadowNode::layout(layoutContext);
}
-AttributedString WindowsTextInputShadowNode::getMostRecentAttributedString(const LayoutContext &layoutContext) const {
- const auto &state = getStateData();
-
- auto reactTreeAttributedString = getAttributedString(layoutContext);
-
- // Sometimes the treeAttributedString will only differ from the state
- // not by inherent properties (string or prop attributes), but by the frame of
- // the parent which has changed Thus, we can't directly compare the entire
- // AttributedString
- bool treeAttributedStringChanged =
- !state.reactTreeAttributedString.compareTextAttributesWithoutFrame(reactTreeAttributedString);
-
- return (!treeAttributedStringChanged ? state.attributedString : reactTreeAttributedString);
+LayoutConstraints WindowsTextInputShadowNode::getTextConstraints(const LayoutConstraints &layoutConstraints) const {
+ if (getConcreteProps().multiline) {
+ return layoutConstraints;
+ } else {
+ // A single line TextInput acts as a horizontal scroller of infinitely
+ // expandable text, so we want to measure the text as if it is allowed to
+ // infinitely expand horizontally, and later clamp to the constraints of the
+ // input.
+ return LayoutConstraints{
+ .minimumSize = layoutConstraints.minimumSize,
+ .maximumSize =
+ Size{
+ .width = std::numeric_limits::infinity(),
+ .height = layoutConstraints.maximumSize.height,
+ },
+ .layoutDirection = layoutConstraints.layoutDirection,
+ };
+ }
}
void WindowsTextInputShadowNode::updateStateIfNeeded(const LayoutContext &layoutContext) {
@@ -146,59 +124,99 @@ void WindowsTextInputShadowNode::updateStateIfNeeded(const LayoutContext &layout
// so no changes are applied There's no way to prevent a state update from
// flowing to Java, so we just ensure it's a noop in those cases.
setStateData(facebook::react::WindowsTextInputState{
- newEventCount,
- newAttributedString,
- reactTreeAttributedString,
- {},
- state.defaultThemePaddingStart,
- state.defaultThemePaddingEnd,
- state.defaultThemePaddingTop,
- state.defaultThemePaddingBottom});
+ AttributedStringBox(newAttributedString), reactTreeAttributedString, {}, newEventCount});
}
-#pragma mark - LayoutableShadowNode
+AttributedString WindowsTextInputShadowNode::getAttributedString(const LayoutContext &layoutContext) const {
+ // Use BaseTextShadowNode to get attributed string from children
-Size WindowsTextInputShadowNode::measureContent(
- const LayoutContext &layoutContext,
- const LayoutConstraints &layoutConstraints) const {
- if (getStateData().cachedAttributedStringId != 0) {
- return m_textLayoutManager
- ->measureCachedSpannableById(
- getStateData().cachedAttributedStringId,
- {}, // TODO getConcreteProps().paragraphAttributes
- layoutConstraints)
- .size;
- }
+ auto childTextAttributes = TextAttributes::defaultTextAttributes();
+ childTextAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
- // Layout is called right after measure.
- // Measure is marked as `const`, and `layout` is not; so State can be updated
- // during layout, but not during `measure`. If State is out-of-date in layout,
- // it's too late: measure will have already operated on old State. Thus, we
- // use the same value here that we *will* use in layout to update the state.
- AttributedString attributedString = getMostRecentAttributedString(layoutContext);
+ childTextAttributes.apply(getConcreteProps().textAttributes);
+ // Don't propagate the background color of the TextInput onto the attributed
+ // string. Android tries to render shadow of the background alongside the
+ // shadow of the text which results in weird artifacts.
+ childTextAttributes.backgroundColor = HostPlatformColor::UndefinedColor;
- if (attributedString.isEmpty()) {
- attributedString = getPlaceholderAttributedString(layoutContext);
- }
+ auto attributedString = AttributedString{};
+ auto attachments = BaseTextShadowNode::Attachments{};
+ BaseTextShadowNode::buildAttributedString(childTextAttributes, *this, attributedString, attachments);
- if (attributedString.isEmpty() && getStateData().mostRecentEventCount != 0) {
- return {0, 0};
+ // BaseTextShadowNode only gets children. We must detect and prepend text
+ // value attributes manually.
+ if (!getConcreteProps().text.empty()) {
+ auto textAttributes = TextAttributes::defaultTextAttributes();
+ textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
+ textAttributes.apply(getConcreteProps().textAttributes);
+ auto fragment = AttributedString::Fragment{};
+ fragment.string = getConcreteProps().text;
+ fragment.textAttributes = textAttributes;
+ // If the TextInput opacity is 0 < n < 1, the opacity of the TextInput and
+ // text value's background will stack. This is a hack/workaround to prevent
+ // that effect.
+ fragment.textAttributes.backgroundColor = clearColor();
+ fragment.parentShadowView = ShadowView(*this);
+ attributedString.prependFragment(std::move(fragment));
}
- TextLayoutContext textLayoutContext;
- textLayoutContext.pointScaleFactor = layoutContext.pointScaleFactor;
- return m_textLayoutManager
- ->measure(
- AttributedStringBox{attributedString},
- {}, // TODO getConcreteProps().paragraphAttributes,
- textLayoutContext,
- layoutConstraints)
- .size;
+ return attributedString;
}
-void WindowsTextInputShadowNode::layout(LayoutContext layoutContext) {
- updateStateIfNeeded(layoutContext);
- ConcreteViewShadowNode::layout(layoutContext);
+AttributedString WindowsTextInputShadowNode::getMostRecentAttributedString(const LayoutContext &layoutContext) const {
+ const auto &state = getStateData();
+
+ auto reactTreeAttributedString = getAttributedString(layoutContext);
+
+ // Sometimes the treeAttributedString will only differ from the state
+ // not by inherent properties (string or prop attributes), but by the frame of
+ // the parent which has changed Thus, we can't directly compare the entire
+ // AttributedString
+ bool treeAttributedStringChanged =
+ !state.reactTreeAttributedString.compareTextAttributesWithoutFrame(reactTreeAttributedString);
+
+ return (!treeAttributedStringChanged ? state.attributedStringBox.getValue() : reactTreeAttributedString);
}
+// For measurement purposes, we want to make sure that there's at least a
+// single character in the string so that the measured height is greater
+// than zero. Otherwise, empty TextInputs with no placeholder don't
+// display at all.
+// TODO T67606511: We will redefine the measurement of empty strings as part
+// of T67606511
+AttributedString WindowsTextInputShadowNode::getPlaceholderAttributedString(const LayoutContext &layoutContext) const {
+ // Return placeholder text, since text and children are empty.
+ auto textAttributedString = AttributedString{};
+ auto fragment = AttributedString::Fragment{};
+ fragment.string = getConcreteProps().placeholder;
+
+ if (fragment.string.empty()) {
+ fragment.string = BaseTextShadowNode::getEmptyPlaceholder();
+ }
+
+ auto textAttributes = TextAttributes::defaultTextAttributes();
+ textAttributes.fontSizeMultiplier = layoutContext.fontSizeMultiplier;
+ textAttributes.apply(getConcreteProps().textAttributes);
+
+ // If there's no text, it's possible that this Fragment isn't actually
+ // appended to the AttributedString (see implementation of appendFragment)
+ fragment.textAttributes = textAttributes;
+ fragment.parentShadowView = ShadowView(*this);
+ textAttributedString.appendFragment(std::move(fragment));
+
+ return textAttributedString;
+ // TextLayoutContext textLayoutContext;
+ // textLayoutContext.pointScaleFactor = layoutContext.pointScaleFactor;
+ // auto textSize = textLayoutManager_
+ // ->measure(
+ // AttributedStringBox{attributedString},
+ // getConcreteProps().paragraphAttributes,
+ // textLayoutContext,
+ // textConstraints)
+ // .size;
+ // return layoutConstraints.clamp(textSize);
+}
+
+#pragma mark - LayoutableShadowNode
+
} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.h
index 38e1b0022f1..ac8cfffe679 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.h
@@ -9,10 +9,10 @@
#include "WindowsTextInputProps.h"
#include "WindowsTextInputState.h"
+#include
#include
#include
-#include
#include
namespace facebook::react {
@@ -28,56 +28,59 @@ class WindowsTextInputShadowNode final : public ConcreteViewShadowNode<
WindowsTextInputEventEmitter,
WindowsTextInputState> {
public:
+ using ConcreteViewShadowNode::ConcreteViewShadowNode;
+
static ShadowNodeTraits BaseTraits() {
auto traits = ConcreteViewShadowNode::BaseTraits();
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
+ traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
traits.set(ShadowNodeTraits::Trait::BaselineYogaNode);
return traits;
}
- using ConcreteViewShadowNode::ConcreteViewShadowNode;
-
- void setContextContainer(ContextContainer *contextContainer);
-
- /*
- * Returns a `AttributedString` which represents text content of the node.
- */
- AttributedString getAttributedString(const LayoutContext &layoutContext) const;
- AttributedString getPlaceholderAttributedString(const LayoutContext &layoutContext) const;
-
/*
* Associates a shared TextLayoutManager with the node.
- * `ParagraphShadowNode` uses the manager to measure text content
- * and construct `ParagraphState` objects.
+ * `TextInputShadowNode` uses the manager to measure text content
+ * and construct `TextInputState` objects.
*/
- void setTextLayoutManager(SharedTextLayoutManager textLayoutManager);
+ void setTextLayoutManager(std::shared_ptr textLayoutManager);
#pragma mark - LayoutableShadowNode
-
+ protected:
Size measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
+
void layout(LayoutContext layoutContext) override;
- private:
- ContextContainer *m_contextContainer{};
+ Float baseline(const LayoutContext &layoutContext, Size size) const override {
+ // Calculate baseline as 80% of the text height
+ return size.height * 0.8f;
+ }
- /**
- * Get the most up-to-date attributed string for measurement and State.
+ std::shared_ptr textLayoutManager_;
+
+ /*
+ * Determines the constraints to use while measure the underlying text
*/
- AttributedString getMostRecentAttributedString(const LayoutContext &layoutContext) const;
+ LayoutConstraints getTextConstraints(const LayoutConstraints &layoutConstraints) const;
+ private:
/*
* Creates a `State` object (with `AttributedText` and
* `TextLayoutManager`) if needed.
*/
void updateStateIfNeeded(const LayoutContext &layoutContext);
- SharedTextLayoutManager m_textLayoutManager;
-
/*
- * Cached attributed string that represents the content of the subtree started
- * from the node.
+ * Returns a `AttributedString` which represents text content of the node.
*/
- mutable std::optional m_cachedAttributedString{};
+ AttributedString getAttributedString(const LayoutContext &layoutContext) const;
+
+ /**
+ * Get the most up-to-date attributed string for measurement and State.
+ */
+ AttributedString getMostRecentAttributedString(const LayoutContext &layoutContext) const;
+
+ AttributedString getPlaceholderAttributedString(const LayoutContext &layoutContext) const;
};
} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.cpp
index 77d89ae820d..d30609720f5 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.cpp
@@ -4,31 +4,21 @@
#include "WindowsTextInputState.h"
-#include
-#include
-#include
-#include
-
-#include
+#ifdef ANDROID
+#include
+#include
+#endif
namespace facebook::react {
WindowsTextInputState::WindowsTextInputState(
- int64_t mostRecentEventCount,
- AttributedString attributedString,
+ AttributedStringBox attributedStringBox,
AttributedString reactTreeAttributedString,
ParagraphAttributes paragraphAttributes,
- double defaultThemePaddingStart,
- double defaultThemePaddingEnd,
- double defaultThemePaddingTop,
- double defaultThemePaddingBottom)
- : mostRecentEventCount(mostRecentEventCount),
- attributedString(std::move(attributedString)),
+ int64_t mostRecentEventCount)
+ : attributedStringBox(std::move(attributedStringBox)),
reactTreeAttributedString(std::move(reactTreeAttributedString)),
paragraphAttributes(std::move(paragraphAttributes)),
- defaultThemePaddingStart(defaultThemePaddingStart),
- defaultThemePaddingEnd(defaultThemePaddingEnd),
- defaultThemePaddingTop(defaultThemePaddingTop),
- defaultThemePaddingBottom(defaultThemePaddingBottom) {}
+ mostRecentEventCount(mostRecentEventCount) {}
} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.h
index 0e8cd188df1..ad5235eaef5 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputState.h
@@ -3,33 +3,29 @@
#pragma once
-#include
-#include
+#include
#include
-#include
-#include
#include
namespace facebook::react {
/*
- * State for component.
+ * State for component.
*/
class WindowsTextInputState final {
public:
- int64_t mostRecentEventCount{0};
+ WindowsTextInputState() = default;
- /**
- * Stores an opaque cache ID used on the Java side to refer to a specific
- * AttributedString for measurement purposes only.
- */
- int64_t cachedAttributedStringId{0};
+ WindowsTextInputState(
+ AttributedStringBox attributedStringBox,
+ AttributedString reactTreeAttributedString,
+ ParagraphAttributes paragraphAttributes,
+ int64_t mostRecentEventCount);
/*
- * All content of component represented as an `AttributedString`.
- * Only set if changed from the React tree's perspective.
+ * All content of component.
*/
- AttributedString attributedString{};
+ AttributedStringBox attributedStringBox;
/*
* All content of component represented as an `AttributedString`.
@@ -44,28 +40,9 @@ class WindowsTextInputState final {
* Represents all visual attributes of a paragraph of text represented as
* a ParagraphAttributes.
*/
- ParagraphAttributes paragraphAttributes{};
+ ParagraphAttributes paragraphAttributes;
- /**
- * Communicates Android theme padding back to the ShadowNode / Component
- * Descriptor for layout.
- */
- double defaultThemePaddingStart{NAN};
- double defaultThemePaddingEnd{NAN};
- double defaultThemePaddingTop{NAN};
- double defaultThemePaddingBottom{NAN};
-
- WindowsTextInputState(
- int64_t mostRecentEventCount,
- AttributedString attributedString,
- AttributedString reactTreeAttributedString,
- ParagraphAttributes paragraphAttributes,
- double defaultThemePaddingStart,
- double defaultThemePaddingEnd,
- double defaultThemePaddingTop,
- double defaultThemePaddingBottom);
-
- WindowsTextInputState() = default;
+ int64_t mostRecentEventCount{0};
};
} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
index d5110cc7b9e..475940e28c6 100644
--- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
@@ -115,7 +115,6 @@ void FabricUIManager::installFabricUIManager() noexcept {
m_scheduler = std::make_shared(
toolbox, (/*animationDriver_ ? animationDriver_.get() :*/ nullptr), this);
- m_surfaceManager = std::make_shared(*m_scheduler);
}
const IComponentViewRegistry &FabricUIManager::GetViewRegistry() const noexcept {
@@ -143,17 +142,38 @@ void FabricUIManager::startSurface(
layoutContext.pointScaleFactor = rootView.ScaleFactor();
layoutContext.fontSizeMultiplier = rootView.FontSizeMultiplier();
- m_surfaceManager->startSurface(
- surfaceId,
- moduleName,
- initialProps,
- layoutConstraints,
- layoutContext // layout context
- );
+ {
+ std::unique_lock lock(m_handlerMutex);
+ auto surfaceHandler = facebook::react::SurfaceHandler{moduleName, surfaceId};
+ surfaceHandler.setContextContainer(m_scheduler->getContextContainer());
+ m_handlerRegistry.emplace(surfaceId, std::move(surfaceHandler));
+ }
+
+ visit(surfaceId, [&](const facebook::react::SurfaceHandler &surfaceHandler) {
+ surfaceHandler.setProps(initialProps);
+ surfaceHandler.constraintLayout(layoutConstraints, layoutContext);
+ m_scheduler->registerSurface(surfaceHandler);
+ surfaceHandler.start();
+ });
+}
+
+void FabricUIManager::setProps(facebook::react::SurfaceId surfaceId, const folly::dynamic &props) const noexcept {
+ visit(surfaceId, [=](const facebook::react::SurfaceHandler &surfaceHandler) { surfaceHandler.setProps(props); });
}
void FabricUIManager::stopSurface(facebook::react::SurfaceId surfaceId) noexcept {
- m_surfaceManager->stopSurface(surfaceId);
+ visit(surfaceId, [&](const facebook::react::SurfaceHandler &surfaceHandler) {
+ surfaceHandler.stop();
+ m_scheduler->unregisterSurface(surfaceHandler);
+ });
+
+ {
+ std::unique_lock lock(m_handlerMutex);
+
+ auto iterator = m_handlerRegistry.find(surfaceId);
+ m_handlerRegistry.erase(iterator);
+ }
+
auto &rootDescriptor = m_registry.componentViewDescriptorWithTag(surfaceId);
rootDescriptor.view.as()->stop();
m_registry.enqueueComponentViewWithComponentHandle(
@@ -164,14 +184,36 @@ facebook::react::Size FabricUIManager::measureSurface(
facebook::react::SurfaceId surfaceId,
const facebook::react::LayoutConstraints &layoutConstraints,
const facebook::react::LayoutContext &layoutContext) const noexcept {
- return m_surfaceManager->measureSurface(surfaceId, layoutConstraints, layoutContext);
+ auto size = facebook::react::Size{};
+
+ visit(surfaceId, [&](const facebook::react::SurfaceHandler &surfaceHandler) {
+ size = surfaceHandler.measure(layoutConstraints, layoutContext);
+ });
+
+ return size;
}
void FabricUIManager::constraintSurfaceLayout(
facebook::react::SurfaceId surfaceId,
const facebook::react::LayoutConstraints &layoutConstraints,
const facebook::react::LayoutContext &layoutContext) const noexcept {
- m_surfaceManager->constraintSurfaceLayout(surfaceId, layoutConstraints, layoutContext);
+ visit(surfaceId, [=](const facebook::react::SurfaceHandler &surfaceHandler) {
+ surfaceHandler.constraintLayout(layoutConstraints, layoutContext);
+ });
+}
+
+void FabricUIManager::visit(
+ facebook::react::SurfaceId surfaceId,
+ const std::function &callback) const noexcept {
+ std::shared_lock lock(m_handlerMutex);
+
+ auto iterator = m_handlerRegistry.find(surfaceId);
+
+ if (iterator == m_handlerRegistry.end()) {
+ return;
+ }
+
+ callback(iterator->second);
}
winrt::Microsoft::ReactNative::ReactNotificationId
diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h
index 486d88cbfe0..25a316c3554 100644
--- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h
+++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h
@@ -6,13 +6,13 @@
#include
#include
#include
-#include
#include
#include "Composition/ComponentViewRegistry.h"
namespace facebook::react {
class Scheduler;
class ReactNativeConfig;
+class SurfaceHandler;
} // namespace facebook::react
namespace Microsoft::ReactNative {
@@ -47,6 +47,8 @@ struct FabricUIManager final : public std::enable_shared_from_this NotifyMountedId() noexcept;
@@ -62,10 +64,13 @@ struct FabricUIManager final : public std::enable_shared_from_this &callback) const noexcept;
+
winrt::Microsoft::ReactNative::ReactContext m_context;
winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext;
std::shared_ptr m_scheduler;
- std::shared_ptr m_surfaceManager;
std::mutex m_schedulerMutex; // Protect m_scheduler
bool m_transactionInFlight{false};
bool m_followUpTransactionRequired{false};
@@ -77,6 +82,9 @@ struct FabricUIManager final : public std::enable_shared_from_this m_surfaceRegistry;
+ std::unordered_map m_handlerRegistry{};
+ mutable std::shared_mutex m_handlerMutex;
+
// Inherited via SchedulerDelegate
virtual void schedulerDidFinishTransaction(
const facebook::react::MountingCoordinator::Shared &mountingCoordinator) override;
diff --git a/vnext/Microsoft.ReactNative/Fabric/ImageRequestParams.cpp b/vnext/Microsoft.ReactNative/Fabric/ImageRequestParams.cpp
new file mode 100644
index 00000000000..86f69e87557
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/Fabric/ImageRequestParams.cpp
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include
+
+namespace facebook::react {
+
+class ImageRequestParams {
+ public:
+ ImageRequestParams() = default;
+ explicit ImageRequestParams(Float blurRadius) : blurRadius(blurRadius) {}
+
+ Float blurRadius{};
+
+ bool operator==(const ImageRequestParams &rhs) const {
+ return this->blurRadius == rhs.blurRadius;
+ }
+
+ bool operator!=(const ImageRequestParams &rhs) const {
+ return !(*this == rhs);
+ }
+};
+
+} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp b/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp
index 9d9e26f7744..964fdeaf78f 100644
--- a/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp
@@ -78,7 +78,9 @@ wicBitmapSourceFromStream(const winrt::Windows::Storage::Streams::IRandomAccessS
}
winrt::Windows::Foundation::IAsyncOperation
-WindowsImageManager::GetImageRandomAccessStreamAsync(ReactImageSource source) const {
+WindowsImageManager::GetImageRandomAccessStreamAsync(
+ ReactImageSource source,
+ std::function progressCallback) const {
co_await winrt::resume_background();
winrt::Windows::Foundation::Uri uri(winrt::to_hstring(source.uri));
@@ -123,6 +125,12 @@ WindowsImageManager::GetImageRandomAccessStreamAsync(ReactImageSource source) co
}
}
+ if (!source.body.empty()) {
+ auto bodyContent = winrt::Windows::Web::Http::HttpStringContent(
+ winrt::to_hstring(source.body), winrt::Windows::Storage::Streams::UnicodeEncoding::Utf8, L"application/json");
+ request.Content(bodyContent);
+ }
+
winrt::Windows::Web::Http::HttpResponseMessage response(co_await m_httpClient.SendRequestAsync(request));
if (!response.IsSuccessStatusCode()) {
@@ -130,8 +138,29 @@ WindowsImageManager::GetImageRandomAccessStreamAsync(ReactImageSource source) co
response.ReasonPhrase(), response.StatusCode(), response.Headers());
}
+ auto inputStream = co_await response.Content().ReadAsInputStreamAsync();
+ auto contentLengthRef = response.Content().Headers().ContentLength();
+ uint64_t total = contentLengthRef ? contentLengthRef.GetUInt64() : 0;
+ uint64_t loaded = 0;
+
winrt::Windows::Storage::Streams::InMemoryRandomAccessStream memoryStream;
- co_await response.Content().WriteToStreamAsync(memoryStream);
+ winrt::Windows::Storage::Streams::DataReader reader(inputStream);
+ constexpr uint32_t bufferSize = 16 * 1024;
+
+ while (true) {
+ uint32_t loadedBuffer = co_await reader.LoadAsync(bufferSize);
+ if (loadedBuffer == 0)
+ break;
+
+ auto buffer = reader.ReadBuffer(loadedBuffer);
+ co_await memoryStream.WriteAsync(buffer);
+ loaded += loadedBuffer;
+
+ if (progressCallback) {
+ progressCallback(loaded, total);
+ }
+ }
+
memoryStream.Seek(0);
co_return winrt::Microsoft::ReactNative::Composition::StreamImageResponse(memoryStream.CloneStream());
@@ -160,7 +189,13 @@ facebook::react::ImageRequest WindowsImageManager::requestImage(
source.width = imageSource.size.width;
source.sourceType = ImageSourceType::Download;
- imageResponseTask = GetImageRandomAccessStreamAsync(source);
+ auto progressCallback = [weakObserverCoordinator](int64_t loaded, int64_t total) {
+ if (auto observerCoordinator = weakObserverCoordinator.lock()) {
+ float progress = total > 0 ? static_cast(loaded) / static_cast(total) : 1.0f;
+ observerCoordinator->nativeImageResponseProgress(progress, loaded, total);
+ }
+ };
+ imageResponseTask = GetImageRandomAccessStreamAsync(source, progressCallback);
}
imageResponseTask.Completed([weakObserverCoordinator](auto asyncOp, auto status) {
@@ -195,11 +230,6 @@ facebook::react::ImageRequest WindowsImageManager::requestImage(
observerCoordinator->nativeImageResponseFailed(facebook::react::ImageLoadError(errorInfo));
break;
}
- case winrt::Windows::Foundation::AsyncStatus::Started: {
- // TODO progress? - Can we register for progress off the download task?
- // observerCoordinator->nativeImageResponseProgress(0.0/*progress*/, 0/*completed*/, 0/*total*/);
- break;
- }
}
});
return imageRequest;
diff --git a/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.h b/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.h
index 2d26bce8c65..8d48afd38db 100644
--- a/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.h
+++ b/vnext/Microsoft.ReactNative/Fabric/WindowsImageManager.h
@@ -22,7 +22,9 @@ struct WindowsImageManager {
private:
winrt::Windows::Foundation::IAsyncOperation
- GetImageRandomAccessStreamAsync(ReactImageSource source) const;
+ GetImageRandomAccessStreamAsync(
+ ReactImageSource source,
+ std::function progressCallback) const;
winrt::Windows::Web::Http::HttpClient m_httpClient;
winrt::Microsoft::ReactNative::ReactContext m_reactContext;
diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp
index 846ff48d012..679b2af55aa 100644
--- a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/TextLayoutManager.cpp
@@ -16,11 +16,60 @@
namespace facebook::react {
+// Creates an empty InlineObject since RN handles actually rendering the Inline object, this just reserves space for it.
+class AttachmentInlineObject : public winrt::implements {
+ public:
+ AttachmentInlineObject(float width, float height) : m_width(width), m_height(height) {}
+
+ // IDWriteInlineObject methods
+ STDMETHOD(Draw)
+ (_In_opt_ void *clientDrawingContext,
+ _In_ IDWriteTextRenderer *renderer,
+ FLOAT originX,
+ FLOAT originY,
+ BOOL isSideways,
+ BOOL isRightToLeft,
+ _In_opt_ IUnknown *clientDrawingEffect) override {
+ // We don't need to draw anything here since the actual rendering is handled by React Native
+ return S_OK;
+ }
+
+ STDMETHOD(GetMetrics)(_Out_ DWRITE_INLINE_OBJECT_METRICS *metrics) override {
+ metrics->width = m_width;
+ metrics->height = m_height;
+ metrics->baseline =
+ m_height; // If the baseline is at the bottom, then baseline = height
+ // (https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_inline_object_metrics)
+ metrics->supportsSideways = true;
+ return S_OK;
+ }
+
+ STDMETHOD(GetOverhangMetrics)(_Out_ DWRITE_OVERHANG_METRICS *overhangs) override {
+ overhangs->left = 0;
+ overhangs->top = 0;
+ overhangs->right = 0;
+ overhangs->bottom = 0;
+ return S_OK;
+ }
+
+ STDMETHOD(GetBreakConditions)
+ (_Out_ DWRITE_BREAK_CONDITION *breakConditionBefore, _Out_ DWRITE_BREAK_CONDITION *breakConditionAfter) override {
+ *breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
+ *breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
+ return S_OK;
+ }
+
+ private:
+ float m_width;
+ float m_height;
+};
+
void TextLayoutManager::GetTextLayout(
const AttributedStringBox &attributedStringBox,
const ParagraphAttributes ¶graphAttributes,
Size size,
- winrt::com_ptr &spTextLayout) noexcept {
+ winrt::com_ptr &spTextLayout,
+ TextMeasurement::Attachments &attachments) noexcept {
const auto &attributedString = attributedStringBox.getValue();
auto fragments = attributedString.getFragments();
auto outerFragment = fragments[0];
@@ -62,6 +111,7 @@ void TextLayoutManager::GetTextLayout(
outerFragment.textAttributes.lineHeight * 0.8f));
}
+ // Set text alignment
DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
if (outerFragment.textAttributes.alignment) {
switch (*outerFragment.textAttributes.alignment) {
@@ -87,6 +137,7 @@ void TextLayoutManager::GetTextLayout(
}
winrt::check_hresult(spTextFormat->SetTextAlignment(alignment));
+ // Get text with Object Replacement Characters for attachments
auto str = GetTransformedText(attributedStringBox);
winrt::check_hresult(Microsoft::ReactNative::DWriteFactory()->CreateTextLayout(
@@ -98,39 +149,75 @@ void TextLayoutManager::GetTextLayout(
spTextLayout.put() // The IDWriteTextLayout interface pointer.
));
+ // Calculate positions for attachments and set inline objects
unsigned int position = 0;
- unsigned int length = 0;
for (const auto &fragment : fragments) {
- length = static_cast(fragment.string.length());
- DWRITE_TEXT_RANGE range = {position, length};
- TextAttributes attributes = fragment.textAttributes;
- DWRITE_FONT_STYLE fragmentStyle = DWRITE_FONT_STYLE_NORMAL;
- if (attributes.fontStyle == facebook::react::FontStyle::Italic)
- fragmentStyle = DWRITE_FONT_STYLE_ITALIC;
- else if (attributes.fontStyle == facebook::react::FontStyle::Oblique)
- fragmentStyle = DWRITE_FONT_STYLE_OBLIQUE;
-
- winrt::check_hresult(spTextLayout->SetFontFamilyName(
- attributes.fontFamily.empty() ? L"Segoe UI"
- : Microsoft::Common::Unicode::Utf8ToUtf16(attributes.fontFamily).c_str(),
- range));
- winrt::check_hresult(spTextLayout->SetFontWeight(
- static_cast(
- attributes.fontWeight.value_or(static_cast