Skip to content

Commit

Permalink
Fix onPress/In/Out and responder handling
Browse files Browse the repository at this point in the history
  • Loading branch information
msand committed Sep 23, 2018
1 parent 35597ed commit ad99f97
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 116 deletions.
118 changes: 6 additions & 112 deletions android/src/main/java/com/horcrux/svg/SvgView.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,12 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.ReactCompoundView;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.TouchEvent;
import com.facebook.react.uimanager.events.TouchEventCoalescingKeyHelper;
import com.facebook.react.uimanager.events.TouchEventType;
import com.facebook.react.views.view.ReactViewGroup;

import javax.annotation.Nullable;
Expand All @@ -34,7 +27,8 @@
* Custom {@link View} implementation that draws an RNSVGSvg React view and its children.
*/
@SuppressLint("ViewConstructor")
public class SvgView extends ViewGroup {
public class SvgView extends ViewGroup implements ReactCompoundView {

@SuppressWarnings("unused")
public enum Events {
@SuppressWarnings("unused")
Expand All @@ -54,16 +48,9 @@ public String toString() {
}

private @Nullable Bitmap mBitmap;
private final EventDispatcher mEventDispatcher;
private long mGestureStartTime = TouchEvent.UNSET;
private int mTargetTag;

private final TouchEventCoalescingKeyHelper mTouchEventCoalescingKeyHelper =
new TouchEventCoalescingKeyHelper();

public SvgView(ReactContext reactContext) {
super(reactContext);
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
}

public void addView(View child, int index, LayoutParams params) {
Expand Down Expand Up @@ -105,17 +92,12 @@ private SvgViewShadowNode getShadowNode() {
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
public int reactTagForTouch(float touchX, float touchY) {
SvgViewShadowNode node = getShadowNode();
if (node != null) {
mTargetTag = node.hitTest(new Point((int) ev.getX(), (int) ev.getY()));
if (mTargetTag != -1) {
handleTouchEvent(ev);
return true;
}
return node.hitTest(new Point((int) touchX, (int) touchY));
}

return super.dispatchTouchEvent(ev);
return getId();
}

@Override
Expand Down Expand Up @@ -144,92 +126,4 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
}

private int getAbsoluteLeft(View view) {
int left = view.getLeft() - view.getScrollX();

if (view.getParent() == view.getRootView() || view.getParent() instanceof ReactRootView) {
return left;
}

View parent = (View) view.getParent();
return left + getAbsoluteLeft(parent);
}

private int getAbsoluteTop(View view) {
int top = view.getTop() - view.getScrollY();

if (view.getParent() == view.getRootView() || view.getParent() instanceof ReactRootView) {
return top;
}

View parent = (View) view.getParent();
return top + getAbsoluteTop(parent);
}

private void dispatch(MotionEvent ev, TouchEventType type) {
ev.offsetLocation(getAbsoluteLeft(this), getAbsoluteTop(this));
mEventDispatcher.dispatchEvent(
TouchEvent.obtain(
mTargetTag,
type,
ev,
mGestureStartTime,
ev.getX(),
ev.getY(),
mTouchEventCoalescingKeyHelper));
}

private void handleTouchEvent(MotionEvent ev) {
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
mGestureStartTime = ev.getEventTime();
dispatch(ev, TouchEventType.START);
} else if (mTargetTag == -1) {
// All the subsequent action types are expected to be called after ACTION_DOWN thus target
// is supposed to be set for them.
Log.e(
"error",
"Unexpected state: received touch event but didn't get starting ACTION_DOWN for this " +
"gesture before");
} else if (action == MotionEvent.ACTION_UP) {
// End of the gesture. We reset target tag to -1 and expect no further event associated with
// this gesture.
dispatch(ev, TouchEventType.END);
mTargetTag = -1;
mGestureStartTime = TouchEvent.UNSET;
} else if (action == MotionEvent.ACTION_MOVE) {
// Update pointer position for current gesture
dispatch(ev, TouchEventType.MOVE);
} else if (action == MotionEvent.ACTION_POINTER_DOWN) {
// New pointer goes down, this can only happen after ACTION_DOWN is sent for the first pointer
dispatch(ev, TouchEventType.START);
} else if (action == MotionEvent.ACTION_POINTER_UP) {
// Exactly onw of the pointers goes up
dispatch(ev, TouchEventType.END);
} else if (action == MotionEvent.ACTION_CANCEL) {
dispatchCancelEvent(ev);
mTargetTag = -1;
mGestureStartTime = TouchEvent.UNSET;
} else {
Log.w(
"IGNORE",
"Warning : touch event was ignored. Action=" + action + " Target=" + mTargetTag);
}
}

private void dispatchCancelEvent(MotionEvent ev) {
// This means the gesture has already ended, via some other CANCEL or UP event. This is not
// expected to happen very often as it would mean some child View has decided to intercept the
// touch stream and start a native gesture only upon receiving the UP/CANCEL event.
if (mTargetTag == -1) {
Log.w(
"error",
"Can't cancel already finished gesture. Is a child View trying to start a gesture from " +
"an UP/CANCEL event?");
return;
}

dispatch(ev, TouchEventType.CANCEL);
}
}
5 changes: 4 additions & 1 deletion elements/Svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
findNodeHandle,
NativeModules
} from "react-native";
import extractResponder from "../lib/extract/extractResponder";
import extractViewBox from "../lib/extract/extractViewBox";
import { ViewBoxAttributes } from "../lib/attributes";
import { numberProp } from "../lib/props";
import Shape from "./Shape";

/** @namespace NativeModules.RNSVGSvgViewManager */
const RNSVGSvgViewManager = NativeModules.RNSVGSvgViewManager;
Expand All @@ -24,7 +26,7 @@ const styles = StyleSheet.create({
}
});

class Svg extends Component {
class Svg extends Shape {
static displayName = "Svg";
static propTypes = {
...ViewPropTypes,
Expand Down Expand Up @@ -93,6 +95,7 @@ class Svg extends Component {
{...props}
bbWidth={w}
bbHeight={h}
{...extractResponder(props, this)}
{...extractViewBox({ viewBox, preserveAspectRatio })}
ref={ele => {
this.root = ele;
Expand Down
1 change: 1 addition & 0 deletions ios/Elements/RNSVGGroup.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
}];
[self setHitArea:[self getPath:context]];
self.clientRect = groupRect;
self.bounds = groupRect;
[self popGlyphContext];
}

Expand Down
7 changes: 4 additions & 3 deletions ios/Elements/RNSVGUse.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ - (void)setHref:(NSString *)href
if ([href isEqualToString:_href]) {
return;
}

[self invalidate];
_href = href;
}
Expand All @@ -26,7 +26,7 @@ - (void)setUsewidth:(NSString *)usewidth
if ([usewidth isEqualToString:_usewidth]) {
return;
}

[self invalidate];
_usewidth = usewidth;
}
Expand All @@ -36,7 +36,7 @@ - (void)setUseheight:(NSString *)useheight
if ([useheight isEqualToString:_useheight]) {
return;
}

[self invalidate];
_useheight = useheight;
}
Expand Down Expand Up @@ -69,6 +69,7 @@ - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);
}
self.clientRect = template.clientRect;
self.bounds = template.clientRect;
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
Expand Down
1 change: 1 addition & 0 deletions ios/RNSVGRenderable.m
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
const CGRect pathBounding = CGPathGetBoundingBox(self.path);
const CGAffineTransform svgToClientTransform = CGAffineTransformConcat(CGContextGetCTM(context), self.svgView.invInitialCTM);
self.clientRect = CGRectApplyAffineTransform(pathBounding, svgToClientTransform);
self.bounds = self.clientRect;

CGPathDrawingMode mode = kCGPathStroke;
BOOL fillColor = NO;
Expand Down

0 comments on commit ad99f97

Please sign in to comment.