Commit 2a68552
committed
feat(ios): honor textDecorationStyle on Text decorations
`textDecorationStyle` is declared on `TextStyleIOS` in the public types
but `wavy` is silently dropped (Fabric's C++ enum doesn't include
`Wavy`, and UIKit's `NSUnderlineStyle` has no native wavy pattern bit).
This PR closes the gap by adding `TextDecorationStyle::Wavy` to the
shared Fabric primitives / conversions and rendering wavy / dotted /
dashed decorations with custom Core Graphics paths instead of UIKit
pattern bits.
Implementation:
- Wavy ranges are tagged with a custom
`RCTCustomDecorationAttributeName` (storing the line kinds, stroke
color, and style key) in `RCTAttributedTextUtils.mm` and painted by
`RCTTextLayoutManager.mm` after `drawGlyphsForGlyphRange:`.
- Wavy uses an adaptation of WebKit's formula from
`Source/WebCore/style/InlineTextBoxStyle.cpp`
(`controlPointDistance = thickness * 1.5 + 0.5`, one cubic Bezier
per wavelength, control points at the midpoint above and below the
y-axis). At iOS point sizes the literal Blink amplitude renders as
a very pronounced wave because Core Graphics paints in points (not
device pixels), so the constants are dialed back to read as a
clear-but-subtle browser-style wave at typical text sizes.
- Dotted uses a custom CG path with a zero-length dash + round line
caps, producing actual circular dots at `2 * thickness` spacing
(UIKit's `NSUnderlineStylePatternDot` does not match browser
geometry on iOS).
- Dashed uses a custom CG path with `[2 * thickness, thickness]`
intervals — short rectangular dashes with a tight gap, closer to
Safari's geometry than UIKit's default `NSUnderlineStylePatternDash`.
- Solid and double continue to use UIKit's native `NSUnderlineStyle`
pattern bits.
- The wavy 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).
The shared C++ enum addition unblocks the same value on Android (see
companion PR).
## Changelog:
[IOS] [ADDED] - `textDecorationStyle: 'wavy'` for `<Text>` (custom CG path)
[IOS] [CHANGED] - `textDecorationStyle: 'dotted'` and `'dashed'` for `<Text>` render with custom CoreGraphics paths instead of UIKit pattern bits, matching browser geometry more closely
## Test Plan:
Side-by-side comparison on iPhone 17 sim (iOS 26.4) of a `<Text>` with
`textDecorationLine="underline"` and `textDecorationStyle` cycling
through `solid` / `double` / `dotted` / `dashed` / `wavy`, verified
against Safari rendering of the same CSS. Trailing periods now fall
under the wavy stroke. Verified with `textDecorationColor` set
distinct from the foreground color.
```tsx
<Text style={{
color: 'black',
textDecorationLine: 'underline',
textDecorationStyle: 'wavy',
textDecorationColor: '#ff00aa',
}}>
Hello
</Text>
```1 parent e2e6553 commit 2a68552
6 files changed
Lines changed: 197 additions & 20 deletions
File tree
- packages/react-native/ReactCommon/react/renderer
- attributedstring
- textlayoutmanager/platform/ios/react/renderer/textlayoutmanager
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
916 | 916 | | |
917 | 917 | | |
918 | 918 | | |
| 919 | + | |
| 920 | + | |
919 | 921 | | |
920 | 922 | | |
921 | 923 | | |
| |||
941 | 943 | | |
942 | 944 | | |
943 | 945 | | |
| 946 | + | |
| 947 | + | |
944 | 948 | | |
945 | 949 | | |
946 | 950 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
134 | 134 | | |
135 | 135 | | |
136 | 136 | | |
137 | | - | |
| 137 | + | |
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
| |||
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
22 | 33 | | |
23 | 34 | | |
24 | 35 | | |
| |||
Lines changed: 40 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
240 | 240 | | |
241 | 241 | | |
242 | 242 | | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
| 243 | + | |
247 | 244 | | |
248 | 245 | | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
253 | 275 | | |
254 | | - | |
255 | | - | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
256 | 279 | | |
257 | | - | |
258 | 280 | | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
263 | 285 | | |
264 | | - | |
265 | | - | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
266 | 289 | | |
267 | 290 | | |
268 | 291 | | |
| |||
Lines changed: 131 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
98 | 229 | | |
99 | 230 | | |
100 | 231 | | |
| |||
Lines changed: 10 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
106 | 106 | | |
107 | 107 | | |
108 | 108 | | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
109 | 115 | | |
110 | | - | |
| 116 | + | |
111 | 117 | | |
112 | | - | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
113 | 121 | | |
114 | 122 | | |
115 | 123 | | |
| |||
0 commit comments