Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/app/ui-tests-app/css/main-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ export function loadExamples() {
examples.set("background-shorthand", "css/background-shorthand");
examples.set("background-image-linear-gradient", "css/background-image-linear-gradient");
examples.set("background-image", "css/background-image");
examples.set("tappable-span", "css/tappable-span");
return examples;
}
9 changes: 9 additions & 0 deletions apps/app/ui-tests-app/css/tappable-span.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EventData, TextBase } from "tns-core-modules/ui/text-base";

export function foxTap(args: EventData) {
console.log("foxTap");
}

export function dogTap(args: EventData) {
console.log("dogTap");
}
17 changes: 17 additions & 0 deletions apps/app/ui-tests-app/css/tappable-span.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Page>
<StackLayout>
<Label automationText="formattedText" id="formattedText" textWrap="true" style="text-transform: capitalize;">
<Label.formattedText>
<FormattedString>
<FormattedString.spans>
<Span text="The quick brown " style="font-weight: bold; color: green;" />
<Span text="fox" style="font-weight: italic; color: green;" linkTap="foxTap"/>
<Span text=" jumps over the lazy " style="font-weight: bold; color: red;" />
<Span text="dog" style="font-weight: bold; color: blue;" linkTap="dogTap"/>
<Span text="." style="font-weight: bold; color: green;" />
</FormattedString.spans>
</FormattedString>
</Label.formattedText>
</Label>
</StackLayout>
</Page>
10 changes: 10 additions & 0 deletions tns-core-modules/text/span.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ export class Span extends ViewBase {
* Gets or sets the text for the span.
*/
public text: string;

/**
* String value used when hooking to linkTap event.
*/
public static linkTapEvent: string;

/**
* Gets if the span is tappable or not.
*/
public readonly tappable: boolean;

//@private
/**
Expand Down
25 changes: 24 additions & 1 deletion tns-core-modules/text/span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import { Span as SpanDefinition } from "./span";
import { ViewBase } from "../ui/core/view";
import { FontStyle, FontWeight, } from "../ui/styling/font";
import { TextDecoration } from "../ui/text-base";
import { TextDecoration, EventData } from "../ui/text-base";

export class Span extends ViewBase implements SpanDefinition {
static linkTapEvent = "linkTap";
private _text: string;
private _tappable: boolean = false;

get fontFamily(): string {
return this.style.fontFamily;
Expand Down Expand Up @@ -68,7 +70,28 @@ export class Span extends ViewBase implements SpanDefinition {
}
}

get tappable(): boolean {
return this._tappable;
}

addEventListener(arg: string, callback: (data: EventData) => void, thisArg?: any) {
super.addEventListener(arg, callback, thisArg);
this._setTappable(this.hasListeners(Span.linkTapEvent));
}

removeEventListener(arg: string, callback?: any, thisArg?: any) {
super.removeEventListener(arg, callback, thisArg);
this._setTappable(this.hasListeners(Span.linkTapEvent));
}

_setTextInternal(value: string): void {
this._text = value;
}

private _setTappable(value: boolean): void {
if (this._tappable !== value) {
this._tappable = value;
this.notifyPropertyChange("tappable", value);
}
}
}
72 changes: 72 additions & 0 deletions tns-core-modules/ui/text-base/text-base.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,41 @@ function initializeTextTransformation(): void {
TextTransformation = TextTransformationImpl;
}

interface ClickableSpan {
new (owner: Span): android.text.style.ClickableSpan;
}

let ClickableSpan: ClickableSpan;

function initializeClickableSpan(): void {
if (ClickableSpan) {
return;
}

class ClickableSpanImpl extends android.text.style.ClickableSpan {
owner: WeakRef<Span>;

constructor(owner: Span) {
super();
this.owner = new WeakRef(owner);
return global.__native(this);
}
onClick(view: android.view.View): void {
const owner = this.owner.get();
if (owner) {
owner._emit(Span.linkTapEvent);
}
view.clearFocus();
view.invalidate();
}
updateDrawState(tp: android.text.TextPaint): void {
// don't style as link
}
}

ClickableSpan = ClickableSpanImpl;
}

export class TextBase extends TextBaseCommon {
nativeViewProtected: android.widget.TextView;
nativeTextViewProtected: android.widget.TextView;
Expand All @@ -59,12 +94,15 @@ export class TextBase extends TextBaseCommon {
private _maxHeight: number;
private _minLines: number;
private _maxLines: number;
private _tappable: boolean = false;
private _defaultMovementMethod: android.text.method.MovementMethod;

public initNativeView(): void {
super.initNativeView();
initializeTextTransformation();
const nativeView = this.nativeTextViewProtected;
this._defaultTransformationMethod = nativeView.getTransformationMethod();
this._defaultMovementMethod = this.nativeView.getMovementMethod();
this._minHeight = nativeView.getMinHeight();
this._maxHeight = nativeView.getMaxHeight();
this._minLines = nativeView.getMinLines();
Expand Down Expand Up @@ -110,6 +148,7 @@ export class TextBase extends TextBaseCommon {
if (!reset && this.formattedText) {
return;
}
this._setTappableState(false);

this._setNativeText(reset);
}
Expand All @@ -130,6 +169,7 @@ export class TextBase extends TextBaseCommon {

const spannableStringBuilder = createSpannableStringBuilder(value);
nativeView.setText(<any>spannableStringBuilder);
this._setTappableState(isStringTappable(value));

textProperty.nativeValueChange(this, (value === null || value === undefined) ? "" : value.toString());

Expand Down Expand Up @@ -312,6 +352,19 @@ export class TextBase extends TextBaseCommon {

this.nativeTextViewProtected.setText(<any>transformedText);
}

_setTappableState(tappable: boolean) {
if (this._tappable !== tappable) {
this._tappable = tappable;
if (this._tappable) {
this.nativeViewProtected.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
this.nativeViewProtected.setHighlightColor(null);
}
else {
this.nativeViewProtected.setMovementMethod(this._defaultMovementMethod);
}
}
}
}

function getCapitalizedString(str: string): string {
Expand Down Expand Up @@ -343,6 +396,19 @@ export function getTransformedText(text: string, textTransform: TextTransform):
}
}

function isStringTappable(formattedString: FormattedString) {
if (!formattedString) {
return false;
}
for (let i = 0, length = formattedString.spans.length; i < length; i++) {
const span = formattedString.spans.getItem(i);
if (span.tappable) {
return true;
}
}
return false;
}

function createSpannableStringBuilder(formattedString: FormattedString): android.text.SpannableStringBuilder {
if (!formattedString || !formattedString.parent) {
return null;
Expand Down Expand Up @@ -441,6 +507,12 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span,
}
}

const tappable = span.tappable;
if (tappable) {
initializeClickableSpan();
ssb.setSpan(new ClickableSpan(span), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

// TODO: Implement letterSpacing for Span here.
// const letterSpacing = formattedString.parent.style.letterSpacing;
// if (letterSpacing > 0) {
Expand Down