Refactor application of text decoration, text transform, letter spacing and formatted text.

Related to #2427
This commit is contained in:
Rossen Hristov
2016-07-22 17:05:03 +03:00
parent 20c7284fb4
commit 15d891cc08
20 changed files with 644 additions and 201 deletions

85
apps/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,85 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch on iOS Device",
"type": "nativescript",
"platform": "ios",
"request": "launch",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": false
},
{
"name": "Attach on iOS Device",
"type": "nativescript",
"platform": "ios",
"request": "attach",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": false
},
{
"name": "Launch on iOS Emulator",
"type": "nativescript",
"platform": "ios",
"request": "launch",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": true
},
{
"name": "Attach on iOS Emulator",
"type": "nativescript",
"platform": "ios",
"request": "attach",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": true
},
{
"name": "Launch on Android Device",
"type": "nativescript",
"platform": "android",
"request": "launch",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": false
},
{
"name": "Launch on Android Emulator",
"type": "nativescript",
"platform": "android",
"request": "launch",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": true
},
{
"name": "Attach on Android Device",
"type": "nativescript",
"platform": "android",
"request": "attach",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": false
},
{
"name": "Attach on Android Emulator",
"type": "nativescript",
"platform": "android",
"request": "attach",
"appRoot": "${workspaceRoot}",
"sourceMaps": true,
"diagnosticLogging": false,
"emulator": true
}
]
}

View File

@ -301,3 +301,67 @@ export var test_WhenFormattedTextPropertyChanges_TextIsUpdated_Button = function
TKUnit.assertEqual(view.text, "");
});
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithoutFormattedText_DoesNotCrash = function () {
let view = new buttonModule.Button();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.text = "NormalText";
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithFormattedText_DoesNotCrash = function () {
let view = new buttonModule.Button();
let formattedString = _generateFormattedString();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.formattedText = formattedString;
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
function _generateFormattedString(): formattedStringModule.FormattedString{
let formattedString = new formattedStringModule.FormattedString();
let span: spanModule.Span;
span = new spanModule.Span();
span.fontFamily = "serif";
span.fontSize = 10;
span.fontAttributes = enums.FontAttributes.Bold;
span.foregroundColor = new colorModule.Color("red");
span.backgroundColor = new colorModule.Color("blue");
span.underline = 0;
span.strikethrough = 1;
span.text = "Formatted";
formattedString.spans.push(span);
span = new spanModule.Span();
span.fontFamily = "sans-serif";
span.fontSize = 20;
span.fontAttributes = enums.FontAttributes.Italic;
span.foregroundColor = new colorModule.Color("green");
span.backgroundColor = new colorModule.Color("yellow");
span.underline = 1;
span.strikethrough = 0;
span.text = "Text";
formattedString.spans.push(span);
return formattedString;
}

View File

@ -22,6 +22,9 @@ import {isIOS} from "platform";
import {Label} from "ui/label";
import {LayoutBase} from "ui/layouts/layout-base";
import * as helper from "../helper";
import viewModule = require("ui/core/view");
import formattedStringModule = require("text/formatted-string");
import spanModule = require("text/span");
export class LabelTest extends testModule.UITest<LabelModule.Label> {
@ -596,3 +599,66 @@ export function createTestCase(): LabelTest {
return new LabelTest();
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithoutFormattedText_DoesNotCrash = function () {
let view = new LabelModule.Label();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.text = "NormalText";
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithFormattedText_DoesNotCrash = function () {
let view = new LabelModule.Label();
let formattedString = _generateFormattedString();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.formattedText = formattedString;
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
function _generateFormattedString(): formattedStringModule.FormattedString{
let formattedString = new formattedStringModule.FormattedString();
let span: spanModule.Span;
span = new spanModule.Span();
span.fontFamily = "serif";
span.fontSize = 10;
span.fontAttributes = enums.FontAttributes.Bold;
span.foregroundColor = new colorModule.Color("red");
span.backgroundColor = new colorModule.Color("blue");
span.underline = 0;
span.strikethrough = 1;
span.text = "Formatted";
formattedString.spans.push(span);
span = new spanModule.Span();
span.fontFamily = "sans-serif";
span.fontSize = 20;
span.fontAttributes = enums.FontAttributes.Italic;
span.foregroundColor = new colorModule.Color("green");
span.backgroundColor = new colorModule.Color("yellow");
span.underline = 1;
span.strikethrough = 0;
span.text = "Text";
formattedString.spans.push(span);
return formattedString;
}

View File

@ -535,3 +535,67 @@ export var test_WhenFormattedTextPropertyChanges_TextIsUpdated_TextBase = functi
TKUnit.assertEqual(view.text, "");
});
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithoutFormattedText_DoesNotCrash = function () {
let view = new textFieldModule.TextField();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.text = "NormalText";
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithFormattedText_DoesNotCrash = function () {
let view = new textFieldModule.TextField();
let formattedString = _generateFormattedString();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.formattedText = formattedString;
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
function _generateFormattedString(): formattedStringModule.FormattedString{
let formattedString = new formattedStringModule.FormattedString();
let span: spanModule.Span;
span = new spanModule.Span();
span.fontFamily = "serif";
span.fontSize = 10;
span.fontAttributes = enums.FontAttributes.Bold;
span.foregroundColor = new colorModule.Color("red");
span.backgroundColor = new colorModule.Color("blue");
span.underline = 0;
span.strikethrough = 1;
span.text = "Formatted";
formattedString.spans.push(span);
span = new spanModule.Span();
span.fontFamily = "sans-serif";
span.fontSize = 20;
span.fontAttributes = enums.FontAttributes.Italic;
span.foregroundColor = new colorModule.Color("green");
span.backgroundColor = new colorModule.Color("yellow");
span.underline = 1;
span.strikethrough = 0;
span.text = "Text";
formattedString.spans.push(span);
return formattedString;
}

View File

@ -6,6 +6,8 @@ import textViewTestsNative = require("./text-view-tests-native");
import colorModule = require("color");
import enums = require("ui/enums");
import platform = require("platform");
import formattedStringModule = require("text/formatted-string");
import spanModule = require("text/span");
// >> require-textmodules
import textViewModule = require("ui/text-view");
@ -494,3 +496,67 @@ export function test_watch_listerer_is_removed_at_onDetach() {
});
}
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithoutFormattedText_DoesNotCrash = function () {
let view = new textViewModule.TextView();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.text = "NormalText";
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
export var test_IntegrationTest_Transform_Decoration_Spacing_WithFormattedText_DoesNotCrash = function () {
let view = new textViewModule.TextView();
let formattedString = _generateFormattedString();
helper.buildUIAndRunTest(view, function (views: Array<viewModule.View>) {
TKUnit.assertEqual(view.text, "", "Text");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.none, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.none, "TextDecoration");
TKUnit.assertTrue(isNaN(view.style.letterSpacing), "LetterSpacing");
view.formattedText = formattedString;
view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 10;");
TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform");
TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration");
TKUnit.assertEqual(view.style.letterSpacing, 10, "LetterSpacing");
});
}
function _generateFormattedString(): formattedStringModule.FormattedString{
let formattedString = new formattedStringModule.FormattedString();
let span: spanModule.Span;
span = new spanModule.Span();
span.fontFamily = "serif";
span.fontSize = 10;
span.fontAttributes = enums.FontAttributes.Bold;
span.foregroundColor = new colorModule.Color("red");
span.backgroundColor = new colorModule.Color("blue");
span.underline = 0;
span.strikethrough = 1;
span.text = "Formatted";
formattedString.spans.push(span);
span = new spanModule.Span();
span.fontFamily = "sans-serif";
span.fontSize = 20;
span.fontAttributes = enums.FontAttributes.Italic;
span.foregroundColor = new colorModule.Color("green");
span.backgroundColor = new colorModule.Color("yellow");
span.underline = 1;
span.strikethrough = 0;
span.text = "Text";
formattedString.spans.push(span);
return formattedString;
}

View File

@ -234,4 +234,8 @@ export class FormattedString extends observable.Observable implements definition
view.formattedText = value;
}
}
public _updateCharactersInRangeReplacementString(rangeLocation: number, rangeLength: number, replacementString: string): void {
//
}
}

View File

@ -98,5 +98,10 @@ declare module "text/formatted-string" {
* A static method used to add child elements of the FormattedString class to a View declared in xml.
*/
public static addFormattedStringToView(view: FormattedStringView, name: string, value: any): void;
//@private
createFormattedStringCore(): void;
_updateCharactersInRangeReplacementString(rangeLocation: number, rangeLength: number, replacementString: string): void;
//@endprivate
}
}

View File

@ -27,4 +27,29 @@ export class FormattedString extends formattedStringCommon.FormattedString {
}
this._formattedText = mas;
}
public _updateCharactersInRangeReplacementString(rangeLocation: number, rangeLength: number, replacementString: string): void {
let deletingText = !replacementString;
let currentLocation = 0;
for (let i = 0; i < this.spans.length; i++) {
let span = <spanModule.Span>this.spans.getItem(i);
if (currentLocation <= rangeLocation && rangeLocation < (currentLocation + span.text.length)){
(<any>span)._text = splice(span.text, rangeLocation - currentLocation, deletingText ? rangeLength : 0, replacementString);
//console.log(`>>> ${span.text}`);
return;
}
currentLocation += span.text.length;
}
}
}
/*
* @param {String} value The string to splice.
* @param {number} start Index at which to start changing the string.
* @param {number} delCount An integer indicating the number of old chars to remove.
* @param {string} newSubStr The String that is spliced in.
* @return {string} A new string with the spliced substring.function splice(value: string, start: number, delCount: number, newSubStr: string) {
*/
function splice(value: string, start: number, delCount: number, newSubStr: string) {
return value.slice(0, start) + newSubStr + value.slice(start + Math.abs(delCount));
};

View File

@ -28,6 +28,7 @@ function onTextPropertyChanged(data: PropertyChangeData) {
button._onTextPropertyChanged(data);
//RemoveThisDoubleCall
button.style._updateTextDecoration();
button.style._updateTextTransform();
}
@ -53,6 +54,8 @@ export class Button extends view.View implements definition.Button {
this.formattedText.updateSpansBindingContext(newValue);
}
//This is because of ListView virtualization
//RemoveThisDoubleCall
this.style._updateTextDecoration();
this.style._updateTextTransform();
}

View File

@ -6,6 +6,7 @@ import view = require("ui/core/view");
import utils = require("utils/utils");
import enums = require("ui/enums");
import dependencyObservable = require("ui/core/dependency-observable");
import types = require("utils/types");
import {PseudoClassHandler} from "ui/core/view";
class TapHandlerImpl extends NSObject {
@ -63,7 +64,9 @@ export class Button extends common.Button {
// set the value for the normal state.
let newText = value ? value._formattedText : null;
this.ios.setAttributedTitleForState(newText, UIControlState.UIControlStateNormal);
//RemoveThisDoubleCall
this.style._updateTextDecoration();
this.style._updateTextTransform();
}
@PseudoClassHandler("normal", "highlighted")
@ -164,29 +167,29 @@ export class ButtonStyler implements style.Styler {
// text-decoration
private static setTextDecorationProperty(view: view.View, newValue: any) {
utils.ios.setTextDecorationAndTransform(view, newValue, view.style.textTransform, view.style.letterSpacing);
ButtonStyler._setButtonTextDecorationAndTransform(<Button>view, newValue, view.style.textTransform, view.style.letterSpacing);
}
private static resetTextDecorationProperty(view: view.View, nativeValue: any) {
utils.ios.setTextDecorationAndTransform(view, enums.TextDecoration.none, view.style.textTransform, view.style.letterSpacing);
ButtonStyler._setButtonTextDecorationAndTransform(<Button>view, enums.TextDecoration.none, view.style.textTransform, view.style.letterSpacing);
}
// text-transform
private static setTextTransformProperty(view: view.View, newValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, newValue, view.style.letterSpacing);
ButtonStyler._setButtonTextDecorationAndTransform(<Button>view, view.style.textDecoration, newValue, view.style.letterSpacing);
}
private static resetTextTransformProperty(view: view.View, nativeValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, enums.TextTransform.none, view.style.letterSpacing);
ButtonStyler._setButtonTextDecorationAndTransform(<Button>view, view.style.textDecoration, enums.TextTransform.none, view.style.letterSpacing);
}
// letter-spacing
private static setLetterSpacingProperty(view: view.View, newValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, view.style.textTransform, newValue);
ButtonStyler._setButtonTextDecorationAndTransform(<Button>view, view.style.textDecoration, view.style.textTransform, newValue);
}
private static resetLetterSpacingProperty(view: view.View, nativeValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, view.style.textTransform, 0);
ButtonStyler._setButtonTextDecorationAndTransform(<Button>view, view.style.textDecoration, view.style.textTransform, 0);
}
// white-space
@ -234,6 +237,77 @@ export class ButtonStyler implements style.Styler {
ButtonStyler.setWhiteSpaceProperty,
ButtonStyler.resetWhiteSpaceProperty), "Button");
}
private static _setButtonTextDecorationAndTransform(button: Button, decoration: string, transform: string, letterSpacing: number) {
let hasLetterSpacing = types.isNumber(letterSpacing) && !isNaN(letterSpacing);
if (button.formattedText) {
if (button.style.textDecoration.indexOf(enums.TextDecoration.none) === -1) {
if (button.style.textDecoration.indexOf(enums.TextDecoration.underline) !== -1) {
button.formattedText.underline = NSUnderlineStyle.NSUnderlineStyleSingle;
}
if (button.style.textDecoration.indexOf(enums.TextDecoration.lineThrough) !== -1) {
button.formattedText.strikethrough = NSUnderlineStyle.NSUnderlineStyleSingle;
}
}
else {
button.formattedText.underline = NSUnderlineStyle.NSUnderlineStyleNone;
}
for (let i = 0; i < button.formattedText.spans.length; i++) {
let span = button.formattedText.spans.getItem(i);
span.text = utils.ios.getTransformedText(button, span.text, transform);
}
if (hasLetterSpacing) {
let attrText = NSMutableAttributedString.alloc().initWithAttributedString(button.ios.attributedTitleForState(UIControlState.UIControlStateNormal));
attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing, { location: 0, length: attrText.length });
button.ios.setAttributedTitleForState(attrText, UIControlState.UIControlStateNormal);
}
}
else {
let source = button.text;
let attributes = new Array();
let range = { location: 0, length: source.length };
var decorationValues = (decoration + "").split(" ");
if (decorationValues.indexOf(enums.TextDecoration.none) === -1 || hasLetterSpacing) {
let dict = new Map<string, number>();
if (decorationValues.indexOf(enums.TextDecoration.underline) !== -1) {
dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.NSUnderlineStyleSingle);
}
if (decorationValues.indexOf(enums.TextDecoration.lineThrough) !== -1) {
dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.NSUnderlineStyleSingle);
}
if (hasLetterSpacing) {
dict.set(NSKernAttributeName, letterSpacing);
}
attributes.push({ attrs: dict, range: NSValue.valueWithRange(range) });
}
source = utils.ios.getTransformedText(button, source, transform);
if (attributes.length > 0) {
let result = NSMutableAttributedString.alloc().initWithString(source);
for (let i = 0; i < attributes.length; i++) {
result.setAttributesRange(attributes[i]["attrs"], attributes[i]["range"].rangeValue);
}
button.ios.setAttributedTitleForState(result, UIControlState.UIControlStateNormal);
}
else {
button.ios.setAttributedTitleForState(NSMutableAttributedString.alloc().initWithString(source), UIControlState.UIControlStateNormal);
button.ios.setTitleForState(source, UIControlState.UIControlStateNormal);
}
}
}
}
ButtonStyler.registerHandlers();

View File

@ -34,7 +34,11 @@ export class EditableTextBase extends common.EditableTextBase {
}
var selectionStart = owner.android.getSelectionStart();
owner.android.removeTextChangedListener(owner._textWatcher);
//RemoveThisDoubleCall
owner.style._updateTextDecoration();
owner.style._updateTextTransform();
owner.android.addTextChangedListener(owner._textWatcher);
owner.android.setSelection(selectionStart);
},

View File

@ -42,9 +42,6 @@ export class Label extends common.Label {
public onLoaded() {
super.onLoaded();
this.style._updateTextDecoration();
this.style._updateTextTransform();
}
_requestLayoutOnTextChanged(): void {

View File

@ -986,39 +986,32 @@ export class Style extends DependencyObservable implements styling.Style {
return;
}
try {
let handler: definition.StylePropertyChangedHandler = getHandler(property, this._view);
if (!handler) {
if (trace.enabled) {
trace.write("No handler for property: " + property.name + " with id: " + property.id + ", view:" + this._view, trace.categories.Style);
}
}
else {
if (trace.enabled) {
trace.write("Found handler for property: " + property.name + ", view:" + this._view, trace.categories.Style);
}
let shouldReset = false;
if (property.equalityComparer) {
shouldReset = property.equalityComparer(newValue, property.defaultValue);
}
else {
shouldReset = (newValue === property.defaultValue);
}
if (shouldReset) {
(<any>handler).resetProperty(property, this._view);
} else {
(<any>handler).applyProperty(property, this._view, newValue);
}
this._view._onStylePropertyChanged(property);
let handler: definition.StylePropertyChangedHandler = getHandler(property, this._view);
if (!handler) {
if (trace.enabled) {
trace.write("No handler for property: " + property.name + " with id: " + property.id + ", view:" + this._view, trace.categories.Style);
}
}
catch (ex) {
else {
if (trace.enabled) {
trace.write("Error setting property: " + property.name + " on " + this._view + ": " + ex, trace.categories.Style, trace.messageType.error);
trace.write("Found handler for property: " + property.name + ", view:" + this._view, trace.categories.Style);
}
let shouldReset = false;
if (property.equalityComparer) {
shouldReset = property.equalityComparer(newValue, property.defaultValue);
}
else {
shouldReset = (newValue === property.defaultValue);
}
if (shouldReset) {
(<any>handler).resetProperty(property, this._view);
} else {
(<any>handler).applyProperty(property, this._view, newValue);
}
this._view._onStylePropertyChanged(property);
}
}

View File

@ -31,6 +31,7 @@ function onTextPropertyChanged(data: dependencyObservable.PropertyChangeData) {
textBase._onTextPropertyChanged(data);
//RemoveThisDoubleCall
textBase.style._updateTextTransform();
textBase.style._updateTextDecoration();
}
@ -54,6 +55,8 @@ export class TextBase extends view.View implements definition.TextBase, formatte
this.formattedText.updateSpansBindingContext(newValue);
}
//This is because of ListView virtualization
//RemoveThisDoubleCall
this.style._updateTextTransform();
this.style._updateTextDecoration();
}
@ -133,4 +136,4 @@ export class TextBase extends view.View implements definition.TextBase, formatte
}
}
tbs.TextBaseStyler.registerHandlers()
tbs.TextBaseStyler.registerHandlers();

View File

@ -3,88 +3,90 @@ import utils = require("utils/utils");
import style = require("ui/styling/style");
import font = require("ui/styling/font");
import enums = require("ui/enums");
import types = require("utils/types");
import { TextBase } from "ui/text-base";
export class TextBaseStyler implements style.Styler {
// font
private static setFontInternalProperty(view: view.View, newValue: any, nativeValue?: any) {
var ios = <utils.ios.TextUIView>view._nativeView;
private static setFontInternalProperty(textBase: TextBase, newValue: any, nativeValue?: any) {
var ios = <utils.ios.TextUIView>textBase._nativeView;
ios.font = (<font.Font>newValue).getUIFont(nativeValue);
}
private static resetFontInternalProperty(view: view.View, nativeValue: any) {
var ios = <utils.ios.TextUIView>view._nativeView;
private static resetFontInternalProperty(textBase: TextBase, nativeValue: any) {
var ios = <utils.ios.TextUIView>textBase._nativeView;
ios.font = nativeValue;
}
private static getNativeFontInternalValue(view: view.View): any {
var ios = <utils.ios.TextUIView>view._nativeView;
private static getNativeFontInternalValue(textBase: TextBase): any {
var ios = <utils.ios.TextUIView>textBase._nativeView;
return ios.font;
}
// text-align
private static setTextAlignmentProperty(view: view.View, newValue: any) {
utils.ios.setTextAlignment(view._nativeView, newValue);
private static setTextAlignmentProperty(textBase: TextBase, newValue: any) {
utils.ios.setTextAlignment(textBase._nativeView, newValue);
}
private static resetTextAlignmentProperty(view: view.View, nativeValue: any) {
var ios = <utils.ios.TextUIView>view._nativeView;
private static resetTextAlignmentProperty(textBase: TextBase, nativeValue: any) {
var ios = <utils.ios.TextUIView>textBase._nativeView;
ios.textAlignment = nativeValue;
}
private static getNativeTextAlignmentValue(view: view.View): any {
var ios = <utils.ios.TextUIView>view._nativeView;
private static getNativeTextAlignmentValue(textBase: TextBase): any {
var ios = <utils.ios.TextUIView>textBase._nativeView;
return ios.textAlignment;
}
// text-decoration
private static setTextDecorationProperty(view: view.View, newValue: any) {
utils.ios.setTextDecorationAndTransform(view, newValue, view.style.textTransform, view.style.letterSpacing);
private static setTextDecorationProperty(textBase: TextBase, newValue: any) {
TextBaseStyler._setTextBaseTextDecorationAndTransform(textBase, newValue, textBase.style.textTransform, textBase.style.letterSpacing);
}
private static resetTextDecorationProperty(view: view.View, nativeValue: any) {
utils.ios.setTextDecorationAndTransform(view, enums.TextDecoration.none, view.style.textTransform, view.style.letterSpacing);
private static resetTextDecorationProperty(textBase: TextBase, nativeValue: any) {
TextBaseStyler._setTextBaseTextDecorationAndTransform(textBase, enums.TextDecoration.none, textBase.style.textTransform, textBase.style.letterSpacing);
}
// text-transform
private static setTextTransformProperty(view: view.View, newValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, newValue, view.style.letterSpacing);
private static setTextTransformProperty(textBase: TextBase, newValue: any) {
TextBaseStyler._setTextBaseTextDecorationAndTransform(textBase, textBase.style.textDecoration, newValue, textBase.style.letterSpacing);
}
private static resetTextTransformProperty(view: view.View, nativeValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, enums.TextTransform.none, view.style.letterSpacing);
}
// white-space
private static setWhiteSpaceProperty(view: view.View, newValue: any) {
utils.ios.setWhiteSpace(view._nativeView, newValue);
}
private static resetWhiteSpaceProperty(view: view.View, nativeValue: any) {
utils.ios.setWhiteSpace(view._nativeView, enums.WhiteSpace.normal);
private static resetTextTransformProperty(textBase: TextBase, nativeValue: any) {
TextBaseStyler._setTextBaseTextDecorationAndTransform(textBase, textBase.style.textDecoration, enums.TextTransform.none, textBase.style.letterSpacing);
}
// letter-spacing
private static setLetterSpacingProperty(view: view.View, newValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, enums.TextTransform.none, newValue);
private static setLetterSpacingProperty(textBase: TextBase, newValue: any) {
TextBaseStyler._setTextBaseTextDecorationAndTransform(textBase, textBase.style.textDecoration, textBase.style.textTransform, newValue);
}
private static resetLetterSpacingProperty(view: view.View, nativeValue: any) {
utils.ios.setTextDecorationAndTransform(view, view.style.textDecoration, enums.TextTransform.none, 0);
private static resetLetterSpacingProperty(textBase: TextBase, nativeValue: any) {
TextBaseStyler._setTextBaseTextDecorationAndTransform(textBase, textBase.style.textDecoration, textBase.style.textTransform, 0);
}
// white-space
private static setWhiteSpaceProperty(textBase: view.View, newValue: any) {
utils.ios.setWhiteSpace(textBase._nativeView, newValue);
}
private static resetWhiteSpaceProperty(textBase: view.View, nativeValue: any) {
utils.ios.setWhiteSpace(textBase._nativeView, enums.WhiteSpace.normal);
}
// color
private static setColorProperty(view: view.View, newValue: any) {
var ios = <utils.ios.TextUIView>view._nativeView;
private static setColorProperty(textBase: view.View, newValue: any) {
var ios = <utils.ios.TextUIView>textBase._nativeView;
ios.textColor = newValue;
}
private static resetColorProperty(view: view.View, nativeValue: any) {
var ios = <utils.ios.TextUIView>view._nativeView;
private static resetColorProperty(textBase: view.View, nativeValue: any) {
var ios = <utils.ios.TextUIView>textBase._nativeView;
ios.textColor = nativeValue;
}
private static getNativeColorValue(view: view.View): any {
var ios = <utils.ios.TextUIView>view._nativeView;
private static getNativeColorValue(textBase: view.View): any {
var ios = <utils.ios.TextUIView>textBase._nativeView;
return ios.textColor;
}
@ -120,4 +122,80 @@ export class TextBaseStyler implements style.Styler {
TextBaseStyler.setLetterSpacingProperty,
TextBaseStyler.resetLetterSpacingProperty), "TextBase");
}
private static _setTextBaseTextDecorationAndTransform(textBase: TextBase, decoration: string, transform: string, letterSpacing: number) {
// if (!textBase["counter"]){
// textBase["counter"] = 0;
// }
// textBase["counter"]++;
// console.log(`>>> ${textBase["counter"]} ${textBase} ${decoration}; ${transform}; ${letterSpacing}; text: ${textBase.text}; formattedText: ${textBase.formattedText}; isLoaded: ${textBase.isLoaded};`);
let hasLetterSpacing = types.isNumber(letterSpacing) && !isNaN(letterSpacing);
let nativeView = <utils.ios.TextUIView>textBase._nativeView;
if (textBase.formattedText) {
if (textBase.style.textDecoration.indexOf(enums.TextDecoration.none) === -1) {
if (textBase.style.textDecoration.indexOf(enums.TextDecoration.underline) !== -1) {
textBase.formattedText.underline = NSUnderlineStyle.NSUnderlineStyleSingle;
}
if (textBase.style.textDecoration.indexOf(enums.TextDecoration.lineThrough) !== -1) {
textBase.formattedText.strikethrough = NSUnderlineStyle.NSUnderlineStyleSingle;
}
}
else {
textBase.formattedText.underline = NSUnderlineStyle.NSUnderlineStyleNone;
}
for (let i = 0; i < textBase.formattedText.spans.length; i++) {
let span = textBase.formattedText.spans.getItem(i);
span.text = utils.ios.getTransformedText(textBase, span.text, transform);
}
if (hasLetterSpacing) {
let attrText = NSMutableAttributedString.alloc().initWithAttributedString(nativeView.attributedText);
attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing, { location: 0, length: attrText.length });
nativeView.attributedText = attrText;
}
}
else {
let source = textBase.text;
let attributes = new Array();
let range = { location: 0, length: source.length };
var decorationValues = (decoration + "").split(" ");
if (decorationValues.indexOf(enums.TextDecoration.none) === -1 || hasLetterSpacing) {
let dict = new Map<string, number>();
if (decorationValues.indexOf(enums.TextDecoration.underline) !== -1) {
dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.NSUnderlineStyleSingle);
}
if (decorationValues.indexOf(enums.TextDecoration.lineThrough) !== -1) {
dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.NSUnderlineStyleSingle);
}
if (hasLetterSpacing) {
dict.set(NSKernAttributeName, letterSpacing);
}
attributes.push({ attrs: dict, range: NSValue.valueWithRange(range) });
}
source = utils.ios.getTransformedText(textBase, source, transform);
if (attributes.length > 0) {
let result = NSMutableAttributedString.alloc().initWithString(<string>source);
for (let i = 0; i < attributes.length; i++) {
result.setAttributesRange(attributes[i]["attrs"], attributes[i]["range"].rangeValue);
}
nativeView.attributedText = result;
}
else {
nativeView.text = source;
}
}
}
}

View File

@ -6,16 +6,22 @@ export class TextBase extends common.TextBase {
public _onTextPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var newValue = types.toUIString(data.newValue);
this.ios.text = newValue;
//RemoveThisDoubleCall
this.style._updateTextDecoration();
this.style._updateTextTransform();
this._requestLayoutOnTextChanged();
}
public _setFormattedTextPropertyToNative(value) {
var newText = value ? value._formattedText : null;
this.ios.attributedText = newText;
//RemoveThisDoubleCall
this.style._updateTextDecoration();
this.style._updateTextTransform();
this.requestLayout();
}

View File

@ -45,6 +45,10 @@ class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate {
}
owner.dismissSoftInput();
if (owner.formattedText){
owner.formattedText.createFormattedStringCore();
}
}
}
@ -71,12 +75,6 @@ class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate {
public textFieldShouldChangeCharactersInRangeReplacementString(textField: UITextField, range: NSRange, replacementString: string): boolean {
let owner = this._owner.get();
if (owner) {
var r = textField.selectedTextRange;
owner.style._updateTextDecoration();
owner.style._updateTextTransform();
textField.selectedTextRange = r;
if (owner.updateTextTrigger === UpdateTextTrigger.textChanged) {
if (textField.secureTextEntry && this.firstEdit) {
owner._onPropertyChangedFromNative(TextBase.textProperty, replacementString);
@ -88,6 +86,10 @@ class UITextFieldDelegateImpl extends NSObject implements UITextFieldDelegate {
}
}
}
if (owner.formattedText){
owner.formattedText._updateCharactersInRangeReplacementString(range.location, range.length, replacementString);
}
}
this.firstEdit = false;
@ -142,9 +144,6 @@ export class TextField extends common.TextField {
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;
this.style._updateTextDecoration();
this.style._updateTextTransform();
}
public onUnloaded() {

View File

@ -28,14 +28,6 @@ class UITextViewDelegateImpl extends NSObject implements UITextViewDelegate {
return true;
}
public textViewDidBeginEditing(textView: UITextView) {
let owner = this._owner.get();
if (owner) {
owner.style._updateTextDecoration();
owner.style._updateTextTransform();
}
}
public textViewDidEndEditing(textView: UITextView) {
let owner = this._owner.get();
if (owner) {
@ -46,6 +38,12 @@ class UITextViewDelegateImpl extends NSObject implements UITextViewDelegate {
owner.dismissSoftInput();
owner._refreshHintState(owner.hint, textView.text);
if (owner.formattedText) {
owner.formattedText.createFormattedStringCore();
}
//RemoveThisDoubleCall
owner.style._updateTextDecoration();
owner.style._updateTextTransform();
}
@ -54,16 +52,20 @@ class UITextViewDelegateImpl extends NSObject implements UITextViewDelegate {
public textViewDidChange(textView: UITextView) {
let owner = this._owner.get();
if (owner) {
var range = textView.selectedRange;
owner.style._updateTextDecoration();
owner.style._updateTextTransform();
textView.selectedRange = range;
if (owner.updateTextTrigger === UpdateTextTrigger.textChanged) {
owner._onPropertyChangedFromNative(TextBase.textProperty, textView.text);
}
}
}
public textViewShouldChangeTextInRangeReplacementText(textView: UITextView, range: NSRange, replacementString: string): boolean {
let owner = this._owner.get();
if (owner && owner.formattedText) {
owner.formattedText._updateCharactersInRangeReplacementString(range.location, range.length, replacementString);
}
return true;
}
}
export class TextView extends common.TextView {
@ -83,9 +85,6 @@ export class TextView extends common.TextView {
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;
this.style._updateTextDecoration();
this.style._updateTextTransform();
}
public onUnloaded() {

View File

@ -154,10 +154,11 @@
* Module with ios specific utilities.
*/
module ios {
export function setTextDecorationAndTransform(view: any, decoration: string, transform: string, letterSpacing : number);
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 {
font: any;
textAlignment: number;

View File

@ -1,5 +1,4 @@
import dts = require("utils/utils");
import types = require("utils/types");
import common = require("./utils-common");
import {Color} from "color";
import enums = require("ui/enums");
@ -41,7 +40,6 @@ export module layout {
}
export module ios {
export function setTextAlignment(view: dts.ios.TextUIView, value: string) {
switch (value) {
case enums.TextAlignment.left:
@ -58,98 +56,7 @@ export module ios {
}
}
export function setTextDecorationAndTransform(v: any, decoration: string, transform: string, letterSpacing: number) {
let hasLetterSpacing = types.isNumber(letterSpacing) && !isNaN(letterSpacing);
if (v.formattedText) {
if (v.style.textDecoration.indexOf(enums.TextDecoration.none) === -1) {
if (v.style.textDecoration.indexOf(enums.TextDecoration.underline) !== -1) {
(<any>v).formattedText.underline = NSUnderlineStyle.NSUnderlineStyleSingle;
}
if (v.style.textDecoration.indexOf(enums.TextDecoration.lineThrough) !== -1) {
(<any>v).formattedText.strikethrough = NSUnderlineStyle.NSUnderlineStyleSingle;
}
} else {
(<any>v).formattedText.underline = NSUnderlineStyle.NSUnderlineStyleNone;
}
for (let i = 0; i < v.formattedText.spans.length; i++) {
let span = v.formattedText.spans.getItem(i);
span.text = getTransformedText(v, span.text, transform);
}
if (hasLetterSpacing) {
let attrText;
if(v._nativeView instanceof UIButton){
attrText = (<UIButton>v._nativeView).attributedTitleForState(UIControlState.UIControlStateNormal);
} else {
attrText = v._nativeView.attributedText;
}
attrText.addAttributeValueRange(NSKernAttributeName, letterSpacing, { location: 0, length: v._nativeView.attributedText.length });
if(v._nativeView instanceof UIButton){
(<UIButton>v._nativeView).setAttributedTitleForState(attrText, UIControlState.UIControlStateNormal);
}
}
} else {
let source = v.text;
let attributes = new Array();
let range = { location: 0, length: source.length };
var decorationValues = (decoration + "").split(" ");
if (decorationValues.indexOf(enums.TextDecoration.none) === -1 || hasLetterSpacing) {
let dict = new Map<string, number>();
if (decorationValues.indexOf(enums.TextDecoration.underline) !== -1) {
dict.set(NSUnderlineStyleAttributeName, NSUnderlineStyle.NSUnderlineStyleSingle);
}
if (decorationValues.indexOf(enums.TextDecoration.lineThrough) !== -1) {
dict.set(NSStrikethroughStyleAttributeName, NSUnderlineStyle.NSUnderlineStyleSingle);
}
if (hasLetterSpacing) {
dict.set(NSKernAttributeName, letterSpacing);
}
attributes.push({ attrs: dict, range: NSValue.valueWithRange(range) });
}
let view: dts.ios.TextUIView | UIButton = v._nativeView;
source = getTransformedText(v, source, transform);
if (attributes.length > 0) {
let result = NSMutableAttributedString.alloc().initWithString(<string>source);
for (let i = 0; i < attributes.length; i++) {
result.setAttributesRange(attributes[i]["attrs"], attributes[i]["range"].rangeValue);
}
if (view instanceof UIButton) {
(<UIButton>view).setAttributedTitleForState(result, UIControlState.UIControlStateNormal);
}
else {
(<dts.ios.TextUIView>view).attributedText = result;
}
} else {
if (view instanceof UIButton) {
(<UIButton>view).setAttributedTitleForState(NSMutableAttributedString.alloc().initWithString(source), UIControlState.UIControlStateNormal);
(<UIButton>view).setTitleForState(<string>source, UIControlState.UIControlStateNormal);
}
else {
(<dts.ios.TextUIView>view).text = <string>source;
}
}
}
}
function getTransformedText(view, source: string, transform: string): string {
export function getTransformedText(view, source: string, transform: string): string {
let result = source;
switch (transform) {