diff --git a/tns-core-modules/ui/button/button-common.ts b/tns-core-modules/ui/button/button-common.ts index 0f0f1d8bc..fc253eff9 100644 --- a/tns-core-modules/ui/button/button-common.ts +++ b/tns-core-modules/ui/button/button-common.ts @@ -1,5 +1,5 @@ import { Button as ButtonDefinition } from "ui/button"; -import { TextBase } from "ui/text-base"; +import { TextBase, WhiteSpace } from "ui/text-base"; export * from "ui/text-base"; @@ -7,9 +7,9 @@ export abstract class ButtonBase extends TextBase implements ButtonDefinition { public static tapEvent = "tap"; get textWrap(): boolean { - return this.style.whiteSpace === "normal"; + return this.style.whiteSpace === WhiteSpace.NORMAL; } set textWrap(value: boolean) { - this.style.whiteSpace = value ? "normal" : "nowrap"; + this.style.whiteSpace = value ? WhiteSpace.NORMAL : WhiteSpace.NO_WRAP; } } \ No newline at end of file diff --git a/tns-core-modules/ui/button/button.android.ts b/tns-core-modules/ui/button/button.android.ts index e530023d1..dc462f493 100644 --- a/tns-core-modules/ui/button/button.android.ts +++ b/tns-core-modules/ui/button/button.android.ts @@ -1,6 +1,6 @@ import { ButtonBase, textProperty, formattedTextProperty, TouchGestureEventData, FormattedString, GestureTypes, TouchAction, - PseudoClassHandler + PseudoClassHandler, TextTransform } from "./button-common"; export * from "./button-common"; @@ -23,7 +23,6 @@ class ClickListener extends java.lang.Object implements android.view.View.OnClic export class Button extends ButtonBase { _button: android.widget.Button; private _isPressed: boolean; - private _transformationMethod; private _highlightedHandler: (args: TouchGestureEventData) => void; get android(): android.widget.Button { @@ -36,22 +35,6 @@ export class Button extends ButtonBase { this._button.setOnClickListener(new ClickListener(weakRef)); } - public _setFormattedTextPropertyToNative(value: FormattedString) { - let newText = value ? value._formattedText : null; - if (newText) { - if (!this._transformationMethod) { - this._transformationMethod = this.android.getTransformationMethod(); - } - this.android.setTransformationMethod(null); - } else { - if (this._transformationMethod && !this.android.getTransformationMethod()) { - this.android.setTransformationMethod(this._transformationMethod); - } - } - - this._button.setText(newText); - } - @PseudoClassHandler("normal", "highlighted", "pressed", "active") _updateHandler(subscribe: boolean) { if (subscribe) { diff --git a/tns-core-modules/ui/button/button.ios.ts b/tns-core-modules/ui/button/button.ios.ts index f09cb0b18..f7d18a440 100644 --- a/tns-core-modules/ui/button/button.ios.ts +++ b/tns-core-modules/ui/button/button.ios.ts @@ -2,7 +2,7 @@ import { View, ButtonBase, PseudoClassHandler, textProperty, formattedTextProperty, whiteSpaceProperty, borderTopWidthProperty, borderRightWidthProperty, borderBottomWidthProperty, borderLeftWidthProperty, - paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty, Length + paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty, Length, WhiteSpace } from "./button-common"; export * from "./button-common"; @@ -42,18 +42,22 @@ export class Button extends ButtonBase { } } - get [whiteSpaceProperty.native](): "normal" | "nowrap" { - return "normal"; + get [whiteSpaceProperty.native](): WhiteSpace { + return WhiteSpace.NORMAL; } - set [whiteSpaceProperty.native](value: "normal" | "nowrap") { + set [whiteSpaceProperty.native](value: WhiteSpace) { let nativeView = this.nativeView.titleLabel; - if (value === "normal") { - nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping; - nativeView.numberOfLines = 0; - } - else { - nativeView.lineBreakMode = NSLineBreakMode.ByTruncatingTail; - nativeView.numberOfLines = 1; + switch(value){ + case WhiteSpace.NORMAL: + nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping; + nativeView.numberOfLines = 0; + break; + case WhiteSpace.NO_WRAP: + nativeView.lineBreakMode = NSLineBreakMode.ByTruncatingTail; + nativeView.numberOfLines = 1; + break; + default: + throw new Error(`Invalid whitespace value: ${value}. Valid values are: "${WhiteSpace.NORMAL}", "${WhiteSpace.NO_WRAP}".`); } } diff --git a/tns-core-modules/ui/label/label.android.ts b/tns-core-modules/ui/label/label.android.ts index 3913891f5..6c80d133e 100644 --- a/tns-core-modules/ui/label/label.android.ts +++ b/tns-core-modules/ui/label/label.android.ts @@ -1,5 +1,5 @@ import { Label as LabelDefinition } from "ui/label"; -import { TextBase } from "ui/text-base"; +import { TextBase, WhiteSpace } from "ui/text-base"; export * from "ui/text-base"; @@ -7,10 +7,10 @@ export class Label extends TextBase implements LabelDefinition { private _android: android.widget.TextView; get textWrap(): boolean { - return this.style.whiteSpace === "normal"; + return this.style.whiteSpace === WhiteSpace.NORMAL; } set textWrap(value: boolean) { - this.style.whiteSpace = value ? "normal" : "nowrap"; + this.style.whiteSpace = value ? WhiteSpace.NORMAL : WhiteSpace.NO_WRAP; } get android(): android.widget.TextView { diff --git a/tns-core-modules/ui/label/label.ios.ts b/tns-core-modules/ui/label/label.ios.ts index d2e10d43b..b4f31f244 100644 --- a/tns-core-modules/ui/label/label.ios.ts +++ b/tns-core-modules/ui/label/label.ios.ts @@ -3,7 +3,7 @@ import { Background, TextBase, View, layout, backgroundInternalProperty, borderTopWidthProperty, borderRightWidthProperty, borderBottomWidthProperty, borderLeftWidthProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty, whiteSpaceProperty, - Length + Length, WhiteSpace } from "ui/text-base"; import { ios } from "ui/styling/background"; @@ -42,10 +42,10 @@ export class Label extends TextBase implements LabelDefinition { } get textWrap(): boolean { - return this.style.whiteSpace === "normal"; + return this.style.whiteSpace === WhiteSpace.NORMAL; } set textWrap(value: boolean) { - this.style.whiteSpace = value ? "normal" : "nowrap"; + this.style.whiteSpace = value ? WhiteSpace.NORMAL : WhiteSpace.NO_WRAP; } public onLoaded() { @@ -86,7 +86,7 @@ export class Label extends TextBase implements LabelDefinition { let nativeSize = nativeView.sizeThatFits(CGSizeMake(width, height)); let labelWidth = nativeSize.width; - if (!this.textWrap && this.style.whiteSpace !== "nowrap") { + if (this.textWrap) { labelWidth = Math.min(labelWidth, width); } @@ -101,18 +101,22 @@ export class Label extends TextBase implements LabelDefinition { } } - get [whiteSpaceProperty.native](): "nowrap" | "normal" { - return "normal"; + get [whiteSpaceProperty.native](): WhiteSpace { + return WhiteSpace.NORMAL; } - set [whiteSpaceProperty.native](value: string) { + set [whiteSpaceProperty.native](value: WhiteSpace) { let nativeView = this.nativeView; - if (value === "normal") { - nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping; - nativeView.numberOfLines = 0; - } - else { - nativeView.lineBreakMode = NSLineBreakMode.ByTruncatingTail; - nativeView.numberOfLines = 1; + switch(value){ + case WhiteSpace.NORMAL: + nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping; + nativeView.numberOfLines = 0; + break; + case WhiteSpace.NO_WRAP: + nativeView.lineBreakMode = NSLineBreakMode.ByTruncatingTail; + nativeView.numberOfLines = 1; + break; + default: + throw new Error(`Invalid whitespace value: ${value}. Valid values are: "${WhiteSpace.NORMAL}", "${WhiteSpace.NO_WRAP}".`); } } diff --git a/tns-core-modules/ui/styling/converters.ts b/tns-core-modules/ui/styling/converters.ts index 211a95ba7..f8b0d2a73 100644 --- a/tns-core-modules/ui/styling/converters.ts +++ b/tns-core-modules/ui/styling/converters.ts @@ -15,54 +15,6 @@ export function fontSizeConverter(value: string): number { return floatConverter(value); } -export function textAlignConverter(value: string): string { - switch (value) { - case "left": - case "center": - case "right": - return value; - default: - throw new Error("CSS text-align \"" + value + "\" is not supported."); - } -} - -export function textDecorationConverter(value: string): string { - const values = (value + "").split(" "); - - if (values.indexOf("none") !== -1 - || values.indexOf("underline") !== -1 - || values.indexOf("line-through") !== -1 - || values.indexOf("underline line-through") !== -1 - || values.indexOf("line-through underline") !== -1 - ) { - return value; - } else { - throw new Error("CSS text-decoration \"" + value + "\" is not supported."); - } -} - -export function whiteSpaceConverter(value: string): string { - switch (value) { - case "normal": - case "nowrap": - return value; - default: - throw new Error("CSS white-space \"" + value + "\" is not supported."); - } -} - -export function textTransformConverter(value: string): string { - switch (value) { - case "none": - case "uppercase": - case "lowercase": - case "capitalize": - return value; - default: - throw new Error("CSS text-transform \"" + value + "\" is not supported."); - } -} - export const numberConverter = parseFloat; export function visibilityConverter(value: string): string { diff --git a/tns-core-modules/ui/styling/style.d.ts b/tns-core-modules/ui/styling/style.d.ts index 2e2e2d9f4..47b37bb6a 100644 --- a/tns-core-modules/ui/styling/style.d.ts +++ b/tns-core-modules/ui/styling/style.d.ts @@ -1,6 +1,6 @@ declare module "ui/styling/style" { import { Length, PercentLength, Color, Background, Font, ViewBase, Observable } from "ui/core/view"; - import { TextDecoration } from "ui/text-base"; + import { TextAlignment, TextDecoration, TextTransform, WhiteSpace } from "ui/text-base"; export interface Thickness { left: number; @@ -86,10 +86,10 @@ declare module "ui/styling/style" { public visibility: "visible" | "hidden" | "collapse" | "collapsed"; public letterSpacing: number; - public textAlignment: "left" | "center" | "right"; + public textAlignment: TextAlignment; public textDecoration: TextDecoration; - public textTransform: "none" | "capitalize" | "uppercase" | "lowercase"; - public whiteSpace: "normal" | "nowrap"; + public textTransform: TextTransform; + public whiteSpace: WhiteSpace; public minWidth: Length; public minHeight: Length; diff --git a/tns-core-modules/ui/styling/style.ts b/tns-core-modules/ui/styling/style.ts index 3f6692022..68e2d9450 100644 --- a/tns-core-modules/ui/styling/style.ts +++ b/tns-core-modules/ui/styling/style.ts @@ -8,7 +8,7 @@ import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "ui/layouts/flexbox-layout"; -import { TextDecoration } from "ui/text-base"; +import { TextAlignment, TextDecoration, TextTransform, WhiteSpace } from "ui/text-base"; export class Style extends Observable implements StyleDefinition { constructor(public view: ViewBase) { @@ -65,10 +65,10 @@ export class Style extends Observable implements StyleDefinition { public visibility: "visible" | "hidden" | "collapse" | "collapsed"; public letterSpacing: number; - public textAlignment: "left" | "center" | "right"; + public textAlignment: TextAlignment; public textDecoration: TextDecoration; - public textTransform: "none" | "capitalize" | "uppercase" | "lowercase"; - public whiteSpace: "normal" | "nowrap"; + public textTransform: TextTransform; + public whiteSpace: WhiteSpace; public minWidth: Length; public minHeight: Length; diff --git a/tns-core-modules/ui/text-base/text-base-common.ts b/tns-core-modules/ui/text-base/text-base-common.ts index ca1c7d664..47fc185ed 100644 --- a/tns-core-modules/ui/text-base/text-base-common.ts +++ b/tns-core-modules/ui/text-base/text-base-common.ts @@ -7,30 +7,6 @@ import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-even export { FormattedString }; export * from "ui/core/view"; -function onFormattedTextPropertyChanged(textBase: TextBaseCommon, oldValue: FormattedString, newValue: FormattedString) { - if (oldValue) { - oldValue.parent = null; - removeWeakEventListener(oldValue, Observable.propertyChangeEvent, textBase.onFormattedTextChanged, textBase); - } - - if (newValue) { - newValue.parent = textBase; - addWeakEventListener(newValue, Observable.propertyChangeEvent, textBase.onFormattedTextChanged, textBase); - } - - // textBase._onFormattedTextPropertyChanged(newValue); -} -function onTextPropertyChanged(textBase: TextBaseCommon, oldValue: string, newValue: string) { - // textBase._onTextPropertyChanged(newValue); - - // //RemoveThisDoubleCall - // textBase.style._updateTextTransform(); - // textBase.style._updateTextDecoration(); -} - -// (textProperty.metadata).onSetNativeValue = onTextPropertyChanged; -// (formattedTextProperty.metadata).onSetNativeValue = onFormattedTextPropertyChanged; - export abstract class TextBaseCommon extends View implements TextBaseDefinition, FormattedStringView { constructor() { @@ -39,24 +15,11 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition, this.formattedText = new FormattedString(); } - public abstract _setFormattedTextPropertyToNative(value): void; - - // public _onBindingContextChanged(oldValue: any, newValue: any) { - // super._onBindingContextChanged(oldValue, newValue); - // if (this.formattedText) { - // this.formattedText.updateSpansBindingContext(newValue); - // } - - // //This is because of ListView virtualization - // //RemoveThisDoubleCall - // this.style._updateTextTransform(); - // this.style._updateTextDecoration(); - // } + public abstract _setFormattedTextPropertyToNative(value: FormattedString): void; public text: string; public formattedText: FormattedString; - // TODO: Do we need to export these properties here?? get fontSize(): number { return this.style.fontSize; } @@ -71,10 +34,10 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition, this.style.letterSpacing = value; } - get textAlignment(): "left" | "center" | "right" { + get textAlignment(): TextAlignment { return this.style.textAlignment; } - set textAlignment(value: "left" | "center" | "right") { + set textAlignment(value: TextAlignment) { this.style.textAlignment = value; } @@ -85,17 +48,17 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition, this.style.textDecoration = value; } - get textTransform(): "none" | "capitalize" | "uppercase" | "lowercase" { + get textTransform(): TextTransform { return this.style.textTransform; } - set textTransform(value: "none" | "capitalize" | "uppercase" | "lowercase") { + set textTransform(value: TextTransform) { this.style.textTransform = value; } - get whiteSpace(): "normal" | "nowrap" { + get whiteSpace(): WhiteSpace { return this.style.whiteSpace; } - set whiteSpace(value: "normal" | "nowrap") { + set whiteSpace(value: WhiteSpace) { this.style.whiteSpace = value; } @@ -114,28 +77,40 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition, } } +//Text export const textProperty = new Property({ name: "text", defaultValue: "" }); textProperty.register(TextBaseCommon); +//FormattedText export const formattedTextProperty = new Property({ name: "formattedText", affectsLayout: isIOS, valueChanged: onFormattedTextPropertyChanged }); formattedTextProperty.register(TextBaseCommon); -export const textAlignmentProperty = new InheritedCssProperty({ - name: "textAlignment", cssName: "text-align", valueConverter: (value) => { - switch (value) { - case "left": - case "center": - case "right": - return <"left" | "center" | "right">value; - - default: - throw new Error(`CSS text-align ${value} is not supported.`); - } +function onFormattedTextPropertyChanged(textBase: TextBaseCommon, oldValue: FormattedString, newValue: FormattedString) { + if (oldValue) { + oldValue.parent = null; + removeWeakEventListener(oldValue, Observable.propertyChangeEvent, textBase.onFormattedTextChanged, textBase); } -}); + + if (newValue) { + newValue.parent = textBase; + addWeakEventListener(newValue, Observable.propertyChangeEvent, textBase.onFormattedTextChanged, textBase); + } +} + +//TextAlignment +export type TextAlignment = "left" | "center" | "right"; +export namespace TextAlignment { + export const LEFT: "left" = "left"; + export const CENTER: "center" = "center"; + export const RIGHT: "right" = "right"; + export const isValid = makeValidator(LEFT, CENTER, RIGHT); + export const parse = makeParser(isValid, undefined); +} + +export const textAlignmentProperty = new InheritedCssProperty({name: "textAlignment", cssName: "text-align", valueConverter: TextAlignment.parse}); textAlignmentProperty.register(Style); -// TextDecoration +//TextDecoration export type TextDecoration = "none" | "underline" | "line-through" | "underline line-through"; export namespace TextDecoration { export const NONE: "none" = "none"; @@ -146,37 +121,32 @@ export namespace TextDecoration { export const isValid = makeValidator(NONE, UNDERLINE, LINE_THROUGH, UNDERLINE_LINE_THROUGH); export const parse = makeParser(isValid, NONE); } -export const textDecorationProperty = new CssProperty({ - name: "textDecoration", cssName: "text-decoration", defaultValue: TextDecoration.NONE, valueConverter: TextDecoration.parse}); +export const textDecorationProperty = new CssProperty({name: "textDecoration", cssName: "text-decoration", defaultValue: TextDecoration.NONE, valueConverter: TextDecoration.parse}); textDecorationProperty.register(Style); -export const textTransformProperty = new CssProperty({ - name: "textTransform", cssName: "text-transform", defaultValue: "none", valueConverter: (value) => { - switch (value) { - case "none": - case "uppercase": - case "lowercase": - case "capitalize": - return <"none" | "capitalize" | "uppercase" | "lowercase">value; - - default: - throw new Error(`CSS text-transform ${value} is not supported.`); - } - } -}); +//TextTransform +export type TextTransform = "none" | "capitalize" | "uppercase" | "lowercase"; +export namespace TextTransform { + export const NONE: "none" = "none"; + export const CAPITALIZE: "capitalize" = "capitalize"; + export const UPPERCASE: "uppercase" = "uppercase"; + export const LOWERCASE: "lowercase" ="lowercase"; + export const isValid = makeValidator(NONE, CAPITALIZE, UPPERCASE, LOWERCASE); + export const parse = makeParser(isValid, NONE); +} +export const textTransformProperty = new CssProperty({name: "textTransform", cssName: "text-transform", defaultValue: TextTransform.NONE, valueConverter: TextTransform.parse}); textTransformProperty.register(Style); -export const whiteSpaceProperty = new CssProperty({ - name: "whiteSpace", cssName: "white-space", valueConverter: (value: "normal" | "nowrap") => { - switch (value) { - case "normal": - case "nowrap": - return value; - default: - throw new Error(`CSS white-space ${value} is not supported.`); - } - } -}); +//Whitespace +export type WhiteSpace = "normal" | "nowrap"; +export namespace WhiteSpace { + export const NORMAL: "normal" = "normal"; + export const NO_WRAP: "nowrap" = "nowrap"; + export const isValid = makeValidator(NORMAL, NO_WRAP); + export const parse = makeParser(isValid, NORMAL); +} + +export const whiteSpaceProperty = new CssProperty({name: "whiteSpace", cssName: "white-space", defaultValue: WhiteSpace.NORMAL, valueConverter: WhiteSpace.parse}); whiteSpaceProperty.register(Style); export const letterSpacingProperty = new CssProperty({ name: "letterSpacing", cssName: "letter-spacing", defaultValue: 0, affectsLayout: isIOS, valueConverter: (v: string) => parseFloat(v) }); diff --git a/tns-core-modules/ui/text-base/text-base.android.ts b/tns-core-modules/ui/text-base/text-base.android.ts index 502f7f48c..cb04e4fda 100644 --- a/tns-core-modules/ui/text-base/text-base.android.ts +++ b/tns-core-modules/ui/text-base/text-base.android.ts @@ -1,83 +1,15 @@ import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, letterSpacingProperty, colorProperty, fontInternalProperty, whiteSpaceProperty, - Font, Color, FormattedString, TextDecoration + Font, Color, FormattedString, TextDecoration, TextAlignment, TextTransform, WhiteSpace } from "./text-base-common"; export * from "./text-base-common"; - -function getCapitalizedString(str: string): string { - let words = str.split(" "); - let newWords = []; - for (let i = 0, length = words.length; i < length; i++) { - let word = words[i].toLowerCase(); - newWords.push(word.substr(0, 1).toUpperCase() + word.substring(1)); - } - - return newWords.join(" "); -} - -export function getTransformedText(text: string, transform: "none" | "capitalize" | "uppercase" | "lowercase"): string { - switch (transform) { - case "uppercase": - return text.toUpperCase(); - - case "lowercase": - return text.toLowerCase(); - - case "capitalize": - return getCapitalizedString(text); - - default: - return text; - } -} - -@Interfaces([android.text.method.TransformationMethod]) -class TextTransformation extends android.text.method.ReplacementTransformationMethod { - constructor(public originalText: string, public formattedText: FormattedString, public textTransform: "none" | "capitalize" | "uppercase" | "lowercase") { - super(); - return global.__native(this); - } - - protected getOriginal(): native.Array { - return this.formattedText ? this.formattedText._formattedText : this.originalText; - } - - protected getReplacement(): native.Array { - let result: string = ""; - let textTransform = this.textTransform - if (this.formattedText) { - for (let i = 0, length = this.formattedText.spans.length; i < length; i++) { - let span = this.formattedText.spans.getItem(i); - result += getTransformedText(span.text, textTransform); - // span.text = formatString(span.text, this.textTransform); - } - } else { - result = getTransformedText(this.originalText, textTransform); - } - - return result; - } -} - export class TextBase extends TextBaseCommon { _transformationMethod: any; _nativeView: android.widget.TextView; - public _setFormattedTextPropertyToNative(value: FormattedString) { - // TODO: Check if there is an option to force call the transformation method without - // creating new native instance. - if (this._nativeView) { - this._nativeView.setTransformationMethod(new TextTransformation(this.text, value, this.style.textTransform)); - } - - let newText = value ? value._formattedText : null; - if (this._nativeView) { - this._nativeView.setText(newText); - } - } - + //Text get [textProperty.native](): string { return this._nativeView.getText(); } @@ -89,6 +21,7 @@ export class TextBase extends TextBaseCommon { this._nativeView.setText(value); } + //FormattedText get [formattedTextProperty.native](): FormattedString { return null; } @@ -96,6 +29,7 @@ export class TextBase extends TextBaseCommon { this._setFormattedTextPropertyToNative(value); } + //Color get [colorProperty.native](): android.content.res.ColorStateList { return this._nativeView.getTextColors(); } @@ -107,6 +41,7 @@ export class TextBase extends TextBaseCommon { } } + //FontInternal get [fontInternalProperty.native](): { typeface: android.graphics.Typeface, fontSize: number } { let textView = this._nativeView; return { @@ -130,74 +65,118 @@ export class TextBase extends TextBaseCommon { textView.setTypeface(typeface); } - get [textAlignmentProperty.native](): string { - let textGravity = this._nativeView.getGravity() & android.view.View.TEXT_ALIGNMENT_GRAVITY; - switch (textGravity) { + //TextAlignment + get [textAlignmentProperty.native](): TextAlignment { + let textAlignmentGravity = this._nativeView.getGravity() & android.view.View.TEXT_ALIGNMENT_GRAVITY; + switch (textAlignmentGravity) { case android.view.Gravity.LEFT: - return "left"; - + return TextAlignment.LEFT; case android.view.Gravity.CENTER_HORIZONTAL: - return "center"; - + return TextAlignment.CENTER; case android.view.Gravity.RIGHT: - return "right"; - + return TextAlignment.RIGHT; default: - throw new Error("Invalid textGravity: " + textGravity); + throw new Error(`Unsupported android.view.View.TEXT_ALIGNMENT_GRAVITY: ${textAlignmentGravity}. Currently supported values are android.view.Gravity.LEFT, android.view.Gravity.CENTER_HORIZONTAL, and android.view.Gravity.RIGHT.`); } } - set [textAlignmentProperty.native](value: string) { + set [textAlignmentProperty.native](value: TextAlignment) { let verticalGravity = this._nativeView.getGravity() & android.view.Gravity.VERTICAL_GRAVITY_MASK; switch (value) { - case "left": + case TextAlignment.LEFT: this._nativeView.setGravity(android.view.Gravity.LEFT | verticalGravity); break; - case "center": + case TextAlignment.CENTER: this._nativeView.setGravity(android.view.Gravity.CENTER_HORIZONTAL | verticalGravity); break; - case "right": + case TextAlignment.RIGHT: this._nativeView.setGravity(android.view.Gravity.RIGHT | verticalGravity); break; default: - break; + throw new Error(`Invalid text alignment value: ${value}. Valid values are: "${TextAlignment.LEFT}", "${TextAlignment.CENTER}", "${TextAlignment.RIGHT}".`); } } + //TextDecoration get [textDecorationProperty.native](): TextDecoration { - return "none"; + return TextDecoration.NONE; } set [textDecorationProperty.native](value: TextDecoration) { - let flags = 0; - let values = (value + "").split(" "); - - if (values.indexOf("underline") !== -1) { - flags = flags | android.graphics.Paint.UNDERLINE_TEXT_FLAG; - } - - if (values.indexOf("line-through") !== -1) { - flags = flags | android.graphics.Paint.STRIKE_THRU_TEXT_FLAG; - } - - if (values.indexOf("none") === -1) { - this._nativeView.setPaintFlags(flags); - } else { - this._nativeView.setPaintFlags(0); + let flags: number; + + switch(value){ + case TextDecoration.NONE: + flags = 0; + break; + case TextDecoration.UNDERLINE: + flags = android.graphics.Paint.UNDERLINE_TEXT_FLAG; + break; + case TextDecoration.LINE_THROUGH: + flags = android.graphics.Paint.STRIKE_THRU_TEXT_FLAG; + break; + case TextDecoration.UNDERLINE_LINE_THROUGH: + flags = android.graphics.Paint.UNDERLINE_TEXT_FLAG | android.graphics.Paint.STRIKE_THRU_TEXT_FLAG; + break; + default: + throw new Error(`Invalid text decoration value: ${value}. Valid values are: "${TextDecoration.NONE}", "${TextDecoration.UNDERLINE}", "${TextDecoration.LINE_THROUGH}", "${TextDecoration.UNDERLINE_LINE_THROUGH}".`); } + + this._nativeView.setPaintFlags(flags); } - get [textTransformProperty.native](): "none" | "capitalize" | "uppercase" | "lowercase" { - return "none"; + //TextTransform + get [textTransformProperty.native](): TextTransform { + return TextTransform.NONE; } - set [textTransformProperty.native](value: "none" | "capitalize" | "uppercase" | "lowercase") { + set [textTransformProperty.native](value: TextTransform) { this._setFormattedTextPropertyToNative(this.formattedText); } - get [whiteSpaceProperty.native](): "normal" | "nowrap" { - return "normal"; + private _originalTransformationMethod: android.text.method.TransformationMethod; + public _setFormattedTextPropertyToNative(value: FormattedString) { + // TODO: Check if there is an option to force call the transformation method without + // creating new native instance. + if (!this._nativeView) { + return; + } + + if (!this._originalTransformationMethod) { + this._originalTransformationMethod = this.android.getTransformationMethod(); + } + + let newText = value ? value._formattedText : null;//newText is of type android.text.SpannableStringBuilder + if (newText) { + this._nativeView.setTransformationMethod(new TextTransformation(this.text, value, this.style.textTransform)); + } + else { + if (this._originalTransformationMethod) { + this.android.setTransformationMethod(this._originalTransformationMethod); + this._originalTransformationMethod = null; + } + } + + this._nativeView.setText(newText); } - set [whiteSpaceProperty.native](value: "normal" | "nowrap") { + + //WhiteSpace + get [whiteSpaceProperty.native](): WhiteSpace { + return WhiteSpace.NORMAL; + } + set [whiteSpaceProperty.native](value: WhiteSpace) { let nativeView = this._nativeView; - let nowrap = value === "nowrap"; + switch(value){ + case WhiteSpace.NORMAL: + nativeView.setSingleLine(false); + nativeView.setEllipsize(null); + break; + case WhiteSpace.NO_WRAP: + nativeView.setSingleLine(true); + nativeView.setEllipsize(android.text.TextUtils.TruncateAt.END); + break; + default: + throw new Error(`Invalid whitespace value: ${value}. Valid values are: "${WhiteSpace.NORMAL}", "${WhiteSpace.NO_WRAP}".`); + } + + let nowrap = value === WhiteSpace.NO_WRAP; nativeView.setSingleLine(nowrap); nativeView.setEllipsize(nowrap ? android.text.TextUtils.TruncateAt.END : null); } @@ -208,4 +187,59 @@ export class TextBase extends TextBaseCommon { set [letterSpacingProperty.native](value: number) { org.nativescript.widgets.ViewHelper.setLetterspacing(this._nativeView, value); } +} + +@Interfaces([android.text.method.TransformationMethod]) +class TextTransformation extends android.text.method.ReplacementTransformationMethod { + constructor(public originalText: string, public formattedText: FormattedString, public textTransform: TextTransform) { + super(); + return global.__native(this); + } + + protected getOriginal(): native.Array { + return this.formattedText ? this.formattedText._formattedText : this.originalText; + } + + protected getReplacement(): native.Array { + let result: string = ""; + let textTransform = this.textTransform + if (this.formattedText) { + for (let i = 0, length = this.formattedText.spans.length; i < length; i++) { + let span = this.formattedText.spans.getItem(i); + result += getTransformedText(span.text, textTransform); + // span.text = formatString(span.text, this.textTransform); + } + } + else { + result = getTransformedText(this.originalText, textTransform); + } + + return result; + } +} + +function getCapitalizedString(str: string): string { + let words = str.split(" "); + let newWords = []; + for (let i = 0, length = words.length; i < length; i++) { + let word = words[i].toLowerCase(); + newWords.push(word.substr(0, 1).toUpperCase() + word.substring(1)); + } + + return newWords.join(" "); +} + +export function getTransformedText(text: string, textTransform: TextTransform): string { + switch (textTransform) { + case TextTransform.NONE: + return text; + case TextTransform.UPPERCASE: + return text.toUpperCase(); + case TextTransform.LOWERCASE: + return text.toLowerCase(); + case TextTransform.CAPITALIZE: + return getCapitalizedString(text); + default: + throw new Error(`Invalid text transform value: ${textTransform}. Valid values are: "${TextTransform.NONE}", "${TextTransform.CAPITALIZE}", "${TextTransform.UPPERCASE}, "${TextTransform.LOWERCASE}".`); + } } \ No newline at end of file diff --git a/tns-core-modules/ui/text-base/text-base.d.ts b/tns-core-modules/ui/text-base/text-base.d.ts index 758ee81f2..1dfdb29d5 100644 --- a/tns-core-modules/ui/text-base/text-base.d.ts +++ b/tns-core-modules/ui/text-base/text-base.d.ts @@ -32,7 +32,7 @@ /** * Gets or sets text-alignment style property. */ - textAlignment: "left" | "center" | "right"; + textAlignment: TextAlignment; /** * Gets or sets text decorations style property. @@ -42,12 +42,12 @@ /** * Gets or sets text transform style property. */ - textTransform: "none" | "capitalize" | "uppercase" | "lowercase"; + textTransform: TextTransform; /** * Gets or sets white space style property. */ - whiteSpace: "normal" | "nowrap"; + whiteSpace: WhiteSpace; /** * Called for every child element declared in xml. @@ -57,9 +57,6 @@ */ _addChildFromBuilder(name: string, value: any): void; - //@private - // _onTextPropertyChanged(value: string): void; - // _setFormattedTextPropertyToNative(value: any): void; /** * @private * Called when the text property is changed to request layout. @@ -68,6 +65,18 @@ //@endprivate } + export const textProperty: Property; + export const formattedTextProperty: Property; + + export type TextAlignment = "left" | "center" | "right"; + export namespace TextAlignment { + export const LEFT: "left"; + export const CENTER: "center"; + export const RIGHT: "right"; + export function isValid(value: any): boolean; + export function parse(value: string): TextAlignment; + } + export type TextDecoration = "none" | "underline" | "line-through" | "underline line-through"; export namespace TextDecoration { export const NONE: "none"; @@ -79,15 +88,29 @@ } export type TextTransform = "none" | "capitalize" | "uppercase" | "lowercase"; + export namespace TextTransform { + export const NONE: "none"; + export const CAPITALIZE: "capitalize"; + export const UPPERCASE: "uppercase"; + export const LOWERCASE: "lowercase"; + export function isValid(value: any): boolean; + export function parse(value: string): TextTransform; + } - export function getTransformedText(text: string, transform: TextTransform): string; + export type WhiteSpace = "normal" | "nowrap"; + export namespace WhiteSpace { + export const NORMAL: "normal"; + export const NO_WRAP: "nowrap"; + export function isValid(value: any): boolean; + export function parse(value: string): WhiteSpace; + } - export const textProperty: Property; - export const formattedTextProperty: Property; - - export const textAlignmentProperty: InheritedCssProperty; + export const textAlignmentProperty: InheritedCssProperty; export const textDecorationProperty: CssProperty; export const textTransformProperty: CssProperty; - export const whiteSpaceProperty: CssProperty; + export const whiteSpaceProperty: CssProperty; export const letterSpacingProperty: CssProperty; + + //Used by tab view + export function getTransformedText(text: string, textTransform: TextTransform): string; } \ No newline at end of file diff --git a/tns-core-modules/ui/text-base/text-base.ios.ts b/tns-core-modules/ui/text-base/text-base.ios.ts index a75413a56..a972a800a 100644 --- a/tns-core-modules/ui/text-base/text-base.ios.ts +++ b/tns-core-modules/ui/text-base/text-base.ios.ts @@ -1,155 +1,15 @@ import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, letterSpacingProperty, colorProperty, fontInternalProperty, Font, Color, FormattedString, - TextDecoration + TextDecoration, TextAlignment, TextTransform } from "./text-base-common"; export * from "./text-base-common"; - -function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString { - return NSString.stringWithString(source instanceof NSAttributedString && source.string || source); -} - -export function getTransformedText(text: string, transform: "none" | "capitalize" | "uppercase" | "lowercase"): string { - switch (transform) { - case "uppercase": - return NSStringFromNSAttributedString(text).uppercaseString; - - case "lowercase": - return NSStringFromNSAttributedString(text).lowercaseString; - - case "capitalize": - return NSStringFromNSAttributedString(text).capitalizedString; - - case "none": - default: - return text; - } -} - -function updateFormattedStringTextDecoration(formattedText: FormattedString, decoration: string, ): void { - - // TODO: Refactor this method so it doesn't modify FormattedString properties. - // Instead it should create NSAttributedString and apply it to the nativeView. - let textDecoration = decoration + ""; - if (textDecoration.indexOf("none") !== -1) { - formattedText.underline = NSUnderlineStyle.StyleNone; - formattedText.strikethrough = NSUnderlineStyle.StyleNone; - } - else { - if (textDecoration.indexOf("underline") !== -1) { - formattedText.underline = NSUnderlineStyle.StyleSingle; - } else { - formattedText.underline = NSUnderlineStyle.StyleNone; - } - - if (textDecoration.indexOf("line-through") !== -1) { - formattedText.strikethrough = NSUnderlineStyle.StyleSingle; - } else { - formattedText.strikethrough = NSUnderlineStyle.StyleNone; - } - } -} - -function updateFormattedStringTextTransformation(formattedText: FormattedString, transform: "none" | "capitalize" | "uppercase" | "lowercase"): void { - // TODO: Refactor this method so it doesn't modify Span properties. - // Instead it should create NSAttributedString and apply it to the nativeView. - for (let i = 0, length = formattedText.spans.length; i < length; i++) { - let span = formattedText.spans.getItem(i); - span.text = getTransformedText(span.text, transform); - } -} - -function setFormattedTextDecorationAndTransform(formattedText: FormattedString, nativeView: UITextField | UITextView | UILabel | UIButton, decoration: string, transform: "none" | "capitalize" | "uppercase" | "lowercase", letterSpacing: number) { - updateFormattedStringTextDecoration(formattedText, decoration); - updateFormattedStringTextTransformation(formattedText, transform); - - if (typeof letterSpacing === "number" && !isNaN(letterSpacing)) { - if (nativeView instanceof UIButton) { - let attrText = NSMutableAttributedString.alloc().initWithAttributedString(nativeView.attributedTitleForState(UIControlState.Normal)); - attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing * nativeView.font.pointSize, { location: 0, length: attrText.length }); - nativeView.setAttributedTitleForState(attrText, UIControlState.Normal); - } else { - let attrText = NSMutableAttributedString.alloc().initWithAttributedString(nativeView.attributedText); - attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing * nativeView.font.pointSize, { location: 0, length: attrText.length }); - nativeView.attributedText = attrText; - } - } -} - -function setTextDecorationAndTransform(text: string, nativeView: UITextField | UITextView | UILabel | UIButton, decoration: string, transform: "none" | "capitalize" | "uppercase" | "lowercase", letterSpacing: number, color: Color) { - let hasLetterSpacing = typeof letterSpacing === "number" && !isNaN(letterSpacing); - - let decorationValues = decoration + ""; - let dict = new Map(); - if (decorationValues.indexOf("none") === -1) { - if (decorationValues.indexOf("underline") !== -1) { - dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.StyleSingle); - } - - if (decorationValues.indexOf("line-through") !== -1) { - dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.StyleSingle); - } - } - - if (hasLetterSpacing) { - dict.set(NSKernAttributeName, letterSpacing * nativeView.font.pointSize); - } - - if (color) { - dict.set(NSForegroundColorAttributeName, color.ios); - } - - let source = getTransformedText(text, transform); - if (dict.size > 0) { - let result = NSMutableAttributedString.alloc().initWithString(source); - result.setAttributesRange(dict, { location: 0, length: source.length }); - if (nativeView instanceof UIButton) { - nativeView.setAttributedTitleForState(result, UIControlState.Normal); - } else { - nativeView.attributedText = result; - } - } else { - if (nativeView instanceof UIButton) { - // Clear attributedText or title won't be affected. - nativeView.setAttributedTitleForState(null, UIControlState.Normal); - nativeView.setTitleForState(source, UIControlState.Normal); - } else { - // Clear attributedText or text won't be affected. - nativeView.attributedText = undefined; - nativeView.text = source; - } - } -} - export class TextBase extends TextBaseCommon { public nativeView: UITextField | UITextView | UILabel | UIButton; - // public _onTextPropertyChanged(value: string) { - // var newValue = toUIString(value); - // this.ios.text = newValue; - - // //RemoveThisDoubleCall - // // this.style._updateTextDecoration(); - // // this.style._updateTextTransform(); - - // this._requestLayoutOnTextChanged(); - // } - - public _setFormattedTextPropertyToNative(value: FormattedString) { - let newText = value ? value._formattedText : null; - let nativeView = this.nativeView; - if (nativeView instanceof UIButton) { - nativeView.setAttributedTitleForState(newText, UIControlState.Normal); - } else { - nativeView.attributedText = newText; - } - //RemoveThisDoubleCall - // this.style._updateTextDecoration(); - // this.style._updateTextTransform(); - } - + //Text get [textProperty.native](): string { let nativeView = this.nativeView; if (nativeView instanceof UIButton) { @@ -176,6 +36,7 @@ export class TextBase extends TextBaseCommon { this._requestLayoutOnTextChanged(); } + //FormattedText get [formattedTextProperty.native](): FormattedString { return null; } @@ -183,6 +44,17 @@ export class TextBase extends TextBaseCommon { this._setFormattedTextPropertyToNative(value); } + public _setFormattedTextPropertyToNative(value: FormattedString) { + let newText = value ? value._formattedText : null; + let nativeView = this.nativeView; + if (nativeView instanceof UIButton) { + nativeView.setAttributedTitleForState(newText, UIControlState.Normal); + } else { + nativeView.attributedText = newText; + } + } + + //Color get [colorProperty.native](): UIColor { let nativeView = this.nativeView; if (nativeView instanceof UIButton) { @@ -200,6 +72,7 @@ export class TextBase extends TextBaseCommon { } } + //FontInternal get [fontInternalProperty.native](): UIFont { let nativeView = this.nativeView; nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView; @@ -212,41 +85,43 @@ export class TextBase extends TextBaseCommon { nativeView.font = font; } - get [textAlignmentProperty.native](): string { + //TextAlignment + get [textAlignmentProperty.native](): TextAlignment { let nativeView = this.nativeView; nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView; switch (nativeView.textAlignment) { case NSTextAlignment.Left: - return "left"; - + return TextAlignment.LEFT; case NSTextAlignment.Center: - return "center"; - + return TextAlignment.CENTER; case NSTextAlignment.Right: - return "right"; + return TextAlignment.RIGHT; + default: + throw new Error(`Unsupported NSTextAlignment: ${nativeView.textAlignment}. Currently supported values are NSTextAlignment.Left, NSTextAlignment.Center, and NSTextAlignment.Right.`); } } - set [textAlignmentProperty.native](value: string) { + set [textAlignmentProperty.native](value: TextAlignment) { let nativeView = this.nativeView; nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView; // NOTE: if Button textAlignment is not enough - set also btn.contentHorizontalAlignment switch (value) { - case "left": + case TextAlignment.LEFT: nativeView.textAlignment = NSTextAlignment.Left; break; - case "center": + case TextAlignment.CENTER: nativeView.textAlignment = NSTextAlignment.Center; break; - case "right": + case TextAlignment.RIGHT: nativeView.textAlignment = NSTextAlignment.Right; break; default: - break; + throw new Error(`Invalid text alignment value: ${value}. Valid values are: "${TextAlignment.LEFT}", "${TextAlignment.CENTER}", "${TextAlignment.RIGHT}".`); } } + //TextDecoration get [textDecorationProperty.native](): TextDecoration { - return "none"; + return TextDecoration.NONE; } set [textDecorationProperty.native](value: TextDecoration) { if (this.formattedText) { @@ -256,10 +131,11 @@ export class TextBase extends TextBaseCommon { } } - get [textTransformProperty.native](): string { - return "none"; + //TextTransform + get [textTransformProperty.native](): TextTransform { + return TextTransform.NONE; } - set [textTransformProperty.native](value: "none" | "capitalize" | "uppercase" | "lowercase") { + set [textTransformProperty.native](value: TextTransform) { if (this.formattedText) { setFormattedTextDecorationAndTransform(this.formattedText, this.nativeView, this.style.textDecoration, value, this.style.letterSpacing); } else { @@ -267,21 +143,7 @@ export class TextBase extends TextBaseCommon { } } - // get [whiteSpaceProperty.native](): string { - // return WhiteSpace.normal; - // } - // set [whiteSpaceProperty.native](value: string) { - // let nativeView = this.nativeView; - // if (value === WhiteSpace.normal) { - // nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping; - // nativeView.numberOfLines = 0; - // } - // else { - // nativeView.lineBreakMode = NSLineBreakMode.ByTruncatingTail; - // nativeView.numberOfLines = 1; - // } - // } - + // LetterSpacing get [letterSpacingProperty.native](): number { return Number.NaN; } @@ -292,20 +154,126 @@ export class TextBase extends TextBaseCommon { setTextDecorationAndTransform(this.text, this.nativeView, this.style.textDecoration, this.style.textTransform, value, this.style.color); } } +} - // private _settingFormattedTextPropertyToNative = false; - // public _onStylePropertyChanged(property: dependencyObservable.Property): void { - // if (this._settingFormattedTextPropertyToNative) { - // // Guard against stack-overflow. - // return; - // } +export function getTransformedText(text: string, textTransform: TextTransform): string { + switch (textTransform) { + case TextTransform.NONE: + return text; + case TextTransform.UPPERCASE: + return NSStringFromNSAttributedString(text).uppercaseString; + case TextTransform.LOWERCASE: + return NSStringFromNSAttributedString(text).lowercaseString; + case TextTransform.CAPITALIZE: + return NSStringFromNSAttributedString(text).capitalizedString; + default: + throw new Error(`Invalid text transform value: ${textTransform}. Valid values are: "${TextTransform.NONE}", "${TextTransform.CAPITALIZE}", "${TextTransform.UPPERCASE}, "${TextTransform.LOWERCASE}".`); + } +} - // if (this.formattedText) { - // // Re-apply the formatted text to override style changes if needed. - // // https://github.com/NativeScript/NativeScript/issues/1078 - // this._settingFormattedTextPropertyToNative = true; - // this._setFormattedTextPropertyToNative(this.formattedText); - // this._settingFormattedTextPropertyToNative = false; - // } - // } -} \ No newline at end of file + +function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString { + return NSString.stringWithString(source instanceof NSAttributedString && source.string || source); +} + +function updateFormattedStringTextDecoration(formattedText: FormattedString, textDecoration: TextDecoration): void { + // TODO: Refactor this method so it doesn't modify FormattedString properties. + // Instead it should create NSAttributedString and apply it to the nativeView. + switch(textDecoration) { + case TextDecoration.NONE: + formattedText.underline = NSUnderlineStyle.StyleNone; + formattedText.strikethrough = NSUnderlineStyle.StyleNone; + break; + case TextDecoration.UNDERLINE: + formattedText.underline = NSUnderlineStyle.StyleSingle; + formattedText.strikethrough = NSUnderlineStyle.StyleNone; + break; + case TextDecoration.LINE_THROUGH: + formattedText.underline = NSUnderlineStyle.StyleNone; + formattedText.strikethrough = NSUnderlineStyle.StyleSingle; + break; + case TextDecoration.UNDERLINE_LINE_THROUGH: + formattedText.underline = NSUnderlineStyle.StyleSingle; + formattedText.strikethrough = NSUnderlineStyle.StyleSingle; + break; + default: + throw new Error(`Invalid text decoration value: ${textDecoration}. Valid values are: "${TextDecoration.NONE}", "${TextDecoration.UNDERLINE}", "${TextDecoration.LINE_THROUGH}", "${TextDecoration.UNDERLINE_LINE_THROUGH}".`); + } +} + +function updateFormattedStringTextTransformation(formattedText: FormattedString, textTransform: TextTransform): void { + // TODO: Refactor this method so it doesn't modify Span properties. + // Instead it should create NSAttributedString and apply it to the nativeView. + for (let i = 0, length = formattedText.spans.length; i < length; i++) { + let span = formattedText.spans.getItem(i); + span.text = getTransformedText(span.text, textTransform); + } +} + +function setFormattedTextDecorationAndTransform(formattedText: FormattedString, nativeView: UITextField | UITextView | UILabel | UIButton, textDecoration: TextDecoration, textTransform: TextTransform, letterSpacing: number) { + updateFormattedStringTextDecoration(formattedText, textDecoration); + updateFormattedStringTextTransformation(formattedText, textTransform); + + if (typeof letterSpacing === "number" && !isNaN(letterSpacing)) { + if (nativeView instanceof UIButton) { + let attrText = NSMutableAttributedString.alloc().initWithAttributedString(nativeView.attributedTitleForState(UIControlState.Normal)); + attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing * nativeView.font.pointSize, { location: 0, length: attrText.length }); + nativeView.setAttributedTitleForState(attrText, UIControlState.Normal); + } else { + let attrText = NSMutableAttributedString.alloc().initWithAttributedString(nativeView.attributedText); + attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing * nativeView.font.pointSize, { location: 0, length: attrText.length }); + nativeView.attributedText = attrText; + } + } +} + +function setTextDecorationAndTransform(text: string, nativeView: UITextField | UITextView | UILabel | UIButton, textDecoration: TextDecoration, textTransform: TextTransform, letterSpacing: number, color: Color) { + let hasLetterSpacing = typeof letterSpacing === "number" && !isNaN(letterSpacing); + + let dict = new Map(); + switch(textDecoration) { + case TextDecoration.NONE: + break; + case TextDecoration.UNDERLINE: + dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.StyleSingle); + break; + case TextDecoration.LINE_THROUGH: + dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.StyleSingle); + break; + case TextDecoration.UNDERLINE_LINE_THROUGH: + dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.StyleSingle); + dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.StyleSingle); + break; + default: + throw new Error(`Invalid text decoration value: ${textDecoration}. Valid values are: "${TextDecoration.NONE}", "${TextDecoration.UNDERLINE}", "${TextDecoration.LINE_THROUGH}", "${TextDecoration.UNDERLINE_LINE_THROUGH}".`); + } + + if (hasLetterSpacing) { + dict.set(NSKernAttributeName, letterSpacing * nativeView.font.pointSize); + } + + if (color) { + dict.set(NSForegroundColorAttributeName, color.ios); + } + + let source = getTransformedText(text, textTransform); + if (dict.size > 0) { + let result = NSMutableAttributedString.alloc().initWithString(source); + result.setAttributesRange(dict, { location: 0, length: source.length }); + if (nativeView instanceof UIButton) { + nativeView.setAttributedTitleForState(result, UIControlState.Normal); + } else { + nativeView.attributedText = result; + } + } else { + if (nativeView instanceof UIButton) { + // Clear attributedText or title won't be affected. + nativeView.setAttributedTitleForState(null, UIControlState.Normal); + nativeView.setTitleForState(source, UIControlState.Normal); + } else { + // Clear attributedText or text won't be affected. + nativeView.attributedText = undefined; + nativeView.text = source; + } + } +} diff --git a/tns-core-modules/utils/utils.android.ts b/tns-core-modules/utils/utils.android.ts index c3a75a483..c9f73467e 100644 --- a/tns-core-modules/utils/utils.android.ts +++ b/tns-core-modules/utils/utils.android.ts @@ -5,95 +5,6 @@ export * from "./utils-common"; // We are using "ad" here to avoid namespace collision with the global android object export module ad { - - export function setTextDecoration(view: android.widget.TextView, value: string) { - var flags = 0; - - var values = (value + "").split(" "); - - if (values.indexOf("underline") !== -1) { - flags = flags | android.graphics.Paint.UNDERLINE_TEXT_FLAG; - } - - if (values.indexOf("line-through") !== -1) { - flags = flags | android.graphics.Paint.STRIKE_THRU_TEXT_FLAG; - } - - if (values.indexOf("none") === -1) { - view.setPaintFlags(flags); - } else { - view.setPaintFlags(0); - } - } - - export function setTextTransform(v, value: string) { - let view = v._nativeView; - let str = view.getText() + ""; - let result = getTransformedString(value, view, str); - - if (v.formattedText) { - for (var i = 0; i < v.formattedText.spans.length; i++) { - var span = v.formattedText.spans.getItem(i); - span.text = getTransformedString(value, view, span.text); - } - } else { - view.setText(result); - } - } - - export function getTransformedString(textTransform: string, view, stringToTransform: string): string { - let result: string; - - switch (textTransform) { - case "uppercase": - view.setTransformationMethod(null); - result = stringToTransform.toUpperCase(); - break; - - case "lowercase": - view.setTransformationMethod(null); - result = stringToTransform.toLowerCase(); - break; - - case "capitalize": - view.setTransformationMethod(null); - result = getCapitalizedString(stringToTransform); - break; - - case "none": - default: - result = view["originalString"] || stringToTransform; - if (view["transformationMethod"]) { - view.setTransformationMethod(view["transformationMethod"]); - } - break; - } - - if (!view["originalString"]) { - view["originalString"] = stringToTransform; - view["transformationMethod"] = view.getTransformationMethod(); - } - - return result; - } - - function getCapitalizedString(str: string): string { - var words = str.split(" "); - var newWords = []; - for (let i = 0; i < words.length; i++) { - let word = words[i].toLowerCase(); - newWords.push(word.substr(0, 1).toUpperCase() + word.substring(1)); - } - - return newWords.join(" "); - } - - export function setWhiteSpace(view: android.widget.TextView, value: string) { - let nowrap = value === "nowrap"; - view.setSingleLine(nowrap); - view.setEllipsize(nowrap ? android.text.TextUtils.TruncateAt.END : null); - } - let nativeApp: android.app.Application; declare var com; export function getApplication() { diff --git a/tns-core-modules/utils/utils.d.ts b/tns-core-modules/utils/utils.d.ts index bccc2768d..abe016546 100644 --- a/tns-core-modules/utils/utils.d.ts +++ b/tns-core-modules/utils/utils.d.ts @@ -78,11 +78,6 @@ * Module with android specific utilities. */ module ad { - export function setTextTransform(view, value: string); - export function setWhiteSpace(view, value: string); - export function setTextDecoration(view, value: string); - export function getTransformedString(textTransform: string, view, stringToTransform: string): string; - /** * Gets the native Android application instance. */ @@ -163,9 +158,6 @@ * Example: getter(NSRunLoop, NSRunLoop.currentRunLoop).runUntilDate(NSDate.dateWithTimeIntervalSinceNow(waitTime)); */ export function getter(_this: any, propertyValue: T | {(): T}): T; - export function getTransformedText(view, source: string, transform: string): string; - export function setWhiteSpace(view, value: string, parentView?: any); - export function setTextAlignment(view, value: string); // Common properties between UILabel, UITextView and UITextField export interface TextUIView { diff --git a/tns-core-modules/utils/utils.ios.ts b/tns-core-modules/utils/utils.ios.ts index ef56be9db..07d95b591 100644 --- a/tns-core-modules/utils/utils.ios.ts +++ b/tns-core-modules/utils/utils.ios.ts @@ -34,22 +34,6 @@ export module layout { } export module ios { - export function setTextAlignment(view: dts.ios.TextUIView, value: string) { - switch (value) { - case "left": - view.textAlignment = NSTextAlignment.Left; - break; - case "center": - view.textAlignment = NSTextAlignment.Center; - break; - case "right": - view.textAlignment = NSTextAlignment.Right; - break; - default: - break; - } - } - export function getter(_this: any, property: T | { (): T }): T { if (typeof property === "function") { return (<{ (): T }>property).call(_this); @@ -58,50 +42,6 @@ export module ios { } } - export function getTransformedText(view, source: string, transform: string): string { - let result = source; - - switch (transform) { - case "uppercase": - result = NSStringFromNSAttributedString(source).uppercaseString; - break; - - case "lowercase": - result = NSStringFromNSAttributedString(source).lowercaseString; - break; - - case "capitalize": - result = NSStringFromNSAttributedString(source).capitalizedString; - break; - - case "none": - default: - result = view.text; - break; - } - - return result; - } - - function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString { - return NSString.stringWithString(source instanceof NSAttributedString && source.string || source); - } - - export function setWhiteSpace(view: dts.ios.TextUIView, value: string, parentView?: UIView) { - if (value === "normal") { - view.lineBreakMode = NSLineBreakMode.ByWordWrapping; - view.numberOfLines = 0; - } - else { - if (parentView) { - view.lineBreakMode = NSLineBreakMode.ByTruncatingMiddle; - } else { - view.lineBreakMode = NSLineBreakMode.ByTruncatingTail; - } - view.numberOfLines = 1; - } - } - export module collections { export function jsArrayToNSArray(str: string[]): NSArray { return NSArray.arrayWithArray(str);