From 0ce6083257ccfe5642a7e0911354be167e1a71d0 Mon Sep 17 00:00:00 2001 From: Bundyo Date: Fri, 4 Oct 2019 09:37:17 +0300 Subject: [PATCH 01/10] Implement Android part of FormattedString's vertical-align property --- .../ui/styling/style-properties.ts | 9 +- nativescript-core/ui/text-base/span.ts | 2 +- .../ui/text-base/text-base-common.ts | 4 +- .../ui/text-base/text-base.android.ts | 120 +++++++++++++----- 4 files changed, 98 insertions(+), 37 deletions(-) diff --git a/nativescript-core/ui/styling/style-properties.ts b/nativescript-core/ui/styling/style-properties.ts index c2628fa2a5..d2b5c3e336 100644 --- a/nativescript-core/ui/styling/style-properties.ts +++ b/nativescript-core/ui/styling/style-properties.ts @@ -358,13 +358,18 @@ export namespace HorizontalAlignment { export const horizontalAlignmentProperty = new CssProperty({ name: "horizontalAlignment", cssName: "horizontal-align", defaultValue: HorizontalAlignment.STRETCH, affectsLayout: isIOS, valueConverter: HorizontalAlignment.parse }); horizontalAlignmentProperty.register(Style); -export type VerticalAlignment = "top" | "middle" | "bottom" | "stretch"; +export type VerticalAlignment = "top" | "middle" | "bottom" | "stretch" | "text-top" | "text-bottom" | "super" | "sub" | "baseline"; export namespace VerticalAlignment { export const TOP: "top" = "top"; export const MIDDLE: "middle" = "middle"; export const BOTTOM: "bottom" = "bottom"; export const STRETCH: "stretch" = "stretch"; - export const isValid = makeValidator(TOP, MIDDLE, BOTTOM, STRETCH); + export const TEXTTOP: "text-top" = "text-top"; + export const TEXTBOTTOM: "text-bottom" = "text-bottom"; + export const SUPER: "super" = "super"; + export const SUB: "sub" = "sub"; + export const BASELINE: "baseline" = "baseline"; + export const isValid = makeValidator(TOP, MIDDLE, BOTTOM, STRETCH, TEXTTOP, TEXTBOTTOM, SUPER, SUB, BASELINE); export const parse = (value: string) => value.toLowerCase() === "center" ? MIDDLE : parseStrict(value); const parseStrict = makeParser(isValid); } diff --git a/nativescript-core/ui/text-base/span.ts b/nativescript-core/ui/text-base/span.ts index c37adf340b..ec232191f1 100644 --- a/nativescript-core/ui/text-base/span.ts +++ b/nativescript-core/ui/text-base/span.ts @@ -65,7 +65,7 @@ export class Span extends ViewBase implements SpanDefinition { } set text(value: string) { if (this._text !== value) { - this._text = value; + this._text = value.replace("\\n", "\n").replace("\\t", "\t"); this.notifyPropertyChange("text", value); } } diff --git a/nativescript-core/ui/text-base/text-base-common.ts b/nativescript-core/ui/text-base/text-base-common.ts index a5da6f3c3f..b0f26dbfa4 100644 --- a/nativescript-core/ui/text-base/text-base-common.ts +++ b/nativescript-core/ui/text-base/text-base-common.ts @@ -217,10 +217,10 @@ const textDecorationConverter = makeParser(makeValidator({ name: "textDecoration", cssName: "text-decoration", defaultValue: "none", valueConverter: textDecorationConverter }); textDecorationProperty.register(Style); -export const letterSpacingProperty = new CssProperty({ name: "letterSpacing", cssName: "letter-spacing", defaultValue: 0, affectsLayout: isIOS, valueConverter: v => parseFloat(v) }); +export const letterSpacingProperty = new InheritedCssProperty({ name: "letterSpacing", cssName: "letter-spacing", defaultValue: 0, affectsLayout: isIOS, valueConverter: v => parseFloat(v) }); letterSpacingProperty.register(Style); -export const lineHeightProperty = new CssProperty({ name: "lineHeight", cssName: "line-height", affectsLayout: isIOS, valueConverter: v => parseFloat(v) }); +export const lineHeightProperty = new InheritedCssProperty({ name: "lineHeight", cssName: "line-height", affectsLayout: isIOS, valueConverter: v => parseFloat(v) }); lineHeightProperty.register(Style); export const resetSymbol = Symbol("textPropertyDefault"); diff --git a/nativescript-core/ui/text-base/text-base.android.ts b/nativescript-core/ui/text-base/text-base.android.ts index 356d32badc..1513274e29 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -11,6 +11,7 @@ import { whiteSpaceProperty, lineHeightProperty, FormattedString, layout, Span, Color, isBold, resetSymbol } from "./text-base-common"; import { isString } from "../../utils/types"; +import { Property } from "tns-core-modules/ui/core/properties/properties"; export * from "./text-base-common"; @@ -33,7 +34,7 @@ function initializeTextTransformation(): void { // NOTE: Do we need to transform the new text here? const formattedText = this.textBase.formattedText; if (formattedText) { - return createSpannableStringBuilder(formattedText); + return createSpannableStringBuilder(formattedText, (view).getTextSize()); } else { const text = this.textBase.text; @@ -170,7 +171,7 @@ export class TextBase extends TextBaseCommon { return; } - const spannableStringBuilder = createSpannableStringBuilder(value); + const spannableStringBuilder = createSpannableStringBuilder(value, this.style.fontSize); nativeView.setText(spannableStringBuilder); this._setTappableState(isStringTappable(value)); @@ -265,10 +266,11 @@ export class TextBase extends TextBaseCommon { } [lineHeightProperty.getDefault](): number { - return this.nativeTextViewProtected.getLineSpacingExtra() / layout.getDisplayDensity(); + return this.nativeTextViewProtected.getLineHeight() / layout.getDisplayDensity(); } [lineHeightProperty.setNative](value: number) { - this.nativeTextViewProtected.setLineSpacing(value * layout.getDisplayDensity(), 1); + const fontHeight = this.nativeTextViewProtected.getPaint().getFontMetricsInt(null); + this.nativeTextViewProtected.setLineSpacing(Math.max(value - fontHeight, 0) * layout.getDisplayDensity(), 1); } [fontInternalProperty.getDefault](): android.graphics.Typeface { @@ -348,7 +350,7 @@ export class TextBase extends TextBaseCommon { let transformedText: any; if (this.formattedText) { - transformedText = createSpannableStringBuilder(this.formattedText); + transformedText = createSpannableStringBuilder(this.formattedText, this.style.fontSize); } else { const text = this.text; const stringValue = (text === null || text === undefined) ? "" : text.toString(); @@ -415,7 +417,7 @@ function isStringTappable(formattedString: FormattedString) { return false; } -function createSpannableStringBuilder(formattedString: FormattedString): android.text.SpannableStringBuilder { +function createSpannableStringBuilder(formattedString: FormattedString, defaultFontSize: number): android.text.SpannableStringBuilder { if (!formattedString || !formattedString.parent) { return null; } @@ -433,7 +435,7 @@ function createSpannableStringBuilder(formattedString: FormattedString): android spanLength = spanText.length; if (spanLength > 0) { ssb.insert(spanStart, spanText); - setSpanModifiers(ssb, span, spanStart, spanStart + spanLength); + setSpanModifiers(ssb, span, spanStart, spanStart + spanLength, defaultFontSize); spanStart += spanLength; } } @@ -441,10 +443,79 @@ function createSpannableStringBuilder(formattedString: FormattedString): android return ssb; } -function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, start: number, end: number): void { +class BaselineAdjustedSpan extends android.text.style.MetricAffectingSpan { + fontSize: number; + align: string | number = "baseline"; + + constructor(fontSize: number, align?: string | number) { + super(); + + this.align = align; + this.fontSize = fontSize; + } + + updateDrawState(paint: android.text.TextPaint) { + this.updateState(paint); + } + + updateMeasureState(paint: android.text.TextPaint) { + this.updateState(paint); + } + + updateState(paint: android.text.TextPaint) { + const metrics = paint.getFontMetrics(); + + if (!this.align || this.align === "baseline") { + return; + } + + if (this.align === "top") { + return paint.baselineShift = -this.fontSize - metrics.bottom - metrics.top; + } + + if (this.align === "bottom") { + return paint.baselineShift = metrics.bottom; + } + + if (this.align === "text-top") { + return paint.baselineShift = -this.fontSize - metrics.descent - metrics.ascent; + } + + if (this.align === "text-bottom") { + return paint.baselineShift = metrics.bottom - metrics.descent; + } + + if (this.align === "middle") { + return paint.baselineShift = (metrics.descent - metrics.ascent) / 2 - metrics.descent; + } + + if (this.align === "super") { + return paint.baselineShift = -this.fontSize * .4; + } + + if (this.align === "sub") { + return paint.baselineShift = (metrics.descent - metrics.ascent) * .4; + } + } +} + +function getClosestPropertyValue(property: any, span: Span) { + if ((>property).isSet(span.style)) { + return span.style[property.name]; + } else if ((>property).isSet(span.parent.style)) { + // parent is FormattedString + return span.parent.style[property.name]; + } else if ((>property).isSet(span.parent.parent.style)) { + // parent.parent is TextBase + return span.parent.parent.style[property.name]; + } +} + +function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, start: number, end: number, defaultFontSize: number): void { const spanStyle = span.style; const bold = isBold(spanStyle.fontWeight); const italic = spanStyle.fontStyle === "italic"; + const align = spanStyle.verticalAlignment; if (bold && italic) { ssb.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD_ITALIC), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -474,45 +545,30 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, ssb.setSpan(new android.text.style.ForegroundColorSpan(color.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - let backgroundColor: Color; - if (backgroundColorProperty.isSet(spanStyle)) { - backgroundColor = spanStyle.backgroundColor; - } else if (backgroundColorProperty.isSet(span.parent.style)) { - // parent is FormattedString - backgroundColor = span.parent.style.backgroundColor; - } else if (backgroundColorProperty.isSet(span.parent.parent.style)) { - // parent.parent is TextBase - backgroundColor = span.parent.parent.style.backgroundColor; - } + let backgroundColor: Color = getClosestPropertyValue(backgroundColorProperty, span); if (backgroundColor) { ssb.setSpan(new android.text.style.BackgroundColorSpan(backgroundColor.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - let valueSource: typeof spanStyle; - if (textDecorationProperty.isSet(spanStyle)) { - valueSource = spanStyle; - } else if (textDecorationProperty.isSet(span.parent.style)) { - // span.parent is FormattedString - valueSource = span.parent.style; - } else if (textDecorationProperty.isSet(span.parent.parent.style)) { - // span.parent.parent is TextBase - valueSource = span.parent.parent.style; - } + let textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); - if (valueSource) { - const textDecorations = valueSource.textDecoration; - const underline = textDecorations.indexOf("underline") !== -1; + if (textDecoration) { + const underline = textDecoration.indexOf("underline") !== -1; if (underline) { ssb.setSpan(new android.text.style.UnderlineSpan(), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - const strikethrough = textDecorations.indexOf("line-through") !== -1; + const strikethrough = textDecoration.indexOf("line-through") !== -1; if (strikethrough) { ssb.setSpan(new android.text.style.StrikethroughSpan(), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } + if (align) { + ssb.setSpan(new BaselineAdjustedSpan(defaultFontSize * layout.getDisplayDensity(), align), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + const tappable = span.tappable; if (tappable) { initializeClickableSpan(); From 010406ab34b6550afa2f367244f38e1ae7bc330f Mon Sep 17 00:00:00 2001 From: Bundyo Date: Thu, 17 Oct 2019 08:06:49 +0300 Subject: [PATCH 02/10] Add partial iOS implementation --- .../ui/text-base/text-base.ios.ts | 95 +++++++++++++------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/nativescript-core/ui/text-base/text-base.ios.ts b/nativescript-core/ui/text-base/text-base.ios.ts index 963451adf2..6730331fea 100644 --- a/nativescript-core/ui/text-base/text-base.ios.ts +++ b/nativescript-core/ui/text-base/text-base.ios.ts @@ -1,15 +1,16 @@ // Types -import { TextDecoration, TextAlignment, TextTransform } from "./text-base-common"; +import { TextDecoration, TextAlignment, TextTransform, layout } from "./text-base-common"; // Requires import { Font } from "../styling/font"; import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, letterSpacingProperty, colorProperty, fontInternalProperty, lineHeightProperty, - FormattedString, Span, Color, isBold, resetSymbol + FormattedString, Span, Color, resetSymbol } from "./text-base-common"; import { isString } from "../../utils/types"; import { ios } from "../../utils/utils"; +import { Property } from "../core/properties/properties"; export * from "./text-base-common"; @@ -229,7 +230,7 @@ export class TextBase extends TextBaseCommon { if (this.style.lineHeight) { const paragraphStyle = NSMutableParagraphStyle.alloc().init(); - paragraphStyle.lineSpacing = this.lineHeight; + paragraphStyle.minimumLineHeight = this.lineHeight; // make sure a possible previously set text alignment setting is not lost when line height is specified if (this.nativeTextViewProtected instanceof UIButton) { paragraphStyle.alignment = (this.nativeTextViewProtected).titleLabel.textAlignment; @@ -370,21 +371,50 @@ export class TextBase extends TextBaseCommon { return mas; } + getBaselineOffset(font: UIFont, defaultFontSize: number, align?: string | number) : number { + console.log(font.pointSize, font.lineHeight, font.ascender, font.descender, font.leading); + + if (align === "top") { + return defaultFontSize - font.descender - font.ascender + font.leading / 2; + } + + if (align === "bottom") { + return -defaultFontSize / 2; + } + + if (align === "text-top") { + return defaultFontSize - font.descender - font.ascender; + } + + if (align === "text-bottom") { + return font.descender / font.pointSize * defaultFontSize; + } + + if (align === "middle") { + return -font.lineHeight / 2 - font.descender; + } + + if (align === "super") { + return defaultFontSize * .4; + } + + if (align === "sub") { + return (font.descender - font.ascender) / font.pointSize * font.lineHeight * .4; + } + + return 0; + } + createMutableStringForSpan(span: Span, text: string): NSMutableAttributedString { const viewFont = this.nativeTextViewProtected.font; let attrDict = <{ key: string, value: any }>{}; const style = span.style; - const bold = isBold(style.fontWeight); - const italic = style.fontStyle === "italic"; + const align = style.verticalAlignment; - let fontFamily = span.fontFamily; - let fontSize = span.fontSize; + let font = new Font(style.fontFamily, style.fontSize || 12, style.fontStyle, style.fontWeight); + let iosFont = font.getUIFont(viewFont); - if (bold || italic || fontFamily || fontSize) { - let font = new Font(style.fontFamily, style.fontSize, style.fontStyle, style.fontWeight); - let iosFont = font.getUIFont(viewFont); - attrDict[NSFontAttributeName] = iosFont; - } + attrDict[NSFontAttributeName] = iosFont; const color = span.color; if (color) { @@ -394,39 +424,50 @@ export class TextBase extends TextBaseCommon { // We don't use isSet function here because defaultValue for backgroundColor is null. const backgroundColor = (style.backgroundColor || (span.parent).backgroundColor - || ((span.parent).parent).backgroundColor); + || (span.parent.parent).backgroundColor); if (backgroundColor) { attrDict[NSBackgroundColorAttributeName] = backgroundColor.ios; } - let valueSource: typeof style; - if (textDecorationProperty.isSet(style)) { - valueSource = style; - } else if (textDecorationProperty.isSet(span.parent.style)) { - // span.parent is FormattedString - valueSource = span.parent.style; - } else if (textDecorationProperty.isSet(span.parent.parent.style)) { - // span.parent.parent is TextBase - valueSource = span.parent.parent.style; - } + let textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); - if (valueSource) { - const textDecorations = valueSource.textDecoration; - const underline = textDecorations.indexOf("underline") !== -1; + if (textDecoration) { + const underline = textDecoration.indexOf("underline") !== -1; if (underline) { attrDict[NSUnderlineStyleAttributeName] = underline; } - const strikethrough = textDecorations.indexOf("line-through") !== -1; + const strikethrough = textDecoration.indexOf("line-through") !== -1; if (strikethrough) { attrDict[NSStrikethroughStyleAttributeName] = strikethrough; } } + if (align) { + attrDict[NSBaselineOffsetAttributeName] = + this.getBaselineOffset( + iosFont, + (span.parent.parent).fontSize * layout.getDisplayDensity(), + align + ); + } + return NSMutableAttributedString.alloc().initWithStringAttributes(text, attrDict); } } +function getClosestPropertyValue(property: any, span: Span) { + if ((>property).isSet(span.style)) { + return span.style[property.name]; + } else if ((>property).isSet(span.parent.style)) { + // parent is FormattedString + return span.parent.style[property.name]; + } else if ((>property).isSet(span.parent.parent.style)) { + // parent.parent is TextBase + return span.parent.parent.style[property.name]; + } +} + export function getTransformedText(text: string, textTransform: TextTransform): string { if (!text || !isString(text)) { return ""; From f3dd4839389cf5eee4dac26bd5b7610c4a60453c Mon Sep 17 00:00:00 2001 From: bundyo Date: Sat, 18 Jan 2020 13:06:27 +0200 Subject: [PATCH 03/10] Fixes to the iOS implementation --- .../ui/text-base/text-base.ios.ts | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/nativescript-core/ui/text-base/text-base.ios.ts b/nativescript-core/ui/text-base/text-base.ios.ts index 6730331fea..6282e52776 100644 --- a/nativescript-core/ui/text-base/text-base.ios.ts +++ b/nativescript-core/ui/text-base/text-base.ios.ts @@ -371,38 +371,38 @@ export class TextBase extends TextBaseCommon { return mas; } - getBaselineOffset(font: UIFont, defaultFontSize: number, align?: string | number) : number { - console.log(font.pointSize, font.lineHeight, font.ascender, font.descender, font.leading); + getBaselineOffset(font: UIFont, align?: string | number) : number { + if (!align || align === "baseline") { + return 0; + } if (align === "top") { - return defaultFontSize - font.descender - font.ascender + font.leading / 2; + return -this.fontSize - font.descender - font.ascender - font.leading / 2; } if (align === "bottom") { - return -defaultFontSize / 2; + return font.descender + font.leading / 2; } if (align === "text-top") { - return defaultFontSize - font.descender - font.ascender; + return -this.fontSize - font.descender - font.ascender; } if (align === "text-bottom") { - return font.descender / font.pointSize * defaultFontSize; + return font.descender; } if (align === "middle") { - return -font.lineHeight / 2 - font.descender; + return (font.descender - font.ascender) / 2 - font.descender; } if (align === "super") { - return defaultFontSize * .4; + return -this.fontSize * .4; } if (align === "sub") { - return (font.descender - font.ascender) / font.pointSize * font.lineHeight * .4; + return (font.descender - font.ascender) * .4; } - - return 0; } createMutableStringForSpan(span: Span, text: string): NSMutableAttributedString { @@ -411,7 +411,7 @@ export class TextBase extends TextBaseCommon { const style = span.style; const align = style.verticalAlignment; - let font = new Font(style.fontFamily, style.fontSize || 12, style.fontStyle, style.fontWeight); + let font = new Font(style.fontFamily, style.fontSize, style.fontStyle, style.fontWeight); let iosFont = font.getUIFont(viewFont); attrDict[NSFontAttributeName] = iosFont; @@ -444,12 +444,7 @@ export class TextBase extends TextBaseCommon { } if (align) { - attrDict[NSBaselineOffsetAttributeName] = - this.getBaselineOffset( - iosFont, - (span.parent.parent).fontSize * layout.getDisplayDensity(), - align - ); + attrDict[NSBaselineOffsetAttributeName] = this.getBaselineOffset(iosFont, align); } return NSMutableAttributedString.alloc().initWithStringAttributes(text, attrDict); From 93910e8b512196d4a131ec6f28de21c9c11c9123 Mon Sep 17 00:00:00 2001 From: bundyo Date: Sat, 18 Jan 2020 13:22:54 +0200 Subject: [PATCH 04/10] Fix linter --- nativescript-core/ui/text-base/text-base.ios.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-core/ui/text-base/text-base.ios.ts b/nativescript-core/ui/text-base/text-base.ios.ts index 6282e52776..a3521025ab 100644 --- a/nativescript-core/ui/text-base/text-base.ios.ts +++ b/nativescript-core/ui/text-base/text-base.ios.ts @@ -371,7 +371,7 @@ export class TextBase extends TextBaseCommon { return mas; } - getBaselineOffset(font: UIFont, align?: string | number) : number { + getBaselineOffset(font: UIFont, align?: string | number): number { if (!align || align === "baseline") { return 0; } From 3c5b60dee7eeec2051ea00f7836e219c24704097 Mon Sep 17 00:00:00 2001 From: bundyo Date: Sat, 18 Jan 2020 13:28:24 +0200 Subject: [PATCH 05/10] Fix wrong import after rebase --- 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 1513274e29..c3c69cfb4c 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -11,7 +11,7 @@ import { whiteSpaceProperty, lineHeightProperty, FormattedString, layout, Span, Color, isBold, resetSymbol } from "./text-base-common"; import { isString } from "../../utils/types"; -import { Property } from "tns-core-modules/ui/core/properties/properties"; +import { Property } from "../core/properties/properties"; export * from "./text-base-common"; From 05b035523263a557b7d15a127a3ce6b57aa5bc6b Mon Sep 17 00:00:00 2001 From: Vasil Trifonov Date: Mon, 20 Jan 2020 17:09:11 +0200 Subject: [PATCH 06/10] fix: fixed setting text with null value --- nativescript-core/ui/text-base/span.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nativescript-core/ui/text-base/span.ts b/nativescript-core/ui/text-base/span.ts index ec232191f1..e2a648a349 100644 --- a/nativescript-core/ui/text-base/span.ts +++ b/nativescript-core/ui/text-base/span.ts @@ -65,7 +65,7 @@ export class Span extends ViewBase implements SpanDefinition { } set text(value: string) { if (this._text !== value) { - this._text = value.replace("\\n", "\n").replace("\\t", "\t"); + this._text = value && value.replace("\\n", "\n").replace("\\t", "\t"); this.notifyPropertyChange("text", value); } } From d8306320582d64802151415162e90b491ea0085d Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sat, 6 Jun 2020 13:26:39 -0700 Subject: [PATCH 07/10] chore: cleanup --- .../ui/text-base/text-base-common.ts | 12 +++++++++ .../ui/text-base/text-base.android.ts | 14 +--------- .../ui/text-base/text-base.ios.ts | 27 +++++-------------- 3 files changed, 20 insertions(+), 33 deletions(-) diff --git a/nativescript-core/ui/text-base/text-base-common.ts b/nativescript-core/ui/text-base/text-base-common.ts index b0f26dbfa4..57b47ddfde 100644 --- a/nativescript-core/ui/text-base/text-base-common.ts +++ b/nativescript-core/ui/text-base/text-base-common.ts @@ -201,6 +201,18 @@ function onFormattedTextPropertyChanged(textBase: TextBaseCommon, oldValue: Form } } +export function getClosestPropertyValue(property: CssProperty, span: Span): T { + if (property.isSet(span.style)) { + return span.style[property.name]; + } else if (property.isSet(span.parent.style)) { + // parent is FormattedString + return span.parent.style[property.name]; + } else if (property.isSet(span.parent.parent.style)) { + // parent.parent is TextBase + return span.parent.parent.style[property.name]; + } +} + const textAlignmentConverter = makeParser(makeValidator("initial", "left", "center", "right")); export const textAlignmentProperty = new InheritedCssProperty({ name: "textAlignment", cssName: "text-align", defaultValue: "initial", valueConverter: textAlignmentConverter }); textAlignmentProperty.register(Style); diff --git a/nativescript-core/ui/text-base/text-base.android.ts b/nativescript-core/ui/text-base/text-base.android.ts index c3c69cfb4c..5c2e0f71ab 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -1,5 +1,5 @@ // Types -import { TextTransformation, TextDecoration, TextAlignment, TextTransform, WhiteSpace } from "./text-base-common"; +import { TextTransformation, TextDecoration, TextAlignment, TextTransform, WhiteSpace, getClosestPropertyValue } from "./text-base-common"; // Requires import { Font } from "../styling/font"; @@ -499,18 +499,6 @@ class BaselineAdjustedSpan extends android.text.style.MetricAffectingSpan { } } -function getClosestPropertyValue(property: any, span: Span) { - if ((>property).isSet(span.style)) { - return span.style[property.name]; - } else if ((>property).isSet(span.parent.style)) { - // parent is FormattedString - return span.parent.style[property.name]; - } else if ((>property).isSet(span.parent.parent.style)) { - // parent.parent is TextBase - return span.parent.parent.style[property.name]; - } -} - function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, start: number, end: number, defaultFontSize: number): void { const spanStyle = span.style; const bold = isBold(spanStyle.fontWeight); diff --git a/nativescript-core/ui/text-base/text-base.ios.ts b/nativescript-core/ui/text-base/text-base.ios.ts index a3521025ab..4b2aeb7e37 100644 --- a/nativescript-core/ui/text-base/text-base.ios.ts +++ b/nativescript-core/ui/text-base/text-base.ios.ts @@ -1,5 +1,5 @@ // Types -import { TextDecoration, TextAlignment, TextTransform, layout } from "./text-base-common"; +import { TextDecoration, TextAlignment, TextTransform, layout, getClosestPropertyValue } from "./text-base-common"; // Requires import { Font } from "../styling/font"; @@ -407,18 +407,17 @@ export class TextBase extends TextBaseCommon { createMutableStringForSpan(span: Span, text: string): NSMutableAttributedString { const viewFont = this.nativeTextViewProtected.font; - let attrDict = <{ key: string, value: any }>{}; + const attrDict = <{ key: string, value: any }>{}; const style = span.style; const align = style.verticalAlignment; - let font = new Font(style.fontFamily, style.fontSize, style.fontStyle, style.fontWeight); - let iosFont = font.getUIFont(viewFont); + const font = new Font(style.fontFamily, style.fontSize, style.fontStyle, style.fontWeight); + const iosFont = font.getUIFont(viewFont); attrDict[NSFontAttributeName] = iosFont; - const color = span.color; - if (color) { - attrDict[NSForegroundColorAttributeName] = color.ios; + if (span.color) { + attrDict[NSForegroundColorAttributeName] = span.color.ios; } // We don't use isSet function here because defaultValue for backgroundColor is null. @@ -429,7 +428,7 @@ export class TextBase extends TextBaseCommon { attrDict[NSBackgroundColorAttributeName] = backgroundColor.ios; } - let textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); + const textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); if (textDecoration) { const underline = textDecoration.indexOf("underline") !== -1; @@ -451,18 +450,6 @@ export class TextBase extends TextBaseCommon { } } -function getClosestPropertyValue(property: any, span: Span) { - if ((>property).isSet(span.style)) { - return span.style[property.name]; - } else if ((>property).isSet(span.parent.style)) { - // parent is FormattedString - return span.parent.style[property.name]; - } else if ((>property).isSet(span.parent.parent.style)) { - // parent.parent is TextBase - return span.parent.parent.style[property.name]; - } -} - export function getTransformedText(text: string, textTransform: TextTransform): string { if (!text || !isString(text)) { return ""; From 68f783421d44c590a53feb97e821d7f56825cf6a Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sat, 6 Jun 2020 13:28:56 -0700 Subject: [PATCH 08/10] chore: cleanup --- nativescript-core/ui/text-base/text-base.android.ts | 1 - 1 file changed, 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 5c2e0f71ab..9207e5983b 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -11,7 +11,6 @@ import { whiteSpaceProperty, lineHeightProperty, FormattedString, layout, Span, Color, isBold, resetSymbol } from "./text-base-common"; import { isString } from "../../utils/types"; -import { Property } from "../core/properties/properties"; export * from "./text-base-common"; From f85b60eaf929d9509c0cba2de86e67c3c76f8d71 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sat, 6 Jun 2020 13:30:32 -0700 Subject: [PATCH 09/10] chore: cleanup --- nativescript-core/ui/text-base/text-base.android.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nativescript-core/ui/text-base/text-base.android.ts b/nativescript-core/ui/text-base/text-base.android.ts index 9207e5983b..808ac3f639 100644 --- a/nativescript-core/ui/text-base/text-base.android.ts +++ b/nativescript-core/ui/text-base/text-base.android.ts @@ -532,13 +532,13 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, ssb.setSpan(new android.text.style.ForegroundColorSpan(color.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - let backgroundColor: Color = getClosestPropertyValue(backgroundColorProperty, span); + const backgroundColor: Color = getClosestPropertyValue(backgroundColorProperty, span); if (backgroundColor) { ssb.setSpan(new android.text.style.BackgroundColorSpan(backgroundColor.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - let textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); + const textDecoration: TextDecoration = getClosestPropertyValue(textDecorationProperty, span); if (textDecoration) { const underline = textDecoration.indexOf("underline") !== -1; From ea134a034f1febc5c0f51775c2b9f8a3c1b64489 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sat, 6 Jun 2020 14:01:49 -0700 Subject: [PATCH 10/10] chore: cleanup --- .../ui/text-base/text-base.ios.ts | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/nativescript-core/ui/text-base/text-base.ios.ts b/nativescript-core/ui/text-base/text-base.ios.ts index 4b2aeb7e37..fa36b9f8ad 100644 --- a/nativescript-core/ui/text-base/text-base.ios.ts +++ b/nativescript-core/ui/text-base/text-base.ios.ts @@ -281,12 +281,15 @@ export class TextBase extends TextBaseCommon { throw new Error(`Invalid text decoration value: ${style.textDecoration}. Valid values are: 'none', 'underline', 'line-through', 'underline line-through'.`); } - if (style.letterSpacing !== 0) { + if (style.letterSpacing !== 0 && this.nativeTextViewProtected.font) { const kern = style.letterSpacing * this.nativeTextViewProtected.font.pointSize dict.set(NSKernAttributeName, kern); - if (this.nativeTextViewProtected instanceof UITextField) { - this.nativeTextViewProtected.defaultTextAttributes.setObjectForKey(kern, NSKernAttributeName); - } + } + + if (style.color) { + dict.set(NSForegroundColorAttributeName, style.color.ios); + } else if (majorVersion >= 13 && UIColor.labelColor) { + dict.set(NSForegroundColorAttributeName, UIColor.labelColor); } const isTextView = this.nativeTextViewProtected instanceof UITextView; @@ -311,19 +314,9 @@ export class TextBase extends TextBaseCommon { dict.set(NSParagraphStyleAttributeName, paragraphStyle); } + const source = getTransformedText(this.text ? this.text.toString() : "", this.textTransform); if (dict.size > 0 || isTextView) { - if (style.color) { - dict.set(NSForegroundColorAttributeName, style.color.ios); - } else if (majorVersion >= 13 && UIColor.labelColor) { - dict.set(NSForegroundColorAttributeName, UIColor.labelColor); - } - } - - const text = this.text; - const string = (text === undefined || text === null) ? "" : text.toString(); - const source = getTransformedText(string, this.textTransform); - if (dict.size > 0 || isTextView) { - if (isTextView) { + if (isTextView && this.nativeTextViewProtected.font) { // UITextView's font seems to change inside. dict.set(NSFontAttributeName, this.nativeTextViewProtected.font); }