react-native-windows 0.0.0-canary.575 → 0.0.0-canary.577
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/Microsoft.ReactNative/INativeUIManager.h +1 -0
- package/Microsoft.ReactNative/LayoutService.cpp +51 -0
- package/Microsoft.ReactNative/LayoutService.h +32 -0
- package/Microsoft.ReactNative/LayoutService.idl +41 -0
- package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +12 -1
- package/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +2 -1
- package/Microsoft.ReactNative/Modules/NativeUIManager.cpp +27 -23
- package/Microsoft.ReactNative/Modules/NativeUIManager.h +4 -1
- package/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +5 -0
- package/Microsoft.ReactNative/Views/ScrollViewManager.cpp +1 -3
- package/Microsoft.ReactNative/Views/Text/TextPropertyChangedParentVisitor.cpp +1 -7
- package/Microsoft.ReactNative/Views/TextViewManager.cpp +46 -30
- package/Microsoft.ReactNative/Views/TextViewManager.h +2 -0
- package/PropertySheets/Generated/PackageVersion.g.props +1 -1
- package/Shared/Networking/WinRTHttpResource.cpp +75 -15
- package/package.json +1 -1
|
@@ -50,6 +50,7 @@ struct INativeUIManager {
|
|
|
50
50
|
virtual void UpdateView(ShadowNode &shadowNode, winrt::Microsoft::ReactNative::JSValueObject &props) = 0;
|
|
51
51
|
virtual void onBatchComplete() = 0;
|
|
52
52
|
virtual void ensureInBatch() = 0;
|
|
53
|
+
virtual bool isInBatch() = 0;
|
|
53
54
|
virtual void measure(
|
|
54
55
|
ShadowNode &shadowNode,
|
|
55
56
|
ShadowNode &shadowRoot,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
#include "pch.h"
|
|
5
|
+
#include "LayoutService.h"
|
|
6
|
+
#include "LayoutService.g.cpp"
|
|
7
|
+
#include <Modules/NativeUIManager.h>
|
|
8
|
+
#include <Modules/PaperUIManagerModule.h>
|
|
9
|
+
|
|
10
|
+
namespace winrt::Microsoft::ReactNative::implementation {
|
|
11
|
+
|
|
12
|
+
LayoutService::LayoutService(Mso::CntPtr<Mso::React::IReactContext> &&context) noexcept : m_context(context) {}
|
|
13
|
+
|
|
14
|
+
/*static*/ winrt::Microsoft::ReactNative::LayoutService LayoutService::FromContext(IReactContext context) {
|
|
15
|
+
return context.Properties()
|
|
16
|
+
.Get(LayoutService::LayoutServiceProperty().Handle())
|
|
17
|
+
.try_as<winrt::Microsoft::ReactNative::LayoutService>();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/*static*/ ReactPropertyId<LayoutService> LayoutService::LayoutServiceProperty() noexcept {
|
|
21
|
+
static ReactPropertyId<LayoutService> layoutServiceProperty{L"ReactNative.UIManager", L"LayoutService"};
|
|
22
|
+
return layoutServiceProperty;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void LayoutService::ApplyLayoutForAllNodes() noexcept {
|
|
26
|
+
if (auto uiManager = ::Microsoft::ReactNative::GetNativeUIManager(*m_context).lock()) {
|
|
27
|
+
uiManager->DoLayout();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
void LayoutService::ApplyLayout(int64_t reactTag, float width, float height) noexcept {
|
|
32
|
+
if (auto uiManager = ::Microsoft::ReactNative::GetNativeUIManager(*m_context).lock()) {
|
|
33
|
+
uiManager->ApplyLayout(reactTag, width, height);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
bool LayoutService::IsInBatch() noexcept {
|
|
38
|
+
if (auto uiManager = ::Microsoft::ReactNative::GetNativeUIManager(*m_context).lock()) {
|
|
39
|
+
return uiManager->isInBatch();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void LayoutService::MarkDirty(int64_t reactTag) noexcept {
|
|
46
|
+
if (auto uiManager = ::Microsoft::ReactNative::GetNativeUIManager(*m_context).lock()) {
|
|
47
|
+
uiManager->DirtyYogaNode(reactTag);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
} // namespace winrt::Microsoft::ReactNative::implementation
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include "LayoutService.g.h"
|
|
7
|
+
#include "INativeUIManager.h"
|
|
8
|
+
#include "ReactHost/React.h"
|
|
9
|
+
#include "ReactPropertyBag.h"
|
|
10
|
+
#include "winrt/Microsoft.ReactNative.h"
|
|
11
|
+
|
|
12
|
+
namespace winrt::Microsoft::ReactNative::implementation {
|
|
13
|
+
struct LayoutService : LayoutServiceT<LayoutService> {
|
|
14
|
+
public:
|
|
15
|
+
LayoutService(Mso::CntPtr<Mso::React::IReactContext> &&context) noexcept;
|
|
16
|
+
static winrt::Microsoft::ReactNative::LayoutService FromContext(IReactContext context);
|
|
17
|
+
static ReactPropertyId<LayoutService> LayoutServiceProperty() noexcept;
|
|
18
|
+
|
|
19
|
+
void ApplyLayoutForAllNodes() noexcept;
|
|
20
|
+
void ApplyLayout(int64_t reactTag, float width, float height) noexcept;
|
|
21
|
+
bool IsInBatch() noexcept;
|
|
22
|
+
void MarkDirty(int64_t reactTag) noexcept;
|
|
23
|
+
|
|
24
|
+
private:
|
|
25
|
+
Mso::CntPtr<Mso::React::IReactContext> m_context;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
} // namespace winrt::Microsoft::ReactNative::implementation
|
|
29
|
+
|
|
30
|
+
namespace winrt::Microsoft::ReactNative::factory_implementation {
|
|
31
|
+
struct LayoutService : LayoutServiceT<LayoutService, implementation::LayoutService> {};
|
|
32
|
+
} // namespace winrt::Microsoft::ReactNative::factory_implementation
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
import "IReactContext.idl";
|
|
5
|
+
|
|
6
|
+
#include "NamespaceRedirect.h"
|
|
7
|
+
#include "DocString.h"
|
|
8
|
+
|
|
9
|
+
namespace Microsoft.ReactNative
|
|
10
|
+
{
|
|
11
|
+
[default_interface]
|
|
12
|
+
[webhosthidden]
|
|
13
|
+
DOC_STRING("Provides access to Yoga layout functionality.")
|
|
14
|
+
runtimeclass LayoutService {
|
|
15
|
+
DOC_STRING("Use this method to get access to the @LayoutService associated with the @IReactContext.")
|
|
16
|
+
static LayoutService FromContext(IReactContext context);
|
|
17
|
+
|
|
18
|
+
DOC_STRING(
|
|
19
|
+
"Recursively applies layout to all root nodes. This method will trigger "
|
|
20
|
+
"a Yoga layout operation on roots attached to the React instance and "
|
|
21
|
+
"apply the layout results to all descendant nodes.")
|
|
22
|
+
void ApplyLayoutForAllNodes();
|
|
23
|
+
|
|
24
|
+
DOC_STRING(
|
|
25
|
+
"Recursively applies layout from the given React node with the supplied "
|
|
26
|
+
"size constraints. This method will trigger a Yoga layout operation on the "
|
|
27
|
+
"given node and its descendants and apply the layout results to these nodes.")
|
|
28
|
+
void ApplyLayout(Int64 reactTag, Single width, Single height);
|
|
29
|
+
|
|
30
|
+
DOC_STRING(
|
|
31
|
+
"Determines whether the UIManager is currently processing a batch of node updates."
|
|
32
|
+
"This is useful for optimizing layout and ensuring that applying layout to a particular "
|
|
33
|
+
"node will not cause tearing in the rendered UI.")
|
|
34
|
+
Boolean IsInBatch {
|
|
35
|
+
get;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
DOC_STRING("Mark a particular React node as dirty for Yoga layout.")
|
|
39
|
+
void MarkDirty(Int64 reactTag);
|
|
40
|
+
}
|
|
41
|
+
} // namespace ReactNative
|
|
@@ -380,6 +380,10 @@
|
|
|
380
380
|
<ClInclude Include="Views\VirtualTextViewManager.h" />
|
|
381
381
|
<ClInclude Include="Views\XamlFeatures.h" />
|
|
382
382
|
<ClInclude Include="XamlLoadState.h" />
|
|
383
|
+
<ClInclude Include="LayoutService.h">
|
|
384
|
+
<DependentUpon>LayoutService.idl</DependentUpon>
|
|
385
|
+
<SubType>Code</SubType>
|
|
386
|
+
</ClInclude>
|
|
383
387
|
<ClInclude Include="XamlUIService.h">
|
|
384
388
|
<DependentUpon>XamlUIService.idl</DependentUpon>
|
|
385
389
|
<SubType>Code</SubType>
|
|
@@ -598,6 +602,10 @@
|
|
|
598
602
|
<ClCompile Include="Views\XamlFeatures.cpp" />
|
|
599
603
|
<ClCompile Include="XamlLoadState.cpp" />
|
|
600
604
|
<ClCompile Include="XamlView.cpp" />
|
|
605
|
+
<ClCompile Include="LayoutService.cpp">
|
|
606
|
+
<DependentUpon>LayoutService.idl</DependentUpon>
|
|
607
|
+
<SubType>Code</SubType>
|
|
608
|
+
</ClCompile>
|
|
601
609
|
<ClCompile Include="XamlUIService.cpp">
|
|
602
610
|
<DependentUpon>XamlUIService.idl</DependentUpon>
|
|
603
611
|
<SubType>Code</SubType>
|
|
@@ -674,6 +682,9 @@
|
|
|
674
682
|
<Midl Include="Views\cppwinrt\Effects.idl" />
|
|
675
683
|
<Midl Include="Views\cppwinrt\DynamicAutomationPeer.idl" />
|
|
676
684
|
<Midl Include="Views\cppwinrt\ViewPanel.idl" />
|
|
685
|
+
<Midl Include="LayoutService.idl">
|
|
686
|
+
<SubType>Designer</SubType>
|
|
687
|
+
</Midl>
|
|
677
688
|
<Midl Include="XamlUIService.idl">
|
|
678
689
|
<SubType>Designer</SubType>
|
|
679
690
|
</Midl>
|
|
@@ -781,4 +792,4 @@
|
|
|
781
792
|
</ClCompile>
|
|
782
793
|
</ItemGroup>
|
|
783
794
|
</Target>
|
|
784
|
-
</Project>
|
|
795
|
+
</Project>
|
|
@@ -752,6 +752,7 @@
|
|
|
752
752
|
<Midl Include="RedBoxHandler.idl" />
|
|
753
753
|
<Midl Include="QuirkSettings.idl" />
|
|
754
754
|
<Midl Include="XamlHelper.idl" />
|
|
755
|
+
<Midl Include="LayoutService.idl" />
|
|
755
756
|
<Midl Include="XamlUIService.idl" />
|
|
756
757
|
<Midl Include="Views\cppwinrt\AccessibilityAction.idl">
|
|
757
758
|
<Filter>Views\cppwinrt</Filter>
|
|
@@ -827,4 +828,4 @@
|
|
|
827
828
|
<Page Include="DevMenuControl.xaml" />
|
|
828
829
|
<Page Include="CoreAppPage.xaml" />
|
|
829
830
|
</ItemGroup>
|
|
830
|
-
</Project>
|
|
831
|
+
</Project>
|
|
@@ -260,6 +260,10 @@ void NativeUIManager::ensureInBatch() {
|
|
|
260
260
|
m_inBatch = true;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
bool NativeUIManager::isInBatch() {
|
|
264
|
+
return m_inBatch;
|
|
265
|
+
}
|
|
266
|
+
|
|
263
267
|
static float NumberOrDefault(const winrt::Microsoft::ReactNative::JSValue &value, float defaultValue) {
|
|
264
268
|
float result = defaultValue;
|
|
265
269
|
|
|
@@ -884,28 +888,28 @@ void NativeUIManager::DoLayout() {
|
|
|
884
888
|
auto &rootTags = m_host->GetAllRootTags();
|
|
885
889
|
for (int64_t rootTag : rootTags) {
|
|
886
890
|
ShadowNodeBase &rootShadowNode = static_cast<ShadowNodeBase &>(m_host->GetShadowNodeForTag(rootTag));
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
{
|
|
894
|
-
SystraceSection s("NativeUIManager::DoLayout::YGNodeCalculateLayout");
|
|
895
|
-
// We must always run layout in LTR mode, which might seem unintuitive.
|
|
896
|
-
// We will flip the root of the tree into RTL by forcing the root XAML node's FlowDirection to RightToLeft
|
|
897
|
-
// which will inherit down the XAML tree, allowing all native controls to pick it up.
|
|
898
|
-
YGNodeCalculateLayout(rootNode, actualWidth, actualHeight, YGDirectionLTR);
|
|
899
|
-
}
|
|
900
|
-
} else {
|
|
901
|
-
assert(false);
|
|
902
|
-
return;
|
|
903
|
-
}
|
|
891
|
+
const auto rootElement = rootShadowNode.GetView().as<xaml::FrameworkElement>();
|
|
892
|
+
float actualWidth = static_cast<float>(rootElement.ActualWidth());
|
|
893
|
+
float actualHeight = static_cast<float>(rootElement.ActualHeight());
|
|
894
|
+
ApplyLayout(rootTag, actualWidth, actualHeight);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
904
897
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
898
|
+
void NativeUIManager::ApplyLayout(int64_t tag, float width, float height) {
|
|
899
|
+
if (YGNodeRef rootNode = GetYogaNode(tag)) {
|
|
900
|
+
SystraceSection s("NativeUIManager::DoLayout::YGNodeCalculateLayout");
|
|
901
|
+
// We must always run layout in LTR mode, which might seem unintuitive.
|
|
902
|
+
// We will flip the root of the tree into RTL by forcing the root XAML node's FlowDirection to RightToLeft
|
|
903
|
+
// which will inherit down the XAML tree, allowing all native controls to pick it up.
|
|
904
|
+
YGNodeCalculateLayout(rootNode, width, height, YGDirectionLTR);
|
|
905
|
+
} else {
|
|
906
|
+
assert(false);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
{
|
|
911
|
+
SystraceSection s("NativeUIManager::DoLayout::SetLayoutProps");
|
|
912
|
+
SetLayoutPropsRecursive(tag);
|
|
909
913
|
}
|
|
910
914
|
}
|
|
911
915
|
|
|
@@ -1132,9 +1136,9 @@ void NativeUIManager::focus(int64_t reactTag) {
|
|
|
1132
1136
|
// Note: It's a known issue that blur on flyout/popup would dismiss them.
|
|
1133
1137
|
void NativeUIManager::blur(int64_t reactTag) {
|
|
1134
1138
|
if (auto shadowNode = static_cast<ShadowNodeBase *>(m_host->FindShadowNodeForTag(reactTag))) {
|
|
1135
|
-
auto view = shadowNode->GetView();
|
|
1136
1139
|
// Only blur if current UI is focused to avoid problem described in PR #2687
|
|
1137
|
-
|
|
1140
|
+
const auto xamlRoot = tryGetXamlRoot(shadowNode->m_rootTag);
|
|
1141
|
+
if (shadowNode->GetView() == xaml::Input::FocusManager::GetFocusedElement(xamlRoot)) {
|
|
1138
1142
|
if (auto reactControl = GetParentXamlReactControl(reactTag).get()) {
|
|
1139
1143
|
reactControl.as<winrt::Microsoft::ReactNative::implementation::ReactRootView>()->blur(shadowNode->GetView());
|
|
1140
1144
|
} else {
|
|
@@ -56,6 +56,7 @@ class NativeUIManager final : public INativeUIManager {
|
|
|
56
56
|
void UpdateView(ShadowNode &shadowNode, winrt::Microsoft::ReactNative::JSValueObject &props) override;
|
|
57
57
|
void onBatchComplete() override;
|
|
58
58
|
void ensureInBatch() override;
|
|
59
|
+
bool isInBatch() override;
|
|
59
60
|
void measure(
|
|
60
61
|
ShadowNode &shadowNode,
|
|
61
62
|
ShadowNode &shadowRoot,
|
|
@@ -100,8 +101,10 @@ class NativeUIManager final : public INativeUIManager {
|
|
|
100
101
|
|
|
101
102
|
int64_t AddMeasuredRootView(facebook::react::IReactRootView *rootView);
|
|
102
103
|
|
|
103
|
-
private:
|
|
104
104
|
void DoLayout();
|
|
105
|
+
void ApplyLayout(int64_t tag, float width = YGUndefined, float height = YGUndefined);
|
|
106
|
+
|
|
107
|
+
private:
|
|
105
108
|
void SetLayoutPropsRecursive(int64_t tag);
|
|
106
109
|
YGNodeRef GetYogaNode(int64_t tag) const;
|
|
107
110
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <appModel.h>
|
|
13
13
|
#include <comUtil/qiCast.h>
|
|
14
14
|
#ifndef CORE_ABI
|
|
15
|
+
#include <LayoutService.h>
|
|
15
16
|
#include <XamlUIService.h>
|
|
16
17
|
#endif
|
|
17
18
|
#include "ReactErrorProvider.h"
|
|
@@ -762,6 +763,10 @@ void ReactInstanceWin::InitUIManager() noexcept {
|
|
|
762
763
|
m_reactContext->Properties().Set(
|
|
763
764
|
implementation::XamlUIService::XamlUIServiceProperty().Handle(),
|
|
764
765
|
winrt::make<implementation::XamlUIService>(m_reactContext));
|
|
766
|
+
|
|
767
|
+
m_reactContext->Properties().Set(
|
|
768
|
+
implementation::LayoutService::LayoutServiceProperty().Handle(),
|
|
769
|
+
winrt::make<implementation::LayoutService>(m_reactContext));
|
|
765
770
|
}
|
|
766
771
|
#endif
|
|
767
772
|
|
|
@@ -90,8 +90,7 @@ void ScrollViewShadowNode::dispatchCommand(
|
|
|
90
90
|
scrollViewer.ChangeView(x, y, nullptr, !animated /*disableAnimation*/);
|
|
91
91
|
} else if (commandId == ScrollViewCommands::ScrollToEnd) {
|
|
92
92
|
bool animated = commandArgs[0].AsBoolean();
|
|
93
|
-
|
|
94
|
-
if (horiz)
|
|
93
|
+
if (m_isHorizontal)
|
|
95
94
|
scrollViewer.ChangeView(scrollViewer.ScrollableWidth(), nullptr, nullptr, !animated /*disableAnimation*/);
|
|
96
95
|
else
|
|
97
96
|
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr, !animated /*disableAnimation*/);
|
|
@@ -490,7 +489,6 @@ XamlView ScrollViewManager::CreateViewCore(int64_t /*tag*/, const winrt::Microso
|
|
|
490
489
|
scrollViewer.VerticalSnapPointsAlignment(winrt::SnapPointsAlignment::Near);
|
|
491
490
|
scrollViewer.VerticalSnapPointsType(winrt::SnapPointsType::Mandatory);
|
|
492
491
|
scrollViewer.HorizontalSnapPointsType(winrt::SnapPointsType::Mandatory);
|
|
493
|
-
scrollViewer.HorizontalScrollMode(winrt::ScrollMode::Disabled);
|
|
494
492
|
|
|
495
493
|
const auto snapPointManager = SnapPointManagingContentControl::Create();
|
|
496
494
|
scrollViewer.Content(*snapPointManager);
|
|
@@ -40,13 +40,7 @@ void TextPropertyChangedParentVisitor::VisitText(ShadowNodeBase *node) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
if (!m_isNested && node->m_children.size() == 1) {
|
|
45
|
-
if (const auto childNode = GetShadowNode(node->m_children[0])) {
|
|
46
|
-
const auto run = static_cast<ShadowNodeBase *>(childNode)->GetView().as<winrt::Run>();
|
|
47
|
-
element.Text(run.Text());
|
|
48
|
-
}
|
|
49
|
-
}
|
|
43
|
+
TextViewManager::UpdateOptimizedText(node);
|
|
50
44
|
}
|
|
51
45
|
|
|
52
46
|
// Refresh text highlighters
|
|
@@ -39,8 +39,7 @@ class TextShadowNode final : public ShadowNodeBase {
|
|
|
39
39
|
friend TextViewManager;
|
|
40
40
|
|
|
41
41
|
private:
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
bool m_isTextOptimized{true};
|
|
44
43
|
bool m_hasDescendantTextHighlighter{false};
|
|
45
44
|
bool m_hasDescendantPressable{false};
|
|
46
45
|
std::optional<winrt::Windows::UI::Color> m_backgroundColor{};
|
|
@@ -49,9 +48,6 @@ class TextShadowNode final : public ShadowNodeBase {
|
|
|
49
48
|
winrt::event_revoker<xaml::Controls::ITextBlock> m_selectionChangedRevoker;
|
|
50
49
|
|
|
51
50
|
public:
|
|
52
|
-
TextShadowNode() {
|
|
53
|
-
m_firstChildNode = nullptr;
|
|
54
|
-
};
|
|
55
51
|
bool ImplementsPadding() override {
|
|
56
52
|
return true;
|
|
57
53
|
}
|
|
@@ -66,25 +62,23 @@ class TextShadowNode final : public ShadowNodeBase {
|
|
|
66
62
|
m_hasDescendantPressable |= textChildNode.hasDescendantPressable;
|
|
67
63
|
}
|
|
68
64
|
|
|
69
|
-
auto
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
const auto wasOptimized = m_isTextOptimized;
|
|
66
|
+
m_isTextOptimized = IsRawTextShadowNode(&childNode) && m_isTextOptimized;
|
|
67
|
+
if (m_isTextOptimized) {
|
|
68
|
+
// Re-build optimized text from children
|
|
69
|
+
UpdateOptimizedText();
|
|
70
|
+
} else if (wasOptimized) {
|
|
71
|
+
// Remove optimized text and re-construct as Inline tree
|
|
72
|
+
UpdateOptimizedText();
|
|
73
|
+
if (const auto uiManager = GetNativeUIManager(GetViewManager()->GetReactContext()).lock()) {
|
|
74
|
+
for (size_t i = 0; i < m_children.size(); ++i) {
|
|
75
|
+
if (const auto childNode =
|
|
76
|
+
static_cast<ShadowNodeBase *>(uiManager->getHost()->FindShadowNodeForTag(m_children[i]))) {
|
|
77
|
+
Super::AddView(*childNode, i);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
78
80
|
}
|
|
79
|
-
} else
|
|
80
|
-
assert(m_children.size() == 2);
|
|
81
|
-
auto textBlock = this->GetView().as<xaml::Controls::TextBlock>();
|
|
82
|
-
textBlock.ClearValue(xaml::Controls::TextBlock::TextProperty());
|
|
83
|
-
Super::AddView(*m_firstChildNode, 0);
|
|
84
|
-
m_firstChildNode = nullptr;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (addInline) {
|
|
81
|
+
} else {
|
|
88
82
|
Super::AddView(child, index);
|
|
89
83
|
}
|
|
90
84
|
|
|
@@ -92,10 +86,9 @@ class TextShadowNode final : public ShadowNodeBase {
|
|
|
92
86
|
}
|
|
93
87
|
|
|
94
88
|
void removeAllChildren() override {
|
|
95
|
-
if (
|
|
89
|
+
if (m_isTextOptimized) {
|
|
96
90
|
auto textBlock = this->GetView().as<xaml::Controls::TextBlock>();
|
|
97
91
|
textBlock.ClearValue(xaml::Controls::TextBlock::TextProperty());
|
|
98
|
-
m_firstChildNode = nullptr;
|
|
99
92
|
} else {
|
|
100
93
|
Super::removeAllChildren();
|
|
101
94
|
}
|
|
@@ -103,17 +96,33 @@ class TextShadowNode final : public ShadowNodeBase {
|
|
|
103
96
|
}
|
|
104
97
|
|
|
105
98
|
void RemoveChildAt(int64_t indexToRemove) override {
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
auto textBlock = this->GetView().as<xaml::Controls::TextBlock>();
|
|
109
|
-
textBlock.ClearValue(xaml::Controls::TextBlock::TextProperty());
|
|
110
|
-
m_firstChildNode = nullptr;
|
|
99
|
+
if (m_isTextOptimized) {
|
|
100
|
+
UpdateOptimizedText();
|
|
111
101
|
} else {
|
|
112
102
|
Super::RemoveChildAt(indexToRemove);
|
|
113
103
|
}
|
|
114
104
|
RecalculateTextHighlighters();
|
|
115
105
|
}
|
|
116
106
|
|
|
107
|
+
void UpdateOptimizedText() {
|
|
108
|
+
if (m_children.size() > 0 && m_isTextOptimized) {
|
|
109
|
+
if (const auto uiManager = GetNativeUIManager(GetViewManager()->GetReactContext()).lock()) {
|
|
110
|
+
winrt::hstring text = L"";
|
|
111
|
+
for (const auto childTag : m_children) {
|
|
112
|
+
if (const auto childNode =
|
|
113
|
+
static_cast<ShadowNodeBase *>(uiManager->getHost()->FindShadowNodeForTag(childTag))) {
|
|
114
|
+
text = text + childNode->GetView().as<winrt::Run>().Text();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
auto textBlock = this->GetView().as<xaml::Controls::TextBlock>();
|
|
118
|
+
textBlock.Text(text);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
auto textBlock = this->GetView().as<xaml::Controls::TextBlock>();
|
|
122
|
+
textBlock.ClearValue(xaml::Controls::TextBlock::TextProperty());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
117
126
|
void RecalculateTextHighlighters() {
|
|
118
127
|
const auto textBlock = this->GetView().as<xaml::Controls::TextBlock>();
|
|
119
128
|
textBlock.TextHighlighters().Clear();
|
|
@@ -389,6 +398,13 @@ void TextViewManager::OnPointerEvent(
|
|
|
389
398
|
}
|
|
390
399
|
}
|
|
391
400
|
|
|
401
|
+
/*static*/ void TextViewManager::UpdateOptimizedText(ShadowNodeBase *node) {
|
|
402
|
+
if (IsTextShadowNode(node)) {
|
|
403
|
+
const auto textNode = static_cast<TextShadowNode *>(node);
|
|
404
|
+
textNode->UpdateOptimizedText();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
392
408
|
/*static*/ void TextViewManager::SetDescendantPressable(ShadowNodeBase *node) {
|
|
393
409
|
if (IsTextShadowNode(node)) {
|
|
394
410
|
const auto textNode = static_cast<TextShadowNode *>(node);
|
|
@@ -29,6 +29,8 @@ class TextViewManager : public FrameworkElementViewManager {
|
|
|
29
29
|
|
|
30
30
|
static void UpdateTextHighlighters(ShadowNodeBase *node, bool highlightAdded);
|
|
31
31
|
|
|
32
|
+
static void UpdateOptimizedText(ShadowNodeBase *node);
|
|
33
|
+
|
|
32
34
|
static void SetDescendantPressable(ShadowNodeBase *node);
|
|
33
35
|
|
|
34
36
|
static TextTransform GetTextTransformValue(ShadowNodeBase *node);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
-->
|
|
11
11
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
12
12
|
<PropertyGroup>
|
|
13
|
-
<ReactNativeWindowsVersion>0.0.0-canary.
|
|
13
|
+
<ReactNativeWindowsVersion>0.0.0-canary.577</ReactNativeWindowsVersion>
|
|
14
14
|
<ReactNativeWindowsMajor>0</ReactNativeWindowsMajor>
|
|
15
15
|
<ReactNativeWindowsMinor>0</ReactNativeWindowsMinor>
|
|
16
16
|
<ReactNativeWindowsPatch>0</ReactNativeWindowsPatch>
|
|
@@ -53,6 +53,34 @@ using winrt::Windows::Web::Http::Headers::HttpMediaTypeHeaderValue;
|
|
|
53
53
|
|
|
54
54
|
namespace Microsoft::React::Networking {
|
|
55
55
|
|
|
56
|
+
// May throw winrt::hresult_error
|
|
57
|
+
void AttachMultipartHeaders(IHttpContent content, const dynamic &headers) {
|
|
58
|
+
HttpMediaTypeHeaderValue contentType{nullptr};
|
|
59
|
+
|
|
60
|
+
// Headers are generally case-insensitive
|
|
61
|
+
// https://www.ietf.org/rfc/rfc2616.txt section 4.2
|
|
62
|
+
// TODO: Consolidate with PerformRequest's header parsing.
|
|
63
|
+
for (auto &header : headers.items()) {
|
|
64
|
+
auto &name = header.first.getString();
|
|
65
|
+
auto &value = header.second.getString();
|
|
66
|
+
|
|
67
|
+
if (boost::iequals(name.c_str(), "Content-Type")) {
|
|
68
|
+
contentType = HttpMediaTypeHeaderValue::Parse(to_hstring(value));
|
|
69
|
+
} else if (boost::iequals(name.c_str(), "Authorization")) {
|
|
70
|
+
bool success = content.Headers().TryAppendWithoutValidation(to_hstring(name), to_hstring(value));
|
|
71
|
+
if (!success) {
|
|
72
|
+
throw hresult_error{E_INVALIDARG, L"Failed to append Authorization"};
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
content.Headers().Append(to_hstring(name), to_hstring(value));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (contentType) {
|
|
80
|
+
content.Headers().ContentType(contentType);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
56
84
|
#pragma region WinRTHttpResource
|
|
57
85
|
|
|
58
86
|
WinRTHttpResource::WinRTHttpResource(IHttpClient &&client) noexcept : m_client{std::move(client)} {}
|
|
@@ -81,20 +109,23 @@ IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
|
|
|
81
109
|
// Headers are generally case-insensitive
|
|
82
110
|
// https://www.ietf.org/rfc/rfc2616.txt section 4.2
|
|
83
111
|
for (auto &header : reqArgs->Headers) {
|
|
84
|
-
|
|
85
|
-
|
|
112
|
+
auto &name = header.first;
|
|
113
|
+
auto &value = header.second;
|
|
114
|
+
|
|
115
|
+
if (boost::iequals(name.c_str(), "Content-Type")) {
|
|
116
|
+
bool success = HttpMediaTypeHeaderValue::TryParse(to_hstring(value), contentType);
|
|
86
117
|
if (!success) {
|
|
87
118
|
if (self->m_onError) {
|
|
88
119
|
self->m_onError(reqArgs->RequestId, "Failed to parse Content-Type", false);
|
|
89
120
|
}
|
|
90
121
|
co_return nullptr;
|
|
91
122
|
}
|
|
92
|
-
} else if (boost::iequals(
|
|
93
|
-
contentEncoding =
|
|
94
|
-
} else if (boost::iequals(
|
|
95
|
-
contentLength =
|
|
96
|
-
} else if (boost::iequals(
|
|
97
|
-
bool success = request.Headers().TryAppendWithoutValidation(to_hstring(
|
|
123
|
+
} else if (boost::iequals(name.c_str(), "Content-Encoding")) {
|
|
124
|
+
contentEncoding = value;
|
|
125
|
+
} else if (boost::iequals(name.c_str(), "Content-Length")) {
|
|
126
|
+
contentLength = value;
|
|
127
|
+
} else if (boost::iequals(name.c_str(), "Authorization")) {
|
|
128
|
+
bool success = request.Headers().TryAppendWithoutValidation(to_hstring(name), to_hstring(value));
|
|
98
129
|
if (!success) {
|
|
99
130
|
if (self->m_onError) {
|
|
100
131
|
self->m_onError(reqArgs->RequestId, "Failed to append Authorization", false);
|
|
@@ -103,7 +134,7 @@ IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
|
|
|
103
134
|
}
|
|
104
135
|
} else {
|
|
105
136
|
try {
|
|
106
|
-
request.Headers().Append(to_hstring(
|
|
137
|
+
request.Headers().Append(to_hstring(name), to_hstring(value));
|
|
107
138
|
} catch (hresult_error const &e) {
|
|
108
139
|
if (self->m_onError) {
|
|
109
140
|
self->m_onError(reqArgs->RequestId, Utilities::HResultToString(e), false);
|
|
@@ -146,9 +177,31 @@ IAsyncOperation<HttpRequestMessage> WinRTHttpResource::CreateRequest(
|
|
|
146
177
|
auto file = co_await StorageFile::GetFileFromApplicationUriAsync(Uri{to_hstring(data["uri"].asString())});
|
|
147
178
|
auto stream = co_await file.OpenReadAsync();
|
|
148
179
|
content = HttpStreamContent{std::move(stream)};
|
|
149
|
-
} else if (!data["
|
|
150
|
-
|
|
151
|
-
|
|
180
|
+
} else if (!data["formData"].empty()) {
|
|
181
|
+
winrt::Windows::Web::Http::HttpMultipartFormDataContent multiPartContent;
|
|
182
|
+
auto formData = data["formData"];
|
|
183
|
+
|
|
184
|
+
// #6046 - Overwriting WinRT's HttpMultipartFormDataContent implicit Content-Type clears the generated boundary
|
|
185
|
+
contentType = nullptr;
|
|
186
|
+
|
|
187
|
+
for (auto &formDataPart : formData) {
|
|
188
|
+
IHttpContent formContent{nullptr};
|
|
189
|
+
if (!formDataPart["string"].isNull()) {
|
|
190
|
+
formContent = HttpStringContent{to_hstring(formDataPart["string"].asString())};
|
|
191
|
+
} else if (!formDataPart["uri"].empty()) {
|
|
192
|
+
auto filePath = to_hstring(formDataPart["uri"].asString());
|
|
193
|
+
auto file = co_await StorageFile::GetFileFromPathAsync(filePath);
|
|
194
|
+
auto stream = co_await file.OpenReadAsync();
|
|
195
|
+
formContent = HttpStreamContent{stream};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (formContent) {
|
|
199
|
+
AttachMultipartHeaders(formContent, formDataPart["headers"]);
|
|
200
|
+
multiPartContent.Add(formContent, to_hstring(formDataPart["fieldName"].asString()));
|
|
201
|
+
}
|
|
202
|
+
} // foreach form data part
|
|
203
|
+
|
|
204
|
+
content = multiPartContent;
|
|
152
205
|
}
|
|
153
206
|
}
|
|
154
207
|
|
|
@@ -316,11 +369,18 @@ WinRTHttpResource::PerformSendRequest(HttpMethod &&method, Uri &&rtUri, IInspect
|
|
|
316
369
|
auto props = winrt::multi_threaded_map<winrt::hstring, IInspectable>();
|
|
317
370
|
props.Insert(L"RequestArgs", coArgs);
|
|
318
371
|
|
|
319
|
-
auto
|
|
320
|
-
|
|
321
|
-
|
|
372
|
+
auto coRequestOp = CreateRequest(std::move(coMethod), std::move(coUri), props);
|
|
373
|
+
co_await lessthrow_await_adapter<IAsyncOperation<HttpRequestMessage>>{coRequestOp};
|
|
374
|
+
auto coRequestOpHR = coRequestOp.ErrorCode();
|
|
375
|
+
if (coRequestOpHR < 0) {
|
|
376
|
+
if (self->m_onError) {
|
|
377
|
+
self->m_onError(reqArgs->RequestId, Utilities::HResultToString(std::move(coRequestOpHR)), false);
|
|
378
|
+
}
|
|
379
|
+
co_return self->UntrackResponse(reqArgs->RequestId);
|
|
322
380
|
}
|
|
323
381
|
|
|
382
|
+
auto coRequest = coRequestOp.GetResults();
|
|
383
|
+
|
|
324
384
|
// If URI handler is available, it takes over request processing.
|
|
325
385
|
if (auto uriHandler = self->m_uriHandler.lock()) {
|
|
326
386
|
auto uri = winrt::to_string(coRequest.RequestUri().ToString());
|