Show mounted ComponentKit views in Flipper
Summary: Before this diff, Flipper showed *leaf* views created by ComponentKit, but not any intermediate views. Now we show both. A new node type `SKComponentMountedView` is used for this purpose. Its descriptor `SKComponentMountedViewDescriptor` mostly delegates to its view's descriptor, but redirects back into ComponentKit for children. Reviewed By: Andrey-Mishanin Differential Revision: D21130997 fbshipit-source-id: b3c12ea7cc1200962b3ba7c269c48d68b1809948
This commit is contained in:
committed by
Facebook GitHub Bot
parent
756987e4bf
commit
d0803ecd56
@@ -16,6 +16,8 @@
|
||||
|
||||
#import "SKComponentLayoutDescriptor.h"
|
||||
#import "SKComponentLayoutWrapper.h"
|
||||
#import "SKComponentMountedView.h"
|
||||
#import "SKComponentMountedViewDescriptor.h"
|
||||
#import "SKComponentRootViewDescriptor.h"
|
||||
|
||||
@implementation FlipperKitLayoutComponentKitSupport
|
||||
@@ -29,6 +31,9 @@
|
||||
[mapper registerDescriptor:[[SKComponentLayoutDescriptor alloc]
|
||||
initWithDescriptorMapper:mapper]
|
||||
forClass:[SKComponentLayoutWrapper class]];
|
||||
[mapper registerDescriptor:[[SKComponentMountedViewDescriptor alloc]
|
||||
initWithDescriptorMapper:mapper]
|
||||
forClass:[SKComponentMountedView class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#import "CKComponent+Sonar.h"
|
||||
#import "SKComponentLayoutWrapper.h"
|
||||
#import "SKComponentMountedView.h"
|
||||
#import "SKSubDescriptor.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@@ -64,21 +65,26 @@ static std::vector<std::pair<NSString*, SKSubDescriptor>>& subDescriptors() {
|
||||
}
|
||||
|
||||
- (NSUInteger)childCountForNode:(SKComponentLayoutWrapper*)node {
|
||||
NSUInteger count = node.children.size();
|
||||
if (count == 0) {
|
||||
count = node.component.viewContext.view ? 1 : 0;
|
||||
if (!node) {
|
||||
return 0; // -children will return garbage if invoked on nil
|
||||
}
|
||||
return count;
|
||||
return node.children.match(
|
||||
[](SKLeafViewChild) -> NSUInteger { return 1; },
|
||||
[](SKMountedViewChild) -> NSUInteger { return 1; },
|
||||
[](const std::vector<SKComponentLayoutWrapper*>& components)
|
||||
-> NSUInteger { return components.size(); });
|
||||
}
|
||||
|
||||
- (id)childForNode:(SKComponentLayoutWrapper*)node atIndex:(NSUInteger)index {
|
||||
if (node.children.size() == 0) {
|
||||
if (node.rootNode == node.component.viewContext.view) {
|
||||
return nil;
|
||||
}
|
||||
return node.component.viewContext.view;
|
||||
if (!node) {
|
||||
return nil; // -children will return garbage if invoked on nil
|
||||
}
|
||||
return node.children[index];
|
||||
return node.children.match(
|
||||
[](SKLeafViewChild leafView) -> id { return leafView.view; },
|
||||
[](SKMountedViewChild mountedView) -> id { return mountedView.view; },
|
||||
[&](const std::vector<SKComponentLayoutWrapper*>& components) -> id {
|
||||
return components[index];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSArray<SKNamed<NSDictionary<NSString*, NSObject*>*>*>*)dataForNode:
|
||||
@@ -86,12 +92,11 @@ static std::vector<std::pair<NSString*, SKSubDescriptor>>& subDescriptors() {
|
||||
NSMutableArray<SKNamed<NSDictionary<NSString*, NSObject*>*>*>* data =
|
||||
[NSMutableArray new];
|
||||
|
||||
if (node.isFlexboxChild) {
|
||||
[data
|
||||
addObject:[SKNamed
|
||||
newWithName:@"Layout"
|
||||
withValue:[self
|
||||
propsForFlexboxChild:node.flexboxChild]]];
|
||||
if (node) {
|
||||
node.flexboxChild.apply([&](const CKFlexboxComponentChild& child) {
|
||||
[data addObject:[SKNamed newWithName:@"Layout"
|
||||
withValue:[self propsForFlexboxChild:child]]];
|
||||
});
|
||||
}
|
||||
NSMutableDictionary<NSString*, NSObject*>* extraData =
|
||||
[[NSMutableDictionary alloc] init];
|
||||
@@ -163,29 +168,33 @@ static std::vector<std::pair<NSString*, SKSubDescriptor>>& subDescriptors() {
|
||||
}
|
||||
|
||||
- (void)hitTest:(SKTouch*)touch forNode:(SKComponentLayoutWrapper*)node {
|
||||
if (node.children.size() == 0) {
|
||||
UIView* componentView = node.component.viewContext.view;
|
||||
if (componentView != nil) {
|
||||
if ([touch containedIn:componentView.bounds]) {
|
||||
[touch continueWithChildIndex:0 withOffset:componentView.bounds.origin];
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
return; // -children will return garbage if invoked on nil
|
||||
}
|
||||
|
||||
NSInteger index = 0;
|
||||
for (index = node.children.size() - 1; index >= 0; index--) {
|
||||
const auto child = node.children[index];
|
||||
|
||||
CGRect frame = {.origin = child.position, .size = child.size};
|
||||
|
||||
if ([touch containedIn:frame]) {
|
||||
[touch continueWithChildIndex:index withOffset:child.position];
|
||||
return;
|
||||
}
|
||||
BOOL didContinueTouch = node.children.match(
|
||||
[&](SKLeafViewChild leafView) -> BOOL {
|
||||
[touch continueWithChildIndex:0 withOffset:{0, 0}];
|
||||
return YES;
|
||||
},
|
||||
[&](SKMountedViewChild mountedView) -> BOOL {
|
||||
[touch continueWithChildIndex:0 withOffset:{0, 0}];
|
||||
return YES;
|
||||
},
|
||||
[&](std::vector<SKComponentLayoutWrapper*> children) -> BOOL {
|
||||
for (auto it = children.rbegin(); it != children.rend(); ++it) {
|
||||
SKComponentLayoutWrapper* wrapper = *it;
|
||||
CGRect frame = {.origin = wrapper.position, .size = wrapper.size};
|
||||
if ([touch containedIn:frame]) {
|
||||
NSUInteger index = std::distance(children.begin(), it.base()) - 1;
|
||||
[touch continueWithChildIndex:index withOffset:wrapper.position];
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
});
|
||||
if (!didContinueTouch) {
|
||||
[touch finish];
|
||||
}
|
||||
|
||||
[touch finish];
|
||||
}
|
||||
|
||||
- (BOOL)matchesQuery:(NSString*)query forNode:(id)node {
|
||||
|
||||
@@ -9,8 +9,35 @@
|
||||
|
||||
#import <ComponentKit/CKComponentLayout.h>
|
||||
#import <ComponentKit/CKFlexboxComponent.h>
|
||||
#import <ComponentKit/CKOptional.h>
|
||||
#import <ComponentKit/CKVariant.h>
|
||||
|
||||
#import <vector>
|
||||
|
||||
@protocol CKInspectableView;
|
||||
@class SKComponentLayoutWrapper;
|
||||
@class SKComponentMountedView;
|
||||
|
||||
// CK::Variant does not support Objective-C types unless they are boxed:
|
||||
struct SKLeafViewChild {
|
||||
UIView* view;
|
||||
};
|
||||
struct SKMountedViewChild {
|
||||
SKComponentMountedView* view;
|
||||
};
|
||||
|
||||
/**
|
||||
The children of a SKComponentLayoutWrapper may be:
|
||||
- A single leaf view, which may have UIView children of its own.
|
||||
- A single non-leaf view, if the component created a view; its children will be
|
||||
the component's child components.
|
||||
- An array of SKComponentLayoutWrappers, if the component did not create a
|
||||
view.
|
||||
*/
|
||||
using SKComponentLayoutWrapperChildren = CK::Variant<
|
||||
SKLeafViewChild,
|
||||
SKMountedViewChild,
|
||||
std::vector<SKComponentLayoutWrapper*>>;
|
||||
|
||||
@interface SKComponentLayoutWrapper : NSObject
|
||||
|
||||
@@ -18,11 +45,11 @@
|
||||
@property(nonatomic, readonly) NSString* identifier;
|
||||
@property(nonatomic, readonly) CGSize size;
|
||||
@property(nonatomic, readonly) CGPoint position;
|
||||
@property(nonatomic, readonly) std::vector<SKComponentLayoutWrapper*> children;
|
||||
@property(nonatomic, readonly) SKComponentLayoutWrapperChildren children;
|
||||
@property(nonatomic, weak, readonly) id<CKInspectableView> rootNode;
|
||||
// Null for layouts which are not direct children of a CKFlexboxComponent
|
||||
@property(nonatomic, readonly) BOOL isFlexboxChild;
|
||||
@property(nonatomic, readonly) CKFlexboxComponentChild flexboxChild;
|
||||
/** CK::none for components that are not the child of a CKFlexboxComponent. */
|
||||
@property(nonatomic, readonly) CK::Optional<CKFlexboxComponentChild>
|
||||
flexboxChild;
|
||||
|
||||
+ (instancetype)newFromRoot:(id<CKInspectableView>)root
|
||||
parentKey:(NSString*)parentKey;
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
#import <ComponentKit/CKInspectableView.h>
|
||||
|
||||
#import "CKComponent+Sonar.h"
|
||||
#import "SKComponentMountedView.h"
|
||||
|
||||
static char const kLayoutWrapperKey = ' ';
|
||||
|
||||
static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
CKComponent* parent,
|
||||
CKComponent* child) {
|
||||
static CK::Optional<CKFlexboxComponentChild> findFlexboxLayoutParams(
|
||||
id<CKMountable> parent,
|
||||
id<CKMountable> child) {
|
||||
if ([parent isKindOfClass:[CKFlexboxComponent class]]) {
|
||||
static Ivar ivar =
|
||||
class_getInstanceVariable([CKFlexboxComponent class], "_children");
|
||||
@@ -42,7 +43,7 @@ static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
return CK::none;
|
||||
}
|
||||
|
||||
@implementation SKComponentLayoutWrapper
|
||||
@@ -66,6 +67,7 @@ static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
SKComponentLayoutWrapper* const wrapper = [[SKComponentLayoutWrapper alloc]
|
||||
initWithLayout:layout
|
||||
position:CGPointMake(0, 0)
|
||||
flexboxChild:CK::none
|
||||
parentKey:[NSString
|
||||
stringWithFormat:@"%@%d.",
|
||||
parentKey,
|
||||
@@ -85,6 +87,8 @@ static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
|
||||
- (instancetype)initWithLayout:(const CKComponentLayout&)layout
|
||||
position:(CGPoint)position
|
||||
flexboxChild:
|
||||
(CK::Optional<CKFlexboxComponentChild>)flexboxChild
|
||||
parentKey:(NSString*)parentKey
|
||||
reuseWrapper:(CKComponentReuseWrapper*)reuseWrapper
|
||||
rootNode:(id<CKInspectableView>)node {
|
||||
@@ -93,6 +97,7 @@ static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
_component = (CKComponent*)layout.component;
|
||||
_size = layout.size;
|
||||
_position = position;
|
||||
_flexboxChild = flexboxChild;
|
||||
_identifier = [parentKey stringByAppendingString:layout.component
|
||||
? layout.component.className
|
||||
: @"(null)"];
|
||||
@@ -105,6 +110,7 @@ static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SKComponentLayoutWrapper*> childComponents;
|
||||
if (layout.children != nullptr) {
|
||||
int index = 0;
|
||||
for (const auto& child : *layout.children) {
|
||||
@@ -115,17 +121,26 @@ static CKFlexboxComponentChild findFlexboxLayoutParams(
|
||||
[[SKComponentLayoutWrapper alloc]
|
||||
initWithLayout:child.layout
|
||||
position:child.position
|
||||
flexboxChild:findFlexboxLayoutParams(
|
||||
_component, child.layout.component)
|
||||
parentKey:[_identifier
|
||||
stringByAppendingFormat:@"[%d].", index++]
|
||||
reuseWrapper:reuseWrapper
|
||||
rootNode:node];
|
||||
childWrapper->_isFlexboxChild =
|
||||
[_component isKindOfClass:[CKFlexboxComponent class]];
|
||||
childWrapper->_flexboxChild = findFlexboxLayoutParams(
|
||||
_component, (CKComponent*)child.layout.component);
|
||||
_children.push_back(childWrapper);
|
||||
childComponents.push_back(childWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
UIView* mountedView = _component.mountedView;
|
||||
if (mountedView && !childComponents.empty()) {
|
||||
_children = SKMountedViewChild{[[SKComponentMountedView alloc]
|
||||
initWithView:mountedView
|
||||
children:childComponents]};
|
||||
} else if (mountedView) {
|
||||
_children = SKLeafViewChild{mountedView}; // leaf view
|
||||
} else {
|
||||
_children = childComponents;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <vector>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class SKComponentLayoutWrapper;
|
||||
|
||||
/**
|
||||
Represents a non-leaf view created by ComponentKit. Its corresponding
|
||||
descriptor CKComponentMountedViewDescriptor delegates to the view's descriptor
|
||||
for attributes and most other behaviors, but redirects back into ComponentKit's
|
||||
SKComponentLayoutWrapper when queried for children.
|
||||
|
||||
In this way, non-leaf views created by ComponentKit appear in the Flipper
|
||||
layout hierarchy as the child of the component that created their view.
|
||||
*/
|
||||
@interface SKComponentMountedView : NSObject
|
||||
|
||||
- (instancetype)initWithView:(UIView*)view
|
||||
children:(std::vector<SKComponentLayoutWrapper*>)children;
|
||||
|
||||
@property(nonatomic, readonly) UIView* view;
|
||||
@property(nonatomic, readonly) std::vector<SKComponentLayoutWrapper*> children;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#if FB_SONARKIT_ENABLED
|
||||
|
||||
#import "SKComponentMountedView.h"
|
||||
|
||||
@implementation SKComponentMountedView
|
||||
|
||||
- (instancetype)initWithView:(UIView*)view
|
||||
children:(std::vector<SKComponentLayoutWrapper*>)children {
|
||||
if (self = [super init]) {
|
||||
_view = view;
|
||||
_children = std::move(children);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <FlipperKitLayoutPlugin/SKNodeDescriptor.h>
|
||||
|
||||
@class SKComponentMountedView;
|
||||
|
||||
@interface SKComponentMountedViewDescriptor
|
||||
: SKNodeDescriptor<SKComponentMountedView*>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#if FB_SONARKIT_ENABLED
|
||||
|
||||
#import "SKComponentMountedViewDescriptor.h"
|
||||
|
||||
#import <ComponentKit/CKComponent.h>
|
||||
#import <ComponentKit/CKComponentInternal.h>
|
||||
|
||||
#import <FlipperKitHighlightOverlay/SKHighlightOverlay.h>
|
||||
#import <FlipperKitLayoutPlugin/SKObject.h>
|
||||
#import <FlipperKitLayoutTextSearchable/FKTextSearchable.h>
|
||||
|
||||
#import "CKComponent+Sonar.h"
|
||||
#import "SKComponentLayoutWrapper.h"
|
||||
#import "SKComponentMountedView.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation SKComponentMountedViewDescriptor
|
||||
|
||||
- (SKNodeDescriptor*)_viewDescriptorFor:(SKComponentMountedView*)node {
|
||||
// For most methods, we delegate to the descriptor for the underlying view.
|
||||
return [self descriptorForClass:[node.view class]];
|
||||
}
|
||||
|
||||
- (NSString*)identifierForNode:(SKComponentMountedView*)node {
|
||||
return [[self _viewDescriptorFor:node] identifierForNode:node.view];
|
||||
}
|
||||
|
||||
- (NSString*)nameForNode:(SKComponentMountedView*)node {
|
||||
return [[self _viewDescriptorFor:node] nameForNode:node.view];
|
||||
}
|
||||
|
||||
- (NSUInteger)childCountForNode:(SKComponentMountedView*)node {
|
||||
// An obvious future improvement: we should also return any
|
||||
// non-ComponentKit-managed child views of our view.
|
||||
// Explicit nil check; -children will return garbage if invoked on nil
|
||||
return node ? node.children.size() : 0;
|
||||
}
|
||||
|
||||
- (id)childForNode:(SKComponentMountedView*)node atIndex:(NSUInteger)index {
|
||||
// Explicit nil check; -children will return garbage if invoked on nil
|
||||
return node ? node.children[index] : nil;
|
||||
}
|
||||
|
||||
- (NSArray<SKNamed<NSDictionary<NSString*, NSObject*>*>*>*)dataForNode:
|
||||
(SKComponentMountedView*)node {
|
||||
return [[self _viewDescriptorFor:node] dataForNode:node.view];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString*, SKNodeUpdateData>*)dataMutationsForNode:
|
||||
(SKComponentMountedView*)node {
|
||||
return [[self _viewDescriptorFor:node] dataMutationsForNode:node.view];
|
||||
}
|
||||
|
||||
- (NSArray<SKNamed<NSString*>*>*)attributesForNode:
|
||||
(SKComponentMountedView*)node {
|
||||
return [[self _viewDescriptorFor:node] attributesForNode:node.view];
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted forNode:(SKComponentMountedView*)node {
|
||||
[[self _viewDescriptorFor:node] setHighlighted:highlighted forNode:node.view];
|
||||
}
|
||||
|
||||
- (void)hitTest:(SKTouch*)touch forNode:(SKComponentMountedView*)node {
|
||||
if (!node) {
|
||||
return; // -children will return garbage if invoked on nil
|
||||
}
|
||||
const auto& children = node.children;
|
||||
for (auto it = children.rbegin(); it != children.rend(); ++it) {
|
||||
SKComponentLayoutWrapper* child = *it;
|
||||
CGRect frame = {.origin = child.position, .size = child.size};
|
||||
if ([touch containedIn:frame]) {
|
||||
NSUInteger index = std::distance(children.begin(), it.base()) - 1;
|
||||
[touch continueWithChildIndex:index withOffset:child.position];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[touch finish];
|
||||
}
|
||||
|
||||
- (BOOL)matchesQuery:(NSString*)query forNode:(SKComponentMountedView*)node {
|
||||
return [[self _viewDescriptorFor:node] matchesQuery:query forNode:node.view];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user