mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
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:
@ -21,6 +21,7 @@ export function loadExamples() {
|
|||||||
examples.set("pgrid", "layouts-percent/grid");
|
examples.set("pgrid", "layouts-percent/grid");
|
||||||
examples.set("pstack", "layouts-percent/stack");
|
examples.set("pstack", "layouts-percent/stack");
|
||||||
examples.set("pwrap", "layouts-percent/wrap");
|
examples.set("pwrap", "layouts-percent/wrap");
|
||||||
|
examples.set("passThroughParent", "layouts/passThroughParent");
|
||||||
examples.set("stacklayout-6059", "layouts/stacklayout-6059");
|
examples.set("stacklayout-6059", "layouts/stacklayout-6059");
|
||||||
|
|
||||||
return examples;
|
return examples;
|
||||||
|
19
apps/app/ui-tests-app/layouts/passThroughParent.ts
Normal file
19
apps/app/ui-tests-app/layouts/passThroughParent.ts
Normal 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");
|
||||||
|
}
|
24
apps/app/ui-tests-app/layouts/passThroughParent.xml
Normal file
24
apps/app/ui-tests-app/layouts/passThroughParent.xml
Normal 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>
|
@ -357,15 +357,20 @@ export class View extends ViewCommon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setOnTouchListener() {
|
private setOnTouchListener() {
|
||||||
if (this.nativeViewProtected && this.hasGestureObservers()) {
|
if (!this.nativeViewProtected || !this.hasGestureObservers()) {
|
||||||
this.touchListenerIsSet = true;
|
return;
|
||||||
if (this.nativeViewProtected.setClickable) {
|
|
||||||
this.nativeViewProtected.setClickable(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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();
|
initializeTouchListener();
|
||||||
this.touchListener = this.touchListener || new TouchListener(this);
|
this.touchListener = this.touchListener || new TouchListener(this);
|
||||||
this.nativeViewProtected.setOnTouchListener(this.touchListener);
|
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) {
|
[isUserInteractionEnabledProperty.setNative](value: boolean) {
|
||||||
if (!value) {
|
if (this.nativeViewProtected.setClickable) {
|
||||||
initializeDisabledListener();
|
this.nativeViewProtected.setClickable(value);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Definitions.
|
// Definitions.
|
||||||
import { Point, View as ViewDefinition, dip } from ".";
|
import { Point, View as ViewDefinition, dip } from ".";
|
||||||
import { ViewBase } from "../view-base";
|
import { ViewBase } from "../view-base";
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ export class LayoutBaseCommon extends CustomLayoutView implements LayoutBaseDefi
|
|||||||
}
|
}
|
||||||
|
|
||||||
public clipToBounds: boolean;
|
public clipToBounds: boolean;
|
||||||
|
public isPassThroughParentEnabled: boolean;
|
||||||
|
|
||||||
public _childIndexToNativeChildIndex(index?: number): number {
|
public _childIndexToNativeChildIndex(index?: number): number {
|
||||||
if (index === undefined) {
|
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 });
|
export const clipToBoundsProperty = new Property<LayoutBaseCommon, boolean>({ name: "clipToBounds", defaultValue: true, valueConverter: booleanConverter });
|
||||||
clipToBoundsProperty.register(LayoutBaseCommon);
|
clipToBoundsProperty.register(LayoutBaseCommon);
|
||||||
|
|
||||||
|
export const isPassThroughParentEnabledProperty = new Property<LayoutBaseCommon, boolean>({ name: "isPassThroughParentEnabled", defaultValue: false, valueConverter: booleanConverter });
|
||||||
|
isPassThroughParentEnabledProperty.register(LayoutBaseCommon);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
LayoutBaseCommon, clipToBoundsProperty,
|
LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty,
|
||||||
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length
|
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length
|
||||||
} from "./layout-base-common";
|
} 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`);
|
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 {
|
[paddingTopProperty.getDefault](): Length {
|
||||||
return { value: this._defaultPaddingTop, unit: "px" };
|
return { value: this._defaultPaddingTop, unit: "px" };
|
||||||
}
|
}
|
||||||
|
8
tns-core-modules/ui/layouts/layout-base.d.ts
vendored
8
tns-core-modules/ui/layouts/layout-base.d.ts
vendored
@ -96,6 +96,14 @@ export class LayoutBase extends CustomLayoutView {
|
|||||||
* Gets or sets a value indicating whether to clip the content of this layout.
|
* Gets or sets a value indicating whether to clip the content of this layout.
|
||||||
*/
|
*/
|
||||||
clipToBounds: boolean;
|
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 clipToBoundsProperty: Property<LayoutBase, boolean>;
|
||||||
|
export const isPassThroughParentEnabledProperty: Property<LayoutBase, boolean>;
|
||||||
|
@ -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";
|
export * from "./layout-base-common";
|
||||||
|
|
||||||
@ -34,4 +36,8 @@ export class LayoutBase extends LayoutBaseCommon {
|
|||||||
[clipToBoundsProperty.setNative](value: boolean) {
|
[clipToBoundsProperty.setNative](value: boolean) {
|
||||||
this._setNativeClipToBounds();
|
this._setNativeClipToBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[isPassThroughParentEnabledProperty.setNative](value: boolean) {
|
||||||
|
(<any>this.nativeViewProtected).setPassThroughParent(value);
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user