Summary: Supporting View Manager Commands on the new UIManager in Fabric. This is needed for things like scrollTo on ScrollView.

Reviewed By: JoshuaGross

Differential Revision: D16175575

fbshipit-source-id: a74effdf7e47b56a150a4e3fb6c4d787659e0250
This commit is contained in:
Eli White 2019-07-19 11:45:40 -07:00 коммит произвёл Facebook Github Bot
Родитель 5ca10c7caa
Коммит 6f09dc03bf
17 изменённых файлов: 173 добавлений и 6 удалений

Просмотреть файл

@ -102,6 +102,11 @@ typedef NS_OPTIONS(NSInteger, RNComponentViewUpdateMask) {
- (void)updateLayoutMetrics:(facebook::react::LayoutMetrics const &)layoutMetrics
oldLayoutMetrics:(facebook::react::LayoutMetrics const &)oldLayoutMetrics;
/*
* Called when receiving a command
*/
- (void)handleCommand:(NSString const *)commandName args:(NSArray const *)args;
/*
* Called right after all update methods were called for a particular component view.
* Useful for performing updates that require knowledge of several independent aspects of the compound mounting change

Просмотреть файл

@ -32,10 +32,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)scheduleTransaction:(facebook::react::MountingCoordinator::Shared const &)mountingCoordinator;
/**
* Dispatch a command to be performed on the main thread.
* Can be called from any thread.
*/
- (void)dispatchCommand:(ReactTag)reactTag commandName:(NSString *)commandName args:(NSArray *)args;
- (void)synchronouslyUpdateViewOnUIThread:(ReactTag)reactTag
changedProps:(NSDictionary *)props
componentDescriptor:(const facebook::react::ComponentDescriptor &)componentDescriptor;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -225,6 +225,22 @@ static void RNPerformMountInstructions(ShadowViewMutationList const &mutations,
});
}
- (void)dispatchCommand:(ReactTag)reactTag commandName:(NSString *)commandName args:(NSArray *)args
{
if (RCTIsMainQueue()) {
// Already on the proper thread, so:
// * No need to do a thread jump;
// * No need to allocate a block.
[self synchronouslyDispatchCommandOnUIThread:reactTag commandName:commandName args:args];
return;
}
RCTExecuteOnMainQueue(^{
RCTAssertMainQueue();
[self synchronouslyDispatchCommandOnUIThread:reactTag commandName:commandName args:args];
});
}
- (void)mountMutations:(MountingCoordinator::Shared const &)mountingCoordinator
{
SystraceSection s("-[RCTMountingManager mountMutations:]");
@ -253,4 +269,13 @@ static void RNPerformMountInstructions(ShadowViewMutationList const &mutations,
[componentView updateProps:newProps oldProps:oldProps];
}
- (void)synchronouslyDispatchCommandOnUIThread:(ReactTag)reactTag
commandName:(NSString *)commandName
args:(NSArray *)args
{
RCTAssertMainQueue();
UIView<RCTComponentViewProtocol> *componentView = [_componentViewRegistry componentViewByTag:reactTag];
[componentView handleCommand:commandName args:args];
}
@end

Просмотреть файл

@ -60,6 +60,11 @@ using namespace facebook::react;
// Default implementation does nothing.
}
- (void)handleCommand:(NSString *)commandName args:(NSArray *)args
{
// Default implementation does nothing.
}
- (void)updateLayoutMetrics:(LayoutMetrics const &)layoutMetrics
oldLayoutMetrics:(LayoutMetrics const &)oldLayoutMetrics
{

Просмотреть файл

@ -28,6 +28,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)schedulerDidFinishTransaction:(facebook::react::MountingCoordinator::Shared const &)mountingCoordinator;
- (void)schedulerDidDispatchCommand:(facebook::react::ShadowView const &)shadowView
commandName:(std::string const &)commandName
args:(folly::dynamic const)args;
@end
/**

Просмотреть файл

@ -34,6 +34,15 @@ class SchedulerDelegateProxy : public SchedulerDelegate {
// Preemptive allocation of native views on iOS does not require this call.
}
void schedulerDidDispatchCommand(
const ShadowView &shadowView,
const std::string &commandName,
const folly::dynamic args) override
{
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
[scheduler.delegate schedulerDidDispatchCommand:shadowView commandName:commandName args:args];
}
private:
void *scheduler_;
};

Просмотреть файл

@ -321,6 +321,17 @@ using namespace facebook::react;
[_mountingManager scheduleTransaction:mountingCoordinator];
}
- (void)schedulerDidDispatchCommand:(facebook::react::ShadowView const &)shadowView
commandName:(std::string const &)commandName
args:(folly::dynamic const)args
{
ReactTag tag = shadowView.tag;
NSString *commandStr = [[NSString alloc] initWithUTF8String:commandName.c_str()];
NSArray *argsArray = convertFollyDynamicToId(args);
[self->_mountingManager dispatchCommand:tag commandName:commandStr args:argsArray];
}
- (void)addObserver:(id<RCTSurfacePresenterObserver>)observer
{
std::unique_lock<better::shared_mutex> lock(_observerListMutex);

Просмотреть файл

@ -248,6 +248,11 @@ inline local_ref<ReadableMap::javaobject> castReadableMap(
return make_local(reinterpret_cast<ReadableMap::javaobject>(nativeMap.get()));
}
inline local_ref<ReadableArray::javaobject> castReadableArray(
local_ref<ReadableNativeArray::javaobject> nativeArray) {
return make_local(reinterpret_cast<ReadableArray::javaobject>(nativeArray.get()));
}
// TODO: this method will be removed when binding for components are code-gen
local_ref<JString> getPlatformComponentName(const ShadowView &shadowView) {
local_ref<JString> componentName;
@ -672,6 +677,31 @@ void Binding::schedulerDidRequestPreliminaryViewAllocation(
isLayoutableShadowNode);
}
void Binding::schedulerDidDispatchCommand(
const ShadowView &shadowView,
std::string const &commandName,
folly::dynamic const args) {
jni::global_ref<jobject> localJavaUIManager = getJavaUIManager();
if (!localJavaUIManager) {
LOG(ERROR) << "Binding::schedulerDidDispatchCommand: JavaUIManager disappeared";
return;
}
static auto dispatchCommand =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(
jint, jstring, ReadableArray::javaobject)>(
"dispatchCommand");
local_ref<JString> command = make_jstring(commandName);
local_ref<ReadableArray::javaobject> argsArray = castReadableArray(
ReadableNativeArray::newObjectCxxArgs(args));
dispatchCommand(localJavaUIManager, shadowView.tag, command.get(), argsArray.get());
}
void Binding::registerNatives() {
registerHybrid(
{makeNativeMethod("initHybrid", Binding::initHybrid),

Просмотреть файл

@ -82,6 +82,11 @@ class Binding : public jni::HybridClass<Binding>, public SchedulerDelegate {
const SurfaceId surfaceId,
const ShadowView &shadowView);
void schedulerDidDispatchCommand(
const ShadowView &shadowView,
std::string const &commandName,
folly::dynamic const args);
void setPixelDensity(float pointScaleFactor);
void uninstallFabricUIManager();

Просмотреть файл

@ -258,5 +258,17 @@ void Scheduler::uiManagerDidCreateShadowNode(
}
}
void Scheduler::uiManagerDispatchCommand(
const SharedShadowNode &shadowNode,
std::string const &commandName,
folly::dynamic const args) {
SystraceSection s("Scheduler::uiManagerDispatchCommand");
if (delegate_) {
auto shadowView = ShadowView(*shadowNode);
delegate_->schedulerDidDispatchCommand(shadowView, commandName, args);
}
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -86,6 +86,10 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
const SharedShadowNodeUnsharedList &rootChildNodes) override;
void uiManagerDidCreateShadowNode(
const SharedShadowNode &shadowNode) override;
void uiManagerDispatchCommand(
const SharedShadowNode &shadowNode,
std::string const &commandName,
folly::dynamic const args) override;
#pragma mark - ShadowTreeDelegate

Просмотреть файл

@ -22,7 +22,7 @@ class SchedulerDelegate {
public:
/*
* Called right after Scheduler computed (and laid out) a new updated version
* of the tree and calculated a set of mutations which are suffisient
* of the tree and calculated a set of mutations which are sufficient
* to construct a new one.
*/
virtual void schedulerDidFinishTransaction(
@ -35,6 +35,11 @@ class SchedulerDelegate {
SurfaceId surfaceId,
const ShadowView &shadowView) = 0;
virtual void schedulerDidDispatchCommand(
const ShadowView &shadowView,
std::string const &commandName,
folly::dynamic const args) = 0;
virtual ~SchedulerDelegate() noexcept = default;
};

Просмотреть файл

@ -167,6 +167,15 @@ void UIManager::updateState(
});
}
void UIManager::dispatchCommand(
const SharedShadowNode &shadowNode,
std::string const &commandName,
folly::dynamic const args) const {
if (delegate_) {
delegate_->uiManagerDispatchCommand(shadowNode, commandName, args);
}
}
void UIManager::setShadowTreeRegistry(ShadowTreeRegistry *shadowTreeRegistry) {
shadowTreeRegistry_ = shadowTreeRegistry;
}

Просмотреть файл

@ -75,6 +75,11 @@ class UIManager {
const SharedShadowNode &shadowNode,
const StateData::Shared &rawStateData) const;
void dispatchCommand(
const SharedShadowNode &shadowNode,
std::string const &commandName,
folly::dynamic const args) const;
ShadowTreeRegistry *shadowTreeRegistry_;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
UIManagerDelegate *delegate_;

Просмотреть файл

@ -141,7 +141,7 @@ jsi::Value UIManagerBinding::get(
runtime,
uiManager.createNode(
tagFromValue(runtime, arguments[0]),
componentNameFromValue(runtime, arguments[1]),
stringFromValue(runtime, arguments[1]),
surfaceIdFromValue(runtime, arguments[2]),
RawProps(runtime, arguments[3]),
eventTargetFromValue(runtime, arguments[4], arguments[0])));
@ -332,6 +332,25 @@ jsi::Value UIManagerBinding::get(
});
}
if (methodName == "dispatchCommand") {
return jsi::Function::createFromHostFunction(
runtime,
name,
3,
[&uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager.dispatchCommand(
shadowNodeFromValue(runtime, arguments[0]),
stringFromValue(runtime, arguments[1]),
commandArgsFromValue(runtime, arguments[2]));
return jsi::Value::undefined();
});
}
// Legacy API
if (methodName == "measureLayout") {
return jsi::Function::createFromHostFunction(

Просмотреть файл

@ -20,7 +20,7 @@ class UIManagerDelegate {
public:
/*
* Called right after the new/updated Shadow Node tree is constructed.
* The tree is not layed out and not sealed at this time.
* The tree is not laid out and not sealed at this time.
*/
virtual void uiManagerDidFinishTransaction(
SurfaceId surfaceId,
@ -28,12 +28,20 @@ class UIManagerDelegate {
/*
* Called each time when UIManager constructs a new Shadow Node. Receiver
* maight use this to preluminary optimistically allocate a new native view
* might use this to optimistically allocate a new native view
* instances.
*/
virtual void uiManagerDidCreateShadowNode(
const SharedShadowNode &shadowNode) = 0;
/*
* Called when UIManager wants to dispatch a command to the mounting layer.
*/
virtual void uiManagerDispatchCommand(
const SharedShadowNode &shadowNode,
std::string const &commandName,
folly::dynamic const args) = 0;
virtual ~UIManagerDelegate() noexcept = default;
};

Просмотреть файл

@ -83,11 +83,17 @@ inline static SurfaceId surfaceIdFromValue(
return (SurfaceId)value.getNumber();
}
inline static std::string componentNameFromValue(
inline static std::string stringFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return value.getString(runtime).utf8(runtime);
}
inline static folly::dynamic commandArgsFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return jsi::dynamicFromValue(runtime, value);
}
} // namespace react
} // namespace facebook