From ee76186bf43da2fe8709856d6ae95a61f058ebe5 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Wed, 14 Apr 2021 14:37:49 +0200 Subject: [PATCH] fix: NS8 and use createFormattedTextNative --- package.json | 50 +++++++++-------- plugin/package.json | 2 +- src/label-common.ts | 4 +- src/label.android.ts | 124 ++++++++++++++++++++++--------------------- src/label.d.ts | 10 ++-- src/label.ios.ts | 81 +++++++++++++--------------- 6 files changed, 132 insertions(+), 139 deletions(-) diff --git a/package.json b/package.json index 251f4aa..5fb2030 100644 --- a/package.json +++ b/package.json @@ -47,36 +47,34 @@ "homepage": "https://github.com/nativescript-community/ui-label", "readmeFilename": "README.md", "devDependencies": { - "@angular/common": "^10.1.0", - "@angular/compiler": "~10.1.0", - "@angular/compiler-cli": "~10.1.0", - "@angular/core": "~10.1.0", - "@angular/forms": "~10.1.0", - "@angular/platform-browser": "~10.1.0", - "@angular/platform-browser-dynamic": "~10.1.0", - "@angular/router": "~10.1.0", - "@commitlint/cli": "^11.0.0", - "@commitlint/config-conventional": "^11.0.0", - "@nativescript-community/text": "^1.4.9", - "@nativescript/angular": "10.1.0", - "@nativescript/core": "7.2.1", - "@nativescript/types-android": "7.2.0", - "@nativescript/types-ios": "7.2.0", - "@nativescript/webpack": "4.1.0", - "@types/node": "^14.14.27", - "@typescript-eslint/eslint-plugin": "4.15.0", - "@typescript-eslint/parser": "4.15.0", - "eslint": "7.19.0", - "eslint-config-prettier": "^8.1.0", + "@angular/common": "^11.2.9", + "@angular/compiler": "~11.2.9", + "@angular/compiler-cli": "~11.2.9", + "@angular/core": "~11.2.9", + "@angular/forms": "~11.2.9", + "@angular/platform-browser": "~11.2.9", + "@angular/platform-browser-dynamic": "~11.2.9", + "@angular/router": "~11.2.9", + "@commitlint/cli": "^12.1.1", + "@commitlint/config-conventional": "^12.1.1", + "@nativescript-community/text": "^1.4.11", + "@nativescript/angular": "11.2.0", + "@nativescript/core": "8.0.1", + "@nativescript/types-android": "8.0.0", + "@nativescript/types-ios": "8.0.0", + "@nativescript/webpack": "5.0.0-beta.6", + "@types/node": "^14.14.37", + "@typescript-eslint/eslint-plugin": "4.22.0", + "@typescript-eslint/parser": "4.22.0", + "eslint": "7.24.0", + "eslint-config-prettier": "^8.2.0", "eslint-plugin-prettier": "^3.3.1", - "husky": "^4.3.8", - "lerna": "^3.22.1", - "npm-watch": "^0.7.0", + "husky": "^6.0.0", + "lerna": "^4.0.0", "prettier": "^2.2.1", - "prompt": "^1.1.0", "rimraf": "^3.0.2", "ts-patch": "^1.3.2", - "typescript": "~4.1.5" + "typescript": "~4.2.4" }, "dependencies": { "ts-node": "^9.1.1" diff --git a/plugin/package.json b/plugin/package.json index fd2b244..5e6374e 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -32,6 +32,6 @@ "license": "Apache-2.0", "readmeFilename": "README.md", "dependencies": { - "@nativescript-community/text": "^1.4.9" + "@nativescript-community/text": "^1.4.10" } } diff --git a/src/label-common.ts b/src/label-common.ts index d2c0351..e3a20cc 100644 --- a/src/label-common.ts +++ b/src/label-common.ts @@ -2,6 +2,7 @@ import { VerticalTextAlignment, cssProperty, init } from '@nativescript-communit import { CSSType, Color, + CoreTypes, CssProperty, FormattedString, Property, @@ -10,7 +11,6 @@ import { Label as TNLabel, booleanConverter } from '@nativescript/core'; -import { dip } from '@nativescript/core/ui/core/view'; import { layout } from '@nativescript/core/utils/utils'; import { Label as LabelViewDefinition, LineBreak, TextShadow } from './label'; @@ -137,7 +137,7 @@ export const maxFontSizeProperty = new CssProperty({ }); maxFontSizeProperty.register(Style); -function parseDIPs(value: string): dip { +function parseDIPs(value: string): CoreTypes.dip { if (value.indexOf('px') !== -1) { return layout.toDeviceIndependentPixels(parseFloat(value.replace('px', '').trim())); } else { diff --git a/src/label.android.ts b/src/label.android.ts index 4388bc9..20a078e 100644 --- a/src/label.android.ts +++ b/src/label.android.ts @@ -8,17 +8,18 @@ } from '@nativescript-community/text'; import { CSSType, + CoreTypes, FormattedString, Observable, Property, PropertyChangeData, Span, View, - ViewBase, booleanConverter, profile } from '@nativescript/core'; import { Color } from '@nativescript/core/color'; +import { CSSShadow } from '@nativescript/core/ui/styling/css-shadow'; import { Font, FontStyle, FontWeight } from '@nativescript/core/ui/styling/font'; import { Length, @@ -31,10 +32,6 @@ import { paddingTopProperty } from '@nativescript/core/ui/styling/style-properties'; import { - TextAlignment, - TextDecoration, - TextTransform, - WhiteSpace, letterSpacingProperty, textAlignmentProperty, textDecorationProperty, @@ -48,13 +45,11 @@ import { autoFontSizeProperty, lineBreakProperty, maxLinesProperty, - needFormattedStringComputation, selectableProperty, textShadowProperty } from './label-common'; -export { enableIOSDTCoreText, createNativeAttributedString } from '@nativescript-community/text'; - +export { createNativeAttributedString, enableIOSDTCoreText } from '@nativescript-community/text'; export * from './label-common'; let TextView: typeof com.nativescript.label.EllipsizingTextView; @@ -98,7 +93,7 @@ export const htmlProperty = new Property({ name: 'html', defaultV type ClickableSpan = new (owner: Span) => android.text.style.ClickableSpan; -function getHorizontalGravity(textAlignment: TextAlignment) { +function getHorizontalGravity(textAlignment: CoreTypes.TextAlignmentType) { switch (textAlignment) { case 'initial': case 'left': @@ -207,6 +202,7 @@ abstract class LabelBase extends View implements LabelViewDefinition { @cssProperty maxFontSize: number; @cssProperty verticalTextAlignment: VerticalTextAlignment; @cssProperty linkColor: Color; + @cssProperty textShadow: CSSShadow; @cssProperty linkUnderline: boolean; public html: string; @cssProperty selectable: boolean; @@ -230,16 +226,16 @@ abstract class LabelBase extends View implements LabelViewDefinition { @cssProperty letterSpacing: number; @cssProperty lineHeight: number; @cssProperty lineBreak: LineBreak; - @cssProperty textAlignment: TextAlignment; - @cssProperty textDecoration: TextDecoration; - @cssProperty textTransform: TextTransform; - @cssProperty whiteSpace: WhiteSpace; + @cssProperty textAlignment: CoreTypes.TextAlignmentType; + @cssProperty textDecoration: CoreTypes.TextDecorationType; + @cssProperty textTransform: CoreTypes.TextTransformType; + @cssProperty whiteSpace: CoreTypes.WhiteSpaceType; - @cssProperty padding: string | Length; - @cssProperty paddingTop: Length; - @cssProperty paddingRight: Length; - @cssProperty paddingBottom: Length; - @cssProperty paddingLeft: Length; + @cssProperty padding: string | CoreTypes.LengthType; + @cssProperty paddingTop: CoreTypes.LengthType; + @cssProperty paddingRight: CoreTypes.LengthType; + @cssProperty paddingBottom: CoreTypes.LengthType; + @cssProperty paddingLeft: CoreTypes.LengthType; // for now code is duplicated as Android version is a full rewrite _canChangeText = true; @@ -358,7 +354,7 @@ export class Label extends LabelBase { } } - [whiteSpaceProperty.setNative](value: WhiteSpace) { + [whiteSpaceProperty.setNative](value: CoreTypes.WhiteSpaceType) { if (!this.lineBreak) { const nativeView = this.nativeTextViewProtected; switch (value) { @@ -374,11 +370,21 @@ export class Label extends LabelBase { } } } - [textShadowProperty.setNative](value: TextShadow) { + [textShadowProperty.getDefault](value: number) { + return { + radius: this.nativeTextViewProtected.getShadowRadius(), + offsetX: this.nativeTextViewProtected.getShadowDx(), + offsetY: this.nativeTextViewProtected.getShadowDy(), + color: this.nativeTextViewProtected.getShadowColor() + }; + } + + [textShadowProperty.setNative](value: CSSShadow) { + // prettier-ignore this.nativeViewProtected.setShadowLayer( - layout.toDevicePixels(value.blurRadius), - layout.toDevicePixels(value.offsetX), - layout.toDevicePixels(value.offsetY), + Length.toDevicePixels(value.blurRadius, java.lang.Float.MIN_VALUE), + Length.toDevicePixels(value.offsetX, 0), + Length.toDevicePixels(value.offsetY, 0), value.color.android ); } @@ -404,11 +410,11 @@ export class Label extends LabelBase { this._setNativeText(); } - [textTransformProperty.setNative](value: TextTransform) { + [textTransformProperty.setNative](value: CoreTypes.TextTransformType) { this._setNativeText(); } - [textAlignmentProperty.setNative](value: TextAlignment) { + [textAlignmentProperty.setNative](value: CoreTypes.TextAlignmentType) { const view = this.nativeTextViewProtected; view.setGravity(getHorizontalGravity(value) | getVerticalGravity(this.verticalTextAlignment)); } @@ -436,7 +442,7 @@ export class Label extends LabelBase { this.nativeTextViewProtected.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value); } - [textDecorationProperty.setNative](value: number | TextDecoration) { + [textDecorationProperty.setNative](value: number | CoreTypes.TextDecorationType) { switch (value) { case 'none': this.nativeTextViewProtected.setPaintFlags(0); @@ -462,40 +468,40 @@ export class Label extends LabelBase { org.nativescript.widgets.ViewHelper.setLetterspacing(this.nativeTextViewProtected, value); } - [paddingTopProperty.getDefault](): Length { + [paddingTopProperty.getDefault](): CoreTypes.LengthType { return { value: this._defaultPaddingTop, unit: 'px' }; } - [paddingTopProperty.setNative](value: Length) { + [paddingTopProperty.setNative](value: CoreTypes.LengthType) { org.nativescript.widgets.ViewHelper.setPaddingTop( this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderTopWidth, 0) ); } - [paddingRightProperty.getDefault](): Length { + [paddingRightProperty.getDefault](): CoreTypes.LengthType { return { value: this._defaultPaddingRight, unit: 'px' }; } - [paddingRightProperty.setNative](value: Length) { + [paddingRightProperty.setNative](value: CoreTypes.LengthType) { org.nativescript.widgets.ViewHelper.setPaddingRight( this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderRightWidth, 0) ); } - [paddingBottomProperty.getDefault](): Length { + [paddingBottomProperty.getDefault](): CoreTypes.LengthType { return { value: this._defaultPaddingBottom, unit: 'px' }; } - [paddingBottomProperty.setNative](value: Length) { + [paddingBottomProperty.setNative](value: CoreTypes.LengthType) { org.nativescript.widgets.ViewHelper.setPaddingBottom( this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderBottomWidth, 0) ); } - [paddingLeftProperty.getDefault](): Length { + [paddingLeftProperty.getDefault](): CoreTypes.LengthType { return { value: this._defaultPaddingLeft, unit: 'px' }; } - [paddingLeftProperty.setNative](value: Length) { + [paddingLeftProperty.setNative](value: CoreTypes.LengthType) { org.nativescript.widgets.ViewHelper.setPaddingLeft( this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0) @@ -522,35 +528,15 @@ export class Label extends LabelBase { [selectableProperty.setNative](value: boolean) { this.nativeTextViewProtected.setTextIsSelectable(value); } + createFormattedTextNative(value: any) { + const result = createNativeAttributedString(value, this); - @profile - createHTMLString() { - const result = createNativeAttributedString({ text: this.html }) as android.text.SpannableStringBuilder; - const urlSpan = result.getSpans(0, result.length(), android.text.style.URLSpan.class); - if (urlSpan.length > 0) { - this._setTappableState(true); - initializeURLClickableSpan(); - for (let index = 0; index < urlSpan.length; index++) { - const span = urlSpan[index]; - const text = span.getURL(); - const start = result.getSpanStart(span); - const end = result.getSpanEnd(span); - result.removeSpan(span); - result.setSpan(new URLClickableSpan(text, this), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - return result; - } - @profile - createSpannableStringBuilder() { - const formattedText = this.formattedText; - const result = createNativeAttributedString(formattedText as any); let indexSearch = 0; let str: string; - formattedText.spans.forEach((s) => { + value.spans.forEach((s) => { if (s.tappable) { if (!str) { - str = formattedText.toString(); + str = value.toString(); this._setTappableState(true); } initializeClickableSpan(); @@ -564,6 +550,24 @@ export class Label extends LabelBase { }); return result; } + @profile + createHTMLString() { + const result = createNativeAttributedString({ text: this.html }, this) as android.text.SpannableStringBuilder; + const urlSpan = result.getSpans(0, result.length(), android.text.style.URLSpan.class); + if (urlSpan.length > 0) { + this._setTappableState(true); + initializeURLClickableSpan(); + for (let index = 0; index < urlSpan.length; index++) { + const span = urlSpan[index]; + const text = span.getURL(); + const start = result.getSpanStart(span); + const end = result.getSpanEnd(span); + result.removeSpan(span); + result.setSpan(new URLClickableSpan(text, this), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + return result; + } _tappable = false; _setTappableState(tappable: boolean) { if (this._tappable !== tappable) { @@ -596,7 +600,7 @@ export class Label extends LabelBase { transformedText = this.createHTMLString(); textProperty.nativeValueChange(this, this.html === null || this.html === undefined ? '' : this.html); } else if (this.formattedText) { - transformedText = this.createSpannableStringBuilder(); + transformedText = this.createFormattedTextNative(this.formattedText); textProperty.nativeValueChange( this, this.formattedText === null || this.formattedText === undefined ? '' : this.formattedText.toString() @@ -659,7 +663,7 @@ function getCapitalizedString(str: string): string { return newWords.join(' '); } -export function getTransformedText(text: string, textTransform: TextTransform): string { +export function getTransformedText(text: string, textTransform: CoreTypes.TextTransformType): string { switch (textTransform) { case 'uppercase': return text.toUpperCase(); diff --git a/src/label.d.ts b/src/label.d.ts index 08eb6ac..5bd90ef 100644 --- a/src/label.d.ts +++ b/src/label.d.ts @@ -2,10 +2,8 @@ * Contains the Label class, which represents a custom label widget which correctly supports html. */ /** */ -import { Color, Label as TNLabel } from '@nativescript/core'; +import { Color, CoreTypes, Label as TNLabel } from '@nativescript/core'; import { Property } from '@nativescript/core/ui/core/properties'; -import { dip } from '@nativescript/core/ui/core/view'; -import { TextAlignment } from '@nativescript/core/ui/text-base'; /** * Represents a label with html content. Use this component instead WebView when you want to show just static HTML content. * [iOS support](https://developer.apple.com/library/ios/documentation/UIKit/Reference/NSAttributedString_UIKit_Additions/#//apple_ref/occ/instm/NSAttributedString/initWithData:options:documentAttributes:error:) @@ -37,9 +35,9 @@ export declare const htmlProperty: Property; export declare const verticalTextAlignmentProperty: Property; export interface TextShadow { - offsetX: dip; - offsetY: dip; - blurRadius: dip; + offsetX: CoreTypes.dip; + offsetY: CoreTypes.dip; + blurRadius: CoreTypes.dip; color: Color; } diff --git a/src/label.ios.ts b/src/label.ios.ts index ceaf2dd..6d8c992 100644 --- a/src/label.ios.ts +++ b/src/label.ios.ts @@ -4,9 +4,8 @@ import { createNativeAttributedString, verticalTextAlignmentProperty } from '@nativescript-community/text'; -import { Color, Font, FormattedString, Span, View } from '@nativescript/core'; +import { Color, CoreTypes, Font, FormattedString, Span, View } from '@nativescript/core'; import { - Length, borderBottomWidthProperty, borderLeftWidthProperty, borderRightWidthProperty, @@ -18,16 +17,7 @@ import { paddingRightProperty, paddingTopProperty } from '@nativescript/core/ui/styling/style-properties'; -import { - TextAlignment, - TextBase, - TextDecoration, - TextTransform, - WhiteSpace, - letterSpacingProperty, - textDecorationProperty, - whiteSpaceProperty -} from '@nativescript/core/ui/text-base'; +import { letterSpacingProperty, textDecorationProperty, whiteSpaceProperty } from '@nativescript/core/ui/text-base'; import { getClosestPropertyValue, lineHeightProperty } from '@nativescript/core/ui/text-base/text-base-common'; import { isNullOrUndefined, isString } from '@nativescript/core/utils/types'; import { iOSNativeHelper, layout } from '@nativescript/core/utils/utils'; @@ -45,9 +35,9 @@ import { textShadowProperty } from './label-common'; -export { enableIOSDTCoreText, createNativeAttributedString } from '@nativescript-community/text'; - +export { createNativeAttributedString, enableIOSDTCoreText } from '@nativescript-community/text'; export * from './label-common'; + const majorVersion = iOSNativeHelper.MajorVersion; enum FixedSize { @@ -70,7 +60,7 @@ declare module '@nativescript/core/ui/text-base' { function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString { return NSString.stringWithString((source instanceof NSAttributedString && source.string) || (source as string)); } -export function getTransformedText(text: string, textTransform: TextTransform): string { +export function getTransformedText(text: string, textTransform: CoreTypes.TextTransformType): string { if (!text || !isString(text)) { return ''; } @@ -100,7 +90,7 @@ function lineBreakToLineBreakMode(value: string) { return NSLineBreakMode.ByWordWrapping; } } -function whiteSpaceToLineBreakMode(value: WhiteSpace) { +function whiteSpaceToLineBreakMode(value: CoreTypes.WhiteSpaceType) { switch (value) { case 'initial': case 'normal': @@ -456,16 +446,19 @@ export class Label extends LabelBase { const fontWeight = this.style.fontWeight; const familyName = this.style.fontFamily || (this.style.fontInternal && this.style.fontInternal.fontFamily) || font?.familyName; - const result = createNativeAttributedString({ - text: this.html, - fontSize, - familyName, - fontWeight, - color: this.color, - letterSpacing: this.letterSpacing, - lineHeight: this.lineHeight, - textAlignment: this.nativeTextViewProtected.textAlignment - }) as NSMutableAttributedString; + const result = createNativeAttributedString( + { + text: this.html, + fontSize, + familyName, + fontWeight, + color: this.color, + letterSpacing: this.letterSpacing, + lineHeight: this.lineHeight, + textAlignment: this.nativeTextViewProtected.textAlignment + }, + this + ) as NSMutableAttributedString; // if (this.linkColor) { // this.nativeTextViewProtected.linkTextAttributes = null; // const color =this.linkColor.ios; @@ -679,7 +672,7 @@ export class Label extends LabelBase { attrDict[NSBackgroundColorAttributeName] = color.ios; } - const textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); + const textDecoration: CoreTypes.TextDecorationType = getClosestPropertyValue(textDecorationProperty, span); if (textDecoration) { const underline = textDecoration.indexOf('underline') !== -1; @@ -709,82 +702,82 @@ export class Label extends LabelBase { return NSMutableAttributedString.alloc().initWithStringAttributes(text, attrDict as any); } - [paddingTopProperty.getDefault](): Length { + [paddingTopProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [paddingTopProperty.setNative](value: Length) { + [paddingTopProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [paddingRightProperty.getDefault](): Length { + [paddingRightProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [paddingRightProperty.setNative](value: Length) { + [paddingRightProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [paddingBottomProperty.getDefault](): Length { + [paddingBottomProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [paddingBottomProperty.setNative](value: Length) { + [paddingBottomProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [paddingLeftProperty.getDefault](): Length { + [paddingLeftProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [paddingLeftProperty.setNative](value: Length) { + [paddingLeftProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [borderTopWidthProperty.getDefault](): Length { + [borderTopWidthProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [borderTopWidthProperty.setNative](value: Length) { + [borderTopWidthProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [borderRightWidthProperty.getDefault](): Length { + [borderRightWidthProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [borderRightWidthProperty.setNative](value: Length) { + [borderRightWidthProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [borderBottomWidthProperty.getDefault](): Length { + [borderBottomWidthProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [borderBottomWidthProperty.setNative](value: Length) { + [borderBottomWidthProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } - [borderLeftWidthProperty.getDefault](): Length { + [borderLeftWidthProperty.getDefault](): CoreTypes.LengthType { return { value: 0, unit: 'px' }; } - [borderLeftWidthProperty.setNative](value: Length) { + [borderLeftWidthProperty.setNative](value: CoreTypes.LengthType) { this.updateTextContainerInset(); } @@ -808,7 +801,7 @@ export class Label extends LabelBase { this.nativeTextViewProtected.layer.shouldRasterize = true; this.nativeTextViewProtected.layer.masksToBounds = false; } - [whiteSpaceProperty.setNative](value: WhiteSpace) { + [whiteSpaceProperty.setNative](value: CoreTypes.WhiteSpaceType) { const nativeView = this.nativeTextViewProtected; // only if no lineBreak if (!this.lineBreak) {