1
1
// Types
2
- import { TextTransformation , TextDecoration , TextAlignment , TextTransform , WhiteSpace } from "./text-base-common" ;
2
+ import { TextTransformation , TextDecoration , TextAlignment , TextTransform , WhiteSpace , getClosestPropertyValue } from "./text-base-common" ;
3
3
4
4
// Requires
5
5
import { Font } from "../styling/font" ;
@@ -33,7 +33,7 @@ function initializeTextTransformation(): void {
33
33
// NOTE: Do we need to transform the new text here?
34
34
const formattedText = this . textBase . formattedText ;
35
35
if ( formattedText ) {
36
- return createSpannableStringBuilder ( formattedText ) ;
36
+ return createSpannableStringBuilder ( formattedText , ( < android . widget . TextView > view ) . getTextSize ( ) ) ;
37
37
}
38
38
else {
39
39
const text = this . textBase . text ;
@@ -170,7 +170,7 @@ export class TextBase extends TextBaseCommon {
170
170
return ;
171
171
}
172
172
173
- const spannableStringBuilder = createSpannableStringBuilder ( value ) ;
173
+ const spannableStringBuilder = createSpannableStringBuilder ( value , this . style . fontSize ) ;
174
174
nativeView . setText ( < any > spannableStringBuilder ) ;
175
175
this . _setTappableState ( isStringTappable ( value ) ) ;
176
176
@@ -265,10 +265,11 @@ export class TextBase extends TextBaseCommon {
265
265
}
266
266
267
267
[ lineHeightProperty . getDefault ] ( ) : number {
268
- return this . nativeTextViewProtected . getLineSpacingExtra ( ) / layout . getDisplayDensity ( ) ;
268
+ return this . nativeTextViewProtected . getLineHeight ( ) / layout . getDisplayDensity ( ) ;
269
269
}
270
270
[ lineHeightProperty . setNative ] ( value : number ) {
271
- this . nativeTextViewProtected . setLineSpacing ( value * layout . getDisplayDensity ( ) , 1 ) ;
271
+ const fontHeight = this . nativeTextViewProtected . getPaint ( ) . getFontMetricsInt ( null ) ;
272
+ this . nativeTextViewProtected . setLineSpacing ( Math . max ( value - fontHeight , 0 ) * layout . getDisplayDensity ( ) , 1 ) ;
272
273
}
273
274
274
275
[ fontInternalProperty . getDefault ] ( ) : android . graphics . Typeface {
@@ -348,7 +349,7 @@ export class TextBase extends TextBaseCommon {
348
349
349
350
let transformedText : any ;
350
351
if ( this . formattedText ) {
351
- transformedText = createSpannableStringBuilder ( this . formattedText ) ;
352
+ transformedText = createSpannableStringBuilder ( this . formattedText , this . style . fontSize ) ;
352
353
} else {
353
354
const text = this . text ;
354
355
const stringValue = ( text === null || text === undefined ) ? "" : text . toString ( ) ;
@@ -415,7 +416,7 @@ function isStringTappable(formattedString: FormattedString) {
415
416
return false ;
416
417
}
417
418
418
- function createSpannableStringBuilder ( formattedString : FormattedString ) : android . text . SpannableStringBuilder {
419
+ function createSpannableStringBuilder ( formattedString : FormattedString , defaultFontSize : number ) : android . text . SpannableStringBuilder {
419
420
if ( ! formattedString || ! formattedString . parent ) {
420
421
return null ;
421
422
}
@@ -433,18 +434,75 @@ function createSpannableStringBuilder(formattedString: FormattedString): android
433
434
spanLength = spanText . length ;
434
435
if ( spanLength > 0 ) {
435
436
ssb . insert ( spanStart , spanText ) ;
436
- setSpanModifiers ( ssb , span , spanStart , spanStart + spanLength ) ;
437
+ setSpanModifiers ( ssb , span , spanStart , spanStart + spanLength , defaultFontSize ) ;
437
438
spanStart += spanLength ;
438
439
}
439
440
}
440
441
441
442
return ssb ;
442
443
}
443
444
444
- function setSpanModifiers ( ssb : android . text . SpannableStringBuilder , span : Span , start : number , end : number ) : void {
445
+ class BaselineAdjustedSpan extends android . text . style . MetricAffectingSpan {
446
+ fontSize : number ;
447
+ align : string | number = "baseline" ;
448
+
449
+ constructor ( fontSize : number , align ?: string | number ) {
450
+ super ( ) ;
451
+
452
+ this . align = align ;
453
+ this . fontSize = fontSize ;
454
+ }
455
+
456
+ updateDrawState ( paint : android . text . TextPaint ) {
457
+ this . updateState ( paint ) ;
458
+ }
459
+
460
+ updateMeasureState ( paint : android . text . TextPaint ) {
461
+ this . updateState ( paint ) ;
462
+ }
463
+
464
+ updateState ( paint : android . text . TextPaint ) {
465
+ const metrics = paint . getFontMetrics ( ) ;
466
+
467
+ if ( ! this . align || this . align === "baseline" ) {
468
+ return ;
469
+ }
470
+
471
+ if ( this . align === "top" ) {
472
+ return paint . baselineShift = - this . fontSize - metrics . bottom - metrics . top ;
473
+ }
474
+
475
+ if ( this . align === "bottom" ) {
476
+ return paint . baselineShift = metrics . bottom ;
477
+ }
478
+
479
+ if ( this . align === "text-top" ) {
480
+ return paint . baselineShift = - this . fontSize - metrics . descent - metrics . ascent ;
481
+ }
482
+
483
+ if ( this . align === "text-bottom" ) {
484
+ return paint . baselineShift = metrics . bottom - metrics . descent ;
485
+ }
486
+
487
+ if ( this . align === "middle" ) {
488
+ return paint . baselineShift = ( metrics . descent - metrics . ascent ) / 2 - metrics . descent ;
489
+ }
490
+
491
+ if ( this . align === "super" ) {
492
+ return paint . baselineShift = - this . fontSize * .4 ;
493
+ }
494
+
495
+ if ( this . align === "sub" ) {
496
+ return paint . baselineShift = ( metrics . descent - metrics . ascent ) * .4 ;
497
+ }
498
+ }
499
+ }
500
+
501
+ function setSpanModifiers ( ssb : android . text . SpannableStringBuilder , span : Span , start : number , end : number , defaultFontSize : number ) : void {
445
502
const spanStyle = span . style ;
446
503
const bold = isBold ( spanStyle . fontWeight ) ;
447
504
const italic = spanStyle . fontStyle === "italic" ;
505
+ const align = spanStyle . verticalAlignment ;
448
506
449
507
if ( bold && italic ) {
450
508
ssb . setSpan ( new android . text . style . StyleSpan ( android . graphics . Typeface . BOLD_ITALIC ) , start , end , android . text . Spanned . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
@@ -474,45 +532,30 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span,
474
532
ssb . setSpan ( new android . text . style . ForegroundColorSpan ( color . android ) , start , end , android . text . Spanned . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
475
533
}
476
534
477
- let backgroundColor : Color ;
478
- if ( backgroundColorProperty . isSet ( spanStyle ) ) {
479
- backgroundColor = spanStyle . backgroundColor ;
480
- } else if ( backgroundColorProperty . isSet ( span . parent . style ) ) {
481
- // parent is FormattedString
482
- backgroundColor = span . parent . style . backgroundColor ;
483
- } else if ( backgroundColorProperty . isSet ( span . parent . parent . style ) ) {
484
- // parent.parent is TextBase
485
- backgroundColor = span . parent . parent . style . backgroundColor ;
486
- }
535
+ const backgroundColor : Color = getClosestPropertyValue ( backgroundColorProperty , span ) ;
487
536
488
537
if ( backgroundColor ) {
489
538
ssb . setSpan ( new android . text . style . BackgroundColorSpan ( backgroundColor . android ) , start , end , android . text . Spanned . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
490
539
}
491
540
492
- let valueSource : typeof spanStyle ;
493
- if ( textDecorationProperty . isSet ( spanStyle ) ) {
494
- valueSource = spanStyle ;
495
- } else if ( textDecorationProperty . isSet ( span . parent . style ) ) {
496
- // span.parent is FormattedString
497
- valueSource = span . parent . style ;
498
- } else if ( textDecorationProperty . isSet ( span . parent . parent . style ) ) {
499
- // span.parent.parent is TextBase
500
- valueSource = span . parent . parent . style ;
501
- }
541
+ const textDecoration : TextDecoration = getClosestPropertyValue ( textDecorationProperty , span ) ;
502
542
503
- if ( valueSource ) {
504
- const textDecorations = valueSource . textDecoration ;
505
- const underline = textDecorations . indexOf ( "underline" ) !== - 1 ;
543
+ if ( textDecoration ) {
544
+ const underline = textDecoration . indexOf ( "underline" ) !== - 1 ;
506
545
if ( underline ) {
507
546
ssb . setSpan ( new android . text . style . UnderlineSpan ( ) , start , end , android . text . Spanned . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
508
547
}
509
548
510
- const strikethrough = textDecorations . indexOf ( "line-through" ) !== - 1 ;
549
+ const strikethrough = textDecoration . indexOf ( "line-through" ) !== - 1 ;
511
550
if ( strikethrough ) {
512
551
ssb . setSpan ( new android . text . style . StrikethroughSpan ( ) , start , end , android . text . Spanned . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
513
552
}
514
553
}
515
554
555
+ if ( align ) {
556
+ ssb . setSpan ( new BaselineAdjustedSpan ( defaultFontSize * layout . getDisplayDensity ( ) , align ) , start , end , android . text . Spanned . SPAN_EXCLUSIVE_EXCLUSIVE ) ;
557
+ }
558
+
516
559
const tappable = span . tappable ;
517
560
if ( tappable ) {
518
561
initializeClickableSpan ( ) ;
0 commit comments