Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook up fabric view component codegen #7759

Merged
16 commits merged into from
May 12, 2021
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Hook up view component codegen",
"packageName": "@react-native-windows/codegen",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Dont format files in codegen",
"packageName": "@rnw-scripts/format-files",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Hook up view component codegen",
"packageName": "react-native-windows",
"email": "30809111+acoates-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
62 changes: 56 additions & 6 deletions packages/@react-native-windows/codegen/src/Cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const argv = yargs.options({
describe: 'C++/C# Namespace to put generated native modules in',
default: 'MyNamespace',
},
libraryName: {
type: 'string',
describe: 'Used for part of the path generated within the codegen dir',
},
}).argv;

import {SchemaType} from 'react-native-tscodegen';
Expand Down Expand Up @@ -141,7 +145,14 @@ function generate(
): boolean {
schemaValidator.validate(schema);

const generatedFiles = [];
const componentOutputdir = path.join(
outputDirectory,
'react/components',
libraryName,
);

const generatedModuleFiles = [];
const generatedComponentFiles = [];
/*
for (const name of generators) {
for (const generator of GENERATORS[name]) {
Expand All @@ -151,31 +162,70 @@ function generate(
*/

const generateNM2 = createNM2Generator({namespace: argv.namespace});
const generatorPropsH = require('react-native-tscodegen/lib/rncodegen/src/generators/components/GeneratePropsH')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI that we're still on an older version of tscodegen/codegen. Zihan published a new version, with some possible changes, but I hadn't pulled it into RNW yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. I keep wondering if we are going to need to pull tscodegen into rnw repo. Right now it seems to be ok with the older version. but it does worry me.

Do we have RN-core publishing the codegen pacakge in daily builds?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. Original plan was for me to hookup nightlies for monorepo packages, but code stopped being pulled into separate packages so the urgency dropped off. There is a ready-made tscodegen that is newer than what we have rn though.

.generate;
const generatorPropsCPP = require('react-native-tscodegen/lib/rncodegen/src/generators/components/GeneratePropsCPP')
.generate;
const generatorShadowNodeH = require('react-native-tscodegen/lib/rncodegen/src/generators/components/GenerateShadowNodeH')
.generate;
const generatorShadowNodeCPP = require('react-native-tscodegen/lib/rncodegen/src/generators/components/GenerateShadowNodeCPP')
.generate;
const generatorComponentDescriptorH = require('react-native-tscodegen/lib/rncodegen/src/generators/components/GenerateComponentDescriptorH')
.generate;
const generatorEventEmitterH = require('react-native-tscodegen/lib/rncodegen/src/generators/components/GenerateEventEmitterH')
.generate;

generatedModuleFiles.push(
...generateNM2(libraryName, schema, moduleSpecName),
);

generatedFiles.push(...generateNM2(libraryName, schema, moduleSpecName));
generatedComponentFiles.push(
...generatorPropsH(libraryName, schema, moduleSpecName),
...generatorPropsCPP(libraryName, schema, moduleSpecName),
...generatorShadowNodeH(libraryName, schema, moduleSpecName),
...generatorShadowNodeCPP(libraryName, schema, moduleSpecName),
...generatorComponentDescriptorH(libraryName, schema, moduleSpecName),
...generatorEventEmitterH(libraryName, schema, moduleSpecName),
);

const filesToUpdate = new Map<string, string>([...generatedFiles]);
const moduleFilesToUpdate = new Map<string, string>([
...generatedModuleFiles,
]);
const componentFilesToUpdate = new Map<string, string>([
...generatedComponentFiles,
]);

if (test === true) {
return checkFilesForChanges(filesToUpdate, outputDirectory);
return (
checkFilesForChanges(moduleFilesToUpdate, outputDirectory) &&
checkFilesForChanges(componentFilesToUpdate, componentOutputdir)
);
}

return writeMapToFiles(filesToUpdate, outputDirectory);
return (
writeMapToFiles(moduleFilesToUpdate, outputDirectory) &&
writeMapToFiles(componentFilesToUpdate, componentOutputdir)
);
}

if ((argv.file && argv.files) || (!argv.file && !argv.files)) {
console.error('You must specify either --file or --files.');
process.exit(1);
}

if (!argv.libraryName) {
console.error('You must specify --libraryName');
process.exit(1);
}

acoates-ms marked this conversation as resolved.
Show resolved Hide resolved
let schema: SchemaType;
if (argv.file) {
schema = parseFile(argv.file);
} else {
schema = combineSchemas(globby.sync(argv.files as string[]));
}

const libraryName = 'libraryName';
const libraryName = argv.libraryName;
const moduleSpecName = 'moduleSpecName';
const outputDirectory = 'codegen';
generate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,32 +314,30 @@ export function createNM2Generator({namespace}: {namespace: string}) {
const files = new Map<string, string>();

const nativeModules = Object.keys(schema.modules)
.map(moduleName => {
const modules = schema.modules[moduleName].nativeModules;
if (!modules) {
throw new Error('modules does not exist');
}

return modules;
})
.map(moduleName => schema.modules[moduleName].nativeModules)
.filter(Boolean)
.reduce((acc, components) => Object.assign(acc, components), {});

Object.keys(nativeModules).forEach(name => {
console.log(`Generating Native${name}Spec.g.h`);
const {properties} = nativeModules[name];
const traversedProperties = renderProperties(properties, false);
const traversedPropertyTuples = renderProperties(properties, true);

files.set(
`Native${name}Spec.g.h`,
moduleTemplate
.replace(/::_MODULE_PROPERTIES_TUPLE_::/g, traversedPropertyTuples)
.replace(/::_MODULE_PROPERTIES_SPEC_ERRORS_::/g, traversedProperties)
.replace(/::_MODULE_NAME_::/g, name)
.replace(/::_NAMESPACE_::/g, namespace),
);
});
if (nativeModules) {
Object.keys(nativeModules).forEach(name => {
console.log(`Generating Native${name}Spec.g.h`);
const {properties} = nativeModules[name];
const traversedProperties = renderProperties(properties, false);
const traversedPropertyTuples = renderProperties(properties, true);

files.set(
`Native${name}Spec.g.h`,
moduleTemplate
.replace(/::_MODULE_PROPERTIES_TUPLE_::/g, traversedPropertyTuples)
.replace(
/::_MODULE_PROPERTIES_SPEC_ERRORS_::/g,
traversedProperties,
)
.replace(/::_MODULE_NAME_::/g, name)
.replace(/::_NAMESPACE_::/g, namespace),
);
});
}

return files;
};
Expand Down
4 changes: 2 additions & 2 deletions packages/@rnw-scripts/format-files/src/formatFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {getNativeBinary} from 'clang-format';

/// These constants control which files are formatted
const includeEndsWith = ['.h', '.cpp'];
const excludePathContains: string[] = [];
const excludePathContains: string[] = ['vnext/codegen'];
acoates-ms marked this conversation as resolved.
Show resolved Hide resolved
const excludePathEndsWith = ['.g.h', '.g.cpp'];

const VERIFY_FLAG = '-verify';
Expand Down Expand Up @@ -109,7 +109,7 @@ function spawnClangFormat(
files = files.filter(
file =>
includeEndsWith.some(_ => file.endsWith(_)) &&
!excludePathContains.some(_ => file.indexOf(_) > 0) &&
!excludePathContains.some(_ => file.includes(_)) &&
!excludePathEndsWith.some(_ => file.endsWith(_)),
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include "ActivityIndicatorComponentView.h"

#include <UI.Xaml.Controls.h>
#include <Utils/ValueUtils.h>

#include <react/components/rnwcore/Props.h>

namespace Microsoft::ReactNative {

ActivityIndicatorComponentView::ActivityIndicatorComponentView() : m_element(xaml::Controls::ProgressRing()) {
static auto const defaultProps = std::make_shared<facebook::react::ActivityIndicatorViewProps const>();
m_props = defaultProps;
}

std::vector<facebook::react::ComponentDescriptorProvider>
ActivityIndicatorComponentView::supplementalComponentDescriptorProviders() noexcept {
return {};
}

void ActivityIndicatorComponentView::mountChildComponentView(
const IComponentView &childComponentView,
uint32_t index) noexcept {
assert(false);
}

void ActivityIndicatorComponentView::unmountChildComponentView(
const IComponentView &childComponentView,
uint32_t index) noexcept {
assert(false);
}

void ActivityIndicatorComponentView::updateProps(
facebook::react::Props::Shared const &props,
facebook::react::Props::Shared const &oldProps) noexcept {
const auto &oldActivityProps = *std::static_pointer_cast<const facebook::react::ActivityIndicatorViewProps>(m_props);
const auto &newActivityProps = *std::static_pointer_cast<const facebook::react::ActivityIndicatorViewProps>(props);

if (oldActivityProps.animating != newActivityProps.animating) {
m_element.IsActive(newActivityProps.animating);
}

if (oldActivityProps.color != newActivityProps.color) {
m_element.Foreground(SolidColorBrushFrom(newActivityProps.color));
}

m_props = std::static_pointer_cast<facebook::react::ActivityIndicatorViewProps const>(props);
}

void ActivityIndicatorComponentView::updateState(
facebook::react::State::Shared const &state,
facebook::react::State::Shared const &oldState) noexcept {}
void ActivityIndicatorComponentView::updateLayoutMetrics(
facebook::react::LayoutMetrics const &layoutMetrics,
facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
// Set Position & Size Properties

m_layoutMetrics = layoutMetrics;

winrt::Microsoft::ReactNative::ViewPanel::SetLeft(m_element, layoutMetrics.frame.origin.x);
winrt::Microsoft::ReactNative::ViewPanel::SetTop(m_element, layoutMetrics.frame.origin.y);

m_element.Width(layoutMetrics.frame.size.width);
m_element.Height(layoutMetrics.frame.size.height);
}
void ActivityIndicatorComponentView::finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept {}
void ActivityIndicatorComponentView::prepareForRecycle() noexcept {}
facebook::react::SharedProps ActivityIndicatorComponentView::props() noexcept {
assert(false);
return {};
}

const xaml::FrameworkElement ActivityIndicatorComponentView::Element() const noexcept {
return m_element;
}

} // namespace Microsoft::ReactNative
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include "ComponentView.h"

#include <Microsoft.ReactNative.Cxx/ReactContext.h>
#include <UI.Xaml.Controls.h>
#include "ViewComponentView.h"

#pragma warning(push)
#pragma warning(disable : 4244 4305)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note 4244 is sdl, is this one that's already being fixed in core?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there is already a PR out for this one.

#include <react/renderer/components/view/ViewProps.h>
#pragma warning(pop)

namespace Microsoft::ReactNative {

struct ActivityIndicatorComponentView : BaseComponentView {
ActivityIndicatorComponentView();

std::vector<facebook::react::ComponentDescriptorProvider> supplementalComponentDescriptorProviders() noexcept
override;
void mountChildComponentView(const IComponentView &childComponentView, uint32_t index) noexcept override;
void unmountChildComponentView(const IComponentView &childComponentView, uint32_t index) noexcept override;
void updateProps(facebook::react::Props::Shared const &props, facebook::react::Props::Shared const &oldProps) noexcept
override;
void updateState(facebook::react::State::Shared const &state, facebook::react::State::Shared const &oldState) noexcept
override;
void updateLayoutMetrics(
facebook::react::LayoutMetrics const &layoutMetrics,
facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept override;
void finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept override;
void prepareForRecycle() noexcept override;
facebook::react::SharedProps props() noexcept override;

const xaml::FrameworkElement Element() const noexcept override;

private:
facebook::react::SharedViewProps m_props;
facebook::react::LayoutMetrics m_layoutMetrics;
xaml::Controls::ProgressRing m_element;
};

} // namespace Microsoft::ReactNative
4 changes: 4 additions & 0 deletions vnext/Microsoft.ReactNative/Fabric/ComponentViewRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <react/renderer/components/scrollview/ScrollViewShadowNode.h>
#pragma warning(pop)

#include <react/components/rnwcore/ShadowNodes.h>
#include <react/renderer/components/image/ImageShadowNode.h>
#include <react/renderer/components/root/RootShadowNode.h>
#include <react/renderer/components/text/ParagraphShadowNode.h>
Expand All @@ -18,6 +19,7 @@
#include <react/renderer/components/textinput/iostextinput/TextInputShadowNode.h>
#include <react/renderer/components/view/ViewShadowNode.h>

#include "ActivityIndicatorComponentView.h"
#include "ImageComponentView.h"
#include "ParagraphComponentView.h"
#include "ScrollViewComponentView.h"
Expand Down Expand Up @@ -45,6 +47,8 @@ ComponentViewDescriptor const &ComponentViewRegistry::dequeueComponentViewWithCo
view = std::make_shared<ScrollViewComponentView>();
} else if (componentHandle == facebook::react::ImageShadowNode::Handle()) {
view = std::make_shared<ImageComponentView>(m_context);
} else if (componentHandle == facebook::react::ActivityIndicatorViewShadowNode::Handle()) {
view = std::make_shared<ActivityIndicatorComponentView>();
} else {
// Just to keep track of what kinds of shadownodes we are being used verify we know about them here
assert(
Expand Down
3 changes: 3 additions & 0 deletions vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <JSI/jsi.h>
#include <SchedulerSettings.h>
#include <UI.Xaml.Controls.h>
#include <react/components/rnwcore/ComponentDescriptors.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/components/image/ImageComponentDescriptor.h>
#include <react/renderer/components/text/ParagraphComponentDescriptor.h>
Expand Down Expand Up @@ -134,6 +135,8 @@ class AsyncEventBeat final : public facebook::react::EventBeat { //, public face
std::shared_ptr<facebook::react::ComponentDescriptorProviderRegistry const> sharedProviderRegistry() {
static auto providerRegistry = []() -> std::shared_ptr<facebook::react::ComponentDescriptorProviderRegistry> {
auto providerRegistry = std::make_shared<facebook::react::ComponentDescriptorProviderRegistry>();
providerRegistry->add(facebook::react::concreteComponentDescriptorProvider<
facebook::react::ActivityIndicatorViewComponentDescriptor>());
providerRegistry->add(
facebook::react::concreteComponentDescriptorProvider<facebook::react::ImageComponentDescriptor>());
providerRegistry->add(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// RN has some weird include directory redirections..this forwards the include to the right place
#include <react/renderer/components/image/conversions.h>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// RN has some weird include directory redirections..this forwards the include to the right place
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// RN has some weird include directory redirections..this forwards the include to the right place
#include <react/renderer/components/view/ViewEventEmitter.h>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// RN has some weird include directory redirections..this forwards the include to the right place
#include <react/renderer/components/view/ViewProps.h>
Loading