From ab4c389ede1f4a40da2a170fcbd5bc934d0718cb Mon Sep 17 00:00:00 2001 From: Darin Dimitrov Date: Tue, 10 Dec 2019 17:16:20 +0200 Subject: [PATCH 01/23] feat: Make css-tree the default parser --- nativescript-core/ui/styling/style-scope.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nativescript-core/ui/styling/style-scope.ts b/nativescript-core/ui/styling/style-scope.ts index ea4087ccdd..06c0e80005 100644 --- a/nativescript-core/ui/styling/style-scope.ts +++ b/nativescript-core/ui/styling/style-scope.ts @@ -53,12 +53,12 @@ function ensureCssAnimationParserModule() { } } -let parser: "rework" | "nativescript" | "css-tree" = "rework"; +let parser: "rework" | "nativescript" | "css-tree" = "css-tree"; try { const appConfig = require("~/package.json"); if (appConfig) { - if (appConfig.cssParser === "css-tree") { - parser = "css-tree"; + if (appConfig.cssParser === "rework") { + parser = "rework"; } else if (appConfig.cssParser === "nativescript") { parser = "nativescript"; } From 954e1c61b55990261ee16639f3366527697b4a4e Mon Sep 17 00:00:00 2001 From: NickIliev Date: Thu, 12 Dec 2019 10:45:20 +0200 Subject: [PATCH 02/23] feat: add integer only keyboard type for text-field and for all editable text components --- .../editable-text-base/editable-text-base-common.ts | 2 +- .../editable-text-base/editable-text-base.android.ts | 7 ++++++- .../ui/editable-text-base/editable-text-base.d.ts | 2 +- .../ui/editable-text-base/editable-text-base.ios.ts | 11 +++++++++-- nativescript-core/ui/enums/enums.d.ts | 6 ++++++ nativescript-core/ui/enums/enums.ts | 1 + nativescript-core/ui/text-field/text-field.android.ts | 3 +++ 7 files changed, 27 insertions(+), 5 deletions(-) diff --git a/nativescript-core/ui/editable-text-base/editable-text-base-common.ts b/nativescript-core/ui/editable-text-base/editable-text-base-common.ts index 24937a733d..46d6d306e6 100644 --- a/nativescript-core/ui/editable-text-base/editable-text-base-common.ts +++ b/nativescript-core/ui/editable-text-base/editable-text-base-common.ts @@ -38,7 +38,7 @@ export abstract class EditableTextBase extends TextBase implements EditableTextB export const placeholderColorProperty = new CssProperty({ name: "placeholderColor", cssName: "placeholder-color", equalityComparer: Color.equals, valueConverter: (v) => new Color(v) }); placeholderColorProperty.register(Style); -const keyboardTypeConverter = makeParser(makeValidator("datetime", "phone", "number", "url", "email")); +const keyboardTypeConverter = makeParser(makeValidator("datetime", "phone", "number", "url", "email", "integer")); export const keyboardTypeProperty = new Property({ name: "keyboardType", valueConverter: keyboardTypeConverter }); keyboardTypeProperty.register(EditableTextBase); diff --git a/nativescript-core/ui/editable-text-base/editable-text-base.android.ts b/nativescript-core/ui/editable-text-base/editable-text-base.android.ts index ce0c15eebe..23d40bd0e2 100644 --- a/nativescript-core/ui/editable-text-base/editable-text-base.android.ts +++ b/nativescript-core/ui/editable-text-base/editable-text-base.android.ts @@ -248,8 +248,9 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { [keyboardTypeProperty.getDefault](): number { return this.nativeTextViewProtected.getInputType(); } - [keyboardTypeProperty.setNative](value: "datetime" | "phone" | "number" | "url" | "email" | number) { + [keyboardTypeProperty.setNative](value: "datetime" | "phone" | "number" | "url" | "email" | "integer" | number) { let newInputType; + switch (value) { case "datetime": newInputType = android.text.InputType.TYPE_CLASS_DATETIME | android.text.InputType.TYPE_DATETIME_VARIATION_NORMAL; @@ -271,6 +272,10 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { newInputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; break; + case "integer": + newInputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD; + break; + default: newInputType = value; break; diff --git a/nativescript-core/ui/editable-text-base/editable-text-base.d.ts b/nativescript-core/ui/editable-text-base/editable-text-base.d.ts index 43585d97b9..ca145aa1ae 100644 --- a/nativescript-core/ui/editable-text-base/editable-text-base.d.ts +++ b/nativescript-core/ui/editable-text-base/editable-text-base.d.ts @@ -65,7 +65,7 @@ export class EditableTextBase extends TextBase { //@endprivate } -export type KeyboardType = "datetime" | "phone" | "number" | "url" | "email"; +export type KeyboardType = "datetime" | "phone" | "number" | "url" | "email" | "integer"; export type ReturnKeyType = "done" | "next" | "go" | "search" | "send"; export type UpdateTextTrigger = "focusLost" | "textChanged"; export type AutocapitalizationType = "none" | "words" | "sentences" | "allcharacters"; diff --git a/nativescript-core/ui/editable-text-base/editable-text-base.ios.ts b/nativescript-core/ui/editable-text-base/editable-text-base.ios.ts index 747fd261a8..4f74250fc4 100644 --- a/nativescript-core/ui/editable-text-base/editable-text-base.ios.ts +++ b/nativescript-core/ui/editable-text-base/editable-text-base.ios.ts @@ -13,7 +13,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { this.notify({ eventName: EditableTextBase.blurEvent, object: this }); } - [keyboardTypeProperty.getDefault](): "datetime" | "phone" | "number" | "url" | "email" | string { + [keyboardTypeProperty.getDefault](): "datetime" | "phone" | "number" | "url" | "email" | "integer" | string { let keyboardType = this.nativeTextViewProtected.keyboardType; switch (keyboardType) { case UIKeyboardType.NumbersAndPunctuation: @@ -28,11 +28,14 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { case UIKeyboardType.EmailAddress: return "email"; + case UIKeyboardType.NumberPad: + return "integer"; + default: return keyboardType.toString(); } } - [keyboardTypeProperty.setNative](value: "datetime" | "phone" | "number" | "url" | "email" | string) { + [keyboardTypeProperty.setNative](value: "datetime" | "phone" | "number" | "url" | "email" | "integer" | string) { let newKeyboardType: UIKeyboardType; switch (value) { case "datetime": @@ -55,6 +58,10 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { newKeyboardType = UIKeyboardType.EmailAddress; break; + case "integer": + newKeyboardType = UIKeyboardType.NumberPad; + break; + default: let kt = +value; if (!isNaN(kt)) { diff --git a/nativescript-core/ui/enums/enums.d.ts b/nativescript-core/ui/enums/enums.d.ts index 7181606b3a..2c82c99ebe 100644 --- a/nativescript-core/ui/enums/enums.d.ts +++ b/nativescript-core/ui/enums/enums.d.ts @@ -73,6 +73,12 @@ export module KeyboardType { * iOS: [UIKeyboardTypeEmailAddress](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType) */ export const email: BaseKeyboardType + + /** + * Android: [TYPE_CLASS_NUMBER](http://developer.android.com/reference/android/text/InputType.html#TYPE_CLASS_NUMBER | [TYPE_NUMBER_VARIATION_PASSWORD](android type_text_variation_password)) + * iOS: [UIKeyboardTypeNumberPad](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextInputTraits_Protocol/index.html#//apple_ref/c/tdef/UIKeyboardType) + */ + export const integer: BaseKeyboardType } /** diff --git a/nativescript-core/ui/enums/enums.ts b/nativescript-core/ui/enums/enums.ts index 97558a2e25..702cd83303 100644 --- a/nativescript-core/ui/enums/enums.ts +++ b/nativescript-core/ui/enums/enums.ts @@ -7,6 +7,7 @@ export module KeyboardType { export const number = "number"; export const url = "url"; export const email = "email"; + export const integer = "integer"; } export module ReturnKeyType { diff --git a/nativescript-core/ui/text-field/text-field.android.ts b/nativescript-core/ui/text-field/text-field.android.ts index e86e4f1f6b..bd8afa4b7c 100644 --- a/nativescript-core/ui/text-field/text-field.android.ts +++ b/nativescript-core/ui/text-field/text-field.android.ts @@ -77,6 +77,9 @@ export class TextField extends TextFieldBase { case "email": inputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; break; + case "integer": + inputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD; + break; default: break; } From 416f1c8101673633a5548466a5a90154356a24a2 Mon Sep 17 00:00:00 2001 From: NickIliev Date: Fri, 3 Jan 2020 12:57:28 +0200 Subject: [PATCH 03/23] feat: add longPress state with UIGestureRecognizer (iOS) --- .../ui/gestures/gestures.android.ts | 16 +++++++++++++-- nativescript-core/ui/gestures/gestures.ios.ts | 20 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/nativescript-core/ui/gestures/gestures.android.ts b/nativescript-core/ui/gestures/gestures.android.ts index 4c60aa53c6..3b3de4bc3c 100644 --- a/nativescript-core/ui/gestures/gestures.android.ts +++ b/nativescript-core/ui/gestures/gestures.android.ts @@ -1,5 +1,5 @@ // Definitions. -import { GestureEventData, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData } from "."; +import { GestureEventData, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, GestureEventDataWithState } from "."; import { View, EventData } from "../core/view"; // Types. @@ -62,7 +62,7 @@ function initializeTapAndDoubleTapGestureListener() { public onLongPress(motionEvent: android.view.MotionEvent): void { if (this._type & GestureTypes.longPress) { - const args = _getArgs(GestureTypes.longPress, this._target, motionEvent); + const args = _getLongPressArgs(GestureTypes.longPress, this._target, GestureStateTypes.began, motionEvent); _executeCallback(this._observer, args); } } @@ -383,6 +383,18 @@ function _getArgs(type: GestureTypes, view: View, e: android.view.MotionEvent): }; } +function _getLongPressArgs(type: GestureTypes, view: View, state: GestureStateTypes, e: android.view.MotionEvent): GestureEventDataWithState { + return { + type: type, + view: view, + android: e, + ios: undefined, + object: view, + eventName: toString(type), + state: state + }; +} + function _getSwipeArgs(direction: SwipeDirection, view: View, initialEvent: android.view.MotionEvent, currentEvent: android.view.MotionEvent): SwipeGestureEventData { return { diff --git a/nativescript-core/ui/gestures/gestures.ios.ts b/nativescript-core/ui/gestures/gestures.ios.ts index 7ecb87657f..d687b90926 100644 --- a/nativescript-core/ui/gestures/gestures.ios.ts +++ b/nativescript-core/ui/gestures/gestures.ios.ts @@ -1,5 +1,5 @@ // Definitions. -import { GestureEventData, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, PinchGestureEventData } from "."; +import { GestureEventData, GestureEventDataWithState, SwipeGestureEventData, PanGestureEventData, RotationGestureEventData, PinchGestureEventData } from "."; import { View, EventData } from "../core/view"; // Types. @@ -167,7 +167,9 @@ export class GesturesObserver extends GesturesObserverBase { } if (type & GestureTypes.longPress) { - nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.longPress)); + nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.longPress, args => { + this._executeCallback(_getLongPressData(args)); + })); } if (type & GestureTypes.touch) { @@ -359,6 +361,20 @@ function _getRotationData(args: GestureEventData): RotationGestureEventData { }; } +function _getLongPressData(args: GestureEventData): GestureEventDataWithState { + const recognizer = args.ios; + + return { + type: args.type, + view: args.view, + ios: args.ios, + android: undefined, + object: args.view, + eventName: toString(args.type), + state: getState(recognizer) + }; +} + class TouchGestureRecognizer extends UIGestureRecognizer { public observer: GesturesObserver; private _eventData: TouchGestureEventData; From 3c340b42dc44a4ef86f1cf5b87649ed42361e614 Mon Sep 17 00:00:00 2001 From: Darin Dimitrov Date: Wed, 8 Jan 2020 15:04:29 +0200 Subject: [PATCH 04/23] test: make image-cache tests async (#8232) --- tests/app/ui/image-cache/image-cache-tests.ts | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/tests/app/ui/image-cache/image-cache-tests.ts b/tests/app/ui/image-cache/image-cache-tests.ts index 16888aec5b..0b437913af 100644 --- a/tests/app/ui/image-cache/image-cache-tests.ts +++ b/tests/app/ui/image-cache/image-cache-tests.ts @@ -1,14 +1,11 @@ import * as imageCacheModule from "@nativescript/core/ui/image-cache"; import { ImageSource } from "@nativescript/core/image-source"; -import * as types from "@nativescript/core/utils/types"; import { isAndroid, device } from "@nativescript/core/platform"; import lazy from "@nativescript/core/utils/lazy"; -import * as TKUnit from "../../tk-unit"; - const sdkVersion = lazy(() => parseInt(device.sdkVersion)); -export const test_ImageCache_ValidUrl = function () { +export const test_ImageCache_ValidUrl = function (done: (err: Error, res?: string) => void) { // see https://github.com/NativeScript/NativeScript/issues/6643 if (isAndroid && sdkVersion() < 20) { return; @@ -17,8 +14,6 @@ export const test_ImageCache_ValidUrl = function () { const cache = new imageCacheModule.Cache(); cache.maxRequests = 5; - let validKey: string; - let imgSource: ImageSource; const url = "https://github.com/NativeScript.png"; // Try to read the image from the cache @@ -26,6 +21,7 @@ export const test_ImageCache_ValidUrl = function () { if (image) { // If present -- use it. imgSource = new ImageSource(image); + done(new Error("The image was found in the cache")); } else { // If not present -- request its download. @@ -35,24 +31,18 @@ export const test_ImageCache_ValidUrl = function () { completed: (image: any, key: string) => { if (url === key) { imgSource = new ImageSource(image); - validKey = key; console.log("Valid url: ", key); + done(null); } } }); } - - TKUnit.waitUntilReady(() => types.isDefined(validKey), 8); - TKUnit.assertEqual(validKey, url, "Key should equal the provided url"); }; -export const test_ImageCache_NothingAtProvidedUrl = function () { +export const test_ImageCache_NothingAtProvidedUrl = function (done: (err: Error, res?: string) => void) { const cache = new imageCacheModule.Cache(); cache.maxRequests = 5; - let errorCaught = false; - let errorMessage: string; - let imgSource: ImageSource; const url = "https://github.com/NativeScript-NoImage.png"; // Try to read the image from the cache @@ -60,6 +50,7 @@ export const test_ImageCache_NothingAtProvidedUrl = function () { if (image) { // If present -- use it. imgSource = new ImageSource(image); + done(new Error("The image was found in the cache")); } else { // If not present -- request its download. @@ -70,15 +61,12 @@ export const test_ImageCache_NothingAtProvidedUrl = function () { if (url === key) { imgSource = new ImageSource(image); } + done(new Error("The completed callback was not expected to be called")); }, error: (key: string) => { console.log("No image for key: ", key); - errorMessage = `No image for key: ${key}`; - errorCaught = true; + done(null); } }); } - - TKUnit.waitUntilReady(() => errorCaught); - TKUnit.assertEqual(`No image for key: ${url}`, errorMessage); }; From 9217094a8e1bb78882e62392d726e0e5d91abc39 Mon Sep 17 00:00:00 2001 From: Kirill Goncharov Date: Wed, 8 Jan 2020 19:48:29 +0300 Subject: [PATCH 05/23] feat(html-view): Additional properties for HtmlView component (#8207) * feat(html-view): Additional properties for HtmlView component - Allow to set text color with `color` CSS property. - Allow to set link color with `link-color` CSS property. - Allow to set font attributes with `font-family` and `font-size` CSS properties. - Make text selectable on Android by default (for consistency with IOS). - Remove extra padding on IOS. * refactor: Move uiColorToHex function to nativescript-core/color module * test: adding test for new HtmlView css properties Co-authored-by: Vasil Trifonov --- api-reports/NativeScript.api.md | 1 + .../app/css/all-non-uniform-border-page.css | 7 ++ .../app/css/all-non-uniform-border-page.xml | 2 +- nativescript-core/color/color-common.ts | 4 ++ nativescript-core/color/color.d.ts | 5 ++ nativescript-core/color/color.ios.ts | 11 +++ .../ui/html-view/html-view-common.ts | 12 +++- .../ui/html-view/html-view.android.ts | 57 ++++++++++++++- .../ui/html-view/html-view.ios.ts | 70 ++++++++++++++++++- 9 files changed, 162 insertions(+), 7 deletions(-) diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index d2d7d93604..3c1d17ad55 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -413,6 +413,7 @@ export class Color { public b: number; public equals(value: Color): boolean; public static equals(value1: Color, value2: Color): boolean; + public static fromIosColor(value: any /* UIColor */): Color; public g: number; public hex: string; ios: any /* UIColor */; diff --git a/e2e/ui-tests-app/app/css/all-non-uniform-border-page.css b/e2e/ui-tests-app/app/css/all-non-uniform-border-page.css index 3529e9d294..a0f54831f2 100644 --- a/e2e/ui-tests-app/app/css/all-non-uniform-border-page.css +++ b/e2e/ui-tests-app/app/css/all-non-uniform-border-page.css @@ -5,3 +5,10 @@ height: 80; font-size: 6; } + +.html-view { + font-size: 10; + link-color: red; + color: green; + font-family: 'Courier New', Courier, monospace; +} diff --git a/e2e/ui-tests-app/app/css/all-non-uniform-border-page.xml b/e2e/ui-tests-app/app/css/all-non-uniform-border-page.xml index 64efc27d91..ed2837afdf 100644 --- a/e2e/ui-tests-app/app/css/all-non-uniform-border-page.xml +++ b/e2e/ui-tests-app/app/css/all-non-uniform-border-page.xml @@ -11,7 +11,7 @@ - + diff --git a/nativescript-core/color/color-common.ts b/nativescript-core/color/color-common.ts index 0a8e12fb42..d929e50558 100644 --- a/nativescript-core/color/color-common.ts +++ b/nativescript-core/color/color-common.ts @@ -157,6 +157,10 @@ export class Color implements definition.Color { public toString(): string { return this.hex; } + + public static fromIosColor(value: UIColor): Color { + return undefined; + } } function isRgbOrRgba(value: string): boolean { diff --git a/nativescript-core/color/color.d.ts b/nativescript-core/color/color.d.ts index ee75d7941a..cb6712cf38 100644 --- a/nativescript-core/color/color.d.ts +++ b/nativescript-core/color/color.d.ts @@ -75,4 +75,9 @@ export class Color { * @param value Input string. */ public static isValid(value: any): boolean; + + /** + * Creates color from iOS-specific UIColor value representation. + */ + public static fromIosColor(value: any /* UIColor */): Color; } diff --git a/nativescript-core/color/color.ios.ts b/nativescript-core/color/color.ios.ts index a0aad04377..648a3759f9 100644 --- a/nativescript-core/color/color.ios.ts +++ b/nativescript-core/color/color.ios.ts @@ -11,4 +11,15 @@ export class Color extends common.Color { return this._ios; } + + public static fromIosColor(value: UIColor): Color { + const rgba = CGColorGetComponents(value.CGColor); + + return new Color( + Math.round(rgba[3] * 255), + Math.round(rgba[0] * 255), + Math.round(rgba[1] * 255), + Math.round(rgba[2] * 255), + ); + } } diff --git a/nativescript-core/ui/html-view/html-view-common.ts b/nativescript-core/ui/html-view/html-view-common.ts index e6c564e865..9c7df2d504 100644 --- a/nativescript-core/ui/html-view/html-view-common.ts +++ b/nativescript-core/ui/html-view/html-view-common.ts @@ -1,5 +1,7 @@ -import { HtmlView as HtmlViewDefinition } from "."; +import { Color } from "../../color"; +import { Style, CssProperty } from "../core/properties"; import { View, Property, CSSType } from "../core/view"; +import { HtmlView as HtmlViewDefinition } from "."; export * from "../core/view"; @@ -13,3 +15,11 @@ HtmlViewBase.prototype.recycleNativeView = "auto"; // TODO: Can we use Label.ios optimization for affectsLayout??? export const htmlProperty = new Property({ name: "html", defaultValue: "", affectsLayout: true }); htmlProperty.register(HtmlViewBase); + +export const linkColorProperty = new CssProperty({ + name: "linkColor", + cssName: "link-color", + equalityComparer: Color.equals, + valueConverter: (value) => new Color(value), +}); +linkColorProperty.register(Style); diff --git a/nativescript-core/ui/html-view/html-view.android.ts b/nativescript-core/ui/html-view/html-view.android.ts index 139735bb66..67379dc807 100644 --- a/nativescript-core/ui/html-view/html-view.android.ts +++ b/nativescript-core/ui/html-view/html-view.android.ts @@ -1,5 +1,13 @@ -import { - HtmlViewBase, htmlProperty +import { Color } from "../../color"; +import { Font } from "../styling/font"; +import { + colorProperty, + fontSizeProperty, + fontInternalProperty, +} from "../styling/style-properties"; +import { + HtmlViewBase, htmlProperty, + linkColorProperty, } from "./html-view-common"; export * from "./html-view-common"; @@ -15,6 +23,9 @@ export class HtmlView extends HtmlViewBase { super.initNativeView(); const nativeView = this.nativeViewProtected; + // Allow text selection + nativeView.setTextIsSelectable(true); + // This makes the html work nativeView.setLinksClickable(true); nativeView.setMovementMethod(android.text.method.LinkMovementMethod.getInstance()); @@ -39,4 +50,46 @@ export class HtmlView extends HtmlViewBase { this.nativeViewProtected.setAutoLinkMask(mask); this.nativeViewProtected.setText(android.text.Html.fromHtml(value)); } + + [colorProperty.getDefault](): android.content.res.ColorStateList { + return this.nativeViewProtected.getTextColors(); + } + [colorProperty.setNative](value: Color | android.content.res.ColorStateList) { + if (value instanceof Color) { + this.nativeViewProtected.setTextColor(value.android); + } else { + this.nativeViewProtected.setTextColor(value); + } + } + + [linkColorProperty.getDefault](): android.content.res.ColorStateList { + return this.nativeViewProtected.getLinkTextColors(); + } + [linkColorProperty.setNative](value: Color | android.content.res.ColorStateList) { + const color = value instanceof Color ? value.android : value; + if (value instanceof Color) { + this.nativeViewProtected.setLinkTextColor(value.android); + } else { + this.nativeViewProtected.setLinkTextColor(value); + } + } + + [fontInternalProperty.getDefault](): android.graphics.Typeface { + return this.nativeViewProtected.getTypeface(); + } + [fontInternalProperty.setNative](value: Font | android.graphics.Typeface) { + const font = value instanceof Font ? value.getAndroidTypeface() : value; + this.nativeViewProtected.setTypeface(font); + } + + [fontSizeProperty.getDefault](): {nativeSize: number} { + return {nativeSize: this.nativeViewProtected.getTextSize()}; + } + [fontSizeProperty.setNative](value: number | {nativeSize: number}) { + if (typeof value === "number") { + this.nativeViewProtected.setTextSize(value); + } else { + this.nativeViewProtected.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.nativeSize); + } + } } diff --git a/nativescript-core/ui/html-view/html-view.ios.ts b/nativescript-core/ui/html-view/html-view.ios.ts index d5c1d5b6e7..89156326fd 100644 --- a/nativescript-core/ui/html-view/html-view.ios.ts +++ b/nativescript-core/ui/html-view/html-view.ios.ts @@ -1,5 +1,12 @@ +import { Color } from "../../color"; +import { Font } from "../styling/font"; import { - HtmlViewBase, View, layout, htmlProperty + colorProperty, + fontInternalProperty, +} from "../styling/style-properties"; +import { + HtmlViewBase, View, layout, htmlProperty, fontSizeProperty, + linkColorProperty, } from "./html-view-common"; import { ios } from "../../utils/utils"; @@ -9,6 +16,7 @@ const majorVersion = ios.MajorVersion; export class HtmlView extends HtmlViewBase { nativeViewProtected: UITextView; + private currentHtml: string; public createNativeView() { const view = UITextView.new(); @@ -21,6 +29,14 @@ export class HtmlView extends HtmlViewBase { return view; } + public initNativeView(): void { + super.initNativeView(); + + // Remove extra padding + this.nativeViewProtected.textContainer.lineFragmentPadding = 0; + this.nativeViewProtected.textContainerInset = (UIEdgeInsets as any).zero; + } + get ios(): UITextView { return this.nativeViewProtected; } @@ -50,8 +66,22 @@ export class HtmlView extends HtmlViewBase { [htmlProperty.getDefault](): string { return ""; } - [htmlProperty.setNative](value: string) { - const htmlString = NSString.stringWithString(value + ""); + + private renderWithStyles() { + let html = this.currentHtml; + const styles = []; + if (this.nativeViewProtected.font) { + styles.push(`font-family: '${this.nativeViewProtected.font.fontName}';`); + styles.push(`font-size: ${this.nativeViewProtected.font.pointSize}px;`); + } + if (this.nativeViewProtected.textColor) { + const textColor = Color.fromIosColor(this.nativeViewProtected.textColor); + styles.push(`color: ${textColor.hex};`); + } + if (styles.length > 0) { + html += ``; + } + const htmlString = NSString.stringWithString(html + ""); const nsData = htmlString.dataUsingEncoding(NSUnicodeStringEncoding); this.nativeViewProtected.attributedText = NSAttributedString.alloc().initWithDataOptionsDocumentAttributesError( nsData, @@ -63,4 +93,38 @@ export class HtmlView extends HtmlViewBase { this.nativeViewProtected.textColor = UIColor.labelColor; } } + + [htmlProperty.setNative](value: string) { + this.currentHtml = value; + this.renderWithStyles(); + } + + [colorProperty.getDefault](): UIColor { + return this.nativeViewProtected.textColor; + } + [colorProperty.setNative](value: Color | UIColor) { + const color = value instanceof Color ? value.ios : value; + this.nativeViewProtected.textColor = color; + this.renderWithStyles(); + } + + [linkColorProperty.getDefault](): UIColor { + return this.nativeViewProtected.linkTextAttributes[NSForegroundColorAttributeName]; + } + [linkColorProperty.setNative](value: Color | UIColor) { + const color = value instanceof Color ? value.ios : value; + const linkTextAttributes = NSDictionary.dictionaryWithObjectForKey( + color, + NSForegroundColorAttributeName, + ); + this.nativeViewProtected.linkTextAttributes = linkTextAttributes; + } + [fontInternalProperty.getDefault](): UIFont { + return this.nativeViewProtected.font; + } + [fontInternalProperty.setNative](value: Font | UIFont) { + const font = value instanceof Font ? value.getUIFont(this.nativeViewProtected.font) : value; + this.nativeViewProtected.font = font; + this.renderWithStyles(); + } } From d65a2db83a32b7371bff0c125ad2d2836012df00 Mon Sep 17 00:00:00 2001 From: SpurguX Date: Thu, 9 Jan 2020 10:54:35 +0200 Subject: [PATCH 06/23] fix(android): Request Timeout #6523 (#8194) Fix http requests to use read timeout in addition to connect timeout. Co-authored-by: Dimitar Topuzov --- .../widgets/src/main/java/org/nativescript/widgets/Async.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/Async.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/Async.java index 9aa553f006..0da7062a9a 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/Async.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/Async.java @@ -525,6 +525,7 @@ protected RequestResult doInBackground(RequestOptions... params) { // apply timeout if (options.timeout > 0) { connection.setConnectTimeout(options.timeout); + connection.setReadTimeout(options.timeout); } // don't follow redirect (30x) responses; by default, HttpURLConnection follows them. From 8550c3293dd4d8c37c83ce9c3a48fb23d91657d8 Mon Sep 17 00:00:00 2001 From: Yurii Cherniavskyi Date: Fri, 10 Jan 2020 10:49:32 +0200 Subject: [PATCH 07/23] fix(ios/bottom-navigation): move TabStrip items event emitting to selectedIndex changed handler (#8160) --- .../bottom-navigation.ios.ts | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts b/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts index f94154dfc2..a5126185e9 100644 --- a/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts +++ b/nativescript-core/ui/bottom-navigation/bottom-navigation.ios.ts @@ -157,23 +157,6 @@ class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControl const owner = this._owner.get(); if (owner) { - if (tabBarController.viewControllers) { - const position = tabBarController.viewControllers.indexOfObject(viewController); - if (position !== NSNotFound) { - const prevPosition = owner.selectedIndex; - const tabStripItems = owner.tabStrip && owner.tabStrip.items; - if (tabStripItems) { - if (tabStripItems[position]) { - tabStripItems[position]._emit(TabStripItem.selectEvent); - } - - if (tabStripItems[prevPosition]) { - tabStripItems[prevPosition]._emit(TabStripItem.unselectEvent); - } - } - } - } - owner._onViewControllerShown(viewController); } @@ -358,6 +341,17 @@ export class BottomNavigation extends TabNavigationBase { newItem.loadView(newItem.content); } + const tabStripItems = this.tabStrip && this.tabStrip.items; + if (tabStripItems) { + if (tabStripItems[newIndex]) { + tabStripItems[newIndex]._emit(TabStripItem.selectEvent); + } + + if (tabStripItems[oldIndex]) { + tabStripItems[oldIndex]._emit(TabStripItem.unselectEvent); + } + } + super.onSelectedIndexChanged(oldIndex, newIndex); } From e8f5ac8522954b4433fa8932032d9fc03d33819e Mon Sep 17 00:00:00 2001 From: Ryan Pendergast Date: Fri, 10 Jan 2020 04:59:46 -0600 Subject: [PATCH 08/23] feat: Add 3D rotation to view - takeover of PR# 5950 (#8136) * feat: add 3d rotation * chore: fix build errors * chore: fix tslint errors * chore: add @types/chai dev dep * chore: unused import cleanup * chore: update tests for x,y rotation * chore: rebase upstream/master * fix: iOS Affine Transform test verification * feat(css): Added optional css-tree parser (#8076) * feat(css): Added optional css-tree parser * test: css-tree parser compat tests * test: more css-tree compat tests * feat(dialogs): Setting the size of popup dialog thru dialog options (#8041) * Added iOS specific height and width attributes to ShowModalOptions * Set the height and width of the popup dialog to the presenting controller * dialog options ios attributes presentationStyle, height & width are made optional * Updated NativeScript.api.md for public API changes * Update with git properties * Public API * CLA update * fix: use iOS native-helper for 3d-rotate * test: Fix tests using _getTransformMismatchError * fix: view.__hasTransfrom not set updating properly * test: fix css-animations test page Co-authored-by: Alexander Vakrilov Co-authored-by: Darin Dimitrov Co-authored-by: Shailesh Lolam Co-authored-by: Dimitar Topuzov --- api-reports/NativeScript.api.md | 12 +- e2e/animation/app/3d-rotate/page.ts | 40 ++++ e2e/animation/app/3d-rotate/page.xml | 24 ++ .../app/css-animations/3d-rotate/page.css | 75 ++++++ .../app/css-animations/3d-rotate/page.ts | 35 +++ .../app/css-animations/3d-rotate/page.xml | 22 ++ e2e/animation/app/css-animations/page.ts | 2 +- e2e/animation/app/css-animations/page.xml | 1 + e2e/animation/app/home/home-page.xml | 3 +- nativescript-core/matrix/matrix.ts | 16 +- nativescript-core/package.json | 3 +- .../ui/animation/animation-common.ts | 47 ++-- .../ui/animation/animation.android.ts | 126 +++++----- nativescript-core/ui/animation/animation.d.ts | 18 +- .../ui/animation/animation.ios.ts | 221 ++++++++++++------ .../ui/animation/keyframe-animation.ts | 8 +- nativescript-core/ui/core/view/view-common.ts | 21 ++ .../ui/core/view/view.android.ts | 19 +- nativescript-core/ui/core/view/view.d.ts | 23 +- nativescript-core/ui/core/view/view.ios.ts | 59 +++-- .../ui/styling/style-properties.ts | 52 ++++- nativescript-core/ui/styling/style-scope.ts | 2 + nativescript-core/ui/styling/style/style.d.ts | 3 + nativescript-core/ui/styling/style/style.ts | 4 + nativescript-core/utils/native-helper.d.ts | 11 +- nativescript-core/utils/native-helper.ios.ts | 28 ++- nativescript-core/utils/utils.d.ts | 1 + nativescript-core/utils/utils.ios.ts | 4 +- tests/app/ui/animation/animation-tests.ts | 1 + tests/app/ui/animation/css-animation-tests.ts | 10 +- .../android/org.nativescript.widgets.d.ts | 8 + 31 files changed, 708 insertions(+), 191 deletions(-) create mode 100644 e2e/animation/app/3d-rotate/page.ts create mode 100644 e2e/animation/app/3d-rotate/page.xml create mode 100644 e2e/animation/app/css-animations/3d-rotate/page.css create mode 100644 e2e/animation/app/css-animations/3d-rotate/page.ts create mode 100644 e2e/animation/app/css-animations/3d-rotate/page.xml diff --git a/api-reports/NativeScript.api.md b/api-reports/NativeScript.api.md index 3c1d17ad55..f6f2c8e68e 100644 --- a/api-reports/NativeScript.api.md +++ b/api-reports/NativeScript.api.md @@ -243,7 +243,8 @@ export interface AnimationDefinition { opacity?: number; - rotate?: number; + // Warning: (ae-forgotten-export) The symbol "Point3D" needs to be exported by the entry point index.d.ts + rotate?: number | Point3D; scale?: Pair; @@ -2086,6 +2087,8 @@ export class Style extends Observable { // (undocumented) public paddingTop: Length; // (undocumented) + public perspective: number; + // (undocumented) public placeholderColor: Color; // Warning: (ae-forgotten-export) The symbol "PropertyBagClass" needs to be exported by the entry point index.d.ts public readonly PropertyBag: PropertyBagClass; @@ -2094,6 +2097,10 @@ export class Style extends Observable { // (undocumented) public rotate: number; // (undocumented) + public rotateX: number; + // (undocumented) + public rotateY: number; + // (undocumented) public scaleX: number; // (undocumented) public scaleY: number; @@ -2702,12 +2709,15 @@ export abstract class View extends ViewBase { opacity: number; originX: number; originY: number; + perspective: number; // (undocumented) _redrawNativeBackground(value: any): void; // (undocumented) _removeAnimation(animation: Animation): boolean; public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number; rotate: number; + rotateX: number; + rotateY: number; scaleX: number; scaleY: number; _setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): { boundsChanged: boolean, sizeChanged: boolean }; diff --git a/e2e/animation/app/3d-rotate/page.ts b/e2e/animation/app/3d-rotate/page.ts new file mode 100644 index 0000000000..e9f473cd88 --- /dev/null +++ b/e2e/animation/app/3d-rotate/page.ts @@ -0,0 +1,40 @@ +import { EventData, Page } from "tns-core-modules/ui/page"; +import { View } from "tns-core-modules/ui/core/view"; +import { Point3D } from "tns-core-modules/ui/animation/animation"; + +let view: View; + +export function pageLoaded(args: EventData) { + const page = args.object; + view = page.getViewById("view"); +} + +export function onAnimateX(args: EventData) { + rotate({ x: 360, y: 0, z: 0 }); +} + +export function onAnimateY(args: EventData) { + rotate({ x: 0, y: 360, z: 0 }); +} + +export function onAnimateZ(args: EventData) { + rotate({ x: 0, y: 0, z: 360 }); +} + +export function onAnimateXYZ(args: EventData) { + rotate({ x: 360, y: 360, z: 360 }); +} + +async function rotate(rotate: Point3D) { + await view.animate({ + rotate, + duration: 1000 + }); + reset(); +} + +function reset() { + view.rotate = 0; + view.rotateX = 0; + view.rotateY = 0; +} diff --git a/e2e/animation/app/3d-rotate/page.xml b/e2e/animation/app/3d-rotate/page.xml new file mode 100644 index 0000000000..c56b6a9faa --- /dev/null +++ b/e2e/animation/app/3d-rotate/page.xml @@ -0,0 +1,24 @@ + + + + + + + + +