feat: add ability to pass touch event thru parent view (#6204)

* feat: enhance hit-testing support
* refactor(android): update passthroughParent logic as per reqs
* refactor: move isPassthroughParentEnabled to LayoutBase
* Update view-common.ts
* refactor: touchListener logic
* refactor: renames
* added ui test page
This commit is contained in:
Manol Donev
2018-09-18 18:59:40 +03:00
committed by GitHub
parent 86be5b6472
commit 262568314b
10 changed files with 85 additions and 21 deletions

View File

@ -21,6 +21,7 @@ export function loadExamples() {
examples.set("pgrid", "layouts-percent/grid");
examples.set("pstack", "layouts-percent/stack");
examples.set("pwrap", "layouts-percent/wrap");
examples.set("passThroughParent", "layouts/passThroughParent");
examples.set("stacklayout-6059", "layouts/stacklayout-6059");
return examples;

View File

@ -0,0 +1,19 @@
export function onOuterWrapLayoutTap() {
console.log("on outer wrap layout tap");
}
export function onStackLayoutThrowTap() {
throw new Error("Should not tap layout with IsPassThroughParentEnabled=true");
}
export function onUserInteractionDisabledTap() {
throw new Error("Should not tap button with IsUserInteractionEnabled=false");
}
export function onDisabledThrowTap() {
throw new Error("Should not tap button with IsEnabled=false");
}
export function onTap() {
console.log("on button tap");
}

View File

@ -0,0 +1,24 @@
<Page class="page" actionBarHidden="true" xmlns="http://schemas.nativescript.org/tns.xsd">
<WrapLayout tap="onOuterWrapLayoutTap" backgroundColor="#bed3f4">
<StackLayout tap="onStackLayoutThrowTap" backgroundColor="#f3f9db" height="200" isPassThroughParentEnabled="true">
<Label text="isPassThroughParentEnabled='true'" isUserInteractionEnabled="false" />
<Button isUserInteractionEnabled="false" tap="onUserInteractionDisabledThrowTap" text="isUserInteractionEnabled='false'"></Button>
<Button isEnabled="false" tap="onDisabledThrowTap" text="isEnabled='false'"></Button>
<Button tap="onTap" text="TAP"></Button>
</StackLayout>
<StackLayout tap="onStackLayoutThrowTap" style.margin="20" height="300" width="300" backgroundColor="#f5edf7" isPassThroughParentEnabled="true">
<Label text="isPassThroughParentEnabled='true'" isUserInteractionEnabled="false" />
<StackLayout tap="onStackLayoutThrowTap" backgroundColor="#f3f9db" height="200" isPassThroughParentEnabled="true">
<Label text="isPassThroughParentEnabled='true'" isUserInteractionEnabled="false" />
<Button isUserInteractionEnabled="false" tap="onUserInteractionDisabledThrowTap" text="isUserInteractionEnabled='false'"></Button>
<Button isEnabled="false" tap="onDisabledThrowTap" text="isEnabled='false'"></Button>
<Button tap="onTap" text="TAP"></Button>
</StackLayout>
</StackLayout>
</WrapLayout>
</Page>

View File

@ -357,15 +357,20 @@ export class View extends ViewCommon {
}
private setOnTouchListener() {
if (this.nativeViewProtected && this.hasGestureObservers()) {
this.touchListenerIsSet = true;
if (this.nativeViewProtected.setClickable) {
this.nativeViewProtected.setClickable(true);
if (!this.nativeViewProtected || !this.hasGestureObservers()) {
return;
}
// do not set noop listener that handles the event (disabled listener) if IsUserInteractionEnabled is
// false as we might need the ability for the event to pass through to a parent view
initializeTouchListener();
this.touchListener = this.touchListener || new TouchListener(this);
this.nativeViewProtected.setOnTouchListener(this.touchListener);
this.touchListenerIsSet = true;
if (this.nativeViewProtected.setClickable) {
this.nativeViewProtected.setClickable(this.isUserInteractionEnabled);
}
}
@ -605,15 +610,8 @@ export class View extends ViewCommon {
}
[isUserInteractionEnabledProperty.setNative](value: boolean) {
if (!value) {
initializeDisabledListener();
// User interaction is disabled -- we stop it and we do not care whether someone wants to listen for gestures.
this.nativeViewProtected.setOnTouchListener(disableUserInteractionListener);
} else {
this.setOnTouchListener();
if (!this.touchListenerIsSet) {
this.nativeViewProtected.setOnTouchListener(null);
}
if (this.nativeViewProtected.setClickable) {
this.nativeViewProtected.setClickable(value);
}
}

View File

@ -1,4 +1,4 @@
// Definitions.
// Definitions.
import { Point, View as ViewDefinition, dip } from ".";
import { ViewBase } from "../view-base";

View File

@ -106,6 +106,7 @@ export class LayoutBaseCommon extends CustomLayoutView implements LayoutBaseDefi
}
public clipToBounds: boolean;
public isPassThroughParentEnabled: boolean;
public _childIndexToNativeChildIndex(index?: number): number {
if (index === undefined) {
@ -151,3 +152,6 @@ export class LayoutBaseCommon extends CustomLayoutView implements LayoutBaseDefi
export const clipToBoundsProperty = new Property<LayoutBaseCommon, boolean>({ name: "clipToBounds", defaultValue: true, valueConverter: booleanConverter });
clipToBoundsProperty.register(LayoutBaseCommon);
export const isPassThroughParentEnabledProperty = new Property<LayoutBaseCommon, boolean>({ name: "isPassThroughParentEnabled", defaultValue: false, valueConverter: booleanConverter });
isPassThroughParentEnabledProperty.register(LayoutBaseCommon);

View File

@ -1,5 +1,5 @@
import {
LayoutBaseCommon, clipToBoundsProperty,
LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty,
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length
} from "./layout-base-common";
@ -25,6 +25,10 @@ export class LayoutBase extends LayoutBaseCommon {
console.warn(`clipToBounds with value false is not supported on Android. You can use this.android.getParent().setClipChildren(false) as an alternative`);
}
[isPassThroughParentEnabledProperty.setNative](value: boolean) {
(<any>this.nativeViewProtected).setPassThroughParent(value);
}
[paddingTopProperty.getDefault](): Length {
return { value: this._defaultPaddingTop, unit: "px" };
}

View File

@ -96,6 +96,14 @@ export class LayoutBase extends CustomLayoutView {
* Gets or sets a value indicating whether to clip the content of this layout.
*/
clipToBounds: boolean;
/**
* Gets or sets a value indicating whether touch event should pass through to a parent view of the
* layout container in case an interactive child view did not handle it.
* Default value of this property is false. This does not affect the appearance of the view.
*/
isPassThroughParentEnabled: boolean;
}
export const clipToBoundsProperty: Property<LayoutBase, boolean>;
export const isPassThroughParentEnabledProperty: Property<LayoutBase, boolean>;

View File

@ -1,4 +1,6 @@
import { LayoutBaseCommon, clipToBoundsProperty, View } from "./layout-base-common";
import {
LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty, View
} from "./layout-base-common";
export * from "./layout-base-common";
@ -34,4 +36,8 @@ export class LayoutBase extends LayoutBaseCommon {
[clipToBoundsProperty.setNative](value: boolean) {
this._setNativeClipToBounds();
}
[isPassThroughParentEnabledProperty.setNative](value: boolean) {
(<any>this.nativeViewProtected).setPassThroughParent(value);
}
}