Skip to content

display: contents based button styling #3634

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

Draft
wants to merge 5 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
580 changes: 478 additions & 102 deletions apps/basic-example/src/RuntimeDecoration.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager
import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager
import com.swmansion.gesturehandler.react.RNGestureHandlerButtonWrapperViewManager
import com.swmansion.gesturehandler.react.RNGestureHandlerDetectorViewManager
import com.swmansion.gesturehandler.react.RNGestureHandlerModule
import com.swmansion.gesturehandler.react.RNGestureHandlerRootViewManager
Expand All @@ -34,13 +35,17 @@ class RNGestureHandlerPackage :
RNGestureHandlerDetectorViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec {
RNGestureHandlerDetectorViewManager()
},
RNGestureHandlerButtonWrapperViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec {
RNGestureHandlerButtonWrapperViewManager()
},
)
}

override fun createViewManagers(reactContext: ReactApplicationContext) = listOf<ViewManager<*, *>>(
RNGestureHandlerRootViewManager(),
RNGestureHandlerButtonViewManager(),
RNGestureHandlerDetectorViewManager(),
RNGestureHandlerButtonWrapperViewManager(),
)

override fun getViewManagerNames(reactContext: ReactApplicationContext) = viewManagers.keys.toList()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.swmansion.gesturehandler.react

import android.content.Context
import com.facebook.react.views.view.ReactViewGroup

class RNGestureHandlerButtonWrapperView(context: Context) : ReactViewGroup(context)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.swmansion.gesturehandler.react

import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.viewmanagers.RNGestureHandlerButtonWrapperManagerDelegate
import com.facebook.react.viewmanagers.RNGestureHandlerButtonWrapperManagerInterface

@ReactModule(name = RNGestureHandlerButtonWrapperViewManager.REACT_CLASS)
class RNGestureHandlerButtonWrapperViewManager :
ViewGroupManager<RNGestureHandlerButtonWrapperView>(),
RNGestureHandlerButtonWrapperManagerInterface<RNGestureHandlerButtonWrapperView> {
private val mDelegate: ViewManagerDelegate<RNGestureHandlerButtonWrapperView>

init {
mDelegate =
RNGestureHandlerButtonWrapperManagerDelegate<
RNGestureHandlerButtonWrapperView,
RNGestureHandlerButtonWrapperViewManager,
>(this)
}

override fun getDelegate(): ViewManagerDelegate<RNGestureHandlerButtonWrapperView> = mDelegate

override fun getName() = REACT_CLASS

override fun createViewInstance(reactContext: ThemedReactContext) = RNGestureHandlerButtonWrapperView(reactContext)

companion object {
const val REACT_CLASS = "RNGestureHandlerButtonWrapper"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#if !TARGET_OS_OSX
#import <UIKit/UIKit.h>
#else
#import <React/RCTUIKit.h>
#endif

#import <React/RCTViewComponentView.h>

#import <react/renderer/components/rngesturehandler_codegen/EventEmitters.h>

using namespace facebook::react;

NS_ASSUME_NONNULL_BEGIN

@interface RNGestureHandlerButtonWrapper : RCTViewComponentView

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#import "RNGestureHandlerButtonWrapper.h"
#import "RNGestureHandlerButtonWrapperComponentDescriptor.h"
#import "RNGestureHandlerModule.h"

#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>

#import <react/renderer/components/rngesturehandler_codegen/EventEmitters.h>
#import <react/renderer/components/rngesturehandler_codegen/Props.h>
#import <react/renderer/components/rngesturehandler_codegen/RCTComponentViewHelpers.h>

#include <unordered_map>

@interface RNGestureHandlerButtonWrapper () <RCTRNGestureHandlerButtonWrapperViewProtocol>
@end

@implementation RNGestureHandlerButtonWrapper

#if TARGET_OS_OSX
+ (BOOL)shouldBeRecycled
{
return NO;
}
#endif

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const RNGestureHandlerButtonWrapperProps>();
_props = defaultProps;
}

return self;
}

#pragma mark - RCTComponentViewProtocol

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RNGestureHandlerButtonWrapperComponentDescriptor>();
}

@end

Class<RCTComponentViewProtocol> RNGestureHandlerButtonWrapperCls(void)
{
return RNGestureHandlerButtonWrapper.class;
}
3 changes: 2 additions & 1 deletion packages/react-native-gesture-handler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@
"ios": {
"componentProvider": {
"RNGestureHandlerButton": "RNGestureHandlerButtonComponentView",
"RNGestureHandlerDetector": "RNGestureHandlerDetector"
"RNGestureHandlerDetector": "RNGestureHandlerDetector",
"RNGestureHandlerButtonWrapper": "RNGestureHandlerButtonWrapper"
}
}
},
Expand Down
5 changes: 4 additions & 1 deletion packages/react-native-gesture-handler/react-native.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ module.exports = {
dependency: {
platforms: {
android: {
componentDescriptors: ['RNGestureHandlerDetectorComponentDescriptor'],
componentDescriptors: [
'RNGestureHandlerDetectorComponentDescriptor',
'RNGestureHandlerButtonWrapperComponentDescriptor',
],
cmakeListsPath: './CMakeLists.txt',
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
#include <react/renderer/components/rngesturehandler_codegen/ShadowNodes.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>

#include <react/renderer/components/rngesturehandler_codegen/RNGestureHandlerButtonWrapperComponentDescriptor.h>
#include <react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h>

namespace facebook::react {

using RNGestureHandlerButtonComponentDescriptor =
ConcreteComponentDescriptor<RNGestureHandlerButtonShadowNode>;

using RNGestureHandlerRootViewComponentDescriptor =
ConcreteComponentDescriptor<RNGestureHandlerRootViewShadowNode>;

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

/**
* This code was generated by
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*
* @generated by codegen project: GenerateComponentDescriptorH.js
*/

#pragma once

#include <react/renderer/core/ConcreteComponentDescriptor.h>

#include "RNGestureHandlerButtonWrapperShadowNode.h"

namespace facebook::react {

class RNGestureHandlerButtonWrapperComponentDescriptor final
: public ConcreteComponentDescriptor<
RNGestureHandlerButtonWrapperShadowNode> {
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
void adopt(ShadowNode &shadowNode) const override {
react_native_assert(
dynamic_cast<RNGestureHandlerButtonWrapperShadowNode *>(&shadowNode));

ConcreteComponentDescriptor::adopt(shadowNode);
}
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@

/**
* This code was generated by
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*
* @generated by codegen project: GenerateShadowNodeCpp.js
*/

#include <react/renderer/components/rngesturehandler_codegen/ShadowNodes.h>

#include "RNGestureHandlerButtonWrapperShadowNode.h"

namespace facebook::react {

extern const char RNGestureHandlerButtonWrapperComponentName[] =
"RNGestureHandlerButtonWrapper";

void RNGestureHandlerButtonWrapperShadowNode::initialize() {
// Disable forcing view flattening
ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);

// When the button wrapper is cloned and has a child node, the child node
// should be cloned as well to ensure it is mutable.
if (!getChildren().empty()) {
prepareChildren();
}
}

void RNGestureHandlerButtonWrapperShadowNode::prepareChildren() {
const auto &children = getChildren();
react_native_assert(
children.size() == 1 &&
"RNGestureHandlerButtonWrapper received more than one child");

const auto directChild = children[0];
react_native_assert(
directChild->getChildren().size() == 1 &&
"RNGestureHandlerButtonWrapper received more than one grandchild");

const auto clonedChild = directChild->clone({});

const auto childWithProtectedAccess =
std::static_pointer_cast<RNGestureHandlerButtonWrapperShadowNode>(
clonedChild);
childWithProtectedAccess->traits_.unset(ShadowNodeTraits::ForceFlattenView);

replaceChild(*directChild, clonedChild);

const auto grandChild = clonedChild->getChildren()[0];
const auto clonedGrandChild = grandChild->clone({});
clonedChild->replaceChild(*grandChild, clonedGrandChild);
}

void RNGestureHandlerButtonWrapperShadowNode::appendChild(
const ShadowNode::Shared &child) {
YogaLayoutableShadowNode::appendChild(child);
prepareChildren();
}

void RNGestureHandlerButtonWrapperShadowNode::layout(
LayoutContext layoutContext) {
YogaLayoutableShadowNode::layout(layoutContext);
react_native_assert(getChildren().size() == 1);
react_native_assert(getChildren()[0]->getChildren().size() == 1);

auto child = std::static_pointer_cast<const YogaLayoutableShadowNode>(
getChildren()[0]);
auto grandChild = std::static_pointer_cast<const YogaLayoutableShadowNode>(
child->getChildren()[0]);

child->ensureUnsealed();
grandChild->ensureUnsealed();

auto mutableChild = std::const_pointer_cast<YogaLayoutableShadowNode>(child);
auto mutableGrandChild =
std::const_pointer_cast<YogaLayoutableShadowNode>(grandChild);

// TODO: figure out the correct way to setup metrics between button wrapper
// and the child
auto metrics = grandChild->getLayoutMetrics();
setLayoutMetrics(metrics);

auto metricsNoOrigin = grandChild->getLayoutMetrics();
metricsNoOrigin.frame.origin = Point{};

mutableChild->setLayoutMetrics(metricsNoOrigin);
mutableGrandChild->setLayoutMetrics(metricsNoOrigin);
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

/**
* This code was generated by
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*
* @generated by codegen project: GenerateShadowNodeH.js
*/

#pragma once

#include <jsi/jsi.h>
#include <react/renderer/components/rngesturehandler_codegen/EventEmitters.h>
#include <react/renderer/components/rngesturehandler_codegen/Props.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/LayoutContext.h>

#include "RNGestureHandlerButtonWrapperState.h"

namespace facebook::react {

JSI_EXPORT extern const char RNGestureHandlerButtonWrapperComponentName[];

/*
* `ShadowNode` for <RNGestureHandlerButtonWrapper> component.
*/
class RNGestureHandlerButtonWrapperShadowNode final
: public ConcreteViewShadowNode<
RNGestureHandlerButtonWrapperComponentName,
RNGestureHandlerButtonWrapperProps,
RNGestureHandlerButtonWrapperEventEmitter,
RNGestureHandlerButtonWrapperState> {
public:
RNGestureHandlerButtonWrapperShadowNode(
const ShadowNodeFragment &fragment,
const ShadowNodeFamily::Shared &family,
ShadowNodeTraits traits)
: ConcreteViewShadowNode(fragment, family, traits) {
initialize();
}

RNGestureHandlerButtonWrapperShadowNode(
const ShadowNode &sourceShadowNode,
const ShadowNodeFragment &fragment)
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
initialize();
}

void layout(LayoutContext layoutContext) override;
void appendChild(const ShadowNode::Shared &child) override;

private:
void initialize();
void prepareChildren();
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* This code was generated by
* [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be
* lost once the code is regenerated.
*
* @generated by codegen project: GenerateStateH.js
*/
#pragma once

#ifdef ANDROID
#include <folly/dynamic.h>
#endif

namespace facebook::react {

class RNGestureHandlerButtonWrapperState {
public:
RNGestureHandlerButtonWrapperState() = default;

#ifdef ANDROID
RNGestureHandlerButtonWrapperState(
RNGestureHandlerButtonWrapperState const &previousState,
folly::dynamic data){};
folly::dynamic getDynamic() const {
return {};
};
#endif
};

} // namespace facebook::react
Loading
Loading