react-native-windows 0.81.0-preview.1 → 0.81.0-preview.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Chakra/ChakraUtils.cpp +0 -2
- package/Directory.Build.props +5 -2
- package/Folly/Folly.vcxproj +6 -7
- package/Folly/cgmanifest.json +1 -1
- package/Microsoft.ReactNative/CompositionSwitcher.idl +163 -162
- package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +104 -4
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +60 -33
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +19 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +102 -18
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +1 -0
- package/Microsoft.ReactNative/Fabric/Composition/UriImageManager.cpp +5 -3
- package/Microsoft.ReactNative/Fabric/WindowsImageManager.cpp +14 -1
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp +21 -2
- package/Microsoft.ReactNative/ReactHost/ReactHost.cpp +39 -10
- package/PropertySheets/Generated/PackageVersion.g.props +2 -2
- package/PropertySheets/React.Cpp.props +1 -0
- package/Shared/MemoryMappedBuffer.cpp +0 -2
- package/package.json +14 -14
- package/template/metro.config.js +2 -4
- package/templates/cpp-app/metro.config.js +2 -4
- package/templates/cpp-lib/example/metro.config.js +2 -3
- package/ReactCommon/TEMP_UntilReactCommonUpdate/react/renderer/css/CSSTokenizer.h +0 -232
package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
CHANGED
|
@@ -35,26 +35,44 @@ struct ModalHostState
|
|
|
35
35
|
struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::Foundation::IInspectable>,
|
|
36
36
|
::Microsoft::ReactNativeSpecs::BaseModalHostView<ModalHostView> {
|
|
37
37
|
~ModalHostView() {
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (m_popUp) {
|
|
39
|
+
// Unregister closing event handler
|
|
40
|
+
if (m_appWindow && m_appWindowClosingToken) {
|
|
41
|
+
m_appWindow.Closing(m_appWindowClosingToken);
|
|
42
|
+
m_appWindowClosingToken.value = 0;
|
|
43
|
+
}
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Reset topWindowID before destroying
|
|
46
|
+
if (m_prevWindowID) {
|
|
47
|
+
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
|
|
48
|
+
m_reactContext.Properties().Handle(), m_prevWindowID);
|
|
49
|
+
m_prevWindowID = 0;
|
|
50
|
+
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
// Close island
|
|
53
|
+
if (m_reactNativeIsland) {
|
|
54
|
+
m_reactNativeIsland.Island().Close();
|
|
55
|
+
m_reactNativeIsland = nullptr;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Hide popup
|
|
59
|
+
if (m_popUp.IsVisible()) {
|
|
60
|
+
m_popUp.Hide();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Destroy AppWindow this automatically resumes parent window to receive inputs
|
|
64
|
+
if (m_appWindow) {
|
|
65
|
+
m_appWindow.Destroy();
|
|
66
|
+
m_appWindow = nullptr;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Bring parent window to foreground
|
|
70
|
+
if (m_parentHwnd) {
|
|
71
|
+
SetForegroundWindow(m_parentHwnd);
|
|
72
|
+
SetFocus(m_parentHwnd);
|
|
57
73
|
}
|
|
74
|
+
|
|
75
|
+
// Close bridge
|
|
58
76
|
m_popUp.Close();
|
|
59
77
|
m_popUp = nullptr;
|
|
60
78
|
}
|
|
@@ -88,7 +106,7 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
|
|
|
88
106
|
QueueShow(view);
|
|
89
107
|
} else {
|
|
90
108
|
m_visible = false;
|
|
91
|
-
|
|
109
|
+
HideWindow();
|
|
92
110
|
}
|
|
93
111
|
}
|
|
94
112
|
|
|
@@ -219,31 +237,33 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
|
|
|
219
237
|
}
|
|
220
238
|
}
|
|
221
239
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
240
|
+
/*
|
|
241
|
+
HideWindow called on visible=false
|
|
242
|
+
unmounts the modal window using onDismiss event
|
|
243
|
+
*/
|
|
244
|
+
void HideWindow() noexcept {
|
|
245
|
+
// Hide popup
|
|
225
246
|
if (m_popUp) {
|
|
226
247
|
m_popUp.Hide();
|
|
227
248
|
}
|
|
228
249
|
|
|
229
|
-
//
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
250
|
+
// Restore message routing to parent
|
|
251
|
+
if (m_prevWindowID) {
|
|
252
|
+
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
|
|
253
|
+
m_reactContext.Properties().Handle(), m_prevWindowID);
|
|
233
254
|
}
|
|
234
255
|
|
|
235
|
-
//
|
|
256
|
+
// Bring parent window to foreground
|
|
257
|
+
if (m_parentHwnd) {
|
|
258
|
+
SetForegroundWindow(m_parentHwnd);
|
|
259
|
+
SetFocus(m_parentHwnd);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Dispatch onDismiss event
|
|
236
263
|
if (auto eventEmitter = EventEmitter()) {
|
|
237
264
|
::Microsoft::ReactNativeSpecs::ModalHostViewEventEmitter::OnDismiss eventArgs;
|
|
238
265
|
eventEmitter->onDismiss(eventArgs);
|
|
239
266
|
}
|
|
240
|
-
|
|
241
|
-
// reset the topWindowID
|
|
242
|
-
if (m_prevWindowID) {
|
|
243
|
-
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
|
|
244
|
-
m_reactContext.Properties().Handle(), m_prevWindowID);
|
|
245
|
-
m_prevWindowID = 0;
|
|
246
|
-
}
|
|
247
267
|
}
|
|
248
268
|
|
|
249
269
|
// creates a new modal window
|
|
@@ -279,9 +299,16 @@ struct ModalHostView : public winrt::implements<ModalHostView, winrt::Windows::F
|
|
|
279
299
|
overlappedPresenter.IsModal(true);
|
|
280
300
|
overlappedPresenter.SetBorderAndTitleBar(true, true);
|
|
281
301
|
|
|
302
|
+
// modal should only have close button
|
|
303
|
+
overlappedPresenter.IsMinimizable(false);
|
|
304
|
+
overlappedPresenter.IsMaximizable(false);
|
|
305
|
+
|
|
282
306
|
// Apply the presenter to the window
|
|
283
307
|
m_appWindow.SetPresenter(overlappedPresenter);
|
|
284
308
|
|
|
309
|
+
// Hide the title bar icon
|
|
310
|
+
m_appWindow.TitleBar().IconShowOptions(winrt::Microsoft::UI::Windowing::IconShowOptions::HideIconAndSystemMenu);
|
|
311
|
+
|
|
285
312
|
// Set initial title using the stored local props
|
|
286
313
|
if (m_localProps && m_localProps->title.has_value()) {
|
|
287
314
|
winrt::hstring titleValue = winrt::to_hstring(m_localProps->title.value());
|
|
@@ -814,6 +814,25 @@ void ScrollViewComponentView::updateProps(
|
|
|
814
814
|
}
|
|
815
815
|
m_scrollVisual.SetSnapPoints(newViewProps.snapToStart, newViewProps.snapToEnd, snapToOffsets.GetView());
|
|
816
816
|
}
|
|
817
|
+
|
|
818
|
+
if (!oldProps || oldViewProps.pagingEnabled != newViewProps.pagingEnabled) {
|
|
819
|
+
m_scrollVisual.PagingEnabled(newViewProps.pagingEnabled);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (!oldProps || oldViewProps.snapToInterval != newViewProps.snapToInterval) {
|
|
823
|
+
m_scrollVisual.SnapToInterval(static_cast<float>(newViewProps.snapToInterval));
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (!oldProps || oldViewProps.snapToAlignment != newViewProps.snapToAlignment) {
|
|
827
|
+
using SnapPointsAlignment = winrt::Microsoft::ReactNative::Composition::Experimental::SnapPointsAlignment;
|
|
828
|
+
SnapPointsAlignment alignment = SnapPointsAlignment::Near; // default is "start"
|
|
829
|
+
if (newViewProps.snapToAlignment == facebook::react::ScrollViewSnapToAlignment::Center) {
|
|
830
|
+
alignment = SnapPointsAlignment::Center;
|
|
831
|
+
} else if (newViewProps.snapToAlignment == facebook::react::ScrollViewSnapToAlignment::End) {
|
|
832
|
+
alignment = SnapPointsAlignment::Far;
|
|
833
|
+
}
|
|
834
|
+
m_scrollVisual.SnapToAlignment(alignment);
|
|
835
|
+
}
|
|
817
836
|
}
|
|
818
837
|
|
|
819
838
|
void ScrollViewComponentView::updateState(
|
package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp
CHANGED
|
@@ -697,10 +697,17 @@ void WindowsTextInputComponentView::OnPointerPressed(
|
|
|
697
697
|
}
|
|
698
698
|
|
|
699
699
|
if (m_textServices && msg) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
700
|
+
if (msg == WM_RBUTTONUP && !windowsTextInputProps().contextMenuHidden) {
|
|
701
|
+
ShowContextMenu(position);
|
|
702
|
+
args.Handled(true);
|
|
703
|
+
} else if (msg == WM_RBUTTONUP && windowsTextInputProps().contextMenuHidden) {
|
|
704
|
+
args.Handled(true);
|
|
705
|
+
} else {
|
|
706
|
+
LRESULT lresult;
|
|
707
|
+
DrawBlock db(*this);
|
|
708
|
+
auto hr = m_textServices->TxSendMessage(msg, static_cast<WPARAM>(wParam), static_cast<LPARAM>(lParam), &lresult);
|
|
709
|
+
args.Handled(hr != S_FALSE);
|
|
710
|
+
}
|
|
704
711
|
}
|
|
705
712
|
|
|
706
713
|
// Emits the OnPressIn event
|
|
@@ -844,8 +851,8 @@ void WindowsTextInputComponentView::OnPointerWheelChanged(
|
|
|
844
851
|
}
|
|
845
852
|
void WindowsTextInputComponentView::OnKeyDown(
|
|
846
853
|
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
|
|
847
|
-
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
848
|
-
// behavior We do forward Ctrl+Tab to the textinput.
|
|
854
|
+
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
855
|
+
// WinUI behavior We do forward Ctrl+Tab to the textinput.
|
|
849
856
|
if (args.Key() != winrt::Windows::System::VirtualKey::Tab ||
|
|
850
857
|
(args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
|
|
851
858
|
winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) {
|
|
@@ -872,8 +879,8 @@ void WindowsTextInputComponentView::OnKeyDown(
|
|
|
872
879
|
|
|
873
880
|
void WindowsTextInputComponentView::OnKeyUp(
|
|
874
881
|
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {
|
|
875
|
-
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
876
|
-
// behavior We do forward Ctrl+Tab to the textinput.
|
|
882
|
+
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
883
|
+
// WinUI behavior We do forward Ctrl+Tab to the textinput.
|
|
877
884
|
if (args.Key() != winrt::Windows::System::VirtualKey::Tab ||
|
|
878
885
|
(args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
|
|
879
886
|
winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) {
|
|
@@ -943,8 +950,8 @@ bool WindowsTextInputComponentView::ShouldSubmit(
|
|
|
943
950
|
|
|
944
951
|
void WindowsTextInputComponentView::OnCharacterReceived(
|
|
945
952
|
const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept {
|
|
946
|
-
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
947
|
-
// behavior We do forward Ctrl+Tab to the textinput.
|
|
953
|
+
// Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with
|
|
954
|
+
// WinUI behavior We do forward Ctrl+Tab to the textinput.
|
|
948
955
|
if ((args.KeyCode() == '\t') &&
|
|
949
956
|
((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) &
|
|
950
957
|
winrt::Microsoft::UI::Input::VirtualKeyStates::Down) != winrt::Microsoft::UI::Input::VirtualKeyStates::Down)) {
|
|
@@ -1547,25 +1554,59 @@ void WindowsTextInputComponentView::UpdateParaFormat() noexcept {
|
|
|
1547
1554
|
m_pf.dwMask = PFM_ALL;
|
|
1548
1555
|
|
|
1549
1556
|
auto &textAlign = windowsTextInputProps().textAlign;
|
|
1557
|
+
auto &baseWritingDirection = windowsTextInputProps().textAttributes.baseWritingDirection;
|
|
1558
|
+
|
|
1559
|
+
// Handle writingDirection (baseWritingDirection)
|
|
1560
|
+
// For WritingDirection::Natural, use the computed layout direction from the layout tree
|
|
1561
|
+
// since direction can be overridden at any point in the tree
|
|
1562
|
+
bool isRTL = false;
|
|
1563
|
+
if (baseWritingDirection.has_value()) {
|
|
1564
|
+
if (*baseWritingDirection == facebook::react::WritingDirection::RightToLeft) {
|
|
1565
|
+
isRTL = true;
|
|
1566
|
+
m_pf.dwMask |= PFM_RTLPARA;
|
|
1567
|
+
m_pf.wEffects |= PFE_RTLPARA;
|
|
1568
|
+
} else if (*baseWritingDirection == facebook::react::WritingDirection::LeftToRight) {
|
|
1569
|
+
isRTL = false;
|
|
1570
|
+
// Ensure RTL flag is not set
|
|
1571
|
+
m_pf.wEffects &= ~PFE_RTLPARA;
|
|
1572
|
+
} else if (*baseWritingDirection == facebook::react::WritingDirection::Natural) {
|
|
1573
|
+
// Natural uses the layout direction computed from the tree
|
|
1574
|
+
isRTL = (layoutMetrics().layoutDirection == facebook::react::LayoutDirection::RightToLeft);
|
|
1575
|
+
if (isRTL) {
|
|
1576
|
+
m_pf.dwMask |= PFM_RTLPARA;
|
|
1577
|
+
m_pf.wEffects |= PFE_RTLPARA;
|
|
1578
|
+
} else {
|
|
1579
|
+
m_pf.wEffects &= ~PFE_RTLPARA;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
} else {
|
|
1583
|
+
// No explicit writing direction set - use layout direction from tree
|
|
1584
|
+
isRTL = (layoutMetrics().layoutDirection == facebook::react::LayoutDirection::RightToLeft);
|
|
1585
|
+
if (isRTL) {
|
|
1586
|
+
m_pf.dwMask |= PFM_RTLPARA;
|
|
1587
|
+
m_pf.wEffects |= PFE_RTLPARA;
|
|
1588
|
+
} else {
|
|
1589
|
+
m_pf.wEffects &= ~PFE_RTLPARA;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1550
1592
|
|
|
1593
|
+
// Handle textAlign
|
|
1551
1594
|
if (textAlign == facebook::react::TextAlignment::Center) {
|
|
1552
1595
|
m_pf.wAlignment = PFA_CENTER;
|
|
1553
1596
|
} else if (textAlign == facebook::react::TextAlignment::Right) {
|
|
1554
1597
|
m_pf.wAlignment = PFA_RIGHT;
|
|
1598
|
+
} else if (textAlign == facebook::react::TextAlignment::Justified) {
|
|
1599
|
+
m_pf.wAlignment = PFA_JUSTIFY;
|
|
1600
|
+
} else if (textAlign == facebook::react::TextAlignment::Natural) {
|
|
1601
|
+
// Natural alignment respects writing direction
|
|
1602
|
+
m_pf.wAlignment = isRTL ? PFA_RIGHT : PFA_LEFT;
|
|
1555
1603
|
} else {
|
|
1604
|
+
// Default to left alignment
|
|
1556
1605
|
m_pf.wAlignment = PFA_LEFT;
|
|
1557
1606
|
}
|
|
1558
1607
|
|
|
1559
1608
|
m_pf.cTabCount = 1;
|
|
1560
1609
|
m_pf.rgxTabs[0] = lDefaultTab;
|
|
1561
|
-
|
|
1562
|
-
/*
|
|
1563
|
-
if (m_spcontroller->IsCurrentReadingOrderRTL())
|
|
1564
|
-
{
|
|
1565
|
-
m_pf.dwMask |= PFM_RTLPARA;
|
|
1566
|
-
m_pf.wEffects |= PFE_RTLPARA;
|
|
1567
|
-
}
|
|
1568
|
-
*/
|
|
1569
1610
|
}
|
|
1570
1611
|
|
|
1571
1612
|
void WindowsTextInputComponentView::OnRenderingDeviceLost() noexcept {
|
|
@@ -1826,4 +1867,47 @@ void WindowsTextInputComponentView::updateSpellCheck(bool enable) noexcept {
|
|
|
1826
1867
|
winrt::check_hresult(
|
|
1827
1868
|
m_textServices->TxSendMessage(EM_SETLANGOPTIONS, IMF_SPELLCHECKING, enable ? newLangOptions : 0, &lresult));
|
|
1828
1869
|
}
|
|
1870
|
+
|
|
1871
|
+
void WindowsTextInputComponentView::ShowContextMenu(const winrt::Windows::Foundation::Point &position) noexcept {
|
|
1872
|
+
HMENU menu = CreatePopupMenu();
|
|
1873
|
+
if (!menu)
|
|
1874
|
+
return;
|
|
1875
|
+
|
|
1876
|
+
CHARRANGE selection;
|
|
1877
|
+
LRESULT res;
|
|
1878
|
+
m_textServices->TxSendMessage(EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection), &res);
|
|
1879
|
+
|
|
1880
|
+
bool hasSelection = selection.cpMin != selection.cpMax;
|
|
1881
|
+
bool isEmpty = GetTextFromRichEdit().empty();
|
|
1882
|
+
bool isReadOnly = windowsTextInputProps().editable == false;
|
|
1883
|
+
bool canPaste = !isReadOnly && IsClipboardFormatAvailable(CF_UNICODETEXT);
|
|
1884
|
+
|
|
1885
|
+
AppendMenuW(menu, MF_STRING | (hasSelection && !isReadOnly ? 0 : MF_GRAYED), 1, L"Cut");
|
|
1886
|
+
AppendMenuW(menu, MF_STRING | (hasSelection ? 0 : MF_GRAYED), 2, L"Copy");
|
|
1887
|
+
AppendMenuW(menu, MF_STRING | (canPaste ? 0 : MF_GRAYED), 3, L"Paste");
|
|
1888
|
+
AppendMenuW(menu, MF_STRING | (!isEmpty && !isReadOnly ? 0 : MF_GRAYED), 4, L"Select All");
|
|
1889
|
+
|
|
1890
|
+
POINT cursorPos;
|
|
1891
|
+
GetCursorPos(&cursorPos);
|
|
1892
|
+
|
|
1893
|
+
HWND hwnd = GetActiveWindow();
|
|
1894
|
+
|
|
1895
|
+
int cmd = TrackPopupMenu(
|
|
1896
|
+
menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_NONOTIFY, cursorPos.x, cursorPos.y, 0, hwnd, NULL);
|
|
1897
|
+
|
|
1898
|
+
if (cmd == 1) { // Cut
|
|
1899
|
+
m_textServices->TxSendMessage(WM_CUT, 0, 0, &res);
|
|
1900
|
+
OnTextUpdated();
|
|
1901
|
+
} else if (cmd == 2) { // Copy
|
|
1902
|
+
m_textServices->TxSendMessage(WM_COPY, 0, 0, &res);
|
|
1903
|
+
} else if (cmd == 3) { // Paste
|
|
1904
|
+
m_textServices->TxSendMessage(WM_PASTE, 0, 0, &res);
|
|
1905
|
+
OnTextUpdated();
|
|
1906
|
+
} else if (cmd == 4) { // Select All
|
|
1907
|
+
m_textServices->TxSendMessage(EM_SETSEL, 0, -1, &res);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
DestroyMenu(menu);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1829
1913
|
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
|
|
@@ -118,6 +118,7 @@ struct WindowsTextInputComponentView
|
|
|
118
118
|
void updateLetterSpacing(float letterSpacing) noexcept;
|
|
119
119
|
void updateAutoCorrect(bool value) noexcept;
|
|
120
120
|
void updateSpellCheck(bool value) noexcept;
|
|
121
|
+
void ShowContextMenu(const winrt::Windows::Foundation::Point &position) noexcept;
|
|
121
122
|
|
|
122
123
|
winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr};
|
|
123
124
|
winrt::Microsoft::ReactNative::Composition::Experimental::ICaretVisual m_caretVisual{nullptr};
|
|
@@ -291,9 +291,11 @@ ImageResponseOrImageErrorInfo ImageFailedResponse::ResolveImage() {
|
|
|
291
291
|
if (imageOrError.errorInfo->error.empty()) {
|
|
292
292
|
imageOrError.errorInfo->error = "Failed to load image.";
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
if (m_responseHeaders) {
|
|
295
|
+
for (auto &&[header, value] : m_responseHeaders) {
|
|
296
|
+
imageOrError.errorInfo->httpResponseHeaders.push_back(
|
|
297
|
+
std::make_pair<std::string, std::string>(winrt::to_string(header), winrt::to_string(value)));
|
|
298
|
+
}
|
|
297
299
|
}
|
|
298
300
|
return imageOrError;
|
|
299
301
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#include <Fabric/Composition/ImageResponseImage.h>
|
|
11
11
|
#include <Fabric/Composition/UriImageManager.h>
|
|
12
12
|
#include <Networking/NetworkPropertyIds.h>
|
|
13
|
+
#include <Utils/CppWinrtLessExceptions.h>
|
|
13
14
|
#include <Utils/ImageUtils.h>
|
|
14
15
|
#include <fmt/format.h>
|
|
15
16
|
#include <functional/functor.h>
|
|
@@ -131,7 +132,19 @@ WindowsImageManager::GetImageRandomAccessStreamAsync(
|
|
|
131
132
|
request.Content(bodyContent);
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
auto asyncOp = m_httpClient.SendRequestAsync(request);
|
|
136
|
+
co_await lessthrow_await_adapter<winrt::Windows::Foundation::IAsyncOperationWithProgress<
|
|
137
|
+
winrt::Windows::Web::Http::HttpResponseMessage,
|
|
138
|
+
winrt::Windows::Web::Http::HttpProgress>>{asyncOp};
|
|
139
|
+
|
|
140
|
+
if (asyncOp.Status() == winrt::Windows::Foundation::AsyncStatus::Error ||
|
|
141
|
+
asyncOp.Status() == winrt::Windows::Foundation::AsyncStatus::Canceled) {
|
|
142
|
+
auto errorMessage = FormatHResultError(winrt::hresult_error(asyncOp.ErrorCode()));
|
|
143
|
+
co_return winrt::Microsoft::ReactNative::Composition::ImageFailedResponse(
|
|
144
|
+
winrt::to_hstring("Network request failed: " + errorMessage));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
winrt::Windows::Web::Http::HttpResponseMessage response = asyncOp.GetResults();
|
|
135
148
|
|
|
136
149
|
if (!response.IsSuccessStatusCode()) {
|
|
137
150
|
co_return winrt::Microsoft::ReactNative::Composition::ImageFailedResponse(
|
|
@@ -136,6 +136,25 @@ void WindowsTextLayoutManager::GetTextLayout(
|
|
|
136
136
|
outerFragment.textAttributes.lineHeight * 0.8f));
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
// Set reading direction (RTL/LTR) based on baseWritingDirection
|
|
140
|
+
// Only set reading direction if explicitly specified to avoid breaking existing layouts
|
|
141
|
+
bool isRTL = false;
|
|
142
|
+
if (outerFragment.textAttributes.baseWritingDirection.has_value()) {
|
|
143
|
+
DWRITE_READING_DIRECTION readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
144
|
+
if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::RightToLeft) {
|
|
145
|
+
readingDirection = DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
146
|
+
isRTL = true;
|
|
147
|
+
} else if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::LeftToRight) {
|
|
148
|
+
readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
149
|
+
isRTL = false;
|
|
150
|
+
} else if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::Natural) {
|
|
151
|
+
// Natural uses the layout direction from textAttributes
|
|
152
|
+
isRTL = (outerFragment.textAttributes.layoutDirection == facebook::react::LayoutDirection::RightToLeft);
|
|
153
|
+
readingDirection = isRTL ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
154
|
+
}
|
|
155
|
+
winrt::check_hresult(spTextFormat->SetReadingDirection(readingDirection));
|
|
156
|
+
}
|
|
157
|
+
|
|
139
158
|
// Set text alignment
|
|
140
159
|
DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
141
160
|
if (outerFragment.textAttributes.alignment) {
|
|
@@ -152,9 +171,9 @@ void WindowsTextLayoutManager::GetTextLayout(
|
|
|
152
171
|
case facebook::react::TextAlignment::Right:
|
|
153
172
|
alignment = DWRITE_TEXT_ALIGNMENT_TRAILING;
|
|
154
173
|
break;
|
|
155
|
-
// TODO use LTR values
|
|
156
174
|
case facebook::react::TextAlignment::Natural:
|
|
157
|
-
alignment
|
|
175
|
+
// Natural alignment respects reading direction if baseWritingDirection was set
|
|
176
|
+
alignment = isRTL ? DWRITE_TEXT_ALIGNMENT_TRAILING : DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
158
177
|
break;
|
|
159
178
|
default:
|
|
160
179
|
assert(false);
|
|
@@ -310,10 +310,6 @@ class ReactNativeWindowsFeatureFlags : public facebook::react::ReactNativeFeatur
|
|
|
310
310
|
bool fuseboxEnabledRelease() override {
|
|
311
311
|
return true; // Enable Fusebox (modern CDP backend) by default for React Native Windows
|
|
312
312
|
}
|
|
313
|
-
|
|
314
|
-
bool fuseboxNetworkInspectionEnabled() override {
|
|
315
|
-
return true; // Enable network inspection support in Fusebox
|
|
316
|
-
}
|
|
317
313
|
};
|
|
318
314
|
|
|
319
315
|
//=============================================================================================
|
|
@@ -326,10 +322,32 @@ class ReactInspectorHostTargetDelegate : public jsinspector_modern::HostTargetDe
|
|
|
326
322
|
ReactInspectorHostTargetDelegate(Mso::WeakPtr<ReactHost> &&reactHost) noexcept : m_reactHost(std::move(reactHost)) {}
|
|
327
323
|
|
|
328
324
|
jsinspector_modern::HostTargetMetadata getMetadata() override {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
325
|
+
jsinspector_modern::HostTargetMetadata metadata{};
|
|
326
|
+
metadata.integrationName = "React Native Windows (Host)";
|
|
327
|
+
metadata.platform = "windows";
|
|
328
|
+
|
|
329
|
+
if (Mso::CntPtr<ReactHost> reactHost = m_reactHost.GetStrongPtr()) {
|
|
330
|
+
const ReactOptions &options = reactHost->Options();
|
|
331
|
+
if (!options.Identity.empty()) {
|
|
332
|
+
std::string identity = options.Identity;
|
|
333
|
+
// Replace illegal characters with underscore
|
|
334
|
+
for (char &c : identity) {
|
|
335
|
+
if (c == '\\' || c == '/' || c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' ||
|
|
336
|
+
c == '|') {
|
|
337
|
+
c = '_';
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
metadata.appDisplayName = identity;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
345
|
+
DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
|
|
346
|
+
if (GetComputerNameW(computerName, &size)) {
|
|
347
|
+
metadata.deviceName = winrt::to_string(computerName);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return metadata;
|
|
333
351
|
}
|
|
334
352
|
|
|
335
353
|
void onReload(jsinspector_modern::HostTargetDelegate::PageReloadRequest const &request) override {
|
|
@@ -630,9 +648,20 @@ void ReactHost::AddInspectorPage() noexcept {
|
|
|
630
648
|
jsinspector_modern::InspectorTargetCapabilities capabilities;
|
|
631
649
|
capabilities.nativePageReloads = true;
|
|
632
650
|
capabilities.prefersFuseboxFrontend = true;
|
|
633
|
-
|
|
651
|
+
|
|
652
|
+
auto metadata = m_inspectorHostTargetDelegate->getMetadata();
|
|
653
|
+
std::string pageName;
|
|
654
|
+
if (metadata.appDisplayName.has_value() && !metadata.appDisplayName.value().empty()) {
|
|
655
|
+
pageName = metadata.appDisplayName.value();
|
|
656
|
+
} else {
|
|
657
|
+
pageName = "React Native Windows (Experimental)";
|
|
658
|
+
}
|
|
659
|
+
if (metadata.deviceName.has_value() && !metadata.deviceName.value().empty()) {
|
|
660
|
+
pageName += " (" + metadata.deviceName.value() + ")";
|
|
661
|
+
}
|
|
662
|
+
|
|
634
663
|
inspectorPageId = jsinspector_modern::getInspectorInstance().addPage(
|
|
635
|
-
|
|
664
|
+
pageName,
|
|
636
665
|
"Hermes",
|
|
637
666
|
[weakInspectorHostTarget =
|
|
638
667
|
std::weak_ptr(m_inspectorHostTarget)](std::unique_ptr<jsinspector_modern::IRemoteConnection> remote)
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.81.0-preview.
|
|
13
|
+
<ReactNativeWindowsVersion>0.81.0-preview.10</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>81</ReactNativeWindowsMinor>
|
|
16
16
|
<ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
|
|
17
17
|
<ReactNativeWindowsCanary>false</ReactNativeWindowsCanary>
|
|
18
|
-
<ReactNativeWindowsCommitId>
|
|
18
|
+
<ReactNativeWindowsCommitId>f8c11e35831ed3a036757abf53f7d3959c186634</ReactNativeWindowsCommitId>
|
|
19
19
|
</PropertyGroup>
|
|
20
20
|
</Project>
|
|
@@ -77,8 +77,6 @@ MemoryMappedBuffer::MemoryMappedBuffer(const wchar_t *const filename, uint32_t o
|
|
|
77
77
|
throw facebook::jsi::JSINativeException(
|
|
78
78
|
"MapViewOfFile/MapViewOfFileFromApp failed with last error " + std::to_string(GetLastError()));
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
WerRegisterMemoryBlock(m_fileData.get(), m_fileSize);
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
size_t MemoryMappedBuffer::size() const {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-windows",
|
|
3
|
-
"version": "0.81.0-preview.
|
|
3
|
+
"version": "0.81.0-preview.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"@react-native-community/cli": "17.0.0",
|
|
27
27
|
"@react-native-community/cli-platform-android": "17.0.0",
|
|
28
28
|
"@react-native-community/cli-platform-ios": "17.0.0",
|
|
29
|
-
"@react-native-windows/cli": "0.81.0-preview.
|
|
29
|
+
"@react-native-windows/cli": "0.81.0-preview.3",
|
|
30
30
|
"@react-native/assets": "1.0.0",
|
|
31
|
-
"@react-native/assets-registry": "0.81.0
|
|
32
|
-
"@react-native/codegen": "0.81.0
|
|
33
|
-
"@react-native/community-cli-plugin": "0.81.0
|
|
34
|
-
"@react-native/gradle-plugin": "0.81.0
|
|
35
|
-
"@react-native/js-polyfills": "0.81.0
|
|
36
|
-
"@react-native/normalize-colors": "0.81.0
|
|
37
|
-
"@react-native/virtualized-lists": "0.81.0
|
|
38
|
-
"@react-native/new-app-screen": "0.81.0
|
|
31
|
+
"@react-native/assets-registry": "0.81.0",
|
|
32
|
+
"@react-native/codegen": "0.81.0",
|
|
33
|
+
"@react-native/community-cli-plugin": "0.81.0",
|
|
34
|
+
"@react-native/gradle-plugin": "0.81.0",
|
|
35
|
+
"@react-native/js-polyfills": "0.81.0",
|
|
36
|
+
"@react-native/normalize-colors": "0.81.0",
|
|
37
|
+
"@react-native/virtualized-lists": "0.81.0",
|
|
38
|
+
"@react-native/new-app-screen": "0.81.0",
|
|
39
39
|
"abort-controller": "^3.0.0",
|
|
40
40
|
"anser": "^1.4.9",
|
|
41
41
|
"ansi-regex": "^5.0.0",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"yargs": "^17.6.2"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@react-native-windows/codegen": "0.81.0-preview.
|
|
72
|
-
"@react-native/metro-config": "0.81.0
|
|
71
|
+
"@react-native-windows/codegen": "0.81.0-preview.3",
|
|
72
|
+
"@react-native/metro-config": "0.81.0",
|
|
73
73
|
"@rnw-scripts/babel-react-native-config": "0.0.0",
|
|
74
74
|
"@rnw-scripts/eslint-config": "1.2.37",
|
|
75
75
|
"@rnw-scripts/jest-out-of-tree-snapshot-resolver": "^1.1.41",
|
|
@@ -85,14 +85,14 @@
|
|
|
85
85
|
"prettier": "2.8.8",
|
|
86
86
|
"react": "19.1.0",
|
|
87
87
|
"react-native": "0.81.0-rc.0",
|
|
88
|
-
"react-native-platform-override": "
|
|
88
|
+
"react-native-platform-override": "0.81.0-preview.10",
|
|
89
89
|
"react-refresh": "^0.14.0",
|
|
90
90
|
"typescript": "5.0.4"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
93
|
"@types/react": "^19.1.0",
|
|
94
94
|
"react": "^19.1.0",
|
|
95
|
-
"react-native": "0.81.0
|
|
95
|
+
"react-native": "0.81.0"
|
|
96
96
|
},
|
|
97
97
|
"beachball": {
|
|
98
98
|
"defaultNpmTag": "preview",
|
package/template/metro.config.js
CHANGED
|
@@ -2,8 +2,6 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
|
6
|
-
|
|
7
5
|
const rnwPath = fs.realpathSync(
|
|
8
6
|
path.resolve(require.resolve('react-native-windows/package.json'), '..'),
|
|
9
7
|
);
|
|
@@ -25,7 +23,7 @@ const config = {
|
|
|
25
23
|
watchFolders: [rnwPath, rnwRootNodeModules, rnwPackages],
|
|
26
24
|
// devMode]{{/devMode}}
|
|
27
25
|
resolver: {
|
|
28
|
-
blockList:
|
|
26
|
+
blockList: [
|
|
29
27
|
// This stops "npx @react-native-community/cli run-windows" from causing the metro server to crash if its already running
|
|
30
28
|
new RegExp(
|
|
31
29
|
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
|
|
@@ -34,7 +32,7 @@ const config = {
|
|
|
34
32
|
new RegExp(`${rnwPath}/build/.*`),
|
|
35
33
|
new RegExp(`${rnwPath}/target/.*`),
|
|
36
34
|
/.*\.ProjectImports\.zip/,
|
|
37
|
-
]
|
|
35
|
+
],
|
|
38
36
|
//{{#devMode}} [devMode
|
|
39
37
|
extraNodeModules: {
|
|
40
38
|
'react-native-windows': rnwPath,
|
|
@@ -2,8 +2,6 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const exclusionList = require('metro-config/src/defaults/exclusionList');
|
|
6
|
-
|
|
7
5
|
const rnwPath = fs.realpathSync(
|
|
8
6
|
path.resolve(require.resolve('react-native-windows/package.json'), '..'),
|
|
9
7
|
);
|
|
@@ -25,7 +23,7 @@ const config = {
|
|
|
25
23
|
watchFolders: [rnwPath, rnwRootNodeModules, rnwPackages],
|
|
26
24
|
// devMode]{{/devMode}}
|
|
27
25
|
resolver: {
|
|
28
|
-
blockList:
|
|
26
|
+
blockList: [
|
|
29
27
|
// This stops "npx @react-native-community/cli run-windows" from causing the metro server to crash if its already running
|
|
30
28
|
new RegExp(
|
|
31
29
|
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
|
|
@@ -34,7 +32,7 @@ const config = {
|
|
|
34
32
|
new RegExp(`${rnwPath}/build/.*`),
|
|
35
33
|
new RegExp(`${rnwPath}/target/.*`),
|
|
36
34
|
/.*\.ProjectImports\.zip/,
|
|
37
|
-
]
|
|
35
|
+
],
|
|
38
36
|
//{{#devMode}} [devMode
|
|
39
37
|
extraNodeModules: {
|
|
40
38
|
'react-native-windows': rnwPath,
|