Commit 87184c8
feat(android): honor textDecorationStyle on Text decorations (#56768)
Summary:
`textDecorationStyle` is declared on `TextStyleAndroid` in the public types but `TA_KEY_TEXT_DECORATION_STYLE` was a no-op handler: every value silently rendered as a solid line, and `wavy` was additionally rejected at the Fabric C++ enum boundary with an `Unsupported value` log. This PR wires the prop through the existing C++ → Kotlin pipeline and implements `solid`, `double`, `dotted`, `dashed`, and `wavy` for both underlines and strikethroughs.
Android's `Layout.draw` paints the underline produced by `setUnderlineText(true)` using `paint.color` only and offers no native way to draw a dotted / dashed / wavy decoration. `ReactUnderlineSpan` and `ReactStrikethroughSpan` now extend `CanvasEffectSpan` and paint the decoration themselves in `onDraw` via `Canvas.drawLine` / `Canvas.drawPath`, dispatching by style. As a side effect this also makes `textDecorationColor` reach the paint, closing the separate long-standing gap filed as #4579 in 2015 — the companion color-focused PR #56767 isolates that fix for reviewers who only want the color change.
`TextDecorationStyle::Wavy` is added to the Fabric C++ primitives / conversions so the `wavy` JS value flows through; the same enum is shared with iOS (see companion iOS PR #56769).
The wavy curve uses Chromium/Blink's formula from `decoration_line_painter.cc` (`wavelength = 1 + 2 * round(2 * thickness + 0.5)`, `controlPointDistance = 0.5 + round(3 * thickness + 0.5)`, one cubic Bezier per wavelength with both control points at the midpoint, one above and one below the y-axis). The minimum stroke thickness is density-aware (1.5 dp) so decorations read consistently across display densities. The drawing loop iterates `while x < x2` so the final cycle continues through the last character (including trailing punctuation that would otherwise be visually uncovered when the run width is not an integer multiple of the wavelength).
`ReactTextView.onDraw` invokes `CanvasEffectSpan.onDraw` after `super.onDraw`, mirroring what `PreparedLayoutTextView.onDraw` already did. Without this, the new spans have no effect on the older view class, which is what some Text components on the new architecture still route through.
Companion PRs (independent, also targeting `main`):
- #56767 — fix(android): textDecorationColor on underlines + strikethroughs. Resolves #4579.
- #56769 — feat(ios): textDecorationStyle wavy/dotted/dashed via custom CG paths. Shares the `TextDecorationStyle::Wavy` enum addition; whichever lands first leaves the other with a trivial conflict to resolve.
## Changelog:
[GENERAL] [ADDED] - `textDecorationStyle: 'wavy'` for `<Text>` (see corresponding iOS PR for the iOS counterpart)
[ANDROID] [ADDED] - Text decorations honor `textDecorationStyle` (`solid`, `double`, `dotted`, `dashed`, `wavy`)
Pull Request resolved: #56768
Test Plan:
Rendered `<Text>` components with `textDecorationLine` set to `"underline"` or `"line-through"` and `textDecorationStyle` cycling through `solid` / `double` / `dotted` / `dashed` / `wavy`. On stock 0.85.2 every value renders as a solid line and `wavy` logs an `Unsupported value` warning; with this patch each style renders with the requested stroke geometry. Verified single-line and wrapped multi-line cases on an Android API 36 emulator: each visual line within a wrapped block receives its own correctly-styled decoration that starts and ends at the line's content boundaries.
```tsx
<Text style={{
color: 'black',
textDecorationLine: 'underline',
textDecorationStyle: 'wavy',
}}>
Hello
</Text>
```
Unit tests added for `TextDecorationStyle.fromString()` covering all five styles plus null, empty, and unknown inputs. Run with `buck2 test //xplat/js/react-native-github/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views:views_text_TextDecorationStyleTestAndroid` — all 8 tests pass.
Fantom integration tests added to `Text-itest.js` verifying all five `textDecorationStyle` values propagate through the Fabric pipeline for both underline, strikethrough, and multi-line wrapped text.
Installed RNTester on a Pixel 8 Pro (Android API 36) and verified all five `textDecorationStyle` values render correctly for both underline and line-through decorations, including multi-line text with inline decoration spanning line breaks. Screenshot: https://pxl.cl/b3c4f
jest-e2e screenshot test updated with new Android screenshot hashes reflecting the updated decoration rendering.
Reviewed By: javache
Differential Revision: D104680895
Pulled By: cortinico
fbshipit-source-id: 7d057326af3809869fdd8394d51aa50ff328ed6f1 parent 62903bc commit 87184c8
25 files changed
Lines changed: 726 additions & 29 deletions
File tree
- packages
- react-native
- Libraries
- StyleSheet
- Text/__tests__
- ReactAndroid/src
- main/java/com/facebook/react/views
- textinput
- text
- internal/span
- test/java/com/facebook/react/views/text
- ReactCommon/react/renderer
- attributedstring
- textlayoutmanager/platform/ios/react/renderer/textlayoutmanager
- rn-tester/js/examples/Text
- scripts/cxx-api/api-snapshots
Lines changed: 14 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
572 | 572 | | |
573 | 573 | | |
574 | 574 | | |
575 | | - | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
576 | 582 | | |
577 | 583 | | |
578 | 584 | | |
| |||
634 | 640 | | |
635 | 641 | | |
636 | 642 | | |
637 | | - | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
638 | 650 | | |
639 | 651 | | |
640 | 652 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1018 | 1018 | | |
1019 | 1019 | | |
1020 | 1020 | | |
1021 | | - | |
| 1021 | + | |
1022 | 1022 | | |
1023 | 1023 | | |
1024 | 1024 | | |
| |||
Lines changed: 198 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
311 | 311 | | |
312 | 312 | | |
313 | 313 | | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
314 | 512 | | |
315 | 513 | | |
316 | 514 | | |
| |||
Lines changed: 35 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
| |||
212 | 213 | | |
213 | 214 | | |
214 | 215 | | |
215 | | - | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
216 | 250 | | |
217 | 251 | | |
218 | 252 | | |
| |||
Lines changed: 22 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
92 | 92 | | |
93 | 93 | | |
94 | 94 | | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
95 | 110 | | |
96 | 111 | | |
97 | 112 | | |
| |||
423 | 438 | | |
424 | 439 | | |
425 | 440 | | |
426 | | - | |
| 441 | + | |
427 | 442 | | |
428 | | - | |
| 443 | + | |
| 444 | + | |
429 | 445 | | |
430 | 446 | | |
431 | 447 | | |
| |||
486 | 502 | | |
487 | 503 | | |
488 | 504 | | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
489 | 509 | | |
490 | 510 | | |
491 | 511 | | |
| |||
0 commit comments