mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Merge remote-tracking branch 'origin/master' into release
This commit is contained in:
@@ -21,9 +21,20 @@ import { Frame, NavigationEntry } from "../ui/frame";
|
||||
import * as utils from "../utils/utils";
|
||||
import { profile, level as profilingLevel, Level } from "../profiling";
|
||||
|
||||
class Responder extends UIResponder {
|
||||
//
|
||||
}
|
||||
// NOTE: UIResponder with implementation of window - related to https://github.com/NativeScript/ios-runtime/issues/430
|
||||
// TODO: Refactor the UIResponder to use Typescript extends when this issue is resolved:
|
||||
// https://github.com/NativeScript/ios-runtime/issues/1012
|
||||
var Responder = (<any>UIResponder).extend({
|
||||
get window() {
|
||||
return iosApp ? iosApp.window : undefined;
|
||||
},
|
||||
set window(setWindow) {
|
||||
// NOOP
|
||||
}
|
||||
}, {
|
||||
protocols: [UIApplicationDelegate]
|
||||
}
|
||||
);
|
||||
|
||||
class NotificationObserver extends NSObject {
|
||||
private _onReceiveCallback: (notification: NSNotification) => void;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tns-core-modules",
|
||||
"description": "Telerik NativeScript Core Modules",
|
||||
"version": "5.1.0",
|
||||
"version": "5.2.0",
|
||||
"homepage": "https://www.nativescript.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -26,7 +26,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"typings": "tns-core-modules.d.ts",
|
||||
"dependencies": {
|
||||
"tns-core-modules-widgets": "5.1.1",
|
||||
"tns-core-modules-widgets": "next",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -46,6 +46,43 @@ export function isEventOrGesture(name: string, view: ViewBase): boolean;
|
||||
*/
|
||||
export function getViewById(view: ViewBase, id: string): ViewBase;
|
||||
|
||||
export interface ShowModalOptions {
|
||||
/**
|
||||
* Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler.
|
||||
*/
|
||||
context: any;
|
||||
|
||||
/**
|
||||
* A function that will be called when the view is closed. Any arguments provided when calling ShownModallyData.closeCallback will be available here.
|
||||
*/
|
||||
closeCallback: Function;
|
||||
|
||||
/**
|
||||
* An optional parameter specifying whether to show the modal view in full-screen mode.
|
||||
*/
|
||||
fullscreen?: boolean;
|
||||
|
||||
/**
|
||||
* An optional parameter specifying whether to show the modal view with animation.
|
||||
*/
|
||||
animated?: boolean;
|
||||
|
||||
/**
|
||||
* An optional parameter specifying whether to stretch the modal view when not in full-screen mode.
|
||||
*/
|
||||
stretched?: boolean;
|
||||
|
||||
/**
|
||||
* An optional parameter that specify options specific to iOS as an object.
|
||||
*/
|
||||
ios?: {
|
||||
/**
|
||||
* The UIModalPresentationStyle to be used when showing the dialog in iOS .
|
||||
*/
|
||||
presentationStyle: any /* UIModalPresentationStyle */
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ViewBase extends Observable {
|
||||
// Dynamic properties.
|
||||
left: Length;
|
||||
@@ -110,6 +147,7 @@ export abstract class ViewBase extends Observable {
|
||||
//@endprivate
|
||||
|
||||
/**
|
||||
* @deprecated Use showModal with ShowModalOptions instead.
|
||||
* Shows the View contained in moduleName as a modal view.
|
||||
* @param moduleName - The name of the module to load starting from the application root.
|
||||
* @param context - Any context you want to pass to the modally shown view.
|
||||
@@ -123,6 +161,7 @@ export abstract class ViewBase extends Observable {
|
||||
showModal(moduleName: string, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean): ViewBase;
|
||||
|
||||
/**
|
||||
* @deprecated Use showModal with ShowModalOptions instead.
|
||||
* Shows the view passed as parameter as a modal view.
|
||||
* @param view - View instance to be shown modally.
|
||||
* @param context - Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler.
|
||||
@@ -133,6 +172,20 @@ export abstract class ViewBase extends Observable {
|
||||
*/
|
||||
showModal(view: ViewBase, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean): ViewBase;
|
||||
|
||||
/**
|
||||
* Shows the View contained in moduleName as a modal view.
|
||||
* @param moduleName - The name of the module to load starting from the application root.
|
||||
* @param modalOptions - A ShowModalOptions instance
|
||||
*/
|
||||
showModal(moduleName: string, modalOptions: ShowModalOptions): ViewBase;
|
||||
|
||||
/**
|
||||
* Shows the view passed as parameter as a modal view.
|
||||
* @param view - View instance to be shown modally.
|
||||
* @param modalOptions - A ShowModalOptions instance
|
||||
*/
|
||||
showModal(view: ViewBase, modalOptions: ShowModalOptions): ViewBase;
|
||||
|
||||
/**
|
||||
* Deprecated. Showing view as modal is deprecated.
|
||||
* Use showModal method with arguments.
|
||||
@@ -259,7 +312,7 @@ export abstract class ViewBase extends Observable {
|
||||
public requestLayout(): void;
|
||||
|
||||
/**
|
||||
* Iterates over children of type ViewBase.
|
||||
* 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;
|
||||
@@ -291,7 +344,7 @@ export abstract class ViewBase extends Observable {
|
||||
/**
|
||||
* @private
|
||||
* Notifies each child's css state for change, recursively.
|
||||
* Either the style scope, className or id properties were changed.
|
||||
* Either the style scope, className or id properties were changed.
|
||||
*/
|
||||
_onCssStateChange(): void;
|
||||
|
||||
@@ -309,7 +362,7 @@ export abstract class ViewBase extends Observable {
|
||||
|
||||
_context: any /* android.content.Context */;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Setups the UI for ViewBase and all its children recursively.
|
||||
* This method should *not* be overridden by derived views.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
import {
|
||||
ViewBase, Property, booleanConverter, EventData, layout,
|
||||
getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
|
||||
InheritedProperty
|
||||
InheritedProperty,
|
||||
ShowModalOptions
|
||||
} from "../view-base";
|
||||
|
||||
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
|
||||
@@ -215,25 +216,41 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public showModal(): ViewDefinition {
|
||||
if (arguments.length === 0) {
|
||||
private getModalOptions(args: IArguments): { view: ViewCommon, options: ShowModalOptions } {
|
||||
if (args.length === 0) {
|
||||
throw new Error("showModal without parameters is deprecated. Please call showModal on a view instance instead.");
|
||||
} else {
|
||||
const firstAgrument = arguments[0];
|
||||
const context: any = arguments[1];
|
||||
const closeCallback: Function = arguments[2];
|
||||
const fullscreen: boolean = arguments[3];
|
||||
const animated = arguments[4];
|
||||
const stretched = arguments[5];
|
||||
let options: ShowModalOptions = null;
|
||||
|
||||
const view = firstAgrument instanceof ViewCommon
|
||||
? firstAgrument : <ViewCommon>createViewFromEntry({ moduleName: firstAgrument });
|
||||
if (args.length === 2) {
|
||||
options = <ShowModalOptions>args[1];
|
||||
} else {
|
||||
// TODO: Add deprecation warning
|
||||
options = {
|
||||
context: args[1],
|
||||
closeCallback: args[2],
|
||||
fullscreen: args[3],
|
||||
animated: args[4],
|
||||
stretched: args[5]
|
||||
};
|
||||
}
|
||||
|
||||
view._showNativeModalView(this, context, closeCallback, fullscreen, animated, stretched);
|
||||
return view;
|
||||
const firstArgument = args[0];
|
||||
const view = firstArgument instanceof ViewCommon
|
||||
? firstArgument : <ViewCommon>createViewFromEntry({ moduleName: firstArgument });
|
||||
|
||||
return { view, options };
|
||||
}
|
||||
}
|
||||
|
||||
public showModal(): ViewDefinition {
|
||||
const { view, options } = this.getModalOptions(arguments);
|
||||
|
||||
view._showNativeModalView(this, options);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public closeModal(...args) {
|
||||
let closeCallback = this._closeModalCallback;
|
||||
if (closeCallback) {
|
||||
@@ -250,12 +267,12 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
return this._modal;
|
||||
}
|
||||
|
||||
protected _showNativeModalView(parent: ViewCommon, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) {
|
||||
protected _showNativeModalView(parent: ViewCommon, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
|
||||
_rootModalViews.push(this);
|
||||
|
||||
parent._modal = this;
|
||||
this._modalParent = parent;
|
||||
this._modalContext = context;
|
||||
this._modalContext = options.context;
|
||||
const that = this;
|
||||
this._closeModalCallback = function (...originalArgs) {
|
||||
if (that._closeModalCallback) {
|
||||
@@ -268,8 +285,8 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
parent._modal = null;
|
||||
|
||||
const whenClosedCallback = () => {
|
||||
if (typeof closeCallback === "function") {
|
||||
closeCallback.apply(undefined, originalArgs);
|
||||
if (typeof options.closeCallback === "function") {
|
||||
options.closeCallback.apply(undefined, originalArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
|
||||
traceEnabled, traceWrite, traceCategories, traceNotifyEvent,
|
||||
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
|
||||
Color, EventData
|
||||
Color, EventData, ShowModalOptions
|
||||
} from "./view-common";
|
||||
|
||||
import {
|
||||
@@ -574,9 +574,8 @@ export class View extends ViewCommon {
|
||||
|
||||
return result | (childMeasuredState & layout.MEASURED_STATE_MASK);
|
||||
}
|
||||
|
||||
protected _showNativeModalView(parent: View, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) {
|
||||
super._showNativeModalView(parent, context, closeCallback, fullscreen, stretched);
|
||||
protected _showNativeModalView(parent: View, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
|
||||
super._showNativeModalView(parent, options);
|
||||
if (!this.backgroundColor) {
|
||||
this.backgroundColor = new Color("White");
|
||||
}
|
||||
@@ -590,8 +589,8 @@ export class View extends ViewCommon {
|
||||
|
||||
const dialogOptions: DialogOptions = {
|
||||
owner: this,
|
||||
fullscreen: !!fullscreen,
|
||||
stretched: !!stretched,
|
||||
fullscreen: !!options.fullscreen,
|
||||
stretched: !!options.stretched,
|
||||
shownCallback: () => this._raiseShownModallyEvent(),
|
||||
dismissCallback: () => this.closeModal()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { booleanConverter, Property } from "../view";
|
||||
|
||||
import {
|
||||
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
|
||||
traceEnabled, traceWrite, traceCategories, traceError, traceMessageType, getAncestor
|
||||
traceEnabled, traceWrite, traceCategories, traceError, traceMessageType, ShowModalOptions
|
||||
} from "./view-common";
|
||||
|
||||
import { ios as iosBackground, Background } from "../../styling/background";
|
||||
@@ -150,7 +150,7 @@ export class View extends ViewCommon {
|
||||
}
|
||||
|
||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
||||
//
|
||||
//
|
||||
}
|
||||
|
||||
public _setNativeViewFrame(nativeView: UIView, frame: CGRect): void {
|
||||
@@ -371,7 +371,7 @@ export class View extends ViewCommon {
|
||||
return this._suspendCATransaction || this._suspendNativeUpdatesCount;
|
||||
}
|
||||
|
||||
protected _showNativeModalView(parent: View, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) {
|
||||
protected _showNativeModalView(parent: View, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
|
||||
const parentWithController = ios.getParentWithViewController(parent);
|
||||
if (!parentWithController) {
|
||||
traceWrite(`Could not find parent with viewController for ${parent} while showing modal view.`,
|
||||
@@ -394,7 +394,7 @@ export class View extends ViewCommon {
|
||||
|
||||
this._setupAsRootView({});
|
||||
|
||||
super._showNativeModalView(parentWithController, context, closeCallback, fullscreen, stretched);
|
||||
super._showNativeModalView(parentWithController, options);
|
||||
let controller = this.viewController;
|
||||
if (!controller) {
|
||||
const nativeView = this.ios || this.nativeViewProtected;
|
||||
@@ -407,17 +407,31 @@ export class View extends ViewCommon {
|
||||
this.viewController = controller;
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
if (options.fullscreen) {
|
||||
controller.modalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||
} else {
|
||||
controller.modalPresentationStyle = UIModalPresentationStyle.FormSheet;
|
||||
}
|
||||
|
||||
if (options.ios && options.ios.presentationStyle) {
|
||||
const presentationStyle = options.ios.presentationStyle;
|
||||
controller.modalPresentationStyle = presentationStyle;
|
||||
|
||||
if (presentationStyle === UIModalPresentationStyle.Popover) {
|
||||
const popoverPresentationController = controller.popoverPresentationController;
|
||||
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
|
||||
popoverPresentationController.sourceView = view;
|
||||
popoverPresentationController.sourceRect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
this.horizontalAlignment = "stretch";
|
||||
this.verticalAlignment = "stretch";
|
||||
|
||||
this._raiseShowingModallyEvent();
|
||||
animated = animated === undefined ? true : !!animated;
|
||||
const animated = options.animated === undefined ? true : !!options.animated;
|
||||
(<any>controller).animated = animated;
|
||||
parentController.presentViewControllerAnimatedCompletion(controller, animated, null);
|
||||
const transitionCoordinator = iosUtils.getter(parentController, parentController.transitionCoordinator);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AndroidFragmentCallbacks, setFragmentCallbacks, setFragmentClass } from "./frame";
|
||||
|
||||
@JavaProxy("com.tns.FragmentClass")
|
||||
class FragmentClass extends android.support.v4.app.Fragment {
|
||||
class FragmentClass extends org.nativescript.widgets.FragmentBase {
|
||||
// This field is updated in the frame module upon `new` (although hacky this eases the Fragment->callbacks association a lot)
|
||||
private _callbacks: AndroidFragmentCallbacks;
|
||||
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
return global.__native(this);
|
||||
@@ -15,8 +15,7 @@ class FragmentClass extends android.support.v4.app.Fragment {
|
||||
}
|
||||
|
||||
public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator {
|
||||
let result = this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator);
|
||||
return result;
|
||||
return this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator);
|
||||
}
|
||||
|
||||
public onStop(): void {
|
||||
|
||||
@@ -86,7 +86,20 @@ export function _setAndroidFragmentTransitions(
|
||||
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : "";
|
||||
}
|
||||
|
||||
let useLollipopTransition = name && (name.indexOf("slide") === 0 || name === "fade" || name === "explode") && sdkVersion() >= 21;
|
||||
let useLollipopTransition = !!(name && (name.indexOf("slide") === 0 || name === "fade" || name === "explode") && sdkVersion() >= 21);
|
||||
// [nested frames / fragments] force disable lollipop transitions in case nested fragments
|
||||
// are detected as applying dummy animator to the nested fragment with the same duration as
|
||||
// the exit animator of the removing parent fragment as a workaround for
|
||||
// https://code.google.com/p/android/issues/detail?id=55228 works only if custom animations are
|
||||
// used
|
||||
// NOTE: this effectively means you cannot use Explode transition in nested frames scenarios as
|
||||
// we have implementations only for slide, fade, and flip
|
||||
if (currentFragment &&
|
||||
currentFragment.getChildFragmentManager() &&
|
||||
currentFragment.getChildFragmentManager().getFragments().toArray().length > 0) {
|
||||
useLollipopTransition = false;
|
||||
}
|
||||
|
||||
if (!animated) {
|
||||
name = "none";
|
||||
} else if (transition) {
|
||||
|
||||
@@ -457,8 +457,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
}
|
||||
|
||||
public _onRootViewReset(): void {
|
||||
this._removeFromFrameStack();
|
||||
super._onRootViewReset();
|
||||
this._removeFromFrameStack();
|
||||
}
|
||||
|
||||
get _childrenCount(): number {
|
||||
|
||||
@@ -209,8 +209,12 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
|
||||
public _onRootViewReset(): void {
|
||||
this.disposeCurrentFragment();
|
||||
super._onRootViewReset();
|
||||
|
||||
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||
// i.e. in a scenario with nested frames / frame with tabview let the descendandt cleanup the inner
|
||||
// fragments first, and then cleanup the parent fragments
|
||||
this.disposeCurrentFragment();
|
||||
}
|
||||
|
||||
onUnloaded() {
|
||||
@@ -223,11 +227,6 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
|
||||
private disposeCurrentFragment(): void {
|
||||
// when interacting with nested fragments it seems Android is smart enough
|
||||
// to automatically remove child fragments when parent fragment is removed;
|
||||
// however, we must add a fragment.isAdded() guard as our logic will try to
|
||||
// explicitly remove the already removed child fragment causing an
|
||||
// IllegalStateException: Fragment has not been attached yet.
|
||||
if (!this._currentEntry ||
|
||||
!this._currentEntry.fragment ||
|
||||
!this._currentEntry.fragment.isAdded()) {
|
||||
@@ -742,7 +741,13 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
}
|
||||
|
||||
@profile
|
||||
public onCreateAnimator(fragment: android.support.v4.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
|
||||
public onCreateAnimator(fragment: org.nativescript.widgets.FragmentBase, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
|
||||
// HACK: FragmentBase class MUST handle removing nested fragment scenario to workaround
|
||||
// https://code.google.com/p/android/issues/detail?id=55228
|
||||
if (!enter && fragment.getRemovingParentFragment()) {
|
||||
return superFunc.call(fragment, transit, enter, nextAnim);
|
||||
}
|
||||
|
||||
let nextAnimString: string;
|
||||
switch (nextAnim) {
|
||||
case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break;
|
||||
|
||||
@@ -45,7 +45,7 @@ function initializeNativeClasses() {
|
||||
return;
|
||||
}
|
||||
|
||||
class TabFragmentImplementation extends android.support.v4.app.Fragment {
|
||||
class TabFragmentImplementation extends org.nativescript.widgets.FragmentBase {
|
||||
private tab: TabView;
|
||||
private index: number;
|
||||
|
||||
@@ -55,7 +55,6 @@ function initializeNativeClasses() {
|
||||
}
|
||||
|
||||
static newInstance(tabId: number, index: number): TabFragmentImplementation {
|
||||
|
||||
const args = new android.os.Bundle();
|
||||
args.putInt(TABID, tabId);
|
||||
args.putInt(INDEX, index);
|
||||
@@ -79,10 +78,6 @@ function initializeNativeClasses() {
|
||||
|
||||
return tabItem.view.nativeViewProtected;
|
||||
}
|
||||
|
||||
public onDestroyView() {
|
||||
super.onDestroyView();
|
||||
}
|
||||
}
|
||||
|
||||
const POSITION_UNCHANGED = -1;
|
||||
@@ -560,8 +555,13 @@ export class TabView extends TabViewBase {
|
||||
}
|
||||
|
||||
public _onRootViewReset(): void {
|
||||
this.disposeCurrentFragments();
|
||||
super._onRootViewReset();
|
||||
|
||||
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||
// i.e. in a scenario with tab frames let the frames cleanup their fragments first, and then
|
||||
// cleanup the tab fragments to avoid
|
||||
// android.content.res.Resources$NotFoundException: Unable to find resource ID #0xfffffff6
|
||||
this.disposeCurrentFragments();
|
||||
}
|
||||
|
||||
private disposeCurrentFragments(): void {
|
||||
|
||||
Reference in New Issue
Block a user