From 89ee60be003443ce7de3a38fb8c33b2ad58a1ea3 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Wed, 18 Mar 2020 16:46:09 +0200 Subject: [PATCH 01/26] chore: bump version to 6.6.0 --- e2e/scoped-packages/package.json | 48 +++++++++++++------------- nativescript-core/package.json | 2 +- tns-core-modules-package/package.json | 2 +- tns-core-modules-widgets/package.json | 2 +- tns-platform-declarations/package.json | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/e2e/scoped-packages/package.json b/e2e/scoped-packages/package.json index e48c642ff6..bd4a487aa7 100644 --- a/e2e/scoped-packages/package.json +++ b/e2e/scoped-packages/package.json @@ -1,26 +1,26 @@ { - "nativescript": { - "id": "org.nativescript.scopedpackages", - "tns-android": { - "version": "6.1.0" + "nativescript": { + "id": "org.nativescript.scopedpackages", + "tns-android": { + "version": "6.1.0" + }, + "tns-ios": { + "version": "6.1.0" + } }, - "tns-ios": { - "version": "6.1.0" - } - }, - "main": "app.js", - "description": "NativeScript Application", - "license": "SEE LICENSE IN ", - "repository": "", - "dependencies": { - "@nativescript/core": "file:../../dist/nativescript-core-6.5.0.tgz", - "nativescript-theme-core": "~1.0.6", - "tns-core-modules": "file:../../dist/tns-core-modules-6.5.0.tgz" - }, - "devDependencies": { - "nativescript-dev-webpack": "~1.2.0", - "typescript": "~3.5.3" - }, - "gitHead": "5fa096231ded5351b53700f0d111d60160e52ebc", - "readme": "NativeScript Application" -} + "main": "app.js", + "description": "NativeScript Application", + "license": "SEE LICENSE IN ", + "repository": "", + "dependencies": { + "@nativescript/core": "file:../../dist/nativescript-core-6.6.0.tgz", + "nativescript-theme-core": "~1.0.6", + "tns-core-modules": "file:../../dist/tns-core-modules-6.6.0.tgz" + }, + "devDependencies": { + "nativescript-dev-webpack": "~1.2.0", + "typescript": "~3.5.3" + }, + "gitHead": "5fa096231ded5351b53700f0d111d60160e52ebc", + "readme": "NativeScript Application" +} \ No newline at end of file diff --git a/nativescript-core/package.json b/nativescript-core/package.json index 0dbf2e07a2..bf97c5caed 100644 --- a/nativescript-core/package.json +++ b/nativescript-core/package.json @@ -3,7 +3,7 @@ "main": "index", "types": "index.d.ts", "description": "Telerik NativeScript Core Modules", - "version": "6.5.0", + "version": "6.6.0", "homepage": "https://www.nativescript.org", "repository": { "type": "git", diff --git a/tns-core-modules-package/package.json b/tns-core-modules-package/package.json index 6f6f124b15..07e31a4c91 100644 --- a/tns-core-modules-package/package.json +++ b/tns-core-modules-package/package.json @@ -3,7 +3,7 @@ "main": "index", "types": "index.d.ts", "description": "Telerik NativeScript Core Modules", - "version": "6.5.0", + "version": "6.6.0", "homepage": "https://www.nativescript.org", "repository": { "type": "git", diff --git a/tns-core-modules-widgets/package.json b/tns-core-modules-widgets/package.json index f2f570228a..1af78f076f 100644 --- a/tns-core-modules-widgets/package.json +++ b/tns-core-modules-widgets/package.json @@ -1,6 +1,6 @@ { "name": "tns-core-modules-widgets", - "version": "6.5.0", + "version": "6.6.0", "description": "Native widgets used in the NativeScript framework.", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/tns-platform-declarations/package.json b/tns-platform-declarations/package.json index e4ef21b7d4..710b2a5960 100644 --- a/tns-platform-declarations/package.json +++ b/tns-platform-declarations/package.json @@ -1,6 +1,6 @@ { "name": "tns-platform-declarations", - "version": "6.5.0", + "version": "6.6.0", "description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects", "main": "", "scripts": { From 4a67a3b73f7097c3bd0f659252fb9f64ad0df217 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Thu, 19 Mar 2020 10:00:29 +0100 Subject: [PATCH 02/26] fix: dont create an actionbar if not necessary (#8402) * fix: dont create an actionbar if not necessary For now i kept the commented code so that you can see the change. Also i changed the android check to behave like iOS * rollback. we now try an make sure the actionbar is created only if needed * actually we should check for false --- nativescript-core/ui/page/page-common.ts | 2 +- nativescript-core/ui/page/page.android.ts | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/nativescript-core/ui/page/page-common.ts b/nativescript-core/ui/page/page-common.ts index 6d69e32d4c..96418f5d03 100644 --- a/nativescript-core/ui/page/page-common.ts +++ b/nativescript-core/ui/page/page-common.ts @@ -138,7 +138,7 @@ export class PageBase extends ContentView implements PageDefinition { public eachChildView(callback: (child: View) => boolean) { super.eachChildView(callback); - if (this.actionBar) { + if (this.hasActionBar) { callback(this.actionBar); } } diff --git a/nativescript-core/ui/page/page.android.ts b/nativescript-core/ui/page/page.android.ts index 39c77f4154..715af532fa 100644 --- a/nativescript-core/ui/page/page.android.ts +++ b/nativescript-core/ui/page/page.android.ts @@ -45,17 +45,30 @@ export class Page extends PageBase { @profile public onLoaded() { super.onLoaded(); - if (this.actionBarHidden !== undefined) { + if (!this.hasActionBar && this.actionBarHidden !== true) { + // ensure actionBar is created + // but we only need to do that if the actionBarHidden is not hidden + this.actionBar = new ActionBar(); + } + if (this.hasActionBar) { this.updateActionBar(); } } private updateActionBar() { - this.actionBar.update(); + // the test is actually to ensure the actionBar is created + // it will be created if not + if (this.actionBar) { + this.actionBar.update(); + } } [actionBarHiddenProperty.setNative](value: boolean) { - this.updateActionBar(); + // in case the actionBar is not created and actionBarHidden is changed to true + // the actionBar will be created by updateActionBar + if (!value || this.hasActionBar) { + this.updateActionBar(); + } } [statusBarStyleProperty.getDefault](): { color: number, systemUiVisibility: number } { From 8dbb623944a600414fa072d069995a9edc839903 Mon Sep 17 00:00:00 2001 From: Sam Donald <32623552+mudlabs@users.noreply.github.com> Date: Thu, 19 Mar 2020 21:51:23 +1100 Subject: [PATCH 03/26] fix: update tap event data (#8415) * Update tap event data object Adds a: - TabGestureEventData interface, which can be used for both tap and doubleTap events. - The event object returned by both tap and doubleTap events now have getX(), getY(), and getPointersCount() methods. These facilitate the same function as those of the touch event object. * ui(gesture): getX,getY in DIP Updates the getX() and getY() methods of Tap, doubleTap and touch events to return DIP instead of DP. * ui(gesture): tap event data includes location Tap and doubleTap event data now include getX and getY methods for event location. These are in DIP format. getPointerCount is also available. * Fix tslint errors * fix minor formatting issues for api-extrector --- api-reports/NativeScript.api.md | 87 +-- .../ui/gestures/gestures.android.ts | 42 +- nativescript-core/ui/gestures/gestures.d.ts | 614 +++++++++--------- nativescript-core/ui/gestures/gestures.ios.ts | 43 +- nativescript-core/ui/index.d.ts | 2 +- nativescript-core/ui/index.ts | 3 +- 6 files changed, 405 insertions(+), 386 deletions(-) diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index 4689936ade..d8c543299e 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -870,17 +870,17 @@ export class Frame extends View { // @public export interface GestureEventData extends EventData { - android: any - ios: any /* UIGestureRecognizer */; - type: GestureTypes; - view: View; -} + android: any + ios: any /* UIGestureRecognizer */; + type: GestureTypes; + view: View; + } // @public export interface GestureEventDataWithState extends GestureEventData { // (undocumented) state: number; -} + } // @public export class GesturesObserver { @@ -897,7 +897,7 @@ export class GesturesObserver { observe(type: GestureTypes); type: GestureTypes; -} + } // @public export enum GestureStateTypes { @@ -905,7 +905,7 @@ export enum GestureStateTypes { cancelled, changed, ended -} + } // @public export enum GestureTypes { @@ -917,7 +917,7 @@ export enum GestureTypes { swipe, tap, touch -} + } // @public export class GridLayout extends LayoutBase { @@ -1011,49 +1011,49 @@ export const Http: { // @public export interface HttpContent { - raw: any; + raw: any; - toArrayBuffer: () => ArrayBuffer; + toArrayBuffer: () => ArrayBuffer; - toFile: (destinationFilePath?: string) => File; + toFile: (destinationFilePath?: string) => File; - toImage: () => Promise; + toImage: () => Promise; - toJSON: (encoding?: HttpResponseEncoding) => any; + toJSON: (encoding?: HttpResponseEncoding) => any; - toString: (encoding?: HttpResponseEncoding) => string; + toString: (encoding?: HttpResponseEncoding) => string; } // @public export interface HttpRequestOptions { - content?: string | FormData | ArrayBuffer; + content?: string | FormData | ArrayBuffer; - dontFollowRedirects?: boolean; + dontFollowRedirects?: boolean; - headers?: any; + headers?: any; - method: string; + method: string; - timeout?: number; + timeout?: number; - url: string; + url: string; } // @public export interface HttpResponse { - content?: HttpContent; + content?: HttpContent; - headers: Headers; + headers: Headers; - statusCode: number; + statusCode: number; } // @public (undocumented) export enum HttpResponseEncoding { - // (undocumented) - GBK, - // (undocumented) - UTF8 + // (undocumented) + GBK, + // (undocumented) + UTF8 } // @public @@ -1646,7 +1646,7 @@ export interface PanGestureEventData extends GestureEventDataWithState { deltaX: number; // (undocumented) deltaY: number; -} + } // @public export interface ParserEvent { @@ -1702,7 +1702,7 @@ export interface PinchGestureEventData extends GestureEventDataWithState { // (undocumented) scale: number; -} + } // @public export class Placeholder extends View { @@ -1767,7 +1767,7 @@ export class Repeater extends CustomLayoutView { export interface RotationGestureEventData extends GestureEventDataWithState { // (undocumented) rotation: number; -} + } // @public export module Screen { @@ -2172,13 +2172,13 @@ export enum SwipeDirection { left, right, up -} + } // @public export interface SwipeGestureEventData extends GestureEventData { // (undocumented) direction: SwipeDirection; -} + } // @public export class Switch extends View { @@ -2417,6 +2417,15 @@ export class TabViewItem extends ViewBase { public view: View; } +// @public +export interface TapGestureEventData extends GestureEventData { + getPointerCount(): number; + + getX(): number; + + getY(): number; + } + // @public export interface Template { (): View; @@ -2536,20 +2545,12 @@ export interface TimerInfo { } // @public -export interface TouchGestureEventData extends GestureEventData { +export interface TouchGestureEventData extends TapGestureEventData { action: "up" | "move" | "down" | "cancel"; - // Warning: (ae-forgotten-export) The symbol "Pointer" needs to be exported by the entry point index.d.ts getActivePointers(): Array; - getAllPointers(): Array; - - getPointerCount(): number; - - getX(): number; - - getY(): number; -} + } // @public (undocumented) export const Trace: { diff --git a/nativescript-core/ui/gestures/gestures.android.ts b/nativescript-core/ui/gestures/gestures.android.ts index d90171f0d6..f4c22343ff 100644 --- a/nativescript-core/ui/gestures/gestures.android.ts +++ b/nativescript-core/ui/gestures/gestures.android.ts @@ -1,5 +1,5 @@ // Definitions. -import { DoubleTapGestureEventData, GestureEventData, GestureEventDataWithState, PanGestureEventData, RotationGestureEventData, SwipeGestureEventData } from "."; +import { GestureEventData, TapGestureEventData, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, GestureEventDataWithState } from "."; import { View, EventData } from "../core/view"; // Types. @@ -71,14 +71,14 @@ function initializeTapAndDoubleTapGestureListener() { if (this._target.getGestureObservers(GestureTypes.doubleTap)) { this._tapTimeoutId = timer.setTimeout(() => { if (this._type & GestureTypes.tap) { - const args = _getArgs(GestureTypes.tap, this._target, motionEvent); + const args = _getTapArgs(GestureTypes.tap, this._target, motionEvent); _executeCallback(this._observer, args); } timer.clearTimeout(this._tapTimeoutId); }, TapAndDoubleTapGestureListenerImpl.DoubleTapTimeout); } else { if (this._type & GestureTypes.tap) { - const args = _getArgs(GestureTypes.tap, this._target, motionEvent); + const args = _getTapArgs(GestureTypes.tap, this._target, motionEvent); _executeCallback(this._observer, args); } } @@ -89,7 +89,7 @@ function initializeTapAndDoubleTapGestureListener() { timer.clearTimeout(this._tapTimeoutId); } if (this._type & GestureTypes.doubleTap) { - const args = _getDoubleTapArgs(this._target, motionEvent); + const args = _getTapArgs(GestureTypes.doubleTap, this._target, motionEvent); _executeCallback(this._observer, args); } } @@ -372,15 +372,18 @@ export class GesturesObserver extends GesturesObserverBase { } } -function _getArgs(type: GestureTypes, view: View, e: android.view.MotionEvent): GestureEventData { - return { - type: type, - view: view, - android: e, - ios: undefined, - object: view, - eventName: toString(type), - }; +function _getTapArgs(type: GestureTypes, view: View, e: android.view.MotionEvent): TapGestureEventData { + return { + type: type, + view: view, + android: e, + ios: undefined, + object: view, + eventName: toString(type), + getPointerCount: () => e.getPointerCount(), + getX: () => layout.toDeviceIndependentPixels(e.getX()), + getY: () => layout.toDeviceIndependentPixels(e.getY()) + }; } function _getLongPressArgs(type: GestureTypes, view: View, state: GestureStateTypes, e: android.view.MotionEvent): GestureEventDataWithState { @@ -395,19 +398,6 @@ function _getLongPressArgs(type: GestureTypes, view: View, state: GestureStateTy }; } -function _getDoubleTapArgs(view: View, e: android.view.MotionEvent): DoubleTapGestureEventData { - return { - type: GestureTypes.doubleTap, - view: view, - android: e, - getX: () => e.getX() / layout.getDisplayDensity(), - getY: () => e.getY() / layout.getDisplayDensity(), - ios: undefined, - object: view, - eventName: toString(GestureTypes.doubleTap), - }; -} - function _getSwipeArgs(direction: SwipeDirection, view: View, initialEvent: android.view.MotionEvent, currentEvent: android.view.MotionEvent): SwipeGestureEventData { return { diff --git a/nativescript-core/ui/gestures/gestures.d.ts b/nativescript-core/ui/gestures/gestures.d.ts index 7c66d18ff9..1527699726 100644 --- a/nativescript-core/ui/gestures/gestures.d.ts +++ b/nativescript-core/ui/gestures/gestures.d.ts @@ -1,309 +1,329 @@ -/** +/** * Contains the GesturesObserver class, which lets you observe and respond to user gestures. * @module "ui/gestures" */ /** */ -import { View, EventData } from "../core/view"; - -/** - * Defines an enum with supported gesture types. - */ -export enum GestureTypes { - /** - * Denotes tap (click) gesture. - */ - tap, - /** - * Denotes double tap gesture. - */ - doubleTap, - /** - * Denotes pinch gesture. - */ - pinch, - /** - * Denotes pan gesture. - */ - pan, - /** - * Denotes swipe gesture. - */ - swipe, - /** - * Denotes rotation gesture. - */ - rotation, - /** - * Denotes long press gesture. - */ - longPress, - /** - * Denotes touch action. - */ - touch -} - -/** - * Defines an enum with supported gesture states. - */ -export enum GestureStateTypes { - /** - * Gesture canceled. - */ - cancelled, - /** - * Gesture began. - */ - began, - /** - * Gesture changed. - */ - changed, - /** - * Gesture ended. - */ - ended -} - -/** - * Defines an enum for swipe gesture direction. - */ -export enum SwipeDirection { - /** - * Denotes right direction for swipe gesture. - */ - right, - /** - * Denotes left direction for swipe gesture. - */ - left, - /** - * Denotes up direction for swipe gesture. - */ - up, - /** - * Denotes down direction for swipe gesture. - */ - down -} - -/** - * Defines a touch action - */ -export module TouchAction { - /** - * Down action. - */ - export const down: string; - - /** - * Up action. - */ - export const up: string; - - /** - * Move action. - */ - export const move: string; - - /** - * Cancel action. - */ - export const cancel: string; -} - -/** - * Provides gesture event data. - */ -export interface GestureEventData extends EventData { - /** - * Gets the type of the gesture. - */ - type: GestureTypes; - /** - * Gets the view which originates the gesture. - */ - view: View; - /** - * Gets the underlying native iOS specific [UIGestureRecognizer](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/). - */ - ios: any /* UIGestureRecognizer */; - /** - * Gets the underlying native android specific [gesture detector](http://developer.android.com/reference/android/view/GestureDetector.html). - */ - android: any -} - -/** - * Provides gesture event data. - */ -export interface TouchGestureEventData extends GestureEventData { - /** - * Gets action of the touch. Possible values: 'up', 'move', 'down', 'cancel' - */ - action: "up" | "move" | "down" | "cancel"; - - /** - * Gets the X coordinate of this event inside the view that triggered the event. - */ - getX(): number; - - /** - * Gets the Y coordinate of this event inside the view that triggered the event. - */ - getY(): number; - - /** - * Gets the number of pointers in the event. - */ + import { View, EventData } from "../core/view"; + + /** + * Defines an enum with supported gesture types. + */ + export enum GestureTypes { + /** + * Denotes tap (click) gesture. + */ + tap, + /** + * Denotes double tap gesture. + */ + doubleTap, + /** + * Denotes pinch gesture. + */ + pinch, + /** + * Denotes pan gesture. + */ + pan, + /** + * Denotes swipe gesture. + */ + swipe, + /** + * Denotes rotation gesture. + */ + rotation, + /** + * Denotes long press gesture. + */ + longPress, + /** + * Denotes touch action. + */ + touch + } + + /** + * Defines an enum with supported gesture states. + */ + export enum GestureStateTypes { + /** + * Gesture canceled. + */ + cancelled, + /** + * Gesture began. + */ + began, + /** + * Gesture changed. + */ + changed, + /** + * Gesture ended. + */ + ended + } + + /** + * Defines an enum for swipe gesture direction. + */ + export enum SwipeDirection { + /** + * Denotes right direction for swipe gesture. + */ + right, + /** + * Denotes left direction for swipe gesture. + */ + left, + /** + * Denotes up direction for swipe gesture. + */ + up, + /** + * Denotes down direction for swipe gesture. + */ + down + } + + /** + * Defines a touch action + */ + export module TouchAction { + /** + * Down action. + */ + export const down: string; + + /** + * Up action. + */ + export const up: string; + + /** + * Move action. + */ + export const move: string; + + /** + * Cancel action. + */ + export const cancel: string; + } + + /** + * Provides gesture event data. + */ + export interface GestureEventData extends EventData { + /** + * Gets the type of the gesture. + */ + type: GestureTypes; + /** + * Gets the view which originates the gesture. + */ + view: View; + /** + * Gets the underlying native iOS specific [UIGestureRecognizer](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/). + */ + ios: any /* UIGestureRecognizer */; + /** + * Gets the underlying native android specific [gesture detector](http://developer.android.com/reference/android/view/GestureDetector.html). + */ + android: any + } + + /** + * Provides gesture event data. + */ + export interface TapGestureEventData extends GestureEventData { + /** + * Gets the number of pointers in the event. + */ getPointerCount(): number; - - /** - * Gets the pointers that triggered the event. - * Note: In Android there is aways only one active pointer. - */ - getActivePointers(): Array; - - /** - * Gets all pointers. - */ - getAllPointers(): Array; -} - -/** - * Pointer is an object representing a finger (or other object) that is touching the screen. - */ -export interface Pointer { /** - * The id of the pointer. - */ - android: any; - - /** - * The UITouch object associated to the touch - */ - ios: any; - - /** - * Gets the X coordinate of the pointer inside the view that triggered the event. + * Gets the X coordinate of this event inside the view that triggered the event */ getX(): number; - /** - * Gets the Y coordinate of the pointer inside the view that triggered the event. + * Gets the Y coordinate of the event inside the view that triggered the event. */ getY(): number; -} - -/** - * Provides gesture event data. - */ -export interface GestureEventDataWithState extends GestureEventData { - state: number; -} - -/** - * Provides gesture event data for pinch gesture. - */ -export interface PinchGestureEventData extends GestureEventDataWithState { - scale: number; - - getFocusX(): number; - getFocusY(): number; -} - -/** - * Provides gesture event data for double tap gesture. - */ -export interface DoubleTapGestureEventData extends GestureEventData { - getX(): number; - getY(): number; -} -/** - * Provides gesture event data for swipe gesture. - */ -export interface SwipeGestureEventData extends GestureEventData { - direction: SwipeDirection; -} - -/** - * Provides gesture event data for pan gesture. - */ -export interface PanGestureEventData extends GestureEventDataWithState { - deltaX: number; - deltaY: number; -} - -/** - * Provides gesture event data for rotation gesture. - */ -export interface RotationGestureEventData extends GestureEventDataWithState { - rotation: number; -} - -/** - * Provides options for the GesturesObserver. - */ -export class GesturesObserver { - /** - * Creates an instance of GesturesObserver class. - * @param target - The view for which the observer is created. - * @param callback - A function that will be executed when a gesture is received. - * @param context - default this argument for the callbacks. - */ - constructor(target: View, callback: (args: GestureEventData) => void, context: any); - - /** - * Registers a gesture observer to a view and gesture. - * @param type - Type of the gesture. - */ - observe(type: GestureTypes); - - /** - * Disconnects the gesture observer. - */ - disconnect(); - - /** - * Gesture type attached to the observer. - */ - type: GestureTypes; - - /** - * A function that will be executed when a gesture is received. - */ - callback: (args: GestureEventData) => void; - - /** - * A context which will be used as `this` in callback execution. - */ - context: any; - - /** - * An internal Android specific method used to pass the motion event to the correct gesture observer. - */ - androidOnTouchEvent: (motionEvent: any /* android.view.MotionEvent */) => void; -} - -/** - * A short-hand function that is used to create a gesture observer for a view and gesture. - * @param target - View which will be watched for originating a specific gesture. - * @param type - Type of the gesture. - * @param callback - A function that will be executed when a gesture is received. - * @param context - this argument for the callback. - */ -export function observe(target: View, type: GestureTypes, callback: (args: GestureEventData) => void, context?: any): GesturesObserver; - -/** - * Returns a string representation of a gesture type. - * @param type - Type of the gesture. - * @param separator(optional) - Text separator between gesture type strings. - */ -export function toString(type: GestureTypes, separator?: string): string; - -/** - * Returns a gesture type enum value from a string (case insensitive). - * @param type - A string representation of a gesture type (e.g. Tap). - */ -export function fromString(type: string): GestureTypes; + } + + /** + * Provides gesture event data. + */ + export interface TouchGestureEventData extends TapGestureEventData { + /** + * Gets action of the touch. Possible values: 'up', 'move', 'down', 'cancel' + */ + action: "up" | "move" | "down" | "cancel"; + /** + * Gets the pointers that triggered the event. + * Note: In Android there is aways only one active pointer. + */ + getActivePointers(): Array; + + /** + * Gets all pointers. + */ + getAllPointers(): Array; + } + + /** + * Pointer is an object representing a finger (or other object) that is touching the screen. + */ + export interface Pointer { + /** + * The id of the pointer. + */ + android: any; + + /** + * The UITouch object associated to the touch + */ + ios: any; + + /** + * Gets the X coordinate of the pointer inside the view that triggered the event. + */ + getX(): number; + + /** + * Gets the Y coordinate of the pointer inside the view that triggered the event. + */ + getY(): number; + + /** + * Gests the X coordinate of the pointer inside the view that triggered the event. + * @returns The X coordinate in _Device Pixels_. + */ + getXPixels(): number + + /** + * Gets the X coordinate of the pointer inside the view that triggered the event. + * @returns The X coordinate in _Device Independent Pixels_. + */ + getXDIP(): number + + /** + * Gests the Y coordinate of the pointer inside the view that triggered the event. + * @returns The Y coordinate in _Device Pixels_. + */ + getYPixels(): number + + /** + * Gets the Y coordinate of the pointer inside the view that triggered the event. + * @returns The Y coordinate in _Device Independent Pixels_. + */ + getYDIP(): number + } + + /** + * Provides gesture event data. + */ + export interface GestureEventDataWithState extends GestureEventData { + state: number; + } + + /** + * Provides gesture event data for pinch gesture. + */ + export interface PinchGestureEventData extends GestureEventDataWithState { + scale: number; + + getFocusX(): number; + getFocusY(): number; + } + + /** + * Provides gesture event data for swipe gesture. + */ + export interface SwipeGestureEventData extends GestureEventData { + direction: SwipeDirection; + } + + /** + * Provides gesture event data for pan gesture. + */ + export interface PanGestureEventData extends GestureEventDataWithState { + deltaX: number; + deltaY: number; + } + + /** + * Provides gesture event data for rotation gesture. + */ + export interface RotationGestureEventData extends GestureEventDataWithState { + rotation: number; + } + + /** + * Provides options for the GesturesObserver. + */ + export class GesturesObserver { + /** + * Creates an instance of GesturesObserver class. + * @param target - The view for which the observer is created. + * @param callback - A function that will be executed when a gesture is received. + * @param context - default this argument for the callbacks. + */ + constructor(target: View, callback: (args: GestureEventData) => void, context: any); + + /** + * Registers a gesture observer to a view and gesture. + * @param type - Type of the gesture. + */ + observe(type: GestureTypes); + + /** + * Disconnects the gesture observer. + */ + disconnect(); + + /** + * Gesture type attached to the observer. + */ + type: GestureTypes; + + /** + * A function that will be executed when a gesture is received. + */ + callback: (args: GestureEventData) => void; + + /** + * A context which will be used as `this` in callback execution. + */ + context: any; + + /** + * An internal Android specific method used to pass the motion event to the correct gesture observer. + */ + androidOnTouchEvent: (motionEvent: any /* android.view.MotionEvent */) => void; + } + + /** + * A short-hand function that is used to create a gesture observer for a view and gesture. + * @param target - View which will be watched for originating a specific gesture. + * @param type - Type of the gesture. + * @param callback - A function that will be executed when a gesture is received. + * @param context - this argument for the callback. + */ + export function observe(target: View, type: GestureTypes, callback: (args: GestureEventData) => void, context?: any): GesturesObserver; + + /** + * Returns a string representation of a gesture type. + * @param type - Type of the gesture. + * @param separator(optional) - Text separator between gesture type strings. + */ + export function toString(type: GestureTypes, separator?: string): string; + + /** + * Returns a gesture type enum value from a string (case insensitive). + * @param type - A string representation of a gesture type (e.g. Tap). + */ + export function fromString(type: string): GestureTypes; + \ No newline at end of file diff --git a/nativescript-core/ui/gestures/gestures.ios.ts b/nativescript-core/ui/gestures/gestures.ios.ts index 2f36f938f9..61b2fb8de7 100644 --- a/nativescript-core/ui/gestures/gestures.ios.ts +++ b/nativescript-core/ui/gestures/gestures.ios.ts @@ -1,10 +1,14 @@ // Definitions. -import { DoubleTapGestureEventData, GestureEventData, GestureEventDataWithState, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, PinchGestureEventData } from "."; + +import { GestureEventData, TapGestureEventData, GestureEventDataWithState, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, PinchGestureEventData } from "."; import { View, EventData } from "../core/view"; // Types. import { GesturesObserverBase, toString, TouchAction, GestureStateTypes, GestureTypes, SwipeDirection } from "./gestures-common"; +// Import layout from utils directly to avoid circular references +import { layout } from "../../utils/utils"; + export * from "./gestures-common"; export function observe(target: View, type: GestureTypes, callback: (args: GestureEventData) => void, context?: any): GesturesObserver { @@ -123,12 +127,14 @@ export class GesturesObserver extends GesturesObserverBase { const nativeView = target.nativeViewProtected; if (type & GestureTypes.tap) { - nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.tap)); + nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.tap, args => { + this._executeCallback(_getTapData(args)); + })); } if (type & GestureTypes.doubleTap) { nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.doubleTap, args => { - this._executeCallback(_getDoubleTapData(args)); + this._executeCallback(_getTapData(args)); })); } @@ -300,20 +306,21 @@ function _getSwipeDirection(direction: UISwipeGestureRecognizerDirection): Swipe } } -function _getDoubleTapData(args: GestureEventData): DoubleTapGestureEventData { - const recognizer = args.ios; - const location: CGPoint = recognizer.locationInView(args.view.nativeViewProtected); - - return { - type: args.type, - view: args.view, - ios: args.ios, - android: undefined, - getX: () => location.x, - getY: () => location.y, - object: args.view, - eventName: toString(args.type) - }; +function _getTapData(args: GestureEventData): TapGestureEventData { + const recognizer = args.ios; + const center = recognizer.locationInView(args.view.nativeViewProtected); + + return { + type: args.type, + view: args.view, + ios: args.ios, + android: undefined, + eventName: args.eventName, + object: args.object, + getPointerCount: () => recognizer.numberOfTouches, + getX: () => layout.toDeviceIndependentPixels(center.x), + getY: () => layout.toDeviceIndependentPixels(center.y) + }; } function _getPinchData(args: GestureEventData): PinchGestureEventData { @@ -461,7 +468,7 @@ class Pointer implements Pointer { } getY(): number { - return this.location.y; + return this.location.y; } } diff --git a/nativescript-core/ui/index.d.ts b/nativescript-core/ui/index.d.ts index ac5934ff0f..5a2cd5c596 100644 --- a/nativescript-core/ui/index.d.ts +++ b/nativescript-core/ui/index.d.ts @@ -10,7 +10,7 @@ export { View, Template, KeyedTemplate, ShownModallyData } from "./core/view"; export { DatePicker } from "./date-picker"; export { EditableTextBase } from "./editable-text-base"; export { Frame, NavigationEntry, NavigationContext, NavigationTransition, BackstackEntry, ViewEntry } from "./frame"; -export { GestureEventData, GestureEventDataWithState, GestureStateTypes, GestureTypes, GesturesObserver, PanGestureEventData, PinchGestureEventData, RotationGestureEventData, SwipeDirection, SwipeGestureEventData, TouchGestureEventData } from "./gestures"; +export { GestureEventData, GestureEventDataWithState, GestureStateTypes, GestureTypes, GesturesObserver, TapGestureEventData, PanGestureEventData, PinchGestureEventData, RotationGestureEventData, SwipeDirection, SwipeGestureEventData, TouchGestureEventData } from "./gestures"; export { HtmlView } from "./html-view"; export { Image } from "./image"; export { Cache as ImageCache, DownloadError, DownloadRequest, DownloadedData } from "./image-cache"; diff --git a/nativescript-core/ui/index.ts b/nativescript-core/ui/index.ts index 073b18e277..f657063c7b 100644 --- a/nativescript-core/ui/index.ts +++ b/nativescript-core/ui/index.ts @@ -22,12 +22,13 @@ export { GestureStateTypes, GestureTypes, GesturesObserver, + TapGestureEventData, PanGestureEventData, PinchGestureEventData, RotationGestureEventData, SwipeDirection, SwipeGestureEventData, - TouchGestureEventData + TouchGestureEventData, } from "./gestures"; export { HtmlView } from "./html-view"; From 92b5b02bf504a77de9331bc99c797fe516650850 Mon Sep 17 00:00:00 2001 From: Luke Curran Date: Fri, 20 Mar 2020 02:19:53 -0600 Subject: [PATCH 04/26] =?UTF-8?q?feat(text-field):=20add=20closeOnReturn?= =?UTF-8?q?=20property=20to=20avoid=20auto=20dismissing=E2=80=A6=20(#8347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(text-field): add closeOnReturn property to avoid auto dismissing input on return press * tslint * Update NS public api Co-authored-by: Vasil Trifonov Co-authored-by: Dimitar Topuzov --- api-reports/NativeScript.api.md | 2 + .../ui/text-field/text-field-common.ts | 4 + .../ui/text-field/text-field.d.ts | 5 + .../ui/text-field/text-field.ios.ts | 4 +- .../text-field-tests-native.android.ts | 9 ++ .../text-field/text-field-tests-native.d.ts | 3 + .../text-field/text-field-tests-native.ios.ts | 12 +++ tests/app/ui/text-field/text-field-tests.ts | 98 ++++++++++++++++++- 8 files changed, 134 insertions(+), 3 deletions(-) diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index d8c543299e..1d9a30d61f 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -2496,6 +2496,8 @@ export class TextBase extends View implements AddChildFromBuilder { export class TextField extends EditableTextBase { android: any /* android.widget.EditText */; + closeOnReturn: boolean; + ios: any /* UITextField */; // (undocumented) diff --git a/nativescript-core/ui/text-field/text-field-common.ts b/nativescript-core/ui/text-field/text-field-common.ts index 2dff648f39..1e75ea9c22 100644 --- a/nativescript-core/ui/text-field/text-field-common.ts +++ b/nativescript-core/ui/text-field/text-field-common.ts @@ -7,9 +7,13 @@ export * from "../editable-text-base"; export class TextFieldBase extends EditableTextBase implements TextFieldDefinition { public static returnPressEvent = "returnPress"; public secure: boolean; + public closeOnReturn: boolean; } TextFieldBase.prototype.recycleNativeView = "auto"; export const secureProperty = new Property({ name: "secure", defaultValue: false, valueConverter: booleanConverter }); secureProperty.register(TextFieldBase); + +export const closeOnReturnProperty = new Property({ name: "closeOnReturn", defaultValue: true, valueConverter: booleanConverter }); +closeOnReturnProperty.register(TextFieldBase); diff --git a/nativescript-core/ui/text-field/text-field.d.ts b/nativescript-core/ui/text-field/text-field.d.ts index 0fa71bb1ef..5746e1bdf8 100644 --- a/nativescript-core/ui/text-field/text-field.d.ts +++ b/nativescript-core/ui/text-field/text-field.d.ts @@ -27,4 +27,9 @@ export class TextField extends EditableTextBase { * Gets or sets if a text field is for password entry. */ secure: boolean; + + /** + * Gets or sets if a text field should dismiss on return. + */ + closeOnReturn: boolean; } diff --git a/nativescript-core/ui/text-field/text-field.ios.ts b/nativescript-core/ui/text-field/text-field.ios.ts index 34be82920d..00e969174e 100644 --- a/nativescript-core/ui/text-field/text-field.ios.ts +++ b/nativescript-core/ui/text-field/text-field.ios.ts @@ -66,7 +66,9 @@ class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate { // Called when the user presses the return button. const owner = this._owner.get(); if (owner) { - owner.dismissSoftInput(); + if (owner.closeOnReturn) { + owner.dismissSoftInput(); + } owner.notify({ eventName: TextField.returnPressEvent, object: owner }); } diff --git a/tests/app/ui/text-field/text-field-tests-native.android.ts b/tests/app/ui/text-field/text-field-tests-native.android.ts index 8dfa28ab6b..b23d62dc3d 100644 --- a/tests/app/ui/text-field/text-field-tests-native.android.ts +++ b/tests/app/ui/text-field/text-field-tests-native.android.ts @@ -59,8 +59,17 @@ export function getNativeTextAlignment(textField: textFieldModule.TextField): st return "unexpected value"; } +export function getNativeFocus(textField: textFieldModule.TextField): boolean { + // + return true; +} + export function typeTextNatively(textField: textFieldModule.TextField, text: string): void { textField.android.requestFocus(); textField.android.setText(text); textField.android.clearFocus(); } + +export function typeTextNativelyWithReturn(textField: textFieldModule.TextField, text: string): void { + // +} diff --git a/tests/app/ui/text-field/text-field-tests-native.d.ts b/tests/app/ui/text-field/text-field-tests-native.d.ts index 42e294ffd1..2aed31bdee 100644 --- a/tests/app/ui/text-field/text-field-tests-native.d.ts +++ b/tests/app/ui/text-field/text-field-tests-native.d.ts @@ -10,4 +10,7 @@ export declare function getNativeColor(textField: textFieldModule.TextField): co export declare function getNativePlaceholderColor(textField: textFieldModule.TextField): colorModule.Color; export declare function getNativeBackgroundColor(textField: textFieldModule.TextField): colorModule.Color; export declare function getNativeTextAlignment(textField: textFieldModule.TextField): string; +export declare function getNativeFocus(textField: textFieldModule.TextField): boolean; export declare function typeTextNatively(textField: textFieldModule.TextField, text: string): void; +export declare function typeTextNativelyWithReturn(textField: textFieldModule.TextField, text: string): void; + diff --git a/tests/app/ui/text-field/text-field-tests-native.ios.ts b/tests/app/ui/text-field/text-field-tests-native.ios.ts index 03f1665932..1d697024b7 100644 --- a/tests/app/ui/text-field/text-field-tests-native.ios.ts +++ b/tests/app/ui/text-field/text-field-tests-native.ios.ts @@ -44,9 +44,21 @@ export function getNativeTextAlignment(textField: textFieldModule.TextField): st } } +export function getNativeFocus(textField: textFieldModule.TextField): boolean { + return textField.nativeView.isFirstResponder; +} + export function typeTextNatively(textField: textFieldModule.TextField, text: string): void { textField.ios.text = text; // Setting the text will not trigger the delegate method, so we have to do it by hand. textField.ios.delegate.textFieldDidEndEditing(textField.ios); } + +export function typeTextNativelyWithReturn(textField: textFieldModule.TextField, text: string): void { + textField.nativeView.becomeFirstResponder(); + + textField.ios.text = text; + + textField.ios.delegate.textFieldShouldReturn(textField.ios); +} diff --git a/tests/app/ui/text-field/text-field-tests.ts b/tests/app/ui/text-field/text-field-tests.ts index b17cff7850..7f46aa0226 100644 --- a/tests/app/ui/text-field/text-field-tests.ts +++ b/tests/app/ui/text-field/text-field-tests.ts @@ -5,9 +5,9 @@ import { Page } from "@nativescript/core/ui/page"; import { StackLayout } from "@nativescript/core/ui/layouts/stack-layout"; import { Color } from "@nativescript/core/color"; import { - getNativeText, getNativeHint, typeTextNatively, getNativeSecure, + getNativeText, getNativeHint, typeTextNatively, typeTextNativelyWithReturn, getNativeSecure, getNativeFontSize, getNativeColor, getNativeBackgroundColor, - getNativeTextAlignment, getNativePlaceholderColor + getNativeTextAlignment, getNativePlaceholderColor, getNativeFocus } from "./text-field-tests-native"; import { FormattedString } from "@nativescript/core/text/formatted-string"; import { Span } from "@nativescript/core/text/span"; @@ -400,6 +400,100 @@ export var testBindSecureToBindingConext = function () { }); }; +// iOS only +export var testBindCloseOnReturnToBindingConext = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + var page = views[1]; + + var model = new Observable(); + model.set("closeOnReturn", false); + page.bindingContext = model; + + var options: BindingOptions = { + sourceProperty: "closeOnReturn", + targetProperty: "closeOnReturn" + }; + + textField.bind(options); + TKUnit.assert(textField.closeOnReturn === false, "Actual: " + textField.closeOnReturn + "; Expected: " + false); + typeTextNativelyWithReturn(textField, "Should not close textfield"); + TKUnit.assert(getNativeFocus(textField) === true, "Actual: " + getNativeFocus(textField) + "; Expected: " + true); + + model.set("closeOnReturn", true); + TKUnit.assert(textField.closeOnReturn === true, "Actual: " + textField.closeOnReturn + "; Expected: " + true); + typeTextNativelyWithReturn(textField, "Should close textfield"); + TKUnit.assert(getNativeFocus(textField) === false, "Actual: " + getNativeFocus(textField) + "; Expected: " + false); + }); +}; + +// iOS only +export var testDontCloseOnReturn = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + + // >> setting-closeOnReturn-property + textField.closeOnReturn = false; + // << setting-closeOnReturn-property + + typeTextNativelyWithReturn(textField, "Should not close textfield"); + + var expectedValue = true; + var actualValue = getNativeFocus(textField); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +}; + +// iOS only +export var testCloseOnReturn = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + + // >> setting-closeOnReturn-property + textField.closeOnReturn = true; + // << setting-closeOnReturn-property + + typeTextNativelyWithReturn(textField, "Should close textfield"); + + var expectedValue = false; + var actualValue = getNativeFocus(textField); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +}; + +// iOS only +export var testCloseOnReturnByDefault = function () { + helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { + if (!isIOS) { + TKUnit.assert(true === true); + + return; + } + var textField = views[0]; + + typeTextNativelyWithReturn(textField, "Should close textfield by default"); + + var expectedValue = false; + var actualValue = getNativeFocus(textField); + TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + }); +}; + var expectedFontSize = 42; export var testLocalFontSizeFromCss = function () { helper.buildUIAndRunTest(_createTextFieldFunc(), function (views: Array) { From 8ab0e72bc95cc5ef46ab95e7129baba7a3a5c449 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Fri, 20 Mar 2020 18:35:28 +0200 Subject: [PATCH 05/26] feat: TappableSpan support (#8256) * feat(android): clickable span Initial support for clickable span on Android * test: clickable-span test page * remove console.log * use _emit instead of notify * rename clickable to tappable in Span * updated NativeScript.api.md * chore: fixing tslint errors * chore: fixed witespacing * moved and improved test page * feat: tappable span iOS implementation Co-authored-by: Eduardo Speroni --- api-reports/NativeScript.api.md | 8 +- e2e/ui-tests-app/app/button/main-page.ts | 1 + nativescript-core/ui/text-base/span.d.ts | 11 +- nativescript-core/ui/text-base/span.ts | 25 +++- .../ui/text-base/text-base.android.ts | 75 ++++++++++++ .../ui/text-base/text-base.ios.ts | 108 ++++++++++++++++++ 6 files changed, 224 insertions(+), 4 deletions(-) diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index 1d9a30d61f..6d461503a7 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -1936,14 +1936,18 @@ export class Span extends ViewBase { public fontWeight: FontWeight; + public static linkTapEvent: string; + // (undocumented) _setTextInternal(value: string): void; + public readonly tappable: boolean; + public text: string; public textDecoration: TextDecoration; //@endprivate -} + } // @public export class StackLayout extends LayoutBase { @@ -2422,7 +2426,7 @@ export interface TapGestureEventData extends GestureEventData { getPointerCount(): number; getX(): number; - + getY(): number; } diff --git a/e2e/ui-tests-app/app/button/main-page.ts b/e2e/ui-tests-app/app/button/main-page.ts index ecb25dc25f..5239daa131 100644 --- a/e2e/ui-tests-app/app/button/main-page.ts +++ b/e2e/ui-tests-app/app/button/main-page.ts @@ -18,6 +18,7 @@ export function loadExamples() { examples.set("issue-4287", "button/issue-4287-page"); examples.set("issue-4385", "button/issue-4385-page"); examples.set("highlight-4740", "button/highlight-4740/highlight-4740-page"); + examples.set("tappable-span", "button/tappable-span-page"); return examples; } diff --git a/nativescript-core/ui/text-base/span.d.ts b/nativescript-core/ui/text-base/span.d.ts index 9c93d8ddca..f648dd198d 100644 --- a/nativescript-core/ui/text-base/span.d.ts +++ b/nativescript-core/ui/text-base/span.d.ts @@ -50,6 +50,15 @@ export class Span extends ViewBase { * Gets or sets the text for the span. */ public text: string; + /** + * String value used when hooking to linkTap event. + */ + public static linkTapEvent: string; + + /** + * Gets if the span is tappable or not. + */ + public readonly tappable: boolean; //@private /** @@ -57,4 +66,4 @@ export class Span extends ViewBase { */ _setTextInternal(value: string): void; //@endprivate -} +} \ No newline at end of file diff --git a/nativescript-core/ui/text-base/span.ts b/nativescript-core/ui/text-base/span.ts index ce485cd15f..c37adf340b 100644 --- a/nativescript-core/ui/text-base/span.ts +++ b/nativescript-core/ui/text-base/span.ts @@ -2,10 +2,12 @@ import { Span as SpanDefinition } from "./span"; import { ViewBase } from "../core/view"; import { FontStyle, FontWeight, } from "../styling/font"; -import { TextDecoration } from "../text-base"; +import { TextDecoration, EventData } from "../text-base"; export class Span extends ViewBase implements SpanDefinition { + static linkTapEvent = "linkTap"; private _text: string; + private _tappable: boolean = false; get fontFamily(): string { return this.style.fontFamily; @@ -68,7 +70,28 @@ export class Span extends ViewBase implements SpanDefinition { } } + get tappable(): boolean { + return this._tappable; + } + + addEventListener(arg: string, callback: (data: EventData) => void, thisArg?: any) { + super.addEventListener(arg, callback, thisArg); + this._setTappable(this.hasListeners(Span.linkTapEvent)); + } + + removeEventListener(arg: string, callback?: any, thisArg?: any) { + super.removeEventListener(arg, callback, thisArg); + this._setTappable(this.hasListeners(Span.linkTapEvent)); + } + _setTextInternal(value: string): void { this._text = value; } + + private _setTappable(value: boolean): void { + if (this._tappable !== value) { + this._tappable = value; + this.notifyPropertyChange("tappable", value); + } + } } diff --git a/nativescript-core/ui/text-base/text-base.android.ts b/nativescript-core/ui/text-base/text-base.android.ts index d7cf1bfa75..200285c8aa 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -51,6 +51,42 @@ function initializeTextTransformation(): void { TextTransformation = TextTransformationImpl; } +interface ClickableSpan { + new (owner: Span): android.text.style.ClickableSpan; +} + +let ClickableSpan: ClickableSpan; + +function initializeClickableSpan(): void { + if (ClickableSpan) { + return; + } + + class ClickableSpanImpl extends android.text.style.ClickableSpan { + owner: WeakRef; + + constructor(owner: Span) { + super(); + this.owner = new WeakRef(owner); + + return global.__native(this); + } + onClick(view: android.view.View): void { + const owner = this.owner.get(); + if (owner) { + owner._emit(Span.linkTapEvent); + } + view.clearFocus(); + view.invalidate(); + } + updateDrawState(tp: android.text.TextPaint): void { + // don't style as link + } + } + + ClickableSpan = ClickableSpanImpl; +} + export class TextBase extends TextBaseCommon { nativeViewProtected: android.widget.TextView; nativeTextViewProtected: android.widget.TextView; @@ -60,12 +96,15 @@ export class TextBase extends TextBaseCommon { private _maxHeight: number; private _minLines: number; private _maxLines: number; + private _tappable: boolean = false; + private _defaultMovementMethod: android.text.method.MovementMethod; public initNativeView(): void { super.initNativeView(); initializeTextTransformation(); const nativeView = this.nativeTextViewProtected; this._defaultTransformationMethod = nativeView.getTransformationMethod(); + this._defaultMovementMethod = this.nativeView.getMovementMethod(); this._minHeight = nativeView.getMinHeight(); this._maxHeight = nativeView.getMaxHeight(); this._minLines = nativeView.getMinLines(); @@ -112,6 +151,8 @@ export class TextBase extends TextBaseCommon { return; } + this._setTappableState(false); + this._setNativeText(reset); } @@ -131,6 +172,7 @@ export class TextBase extends TextBaseCommon { const spannableStringBuilder = createSpannableStringBuilder(value); nativeView.setText(spannableStringBuilder); + this._setTappableState(isStringTappable(value)); textProperty.nativeValueChange(this, (value === null || value === undefined) ? "" : value.toString()); @@ -315,6 +357,19 @@ export class TextBase extends TextBaseCommon { this.nativeTextViewProtected.setText(transformedText); } + + _setTappableState(tappable: boolean) { + if (this._tappable !== tappable) { + this._tappable = tappable; + if (this._tappable) { + this.nativeViewProtected.setMovementMethod(android.text.method.LinkMovementMethod.getInstance()); + this.nativeViewProtected.setHighlightColor(null); + } + else { + this.nativeViewProtected.setMovementMethod(this._defaultMovementMethod); + } + } + } } function getCapitalizedString(str: string): string { @@ -346,6 +401,20 @@ export function getTransformedText(text: string, textTransform: TextTransform): } } +function isStringTappable(formattedString: FormattedString) { + if (!formattedString) { + return false; + } + for (let i = 0, length = formattedString.spans.length; i < length; i++) { + const span = formattedString.spans.getItem(i); + if (span.tappable) { + return true; + } + } + + return false; +} + function createSpannableStringBuilder(formattedString: FormattedString): android.text.SpannableStringBuilder { if (!formattedString || !formattedString.parent) { return null; @@ -444,6 +513,12 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, } } + const tappable = span.tappable; + if (tappable) { + initializeClickableSpan(); + ssb.setSpan(new ClickableSpan(span), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + // TODO: Implement letterSpacing for Span here. // const letterSpacing = formattedString.parent.style.letterSpacing; // if (letterSpacing > 0) { diff --git a/nativescript-core/ui/text-base/text-base.ios.ts b/nativescript-core/ui/text-base/text-base.ios.ts index 568f2ef038..0a1618afbb 100644 --- a/nativescript-core/ui/text-base/text-base.ios.ts +++ b/nativescript-core/ui/text-base/text-base.ios.ts @@ -15,10 +15,101 @@ export * from "./text-base-common"; const majorVersion = ios.MajorVersion; +class UILabelClickHandlerImpl extends NSObject { + private _owner: WeakRef; + + public static initWithOwner(owner: WeakRef): UILabelClickHandlerImpl { + let handler = UILabelClickHandlerImpl.new(); + handler._owner = owner; + + return handler; + } + + public linkTap(tapGesture: UITapGestureRecognizer) { + let owner = this._owner.get(); + if (owner) { + // https://stackoverflow.com/a/35789589 + let label = owner.nativeTextViewProtected; + let layoutManager = NSLayoutManager.alloc().init(); + let textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero); + let textStorage = NSTextStorage.alloc().initWithAttributedString(owner.nativeTextViewProtected["attributedText"]); + + layoutManager.addTextContainer(textContainer); + textStorage.addLayoutManager(layoutManager); + + textContainer.lineFragmentPadding = 0; + textContainer.lineBreakMode = label.lineBreakMode; + textContainer.maximumNumberOfLines = label.numberOfLines; + let labelSize = label.bounds.size; + textContainer.size = labelSize; + + let locationOfTouchInLabel = tapGesture.locationInView(label); + let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer); + + let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, + (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y); + + let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x, + locationOfTouchInLabel.y - textContainerOffset.y); + + let indexOfCharacter = layoutManager.characterIndexForPointInTextContainerFractionOfDistanceBetweenInsertionPoints( + locationOfTouchInTextContainer, textContainer, null); + + let span: Span = null; + // try to find the corresponding span using the spanRanges + for (let i = 0; i < owner._spanRanges.length; i++) { + let range = owner._spanRanges[i]; + if ((range.location <= indexOfCharacter) && (range.location + range.length) > indexOfCharacter) { + if (owner.formattedText && owner.formattedText.spans.length > i) { + span = owner.formattedText.spans.getItem(i); + } + break; + } + } + + if (span && span.tappable) { + // if the span is found and tappable emit the linkTap event + span._emit(Span.linkTapEvent); + } + } + } + + public static ObjCExposedMethods = { + "linkTap": { returns: interop.types.void, params: [interop.types.id] } + }; +} + export class TextBase extends TextBaseCommon { public nativeViewProtected: UITextField | UITextView | UILabel | UIButton; public nativeTextViewProtected: UITextField | UITextView | UILabel | UIButton; + private _tappable: boolean = false; + private _tapGestureRecognizer: UITapGestureRecognizer; + public _spanRanges: NSRange[]; + + public initNativeView(): void { + super.initNativeView(); + this._setTappableState(false); + } + + _setTappableState(tappable: boolean) { + if (this._tappable !== tappable) { + this._tappable = tappable; + if (this._tappable) { + const tapHandler = UILabelClickHandlerImpl.initWithOwner(new WeakRef(this)); + // associate handler with menuItem or it will get collected by JSC. + (this).handler = tapHandler; + + this._tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(tapHandler, "linkTap"); + this.nativeViewProtected.userInteractionEnabled = true; + this.nativeViewProtected.addGestureRecognizer(this._tapGestureRecognizer); + } + else { + this.nativeViewProtected.userInteractionEnabled = false; + this.nativeViewProtected.removeGestureRecognizer(this._tapGestureRecognizer); + } + } + } [textProperty.getDefault](): number | symbol { return resetSymbol; @@ -35,6 +126,7 @@ export class TextBase extends TextBaseCommon { [formattedTextProperty.setNative](value: FormattedString) { this._setNativeText(); + this._setTappableState(isStringTappable(value)); textProperty.nativeValueChange(this, !value ? "" : value.toString()); this._requestLayoutOnTextChanged(); } @@ -253,6 +345,7 @@ export class TextBase extends TextBaseCommon { createNSMutableAttributedString(formattedString: FormattedString): NSMutableAttributedString { let mas = NSMutableAttributedString.alloc().init(); + this._spanRanges = []; if (formattedString && formattedString.parent) { for (let i = 0, spanStart = 0, length = formattedString.spans.length; i < length; i++) { const span = formattedString.spans.getItem(i); @@ -265,6 +358,7 @@ export class TextBase extends TextBaseCommon { const nsAttributedString = this.createMutableStringForSpan(span, spanText); mas.insertAttributedStringAtIndex(nsAttributedString, spanStart); + this._spanRanges.push({location: spanStart, length: spanText.length}); spanStart += spanText.length; } } @@ -349,3 +443,17 @@ export function getTransformedText(text: string, textTransform: TextTransform): function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString { return NSString.stringWithString(source instanceof NSAttributedString && source.string || source); } + +function isStringTappable(formattedString: FormattedString) { + if (!formattedString) { + return false; + } + for (let i = 0, length = formattedString.spans.length; i < length; i++) { + const span = formattedString.spans.getItem(i); + if (span.tappable) { + return true; + } + } + + return false; +} \ No newline at end of file From 42fc4acea38aa0254490521f8226d31062d98106 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Mon, 23 Mar 2020 03:16:40 -0700 Subject: [PATCH 06/26] feat(ios): textfield option to disable iOS autofill strong password handling (#8348) * feat(ios): textfield option to disable autofill strong password handling * chore: api change report --- api-reports/NativeScript.api.md | 129 +++++++++--------- .../ui/text-field/text-field-common.ts | 2 + .../ui/text-field/text-field.d.ts | 5 + .../ui/text-field/text-field.ios.ts | 8 ++ 4 files changed, 80 insertions(+), 64 deletions(-) diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index 6d461503a7..3197a3d910 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -878,45 +878,45 @@ export interface GestureEventData extends EventData { // @public export interface GestureEventDataWithState extends GestureEventData { - // (undocumented) - state: number; + // (undocumented) + state: number; } // @public export class GesturesObserver { - constructor(target: View, callback: (args: GestureEventData) => void, context: any); + constructor(target: View, callback: (args: GestureEventData) => void, context: any); - androidOnTouchEvent: (motionEvent: any /* android.view.MotionEvent */) => void; + androidOnTouchEvent: (motionEvent: any /* android.view.MotionEvent */) => void; - callback: (args: GestureEventData) => void; + callback: (args: GestureEventData) => void; - context: any; + context: any; - disconnect(); + disconnect(); - observe(type: GestureTypes); + observe(type: GestureTypes); - type: GestureTypes; + type: GestureTypes; } // @public export enum GestureStateTypes { - began, - cancelled, - changed, - ended + began, + cancelled, + changed, + ended } // @public export enum GestureTypes { - doubleTap, - longPress, - pan, - pinch, - rotation, - swipe, - tap, - touch + doubleTap, + longPress, + pan, + pinch, + rotation, + swipe, + tap, + touch } // @public @@ -1011,49 +1011,49 @@ export const Http: { // @public export interface HttpContent { - raw: any; + raw: any; - toArrayBuffer: () => ArrayBuffer; + toArrayBuffer: () => ArrayBuffer; - toFile: (destinationFilePath?: string) => File; + toFile: (destinationFilePath?: string) => File; - toImage: () => Promise; + toImage: () => Promise; - toJSON: (encoding?: HttpResponseEncoding) => any; + toJSON: (encoding?: HttpResponseEncoding) => any; - toString: (encoding?: HttpResponseEncoding) => string; + toString: (encoding?: HttpResponseEncoding) => string; } // @public export interface HttpRequestOptions { - content?: string | FormData | ArrayBuffer; + content?: string | FormData | ArrayBuffer; - dontFollowRedirects?: boolean; + dontFollowRedirects?: boolean; - headers?: any; + headers?: any; - method: string; + method: string; - timeout?: number; + timeout?: number; - url: string; + url: string; } // @public export interface HttpResponse { - content?: HttpContent; + content?: HttpContent; - headers: Headers; + headers: Headers; - statusCode: number; + statusCode: number; } // @public (undocumented) export enum HttpResponseEncoding { - // (undocumented) - GBK, - // (undocumented) - UTF8 + // (undocumented) + GBK, + // (undocumented) + UTF8 } // @public @@ -1642,10 +1642,10 @@ export class Page extends ContentView { // @public export interface PanGestureEventData extends GestureEventDataWithState { - // (undocumented) - deltaX: number; - // (undocumented) - deltaY: number; + // (undocumented) + deltaX: number; + // (undocumented) + deltaY: number; } // @public @@ -1694,14 +1694,14 @@ export module path { // @public export interface PinchGestureEventData extends GestureEventDataWithState { - // (undocumented) - getFocusX(): number; + // (undocumented) + getFocusX(): number; - // (undocumented) - getFocusY(): number; + // (undocumented) + getFocusY(): number; - // (undocumented) - scale: number; + // (undocumented) + scale: number; } // @public @@ -1765,8 +1765,8 @@ export class Repeater extends CustomLayoutView { // @public export interface RotationGestureEventData extends GestureEventDataWithState { - // (undocumented) - rotation: number; + // (undocumented) + rotation: number; } // @public @@ -2172,16 +2172,16 @@ export class Style extends Observable { // @public export enum SwipeDirection { - down, - left, - right, - up + down, + left, + right, + up } // @public export interface SwipeGestureEventData extends GestureEventData { - // (undocumented) - direction: SwipeDirection; + // (undocumented) + direction: SwipeDirection; } // @public @@ -2424,10 +2424,9 @@ export class TabViewItem extends ViewBase { // @public export interface TapGestureEventData extends GestureEventData { getPointerCount(): number; - getX(): number; - getY(): number; + } // @public @@ -2508,6 +2507,8 @@ export class TextField extends EditableTextBase { public static returnPressEvent: string; secure: boolean; + + secureWithoutAutofill: boolean; } // @public @@ -2552,10 +2553,10 @@ export interface TimerInfo { // @public export interface TouchGestureEventData extends TapGestureEventData { - action: "up" | "move" | "down" | "cancel"; - // Warning: (ae-forgotten-export) The symbol "Pointer" needs to be exported by the entry point index.d.ts - getActivePointers(): Array; - getAllPointers(): Array; + action: "up" | "move" | "down" | "cancel"; + // Warning: (ae-forgotten-export) The symbol "Pointer" needs to be exported by the entry point index.d.ts + getActivePointers(): Array; + getAllPointers(): Array; } // @public (undocumented) diff --git a/nativescript-core/ui/text-field/text-field-common.ts b/nativescript-core/ui/text-field/text-field-common.ts index 1e75ea9c22..787bb9ba8b 100644 --- a/nativescript-core/ui/text-field/text-field-common.ts +++ b/nativescript-core/ui/text-field/text-field-common.ts @@ -8,6 +8,8 @@ export class TextFieldBase extends EditableTextBase implements TextFieldDefiniti public static returnPressEvent = "returnPress"; public secure: boolean; public closeOnReturn: boolean; + // iOS only (to avoid 12+ suggested strong password handling) + public secureWithoutAutofill: boolean; } TextFieldBase.prototype.recycleNativeView = "auto"; diff --git a/nativescript-core/ui/text-field/text-field.d.ts b/nativescript-core/ui/text-field/text-field.d.ts index 5746e1bdf8..e6983069c0 100644 --- a/nativescript-core/ui/text-field/text-field.d.ts +++ b/nativescript-core/ui/text-field/text-field.d.ts @@ -32,4 +32,9 @@ export class TextField extends EditableTextBase { * Gets or sets if a text field should dismiss on return. */ closeOnReturn: boolean; + + /** + * iOS only (to avoid 12+ auto suggested strong password handling) + */ + secureWithoutAutofill: boolean; } diff --git a/nativescript-core/ui/text-field/text-field.ios.ts b/nativescript-core/ui/text-field/text-field.ios.ts index 00e969174e..ef7c1e9eb4 100644 --- a/nativescript-core/ui/text-field/text-field.ios.ts +++ b/nativescript-core/ui/text-field/text-field.ios.ts @@ -78,6 +78,14 @@ class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate { public textFieldShouldChangeCharactersInRangeReplacementString(textField: UITextField, range: NSRange, replacementString: string): boolean { const owner = this._owner.get(); if (owner) { + if (owner.secureWithoutAutofill && !textField.secureTextEntry) { + /** + * Helps avoid iOS 12+ autofill strong password suggestion prompt + * Discussed in several circles but for example: + * https://github.com/expo/expo/issues/2571#issuecomment-473347380 + */ + textField.secureTextEntry = true; + } const delta = replacementString.length - range.length; if (delta > 0) { if (textField.text.length + delta > owner.maxLength) { From 1f04469fb3b77215b3f5dc836f1967e4b84c8cc3 Mon Sep 17 00:00:00 2001 From: Nicu Date: Tue, 24 Mar 2020 10:17:35 +0200 Subject: [PATCH 07/26] feat(css-bkg-pos): Added possibility to declare background pos by single numeric value (#7958) * feat(css-bkg-pos): Added possibility to declare background pos by single numeric value * feat(css-bkg-pos): Implemented numeric bkg pos for iOS * feat(css-bkg-pos): removed unnecessary code Co-authored-by: Vasil Trifonov --- .../ui/styling/background.ios.ts | 41 +++++++++++++------ .../nativescript/widgets/BorderDrawable.java | 33 +++++++++++---- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/nativescript-core/ui/styling/background.ios.ts b/nativescript-core/ui/styling/background.ios.ts index 96dd41aef3..4471e3e4d5 100644 --- a/nativescript-core/ui/styling/background.ios.ts +++ b/nativescript-core/ui/styling/background.ios.ts @@ -204,18 +204,23 @@ function parsePosition(pos: string): { x: CSSValue, y: CSSValue } { return { x: values[0], y: values[1] }; } - if (values.length === 1 && values[0].type === "ident") { - const val = values[0].string.toLocaleLowerCase(); - const center = { type: "ident", string: "center" }; - - // If you only one keyword is specified, the other value is "center" - if (val === "left" || val === "right") { - return { x: values[0], y: center }; - } else if (val === "top" || val === "bottom") { - return { x: center, y: values[0] }; - } else if (val === "center") { - return { x: center, y: center }; - } + if (values.length === 1) { + const center = { type: "ident", string: "center" }; + + if (values[0].type === "ident") { + const val = values[0].string.toLocaleLowerCase(); + + // If you only one keyword is specified, the other value is "center" + if (val === "left" || val === "right") { + return { x: values[0], y: center }; + } else if (val === "top" || val === "bottom") { + return { x: center, y: values[0] }; + } else if (val === "center") { + return { x: center, y: center }; + } + } else if (values[0].type === "number") { + return {x: values[0], y: center}; + } } return null; @@ -322,6 +327,18 @@ function getDrawParams(this: void, image: UIImage, background: BackgroundDefinit } else if (v.y.string.toLowerCase() === "bottom") { res.posY = spaceY; } + } else if (v.x.type === "number" && v.y.type === "ident") { + if (v.x.unit === "%") { + res.posX = spaceX * v.x.value / 100; + } else if (v.x.unit === "px" || v.x.unit === "") { + res.posX = v.x.value; + } + + if (v.y.string.toLowerCase() === "center") { + res.posY = spaceY / 2; + } else if (v.y.string.toLowerCase() === "bottom") { + res.posY = spaceY; + } } } } diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BorderDrawable.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BorderDrawable.java index 0abf8d26b2..2f7853a5aa 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BorderDrawable.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BorderDrawable.java @@ -713,6 +713,18 @@ private BackgroundDrawParams getDrawParams(float width, float height) { res.posX = spaceX; } + if ("center".equals(vy.getString().toLowerCase(Locale.ENGLISH))) { + res.posY = spaceY / 2; + } else if ("bottom".equals(vy.getString().toLowerCase(Locale.ENGLISH))) { + res.posY = spaceY; + } + } else if ("number".equals(vx.getType()) && "ident".equals(vy.getType())) { + if ("%".equals(vx.getUnit())) { + res.posX = spaceX * vx.getValue() / 100; + } else if ("px".equals(vx.getUnit()) || vx.getUnit() == null || vx.getUnit().isEmpty()) { + res.posX = vx.getValue(); + } + if ("center".equals(vy.getString().toLowerCase(Locale.ENGLISH))) { res.posY = spaceY / 2; } else if ("bottom".equals(vy.getString().toLowerCase(Locale.ENGLISH))) { @@ -731,17 +743,22 @@ private static CSSValue[] parsePosition(CSSValue[] values) { } CSSValue[] result = null; - if (values.length == 1 && "ident".equals(values[0].getType())) { - String val = values[0].getString().toLowerCase(Locale.ENGLISH); + if (values.length == 1) { + // If you only one keyword is specified, the other value is "center" CSSValue center = new CSSValue("ident", "center", null, 0); - // If you only one keyword is specified, the other value is "center" - if ("left".equals(val) || "right".equals(val)) { + if ("ident".equals(values[0].getType())) { + String val = values[0].getString().toLowerCase(Locale.ENGLISH); + + if ("left".equals(val) || "right".equals(val)) { + result = new CSSValue[]{values[0], center}; + } else if ("top".equals(val) || "bottom".equals(val)) { + result = new CSSValue[]{center, values[0]}; + } else if ("center".equals(val)) { + result = new CSSValue[]{center, center}; + } + } else if ("number".equals(values[0].getType())) { result = new CSSValue[]{values[0], center}; - } else if ("top".equals(val) || "bottom".equals(val)) { - result = new CSSValue[]{center, values[0]}; - } else if ("center".equals(val)) { - result = new CSSValue[]{center, center}; } } From 66cc8a477e166cf4dbea33a06a49eea018bd8933 Mon Sep 17 00:00:00 2001 From: "Bundyo (Kamen Bundev)" Date: Thu, 26 Mar 2020 13:07:04 +0200 Subject: [PATCH 08/26] fix(button-android): use setMinWidth to set TextView widgets min-width (#7804) * fix: setMinimumWidth does not really set the minWidth for TextView widgets * chore: fix versions * refactor(android): default minWidth/Height values for button * reafctor: reset minWidth when view is in flexbox for backcompat * refactor: ensure back-compatible layout for btns * chore: cut 6.5.0 release (#8443) * release: cut 6.5.0 release * Update CHANGELOG.md * Update CHANGELOG.md Co-authored-by: Vasil Trifonov Co-authored-by: vakrilov Co-authored-by: Dimitar Topuzov Co-authored-by: Vasil Trifonov --- CHANGELOG.md | 19 +++ api-reports/NativeScript.api.md | 2 + nativescript-core/package.json | 16 +-- nativescript-core/ui/button/button.android.ts | 17 ++- .../ui/core/view-base/view-base.d.ts | 7 ++ .../ui/core/view-base/view-base.ts | 1 + .../ui/core/view/view.android.ts | 21 ++-- nativescript-core/ui/core/view/view.d.ts | 4 +- .../flexbox-layout/flexbox-layout.android.ts | 29 +++-- tns-core-modules-package/package.json | 2 +- .../org/nativescript/widgets/ViewHelper.java | 112 ++++++++++-------- tns-platform-declarations/package.json | 2 +- 12 files changed, 152 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a7076f3bd..c4f31053e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# [6.5.0](https://github.com/NativeScript/NativeScript/compare/6.4.2...6.5.0) (2020-03-18) + + +### Bug Fixes + +* Fix issue for view not being removed from its parent ([#8432](https://github.com/NativeScript/NativeScript/issues/8432)) ([e081340](https://github.com/NativeScript/NativeScript/commit/e081340665605c54530587cc0cce9bc339585557)) +* **d.ts:** Update iOS typings iOS 13.2 ([#8430](https://github.com/NativeScript/NativeScript/issues/8430)) ([18a9b1a](https://github.com/NativeScript/NativeScript/commit/18a9b1aea89ee9defee990ace7d2409327a47a28)) +* **bottom-nav:** Android TabStripItems not aligned correctly when one of the image is smaller ([#8414](https://github.com/NativeScript/NativeScript/issues/8414))([9830be7](https://github.com/NativeScript/NativeScript/commit/9830be7230355638ace604145a3e3fad393c2a93)) +* **tabs:** delay loadView when animation runs ([#8353](https://github.com/NativeScript/NativeScript/issues/8353)) ([e649a6c](https://github.com/NativeScript/NativeScript/commit/e649a6cfd618c86a1dc7fa84e3197dfb78c3bc74)) + + +### Features + +* **tabs:** Tabs styling improvements ([#8366](https://github.com/NativeScript/NativeScript/pull/8366))([4589431](https://github.com/NativeScript/NativeScript/commit/458943111e909fcdad47d12e7ee4bcd9732f4e90)) +* **tabs:** Added selectedItemColor and unSelectedItemColor to the TabStrip ([#8435](https://github.com/NativeScript/NativeScript/issues/8435))([243dc98](https://github.com/NativeScript/NativeScript/commit/243dc98005d43617872da5cfc010e76178aa7f97)) +* **gestures:** add locationX and locationY to double tap event data ([#8338](https://github.com/NativeScript/NativeScript/pull/8338))([5ff78e2](https://github.com/NativeScript/NativeScript/commit/5ff78e2ad2612259bff36d3aaf5a0292309f5a78)) +* **tabs:** Added iconClass property to TabStripItem ([#8439](https://github.com/NativeScript/NativeScript/issues/8439))([270988d](https://github.com/NativeScript/NativeScript/commit/270988d26e1eeab97b4e5781451388e3a0a347e3)) + + ## [6.4.2](https://github.com/NativeScript/NativeScript/compare/6.4.1...6.4.2) (2020-02-27) diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index 3197a3d910..1732b30d2d 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -2896,6 +2896,8 @@ export abstract class ViewBase extends Observable { public _goToVisualState(state: string): void; public id: string; // (undocumented) + public _ignoreFlexMinWidthHeightReset: boolean; + // (undocumented) _inheritStyleScope(styleScope: any /* StyleScope */): void; initNativeView(): void; // (undocumented) diff --git a/nativescript-core/package.json b/nativescript-core/package.json index bf97c5caed..73981e6ccd 100644 --- a/nativescript-core/package.json +++ b/nativescript-core/package.json @@ -41,12 +41,14 @@ "ios": "6.0.0", "android": "6.0.0" }, - "hooks": [{ - "name": "nativescript-core", - "type": "before-checkForChanges", - "script": "cli-hooks/before-checkForChanges.js", - "inject": true - }] + "hooks": [ + { + "name": "nativescript-core", + "type": "before-checkForChanges", + "script": "cli-hooks/before-checkForChanges.js", + "inject": true + } + ] }, "snapshot": { "android": { @@ -58,4 +60,4 @@ } } } -} \ No newline at end of file +} diff --git a/nativescript-core/ui/button/button.android.ts b/nativescript-core/ui/button/button.android.ts index 81ab02dfc2..ea78c5f233 100644 --- a/nativescript-core/ui/button/button.android.ts +++ b/nativescript-core/ui/button/button.android.ts @@ -1,7 +1,8 @@ import { ButtonBase, PseudoClassHandler, paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, - Length, zIndexProperty, textAlignmentProperty, TextAlignment + Length, zIndexProperty, textAlignmentProperty, TextAlignment, layout, + minWidthProperty, minHeightProperty } from "./button-common"; import { profile } from "../../profiling"; import { TouchGestureEventData, GestureTypes, TouchAction } from "../gestures"; @@ -111,6 +112,18 @@ export class Button extends ButtonBase { } } + [minWidthProperty.getDefault](): Length { + const dips = org.nativescript.widgets.ViewHelper.getMinWidth(this.nativeViewProtected); + + return { value: dips, unit: "px" }; + } + + [minHeightProperty.getDefault](): Length { + const dips = org.nativescript.widgets.ViewHelper.getMinHeight(this.nativeViewProtected); + + return { value: dips, unit: "px" }; + } + [paddingTopProperty.getDefault](): Length { return { value: this._defaultPaddingTop, unit: "px" }; } @@ -177,3 +190,5 @@ export class Button extends ButtonBase { return 4; // 4dp @dimen/button_pressed_z_material } } + +Button.prototype._ignoreFlexMinWidthHeightReset = true; diff --git a/nativescript-core/ui/core/view-base/view-base.d.ts b/nativescript-core/ui/core/view-base/view-base.d.ts index 4ad892f64a..c6b0a9a244 100644 --- a/nativescript-core/ui/core/view-base/view-base.d.ts +++ b/nativescript-core/ui/core/view-base/view-base.d.ts @@ -432,10 +432,17 @@ export abstract class ViewBase extends Observable { * @private */ public recycleNativeView: "always" | "never" | "auto"; + /** * @private */ public _isPaddingRelative: boolean; + + /** + * @private + */ + public _ignoreFlexMinWidthHeightReset: boolean; + public _styleScope: any; /** diff --git a/nativescript-core/ui/core/view-base/view-base.ts b/nativescript-core/ui/core/view-base/view-base.ts index 2046190fae..77bdb0e11b 100644 --- a/nativescript-core/ui/core/view-base/view-base.ts +++ b/nativescript-core/ui/core/view-base/view-base.ts @@ -227,6 +227,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition _oldTop: number; _oldRight: number; _oldBottom: number; + _ignoreFlexMinWidthHeightReset: boolean; public effectiveMinWidth: number; public effectiveMinHeight: number; diff --git a/nativescript-core/ui/core/view/view.android.ts b/nativescript-core/ui/core/view/view.android.ts index bbfab01942..54dcd25998 100644 --- a/nativescript-core/ui/core/view/view.android.ts +++ b/nativescript-core/ui/core/view/view.android.ts @@ -389,7 +389,7 @@ export class View extends ViewCommon { this.nativeViewProtected.setClickable(this._isClickable); } } - + this._manager = null; this._rootManager = null; super.onUnloaded(); @@ -982,17 +982,17 @@ export class View extends ViewCommon { [minWidthProperty.setNative](value: Length) { if (this.parent instanceof CustomLayoutView && this.parent.nativeViewProtected) { - this.parent._setChildMinWidthNative(this); + this.parent._setChildMinWidthNative(this, value); } else { - this._setMinWidthNative(this.minWidth); + this._setMinWidthNative(value); } } [minHeightProperty.setNative](value: Length) { if (this.parent instanceof CustomLayoutView && this.parent.nativeViewProtected) { - this.parent._setChildMinHeightNative(this); + this.parent._setChildMinHeightNative(this, value); } else { - this._setMinHeightNative(this.minHeight); + this._setMinHeightNative(value); } } @@ -1050,16 +1050,15 @@ export class CustomLayoutView extends ContainerView implements CustomLayoutViewD } public _updateNativeLayoutParams(child: View): void { - this._setChildMinWidthNative(child); - this._setChildMinHeightNative(child); + // noop } - public _setChildMinWidthNative(child: View): void { - child._setMinWidthNative(child.minWidth); + public _setChildMinWidthNative(child: View, value: Length): void { + child._setMinWidthNative(value); } - public _setChildMinHeightNative(child: View): void { - child._setMinHeightNative(child.minHeight); + public _setChildMinHeightNative(child: View, value: Length): void { + child._setMinHeightNative(value); } public _removeViewFromNativeVisualTree(child: ViewCommon): void { diff --git a/nativescript-core/ui/core/view/view.d.ts b/nativescript-core/ui/core/view/view.d.ts index 41ca87b94e..3b226ca317 100644 --- a/nativescript-core/ui/core/view/view.d.ts +++ b/nativescript-core/ui/core/view/view.d.ts @@ -796,11 +796,11 @@ export class CustomLayoutView extends ContainerView { /** * @private */ - _setChildMinWidthNative(child: View): void; + _setChildMinWidthNative(child: View, value: Length): void; /** * @private */ - _setChildMinHeightNative(child: View): void; + _setChildMinHeightNative(child: View, value: Length): void; //@endprivate } diff --git a/nativescript-core/ui/layouts/flexbox-layout/flexbox-layout.android.ts b/nativescript-core/ui/layouts/flexbox-layout/flexbox-layout.android.ts index 13932225a4..8f64adb543 100644 --- a/nativescript-core/ui/layouts/flexbox-layout/flexbox-layout.android.ts +++ b/nativescript-core/ui/layouts/flexbox-layout/flexbox-layout.android.ts @@ -7,7 +7,9 @@ import { flexShrinkProperty, FlexShrink, flexWrapBeforeProperty, FlexWrapBefore, alignSelfProperty, AlignSelf, - flexDirectionProperty, flexWrapProperty, justifyContentProperty, alignItemsProperty, alignContentProperty + flexDirectionProperty, flexWrapProperty, justifyContentProperty, alignItemsProperty, alignContentProperty, + minWidthProperty, minHeightProperty, + CssProperty } from "./flexbox-layout-common"; export * from "./flexbox-layout-common"; @@ -137,6 +139,11 @@ export class FlexboxLayout extends FlexboxLayoutBase { public _updateNativeLayoutParams(child: View): void { super._updateNativeLayoutParams(child); + // NOTE: If minWidth/Height is not set, the next code will clear the default native values for minWidth/Height. + // Flex box will not respect the button default min width. Keeping this behavior for back-compatibility. + this._setChildMinWidthNative(child, child.minWidth); + this._setChildMinHeightNative(child, child.minHeight); + const lp = child.nativeViewProtected.getLayoutParams(); const style = child.style; lp.order = style.order; @@ -147,22 +154,30 @@ export class FlexboxLayout extends FlexboxLayoutBase { child.nativeViewProtected.setLayoutParams(lp); } - public _setChildMinWidthNative(child: View): void { - child._setMinWidthNative(0); + public _setChildMinWidthNative(child: View, value: Length): void { + // Check needed to maintain back-compat after https://github.com/NativeScript/NativeScript/pull/7804 + if (!child._ignoreFlexMinWidthHeightReset) { + child._setMinWidthNative(0); + } + const nativeView = child.nativeViewProtected; const lp = nativeView.getLayoutParams(); if (lp instanceof widgetLayoutParams) { - lp.minWidth = Length.toDevicePixels(child.style.minWidth, 0); + lp.minWidth = Length.toDevicePixels(value, 0); nativeView.setLayoutParams(lp); } } - public _setChildMinHeightNative(child: View): void { - child._setMinHeightNative(0); + public _setChildMinHeightNative(child: View, value: Length): void { + // Check needed to maintain back-compat after https://github.com/NativeScript/NativeScript/pull/7804 + if (!child._ignoreFlexMinWidthHeightReset) { + child._setMinHeightNative(0); + } + const nativeView = child.nativeViewProtected; const lp = nativeView.getLayoutParams(); if (lp instanceof widgetLayoutParams) { - lp.minHeight = Length.toDevicePixels(child.style.minHeight, 0); + lp.minHeight = Length.toDevicePixels(value, 0); nativeView.setLayoutParams(lp); } } diff --git a/tns-core-modules-package/package.json b/tns-core-modules-package/package.json index 07e31a4c91..1421bbac79 100644 --- a/tns-core-modules-package/package.json +++ b/tns-core-modules-package/package.json @@ -22,4 +22,4 @@ "android": "5.0.0" } } -} \ No newline at end of file +} diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/ViewHelper.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/ViewHelper.java index 0214b02cae..ba9eba3d87 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/ViewHelper.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/ViewHelper.java @@ -5,6 +5,7 @@ import android.view.Gravity; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.TextView; /** * Created by hhristov on 8/23/16. @@ -22,6 +23,10 @@ public static int getMinWidth(android.view.View view) { } public static void setMinWidth(android.view.View view, int value) { + if (view instanceof TextView) { + ((android.widget.TextView) view).setMinWidth(value); + } + view.setMinimumWidth(value); } @@ -30,6 +35,10 @@ public static int getMinHeight(android.view.View view) { } public static void setMinHeight(android.view.View view, int value) { + if (view instanceof TextView) { + ((android.widget.TextView) view).setMinHeight(value); + } + view.setMinimumHeight(value); } @@ -47,10 +56,10 @@ public static void setWidth(android.view.View view, int value) { if (params == null) { params = new CommonLayoutParams(); } - + params.width = value; if (params instanceof CommonLayoutParams) { - ((CommonLayoutParams)params).widthPercent = -1; + ((CommonLayoutParams) params).widthPercent = -1; } view.setLayoutParams(params); @@ -65,7 +74,9 @@ public static void setWidthPercent(android.view.View view, float value) { if (params instanceof CommonLayoutParams) { CommonLayoutParams lp = (CommonLayoutParams) params; lp.widthPercent = value; - lp.width = (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL ? ViewGroup.LayoutParams.MATCH_PARENT : ViewGroup.LayoutParams.WRAP_CONTENT; + lp.width = (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL + ? ViewGroup.LayoutParams.MATCH_PARENT + : ViewGroup.LayoutParams.WRAP_CONTENT; view.setLayoutParams(params); } } @@ -84,10 +95,10 @@ public static void setHeight(android.view.View view, int value) { if (params == null) { params = new CommonLayoutParams(); } - + params.height = value; if (params instanceof CommonLayoutParams) { - ((CommonLayoutParams)params).heightPercent = -1; + ((CommonLayoutParams) params).heightPercent = -1; } view.setLayoutParams(params); @@ -102,7 +113,9 @@ public static void setHeightPercent(android.view.View view, float value) { if (params instanceof CommonLayoutParams) { CommonLayoutParams lp = (CommonLayoutParams) params; lp.heightPercent = value; - lp.height = (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL ? ViewGroup.LayoutParams.MATCH_PARENT : ViewGroup.LayoutParams.WRAP_CONTENT; + lp.height = (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL + ? ViewGroup.LayoutParams.MATCH_PARENT + : ViewGroup.LayoutParams.WRAP_CONTENT; view.setLayoutParams(params); } } @@ -321,14 +334,14 @@ public static String getHorizontalAlignment(android.view.View view) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) params; if (Gravity.isHorizontal(lp.gravity)) { switch (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - return "left"; - case Gravity.CENTER: - return "center"; - case Gravity.RIGHT: - return "right"; - case Gravity.FILL_HORIZONTAL: - return "stretch"; + case Gravity.LEFT: + return "left"; + case Gravity.CENTER: + return "center"; + case Gravity.RIGHT: + return "right"; + case Gravity.FILL_HORIZONTAL: + return "stretch"; } } @@ -348,19 +361,19 @@ public static void setHorizontalAlignment(android.view.View view, String value) if (params instanceof FrameLayout.LayoutParams) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) params; switch (value) { - case "left": - lp.gravity = Gravity.LEFT | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); - break; - case "center": - case "middle": - lp.gravity = Gravity.CENTER_HORIZONTAL | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); - break; - case "right": - lp.gravity = Gravity.RIGHT | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); - break; - case "stretch": - lp.gravity = Gravity.FILL_HORIZONTAL | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); - break; + case "left": + lp.gravity = Gravity.LEFT | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); + break; + case "center": + case "middle": + lp.gravity = Gravity.CENTER_HORIZONTAL | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); + break; + case "right": + lp.gravity = Gravity.RIGHT | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); + break; + case "stretch": + lp.gravity = Gravity.FILL_HORIZONTAL | (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK); + break; } view.setLayoutParams(params); } @@ -372,14 +385,14 @@ public static String getVerticalAlignment(android.view.View view) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) params; if (Gravity.isHorizontal(lp.gravity)) { switch (lp.gravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.TOP: - return "top"; - case Gravity.CENTER: - return "center"; - case Gravity.BOTTOM: - return "bottom"; - case Gravity.FILL_VERTICAL: - return "stretch"; + case Gravity.TOP: + return "top"; + case Gravity.CENTER: + return "center"; + case Gravity.BOTTOM: + return "bottom"; + case Gravity.FILL_VERTICAL: + return "stretch"; } } @@ -399,19 +412,19 @@ public static void setVerticalAlignment(android.view.View view, String value) { if (params instanceof FrameLayout.LayoutParams) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) params; switch (value) { - case "top": - lp.gravity = Gravity.TOP | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); - break; - case "center": - case "middle": - lp.gravity = Gravity.CENTER_VERTICAL | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); - break; - case "bottom": - lp.gravity = Gravity.BOTTOM | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); - break; - case "stretch": - lp.gravity = Gravity.FILL_VERTICAL | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); - break; + case "top": + lp.gravity = Gravity.TOP | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); + break; + case "center": + case "middle": + lp.gravity = Gravity.CENTER_VERTICAL | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); + break; + case "bottom": + lp.gravity = Gravity.BOTTOM | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); + break; + case "stretch": + lp.gravity = Gravity.FILL_VERTICAL | (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK); + break; } view.setLayoutParams(params); } @@ -480,7 +493,7 @@ public static float getRotateY(android.view.View view) { public static void setRotateY(android.view.View view, float value) { view.setRotationY(value); } - + public static void setPerspective(android.view.View view, float value) { view.setCameraDistance(value); } @@ -549,4 +562,3 @@ public static void setLetterspacing(android.widget.TextView textView, float valu } } } - diff --git a/tns-platform-declarations/package.json b/tns-platform-declarations/package.json index 710b2a5960..ff585993f1 100644 --- a/tns-platform-declarations/package.json +++ b/tns-platform-declarations/package.json @@ -34,4 +34,4 @@ "devDependencies": { "typescript": "^3.7.5" } -} \ No newline at end of file +} From 5c3ba11d95c869412e9bfb13d0abf5c80bfeb467 Mon Sep 17 00:00:00 2001 From: Vasko Date: Thu, 26 Mar 2020 16:21:00 +0200 Subject: [PATCH 09/26] chore: git ignore fix --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 57ff50b799..f7342105d6 100644 --- a/.gitignore +++ b/.gitignore @@ -72,7 +72,7 @@ nativescript-core/css/system-classes.js* nativescript-core/nativescript-core.d.ts .vs/ -e2e/ +e2e/* !e2e/animation !e2e/config !e2e/cuteness.io From c9cea472caed0439259c7c987ec2fef7e6d77c3f Mon Sep 17 00:00:00 2001 From: "Bundyo (Kamen Bundev)" Date: Thu, 26 Mar 2020 19:04:42 +0200 Subject: [PATCH 10/26] refactor(css): attribute selectors match web counterparts (#7848) * Improve CSS selector parsing/matching by 30% - 40% with some JavaScript optimization and excluding ProxyViewContainer from the process Change the specificity to be divisible to 10 * fix: selector match * fix: lint errors * refactor: restore processing of ProxyViewContainer * chore: lower the number of expected cycles * fix: some css selector fixes Co-authored-by: Manol Donev Co-authored-by: Manol Donev Co-authored-by: Vasil Trifonov --- .../ui/styling/css-selector/css-selector.d.ts | 5 +- .../ui/styling/css-selector/css-selector.ts | 191 ++++++++++-------- nativescript-core/ui/styling/style-scope.ts | 2 +- tests/webpack.config.js | 2 +- 4 files changed, 107 insertions(+), 93 deletions(-) diff --git a/nativescript-core/ui/styling/css-selector/css-selector.d.ts b/nativescript-core/ui/styling/css-selector/css-selector.d.ts index dffc447e26..3ebba8e2ba 100644 --- a/nativescript-core/ui/styling/css-selector/css-selector.d.ts +++ b/nativescript-core/ui/styling/css-selector/css-selector.d.ts @@ -12,11 +12,12 @@ export interface Node { parent?: Node; id?: string; + nodeName?: string; cssType?: string; cssClasses?: Set; cssPseudoClasses?: Set; - getChildIndex?(node: Node): number - getChildAt?(index: number): Node + getChildIndex?(node: Node): number; + getChildAt?(index: number): Node; } export interface Declaration { diff --git a/nativescript-core/ui/styling/css-selector/css-selector.ts b/nativescript-core/ui/styling/css-selector/css-selector.ts index 3bfda011af..bac516bf21 100644 --- a/nativescript-core/ui/styling/css-selector/css-selector.ts +++ b/nativescript-core/ui/styling/css-selector/css-selector.ts @@ -1,19 +1,18 @@ import { Node, Declaration, Changes, ChangeMap } from "."; import { isNullOrUndefined } from "../../../utils/types"; -import { escapeRegexSymbols } from "../../../utils/utils-common"; import * as cssParser from "../../../css"; import * as parser from "../../../css/parser"; const enum Specificity { - Inline = 0x01000000, - Id = 0x00010000, - Attribute = 0x00000100, - Class = 0x00000100, - PseudoClass = 0x00000100, - Type = 0x00000001, - Universal = 0x00000000, - Invalid = 0x00000000 + Inline = 1000, + Id = 100, + Attribute = 10, + Class = 10, + PseudoClass = 10, + Type = 1, + Universal = 0, + Invalid = 0 } const enum Rarity { @@ -71,6 +70,7 @@ function SelectorProperties(specificity: Specificity, rarity: Rarity, dynamic: b declare type Combinator = "+" | ">" | "~" | " "; @SelectorProperties(Specificity.Universal, Rarity.Universal, Match.Static) export abstract class SelectorCore { + public pos: number; public specificity: number; public rarity: Rarity; public combinator: Combinator; @@ -167,43 +167,35 @@ export class AttributeSelector extends SimpleSelector { this.match = node => false; } - let escapedValue = escapeRegexSymbols(value); - let regexp: RegExp = null; - switch (test) { - case "^=": // PrefixMatch - regexp = new RegExp("^" + escapedValue); - break; - case "$=": // SuffixMatch - regexp = new RegExp(escapedValue + "$"); - break; - case "*=": // SubstringMatch - regexp = new RegExp(escapedValue); - break; - case "=": // Equals - regexp = new RegExp("^" + escapedValue + "$"); - break; - case "~=": // Includes - if (/\s/.test(value)) { - this.match = node => false; - - return; - } - regexp = new RegExp("(^|\\s)" + escapedValue + "(\\s|$)"); - break; - case "|=": // DashMatch - regexp = new RegExp("^" + escapedValue + "(-|$)"); - break; - } + this.match = node => { + const attr = node[attribute] + ""; - if (regexp) { - this.match = node => regexp.test(node[attribute] + ""); + if (test === "=") { // Equals + return attr === value; + } - return; - } else { - this.match = node => false; + if (test === "^=") { // PrefixMatch + return attr.startsWith(value); + } - return; - } + if (test === "$=") { // SuffixMatch + return attr.endsWith(value); + } + + if (test === "*=") { // SubstringMatch + return attr.indexOf(value) !== -1; + } + + if (test === "~=") { // Includes + const words = attr.split(" "); + + return words && words.indexOf(value) !== -1; + } + + if (test === "|=") { // DashMatch + return attr === value || attr.startsWith(value + "-"); + } + }; } public toString(): string { return `[${this.attribute}${wrap(this.test)}${(this.test && this.value) || ""}]${wrap(this.combinator)}`; } public match(node: Node): boolean { return false; } @@ -252,7 +244,13 @@ export class Selector extends SelectorCore { let siblingGroup: SimpleSelector[]; let lastGroup: SimpleSelector[][]; let groups: SimpleSelector[][][] = []; - selectors.reverse().forEach(sel => { + + this.specificity = 0; + this.dynamic = false; + + for (let i = selectors.length - 1; i > -1; i--) { + const sel = selectors[i]; + if (supportedCombinator.indexOf(sel.combinator) === -1) { throw new Error(`Unsupported combinator "${sel.combinator}".`); } @@ -262,16 +260,22 @@ export class Selector extends SelectorCore { if (sel.combinator === ">") { lastGroup.push(siblingGroup = []); } + + this.specificity += sel.specificity; + + if (sel.dynamic) { + this.dynamic = true; + } + siblingGroup.push(sel); - }); + } + this.groups = groups.map(g => new Selector.ChildGroup(g.map(sg => new Selector.SiblingGroup(sg) )) ); - this.last = selectors[0]; - this.specificity = selectors.reduce((sum, sel) => sel.specificity + sum, 0); - this.dynamic = selectors.some(sel => sel.dynamic); + this.last = selectors[selectors.length - 1]; } public toString(): string { return this.selectors.join(""); } @@ -364,15 +368,15 @@ export namespace Selector { } public match(node: Node): Node { - return this.selectors.every((sel, i) => (i === 0 ? node : node = node.parent) && !!sel.match(node)) ? node : null; + return this.selectors.every((sel, i) => (node = (i === 0 ? node : node.parent)) && sel.match(node)) ? node : null; } public mayMatch(node: Node): Node { - return this.selectors.every((sel, i) => (i === 0 ? node : node = node.parent) && !!sel.mayMatch(node)) ? node : null; + return this.selectors.every((sel, i) => (node = (i === 0 ? node : node.parent)) && sel.mayMatch(node)) ? node : null; } public trackChanges(node: Node, map: ChangeAccumulator) { - this.selectors.forEach((sel, i) => (i === 0 ? node : node = node.parent) && sel.trackChanges(node, map)); + this.selectors.forEach((sel, i) => (node = (i === 0 ? node : node.parent)) && sel.trackChanges(node, map)); } } export class SiblingGroup { @@ -383,15 +387,15 @@ export namespace Selector { } public match(node: Node): Node { - return this.selectors.every((sel, i) => (i === 0 ? node : node = getNodeDirectSibling(node)) && sel.match(node)) ? node : null; + return this.selectors.every((sel, i) => (node = (i === 0 ? node : getNodeDirectSibling(node))) && sel.match(node)) ? node : null; } public mayMatch(node: Node): Node { - return this.selectors.every((sel, i) => (i === 0 ? node : node = getNodeDirectSibling(node)) && sel.mayMatch(node)) ? node : null; + return this.selectors.every((sel, i) => (node = (i === 0 ? node : getNodeDirectSibling(node))) && sel.mayMatch(node)) ? node : null; } public trackChanges(node: Node, map: ChangeAccumulator) { - this.selectors.forEach((sel, i) => (i === 0 ? node : node = getNodeDirectSibling(node)) && sel.trackChanges(node, map)); + this.selectors.forEach((sel, i) => (node = (i === 0 ? node : getNodeDirectSibling(node))) && sel.trackChanges(node, map)); } } export interface Bound { @@ -412,9 +416,8 @@ export function fromAstNodes(astRules: cssParser.Node[]): RuleSet[] { return (astRules.filter(isRule)).map(rule => { let declarations = rule.declarations.filter(isDeclaration).map(createDeclaration); let selectors = rule.selectors.map(createSelector); - let ruleset = new RuleSet(selectors, declarations); - return ruleset; + return new RuleSet(selectors, declarations); }); } @@ -423,13 +426,28 @@ function createDeclaration(decl: cssParser.Declaration): any { } function createSimpleSelectorFromAst(ast: parser.SimpleSelector): SimpleSelector { - switch (ast.type) { - case "*": return new UniversalSelector(); - case "#": return new IdSelector(ast.identifier); - case "": return new TypeSelector(ast.identifier.replace(/-/, "").toLowerCase()); - case ".": return new ClassSelector(ast.identifier); - case ":": return new PseudoClassSelector(ast.identifier); - case "[]": return ast.test ? new AttributeSelector(ast.property, ast.test, ast.value) : new AttributeSelector(ast.property); + if (ast.type === ".") { + return new ClassSelector(ast.identifier); + } + + if (ast.type === "") { + return new TypeSelector(ast.identifier.replace("-", "").toLowerCase()); + } + + if (ast.type === "#") { + return new IdSelector(ast.identifier); + } + + if (ast.type === "[]") { + return new AttributeSelector(ast.property, ast.test, ast.test && ast.value); + } + + if (ast.type === ":") { + return new PseudoClassSelector(ast.identifier); + } + + if (ast.type === "*") { + return new UniversalSelector(); } } @@ -485,18 +503,14 @@ function isDeclaration(node: cssParser.Node): node is cssParser.Declaration { return node.type === "declaration"; } -interface SelectorInDocument { - pos: number; - sel: SelectorCore; -} interface SelectorMap { - [key: string]: SelectorInDocument[]; + [key: string]: SelectorCore[]; } export class SelectorsMap implements LookupSorter { private id: SelectorMap = {}; private class: SelectorMap = {}; private type: SelectorMap = {}; - private universal: SelectorInDocument[] = []; + private universal: SelectorCore[] = []; private position = 0; @@ -505,24 +519,23 @@ export class SelectorsMap implements LookupSorter { } query(node: T): SelectorsMatch { - let selectorClasses = [ + const selectorsMatch = new SelectorsMatch(); + const { cssClasses, id, cssType } = node; + const selectorClasses = [ this.universal, - this.id[node.id], - this.type[node.cssType] + this.id[id], + this.type[cssType] ]; - if (node.cssClasses) { - node.cssClasses.forEach(c => selectorClasses.push(this.class[c])); + + if (cssClasses && cssClasses.size) { + cssClasses.forEach(c => selectorClasses.push(this.class[c])); } - let selectors = selectorClasses - .filter(arr => !!arr) - .reduce((cur, next) => cur.concat(next), []); - let selectorsMatch = new SelectorsMatch(); + const selectors = selectorClasses.reduce((cur, next) => cur.concat(next || []), []); selectorsMatch.selectors = selectors - .filter(sel => sel.sel.accumulateChanges(node, selectorsMatch)) - .sort((a, b) => a.sel.specificity - b.sel.specificity || a.pos - b.pos) - .map(docSel => docSel.sel); + .filter(sel => sel.accumulateChanges(node, selectorsMatch)) + .sort((a, b) => a.specificity - b.specificity || a.pos - b.pos); return selectorsMatch; } @@ -537,17 +550,17 @@ export class SelectorsMap implements LookupSorter { sortAsUniversal(sel: SelectorCore): void { this.universal.push(this.makeDocSelector(sel)); } private addToMap(map: SelectorMap, head: string, sel: SelectorCore): void { - this.position++; - let list = map[head]; - if (list) { - list.push(this.makeDocSelector(sel)); - } else { - map[head] = [this.makeDocSelector(sel)]; + if (!map[head]) { + map[head] = []; } + + map[head].push(this.makeDocSelector(sel)); } - private makeDocSelector(sel: SelectorCore): SelectorInDocument { - return { sel, pos: this.position++ }; + private makeDocSelector(sel: SelectorCore): SelectorCore { + sel.pos = this.position++; + + return sel; } } diff --git a/nativescript-core/ui/styling/style-scope.ts b/nativescript-core/ui/styling/style-scope.ts index 47b96e92bc..0f4addb6bf 100644 --- a/nativescript-core/ui/styling/style-scope.ts +++ b/nativescript-core/ui/styling/style-scope.ts @@ -801,7 +801,7 @@ export class StyleScope { } if (toMerge.length > 0) { - this._mergedCssSelectors = toMerge.filter(m => !!m).reduce((merged, next) => merged.concat(next), []); + this._mergedCssSelectors = toMerge.reduce((merged, next) => merged.concat(next || []), []); this._applyKeyframesOnSelectors(); this._selectors = new SelectorsMap(this._mergedCssSelectors); } diff --git a/tests/webpack.config.js b/tests/webpack.config.js index e3f351435f..fbc8991441 100644 --- a/tests/webpack.config.js +++ b/tests/webpack.config.js @@ -12,7 +12,7 @@ const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeS const TerserPlugin = require("terser-webpack-plugin"); const hashSalt = Date.now().toString(); -const ANDROID_MAX_CYCLES = 66; +const ANDROID_MAX_CYCLES = 65; const IOS_MAX_CYCLES = 32; let numCyclesDetected = 0; From dc7215fb0233145e5095a59bbef36534e623be15 Mon Sep 17 00:00:00 2001 From: Hk-tang Date: Mon, 30 Mar 2020 09:47:39 -0600 Subject: [PATCH 11/26] build: remove unecessary permission grant (#8464) Granting WRITE_EXTERNAL_PERMISSION implicitly adds READ_EXTERNAL_PERMISSION --- .../app/App_Resources/Android/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/e2e/file-qualifiers/app/App_Resources/Android/src/main/AndroidManifest.xml b/e2e/file-qualifiers/app/App_Resources/Android/src/main/AndroidManifest.xml index 1bd6826c34..99b1823976 100644 --- a/e2e/file-qualifiers/app/App_Resources/Android/src/main/AndroidManifest.xml +++ b/e2e/file-qualifiers/app/App_Resources/Android/src/main/AndroidManifest.xml @@ -10,7 +10,6 @@ android:largeScreens="true" android:xlargeScreens="true"/> - From 2b06adca2740e951fa9c0713667ff4f2ac33aa20 Mon Sep 17 00:00:00 2001 From: Nick Iliev Date: Wed, 1 Apr 2020 17:41:58 +0300 Subject: [PATCH 12/26] fix: call to nativeView (#8488) --- nativescript-core/ui/text-base/text-base.android.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-core/ui/text-base/text-base.android.ts b/nativescript-core/ui/text-base/text-base.android.ts index 200285c8aa..356d32badc 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -104,7 +104,7 @@ export class TextBase extends TextBaseCommon { initializeTextTransformation(); const nativeView = this.nativeTextViewProtected; this._defaultTransformationMethod = nativeView.getTransformationMethod(); - this._defaultMovementMethod = this.nativeView.getMovementMethod(); + this._defaultMovementMethod = nativeView.getMovementMethod(); this._minHeight = nativeView.getMinHeight(); this._maxHeight = nativeView.getMaxHeight(); this._minLines = nativeView.getMinLines(); From e4211294580e20ca83668bd7ee584a77a25fae28 Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Wed, 1 Apr 2020 17:42:24 +0300 Subject: [PATCH 13/26] fix: do not clear androidView._cachedDrawable (#8475) --- nativescript-core/ui/styling/background.android.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/nativescript-core/ui/styling/background.android.ts b/nativescript-core/ui/styling/background.android.ts index 7b545217ef..6b6d8b85a3 100644 --- a/nativescript-core/ui/styling/background.android.ts +++ b/nativescript-core/ui/styling/background.android.ts @@ -91,8 +91,6 @@ export module ad { } nativeView.setBackground(defaultDrawable); - // TODO: Do we need to clear the drawable here? Can't we just reuse it again? - androidView._cachedDrawable = undefined; } // TODO: Can we move BorderWidths as separate native setter? From 56f6626891a701078ba3b5c0f42173d9d3014b69 Mon Sep 17 00:00:00 2001 From: Nick Iliev Date: Thu, 2 Apr 2020 14:18:47 +0300 Subject: [PATCH 14/26] fix: remove duplciate snippet (#8492) --- tests/app/ui/text-field/text-field-tests.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/app/ui/text-field/text-field-tests.ts b/tests/app/ui/text-field/text-field-tests.ts index 7f46aa0226..2eca0726dc 100644 --- a/tests/app/ui/text-field/text-field-tests.ts +++ b/tests/app/ui/text-field/text-field-tests.ts @@ -463,10 +463,7 @@ export var testCloseOnReturn = function () { return; } var textField = views[0]; - - // >> setting-closeOnReturn-property textField.closeOnReturn = true; - // << setting-closeOnReturn-property typeTextNativelyWithReturn(textField, "Should close textfield"); From ad9daa825f25b528fcd4730893710642381aaafe Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Fri, 3 Apr 2020 15:40:14 +0200 Subject: [PATCH 15/26] fix: dont default to Font.default (#8401) * fix: dont default to Font.default This would cause a font to be set for any label even when using default system font. This will also cause a typeface which is pretty long * lint: lint fixes * fix: added null font guards * fix: Used default bold for TabView Co-authored-by: Vasko --- .../image-source/image-source.android.ts | 1 + .../image-source/image-source.ios.ts | 1 + .../bottom-navigation.ios.ts | 4 ++-- .../ui/styling/style-properties.ts | 13 +++++++----- nativescript-core/ui/tab-view/tab-view.ios.ts | 2 +- nativescript-core/ui/tabs/tabs.ios.ts | 6 +++--- .../bottom-navigation-tests-native.ios.ts | 3 ++- .../ui/tab-view/tab-view-tests-native.ios.ts | 3 ++- tests/app/ui/tabs/tabs-tests-native.ios.ts | 3 ++- .../widgets/BottomNavigationBar.java | 1 - .../org/nativescript/widgets/TabLayout.java | 20 +++++++++---------- .../org/nativescript/widgets/TabsBar.java | 1 - 12 files changed, 32 insertions(+), 26 deletions(-) diff --git a/nativescript-core/image-source/image-source.android.ts b/nativescript-core/image-source/image-source.android.ts index 17479ad10d..18a0fd76ec 100644 --- a/nativescript-core/image-source/image-source.android.ts +++ b/nativescript-core/image-source/image-source.android.ts @@ -176,6 +176,7 @@ export class ImageSource implements ImageSourceDefinition { } static fromFontIconCodeSync(source: string, font: Font, color: Color): ImageSource { + font = font || Font.default; const paint = new android.graphics.Paint(); paint.setTypeface(font.getAndroidTypeface()); paint.setAntiAlias(true); diff --git a/nativescript-core/image-source/image-source.ios.ts b/nativescript-core/image-source/image-source.ios.ts index c020bd0fee..c0d007a1af 100644 --- a/nativescript-core/image-source/image-source.ios.ts +++ b/nativescript-core/image-source/image-source.ios.ts @@ -165,6 +165,7 @@ export class ImageSource implements ImageSourceDefinition { } static fromFontIconCodeSync(source: string, font: Font, color: Color): ImageSource { + font = font || Font.default; let fontSize = layout.toDevicePixels(font.fontSize); if (!fontSize) { // TODO: Consider making 36 font size as default for optimal look on TabView and ActionBar diff --git a/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts b/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts index e57f718d73..73fc214b25 100644 --- a/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts +++ b/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts @@ -654,7 +654,7 @@ export class BottomNavigation extends TabNavigationBase { } const target = tabStripItem.image; - const font = target.style.fontInternal; + const font = target.style.fontInternal || Font.default; if (!color) { color = target.style.color; } @@ -774,7 +774,7 @@ export class BottomNavigation extends TabNavigationBase { const defaultTabItemFontSize = 10; const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize; - const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); + const font: UIFont = (view.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); const tabItemTextColor = view.style.color; const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null; let attributes: any = { [NSFontAttributeName]: font }; diff --git a/nativescript-core/ui/styling/style-properties.ts b/nativescript-core/ui/styling/style-properties.ts index 8ee732be0c..c2628fa2a5 100644 --- a/nativescript-core/ui/styling/style-properties.ts +++ b/nativescript-core/ui/styling/style-properties.ts @@ -1154,12 +1154,12 @@ opacityProperty.register(Style); export const colorProperty = new InheritedCssProperty({ name: "color", cssName: "color", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) }); colorProperty.register(Style); -export const fontInternalProperty = new CssProperty({ name: "fontInternal", cssName: "_fontInternal", defaultValue: Font.default }); +export const fontInternalProperty = new CssProperty({ name: "fontInternal", cssName: "_fontInternal" }); fontInternalProperty.register(Style); export const fontFamilyProperty = new InheritedCssProperty({ name: "fontFamily", cssName: "font-family", affectsLayout: isIOS, valueChanged: (target, oldValue, newValue) => { - let currentFont = target.fontInternal; + let currentFont = target.fontInternal || Font.default; if (currentFont.fontFamily !== newValue) { const newFont = currentFont.withFontFamily(newValue); target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont; @@ -1170,7 +1170,10 @@ fontFamilyProperty.register(Style); export const fontSizeProperty = new InheritedCssProperty({ name: "fontSize", cssName: "font-size", affectsLayout: isIOS, valueChanged: (target, oldValue, newValue) => { - let currentFont = target.fontInternal; + if (target.viewRef["handleFontSize"] === true) { + return; + } + let currentFont = target.fontInternal || Font.default; if (currentFont.fontSize !== newValue) { const newFont = currentFont.withFontSize(newValue); target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont; @@ -1182,7 +1185,7 @@ fontSizeProperty.register(Style); export const fontStyleProperty = new InheritedCssProperty({ name: "fontStyle", cssName: "font-style", affectsLayout: isIOS, defaultValue: FontStyle.NORMAL, valueConverter: FontStyle.parse, valueChanged: (target, oldValue, newValue) => { - let currentFont = target.fontInternal; + let currentFont = target.fontInternal || Font.default; if (currentFont.fontStyle !== newValue) { const newFont = currentFont.withFontStyle(newValue); target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont; @@ -1193,7 +1196,7 @@ fontStyleProperty.register(Style); export const fontWeightProperty = new InheritedCssProperty({ name: "fontWeight", cssName: "font-weight", affectsLayout: isIOS, defaultValue: FontWeight.NORMAL, valueConverter: FontWeight.parse, valueChanged: (target, oldValue, newValue) => { - let currentFont = target.fontInternal; + let currentFont = target.fontInternal || Font.default; if (currentFont.fontWeight !== newValue) { const newFont = currentFont.withFontWeight(newValue); target.fontInternal = Font.equals(Font.default, newFont) ? unsetValue : newFont; diff --git a/nativescript-core/ui/tab-view/tab-view.ios.ts b/nativescript-core/ui/tab-view/tab-view.ios.ts index ec96eaaab7..d6d61211dd 100644 --- a/nativescript-core/ui/tab-view/tab-view.ios.ts +++ b/nativescript-core/ui/tab-view/tab-view.ios.ts @@ -587,7 +587,7 @@ function getTitleAttributesForStates(tabView: TabView): TabStates { const defaultTabItemFontSize = 10; const tabItemFontSize = tabView.style.tabTextFontSize || defaultTabItemFontSize; - const font: UIFont = tabView.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); + const font: UIFont = (tabView.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); const tabItemTextColor = tabView.style.tabTextColor; const textColor = tabItemTextColor instanceof Color ? tabItemTextColor.ios : null; result.normalState = { [NSFontAttributeName]: font }; diff --git a/nativescript-core/ui/tabs/tabs.ios.ts b/nativescript-core/ui/tabs/tabs.ios.ts index 15c81af55b..e22132fa7c 100644 --- a/nativescript-core/ui/tabs/tabs.ios.ts +++ b/nativescript-core/ui/tabs/tabs.ios.ts @@ -824,7 +824,7 @@ export class Tabs extends TabsBase { } const target = tabStripItem.image; - const font = target.style.fontInternal; + const font = target.style.fontInternal || Font.default; if (!color) { color = target.style.color; } @@ -997,7 +997,7 @@ export class Tabs extends TabsBase { public setTabBarFontInternal(value: Font): void { const defaultTabItemFontSize = 10; const tabItemFontSize = this.tabStrip.style.fontSize || defaultTabItemFontSize; - const font: UIFont = this.tabStrip.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); + const font: UIFont = (this.tabStrip.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); this._ios.tabBar.unselectedItemTitleFont = font; this._ios.tabBar.selectedItemTitleFont = font; @@ -1194,7 +1194,7 @@ export class Tabs extends TabsBase { const defaultTabItemFontSize = 10; const tabItemFontSize = view.style.fontSize || defaultTabItemFontSize; - const font: UIFont = view.style.fontInternal.getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); + const font: UIFont = (view.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(tabItemFontSize)); this.viewController.tabBar.unselectedItemTitleFont = font; this.viewController.tabBar.selectedItemTitleFont = font; diff --git a/tests/app/ui/bottom-navigation/bottom-navigation-tests-native.ios.ts b/tests/app/ui/bottom-navigation/bottom-navigation-tests-native.ios.ts index c7a3548a94..f1caa18fd0 100644 --- a/tests/app/ui/bottom-navigation/bottom-navigation-tests-native.ios.ts +++ b/tests/app/ui/bottom-navigation/bottom-navigation-tests-native.ios.ts @@ -1,4 +1,5 @@ import tabViewModule = require("@nativescript/core/ui/tab-view"); +import { Font } from "@nativescript/core/ui/styling/font"; export function getNativeTabCount(tabView: tabViewModule.TabView): number { if (!tabView.ios.viewControllers) { @@ -30,5 +31,5 @@ export function getNativeFont(tabView: tabViewModule.TabView): UIFont { } export function getOriginalFont(tabView: tabViewModule.TabView): UIFont { - return tabView.style.fontInternal.getUIFont(UIFont.systemFontOfSize(10)); + return (tabView.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(10)); } diff --git a/tests/app/ui/tab-view/tab-view-tests-native.ios.ts b/tests/app/ui/tab-view/tab-view-tests-native.ios.ts index c7a3548a94..f1caa18fd0 100644 --- a/tests/app/ui/tab-view/tab-view-tests-native.ios.ts +++ b/tests/app/ui/tab-view/tab-view-tests-native.ios.ts @@ -1,4 +1,5 @@ import tabViewModule = require("@nativescript/core/ui/tab-view"); +import { Font } from "@nativescript/core/ui/styling/font"; export function getNativeTabCount(tabView: tabViewModule.TabView): number { if (!tabView.ios.viewControllers) { @@ -30,5 +31,5 @@ export function getNativeFont(tabView: tabViewModule.TabView): UIFont { } export function getOriginalFont(tabView: tabViewModule.TabView): UIFont { - return tabView.style.fontInternal.getUIFont(UIFont.systemFontOfSize(10)); + return (tabView.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(10)); } diff --git a/tests/app/ui/tabs/tabs-tests-native.ios.ts b/tests/app/ui/tabs/tabs-tests-native.ios.ts index e55b8307de..f33e6e7790 100644 --- a/tests/app/ui/tabs/tabs-tests-native.ios.ts +++ b/tests/app/ui/tabs/tabs-tests-native.ios.ts @@ -1,4 +1,5 @@ import { Tabs } from "@nativescript/core/ui/tabs"; +import { Font } from "@nativescript/core/ui/styling/font"; // TODO: Should we add getCount to UIPageViewController??? export function getNativeTabCount(tabView: Tabs): number { @@ -39,5 +40,5 @@ export function getNativeFont(tabView: Tabs): UIFont { } export function getOriginalFont(tabView: Tabs): UIFont { - return tabView.style.fontInternal.getUIFont(UIFont.systemFontOfSize(10)); + return (tabView.style.fontInternal || Font.default).getUIFont(UIFont.systemFontOfSize(10)); } diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BottomNavigationBar.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BottomNavigationBar.java index 13760a6838..98ab8834ed 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BottomNavigationBar.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/BottomNavigationBar.java @@ -178,7 +178,6 @@ protected View createDefaultTabView(Context context, TabItemSpec tabItem) { titleTextView.setGravity(Gravity.CENTER); titleTextView.setMaxWidth((int) (ITEM_TEXT_MAX_WIDTH * density)); titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ITEM_TEXT_SIZE_SP); - titleTextView.setTypeface(Typeface.DEFAULT_BOLD); titleTextView.setEllipsize(TextUtils.TruncateAt.END); titleTextView.setMaxLines(1); titleTextView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabLayout.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabLayout.java index a784d5431a..32337a760e 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabLayout.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabLayout.java @@ -160,7 +160,7 @@ public float getTabTextFontSize(){ * {@link TabLayout} you are required to set any * {@link ViewPager.OnPageChangeListener} through this method. This is so * that the layout can update it's scroll position correctly. - * + * * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) */ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { @@ -196,25 +196,25 @@ public void updateItemAt(int position, TabItemSpec tabItem) { TextView textView = (TextView)ll.getChildAt(1); this.setupItem(ll, textView, imgView, tabItem); } - + /** * Gets the TextView for tab item at index */ public TextView getTextViewForItemAt(int index){ LinearLayout ll = this.getViewForItemAt(index); - return (ll != null) ? (TextView)ll.getChildAt(1) : null; + return (ll != null) ? (TextView)ll.getChildAt(1) : null; } - + /** * Gets the LinearLayout container for tab item at index */ public LinearLayout getViewForItemAt(int index){ LinearLayout result = null; - + if(this.mTabStrip.getChildCount() > index){ result = (LinearLayout)this.mTabStrip.getChildAt(index); } - + return result; } @@ -263,10 +263,10 @@ protected View createDefaultTabView(Context context, TabItemSpec tabItem) { ll.addView(textView); return ll; } - + private void setupItem(LinearLayout ll, TextView textView,ImageView imgView, TabItemSpec tabItem){ float density = getResources().getDisplayMetrics().density; - + if (tabItem.iconId != 0) { imgView.setImageResource(tabItem.iconId); imgView.setVisibility(VISIBLE); @@ -293,7 +293,7 @@ private void setupItem(LinearLayout ll, TextView textView,ImageView imgView, Tab } else { ll.setMinimumHeight((int) (SMALL_MIN_HEIGHT * density)); } - + if (mDistributeEvenly) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); lp.width = 0; @@ -438,4 +438,4 @@ public void onClick(View v) { } } } -} \ No newline at end of file +} diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java index b34137ba4d..3f9e53dbe2 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java @@ -250,7 +250,6 @@ protected View createDefaultTabView(Context context, TabItemSpec tabItem) { textView.setGravity(Gravity.CENTER); textView.setMaxWidth((int) (TEXT_MAX_WIDTH * density)); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); - textView.setTypeface(Typeface.DEFAULT_BOLD); textView.setEllipsize(TextUtils.TruncateAt.END); textView.setMaxLines(2); textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); From da80cd5e1a710343373c4fbd1e1750a12b2fac7a Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Fri, 3 Apr 2020 17:27:37 +0300 Subject: [PATCH 16/26] test: added missing page (#8493) * test: added missing page * fix: use strings instead of Visiblity --- .../app/button/tappable-span-page.ts | 27 +++++++++++++++++++ .../app/button/tappable-span-page.xml | 19 +++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 e2e/ui-tests-app/app/button/tappable-span-page.ts create mode 100644 e2e/ui-tests-app/app/button/tappable-span-page.xml diff --git a/e2e/ui-tests-app/app/button/tappable-span-page.ts b/e2e/ui-tests-app/app/button/tappable-span-page.ts new file mode 100644 index 0000000000..44e1a5c937 --- /dev/null +++ b/e2e/ui-tests-app/app/button/tappable-span-page.ts @@ -0,0 +1,27 @@ +import { Label } from "tns-core-modules/ui/label"; +import { EventData, TextBase } from "tns-core-modules/ui/text-base"; +import { Page } from "tns-core-modules/ui/page"; + +export function foxTap(args: EventData) { + let page = (args.object).page; + let foxTapped = page.getViewById