Skip to content

Commit 5fe8721

Browse files
authored
[Fabric] Add support for custom native component to have c++ state and custom measure (#12418)
* [Fabric] Add support for custom native component to have c++ state and custom measure * format * Change files * fix * fix * fix * More c++ state methods * format * code review feedback
1 parent 128ef19 commit 5fe8721

15 files changed

+596
-30
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Add support for custom native component to have c++ state and custom measure",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#include "AbiState.h"
5+
6+
namespace Microsoft::ReactNative {
7+
8+
AbiComponentState::AbiComponentState(facebook::react::State::Shared const &state) {
9+
m_state = std::static_pointer_cast<facebook::react::ConcreteState<AbiStateData> const>(state);
10+
}
11+
12+
winrt::Windows::Foundation::IInspectable AbiComponentState::Data() noexcept {
13+
return m_state->getData().userdata;
14+
}
15+
16+
void AbiComponentState::UpdateState(const winrt::Windows::Foundation::IInspectable &data) noexcept {
17+
AbiStateData state;
18+
state.userdata = data;
19+
m_state->updateState(std::move(state));
20+
}
21+
22+
// Verify winrt::Microsoft::ReactNative::EventPriority and facebook::react::EventPriority are in sync
23+
static_assert(
24+
winrt::Microsoft::ReactNative::EventPriority::SynchronousUnbatched ==
25+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::SynchronousUnbatched));
26+
static_assert(
27+
winrt::Microsoft::ReactNative::EventPriority::SynchronousBatched ==
28+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::SynchronousBatched));
29+
static_assert(
30+
winrt::Microsoft::ReactNative::EventPriority::AsynchronousUnbatched ==
31+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::AsynchronousUnbatched));
32+
static_assert(
33+
winrt::Microsoft::ReactNative::EventPriority::AsynchronousBatched ==
34+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::AsynchronousBatched));
35+
static_assert(
36+
winrt::Microsoft::ReactNative::EventPriority::Sync ==
37+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::Sync));
38+
static_assert(
39+
winrt::Microsoft::ReactNative::EventPriority::Work ==
40+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::Work));
41+
static_assert(
42+
winrt::Microsoft::ReactNative::EventPriority::Interactive ==
43+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::Interactive));
44+
static_assert(
45+
winrt::Microsoft::ReactNative::EventPriority::Deferred ==
46+
static_cast<winrt::Microsoft::ReactNative::EventPriority>(facebook::react::EventPriority::Deferred));
47+
48+
void AbiComponentState::UpdateStateWithPriority(
49+
const winrt::Windows::Foundation::IInspectable &data,
50+
winrt::Microsoft::ReactNative::EventPriority priority) noexcept {
51+
AbiStateData state;
52+
state.userdata = data;
53+
m_state->updateState(std::move(state), static_cast<facebook::react::EventPriority>(priority));
54+
}
55+
56+
void AbiComponentState::UpdateStateWithMutation(
57+
const winrt::Microsoft::ReactNative::StateUpdateMutation &mutation) noexcept {
58+
UpdateStateWithMutationAndPriority(mutation, winrt::Microsoft::ReactNative::EventPriority::AsynchronousUnbatched);
59+
}
60+
61+
void AbiComponentState::UpdateStateWithMutationAndPriority(
62+
const winrt::Microsoft::ReactNative::StateUpdateMutation &mutation,
63+
winrt::Microsoft::ReactNative::EventPriority priority) noexcept {
64+
m_state->updateState(
65+
[mutation](const AbiStateData &oldData) {
66+
return std::make_shared<AbiStateData const>(mutation(oldData.userdata));
67+
},
68+
static_cast<facebook::react::EventPriority>(priority));
69+
}
70+
71+
} // namespace Microsoft::ReactNative
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#pragma once
5+
6+
#include "ViewProps.g.h"
7+
8+
#include <react/renderer/core/ConcreteState.h>
9+
#include <react/renderer/core/State.h>
10+
#include "winrt/Microsoft.ReactNative.h"
11+
12+
namespace Microsoft::ReactNative {
13+
14+
class AbiStateData final {
15+
public:
16+
AbiStateData() = default;
17+
AbiStateData(winrt::Windows::Foundation::IInspectable data) : userdata(data) {}
18+
winrt::Windows::Foundation::IInspectable userdata;
19+
};
20+
21+
struct AbiComponentState : winrt::implements<AbiComponentState, winrt::Microsoft::ReactNative::IComponentState> {
22+
AbiComponentState(facebook::react::State::Shared const &state);
23+
24+
winrt::Windows::Foundation::IInspectable Data() noexcept;
25+
void UpdateState(const winrt::Windows::Foundation::IInspectable &data) noexcept;
26+
void UpdateStateWithPriority(
27+
const winrt::Windows::Foundation::IInspectable &data,
28+
winrt::Microsoft::ReactNative::EventPriority priority) noexcept;
29+
void UpdateStateWithMutation(const winrt::Microsoft::ReactNative::StateUpdateMutation &mutation) noexcept;
30+
void UpdateStateWithMutationAndPriority(
31+
const winrt::Microsoft::ReactNative::StateUpdateMutation &mutation,
32+
winrt::Microsoft::ReactNative::EventPriority priority) noexcept;
33+
34+
private:
35+
std::shared_ptr<facebook::react::ConcreteState<AbiStateData> const> m_state;
36+
};
37+
38+
} // namespace Microsoft::ReactNative

vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212

1313
namespace Microsoft::ReactNative {
1414

15-
extern const char AbiViewComponentName[] = "AbiView";
16-
1715
AbiViewComponentDescriptor::AbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const &parameters)
1816
: ComponentDescriptor(parameters) {
1917
auto flavor = std::static_pointer_cast<std::string const>(this->flavor_);
@@ -34,16 +32,25 @@ facebook::react::ComponentName AbiViewComponentDescriptor::getComponentName() co
3432
}
3533

3634
facebook::react::ShadowNodeTraits AbiViewComponentDescriptor::getTraits() const {
37-
return ShadowNodeT::BaseTraits();
35+
auto traits = ShadowNodeT::BaseTraits();
36+
if (winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
37+
->MeasureContentHandler()) {
38+
traits.set(facebook::react::ShadowNodeTraits::LeafYogaNode);
39+
traits.set(facebook::react::ShadowNodeTraits::MeasurableYogaNode);
40+
}
41+
return traits;
3842
}
3943

4044
facebook::react::ShadowNode::Shared AbiViewComponentDescriptor::createShadowNode(
4145
const facebook::react::ShadowNodeFragment &fragment,
4246
facebook::react::ShadowNodeFamily::Shared const &family) const {
4347
auto shadowNode = std::make_shared<ShadowNodeT>(fragment, family, getTraits());
4448

45-
adopt(*shadowNode);
49+
shadowNode->Proxy(winrt::make<winrt::Microsoft::ReactNative::implementation::YogaLayoutableShadowNode>(shadowNode));
50+
winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
51+
->CreateShadowNode(shadowNode->Proxy());
4652

53+
adopt(*shadowNode);
4754
return shadowNode;
4855
}
4956

@@ -52,6 +59,10 @@ facebook::react::ShadowNode::Unshared AbiViewComponentDescriptor::cloneShadowNod
5259
const facebook::react::ShadowNodeFragment &fragment) const {
5360
auto shadowNode = std::make_shared<ShadowNodeT>(sourceShadowNode, fragment);
5461

62+
shadowNode->Proxy(winrt::make<winrt::Microsoft::ReactNative::implementation::YogaLayoutableShadowNode>(shadowNode));
63+
winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
64+
->CloneShadowNode(shadowNode->Proxy(), static_cast<const ShadowNodeT &>(sourceShadowNode).Proxy());
65+
5566
adopt(*shadowNode);
5667
return shadowNode;
5768
}
@@ -101,6 +112,15 @@ facebook::react::Props::Shared AbiViewComponentDescriptor::cloneProps(
101112
return shadowNodeProps;
102113
};
103114

115+
AbiViewComponentDescriptor::ConcreteStateData AbiViewComponentDescriptor::initialStateData(
116+
const facebook::react::Props::Shared &props,
117+
const facebook::react::ShadowNodeFamily::Shared & /*family*/,
118+
const facebook::react::ComponentDescriptor &componentDescriptor) noexcept {
119+
return {winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(
120+
static_cast<const AbiViewComponentDescriptor &>(componentDescriptor).m_builder)
121+
->InitialStateData(std::static_pointer_cast<AbiViewProps const>(props)->UserProps())};
122+
}
123+
104124
facebook::react::State::Shared AbiViewComponentDescriptor::createInitialState(
105125
facebook::react::Props::Shared const &props,
106126
facebook::react::ShadowNodeFamily::Shared const &family) const {
@@ -110,7 +130,8 @@ facebook::react::State::Shared AbiViewComponentDescriptor::createInitialState(
110130
}
111131

112132
return std::make_shared<ConcreteState>(
113-
std::make_shared<ConcreteStateData const>(ConcreteShadowNode::initialStateData(props, family, *this)), family);
133+
std::make_shared<ConcreteStateData const>(AbiViewComponentDescriptor::initialStateData(props, family, *this)),
134+
family);
114135
}
115136

116137
facebook::react::State::Shared AbiViewComponentDescriptor::createState(
@@ -154,8 +175,17 @@ facebook::react::SharedEventEmitter AbiViewComponentDescriptor::createEventEmitt
154175
* `ModalHostViewComponentDescriptor`.
155176
*/
156177
void AbiViewComponentDescriptor::adopt(facebook::react::ShadowNode &shadowNode) const {
157-
// Default implementation does nothing.
158178
react_native_assert(shadowNode.getComponentHandle() == getComponentHandle());
179+
180+
auto &abiViewShadowNode = static_cast<AbiViewShadowNode &>(shadowNode);
181+
182+
abiViewShadowNode.Builder(m_builder);
183+
184+
if (winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
185+
->MeasureContentHandler()) {
186+
abiViewShadowNode.dirtyLayout();
187+
abiViewShadowNode.enableMeasurement();
188+
}
159189
}
160190

161191
} // namespace Microsoft::ReactNative

vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,11 @@
66
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
77
#include <react/renderer/core/ComponentDescriptor.h>
88
#include "AbiViewProps.h"
9+
#include "AbiViewShadowNode.h"
910
#include "winrt/Microsoft.ReactNative.h"
1011

1112
namespace Microsoft::ReactNative {
1213

13-
extern const char AbiViewComponentName[];
14-
15-
class AbiViewState {
16-
public:
17-
AbiViewState() = default;
18-
};
19-
20-
// Need to replace ConcreteShadowNode template usage with a specific impl that can have a different name per type
21-
using AbiViewShadowNode = facebook::react::
22-
ConcreteViewShadowNode<AbiViewComponentName, AbiViewProps, facebook::react::ViewEventEmitter, AbiViewState>;
23-
2414
class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor {
2515
using ShadowNodeT = AbiViewShadowNode;
2616
using SharedShadowNodeT = std::shared_ptr<const ShadowNodeT>;
@@ -82,6 +72,11 @@ class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor {
8272
virtual void adopt(facebook::react::ShadowNode &shadowNode) const;
8373

8474
private:
75+
static ConcreteStateData initialStateData(
76+
const facebook::react::Props::Shared & /*props*/,
77+
const facebook::react::ShadowNodeFamily::Shared & /*family*/,
78+
const facebook::react::ComponentDescriptor & /*componentDescriptor*/) noexcept;
79+
8580
winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder;
8681
};
8782

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#include "AbiViewShadowNode.h"
5+
6+
#include <Fabric/Composition/ReactCompositionViewComponentBuilder.h>
7+
#include <react/debug/react_native_assert.h>
8+
#include <react/renderer/core/LayoutConstraints.h>
9+
#include <react/renderer/core/LayoutContext.h>
10+
#include <react/renderer/core/TraitCast.h>
11+
#include <react/renderer/core/conversions.h>
12+
13+
#include <utility>
14+
15+
namespace winrt::Microsoft::ReactNative::implementation {
16+
17+
ShadowNode::ShadowNode(facebook::react::ShadowNode::Shared shadowNode) noexcept : m_shadowNode(shadowNode) {}
18+
19+
void ShadowNode::EnsureUnsealed() noexcept {
20+
m_shadowNode->ensureUnsealed();
21+
}
22+
23+
winrt::IInspectable ShadowNode::Tag() const noexcept {
24+
return m_tag;
25+
}
26+
27+
void ShadowNode::Tag(winrt::IInspectable tag) noexcept {
28+
m_tag = tag;
29+
}
30+
31+
winrt::IInspectable ShadowNode::StateData() const noexcept {
32+
auto state = m_shadowNode->getState();
33+
react_native_assert(state && "State must not be `nullptr`.");
34+
auto abiStateData =
35+
static_cast<const facebook::react::ConcreteState<::Microsoft::ReactNative::AbiStateData> *>(state.get())
36+
->getData();
37+
return abiStateData.userdata;
38+
}
39+
40+
void ShadowNode::StateData(winrt::IInspectable tag) noexcept {
41+
m_shadowNode->ensureUnsealed();
42+
43+
auto &state = const_cast<facebook::react::State::Shared &>(m_shadowNode->getState());
44+
state = std::make_shared<const facebook::react::ConcreteState<::Microsoft::ReactNative::AbiStateData>>(
45+
std::make_shared<const ::Microsoft::ReactNative::AbiStateData>(tag), *state);
46+
}
47+
48+
YogaLayoutableShadowNode::YogaLayoutableShadowNode(facebook::react::ShadowNode::Shared shadowNode) noexcept
49+
: base_type(shadowNode) {}
50+
51+
void YogaLayoutableShadowNode::Layout(winrt::Microsoft::ReactNative::LayoutContext layoutContext) noexcept {
52+
std::const_pointer_cast<facebook::react::YogaLayoutableShadowNode>(
53+
facebook::react::traitCast<facebook::react::YogaLayoutableShadowNode>(m_shadowNode))
54+
->facebook::react::YogaLayoutableShadowNode::layout(
55+
winrt::get_self<winrt::Microsoft::ReactNative::implementation::LayoutContext>(layoutContext)
56+
->m_layoutContext);
57+
}
58+
59+
} // namespace winrt::Microsoft::ReactNative::implementation
60+
61+
namespace Microsoft::ReactNative {
62+
63+
extern const char AbiViewComponentName[] = "AbiView";
64+
65+
facebook::react::Size AbiViewShadowNode::measureContent(
66+
const facebook::react::LayoutContext &layoutContext,
67+
const facebook::react::LayoutConstraints &layoutConstraints) const {
68+
if (auto measureContent =
69+
winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
70+
->MeasureContentHandler()) {
71+
winrt::Microsoft::ReactNative::LayoutConstraints winrtLayoutContraints;
72+
static_assert(
73+
winrt::Microsoft::ReactNative::LayoutDirection::Undefined ==
74+
static_cast<winrt::Microsoft::ReactNative::LayoutDirection>(facebook::react::LayoutDirection::Undefined));
75+
static_assert(
76+
winrt::Microsoft::ReactNative::LayoutDirection::LeftToRight ==
77+
static_cast<winrt::Microsoft::ReactNative::LayoutDirection>(facebook::react::LayoutDirection::LeftToRight));
78+
static_assert(
79+
winrt::Microsoft::ReactNative::LayoutDirection::RightToLeft ==
80+
static_cast<winrt::Microsoft::ReactNative::LayoutDirection>(facebook::react::LayoutDirection::RightToLeft));
81+
winrtLayoutContraints.LayoutDirection =
82+
static_cast<winrt::Microsoft::ReactNative::LayoutDirection>(layoutConstraints.layoutDirection);
83+
winrtLayoutContraints.MaximumSize = {layoutConstraints.maximumSize.width, layoutConstraints.maximumSize.height};
84+
winrtLayoutContraints.MinimumSize = {layoutConstraints.minimumSize.width, layoutConstraints.minimumSize.height};
85+
86+
auto size = measureContent(
87+
m_proxy,
88+
winrt::make<winrt::Microsoft::ReactNative::implementation::LayoutContext>(layoutContext),
89+
winrtLayoutContraints);
90+
return {size.Width, size.Height};
91+
}
92+
93+
return ConcreteViewShadowNode::measureContent(layoutContext, layoutConstraints);
94+
}
95+
96+
void AbiViewShadowNode::layout(facebook::react::LayoutContext layoutContext) {
97+
if (auto layoutHandler =
98+
winrt::get_self<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder>(m_builder)
99+
->LayoutHandler()) {
100+
layoutHandler(m_proxy, winrt::make<winrt::Microsoft::ReactNative::implementation::LayoutContext>(layoutContext));
101+
} else {
102+
ConcreteViewShadowNode::layout(layoutContext);
103+
}
104+
}
105+
106+
void AbiViewShadowNode::Builder(winrt::Microsoft::ReactNative::IReactViewComponentBuilder builder) noexcept {
107+
m_builder = builder;
108+
}
109+
110+
winrt::Microsoft::ReactNative::IReactViewComponentBuilder AbiViewShadowNode::Builder() const noexcept {
111+
return m_builder;
112+
}
113+
114+
void AbiViewShadowNode::Proxy(winrt::Microsoft::ReactNative::ShadowNode proxy) noexcept {
115+
m_proxy = proxy;
116+
}
117+
118+
winrt::Microsoft::ReactNative::ShadowNode AbiViewShadowNode::Proxy() const noexcept {
119+
return m_proxy;
120+
}
121+
122+
} // namespace Microsoft::ReactNative

0 commit comments

Comments
 (0)