mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
whiteSpace default value change to undefined (#3782)
TKUnit default message change to empty string isSet method is now instance method of Property classes fix detaching from parent bindingContext - were using oldParent.parent instead of parent editable-text-base.android - onTextChanged implementation commented. Does nothing. frame - onCreateView wrapped in try/catch and shows label with exception message if any text-base.android - should support reset of nativeView. TransformationMethod won’t be set if TextField is secure Change some types to their string couterparts TextField.android won’t support multilines anymore in order to work as iOS In android when page is removed from native backstack we won’t call tearDownUI again a second time
This commit is contained in:
@ -214,7 +214,7 @@ export function assertNotEqual(actual: any, expected: any, message?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEqual<T extends { equals?(arg: T): boolean }>(actual: T, expected: T, message?: string) {
|
||||
export function assertEqual<T extends { equals?(arg: T): boolean }>(actual: T, expected: T, message: string = '') {
|
||||
if (!types.isNullOrUndefined(actual)
|
||||
&& !types.isNullOrUndefined(expected)
|
||||
&& types.getClass(actual) === types.getClass(expected)
|
||||
|
@ -508,18 +508,17 @@ export function test_TwoElementsBindingToSameBindingContext() {
|
||||
|
||||
export function test_BindingContext_NavigatingForwardAndBack() {
|
||||
const expectedValue = "Tralala";
|
||||
const moduleName = __dirname.substr(fs.knownFolders.currentApp().path.length);
|
||||
const testFunc = function (page: Page) {
|
||||
const innerTestFunc = function (childPage: Page) {
|
||||
const testTextField: TextField = <TextField>(childPage.getViewById("testTextField"));
|
||||
testTextField.text = expectedValue;
|
||||
};
|
||||
const moduleName = __dirname.substr(fs.knownFolders.currentApp().path.length);
|
||||
helper.navigateToModuleAndRunTest(("." + moduleName + "/bindingContext_testPage2"), page.bindingContext, innerTestFunc);
|
||||
const testLabel: Label = <Label>(page.getViewById("testLabel"));
|
||||
TKUnit.assertEqual(testLabel.text, expectedValue);
|
||||
};
|
||||
|
||||
const moduleName = __dirname.substr(fs.knownFolders.currentApp().path.length);
|
||||
helper.navigateToModuleAndRunTest(("." + moduleName + "/bindingContext_testPage1"), null, testFunc);
|
||||
};
|
||||
|
||||
|
@ -5,37 +5,12 @@ import { Label } from "tns-core-modules/ui/label";
|
||||
import { TextField } from "tns-core-modules/ui/text-field";
|
||||
import { TextView } from "tns-core-modules/ui/text-view";
|
||||
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
|
||||
import { Page } from "tns-core-modules/ui/page";
|
||||
import { Color } from "tns-core-modules/color";
|
||||
import { isAndroid, isIOS } from "tns-core-modules/platform";
|
||||
import { View } from "tns-core-modules/ui/core/view";
|
||||
import { Length, PercentLength } from "tns-core-modules/ui/core/view";
|
||||
import * as fontModule from "tns-core-modules/ui/styling/font";
|
||||
|
||||
let testBtn: Button;
|
||||
let testPage: Page;
|
||||
|
||||
export function setUpModule() {
|
||||
const pageFactory = function () {
|
||||
testPage = new Page();
|
||||
testBtn = new Button();
|
||||
testBtn.text = "test";
|
||||
testBtn.id = "testBtn";
|
||||
testPage.content = testBtn;
|
||||
return testPage;
|
||||
};
|
||||
helper.navigate(pageFactory);
|
||||
}
|
||||
|
||||
export function tearDownModule() {
|
||||
testBtn = null;
|
||||
testPage = null;
|
||||
}
|
||||
|
||||
export function tearDown() {
|
||||
testPage.css = "";
|
||||
}
|
||||
|
||||
export function test_setting_textDecoration_property_from_CSS_is_applied_to_Style() {
|
||||
test_property_from_CSS_is_applied_to_style("textDecoration", "text-decoration", "underline");
|
||||
}
|
||||
@ -214,14 +189,18 @@ function test_property_from_CSS_is_applied_to_style(propName: string, cssName: s
|
||||
cssValue = value + "";
|
||||
}
|
||||
|
||||
testPage.css = "#testBtn { " + cssName + ": " + cssValue + " }";
|
||||
const btn = new Button();
|
||||
btn.id = "testBtn";
|
||||
|
||||
const page = helper.getCurrentPage();
|
||||
page.css = "#testBtn { " + cssName + ": " + cssValue + " }";
|
||||
page.content = btn;
|
||||
|
||||
if (useDeepEquals) {
|
||||
TKUnit.assertDeepEqual(testBtn.style[propName], value);
|
||||
TKUnit.assertDeepEqual(btn.style[propName], value);
|
||||
} else {
|
||||
TKUnit.assertEqual(testBtn.style[propName], value, "Setting property " + propName + " with CSS name " + cssName);
|
||||
TKUnit.assertEqual(btn.style[propName], value, "Setting property " + propName + " with CSS name " + cssName);
|
||||
}
|
||||
testPage.css = "";
|
||||
}
|
||||
|
||||
export function test_width_property_is_synced_in_style_and_view() {
|
||||
|
@ -38,7 +38,6 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti
|
||||
this.style.fontSize = value;
|
||||
}
|
||||
|
||||
// Italic
|
||||
get fontStyle(): FontStyle {
|
||||
return this.style.fontStyle;
|
||||
}
|
||||
@ -46,7 +45,6 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti
|
||||
this.style.fontStyle = value;
|
||||
}
|
||||
|
||||
// Bold
|
||||
get fontWeight(): FontWeight {
|
||||
return this.style.fontWeight;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
ButtonBase, PseudoClassHandler,
|
||||
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length, zIndexProperty
|
||||
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
|
||||
Length, zIndexProperty
|
||||
} from "./button-common";
|
||||
|
||||
import { TouchGestureEventData, GestureTypes, TouchAction } from "../gestures";
|
||||
@ -35,16 +36,13 @@ function initializeClickListener(): void {
|
||||
|
||||
export class Button extends ButtonBase {
|
||||
_button: android.widget.Button;
|
||||
|
||||
private _highlightedHandler: (args: TouchGestureEventData) => void;
|
||||
|
||||
get android(): android.widget.Button {
|
||||
return this._button;
|
||||
}
|
||||
|
||||
public _createNativeView() {
|
||||
initializeClickListener();
|
||||
const button = this._button = new android.widget.Button(this._context);
|
||||
this._button.setOnClickListener(new ClickListener(this));
|
||||
button.setOnClickListener(new ClickListener(this));
|
||||
return button;
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,6 @@ import { Style } from "../../styling/style";
|
||||
|
||||
export { Style };
|
||||
|
||||
//@private
|
||||
export function _isSet(cssProperty: CssProperty<any, any>, instance: Style): boolean;
|
||||
//@endprivate
|
||||
|
||||
/**
|
||||
* Value specifing that Property should be set to its initial value.
|
||||
*/
|
||||
@ -29,6 +25,13 @@ export interface CssPropertyOptions<T extends Style, U> extends PropertyOptions<
|
||||
readonly cssName: string;
|
||||
}
|
||||
|
||||
export interface ShorthandPropertyOptions<P> {
|
||||
readonly name: string,
|
||||
readonly cssName: string;
|
||||
readonly converter: (value: string | P) => [CssProperty<any, any>, any][],
|
||||
readonly getter: (this: Style) => string | P
|
||||
}
|
||||
|
||||
export interface CssAnimationPropertyOptions<T, U> {
|
||||
readonly name: string;
|
||||
readonly cssName?: string;
|
||||
@ -38,21 +41,6 @@ export interface CssAnimationPropertyOptions<T, U> {
|
||||
readonly valueConverter?: (value: string) => U;
|
||||
}
|
||||
|
||||
export class CssAnimationProperty<T extends Style, U> {
|
||||
constructor(options: CssAnimationPropertyOptions<T, U>);
|
||||
|
||||
public readonly name: string;
|
||||
public readonly cssName: string;
|
||||
public readonly native: symbol;
|
||||
|
||||
readonly keyframe: string;
|
||||
|
||||
public register(cls: { prototype: T }): void;
|
||||
|
||||
public _valueConverter?: (value: string) => any;
|
||||
public static _getByCssName(name: string): CssAnimationProperty<any, any>;
|
||||
}
|
||||
|
||||
export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<U> {
|
||||
constructor(options: PropertyOptions<T, U>);
|
||||
|
||||
@ -60,6 +48,7 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
||||
public readonly defaultValue: U;
|
||||
public register(cls: { prototype: T }): void;
|
||||
public nativeValueChange(T, U): void;
|
||||
public isSet(instance: T): boolean;
|
||||
}
|
||||
|
||||
export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> implements TypedPropertyDescriptor<U> {
|
||||
@ -80,28 +69,39 @@ export class CssProperty<T extends Style, U> {
|
||||
public readonly cssName: string;
|
||||
public readonly defaultValue: U;
|
||||
public register(cls: { prototype: T }): void;
|
||||
public isSet(instance: T): boolean;
|
||||
}
|
||||
|
||||
export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U> {
|
||||
constructor(options: CssPropertyOptions<T, U>);
|
||||
}
|
||||
|
||||
export interface ShorthandPropertyOptions<P> {
|
||||
readonly name: string,
|
||||
readonly cssName: string;
|
||||
readonly converter: (value: string | P) => [CssProperty<any, any>, any][],
|
||||
readonly getter: (this: Style) => string | P
|
||||
}
|
||||
|
||||
export class ShorthandProperty<T extends Style, P> {
|
||||
constructor(options: ShorthandPropertyOptions<P>);
|
||||
|
||||
public readonly native: symbol;
|
||||
public readonly name: string;
|
||||
public readonly cssName: string;
|
||||
public readonly native: symbol;
|
||||
|
||||
public register(cls: { prototype: T }): void;
|
||||
}
|
||||
|
||||
export class CssAnimationProperty<T extends Style, U> {
|
||||
constructor(options: CssAnimationPropertyOptions<T, U>);
|
||||
|
||||
public readonly name: string;
|
||||
public readonly cssName: string;
|
||||
public readonly native: symbol;
|
||||
|
||||
readonly keyframe: string;
|
||||
|
||||
public register(cls: { prototype: T }): void;
|
||||
public isSet(instance: T): boolean;
|
||||
|
||||
public _valueConverter?: (value: string) => any;
|
||||
public static _getByCssName(name: string): CssAnimationProperty<any, any>;
|
||||
}
|
||||
|
||||
export function initNativeView(view: ViewBase): void;
|
||||
export function resetNativeView(view: ViewBase): void;
|
||||
export function resetCSSProperties(style: Style): void;
|
||||
@ -110,4 +110,4 @@ export function propagateInheritableCssProperties(style: Style): void;
|
||||
export function clearInheritedProperties(view: ViewBase): void;
|
||||
|
||||
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
|
||||
export function makeParser<T>(isValid: (value: any) => boolean): (value: any) => T;
|
||||
export function makeParser<T>(isValid: (value: any) => boolean): (value: any) => T;
|
@ -26,10 +26,6 @@ function print(map) {
|
||||
}
|
||||
}
|
||||
|
||||
export function _isSet(cssProperty: CssProperty<any, any>, instance: Style): boolean {
|
||||
return cssProperty.sourceKey in instance;
|
||||
}
|
||||
|
||||
export function _printUnregisteredProperties(): void {
|
||||
print(symbolPropertyMap);
|
||||
print(cssSymbolPropertyMap);
|
||||
@ -177,6 +173,10 @@ export class Property<T extends ViewBase, U> implements TypedPropertyDescriptor<
|
||||
this.registered = true;
|
||||
Object.defineProperty(cls.prototype, this.name, this);
|
||||
}
|
||||
|
||||
public isSet(instance: T): boolean {
|
||||
return this.key in instance;
|
||||
}
|
||||
}
|
||||
|
||||
export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> implements definitions.CoercibleProperty<T, U> {
|
||||
@ -544,6 +544,10 @@ export class CssProperty<T extends Style, U> implements definitions.CssProperty<
|
||||
Object.defineProperty(cls.prototype, this.cssLocalName, this.localValueDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public isSet(instance: T): boolean {
|
||||
return this.key in instance;
|
||||
}
|
||||
}
|
||||
|
||||
export class CssAnimationProperty<T extends Style, U> {
|
||||
@ -555,6 +559,7 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
|
||||
public readonly keyframe: string;
|
||||
public readonly defaultValueKey: symbol;
|
||||
public readonly computedValueKey: symbol;
|
||||
|
||||
private static properties: { [cssName: string]: CssAnimationProperty<any, any> } = {};
|
||||
|
||||
@ -582,6 +587,7 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
const styleValue = Symbol(propertyName);
|
||||
const keyframeValue = Symbol(keyframeName);
|
||||
const computedValue = Symbol("computed-value:" + propertyName);
|
||||
this.computedValueKey = computedValue;
|
||||
const computedSource = Symbol("computed-source:" + propertyName);
|
||||
|
||||
const native = this.native = Symbol("native:" + propertyName);
|
||||
@ -664,6 +670,10 @@ export class CssAnimationProperty<T extends Style, U> {
|
||||
public static _getByCssName(name: string): CssAnimationProperty<any, any> {
|
||||
return this.properties[name];
|
||||
}
|
||||
|
||||
public isSet(instance: T): boolean {
|
||||
return instance[this.computedValueKey] !== unsetValue;
|
||||
}
|
||||
}
|
||||
|
||||
export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U> implements definitions.InheritedCssProperty<T, U> {
|
||||
|
@ -6,7 +6,7 @@ import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../la
|
||||
import { KeyframeAnimation } from "../../animation/keyframe-animation";
|
||||
|
||||
// Types.
|
||||
import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, resetCSSProperties, initNativeView, resetNativeView, _isSet } from "../properties";
|
||||
import { Property, InheritedProperty, Style, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, resetCSSProperties, initNativeView, resetNativeView } from "../properties";
|
||||
import { Binding, BindingOptions, Observable, WrappedValue, PropertyChangeData, traceEnabled, traceWrite, traceCategories, traceNotifyEvent } from "../bindable";
|
||||
import { isIOS, isAndroid } from "../../../platform";
|
||||
import { layout } from "../../../utils/utils";
|
||||
@ -97,6 +97,8 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
|
||||
public recycleNativeView: boolean;
|
||||
|
||||
private _iosView: Object;
|
||||
private _androidView: Object;
|
||||
private _style: Style;
|
||||
private _isLoaded: boolean;
|
||||
private _registeredAnimations: Array<KeyframeAnimation>;
|
||||
@ -180,11 +182,11 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
}
|
||||
|
||||
get android(): any {
|
||||
return undefined;
|
||||
return this._androidView;
|
||||
}
|
||||
|
||||
get ios(): any {
|
||||
return undefined;
|
||||
return this._iosView;
|
||||
}
|
||||
|
||||
get isLoaded(): boolean {
|
||||
@ -581,8 +583,7 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
traceNotifyEvent(this, "_onContextChanged");
|
||||
|
||||
if (isAndroid) {
|
||||
const native: any = this._createNativeView();
|
||||
const nativeView: android.view.View = this.nativeView = native;
|
||||
const nativeView = this._androidView = this.nativeView = <android.view.View>this._createNativeView();
|
||||
if (nativeView) {
|
||||
let result: android.graphics.Rect = (<any>nativeView).defaultPaddings;
|
||||
if (result === undefined) {
|
||||
@ -596,23 +597,23 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
this._defaultPaddingLeft = result.left;
|
||||
|
||||
const style = this.style;
|
||||
if (!_isSet(paddingTopProperty, style)) {
|
||||
if (!paddingTopProperty.isSet(style)) {
|
||||
this.effectivePaddingTop = this._defaultPaddingTop;
|
||||
}
|
||||
if (!_isSet(paddingRightProperty, style)) {
|
||||
if (!paddingRightProperty.isSet(style)) {
|
||||
this.effectivePaddingRight = this._defaultPaddingRight;
|
||||
}
|
||||
if (!_isSet(paddingBottomProperty, style)) {
|
||||
if (!paddingBottomProperty.isSet(style)) {
|
||||
this.effectivePaddingBottom = this._defaultPaddingBottom;
|
||||
}
|
||||
if (!_isSet(paddingLeftProperty, style)) {
|
||||
if (!paddingLeftProperty.isSet(style)) {
|
||||
this.effectivePaddingLeft = this._defaultPaddingLeft;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: Implement _createNativeView for iOS
|
||||
this._createNativeView();
|
||||
this.nativeView = (<any>this)._nativeView;
|
||||
this.nativeView = this._iosView = (<any>this)._nativeView;
|
||||
}
|
||||
|
||||
this._initNativeView();
|
||||
@ -636,6 +637,8 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`${this}._tearDownUI(${force})`, traceCategories.VisualTreeEvents);
|
||||
}
|
||||
|
||||
this._resetNativeView();
|
||||
|
||||
this.eachChild((child) => {
|
||||
child._tearDownUI(force);
|
||||
@ -646,8 +649,6 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
this.parent._removeViewFromNativeVisualTree(this);
|
||||
}
|
||||
|
||||
this._resetNativeView();
|
||||
|
||||
this._disposeNativeView();
|
||||
|
||||
this._context = null;
|
||||
@ -713,7 +714,7 @@ export class ViewBase extends Observable implements ViewBaseDefinition {
|
||||
if (oldParent) {
|
||||
clearInheritedProperties(this);
|
||||
if (this.bindingContextBoundToParentBindingContextChanged) {
|
||||
oldParent.parent.off("bindingContextChange", this.bindingContextChanged, this);
|
||||
oldParent.off("bindingContextChange", this.bindingContextChanged, this);
|
||||
}
|
||||
} else if (this.shouldAddHandlerToParentBindingContextChanged) {
|
||||
const parent = this.parent;
|
||||
|
@ -39,11 +39,11 @@ function initializeEditTextListeners(): void {
|
||||
}
|
||||
|
||||
public onTextChanged(text: string, start: number, before: number, count: number) {
|
||||
const owner = this.owner;
|
||||
let selectionStart = owner.android.getSelectionStart();
|
||||
owner.android.removeTextChangedListener(owner._editTextListeners);
|
||||
owner.android.addTextChangedListener(owner._editTextListeners);
|
||||
owner.android.setSelection(selectionStart);
|
||||
// const owner = this.owner;
|
||||
// let selectionStart = owner.android.getSelectionStart();
|
||||
// owner.android.removeTextChangedListener(owner._editTextListeners);
|
||||
// owner.android.addTextChangedListener(owner._editTextListeners);
|
||||
// owner.android.setSelection(selectionStart);
|
||||
}
|
||||
|
||||
public afterTextChanged(editable: android.text.IEditable) {
|
||||
|
@ -708,12 +708,17 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
}
|
||||
const entry = this.entry;
|
||||
const page = entry.resolvedPage;
|
||||
if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) {
|
||||
fragment.getFragmentManager().beginTransaction().hide(fragment).commit();
|
||||
this.frame._addView(page);
|
||||
}
|
||||
else {
|
||||
onFragmentShown(fragment);
|
||||
try {
|
||||
if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) {
|
||||
fragment.getFragmentManager().beginTransaction().hide(fragment).commit();
|
||||
this.frame._addView(page);
|
||||
} else {
|
||||
onFragmentShown(fragment);
|
||||
}
|
||||
} catch (ex) {
|
||||
const label = new android.widget.TextView(container.getContext());
|
||||
label.setText(ex.message + ", " + ex.stackTrace);
|
||||
return label;
|
||||
}
|
||||
|
||||
return page._nativeView;
|
||||
|
@ -1,6 +1,10 @@
|
||||
// Definitions.
|
||||
import { TextBase as TextBaseDefinition } from ".";
|
||||
import { View, ViewBase, Property, CssProperty, InheritedCssProperty, Style, isIOS, Observable, makeValidator, makeParser, Length } from "../core/view";
|
||||
import { FontWeight } from "../styling/font";
|
||||
import { PropertyChangeData } from "../../data/observable";
|
||||
|
||||
// Types.
|
||||
import { View, ViewBase, Property, CssProperty, InheritedCssProperty, Style, isIOS, Observable, makeValidator, makeParser, Length } from "../core/view";
|
||||
import { FormattedString, Span } from "../../text/formatted-string";
|
||||
|
||||
export { FormattedString, Span };
|
||||
@ -12,8 +16,6 @@ const CHILD_FORMATTED_STRING = "FormattedString";
|
||||
|
||||
export abstract class TextBaseCommon extends View implements TextBaseDefinition {
|
||||
|
||||
// public abstract _setFormattedTextPropertyToNative(value: FormattedString): void;
|
||||
|
||||
public text: string;
|
||||
public formattedText: FormattedString;
|
||||
|
||||
@ -132,11 +134,13 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition
|
||||
}
|
||||
}
|
||||
|
||||
//Text
|
||||
export function isBold(fontWeight: FontWeight): boolean {
|
||||
return fontWeight === "bold" || fontWeight === "700" || fontWeight === "800" || fontWeight === "900";
|
||||
}
|
||||
|
||||
export const textProperty = new Property<TextBaseCommon, string>({ name: "text", defaultValue: "" });
|
||||
textProperty.register(TextBaseCommon);
|
||||
|
||||
//FormattedText
|
||||
export const formattedTextProperty = new Property<TextBaseCommon, FormattedString>({
|
||||
name: "formattedText",
|
||||
affectsLayout: isIOS,
|
||||
@ -156,7 +160,6 @@ function onFormattedTextPropertyChanged(textBase: TextBaseCommon, oldValue: Form
|
||||
}
|
||||
}
|
||||
|
||||
//TextAlignment
|
||||
export type TextAlignment = "left" | "center" | "right";
|
||||
export namespace TextAlignment {
|
||||
export const LEFT: "left" = "left";
|
||||
@ -173,7 +176,6 @@ export const textAlignmentProperty = new InheritedCssProperty<Style, TextAlignme
|
||||
});
|
||||
textAlignmentProperty.register(Style);
|
||||
|
||||
//TextDecoration
|
||||
export type TextDecoration = "none" | "underline" | "line-through" | "underline line-through";
|
||||
export namespace TextDecoration {
|
||||
export const NONE: "none" = "none";
|
||||
@ -184,15 +186,15 @@ export namespace TextDecoration {
|
||||
export const isValid = makeValidator<TextDecoration>(NONE, UNDERLINE, LINE_THROUGH, UNDERLINE_LINE_THROUGH);
|
||||
export const parse = makeParser<TextDecoration>(isValid);
|
||||
}
|
||||
|
||||
export const textDecorationProperty = new CssProperty<Style, TextDecoration>({
|
||||
name: "textDecoration",
|
||||
cssName: "text-decoration",
|
||||
defaultValue: TextDecoration.NONE,
|
||||
defaultValue: "none",
|
||||
valueConverter: TextDecoration.parse
|
||||
});
|
||||
textDecorationProperty.register(Style);
|
||||
|
||||
//TextTransform
|
||||
export type TextTransform = "none" | "capitalize" | "uppercase" | "lowercase";
|
||||
export namespace TextTransform {
|
||||
export const NONE: "none" = "none";
|
||||
@ -202,15 +204,15 @@ export namespace TextTransform {
|
||||
export const isValid = makeValidator<TextTransform>(NONE, CAPITALIZE, UPPERCASE, LOWERCASE);
|
||||
export const parse = makeParser<TextTransform>(isValid);
|
||||
}
|
||||
|
||||
export const textTransformProperty = new CssProperty<Style, TextTransform>({
|
||||
name: "textTransform",
|
||||
cssName: "text-transform",
|
||||
defaultValue: TextTransform.NONE,
|
||||
defaultValue: "none",
|
||||
valueConverter: TextTransform.parse
|
||||
});
|
||||
textTransformProperty.register(Style);
|
||||
|
||||
//Whitespace
|
||||
export type WhiteSpace = "normal" | "nowrap";
|
||||
export namespace WhiteSpace {
|
||||
export const NORMAL: "normal" = "normal";
|
||||
@ -219,11 +221,9 @@ export namespace WhiteSpace {
|
||||
export const parse = makeParser<WhiteSpace>(isValid);
|
||||
}
|
||||
|
||||
//NB: Default value is deferent for Android and IOS
|
||||
export const whiteSpaceProperty = new CssProperty<Style, WhiteSpace>({
|
||||
name: "whiteSpace",
|
||||
cssName: "white-space",
|
||||
defaultValue: isIOS ? WhiteSpace.NO_WRAP : WhiteSpace.NORMAL,
|
||||
valueConverter: WhiteSpace.parse
|
||||
});
|
||||
whiteSpaceProperty.register(Style);
|
||||
@ -233,6 +233,6 @@ export const letterSpacingProperty = new CssProperty<Style, number>({
|
||||
cssName: "letter-spacing",
|
||||
defaultValue: 0,
|
||||
affectsLayout: isIOS,
|
||||
valueConverter: (v: string) => parseFloat(v)
|
||||
valueConverter: v => parseFloat(v)
|
||||
});
|
||||
letterSpacingProperty.register(Style);
|
||||
|
@ -1,15 +1,13 @@
|
||||
import { Font, FontWeight, FontStyle } from "../styling/font";
|
||||
|
||||
import { Font } from "../styling/font";
|
||||
import { backgroundColorProperty } from "../styling/style-properties";
|
||||
import {
|
||||
TextBaseCommon, formattedTextProperty, textAlignmentProperty, textDecorationProperty, fontSizeProperty,
|
||||
textProperty, textTransformProperty, letterSpacingProperty, colorProperty, fontInternalProperty,
|
||||
whiteSpaceProperty, FormattedString, TextDecoration, TextAlignment, TextTransform, WhiteSpace,
|
||||
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length,
|
||||
layout, Span, Color
|
||||
whiteSpaceProperty, FormattedString, TextDecoration, TextAlignment, TextTransform, WhiteSpace,
|
||||
layout, Span, Color, isBold
|
||||
} from "./text-base-common";
|
||||
|
||||
import { _isSet as isSet } from "../core/properties";
|
||||
|
||||
export * from "./text-base-common";
|
||||
|
||||
interface TextTransformation {
|
||||
@ -24,34 +22,14 @@ function initializeTextTransformation(): void {
|
||||
}
|
||||
|
||||
@Interfaces([android.text.method.TransformationMethod])
|
||||
class TextTransformationImpl extends android.text.method.ReplacementTransformationMethod implements android.text.method.TransformationMethod {
|
||||
class TextTransformationImpl extends java.lang.Object implements android.text.method.TransformationMethod {
|
||||
constructor(public textBase: TextBase) {
|
||||
super();
|
||||
return global.__native(this);
|
||||
}
|
||||
|
||||
protected getOriginal(): native.Array<string> {
|
||||
return convertStringToNativeCharArray(this.textBase.formattedText ? this.textBase.formattedText.toString() : this.textBase.text);
|
||||
}
|
||||
|
||||
protected getReplacement(): native.Array<string> {
|
||||
let replacementString: string = "";
|
||||
const formattedText = this.textBase.formattedText;
|
||||
const textTransform = this.textBase.textTransform;
|
||||
if (formattedText) {
|
||||
for (let i = 0, length = formattedText.spans.length; i < length; i++) {
|
||||
let span = formattedText.spans.getItem(i);
|
||||
replacementString += getTransformedText(span.text, textTransform);
|
||||
}
|
||||
}
|
||||
else {
|
||||
replacementString = getTransformedText(this.textBase.text, textTransform);
|
||||
}
|
||||
|
||||
return convertStringToNativeCharArray(replacementString);
|
||||
}
|
||||
|
||||
public getTransformation(charSeq: any, view: android.view.View): any {
|
||||
// NOTE: Do we need to transform the new text here?
|
||||
const formattedText = this.textBase.formattedText;
|
||||
if (formattedText) {
|
||||
return createSpannableStringBuilder(formattedText);
|
||||
@ -60,173 +38,210 @@ function initializeTextTransformation(): void {
|
||||
return getTransformedText(this.textBase.text, this.textBase.textTransform);
|
||||
}
|
||||
}
|
||||
|
||||
public onFocusChanged(view: android.view.View, sourceText: string, focused: boolean, direction: number, previouslyFocusedRect: android.graphics.Rect): void {
|
||||
// Do nothing for now.
|
||||
}
|
||||
}
|
||||
|
||||
TextTransformation = TextTransformationImpl;
|
||||
}
|
||||
|
||||
export class TextBase extends TextBaseCommon {
|
||||
_nativeView: android.widget.TextView;
|
||||
nativeView: android.widget.TextView;
|
||||
_defaultTransformationMethod: android.text.method.TransformationMethod;
|
||||
|
||||
public _initNativeView(): void {
|
||||
this._defaultTransformationMethod = this.nativeView.getTransformationMethod();
|
||||
super._initNativeView();
|
||||
}
|
||||
|
||||
public _resetNativeView(): void {
|
||||
super._resetNativeView();
|
||||
// We reset it here too because this could be changed by multiple properties - whiteSpace, secure, textTransform
|
||||
this.nativeView.setTransformationMethod(this._defaultTransformationMethod);
|
||||
}
|
||||
|
||||
//Text
|
||||
get [textProperty.native](): string {
|
||||
return this._nativeView.getText();
|
||||
return '';
|
||||
}
|
||||
set [textProperty.native](value: string) {
|
||||
const text = (value === null || value === undefined) ? '' : value.toString();
|
||||
this._nativeView.setText(text);
|
||||
if (this.formattedText) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._setNativeText();
|
||||
}
|
||||
|
||||
//FormattedText
|
||||
get [formattedTextProperty.native](): FormattedString {
|
||||
return null;
|
||||
}
|
||||
set [formattedTextProperty.native](value: FormattedString) {
|
||||
// Don't change the transformation method if this is secure TextField or we'll lose the hiding characters.
|
||||
if ((<any>this).secure) {
|
||||
return;
|
||||
}
|
||||
|
||||
initializeTextTransformation();
|
||||
|
||||
let spannableStringBuilder = createSpannableStringBuilder(value);
|
||||
this._nativeView.setText(<any>spannableStringBuilder);
|
||||
const spannableStringBuilder = createSpannableStringBuilder(value);
|
||||
this.nativeView.setText(<any>spannableStringBuilder);
|
||||
textProperty.nativeValueChange(this, (value === null || value === undefined) ? '' : value.toString());
|
||||
|
||||
if (spannableStringBuilder && this._nativeView instanceof android.widget.Button &&
|
||||
!(this._nativeView.getTransformationMethod() instanceof TextTransformation)) {
|
||||
|
||||
if (spannableStringBuilder && this.nativeView instanceof android.widget.Button &&
|
||||
!(this.nativeView.getTransformationMethod() instanceof TextTransformation)) {
|
||||
// Replace Android Button's default transformation (in case the developer has not already specified a text-transform) method
|
||||
// with our transformation method which can handle formatted text.
|
||||
// Otherwise, the default tranformation method of the Android Button will overwrite and ignore our spannableStringBuilder.
|
||||
// We can't set it to NONE since it is the default value. Set it to something else first.
|
||||
this.style[textTransformProperty.cssName] = TextTransform.UPPERCASE;
|
||||
this.style[textTransformProperty.cssName] = TextTransform.NONE;
|
||||
this.nativeView.setTransformationMethod(new TextTransformation(this));
|
||||
}
|
||||
}
|
||||
|
||||
//TextTransform
|
||||
get [textTransformProperty.native](): android.text.method.TransformationMethod {
|
||||
return this._nativeView.getTransformationMethod();
|
||||
get [textTransformProperty.native](): string {
|
||||
return "default";
|
||||
}
|
||||
|
||||
set [textTransformProperty.native](value: TextTransform | android.text.method.TransformationMethod) {
|
||||
// In case of reset.
|
||||
if (value === "default") {
|
||||
this.nativeView.setTransformationMethod(this._defaultTransformationMethod);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't change the transformation method if this is secure TextField or we'll lose the hiding characters.
|
||||
if ((<any>this).secure) {
|
||||
return;
|
||||
}
|
||||
|
||||
initializeTextTransformation();
|
||||
if (typeof value === "string") {
|
||||
this._nativeView.setTransformationMethod(new TextTransformation(this));
|
||||
this.nativeView.setTransformationMethod(new TextTransformation(this));
|
||||
} else {
|
||||
this._nativeView.setTransformationMethod(value);
|
||||
this.nativeView.setTransformationMethod(value);
|
||||
}
|
||||
}
|
||||
|
||||
//Color
|
||||
get [colorProperty.native](): android.content.res.ColorStateList {
|
||||
return this._nativeView.getTextColors();
|
||||
return this.nativeView.getTextColors();
|
||||
}
|
||||
set [colorProperty.native](value: Color | android.content.res.ColorStateList) {
|
||||
if (value instanceof Color) {
|
||||
this._nativeView.setTextColor(value.android);
|
||||
} else {
|
||||
this._nativeView.setTextColor(value);
|
||||
if (!this.formattedText || !(value instanceof Color)) {
|
||||
if (value instanceof Color) {
|
||||
this.nativeView.setTextColor(value.android);
|
||||
} else {
|
||||
this.nativeView.setTextColor(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FontSize
|
||||
get [fontSizeProperty.native](): { nativeSize: number } {
|
||||
return { nativeSize: this._nativeView.getTextSize() };
|
||||
return { nativeSize: this.nativeView.getTextSize() };
|
||||
}
|
||||
set [fontSizeProperty.native](value: number | { nativeSize: number }) {
|
||||
if (typeof value === "number") {
|
||||
this._nativeView.setTextSize(value);
|
||||
} else {
|
||||
this._nativeView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.nativeSize);
|
||||
if (!this.formattedText || (typeof value !== "number")) {
|
||||
if (typeof value === "number") {
|
||||
this.nativeView.setTextSize(value);
|
||||
} else {
|
||||
this.nativeView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.nativeSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FontInternal
|
||||
get [fontInternalProperty.native](): android.graphics.Typeface {
|
||||
return this._nativeView.getTypeface();
|
||||
return this.nativeView.getTypeface();
|
||||
}
|
||||
set [fontInternalProperty.native](value: Font | android.graphics.Typeface) {
|
||||
this._nativeView.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value);
|
||||
if (!this.formattedText || !(value instanceof Font)) {
|
||||
this.nativeView.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value);
|
||||
}
|
||||
}
|
||||
|
||||
//TextAlignment
|
||||
get [textAlignmentProperty.native](): TextAlignment {
|
||||
let textAlignmentGravity = this._nativeView.getGravity() & android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
let textAlignmentGravity = this.nativeView.getGravity() & android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
switch (textAlignmentGravity) {
|
||||
case android.view.Gravity.LEFT:
|
||||
return TextAlignment.LEFT;
|
||||
case android.view.Gravity.CENTER_HORIZONTAL:
|
||||
return TextAlignment.CENTER;
|
||||
return "center";
|
||||
case android.view.Gravity.RIGHT:
|
||||
return TextAlignment.RIGHT;
|
||||
return "right";
|
||||
case android.view.Gravity.LEFT:
|
||||
default:
|
||||
return TextAlignment.LEFT;
|
||||
return "left";
|
||||
}
|
||||
}
|
||||
set [textAlignmentProperty.native](value: TextAlignment) {
|
||||
let verticalGravity = this._nativeView.getGravity() & android.view.Gravity.VERTICAL_GRAVITY_MASK;
|
||||
let verticalGravity = this.nativeView.getGravity() & android.view.Gravity.VERTICAL_GRAVITY_MASK;
|
||||
switch (value) {
|
||||
case TextAlignment.LEFT:
|
||||
this._nativeView.setGravity(android.view.Gravity.LEFT | verticalGravity);
|
||||
case "left":
|
||||
this.nativeView.setGravity(android.view.Gravity.LEFT | verticalGravity);
|
||||
break;
|
||||
case TextAlignment.CENTER:
|
||||
this._nativeView.setGravity(android.view.Gravity.CENTER_HORIZONTAL | verticalGravity);
|
||||
case "center":
|
||||
this.nativeView.setGravity(android.view.Gravity.CENTER_HORIZONTAL | verticalGravity);
|
||||
break;
|
||||
case TextAlignment.RIGHT:
|
||||
this._nativeView.setGravity(android.view.Gravity.RIGHT | verticalGravity);
|
||||
case "right":
|
||||
this.nativeView.setGravity(android.view.Gravity.RIGHT | verticalGravity);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid text alignment value: ${value}. Valid values are: "${TextAlignment.LEFT}", "${TextAlignment.CENTER}", "${TextAlignment.RIGHT}".`);
|
||||
throw new Error(`Invalid text alignment value: ${value}. Valid values are: 'left', 'center', 'right'.`);
|
||||
}
|
||||
}
|
||||
|
||||
//TextDecoration
|
||||
get [textDecorationProperty.native](): TextDecoration {
|
||||
return TextDecoration.NONE;
|
||||
get [textDecorationProperty.native](): number {
|
||||
return -1;
|
||||
}
|
||||
set [textDecorationProperty.native](value: TextDecoration) {
|
||||
let flags: number;
|
||||
set [textDecorationProperty.native](value: TextDecoration | number) {
|
||||
const isReset = typeof value === "number";
|
||||
if (!this.formattedText || isReset) {
|
||||
value = isReset ? "none" : value;
|
||||
let flags: number;
|
||||
switch (value) {
|
||||
case "none":
|
||||
flags = 0;
|
||||
break;
|
||||
case "underline":
|
||||
flags = android.graphics.Paint.UNDERLINE_TEXT_FLAG;
|
||||
break;
|
||||
case "line-through":
|
||||
flags = android.graphics.Paint.STRIKE_THRU_TEXT_FLAG;
|
||||
break;
|
||||
case "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: 'none', 'underline', 'line-through', 'underline line-through'.`);
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
this._setNativeText();
|
||||
}
|
||||
|
||||
this._nativeView.setPaintFlags(flags);
|
||||
}
|
||||
|
||||
//WhiteSpace
|
||||
// Overriden in TextField becasue setSingleLine(false) will remove methodTransformation.
|
||||
// and we don't want to allow TextField to be multiline
|
||||
get [whiteSpaceProperty.native](): WhiteSpace {
|
||||
return WhiteSpace.NORMAL;
|
||||
return "normal";
|
||||
}
|
||||
|
||||
set [whiteSpaceProperty.native](value: WhiteSpace) {
|
||||
let nativeView = this._nativeView;
|
||||
const nativeView = this.nativeView;
|
||||
switch (value) {
|
||||
case WhiteSpace.NORMAL:
|
||||
case "normal":
|
||||
nativeView.setSingleLine(false);
|
||||
nativeView.setEllipsize(null);
|
||||
break;
|
||||
case WhiteSpace.NO_WRAP:
|
||||
case "nowrap":
|
||||
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}".`);
|
||||
throw new Error(`Invalid whitespace value: ${value}. Valid values are: 'normal', nowrap'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get [letterSpacingProperty.native](): number {
|
||||
return org.nativescript.widgets.ViewHelper.getLetterspacing(this._nativeView);
|
||||
return org.nativescript.widgets.ViewHelper.getLetterspacing(this.nativeView);
|
||||
}
|
||||
set [letterSpacingProperty.native](value: number) {
|
||||
org.nativescript.widgets.ViewHelper.setLetterspacing(this._nativeView, value);
|
||||
org.nativescript.widgets.ViewHelper.setLetterspacing(this.nativeView, value);
|
||||
}
|
||||
|
||||
get [paddingTopProperty.native](): Length {
|
||||
@ -256,6 +271,19 @@ export class TextBase extends TextBaseCommon {
|
||||
set [paddingLeftProperty.native](value: Length) {
|
||||
org.nativescript.widgets.ViewHelper.setPaddingLeft(this.nativeView, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0));
|
||||
}
|
||||
|
||||
_setNativeText() {
|
||||
let transformedText: any;
|
||||
if (this.formattedText) {
|
||||
transformedText = createSpannableStringBuilder(this.formattedText);
|
||||
} else {
|
||||
const text = this.text;
|
||||
const stringValue = (text === null || text === undefined) ? '' : text.toString();
|
||||
transformedText = getTransformedText(stringValue, this.textTransform);
|
||||
}
|
||||
|
||||
this.nativeView.setText(<any>transformedText);
|
||||
}
|
||||
}
|
||||
|
||||
function getCapitalizedString(str: string): string {
|
||||
@ -269,67 +297,51 @@ function getCapitalizedString(str: string): string {
|
||||
return newWords.join(" ");
|
||||
}
|
||||
|
||||
function getTransformedText(text: string, textTransform: TextTransform): string {
|
||||
export function getTransformedText(text: string, textTransform: TextTransform): string {
|
||||
switch (textTransform) {
|
||||
case TextTransform.NONE:
|
||||
case "none":
|
||||
return text;
|
||||
case TextTransform.UPPERCASE:
|
||||
case "uppercase":
|
||||
return text.toUpperCase();
|
||||
case TextTransform.LOWERCASE:
|
||||
case "lowercase":
|
||||
return text.toLowerCase();
|
||||
case TextTransform.CAPITALIZE:
|
||||
case "capitalize":
|
||||
return getCapitalizedString(text);
|
||||
default:
|
||||
throw new Error(`Invalid text transform value: ${textTransform}. Valid values are: "${TextTransform.NONE}", "${TextTransform.CAPITALIZE}", "${TextTransform.UPPERCASE}", "${TextTransform.LOWERCASE}".`);
|
||||
throw new Error(`Invalid text transform value: ${textTransform}. Valid values are: 'none', 'capitalize', 'uppercase', 'lowercase'.`);
|
||||
}
|
||||
}
|
||||
|
||||
function createSpannableStringBuilder(formattedString: FormattedString): android.text.SpannableStringBuilder {
|
||||
let ssb = new android.text.SpannableStringBuilder();
|
||||
|
||||
if (formattedString === null || formattedString === undefined) {
|
||||
return ssb;
|
||||
if (!formattedString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ssb = new android.text.SpannableStringBuilder();
|
||||
for (let i = 0, spanStart = 0, spanLength = 0, length = formattedString.spans.length; i < length; i++) {
|
||||
const span = formattedString.spans.getItem(i);
|
||||
const text = span.text;
|
||||
const textTransform = (<TextBase>formattedString.parent).textTransform;
|
||||
let spanText = (text === null || text === undefined) ? '' : text.toString();
|
||||
if (textTransform) {
|
||||
if (textTransform !== "none") {
|
||||
spanText = getTransformedText(spanText, textTransform);
|
||||
}
|
||||
|
||||
spanLength = spanText.length;
|
||||
if (spanLength !== 0) {
|
||||
if (spanLength > 0) {
|
||||
ssb.insert(spanStart, spanText);
|
||||
setSpanModifiers(ssb, span, spanStart, spanStart + spanLength);
|
||||
spanStart += spanLength;
|
||||
}
|
||||
}
|
||||
|
||||
return ssb;
|
||||
}
|
||||
|
||||
function convertStringToNativeCharArray(value: string): native.Array<string> {
|
||||
let nativeCharArray: native.Array<string> = [];
|
||||
|
||||
for (let i = 0, length = value.length; i < length; i++) {
|
||||
nativeCharArray[i] = value.charAt(i);
|
||||
}
|
||||
return nativeCharArray;
|
||||
}
|
||||
|
||||
function isBold(fontWeight: FontWeight): boolean {
|
||||
return fontWeight === FontWeight.BOLD
|
||||
|| fontWeight === "700"
|
||||
|| fontWeight === FontWeight.EXTRA_BOLD
|
||||
|| fontWeight === FontWeight.BLACK;
|
||||
}
|
||||
|
||||
function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, start: number, end: number): void {
|
||||
const style = span.style;
|
||||
const bold = isBold(style.fontWeight);
|
||||
const italic = style.fontStyle === FontStyle.ITALIC;
|
||||
const spanStyle = span.style;
|
||||
const bold = isBold(spanStyle.fontWeight);
|
||||
const italic = spanStyle.fontStyle === "italic";
|
||||
|
||||
if (bold && italic) {
|
||||
ssb.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD_ITALIC), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
@ -358,33 +370,48 @@ 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);
|
||||
}
|
||||
|
||||
// We don't use isSet function here because defaultValue for backgroundColor is null.
|
||||
const backgroundColor = style.backgroundColor || (<FormattedString>span.parent).backgroundColor || (<TextBase>(<FormattedString>span.parent).parent).backgroundColor;
|
||||
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;
|
||||
}
|
||||
|
||||
if (backgroundColor) {
|
||||
ssb.setSpan(new android.text.style.BackgroundColorSpan(backgroundColor.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
let valueSource: typeof style;
|
||||
if (isSet(textDecorationProperty, style)) {
|
||||
valueSource = style;
|
||||
} else if (isSet(textDecorationProperty, span.parent.style)) {
|
||||
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 (isSet(textDecorationProperty, span.parent.parent.style)) {
|
||||
} else if (textDecorationProperty.isSet(span.parent.parent.style)) {
|
||||
// span.parent.parent is TextBase
|
||||
valueSource = span.parent.parent.style;
|
||||
}
|
||||
|
||||
if (valueSource) {
|
||||
const textDecorations = valueSource.textDecoration;
|
||||
const underline = textDecorations.indexOf(TextDecoration.UNDERLINE) !== -1;
|
||||
const underline = textDecorations.indexOf('underline') !== -1;
|
||||
if (underline) {
|
||||
ssb.setSpan(new android.text.style.UnderlineSpan(), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
const strikethrough = textDecorations.indexOf(TextDecoration.LINE_THROUGH) !== -1;
|
||||
const strikethrough = textDecorations.indexOf('line-through') !== -1;
|
||||
if (strikethrough) {
|
||||
ssb.setSpan(new android.text.style.StrikethroughSpan(), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement letterSpacing for Span here.
|
||||
// const letterSpacing = formattedString.parent.style.letterSpacing;
|
||||
// if (letterSpacing > 0) {
|
||||
// ssb.setSpan(new android.text.style.ScaleXSpan((letterSpacing + 1) / 10), start, end, android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
// }
|
||||
}
|
||||
|
@ -1,19 +1,16 @@
|
||||
import { Font, FontWeight, FontStyle } from "../styling/font";
|
||||
import { Font } from "../styling/font";
|
||||
import {
|
||||
TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty,
|
||||
textTransformProperty, letterSpacingProperty, colorProperty, fontInternalProperty, FormattedString,
|
||||
TextDecoration, TextAlignment, TextTransform, Span, Color
|
||||
TextDecoration, TextAlignment, TextTransform, Span, Color, isBold
|
||||
} from "./text-base-common";
|
||||
|
||||
import { _isSet as isSet } from "../core/properties";
|
||||
|
||||
export * from "./text-base-common";
|
||||
|
||||
export class TextBase extends TextBaseCommon {
|
||||
|
||||
public nativeView: UITextField | UITextView | UILabel | UIButton;
|
||||
|
||||
//Text
|
||||
get [textProperty.native](): string {
|
||||
return '';
|
||||
}
|
||||
@ -26,7 +23,6 @@ export class TextBase extends TextBaseCommon {
|
||||
this._requestLayoutOnTextChanged();
|
||||
}
|
||||
|
||||
//FormattedText
|
||||
get [formattedTextProperty.native](): FormattedString {
|
||||
return null;
|
||||
}
|
||||
@ -36,7 +32,6 @@ export class TextBase extends TextBaseCommon {
|
||||
this._requestLayoutOnTextChanged();
|
||||
}
|
||||
|
||||
//Color
|
||||
get [colorProperty.native](): UIColor {
|
||||
let nativeView = this.nativeView;
|
||||
if (nativeView instanceof UIButton) {
|
||||
@ -57,7 +52,6 @@ export class TextBase extends TextBaseCommon {
|
||||
}
|
||||
}
|
||||
|
||||
//FontInternal
|
||||
get [fontInternalProperty.native](): UIFont {
|
||||
let nativeView = this.nativeView;
|
||||
nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView;
|
||||
@ -72,7 +66,6 @@ export class TextBase extends TextBaseCommon {
|
||||
}
|
||||
}
|
||||
|
||||
//TextAlignment
|
||||
get [textAlignmentProperty.native](): TextAlignment {
|
||||
let nativeView = this.nativeView;
|
||||
nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView;
|
||||
@ -104,27 +97,24 @@ export class TextBase extends TextBaseCommon {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid text alignment value: ${value}. Valid values are: "${TextAlignment.LEFT}", "${TextAlignment.CENTER}", "${TextAlignment.RIGHT}".`);
|
||||
throw new Error(`Invalid text alignment value: ${value}. Valid values are: 'left', 'center', 'right'.`);
|
||||
}
|
||||
}
|
||||
|
||||
//TextDecoration
|
||||
get [textDecorationProperty.native](): TextDecoration {
|
||||
return TextDecoration.NONE;
|
||||
return "none";
|
||||
}
|
||||
set [textDecorationProperty.native](value: TextDecoration) {
|
||||
this._setNativeText();
|
||||
}
|
||||
|
||||
//TextTransform
|
||||
get [textTransformProperty.native](): TextTransform {
|
||||
return TextTransform.NONE;
|
||||
return "none";
|
||||
}
|
||||
set [textTransformProperty.native](value: TextTransform) {
|
||||
this._setNativeText();
|
||||
}
|
||||
|
||||
// LetterSpacing.
|
||||
get [letterSpacingProperty.native](): number {
|
||||
return 0;
|
||||
}
|
||||
@ -142,6 +132,7 @@ export class TextBase extends TextBaseCommon {
|
||||
|
||||
setFormattedTextDecorationAndTransform() {
|
||||
const attrText = this.createNSMutableAttributedString(this.formattedText);
|
||||
// TODO: letterSpacing should be applied per Span.
|
||||
if (this.letterSpacing !== 0) {
|
||||
attrText.addAttributeValueRange(NSKernAttributeName, this.letterSpacing * this.nativeView.font.pointSize, { location: 0, length: attrText.length });
|
||||
}
|
||||
@ -159,20 +150,20 @@ export class TextBase extends TextBaseCommon {
|
||||
|
||||
let dict = new Map<string, any>();
|
||||
switch (style.textDecoration) {
|
||||
case TextDecoration.NONE:
|
||||
case "none":
|
||||
break;
|
||||
case TextDecoration.UNDERLINE:
|
||||
case "underline":
|
||||
dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.StyleSingle);
|
||||
break;
|
||||
case TextDecoration.LINE_THROUGH:
|
||||
case "line-through":
|
||||
dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.StyleSingle);
|
||||
break;
|
||||
case TextDecoration.UNDERLINE_LINE_THROUGH:
|
||||
case "underline line-through":
|
||||
dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.StyleSingle);
|
||||
dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.StyleSingle);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid text decoration value: ${style.textDecoration}. Valid values are: "${TextDecoration.NONE}", "${TextDecoration.UNDERLINE}", "${TextDecoration.LINE_THROUGH}", "${TextDecoration.UNDERLINE_LINE_THROUGH}".`);
|
||||
throw new Error(`Invalid text decoration value: ${style.textDecoration}. Valid values are: 'none', 'underline', 'line-through', 'underline line-through'.`);
|
||||
}
|
||||
|
||||
if (style.letterSpacing !== 0) {
|
||||
@ -237,7 +228,7 @@ export class TextBase extends TextBaseCommon {
|
||||
let attrDict = <{ key: string, value: any }>{};
|
||||
const style = span.style;
|
||||
const bold = isBold(style.fontWeight);
|
||||
const italic = style.fontStyle === FontStyle.ITALIC;
|
||||
const italic = style.fontStyle === "italic";
|
||||
|
||||
let fontFamily = span.fontFamily;
|
||||
let fontSize = span.fontSize;
|
||||
@ -290,24 +281,24 @@ export class TextBase extends TextBaseCommon {
|
||||
}
|
||||
|
||||
let valueSource: typeof style;
|
||||
if (isSet(textDecorationProperty, style)) {
|
||||
if (textDecorationProperty.isSet(style)) {
|
||||
valueSource = style;
|
||||
} else if (isSet(textDecorationProperty, span.parent.style)) {
|
||||
} else if (textDecorationProperty.isSet(span.parent.style)) {
|
||||
// span.parent is FormattedString
|
||||
valueSource = span.parent.style;
|
||||
} else if (isSet(textDecorationProperty, span.parent.parent.style)) {
|
||||
} else if (textDecorationProperty.isSet(span.parent.parent.style)) {
|
||||
// span.parent.parent is TextBase
|
||||
valueSource = span.parent.parent.style;
|
||||
}
|
||||
|
||||
if (valueSource) {
|
||||
const textDecorations = valueSource.textDecoration;
|
||||
const underline = textDecorations.indexOf(TextDecoration.UNDERLINE) !== -1;
|
||||
const underline = textDecorations.indexOf('underline') !== -1;
|
||||
if (underline) {
|
||||
attrDict[NSUnderlineStyleAttributeName] = underline;
|
||||
}
|
||||
|
||||
const strikethrough = textDecorations.indexOf(TextDecoration.LINE_THROUGH) !== -1;
|
||||
const strikethrough = textDecorations.indexOf('line-through') !== -1;
|
||||
if (strikethrough) {
|
||||
attrDict[NSStrikethroughStyleAttributeName] = strikethrough;
|
||||
}
|
||||
@ -319,26 +310,19 @@ export class TextBase extends TextBaseCommon {
|
||||
|
||||
export function getTransformedText(text: string, textTransform: TextTransform): string {
|
||||
switch (textTransform) {
|
||||
case TextTransform.NONE:
|
||||
case "none":
|
||||
return text;
|
||||
case TextTransform.UPPERCASE:
|
||||
case "uppercase":
|
||||
return NSStringFromNSAttributedString(text).uppercaseString;
|
||||
case TextTransform.LOWERCASE:
|
||||
case "lowercase":
|
||||
return NSStringFromNSAttributedString(text).lowercaseString;
|
||||
case TextTransform.CAPITALIZE:
|
||||
case "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}".`);
|
||||
throw new Error(`Invalid text transform value: ${textTransform}. Valid values are: 'none', 'capitalize', 'uppercase', 'lowercase'.`);
|
||||
}
|
||||
}
|
||||
|
||||
function NSStringFromNSAttributedString(source: NSAttributedString | string): NSString {
|
||||
return NSString.stringWithString(source instanceof NSAttributedString && source.string || <string>source);
|
||||
}
|
||||
|
||||
function isBold(fontWeight: FontWeight): boolean {
|
||||
return fontWeight === FontWeight.BOLD
|
||||
|| fontWeight === "700"
|
||||
|| fontWeight === FontWeight.EXTRA_BOLD
|
||||
|| fontWeight === FontWeight.BLACK;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { TextFieldBase, secureProperty } from "./text-field-common";
|
||||
import { TextFieldBase, secureProperty, whiteSpaceProperty, WhiteSpace } from "./text-field-common";
|
||||
|
||||
export * from "./text-field-common";
|
||||
|
||||
@ -19,10 +19,10 @@ export class TextField extends TextFieldBase {
|
||||
return false;
|
||||
}
|
||||
set [secureProperty.native](value: boolean) {
|
||||
let nativeView = this.nativeView;
|
||||
let currentInputType = nativeView.getInputType();
|
||||
let currentClass = currentInputType & android.text.InputType.TYPE_MASK_CLASS;
|
||||
let currentFlags = currentInputType & android.text.InputType.TYPE_MASK_FLAGS;
|
||||
const nativeView = this.nativeView;
|
||||
const currentInputType = nativeView.getInputType();
|
||||
const currentClass = currentInputType & android.text.InputType.TYPE_MASK_CLASS;
|
||||
const currentFlags = currentInputType & android.text.InputType.TYPE_MASK_FLAGS;
|
||||
let newInputType = currentInputType;
|
||||
|
||||
// Password variations are supported only for Text and Number classes.
|
||||
@ -48,4 +48,11 @@ export class TextField extends TextFieldBase {
|
||||
|
||||
nativeView.setInputType(newInputType);
|
||||
}
|
||||
}
|
||||
|
||||
get [whiteSpaceProperty.native](): WhiteSpace {
|
||||
return "nowrap";
|
||||
}
|
||||
set [whiteSpaceProperty.native](value: WhiteSpace) {
|
||||
// Don't change it otherwise TextField will go to multiline mode.
|
||||
}
|
||||
}
|
@ -428,7 +428,10 @@ export function _removePageNativeViewFromAndroidParent(page: Page): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`REMOVED ${page}._nativeView from its Android parent`, traceCategories.Transition);
|
||||
}
|
||||
page._tearDownUI(true);
|
||||
|
||||
if (page._context) {
|
||||
page._tearDownUI(true);
|
||||
}
|
||||
androidParent.removeView(page._nativeView);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user