diff --git a/apps/app/ui-tests-app/layouts/main-page.ts b/apps/app/ui-tests-app/layouts/main-page.ts
index 3111f135b..5d0b7adf2 100644
--- a/apps/app/ui-tests-app/layouts/main-page.ts
+++ b/apps/app/ui-tests-app/layouts/main-page.ts
@@ -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;
diff --git a/apps/app/ui-tests-app/layouts/passThroughParent.ts b/apps/app/ui-tests-app/layouts/passThroughParent.ts
new file mode 100644
index 000000000..1080e0b3c
--- /dev/null
+++ b/apps/app/ui-tests-app/layouts/passThroughParent.ts
@@ -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");
+}
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/layouts/passThroughParent.xml b/apps/app/ui-tests-app/layouts/passThroughParent.xml
new file mode 100644
index 000000000..d6795f742
--- /dev/null
+++ b/apps/app/ui-tests-app/layouts/passThroughParent.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tns-core-modules/ui/core/view/view-common.ts b/tns-core-modules/ui/core/view/view-common.ts
index 0f9cb7c42..2ceb7cf9a 100644
--- a/tns-core-modules/ui/core/view/view-common.ts
+++ b/tns-core-modules/ui/core/view/view-common.ts
@@ -1021,4 +1021,4 @@ export const isEnabledProperty = new Property({
isEnabledProperty.register(ViewCommon);
export const isUserInteractionEnabledProperty = new Property({ name: "isUserInteractionEnabled", defaultValue: true, valueConverter: booleanConverter });
-isUserInteractionEnabledProperty.register(ViewCommon);
\ No newline at end of file
+isUserInteractionEnabledProperty.register(ViewCommon);
diff --git a/tns-core-modules/ui/core/view/view.android.ts b/tns-core-modules/ui/core/view/view.android.ts
index 63398ea40..54ddc9a05 100644
--- a/tns-core-modules/ui/core/view/view.android.ts
+++ b/tns-core-modules/ui/core/view/view.android.ts
@@ -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);
- 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);
}
}
diff --git a/tns-core-modules/ui/core/view/view.ios.ts b/tns-core-modules/ui/core/view/view.ios.ts
index a50543d97..a1a68d7b9 100644
--- a/tns-core-modules/ui/core/view/view.ios.ts
+++ b/tns-core-modules/ui/core/view/view.ios.ts
@@ -1,4 +1,4 @@
-// Definitions.
+// Definitions.
import { Point, View as ViewDefinition, dip } from ".";
import { ViewBase } from "../view-base";
diff --git a/tns-core-modules/ui/layouts/layout-base-common.ts b/tns-core-modules/ui/layouts/layout-base-common.ts
index 167d0a363..104338337 100644
--- a/tns-core-modules/ui/layouts/layout-base-common.ts
+++ b/tns-core-modules/ui/layouts/layout-base-common.ts
@@ -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({ name: "clipToBounds", defaultValue: true, valueConverter: booleanConverter });
clipToBoundsProperty.register(LayoutBaseCommon);
+
+export const isPassThroughParentEnabledProperty = new Property({ name: "isPassThroughParentEnabled", defaultValue: false, valueConverter: booleanConverter });
+isPassThroughParentEnabledProperty.register(LayoutBaseCommon);
diff --git a/tns-core-modules/ui/layouts/layout-base.android.ts b/tns-core-modules/ui/layouts/layout-base.android.ts
index 2f0eb60dd..17b70fde4 100644
--- a/tns-core-modules/ui/layouts/layout-base.android.ts
+++ b/tns-core-modules/ui/layouts/layout-base.android.ts
@@ -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) {
+ (this.nativeViewProtected).setPassThroughParent(value);
+ }
+
[paddingTopProperty.getDefault](): Length {
return { value: this._defaultPaddingTop, unit: "px" };
}
diff --git a/tns-core-modules/ui/layouts/layout-base.d.ts b/tns-core-modules/ui/layouts/layout-base.d.ts
index ded023594..4024b9209 100644
--- a/tns-core-modules/ui/layouts/layout-base.d.ts
+++ b/tns-core-modules/ui/layouts/layout-base.d.ts
@@ -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;
+export const isPassThroughParentEnabledProperty: Property;
diff --git a/tns-core-modules/ui/layouts/layout-base.ios.ts b/tns-core-modules/ui/layouts/layout-base.ios.ts
index e0ff79080..900c1200c 100644
--- a/tns-core-modules/ui/layouts/layout-base.ios.ts
+++ b/tns-core-modules/ui/layouts/layout-base.ios.ts
@@ -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) {
+ (this.nativeViewProtected).setPassThroughParent(value);
+ }
}
\ No newline at end of file