Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.view.MotionEvent.PointerProperties
import android.view.View
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.bridge.WritableArray
import com.facebook.react.uimanager.PixelUtil
Expand Down Expand Up @@ -182,18 +181,6 @@ open class GestureHandler {
hitSlop!![HIT_SLOP_BOTTOM_IDX] = bottomPad
hitSlop!![HIT_SLOP_WIDTH_IDX] = width
hitSlop!![HIT_SLOP_HEIGHT_IDX] = height
require(!(hitSlopSet(width) && hitSlopSet(leftPad) && hitSlopSet(rightPad))) {
"Cannot have all of left, right and width defined"
}
require(!(hitSlopSet(width) && !hitSlopSet(leftPad) && !hitSlopSet(rightPad))) {
"When width is set one of left or right pads need to be defined"
}
require(!(hitSlopSet(height) && hitSlopSet(bottomPad) && hitSlopSet(topPad))) {
"Cannot have all of top, bottom and height defined"
}
require(!(hitSlopSet(height) && !hitSlopSet(bottomPad) && !hitSlopSet(topPad))) {
"When height is set one of top or bottom pads need to be defined"
}
}

fun setHitSlop(padding: Float?) {
Expand Down Expand Up @@ -968,68 +955,37 @@ open class GestureHandler {
private const val KEY_MANUAL_ACTIVATION = "manualActivation"
private const val KEY_MOUSE_BUTTON = "mouseButton"
private const val KEY_HIT_SLOP = "hitSlop"
private const val KEY_HIT_SLOP_LEFT = "left"
private const val KEY_HIT_SLOP_TOP = "top"
private const val KEY_HIT_SLOP_RIGHT = "right"
private const val KEY_HIT_SLOP_BOTTOM = "bottom"
private const val KEY_HIT_SLOP_VERTICAL = "vertical"
private const val KEY_HIT_SLOP_HORIZONTAL = "horizontal"
private const val KEY_HIT_SLOP_WIDTH = "width"
private const val KEY_HIT_SLOP_HEIGHT = "height"
private const val KEY_TEST_ID = "testID"
private const val KEY_CANCELS_JS_RESPONDER = "cancelsJSResponder"

/**
* `hitSlop` arrives already normalized by the JS side as
* `[left, top, right, bottom, width, height]`, where `null` marks an edge that was not
* specified. Validation of the `width`/`height` combinations happens in JS as well, so all
* that is left here is converting the values from DIP to pixels.
*/
private fun handleHitSlopProperty(handler: GestureHandler, config: ReadableMap) {
if (config.isNull(KEY_HIT_SLOP)) {
handler.setHitSlop(null)
} else if (config.getType(KEY_HIT_SLOP) == ReadableType.Number) {
val hitSlop = PixelUtil.toPixelFromDIP(config.getDouble(KEY_HIT_SLOP))
handler.setHitSlop(
hitSlop,
hitSlop,
hitSlop,
hitSlop,
GestureHandler.HIT_SLOP_NONE,
GestureHandler.HIT_SLOP_NONE,
)
return
}

val hitSlop = config.getArray(KEY_HIT_SLOP)!!

fun edge(index: Int) = if (hitSlop.isNull(index)) {
GestureHandler.HIT_SLOP_NONE
} else {
Comment thread
m-bert marked this conversation as resolved.
val hitSlop = config.getMap(KEY_HIT_SLOP)!!
var left = GestureHandler.HIT_SLOP_NONE
var top = GestureHandler.HIT_SLOP_NONE
var right = GestureHandler.HIT_SLOP_NONE
var bottom = GestureHandler.HIT_SLOP_NONE
var width = GestureHandler.HIT_SLOP_NONE
var height = GestureHandler.HIT_SLOP_NONE
if (hitSlop.hasKey(KEY_HIT_SLOP_HORIZONTAL)) {
val horizontalPad = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_HORIZONTAL))
right = horizontalPad
left = right
}
if (hitSlop.hasKey(KEY_HIT_SLOP_VERTICAL)) {
val verticalPad = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_VERTICAL))
bottom = verticalPad
top = bottom
}
if (hitSlop.hasKey(KEY_HIT_SLOP_LEFT)) {
left = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_LEFT))
}
if (hitSlop.hasKey(KEY_HIT_SLOP_TOP)) {
top = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_TOP))
}
if (hitSlop.hasKey(KEY_HIT_SLOP_RIGHT)) {
right = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_RIGHT))
}
if (hitSlop.hasKey(KEY_HIT_SLOP_BOTTOM)) {
bottom = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_BOTTOM))
}
if (hitSlop.hasKey(KEY_HIT_SLOP_WIDTH)) {
width = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_WIDTH))
}
if (hitSlop.hasKey(KEY_HIT_SLOP_HEIGHT)) {
height = PixelUtil.toPixelFromDIP(hitSlop.getDouble(KEY_HIT_SLOP_HEIGHT))
}
handler.setHitSlop(left, top, right, bottom, width, height)
PixelUtil.toPixelFromDIP(hitSlop.getDouble(index))
}

handler.setHitSlop(
edge(HIT_SLOP_LEFT_IDX),
edge(HIT_SLOP_TOP_IDX),
edge(HIT_SLOP_RIGHT_IDX),
edge(HIT_SLOP_BOTTOM_IDX),
edge(HIT_SLOP_WIDTH_IDX),
edge(HIT_SLOP_HEIGHT_IDX),
)
Comment thread
coado marked this conversation as resolved.
}
}
}
Expand Down
52 changes: 27 additions & 25 deletions packages/react-native-gesture-handler/apple/RNGestureHandler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,24 @@ - (RNGestureHandler *)gestureHandler

static RNGHHitSlop RNGHHitSlopEmpty = {NAN, NAN, NAN, NAN, NAN, NAN};

#define RNGH_HIT_SLOP_GET(key) (prop[key] == nil ? NAN : [prop[key] doubleValue])
// `hitSlop` reaches the native side already normalized by JS into
// `[left, top, right, bottom, width, height]`, where `null` marks an unspecified edge.
typedef NS_ENUM(NSUInteger, RNGHHitSlopIndex) {
RNGHHitSlopIndexLeft = 0,
RNGHHitSlopIndexTop,
RNGHHitSlopIndexRight,
RNGHHitSlopIndexBottom,
RNGHHitSlopIndexWidth,
RNGHHitSlopIndexHeight,
RNGHHitSlopIndexCount,
};

static CGFloat RNGHHitSlopEdge(NSArray *hitSlop, RNGHHitSlopIndex index)
{
id value = hitSlop[index];
return [value isKindOfClass:[NSNumber class]] ? [value doubleValue] : NAN;
}

#define RNGH_HIT_SLOP_IS_SET(hitSlop) \
(!isnan(hitSlop.left) || !isnan(hitSlop.right) || !isnan(hitSlop.top) || !isnan(hitSlop.bottom))
#define RNGH_HIT_SLOP_INSET(key) (isnan(hitSlop.key) ? 0. : hitSlop.key)
Expand Down Expand Up @@ -167,31 +184,16 @@ - (void)updateConfig:(NSDictionary *)config
}

prop = config[@"hitSlop"];
if ([prop isKindOfClass:[NSNumber class]]) {
_hitSlop.left = _hitSlop.right = _hitSlop.top = _hitSlop.bottom = [prop doubleValue];
} else if ([prop isKindOfClass:[NSNull class]]) {
_hitSlop = RNGHHitSlopEmpty;
if ([prop isKindOfClass:[NSArray class]]) {
_hitSlop.left = RNGHHitSlopEdge(prop, RNGHHitSlopIndexLeft);
_hitSlop.top = RNGHHitSlopEdge(prop, RNGHHitSlopIndexTop);
_hitSlop.right = RNGHHitSlopEdge(prop, RNGHHitSlopIndexRight);
_hitSlop.bottom = RNGHHitSlopEdge(prop, RNGHHitSlopIndexBottom);
_hitSlop.width = RNGHHitSlopEdge(prop, RNGHHitSlopIndexWidth);
_hitSlop.height = RNGHHitSlopEdge(prop, RNGHHitSlopIndexHeight);
} else if (prop != nil) {
_hitSlop.left = _hitSlop.right = RNGH_HIT_SLOP_GET(@"horizontal");
_hitSlop.top = _hitSlop.bottom = RNGH_HIT_SLOP_GET(@"vertical");
_hitSlop.left = RNGH_HIT_SLOP_GET(@"left");
_hitSlop.right = RNGH_HIT_SLOP_GET(@"right");
_hitSlop.top = RNGH_HIT_SLOP_GET(@"top");
_hitSlop.bottom = RNGH_HIT_SLOP_GET(@"bottom");
_hitSlop.width = RNGH_HIT_SLOP_GET(@"width");
_hitSlop.height = RNGH_HIT_SLOP_GET(@"height");
if (isnan(_hitSlop.left) && isnan(_hitSlop.right) && !isnan(_hitSlop.width)) {
RCTLogError(@"When width is set one of left or right pads need to be defined");
}
if (!isnan(_hitSlop.width) && !isnan(_hitSlop.left) && !isnan(_hitSlop.right)) {
RCTLogError(@"Cannot have all of left, right and width defined");
}
if (isnan(_hitSlop.top) && isnan(_hitSlop.bottom) && !isnan(_hitSlop.height)) {
RCTLogError(@"When height is set one of top or bottom pads need to be defined");
}
if (!isnan(_hitSlop.height) && !isnan(_hitSlop.top) && !isnan(_hitSlop.bottom)) {
RCTLogError(@"Cannot have all of top, bottom and height defined");
}
// An explicit `null` clears the hit slop; a missing key leaves the previous value alone.
_hitSlop = RNGHHitSlopEmpty;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,16 @@ - (NSDictionary *)buildManagedHandlerConfig:(const RNGestureHandlerButtonProps &
// and the handler keeps its unset default instead of hit-testing against an identical frame.
if (props.gestureHitSlop.top != 0 || props.gestureHitSlop.left != 0 || props.gestureHitSlop.bottom != 0 ||
props.gestureHitSlop.right != 0) {
config[@"hitSlop"] = @{
@"top" : @(props.gestureHitSlop.top),
@"left" : @(props.gestureHitSlop.left),
@"bottom" : @(props.gestureHitSlop.bottom),
@"right" : @(props.gestureHitSlop.right),
};
// Matches the normalized `[left, top, right, bottom, width, height]` layout the JS side sends;
// the button only exposes the four edges, so width and height are always unset.
config[@"hitSlop"] = @[
@(props.gestureHitSlop.left),
@(props.gestureHitSlop.top),
@(props.gestureHitSlop.right),
@(props.gestureHitSlop.bottom),
[NSNull null],
[NSNull null],
];
}

return config;
Expand Down
Loading
Loading