mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Merge branch 'ios-safe-area' of github.com:NativeScript/NativeScript into ios-safe-area
This commit is contained in:
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,6 +1,25 @@
|
||||
Cross Platform Modules Changelog
|
||||
==============================
|
||||
|
||||
<a name="4.2.1"></a>
|
||||
## [4.2.1](https://github.com/NativeScript/NativeScript/compare/4.2.0...4.2.1) (2018-09-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enable reportProgress property for NativeScirpt Angular's HTTPClient ([#6154](https://github.com/NativeScript/NativeScript/issues/6154)) ([349850f](https://github.com/NativeScript/NativeScript/commit/349850f))
|
||||
* **ios:** listview scrollToIndex crash with async data ([#6182](https://github.com/NativeScript/NativeScript/issues/6182)) ([ca6cccb](https://github.com/NativeScript/NativeScript/commit/ca6cccb))
|
||||
* **ios:** touch delegate does not call base class touch methods ([#6113](https://github.com/NativeScript/NativeScript/pull/6113)) ([284cd5](https://github.com/NativeScript/NativeScript/commit/284cd5))
|
||||
* **ios:** TimePicker minuteInterval property ([#6116](https://github.com/NativeScript/NativeScript/issues/6116)) ([ca9bad6](https://github.com/NativeScript/NativeScript/commit/ca9bad6))
|
||||
* **android:** parallel navigation actions should not be triggered ([#6275](https://github.com/NativeScript/NativeScript/issues/6275)) ([405ccae](https://github.com/NativeScript/NativeScript/commit/405ccae))
|
||||
* **android:** HEAD request should return statusCode ([fe35567](https://github.com/NativeScript/NativeScript/commit/fe35567))
|
||||
* observable array reduce bug ([#6219](https://github.com/NativeScript/NativeScript/issues/6219)) ([b028dd9](https://github.com/NativeScript/NativeScript/commit/b028dd9))
|
||||
* Page and Frame isLoaded undefined checks ([#6255](https://github.com/NativeScript/NativeScript/issues/6255)) ([4a11cf9](https://github.com/NativeScript/NativeScript/commit/4a11cf9))
|
||||
* **android/platform:** reinitialise screen metrics on orientation change ([#6164](https://github.com/NativeScript/NativeScript/issues/6164)) ([040781c](https://github.com/NativeScript/NativeScript/commit/040781c))
|
||||
* **ios:** nowrap label measure in horizontal stack layout ([#6186](https://github.com/NativeScript/NativeScript/issues/6186)) ([efd5f8d](https://github.com/NativeScript/NativeScript/commit/efd5f8d))
|
||||
* **list-view:** Layout list-view items on request ([#6159](https://github.com/NativeScript/NativeScript/issues/6159)) ([115a4c1](https://github.com/NativeScript/NativeScript/commit/115a4c1))
|
||||
* **modals:** application activityBackPressed event not fired for modals ([#6261](https://github.com/NativeScript/NativeScript/issues/6261)) ([13d4f34](https://github.com/NativeScript/NativeScript/commit/13d4f34))
|
||||
|
||||
|
||||
<a name="4.2.0"></a>
|
||||
# [4.2.0](https://github.com/NativeScript/NativeScript/compare/4.1.1...4.2.0) (2018-08-08)
|
||||
|
||||
|
||||
@@ -80,6 +80,6 @@ We [worked together with the Google Angular team](http://angularjs.blogspot.com/
|
||||
|
||||
## Get Help
|
||||
|
||||
Please, use [github issues](https://github.com/NativeScript/NativeScript/issues) strictly for [reporting a bugs](CONTRIBUTING.md#bugs) or [requesting features](CONTRIBUTING.md#features). For general NativeScript questions and support, check out the [NativeScript community forum](https://discourse.nativescript.org/) or ask our experts in [NativeScript community Slack channel](http://developer.telerik.com/wp-login.php?action=slack-invitation).
|
||||
Please, use [github issues](https://github.com/NativeScript/NativeScript/issues) strictly for [reporting a bugs](CONTRIBUTING.md#bugs) or [requesting features](CONTRIBUTING.md#features). For general NativeScript questions and support, check out [Stack Overflow](https://stackoverflow.com/questions/tagged/nativescript) or ask our experts in [NativeScript community Slack channel](http://developer.telerik.com/wp-login.php?action=slack-invitation).
|
||||
|
||||

|
||||
|
||||
@@ -14,6 +14,7 @@ export function loadExamples() {
|
||||
examples.set("gestures", "events/gestures");
|
||||
examples.set("touch", "events/touch-event");
|
||||
examples.set("pan", "events/pan-event");
|
||||
examples.set("swipe-passtrough", "events/swipe-event-passtrough");
|
||||
examples.set("handlers", "events/handlers");
|
||||
examples.set("console", "events/console");
|
||||
examples.set("i61", "events/i61");
|
||||
|
||||
22
apps/app/ui-tests-app/events/swipe-event-passtrough.ts
Normal file
22
apps/app/ui-tests-app/events/swipe-event-passtrough.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { EventData } from "tns-core-modules/data/observable";
|
||||
import { Page } from "tns-core-modules/ui/page";
|
||||
import { SwipeGestureEventData } from "tns-core-modules/ui/gestures";
|
||||
import { TextView } from "tns-core-modules/ui/text-view";
|
||||
|
||||
let outputText: TextView;
|
||||
export function navigatingTo(args: EventData) {
|
||||
var page = <Page>args.object;
|
||||
outputText = page.getViewById<TextView>("output");
|
||||
}
|
||||
|
||||
export function onSwipe(data: SwipeGestureEventData) {
|
||||
const msg = `swipe state:${data.direction}`;
|
||||
console.log(msg);
|
||||
outputText.text += msg + "\n";
|
||||
}
|
||||
|
||||
export function onTap(args) {
|
||||
const msg = `tapEvent triggered`;
|
||||
console.log(msg);
|
||||
outputText.text += msg + "\n";
|
||||
}
|
||||
10
apps/app/ui-tests-app/events/swipe-event-passtrough.xml
Normal file
10
apps/app/ui-tests-app/events/swipe-event-passtrough.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<Page navigatingTo="navigatingTo">
|
||||
<GridLayout rows="*, *" columns="*, *, *" swipe="onSwipe" backgroundColor="lightgreen">
|
||||
<StackLayout row="0" col="1" automationText="swipeHere" tap="onTap" backgroundColor="lightblue">
|
||||
<Label text="swipe/tap inside" verticalAlignement="top"/>
|
||||
</StackLayout>
|
||||
<Label row="1" colSpan="3"
|
||||
text="" textWrap="true"
|
||||
id="output" style="font-size: 10, font-family: monospace;"/>
|
||||
</GridLayout>
|
||||
</Page>
|
||||
@@ -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;
|
||||
|
||||
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>
|
||||
@@ -27,7 +27,7 @@ extract_snippets() {
|
||||
|
||||
npm install markdown-snippet-injector
|
||||
|
||||
for SNIPPET_DIR in {tests,apps,tns-core-modules} ; do
|
||||
for SNIPPET_DIR in {tests/app,apps/app,tns-core-modules} ; do
|
||||
echo "Extracting snippets from: $SNIPPET_DIR"
|
||||
node "$BIN" --root="$SNIPPET_DIR" --target="$TARGET_DIR" \
|
||||
--sourceext=".js|.ts|.xml|.html|.css"
|
||||
|
||||
@@ -83,6 +83,18 @@
|
||||
"fullReset": false,
|
||||
"app": ""
|
||||
},
|
||||
"android28": {
|
||||
"platformName": "Android",
|
||||
"platformVersion": "28",
|
||||
"deviceName": "Emulator-Api28-Google",
|
||||
"avd": "Emulator-Api28-Google",
|
||||
"lt": 60000,
|
||||
"appActivity": "com.tns.NativeScriptActivity",
|
||||
"newCommandTimeout": 720,
|
||||
"noReset": true,
|
||||
"fullReset": false,
|
||||
"app": ""
|
||||
},
|
||||
"sim.iPhone7.iOS110": {
|
||||
"platformName": "iOS",
|
||||
"platformVersion": "11.2",
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { android as androidApp, AndroidActivityBackPressedEventData } from "tns-core-modules/application";
|
||||
import { fromObject, Observable } from "tns-core-modules/data/observable"
|
||||
|
||||
let context: Observable;
|
||||
function activityBackPressedCallback(args: AndroidActivityBackPressedEventData) {
|
||||
if (context && context.get("shouldCancel")) {
|
||||
context.set("shouldCancel", false);
|
||||
context.set("message", "Back-pressed canceled!");
|
||||
args.cancel = true;
|
||||
}
|
||||
}
|
||||
export function onLoaded(args) {
|
||||
console.log("back-button modal test loaded");
|
||||
context = fromObject({
|
||||
message: "First back-press will be canceled",
|
||||
shouldCancel: true
|
||||
});
|
||||
|
||||
args.object.bindingContext = context;
|
||||
|
||||
if (androidApp) {
|
||||
androidApp.on("activityBackPressed", activityBackPressedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
export function onUnloaded(args) {
|
||||
console.log("back-button modal test unloaded");
|
||||
|
||||
if (androidApp) {
|
||||
androidApp.off("activityBackPressed", activityBackPressedCallback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<StackLayout loaded="onLoaded" unloaded="onUnloaded">
|
||||
<Label text="{{ message }}" />
|
||||
<Label text="{{ 'will cancel next back press: ' + shouldCancel }}" />
|
||||
</StackLayout>
|
||||
19
e2e/modal-navigation/app/app.android.css
Normal file
19
e2e/modal-navigation/app/app.android.css
Normal file
@@ -0,0 +1,19 @@
|
||||
#home-page {
|
||||
font-size: 8;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#action-bar-home-page{
|
||||
font-size: 10;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#home-page Button {
|
||||
margin-bottom: 5px;
|
||||
border-color: gray;
|
||||
border-width: 2px;
|
||||
border-radius: 8px;
|
||||
padding: 0px;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
In NativeScript, the app.css file is where you place CSS rules that
|
||||
you would like to apply to your entire application. Check out
|
||||
http://docs.nativescript.org/ui/styling for a full list of the CSS
|
||||
selectors and properties you can use to style UI components.
|
||||
|
||||
/*
|
||||
For example, the following CSS rule changes the font size of all UI
|
||||
components that have the btn class name.
|
||||
*/
|
||||
.btn {
|
||||
font-size: 18;
|
||||
}
|
||||
|
||||
14
e2e/modal-navigation/app/app.ios.css
Normal file
14
e2e/modal-navigation/app/app.ios.css
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#home-page {
|
||||
font-size: 13;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#home-page Button {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
border-color: gray;
|
||||
border-width: 2px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
@@ -57,6 +57,15 @@ export function onModalLayout(args: EventData) {
|
||||
false);
|
||||
}
|
||||
|
||||
export function onAndroidBackEvents(args: EventData) {
|
||||
const view = args.object as View;
|
||||
view.showModal(
|
||||
"android-back-button/android-back-button-page",
|
||||
null,
|
||||
() => console.log("android-back-button modal page layout closed"),
|
||||
true, true, true);
|
||||
}
|
||||
|
||||
export function onModalTabView(args: EventData) {
|
||||
const fullscreen = false;
|
||||
const animated = false;
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
<Page class="page"
|
||||
<Page xmlns="http://schemas.nativescript.org/tns.xsd" class="page"
|
||||
navigatingTo="onNavigatingTo"
|
||||
navigatingFrom="onNavigatingFrom"
|
||||
navigatedTo="onNavigatedTo"
|
||||
navigatedFrom="onNavigatedFrom"
|
||||
xmlns="http://schemas.nativescript.org/tns.xsd">
|
||||
navigatedFrom="onNavigatedFrom">
|
||||
|
||||
<ActionBar class="action-bar">
|
||||
<ActionBar class="action-bar" id="action-bar-home-page" height="5%">
|
||||
<Label class="action-bar-title" text="Home"></Label>
|
||||
</ActionBar>
|
||||
|
||||
<StackLayout>
|
||||
<Button text="Show Modal Without Page" tap="onModalNoPage" />
|
||||
<Button text="Show Modal Page With Frame" tap="onModalFrame" />
|
||||
<Button text="Show Modal Page" tap="onModalPage" />
|
||||
<Button text="Show Modal Layout" tap="onModalLayout" />
|
||||
<Button text="Show Modal TabView" tap="onModalTabView" />
|
||||
<Button text="Navigate To Second Page" tap="onNavigate" />
|
||||
<Button text="Reset Frame Root View" tap="onFrameRootViewReset" />
|
||||
<Button text="Reset Tab Root View" tap="onTabRootViewReset" />
|
||||
<Button text="Reset Layout Root View" tap="onLayoutRootViewReset" />
|
||||
<GridLayout rows="auto" columns="auto, *">
|
||||
<StackLayout id="home-page" row="0" col="0" borderColor="yellowgreen" borderWidth="1" borderRadius="5" padding="2">
|
||||
<TextView text="Reset root" />
|
||||
<Button text="Reset Frame Root View" tap="onFrameRootViewReset" textAlignment="left" />
|
||||
<Button text="Reset Tab Root View" tap="onTabRootViewReset" textAlignment="left" />
|
||||
<Button text="Reset Layout Root View" tap="onLayoutRootViewReset" textAlignment="left" />
|
||||
</StackLayout>
|
||||
<StackLayout id="home-page" row="0" col="1" borderColor="blue" borderWidth="1" borderRadius="5" marginLeft="2" padding="2">
|
||||
<TextView text="Navigate to example" />
|
||||
<Button text="Show Modal Without Page" tap="onModalNoPage" textAlignment="left" />
|
||||
<Button text="Show Modal Page With Frame" tap="onModalFrame" textAlignment="left" />
|
||||
<Button text="Show Modal Page" tap="onModalPage" textAlignment="left" />
|
||||
<Button text="Show Modal Layout" tap="onModalLayout" textAlignment="left" />
|
||||
<Button text="Show Modal TabView" tap="onModalTabView" textAlignment="left" />
|
||||
<Button text="Android Back Btn Events" tap="onAndroidBackEvents" textAlignment="left" />
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
</Page>
|
||||
|
||||
39
e2e/modal-navigation/e2e/android-back-button.e2e-spec.ts
Normal file
39
e2e/modal-navigation/e2e/android-back-button.e2e-spec.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { AppiumDriver, createDriver, SearchOptions } from "nativescript-dev-appium";
|
||||
import { Screen } from "./screen"
|
||||
import { assert } from "chai";
|
||||
|
||||
const exampleAndroidBackBtnEvents = "Android Back Btn Events";
|
||||
|
||||
describe("android-navigate-back", () => {
|
||||
let driver: AppiumDriver;
|
||||
let screen: Screen;
|
||||
|
||||
before(async () => {
|
||||
driver = await createDriver();
|
||||
screen = new Screen(driver);
|
||||
const btnShowNestedModalFrame = await driver.findElementByText(exampleAndroidBackBtnEvents);
|
||||
await btnShowNestedModalFrame.click();
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
if (this.currentTest.state === "failed") {
|
||||
await driver.logTestArtifacts(this.currentTest.title);
|
||||
}
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await driver.resetApp();
|
||||
});
|
||||
|
||||
it("should skip first navigate back", async function () {
|
||||
if (driver.isIOS) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
await driver.navBack();
|
||||
const textElement = await driver.findElementsByText("will cancel next back press: false", SearchOptions.contains, 10);
|
||||
assert.isTrue(textElement !== null);
|
||||
await driver.navBack();
|
||||
await screen.loadedHome();
|
||||
})
|
||||
})
|
||||
@@ -33,8 +33,7 @@ describe("modal-frame:", () => {
|
||||
|
||||
afterEach(async function () {
|
||||
if (this.currentTest.state === "failed") {
|
||||
await driver.logPageSource(this.currentTest.title);
|
||||
await driver.logScreenshot(this.currentTest.title);
|
||||
await driver.logTestArtifacts(this.currentTest.title);
|
||||
await driver.resetApp();
|
||||
await screen[root]();
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ describe("modal-layout:", () => {
|
||||
|
||||
afterEach(async function () {
|
||||
if (this.currentTest.state === "failed") {
|
||||
await driver.logPageSource(this.currentTest.title);
|
||||
await driver.logScreenshot(this.currentTest.title);
|
||||
await driver.logTestArtifacts(this.currentTest.title);
|
||||
await driver.resetApp();
|
||||
await screen[root]();
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ describe("modal-page:", () => {
|
||||
|
||||
afterEach(async function () {
|
||||
if (this.currentTest.state === "failed") {
|
||||
await driver.logPageSource(this.currentTest.title);
|
||||
await driver.logScreenshot(this.currentTest.title);
|
||||
await driver.logTestArtifacts(this.currentTest.title);
|
||||
await driver.resetApp();
|
||||
await screen[root]();
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ describe("modal-tab:", () => {
|
||||
|
||||
afterEach(async function () {
|
||||
if (this.currentTest.state === "failed") {
|
||||
await driver.logPageSource(this.currentTest.title);
|
||||
await driver.logScreenshot(this.currentTest.title);
|
||||
await driver.logTestArtifacts(this.currentTest.title);
|
||||
await driver.resetApp();
|
||||
await screen[root]();
|
||||
}
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
"nativescript-dev-appium": "next",
|
||||
"nativescript-dev-typescript": "next",
|
||||
"nativescript-dev-webpack": "next",
|
||||
"rimraf": "^2.6.2",
|
||||
"typescript": "^3.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
|
||||
"e2e-watch": "tsc -p e2e --watch"
|
||||
"e2e": "npm run clean-e2e && tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json",
|
||||
"e2e-watch": "tsc -p e2e --watch",
|
||||
"clean-e2e": "rimraf 'e2e/**/*.js' 'e2e/**/*.js.map' 'e2e/**/*.map'"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +253,15 @@ export abstract class ViewBase extends Observable {
|
||||
public bind(options: BindingOptions, source?: Object): void;
|
||||
public unbind(property: string): void;
|
||||
|
||||
/**
|
||||
* Invalidates the layout of the view and triggers a new layout pass.
|
||||
*/
|
||||
public requestLayout(): void;
|
||||
|
||||
/**
|
||||
* Iterates over children of type ViewBase.
|
||||
* @param callback Called for each child of type ViewBase. Iteration stops if this method returns falsy value.
|
||||
*/
|
||||
public eachChild(callback: (child: ViewBase) => boolean): void;
|
||||
|
||||
public _addView(view: ViewBase, atIndex?: number): void;
|
||||
|
||||
@@ -595,13 +595,13 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
}
|
||||
|
||||
public loadView(view: ViewBase): void {
|
||||
if (!view.isLoaded) {
|
||||
if (view && !view.isLoaded) {
|
||||
view.callLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
public unloadView(view: ViewBase): void {
|
||||
if (view.isLoaded) {
|
||||
if (view && view.isLoaded) {
|
||||
view.callUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Definitions.
|
||||
import { Point, CustomLayoutView as CustomLayoutViewDefinition, dip } from ".";
|
||||
import { GestureTypes, GestureEventData } from "../../gestures";
|
||||
import { AndroidActivityBackPressedEventData } from "../../../application";
|
||||
// Types.
|
||||
import {
|
||||
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
|
||||
@@ -22,6 +21,7 @@ import {
|
||||
import { Background, ad as androidBackground } from "../../styling/background";
|
||||
import { profile } from "../../../profiling";
|
||||
import { topmost } from "../../frame/frame-stack";
|
||||
import { AndroidActivityBackPressedEventData, android as androidApp } from "../../../application";
|
||||
|
||||
export * from "./view-common";
|
||||
|
||||
@@ -72,12 +72,7 @@ function initializeTouchListener(): void {
|
||||
|
||||
onTouch(view: android.view.View, event: android.view.MotionEvent): boolean {
|
||||
const owner = this.owner;
|
||||
for (let type in owner._gestureObservers) {
|
||||
let list = owner._gestureObservers[type];
|
||||
list.forEach(element => {
|
||||
element.androidOnTouchEvent(event);
|
||||
});
|
||||
}
|
||||
owner.handleGestureTouch(event);
|
||||
|
||||
let nativeView = owner.nativeViewProtected;
|
||||
if (!nativeView || !nativeView.onTouchEvent) {
|
||||
@@ -112,6 +107,13 @@ function initializeDialogFragment() {
|
||||
activity: view._context,
|
||||
cancel: false,
|
||||
};
|
||||
|
||||
// Fist fire application.android global event
|
||||
androidApp.notify(args);
|
||||
if (args.cancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
view.notify(args);
|
||||
|
||||
if (!args.cancel && !view.onBackPressed()) {
|
||||
@@ -178,7 +180,7 @@ function initializeDialogFragment() {
|
||||
}
|
||||
|
||||
const owner = this.owner;
|
||||
if (!owner.isLoaded) {
|
||||
if (owner && !owner.isLoaded) {
|
||||
owner.callLoaded();
|
||||
}
|
||||
|
||||
@@ -194,7 +196,7 @@ function initializeDialogFragment() {
|
||||
}
|
||||
|
||||
const owner = this.owner;
|
||||
if (owner.isLoaded) {
|
||||
if (owner && owner.isLoaded) {
|
||||
owner.callUnloaded();
|
||||
}
|
||||
}
|
||||
@@ -320,6 +322,18 @@ export class View extends ViewCommon {
|
||||
return false;
|
||||
}
|
||||
|
||||
public handleGestureTouch(event: android.view.MotionEvent): any {
|
||||
for (let type in this._gestureObservers) {
|
||||
let list = this._gestureObservers[type];
|
||||
list.forEach(element => {
|
||||
element.androidOnTouchEvent(event);
|
||||
});
|
||||
}
|
||||
if (this.parent instanceof View) {
|
||||
this.parent.handleGestureTouch(event);
|
||||
}
|
||||
}
|
||||
|
||||
private hasGestureObservers() {
|
||||
return this._gestureObservers && Object.keys(this._gestureObservers).length > 0
|
||||
}
|
||||
@@ -343,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +509,7 @@ export class View extends ViewCommon {
|
||||
|
||||
public getLocationRelativeTo(otherView: ViewCommon): Point {
|
||||
if (!this.nativeViewProtected || !this.nativeViewProtected.getWindowToken() ||
|
||||
!otherView.nativeViewProtected || !otherView.nativeViewProtected.getWindowToken() ||
|
||||
!otherView || !otherView.nativeViewProtected || !otherView.nativeViewProtected.getWindowToken() ||
|
||||
this.nativeViewProtected.getWindowToken() !== otherView.nativeViewProtected.getWindowToken()) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -591,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
tns-core-modules/ui/core/view/view.d.ts
vendored
4
tns-core-modules/ui/core/view/view.d.ts
vendored
@@ -586,6 +586,10 @@ export abstract class View extends ViewBase {
|
||||
|
||||
_eachLayoutView(callback: (View) => void): void;
|
||||
|
||||
/**
|
||||
* Iterates over children of type View.
|
||||
* @param callback Called for each child of type View. Iteration stops if this method returns falsy value.
|
||||
*/
|
||||
public eachChildView(callback: (view: View) => boolean): void;
|
||||
|
||||
//@private
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Definitions.
|
||||
// Definitions.
|
||||
import { Point, View as ViewDefinition, dip } from ".";
|
||||
import { ViewBase } from "../view-base";
|
||||
import { booleanConverter, Property } from "../view";
|
||||
|
||||
@@ -531,6 +531,10 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
public _onLivesync(): boolean {
|
||||
super._onLivesync();
|
||||
|
||||
if (!this._currentEntry || !this._currentEntry.entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const currentEntry = this._currentEntry.entry;
|
||||
const newEntry: NavigationEntry = {
|
||||
animated: false,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Page } from "../page";
|
||||
import * as application from "../../application";
|
||||
import {
|
||||
FrameBase, stack, goBack, View, Observable,
|
||||
traceEnabled, traceWrite, traceCategories
|
||||
traceEnabled, traceWrite, traceCategories, traceError
|
||||
} from "./frame-common";
|
||||
|
||||
import {
|
||||
@@ -138,7 +138,7 @@ export class Frame extends FrameBase {
|
||||
// In this case call _navigateCore in order to recreate the current fragment.
|
||||
// Don't call navigate because it will fire navigation events.
|
||||
// As JS instances are alive it is already done for the current page.
|
||||
if (!this.isLoaded || !this._attachedToWindow) {
|
||||
if (!this.isLoaded || this._executingEntry || !this._attachedToWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -696,8 +696,23 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
}
|
||||
|
||||
const entry = this.entry;
|
||||
if (!entry) {
|
||||
traceError(`${fragment}.onCreateView: entry is null or undefined`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const page = entry.resolvedPage;
|
||||
if (!page) {
|
||||
traceError(`${fragment}.onCreateView: entry has no resolvedPage`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const frame = this.frame;
|
||||
if (!frame) {
|
||||
traceError(`${fragment}.onCreateView: this.frame is null or undefined`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (page.parent === frame) {
|
||||
// If we are navigating to a page that was destroyed
|
||||
// reinitialize its UI.
|
||||
@@ -706,12 +721,12 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
page._setupUI(context);
|
||||
}
|
||||
} else {
|
||||
if (!this.frame._styleScope) {
|
||||
if (!frame._styleScope) {
|
||||
// Make sure page will have styleScope even if parents don't.
|
||||
page._updateStyleScope();
|
||||
}
|
||||
|
||||
this.frame._addView(page);
|
||||
frame._addView(page);
|
||||
}
|
||||
|
||||
if (frame.isLoaded && !page.isLoaded) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" };
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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>;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { LayoutBaseCommon, clipToBoundsProperty, View, layout } from "./layout-base-common";
|
||||
import { ios as iosUtils } from "../../utils/utils";
|
||||
import {
|
||||
LayoutBaseCommon, clipToBoundsProperty, isPassThroughParentEnabledProperty, View
|
||||
} from "./layout-base-common";
|
||||
|
||||
export * from "./layout-base-common";
|
||||
|
||||
const majorVersion = iosUtils.MajorVersion;
|
||||
|
||||
export class LayoutBase extends LayoutBaseCommon {
|
||||
nativeViewProtected: UIView;
|
||||
|
||||
@@ -37,4 +36,8 @@ export class LayoutBase extends LayoutBaseCommon {
|
||||
[clipToBoundsProperty.setNative](value: boolean) {
|
||||
this._setNativeClipToBounds();
|
||||
}
|
||||
|
||||
[isPassThroughParentEnabledProperty.setNative](value: boolean) {
|
||||
(<any>this.nativeViewProtected).setPassThroughParent(value);
|
||||
}
|
||||
}
|
||||
@@ -357,7 +357,7 @@ export class CssState {
|
||||
* As a result, at some point in time, the selectors matched have to be requerried from the style scope and applied to the view.
|
||||
*/
|
||||
public onChange(): void {
|
||||
if (this.view.isLoaded) {
|
||||
if (this.view && this.view.isLoaded) {
|
||||
this.unsubscribeFromDynamicUpdates();
|
||||
this.updateMatch();
|
||||
this.subscribeForDynamicUpdates();
|
||||
|
||||
Reference in New Issue
Block a user