Merge branch 'master' into animate-width-height

This commit is contained in:
Martin Yankov
2019-05-21 14:12:14 +03:00
committed by GitHub
52 changed files with 638 additions and 194 deletions

View File

@@ -1,3 +1,30 @@
<a name="5.4.0"></a>
# [5.4.0](https://github.com/NativeScript/NativeScript/compare/5.3.2...5.4.0) (2019-05-14)
### Bug Fixes
* throw if failed to load component ([#7186](https://github.com/NativeScript/NativeScript/issues/7186)) ([b7abb3d](https://github.com/NativeScript/NativeScript/commit/b7abb3d))
* **android/button:** possible incorrect button visual state ([#7190](https://github.com/NativeScript/NativeScript/issues/7190)) ([2a02360](https://github.com/NativeScript/NativeScript/commit/2a02360))
* **devtools-ios:** Ensure UI modifications run on main thread ([c60f74d](https://github.com/NativeScript/NativeScript/commit/c60f74d)), closes [#7219](https://github.com/NativeScript/NativeScript/issues/7219)
* **ios:** add null check to TouchGestureRecognizer ([#7182](https://github.com/NativeScript/NativeScript/issues/7182)) ([7d3f0d9](https://github.com/NativeScript/NativeScript/commit/7d3f0d9))
* **ios:** application.displayedEvent does not depend on trace to fire ([#7141](https://github.com/NativeScript/NativeScript/issues/7141)) ([#7156](https://github.com/NativeScript/NativeScript/issues/7156)) ([4e56c89](https://github.com/NativeScript/NativeScript/commit/4e56c89))
* **ios:** image cache module not longer sets null in NSCache ([#7171](https://github.com/NativeScript/NativeScript/issues/7171)) ([4f79d2e](https://github.com/NativeScript/NativeScript/commit/4f79d2e))
* **ios-modal:** closeCallback not being called with popover presentation style ([#7189](https://github.com/NativeScript/NativeScript/issues/7189)) ([aa44eb9](https://github.com/NativeScript/NativeScript/commit/aa44eb9))
* **ios-tabview:** crash when setting tab text color ([#7188](https://github.com/NativeScript/NativeScript/issues/7188)) ([77c45da](https://github.com/NativeScript/NativeScript/commit/77c45da))
* **text:** crash when removing FormattedText ([#7237](https://github.com/NativeScript/NativeScript/issues/7237)) ([37b53c6](https://github.com/NativeScript/NativeScript/commit/37b53c6))
### Features
* **android:** elevation shadow support ([#7136](https://github.com/NativeScript/NativeScript/issues/7136)) ([cf533a7](https://github.com/NativeScript/NativeScript/commit/cf533a7))
* **android:** implement a 'activityNewIntent' event ([3efc06e](https://github.com/NativeScript/NativeScript/commit/3efc06e))
* **android:** implement BorderDrawable outline ([ec07a99](https://github.com/NativeScript/NativeScript/commit/ec07a99))
* **hmr:** preserve navigation history on applying changes ([#7146](https://github.com/NativeScript/NativeScript/issues/7146)) ([d35e14e](https://github.com/NativeScript/NativeScript/commit/d35e14e))
* **switch:** add property for off state background color ([#7138](https://github.com/NativeScript/NativeScript/issues/7138)) ([f0146f0](https://github.com/NativeScript/NativeScript/commit/f0146f0))
## [5.3.2](https://github.com/NativeScript/NativeScript/compare/5.3.1...5.3.2) (2019-05-03)

View File

@@ -46,7 +46,7 @@ Button {
}
#s10 {
border-width: 5 0; border-color: black; background-color: white;
android-elevation: 0; border-width: 5 0; border-color: black;
}
#s11 {

View File

@@ -0,0 +1,43 @@
Label, Button {
text-align: center;
padding: 10;
margin: 16;
background-color: #bbb;
}
.elevation-0 {
android-elevation: 0;
}
.elevation-2 {
android-elevation: 2;
}
.elevation-4 {
android-elevation: 4;
}
.elevation-4:highlighted {
android-elevation: 2;
}
.elevation-8 {
android-elevation: 8;
}
.elevation-10 {
android-elevation: 10;
}
.pressed-z-10 {
android-dynamic-elevation-offset: 10;
}
.round {
color: #fff;
background-color: #ff1744;
border-radius: 50%; /* TODO kills elevation */
width: 80;
height: 80;
android-elevation: 8;
}

View File

@@ -0,0 +1,20 @@
import { Button } from "tns-core-modules/ui/button/button";
import { EventData } from "tns-core-modules/ui/page/page";
const states = [
{ class: "", text: "default elevation" },
{ class: "elevation-10", text: "elevetion 10" },
{ class: "elevation-10 pressed-z-10", text: "elevetion 10 pressed-z 10" },
{ class: "elevation-0", text: "elevetion 0" },
]
let currentState = 0;
export function buttonTap(args: EventData) {
let btn: Button = args.object as Button;
currentState++;
if (currentState >= states.length) {
currentState = 0;
}
btn.className = states[currentState].class;
btn.text = states[currentState].text;
}

View File

@@ -0,0 +1,25 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
<ScrollView>
<StackLayout backgroundColor="aliceblue" androidElevation="2" padding="10" margin="20">
<Button text="default elevation" tap="buttonTap"/>
<Button class="elevation-0" text="elevation 0"/>
<Label class="elevation-2" text="Label with elevation 2"/>
<StackLayout androidElevation="4" backgroundColor="#ff1744" horizontalAlignment="center" margin="20">
<Button class="elevation-2" text="elevation 2"/>
<Button class="elevation-4" text="elevation 4"/>
</StackLayout>
<StackLayout orientation="horizontal" horizontalAlignment="center">
<Button class="round" androidElevation="4" text="el. 4"/>
<Button class="elevation-8 round" text="el. 8"/>
</StackLayout>
<Label class="elevation-8" text="elevation 8"/>
<Button class="elevation-8" text="elevation 8, with radius" borderRadius="4"/>
<Button class="elevation-10" text="elevation 10"/>
</StackLayout>
</ScrollView>
</Page>

View File

@@ -38,6 +38,7 @@ export function loadExamples() {
examples.set("margins-paddings-with-percentage", "css/margins-paddings-with-percentage");
examples.set("padding-and-border", "css/padding-and-border");
examples.set("combinators", "css/combinators");
examples.set("elevation", "css/elevation");
examples.set("styled-formatted-text", "css/styled-formatted-text");
examples.set("non-uniform-radius", "css/non-uniform-radius");
examples.set("missing-background-image", "css/missing-background-image");

View File

@@ -4,7 +4,7 @@
<Label text="pixels" />
<StackLayout borderWidth="1px" borderColor="black" horizontalAlignment="left" verticalAlignment="top" orientation="horizontal" padding="1px">
<Label text="Label" borderWidth="1px" borderRadius="10px" borderColor="green" padding="1px" margin="1px" />
<Button text="Button" borderWidth="1px" borderRadius="10px" borderColor="green" padding="1px" margin="1px" backgroundColor="orange" />
<Button text="Button" borderWidth="1px" borderRadius="10px" borderColor="green" padding="1px" margin="1px" androidElevation="0" />
<TextView text="Text View" borderWidth="1px" borderRadius="10px" borderColor="green" padding="1px" margin="1px" />
<TextField text="Text Field" borderWidth="1px" borderRadius="10px" borderColor="green" padding="1px" margin="1px" />
<Image src="~/ui-tests-app/resources/images/gravatar.png" borderWidth="1px" borderRadius="10px" borderColor="green" padding="1px" margin="1px" />
@@ -12,7 +12,7 @@
<StackLayout borderWidth="2px" borderColor="green" orientation="horizontal" padding="2px">
<Label text="Label" borderWidth="5px" borderRadius="10px" borderColor="black" padding="5px" margin="5px" />
<Button text="Button" borderWidth="4px" borderRadius="10px" borderColor="black" padding="4px" margin="4px" backgroundColor="orange" />
<Button text="Button" borderWidth="4px" borderRadius="10px" borderColor="black" padding="4px" margin="4px" androidElevation="0" />
<TextView text="Text View" borderWidth="3px" borderRadius="10px" borderColor="black" padding="3px" margin="3px" />
<TextField text="Text Field" borderWidth="2px" borderRadius="10px" borderColor="black" padding="2px" margin="2px" />
</StackLayout>
@@ -32,7 +32,7 @@
<Label text="device independent pixels" />
<StackLayout borderWidth="1" borderColor="black" horizontalAlignment="left" verticalAlignment="top" orientation="horizontal" padding="1">
<Label text="Label" borderWidth="1" borderRadius="10" borderColor="green" padding="1" margin="1" />
<Button text="Button" borderWidth="1" borderRadius="10" borderColor="green" padding="1" margin="1" backgroundColor="orange" />
<Button text="Button" borderWidth="1" borderRadius="10" borderColor="green" padding="1" margin="1" androidElevation="0" />
<TextView text="Text View" borderWidth="1" borderRadius="10" borderColor="green" padding="1" margin="1" />
<TextField text="Text Field" borderWidth="1" borderRadius="10" borderColor="green" padding="1" margin="1" />
<Image src="~/ui-tests-app/resources/images/gravatar.png" borderWidth="1" borderRadius="10" borderColor="green" padding="1" margin="1" />
@@ -40,7 +40,7 @@
<StackLayout borderWidth="2" borderColor="green" orientation="horizontal" padding="2">
<Label text="Label" borderWidth="5" borderRadius="10" borderColor="black" padding="5" margin="5" />
<Button text="Button" borderWidth="4" borderRadius="10" borderColor="black" padding="4" margin="4" backgroundColor="orange" />
<Button text="Button" borderWidth="4" borderRadius="10" borderColor="black" padding="4" margin="4" androidElevation="0" />
<TextView text="Text View" borderWidth="3" borderRadius="10" borderColor="black" padding="3" margin="3" />
<TextField text="Text Field" borderWidth="2" borderRadius="10" borderColor="black" padding="2" margin="2" />
</StackLayout>

View File

@@ -20,9 +20,9 @@
border-color: gray;
}
.control Button {
android-elevation: 0;
border-width: 1 1 0 0;
border-color: gray;
background-color: white;
font-size: 9;
padding: 0;
height: 0;

View File

@@ -1,16 +1,16 @@
#button1 {
border-color: transparent;
android-elevation: 0;
border-color: rgba(0, 0, 0, 0);
border-width: 3;
background-color: white;
}
#button2 {
android-elevation: 0;
border-color: rgba(0, 0, 0, 0.5);
border-width: 3;
background-color: white;
}
#button3 {
android-elevation: 0;
border-width: 3;
background-color: white;
}

View File

@@ -27,5 +27,6 @@ export function loadExamples() {
examples.set("tab-view-tab-text-font-size", "tab-view/tab-view-tab-text-font-size");
examples.set("tab-view-android-swipe", "tab-view/tab-view-android-swipe");
examples.set("tab-view-icon-title-placement", "tab-view/icon-title-placement");
examples.set("tab-text-color", "tab-view/tab-text-color");
return examples;
}

View File

@@ -0,0 +1,24 @@
<Page>
<TabView tabTextColor="red">
<TabView.items>
<TabViewItem title="Tab 1">
<TabViewItem.view>
<Label text="Label 1" />
</TabViewItem.view>
</TabViewItem>
<TabViewItem title="Tab 2">
<TabViewItem.view>
<Label text="Label 2" />
</TabViewItem.view>
</TabViewItem>
<TabViewItem title="Tab 3">
<TabViewItem.view>
<Label text="Label 3" />
</TabViewItem.view>
</TabViewItem>
</TabView.items>
</TabView>
</Page>

View File

@@ -19,7 +19,6 @@
"devDependencies": {
"@types/chai": "~4.1.7",
"@types/mocha": "~5.2.5",
"@types/node": "~10.12.18",
"babel-traverse": "6.26.0",
"babel-types": "6.26.0",
"babylon": "6.18.0",

View File

@@ -1,7 +1,7 @@
import * as application from "tns-core-modules/application";
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
import { NavigatedData, Page } from "tns-core-modules/ui/page";
import { View, EventData } from "tns-core-modules/ui/core/view";
import { View, EventData, ShowModalOptions } from "tns-core-modules/ui/core/view";
import { Frame } from "tns-core-modules/ui/frame";
export function onNavigatingTo(args: NavigatedData) {
@@ -29,6 +29,20 @@ export function onModalNoPage(args: EventData) {
false);
}
export function onPopoverModal(args: EventData) {
const view = args.object as View;
let options: ShowModalOptions = {
context: "context",
closeCallback: () => console.log("home-page modal popover frame closed"),
animated: false,
ios: {
presentationStyle: UIModalPresentationStyle.Popover
}
}
view.showModal("modal-no-page/modal-no-page", options);
}
export function onModalFrame(args: EventData) {
const view = args.object as View;

View File

@@ -20,6 +20,7 @@
<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 'popover' modal" tap="onPopoverModal" 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" />

View File

@@ -3,7 +3,7 @@ import { NavigatedData, Page } from "tns-core-modules/ui/page";
import { View, EventData } from "tns-core-modules/ui/core/view";
import { Frame, ShownModallyData } from "tns-core-modules/ui/frame";
import { fromObject } from "tns-core-modules/data/observable";
import { confirm } from "ui/dialogs";
import { confirm } from "tns-core-modules/ui/dialogs";
export function onLoaded(args) {
console.log("modal-no-page loaded");

View File

@@ -3,7 +3,7 @@ import { NavigatedData, Page } from "tns-core-modules/ui/page";
import { View, EventData } from "tns-core-modules/ui/core/view";
import { Frame, ShownModallyData } from "tns-core-modules/ui/frame";
import { fromObject } from "tns-core-modules/data/observable";
import { confirm } from "ui/dialogs";
import { confirm } from "tns-core-modules/ui/dialogs";
export function onShowingModally(args: ShownModallyData) {
console.log("modal-page showingModally");

View File

@@ -19,13 +19,13 @@
"devDependencies": {
"@types/chai": "~4.1.7",
"@types/mocha": "~5.2.5",
"@types/node": "^7.0.5",
"mocha": "~5.2.0",
"mochawesome": "~3.1.2",
"nativescript-dev-appium": "next",
"nativescript-dev-typescript": "next",
"nativescript-dev-webpack": "next",
"rimraf": "^2.6.2",
"tns-platform-declarations": "next",
"typescript": "^3.1.6"
},
"scripts": {

1
e2e/modal-navigation/references.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference path="./node_modules/tns-platform-declarations/ios.d.ts" />

View File

@@ -19,7 +19,6 @@
"devDependencies": {
"@types/chai": "~4.1.7",
"@types/mocha": "~5.2.5",
"@types/node": "^7.0.5",
"mocha": "~5.2.0",
"mochawesome": "~3.1.2",
"nativescript-dev-appium": "next",

View File

@@ -80,7 +80,7 @@
"test-watch-ios": "npm run pretest && concurrently --kill-others \"npm run tsc-tiw\" \"tns livesync ios --path tests --watch\"",
"prepublish": "echo \"Development reminder for setup\n - tns-core-modules: npm run setup\n - tns-core-modules-widgets: npm run setup-widgets\n\"",
"typedoc": "typedoc --tsconfig tsconfig.typedoc.json --out bin/dist/apiref --includeDeclarations --name NativeScript --theme ./node_modules/nativescript-typedoc-theme --excludeExternals --externalPattern \"**/+(tns-core-modules|module).d.ts\"",
"dev-typedoc": "npm run typedoc && cd bin/dist/apiref && http-server",
"dev-typedoc": "npm run typedoc && cd bin/dist/apiref && npx http-server",
"test-tsc-es2016": "npm run tsc -- -p tsconfig.public.es2016.json",
"tslint": "tslint --config build/tslint.json 'tns-core-modules/**/*.ts' 'tests/**/*.ts' 'apps/**/*.ts' 'e2e/**/*.ts' -e '**/node_modules/**' -e '**/platforms/**'",
"madge-ios": "tsc --skipLibCheck && tns prepare ios --path tests && madge --circular tests/platforms/ios/tests/app/tns_modules/tns-core-modules",

View File

@@ -1,9 +1,19 @@
import * as imageCacheModule from "tns-core-modules/ui/image-cache";
import * as imageSource from "tns-core-modules/image-source";
import * as types from "tns-core-modules/utils/types";
import { device } from "tns-core-modules/platform";
import lazy from "tns-core-modules/utils/lazy";
import * as TKUnit from "../../TKUnit";
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
export const test_ImageCache_ValidUrl = function() {
// see https://github.com/NativeScript/NativeScript/issues/6643
if (sdkVersion() < 20) {
return;
}
const cache = new imageCacheModule.Cache();
cache.maxRequests = 5;

View File

@@ -162,10 +162,10 @@ export class LabelTest extends testModule.UITest<LabelModule.Label> {
const span = new Span();
span.text = "long label";
span.fontWeight = "bold";
const span2 = new Span();
span2.text = "long label";
const formattedString = new FormattedString();
formattedString.spans.push(span);
formattedString.spans.push(span2);
@@ -176,7 +176,7 @@ export class LabelTest extends testModule.UITest<LabelModule.Label> {
const span3 = new Span();
span3.text = "short label";
span3.fontWeight = "bold";
const formattedString2 = new FormattedString();
formattedString2.spans.push(span3);
label.formattedText = formattedString2;
@@ -210,7 +210,7 @@ export class LabelTest extends testModule.UITest<LabelModule.Label> {
const span = new Span();
span.text = "short label";
span.fontWeight = "bold";
const formattedString = new FormattedString();
formattedString.spans.push(span);
label.formattedText = formattedString;
@@ -220,10 +220,10 @@ export class LabelTest extends testModule.UITest<LabelModule.Label> {
const span2 = new Span();
span2.text = "long label";
span2.fontWeight = "bold";
const span3 = new Span();
span3.text = "long label";
const formattedString2 = new FormattedString();
formattedString2.spans.push(span2);
formattedString2.spans.push(span3);
@@ -733,6 +733,21 @@ export class LabelTest extends testModule.UITest<LabelModule.Label> {
return host;
});
}
public test_FormattedText_ShouldNotCrash_WheRemovedFromSpan() {
const label = this.testView;
label.color = new colorModule.Color("red");
this.waitUntilTestElementIsLoaded();
const span = new Span();
span.text = "test";
const formattedString = new FormattedString();
formattedString.spans.push(span);
label._addChildFromBuilder("FormattedString", formattedString);
label._removeView(formattedString);
};
}
export function createTestCase(): LabelTest {

View File

@@ -18,6 +18,47 @@ export function test_releaseNativeObject_canBeCalledWithNativeObject() {
}
};
export function test_executeOnMainThread_Works(done: Function) {
utils.executeOnMainThread(() => {
try {
TKUnit.assertTrue(utils.isMainThread());
done();
} catch (e) {
done(e);
}
});
}
export function test_dispatchToMainThread_Works(done: Function) {
utils.dispatchToMainThread(() => {
try {
TKUnit.assertTrue(utils.isMainThread());
done();
} catch (e) {
done(e);
}
});
}
export function test_mainThreadify_PassesArgs(done: Function) {
const expectedN = 434;
const expectedB = true;
const expectedS = "string";
const f = utils.mainThreadify(function (n: number, b: boolean, s: string) {
try {
TKUnit.assertTrue(utils.isMainThread());
TKUnit.assertEqual(n, expectedN);
TKUnit.assertEqual(b, expectedB);
TKUnit.assertEqual(s, expectedS);
done();
} catch (e) {
done(e);
}
});
f(expectedN, expectedB, expectedS);
}
function test_releaseNativeObject_canBeCalledWithNativeObject_iOS() {
let deallocated = false;
const obj = new ((<any>NSObject).extend({

View File

@@ -1,6 +1,6 @@
{
"name": "tns-core-modules-widgets",
"version": "5.4.0",
"version": "6.0.0",
"description": "Native widgets used in the NativeScript framework.",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"

View File

@@ -10,7 +10,6 @@ import {
notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent,
orientationChangedEvent, setApplication, livesync, displayedEvent, getCssFileName
} from "./application-common";
import { ModuleType } from "../ui/core/view/view-common";
// First reexport so that app module is initialized.
export * from "./application-common";
@@ -231,7 +230,7 @@ class IOSApplication implements IOSApplicationDefinition {
public _onLivesync(context?: ModuleContext): void {
// Handle application root module
const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== ModuleType.style;
const isAppRootModuleChanged = context && context.path && context.path.includes(getMainEntry().moduleName) && context.type !== "style";
// Set window content when:
// + Application root module is changed

View File

@@ -2,6 +2,7 @@ import { getNodeById } from "./dom-node";
// Needed for typings only
import { ViewBase } from "../ui/core/view-base";
import { mainThreadify } from "../utils/utils";
// Use lazy requires for core modules
const frameTopmost = () => require("../ui/frame").topmost();
@@ -11,7 +12,7 @@ function unsetViewValue(view, name) {
if (!unsetValue) {
unsetValue = require("../ui/core/properties").unsetValue;
}
view[name] = unsetValue;
}
@@ -30,10 +31,10 @@ export function getDocument() {
if (!topMostFrame) {
return undefined;
}
try {
topMostFrame.ensureDomNode();
} catch (e) {
console.log("ERROR in getDocument(): " + e);
}
@@ -49,7 +50,7 @@ export function getComputedStylesForNode(nodeId): Array<{ name: string, value: s
return [];
}
export function removeNode(nodeId) {
export const removeNode = mainThreadify(function removeNode(nodeId) {
const view = getViewById(nodeId);
if (view) {
// Avoid importing layout and content view
@@ -63,9 +64,9 @@ export function removeNode(nodeId) {
console.log("Can't remove child from " + parent);
}
}
}
});
export function setAttributeAsText(nodeId, text, name) {
export const setAttributeAsText = mainThreadify(function setAttributeAsText(nodeId, text, name) {
const view = getViewById(nodeId);
if (view) {
// attribute is registered for the view instance
@@ -93,4 +94,4 @@ export function setAttributeAsText(nodeId, text, name) {
view.domNode.loadAttributes();
}
}
});

View File

@@ -66,11 +66,7 @@ declare function clearTimeout(timeoutId: number): void;
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number;
declare function clearInterval(intervalId: number): void;
declare enum ModuleType {
markup = "markup",
script = "script",
style = "style"
}
declare type ModuleType = "markup" | "script" | "style";
/**
* Define a module context for Hot Module Replacement.

View File

@@ -1,7 +1,7 @@
{
"name": "tns-core-modules",
"description": "Telerik NativeScript Core Modules",
"version": "5.4.0",
"version": "6.0.0",
"homepage": "https://www.nativescript.org",
"repository": {
"type": "git",

View File

@@ -5,9 +5,13 @@
} from "./button-common";
import { profile } from "../../profiling";
import { TouchGestureEventData, GestureTypes, TouchAction } from "../gestures";
import { device } from "../../platform";
import lazy from "../../utils/lazy";
export * from "./button-common";
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
interface ClickListener {
new(owner: Button): android.view.View.OnClickListener;
}
@@ -151,4 +155,23 @@ export class Button extends ButtonBase {
const newValue = value === "initial" ? "center" : value;
super[textAlignmentProperty.setNative](newValue);
}
}
protected getDefaultElevation(): number {
if (sdkVersion() < 21) {
return 0;
}
// NOTE: Button widget has StateListAnimator that defines the elevation value and
// at the time of the getDefault() query the animator is not applied yet so we
// return the hardcoded @dimen/button_elevation_material value 2dp here instead
return 2;
}
protected getDefaultDynamicElevationOffset(): number {
if (sdkVersion() < 21) {
return 0;
}
return 4; // 4dp @dimen/button_pressed_z_material
}
}

View File

@@ -1018,7 +1018,7 @@ export const classNameProperty = new Property<ViewBase, string>({
valueChanged(view: ViewBase, oldValue: string, newValue: string) {
let classes = view.cssClasses;
classes.clear();
if (typeof newValue === "string") {
if (typeof newValue === "string" && newValue !== "") {
newValue.split(" ").forEach(c => classes.add(c));
}
view._onCssStateChange();

View File

@@ -21,6 +21,7 @@ import {
} from "../../gestures";
import { createViewFromEntry } from "../../builder";
import { isAndroid } from "../../../platform";
import { StyleScope } from "../../styling/style-scope";
import { LinearGradient } from "../../styling/linear-gradient";
import { BackgroundRepeat } from "../../styling/style-properties";
@@ -37,18 +38,25 @@ function ensureAnimationModule() {
}
}
export enum ModuleType {
markup = "markup",
script = "script",
style = "style"
}
export function CSSType(type: string): ClassDecorator {
return (cls) => {
cls.prototype.cssType = type;
};
}
export function viewMatchesModuleContext(
view: ViewDefinition,
context: ModuleContext,
types: ModuleType[]): boolean {
return context &&
view._moduleName &&
context.type &&
types.some(type => type === context.type) &&
context.path &&
context.path.includes(view._moduleName);
}
export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator {
const stateEventNames = pseudoClasses.map(s => ":" + s);
const listeners = Symbol("listeners");
@@ -147,62 +155,36 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
_rootModalViews.forEach(v => v.closeModal());
_rootModalViews.length = 0;
if (context && context.type && context.path) {
// Handle local styles
if (context.type === ModuleType.style) {
return this.changeLocalStyles(context.path);
}
// Handle module markup and script changes
else {
return this.changeModule(context);
}
}
return false;
}
private changeLocalStyles(contextPath: string): boolean {
if (!this.changeStyles(this, contextPath)) {
eachDescendant(this, (child: ViewBase) => {
this.changeStyles(child, contextPath);
return true;
});
}
// Do not reset activity/window content for local styles changes
return true;
}
private changeStyles(view: ViewBase, contextPath: string): boolean {
if (traceEnabled()) {
traceWrite(`${view}.${view._moduleName}`, traceCategories.Livesync);
}
if (view._moduleName && contextPath.includes(view._moduleName)) {
(<this>view).changeCssFile(contextPath);
if (this._handleLivesync(context)) {
return true;
}
return false;
}
private changeModule(context: ModuleContext): boolean {
eachDescendant(this, (child: ViewBase) => {
if (traceEnabled()) {
traceWrite(`${child}.${child._moduleName}`, traceCategories.Livesync);
let handled = false;
this.eachChildView((child) => {
if (child._onLivesync(context)) {
handled = true;
return false;
}
// Handle changes in module's Page
if (child._moduleName && context.path.includes(child._moduleName) && child.page) {
child.page._onLivesync(context);
}
return true;
});
return handled;
}
// Do not reset activity/window content for module changes
return true;
public _handleLivesync(context?: ModuleContext): boolean {
if (traceEnabled()) {
traceWrite(`${this}._handleLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
// Handle local CSS
if (viewMatchesModuleContext(this, context, ["style"])) {
if (traceEnabled()) {
traceWrite(`Change Handled: Changing CSS for ${this}`, traceCategories.Livesync);
}
this.changeCssFile(context.path);
return true;
}
return false;
}
_setupAsRootView(context: any): void {
@@ -368,7 +350,11 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
}
}
that._hideNativeModalView(parent, whenClosedCallback);
if (isAndroid || (parent.viewController && parent.viewController.presentedViewController)) {
that._hideNativeModalView(parent, whenClosedCallback);
} else {
whenClosedCallback();
}
}
};
}
@@ -697,6 +683,20 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
this.style.scaleY = value;
}
get androidElevation(): number {
return this.style.androidElevation;
}
set androidElevation(value: number) {
this.style.androidElevation = value;
}
get androidDynamicElevationOffset(): number {
return this.style.androidDynamicElevationOffset;
}
set androidDynamicElevationOffset(value: number) {
this.style.androidDynamicElevationOffset = value;
}
//END Style property shortcuts
public automationText: string;

View File

@@ -15,19 +15,27 @@ import {
minWidthProperty, minHeightProperty, widthProperty, heightProperty,
marginLeftProperty, marginTopProperty, marginRightProperty, marginBottomProperty,
rotateProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty,
zIndexProperty, backgroundInternalProperty
zIndexProperty, backgroundInternalProperty, androidElevationProperty, androidDynamicElevationOffsetProperty
} from "../../styling/style-properties";
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";
import { device } from "../../../platform";
import lazy from "../../../utils/lazy";
export * from "./view-common";
const DOMID = "_domId";
const androidBackPressedEvent = "androidBackPressed";
const shortAnimTime = 17694720; // android.R.integer.config_shortAnimTime
const statePressed = 16842919; // android.R.attr.state_pressed
const stateEnabled = 16842910; // android.R.attr.state_enabled
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
const modalMap = new Map<number, DialogOptions>();
let TouchListener: TouchListener;
@@ -255,6 +263,8 @@ export class View extends ViewCommon {
private layoutChangeListener: android.view.View.OnLayoutChangeListener;
private _manager: android.support.v4.app.FragmentManager;
private _rootManager: android.support.v4.app.FragmentManager;
private _originalElevation: number;
private _originalStateListAnimator: any; /* android.animation.StateListAnimator; */
nativeViewProtected: android.view.View;
@@ -709,6 +719,94 @@ export class View extends ViewCommon {
this.nativeViewProtected.setAlpha(float(value));
}
[androidElevationProperty.getDefault](): number {
return this.getDefaultElevation();
}
[androidElevationProperty.setNative](value: number) {
if (sdkVersion() < 21) {
return;
}
this.refreshStateListAnimator();
}
[androidDynamicElevationOffsetProperty.getDefault](): number {
return this.getDefaultDynamicElevationOffset();
}
[androidDynamicElevationOffsetProperty.setNative](value: number) {
if (sdkVersion() < 21) {
return;
}
this.refreshStateListAnimator();
}
protected getDefaultElevation(): number {
if (sdkVersion() < 21) {
return 0;
}
// NOTE: overriden in Button implementation as for widgets with StateListAnimator (Button)
// nativeView.getElevation() returns 0 at the time of the getDefault() query
return layout.toDeviceIndependentPixels((<any>this.nativeViewProtected).getElevation());
}
protected getDefaultDynamicElevationOffset() {
// NOTE: overriden in Button implementation
return 0;
}
private refreshStateListAnimator() {
const nativeView: any = this.nativeViewProtected;
const ObjectAnimator = android.animation.ObjectAnimator;
const AnimatorSet = android.animation.AnimatorSet;
const duration = nativeView.getContext().getResources().getInteger(shortAnimTime) / 2;
let elevation = this.androidElevation;
if (typeof elevation === "undefined" || elevation === null) {
elevation = this.getDefaultElevation();
}
elevation = layout.toDevicePixels(elevation);
const z = layout.toDevicePixels(0);
let pressedZ = this.androidDynamicElevationOffset;
if (typeof pressedZ === "undefined" || pressedZ === null) {
pressedZ = this.getDefaultDynamicElevationOffset();
}
pressedZ = layout.toDevicePixels(pressedZ);
const pressedSet = new AnimatorSet();
pressedSet.playTogether(java.util.Arrays.asList([
ObjectAnimator.ofFloat(nativeView, "translationZ", [pressedZ])
.setDuration(duration),
ObjectAnimator.ofFloat(nativeView, "elevation", [elevation])
.setDuration(0),
]));
const notPressedSet = new AnimatorSet();
notPressedSet.playTogether(java.util.Arrays.asList([
ObjectAnimator.ofFloat(nativeView, "translationZ", [z])
.setDuration(duration),
ObjectAnimator.ofFloat(nativeView, "elevation", [elevation])
.setDuration(0),
]));
const defaultSet = new AnimatorSet();
defaultSet.playTogether(java.util.Arrays.asList([
ObjectAnimator.ofFloat(nativeView, "translationZ", [0]).setDuration(0),
ObjectAnimator.ofFloat(nativeView, "elevation", [0]).setDuration(0),
]));
const stateListAnimator = new (<any>android.animation).StateListAnimator();
stateListAnimator.addState([statePressed, stateEnabled], pressedSet);
stateListAnimator.addState([stateEnabled], notPressedSet);
stateListAnimator.addState([], defaultSet);
nativeView.setStateListAnimator(stateListAnimator);
}
[horizontalAlignmentProperty.getDefault](): HorizontalAlignment {
return <HorizontalAlignment>org.nativescript.widgets.ViewHelper.getHorizontalAlignment(this.nativeViewProtected);
}
@@ -946,7 +1044,7 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO
const { getter, setter, auto = 0 } = options;
let setPixels, getPixels, setPercent;
if (getter) {
View.prototype[getter] = function (this: View): PercentLength {
View.prototype[getter] = function(this: View): PercentLength {
if (options) {
setPixels = options.setPixels;
getPixels = options.getPixels;
@@ -962,7 +1060,7 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO
}
}
if (setter) {
View.prototype[setter] = function (this: View, length: PercentLength) {
View.prototype[setter] = function(this: View, length: PercentLength) {
if (options) {
setPixels = options.setPixels;
getPixels = options.getPixels;

View File

@@ -32,6 +32,17 @@ export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator;
*/
export function CSSType(type: string): ClassDecorator;
/**
*
* @param view The view
* @param context The ModuleType
* @param type Type of the ModuleType to be matched
*/
export function viewMatchesModuleContext(
view: View,
context: ModuleContext,
type: ModuleType[]): boolean;
/**
* Denotes a length number that is in device independent pixel units.
*/
@@ -213,6 +224,16 @@ export abstract class View extends ViewBase {
*/
color: Color;
/**
* Gets or sets the elevation of the android view.
*/
androidElevation: number;
/**
* Gets or sets the dynamic elevation offset of the android view.
*/
androidDynamicElevationOffset: number;
/**
* Gets or sets the background style property.
*/
@@ -474,7 +495,7 @@ export abstract class View extends ViewBase {
/**
* @deprecated use on() instead
*
*
* @param type - Type of the gesture.
* @param callback - A function that will be executed when gesture is received.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
@@ -683,6 +704,10 @@ export abstract class View extends ViewBase {
* @private
*/
_onLivesync(context?: { type: string, path: string }): boolean;
/**
* @private
*/
_handleLivesync(context?: { type: string, path: string }): boolean;
/**
* @private
*/

View File

@@ -29,6 +29,7 @@ const majorVersion = iosUtils.MajorVersion;
export class View extends ViewCommon {
nativeViewProtected: UIView;
viewController: UIViewController;
private _popoverPresentationDelegate: ios.UIPopoverPresentationControllerDelegateImp;
private _isLaidOut = false;
private _hasTransfrom = false;
@@ -416,6 +417,8 @@ export class View extends ViewCommon {
if (presentationStyle === UIModalPresentationStyle.Popover) {
const popoverPresentationController = controller.popoverPresentationController;
this._popoverPresentationDelegate = ios.UIPopoverPresentationControllerDelegateImp.initWithOwnerAndCallback(new WeakRef(this), this._closeModalCallback);
popoverPresentationController.delegate = this._popoverPresentationDelegate;
const view = parent.nativeViewProtected;
// Note: sourceView and sourceRect are needed to specify the anchor location for the popover.
// Note: sourceView should be the button triggering the modal. If it the Page the popover might appear "behind" the page content
@@ -906,7 +909,7 @@ export namespace ios {
public viewDidLoad(): void {
super.viewDidLoad();
// Unify translucent and opaque bars layout
// this.edgesForExtendedLayout = UIRectEdgeBottom;
this.extendedLayoutIncludesOpaqueBars = true;
@@ -982,4 +985,26 @@ export namespace ios {
}
}
}
export class UIPopoverPresentationControllerDelegateImp extends NSObject implements UIPopoverPresentationControllerDelegate {
public static ObjCProtocols = [UIPopoverPresentationControllerDelegate];
private owner: WeakRef<View>;
private closedCallback: Function;
public static initWithOwnerAndCallback(owner: WeakRef<View>, whenClosedCallback: Function): UIPopoverPresentationControllerDelegateImp {
const instance = <UIPopoverPresentationControllerDelegateImp>super.new();
instance.owner = owner;
instance.closedCallback = whenClosedCallback;
return instance;
}
public popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
const owner = this.owner.get();
if (owner && typeof this.closedCallback === "function") {
this.closedCallback();
}
}
}
}

View File

@@ -3,12 +3,13 @@ import { Frame as FrameDefinition, NavigationEntry, BackstackEntry, NavigationTr
import { Page } from "../page";
// Types.
import { getAncestor } from "../core/view/view-common";
import { getAncestor, viewMatchesModuleContext } from "../core/view/view-common";
import { View, CustomLayoutView, isIOS, isAndroid, traceEnabled, traceWrite, traceCategories, Property, CSSType } from "../core/view";
import { createViewFromEntry } from "../builder";
import { profile } from "../../profiling";
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
import { getModuleName } from "../../utils/utils";
export * from "../core/view";
export enum NavigationType {
@@ -574,7 +575,38 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
return result;
}
public _onLivesync(): boolean {
public _onLivesync(context?: ModuleContext): boolean {
if (super._onLivesync(context)) {
return true;
}
// Fallback
if (!context) {
return this.legacyLivesync();
}
return false;
}
public _handleLivesync(context?: ModuleContext): boolean {
if (super._handleLivesync(context)) {
return true;
}
// Handle markup/script changes in currentPage
if (this.currentPage &&
viewMatchesModuleContext(this.currentPage, context, ["markup", "script"])) {
traceWrite(`Change Handled: Replacing page ${context.path}`, traceCategories.Livesync);
this.replacePage(context);
return true;
}
return false;
}
private legacyLivesync(): boolean {
// Reset activity/window content when:
// + Changes are not handled on View
// + There is no ModuleContext
@@ -609,6 +641,27 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
this.navigate(newEntry);
return true;
}
protected replacePage(context: ModuleContext): void {
// Set NavigationType.replace for HMR.
// In IOS on `viewDidAppear()` this will be set to NavigationType.forward.
this.navigationType = NavigationType.replace;
const currentBackstackEntry = this._currentEntry;
const contextModuleName = getModuleName(context.path);
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
const newBackstackEntry: BackstackEntry = {
entry: currentBackstackEntry.entry,
resolvedPage: newPage,
navDepth: currentBackstackEntry.navDepth,
fragmentTag: currentBackstackEntry.fragmentTag,
frameId: currentBackstackEntry.frameId
};
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
this.performNavigation(navContext);
}
}
export function getFrameById(id: string): FrameBase {

View File

@@ -3,7 +3,6 @@ import {
AndroidFrame as AndroidFrameDefinition, AndroidActivityCallbacks,
AndroidFragmentCallbacks, BackstackEntry, NavigationTransition
} from ".";
import { ModuleType } from "../../ui/core/view/view-common";
import { Page } from "../page";
// Types.
@@ -90,7 +89,7 @@ export function reloadPage(context?: ModuleContext): void {
if (callbacks) {
const rootView: View = callbacks.getRootView();
// Handle application root module
const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== ModuleType.style;
const isAppRootModuleChanged = context && context.path && context.path.includes(application.getMainEntry().moduleName) && context.type !== "style";
// Reset activity content when:
// + Application root module is changed
@@ -339,39 +338,6 @@ export class Frame extends FrameBase {
return false;
}
public _onLivesync(context?: ModuleContext): boolean {
if (traceEnabled()) {
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}
if (context && context.type && context.path) {
// Set NavigationType.replace for HMR.
this.navigationType = NavigationType.replace;
const currentBackstackEntry = this._currentEntry;
const contextModuleName = getModuleName(context.path);
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
const newBackstackEntry: BackstackEntry = {
entry: currentBackstackEntry.entry,
resolvedPage: newPage,
navDepth: currentBackstackEntry.navDepth,
fragmentTag: currentBackstackEntry.fragmentTag,
frameId: currentBackstackEntry.frameId
};
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
this.performNavigation(navContext);
return true;
} else {
// Fallback
return super._onLivesync();
}
}
@profile
public _navigateCore(newEntry: BackstackEntry) {
super._navigateCore(newEntry);

View File

@@ -63,39 +63,6 @@ export class Frame extends FrameBase {
}
}
public _onLivesync(context?: ModuleContext): boolean {
if (traceEnabled()) {
traceWrite(`${this}._onLivesync(${JSON.stringify(context)})`, traceCategories.Livesync);
}
if (!this._currentEntry || !this._currentEntry.entry) {
return false;
}
if (context && context.type && context.path) {
// Set NavigationType.replace for HMR.
// When `viewDidAppear()` set to NavigationType.forward.
this.navigationType = NavigationType.replace;
const currentBackstackEntry = this._currentEntry;
const contextModuleName = utils.getModuleName(context.path);
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
const newBackstackEntry: BackstackEntry = {
entry: currentBackstackEntry.entry,
resolvedPage: newPage,
navDepth: currentBackstackEntry.navDepth,
fragmentTag: undefined
}
const navContext: NavigationContext = { entry: newBackstackEntry, isBackNavigation: false };
this.performNavigation(navContext);
return true;
} else {
// Fallback
return super._onLivesync();
}
}
@profile
public _navigateCore(backstackEntry: BackstackEntry) {
// NavigationType.replace for HMR.

View File

@@ -99,10 +99,6 @@ export class PageBase extends ContentView implements PageDefinition {
};
}
public _onLivesync(context?: ModuleContext): boolean {
return this.frame ? this.frame._onLivesync(context) : false;
}
@profile
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
this._navigationContext = context;

View File

@@ -1127,3 +1127,9 @@ export const visibilityProperty = new CssProperty<Style, Visibility>({
}
});
visibilityProperty.register(Style);
export const androidElevationProperty = new CssProperty<Style, number>({ name: "androidElevation", cssName: "android-elevation", valueConverter: parseFloat });
androidElevationProperty.register(Style);
export const androidDynamicElevationOffsetProperty = new CssProperty<Style, number>({ name: "androidDynamicElevationOffset", cssName: "android-dynamic-elevation-offset", valueConverter: parseFloat });
androidDynamicElevationOffsetProperty.register(Style);

View File

@@ -92,6 +92,8 @@ export class Style extends Observable {
public fontWeight: FontWeight;
public font: string;
public androidElevation: number;
public androidDynamicElevationOffset: number;
public zIndex: number;
public opacity: number;
public visibility: Visibility;
@@ -127,13 +129,13 @@ export class Style extends Observable {
public selectedTabTextColor: Color;
public androidSelectedTabHighlightColor: Color;
// ListView-specific props
// ListView-specific props
public separatorColor: Color;
//SegmentedBar-specific props
public selectedBackgroundColor: Color;
// Page-specific props
// Page-specific props
public statusBarStyle: "light" | "dark";
public androidStatusBarBackground: Color;
@@ -167,4 +169,4 @@ interface PropertyBagClass {
}
interface PropertyBag {
[property: string]: string;
}
}

View File

@@ -65,6 +65,8 @@ export class Style extends Observable implements StyleDefinition {
public fontWeight: FontWeight;
public font: string;
public androidElevation: number;
public androidDynamicElevationOffset: number;
public zIndex: number;
public opacity: number;
public visibility: Visibility;
@@ -100,13 +102,13 @@ export class Style extends Observable implements StyleDefinition {
public selectedTabTextColor: Color;
public androidSelectedTabHighlightColor: Color;
// ListView-specific props
// ListView-specific props
public separatorColor: Color;
//SegmentedBar-specific props
public selectedBackgroundColor: Color;
// Page-specific props
// Page-specific props
public statusBarStyle: "light" | "dark";
public androidStatusBarBackground: Color;
@@ -124,4 +126,4 @@ export class Style extends Observable implements StyleDefinition {
public PropertyBag: { new(): { [property: string]: string }, prototype: { [property: string]: string } };
}
Style.prototype.PropertyBag = class { [property: string]: string; }
Style.prototype.PropertyBag = class { [property: string]: string; }

View File

@@ -576,7 +576,7 @@ function getTitleAttributesForStates(tabView: TabView): TabStates {
}
const tabSelectedItemTextColor = tabView.style.selectedTabTextColor;
const selectedTextColor = tabItemTextColor instanceof Color ? tabSelectedItemTextColor.ios : null;
const selectedTextColor = tabSelectedItemTextColor instanceof Color ? tabSelectedItemTextColor.ios : null;
result.selectedState = { [NSFontAttributeName]: font }
if (selectedTextColor) {
result.selectedState[UITextAttributeTextColor] = selectedTextColor

View File

@@ -12,7 +12,7 @@ import { isString } from "../../utils/types";
export * from "./text-base-common";
interface TextTransformation {
new (owner: TextBase): android.text.method.TransformationMethod;
new(owner: TextBase): android.text.method.TransformationMethod;
}
let TextTransformation: TextTransformation;
@@ -344,7 +344,7 @@ export function getTransformedText(text: string, textTransform: TextTransform):
}
function createSpannableStringBuilder(formattedString: FormattedString): android.text.SpannableStringBuilder {
if (!formattedString) {
if (!formattedString || !formattedString.parent) {
return null;
}

View File

@@ -231,7 +231,7 @@ export class TextBase extends TextBaseCommon {
createNSMutableAttributedString(formattedString: FormattedString): NSMutableAttributedString {
let mas = NSMutableAttributedString.alloc().init();
if (formattedString) {
if (formattedString && formattedString.parent) {
for (let i = 0, spanStart = 0, length = formattedString.spans.length; i < length; i++) {
const span = formattedString.spans.getItem(i);
const text = span.text;

View File

@@ -0,0 +1,10 @@
export function dispatchToMainThread(func: () => void) {
new android.os.Handler(android.os.Looper.getMainLooper())
.post(new java.lang.Runnable({
run: func
}));
}
export function isMainThread(): Boolean {
return android.os.Looper.myLooper() === android.os.Looper.getMainLooper();
}

View File

@@ -0,0 +1,10 @@
/**
* Dispatches the passed function for execution on the main thread
* @param func The function to execute on the main thread.
*/
export function dispatchToMainThread(func: Function);
/**
* @returns Boolean value indicating whether the current thread is the main thread
*/
export function isMainThread(): boolean

View File

@@ -0,0 +1,7 @@
export function dispatchToMainThread(func: () => void) {
NSOperationQueue.mainQueue.addOperationWithBlock(func);
}
export function isMainThread(): Boolean {
return NSThread.isMainThread;
}

View File

@@ -1,4 +1,7 @@
import * as types from "./types";
import { dispatchToMainThread, isMainThread } from "./mainthread-helper"
export * from "./mainthread-helper"
export const RESOURCE_PREFIX = "res://";
export const FILE_PREFIX = "file:///";
@@ -154,3 +157,18 @@ export function hasDuplicates(arr: Array<any>): boolean {
export function eliminateDuplicates(arr: Array<any>): Array<any> {
return Array.from(new Set(arr));
}
export function executeOnMainThread(func: Function) {
if (isMainThread()) {
return func();
} else {
dispatchToMainThread(func);
}
}
export function mainThreadify(func: Function): (...args: any[]) => void {
return function () {
const argsToPass = arguments;
executeOnMainThread(() => func.apply(this, argsToPass));
}
}

View File

@@ -4,6 +4,8 @@
import { dip, px } from "../ui/core/view";
export * from "./mainthread-helper"
export const RESOURCE_PREFIX: string;
export const FILE_PREFIX: string;
@@ -269,6 +271,22 @@ export function GC();
*/
export function releaseNativeObject(object: any /*java.lang.Object | NSObject*/);
/**
* Checks if the current thread is the main thread. Directly calls the passed function
* if it is, or dispatches it to the main thread otherwise.
* @param func The function to execute on the main thread.
*/
export function executeOnMainThread(func: Function);
/**
* Returns a function wrapper which executes the supplied function on the main thread.
* The wrapper behaves like the original function and passes all of its arguments BUT
* discards its return value.
* @param func The function to execute on the main thread
* @returns The wrapper function which schedules execution to the main thread
*/
export function mainThreadify(func: Function): (...args: any[]) => void
/**
* Returns true if the specified path points to a resource or local file.
* @param path The path.

View File

@@ -1,6 +1,6 @@
{
"name": "tns-platform-declarations",
"version": "5.4.0",
"version": "6.0.0",
"description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects",
"main": "",
"scripts": {

View File

View File

@@ -8,12 +8,13 @@ const tnsCoreModules = path.resolve(__dirname, "..", "tns-core-modules");
moduleAlias.addPath(tnsCoreModules);
moduleAlias.addAliases({
// NOTE: require("tns-core-modules/platform") with these aliases will work in node but fail in Angular AoT
// "tns-core-modules/platform": path.resolve(__dirname, "polyfills", "platform"),
// "tns-core-modules/file-system/file-system-access": path.resolve(__dirname, "polyfills", "file-system-access"),
// "tns-core-modules/utils/utils": path.resolve(tnsCoreModules, "utils/utils-common"),
// "tns-core-modules/color": path.resolve(tnsCoreModules, "color/color-common"),
// "tns-core-modules/ui/styling/font": path.resolve(tnsCoreModules, "ui/styling/font-common"),
// "tns-core-modules/ui/styling/background": path.resolve(tnsCoreModules, "ui/styling/background-common"),
"tns-core-modules/platform": path.resolve(__dirname, "polyfills", "platform"),
"tns-core-modules/file-system/file-system-access": path.resolve(__dirname, "polyfills", "file-system-access"),
"tns-core-modules/utils/utils": path.resolve(tnsCoreModules, "utils/utils-common"),
"./mainthread-helper": path.resolve(__dirname, "polyfills", "mainthread-helper"),
"tns-core-modules/color": path.resolve(tnsCoreModules, "color/color-common"),
"tns-core-modules/ui/styling/font": path.resolve(tnsCoreModules, "ui/styling/font-common"),
"tns-core-modules/ui/styling/background": path.resolve(tnsCoreModules, "ui/styling/background-common"),
"tns-core-modules": tnsCoreModules,
"~": __dirname