1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.compose.ui.text
18
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.runtime.Stable
21 import androidx.compose.ui.graphics.Brush
22 import androidx.compose.ui.graphics.Color
23 import androidx.compose.ui.graphics.Shadow
24 import androidx.compose.ui.graphics.drawscope.DrawStyle
25 import androidx.compose.ui.graphics.drawscope.Fill
26 import androidx.compose.ui.graphics.isSpecified
27 import androidx.compose.ui.graphics.lerp
28 import androidx.compose.ui.graphics.takeOrElse
29 import androidx.compose.ui.text.font.FontFamily
30 import androidx.compose.ui.text.font.FontStyle
31 import androidx.compose.ui.text.font.FontSynthesis
32 import androidx.compose.ui.text.font.FontWeight
33 import androidx.compose.ui.text.font.lerp
34 import androidx.compose.ui.text.intl.LocaleList
35 import androidx.compose.ui.text.style.BaselineShift
36 import androidx.compose.ui.text.style.TextDecoration
37 import androidx.compose.ui.text.style.TextForegroundStyle
38 import androidx.compose.ui.text.style.TextGeometricTransform
39 import androidx.compose.ui.text.style.lerp
40 import androidx.compose.ui.unit.TextUnit
41 import androidx.compose.ui.unit.isSpecified
42 import androidx.compose.ui.unit.isUnspecified
43 import androidx.compose.ui.unit.lerp
44 import androidx.compose.ui.unit.sp
45
46 /** The default font size if none is specified. */
47 private val DefaultFontSize = 14.sp
48 private val DefaultLetterSpacing = 0.sp
49 private val DefaultBackgroundColor = Color.Transparent
50 // TODO(nona): Introduce TextUnit.Original for representing "do not change the original result".
51 // Need to distinguish from Inherit.
52 private val DefaultColor = Color.Black
53 private val DefaultColorForegroundStyle = TextForegroundStyle.from(DefaultColor)
54
55 /**
56 * Styling configuration for a text span. This configuration only allows character level styling, in
57 * order to set paragraph level styling such as line height, or text alignment please see
58 * [ParagraphStyle].
59 *
60 * @sample androidx.compose.ui.text.samples.SpanStyleSample
61 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
62 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This may be
63 * [TextUnit.Unspecified] for inheriting from another [SpanStyle].
64 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
65 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
66 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight or
67 * style cannot be found in the provided font family.
68 * @param fontFamily The font family to be used when rendering the text.
69 * @param fontFeatureSettings The advanced typography settings provided by font. The format is the
70 * same as the CSS font-feature-settings attribute:
71 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
72 * @param letterSpacing The amount of space (in em) to add between each letter.
73 * @param baselineShift The amount by which the text is shifted up from the current baseline.
74 * @param textGeometricTransform The geometric transformation applied the text.
75 * @param localeList The locale list used to select region-specific glyphs.
76 * @param background The background color for the text.
77 * @param textDecoration The decorations to paint on the text (e.g., an underline).
78 * @param shadow The shadow effect applied on the text.
79 * @param platformStyle Platform specific [SpanStyle] parameters.
80 * @param drawStyle Drawing style of text, whether fill in the text while drawing or stroke around
81 * the edges.
82 * @see AnnotatedString
83 * @see TextStyle
84 * @see ParagraphStyle
85 */
86 @Immutable
87 class SpanStyle
88 internal constructor(
89 // The fill to draw text, a unified representation of Color and Brush.
90 internal val textForegroundStyle: TextForegroundStyle,
91 val fontSize: TextUnit = TextUnit.Unspecified,
92 val fontWeight: FontWeight? = null,
93 val fontStyle: FontStyle? = null,
94 val fontSynthesis: FontSynthesis? = null,
95 val fontFamily: FontFamily? = null,
96 val fontFeatureSettings: String? = null,
97 val letterSpacing: TextUnit = TextUnit.Unspecified,
98 val baselineShift: BaselineShift? = null,
99 val textGeometricTransform: TextGeometricTransform? = null,
100 val localeList: LocaleList? = null,
101 val background: Color = Color.Unspecified,
102 val textDecoration: TextDecoration? = null,
103 val shadow: Shadow? = null,
104 val platformStyle: PlatformSpanStyle? = null,
105 val drawStyle: DrawStyle? = null
106 ) : AnnotatedString.Annotation {
107
108 /**
109 * Styling configuration for a text span. This configuration only allows character level
110 * styling, in order to set paragraph level styling such as line height, or text alignment
111 * please see [ParagraphStyle].
112 *
113 * @sample androidx.compose.ui.text.samples.SpanStyleSample
114 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
115 * @param color The text color.
116 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
117 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
118 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
119 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
120 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
121 * or style cannot be found in the provided font family.
122 * @param fontFamily The font family to be used when rendering the text.
123 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
124 * the same as the CSS font-feature-settings attribute:
125 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
126 * @param letterSpacing The amount of space (in em) to add between each letter.
127 * @param baselineShift The amount by which the text is shifted up from the current baseline.
128 * @param textGeometricTransform The geometric transformation applied the text.
129 * @param localeList The locale list used to select region-specific glyphs.
130 * @param background The background color for the text.
131 * @param textDecoration The decorations to paint on the text (e.g., an underline).
132 * @param shadow The shadow effect applied on the text.
133 * @see AnnotatedString
134 * @see TextStyle
135 * @see ParagraphStyle
136 */
137 @Deprecated(
138 "SpanStyle constructors that do not take new stable parameters " +
139 "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
140 "constructor.",
141 level = DeprecationLevel.HIDDEN
142 )
143 constructor(
144 color: Color = Color.Unspecified,
145 fontSize: TextUnit = TextUnit.Unspecified,
146 fontWeight: FontWeight? = null,
147 fontStyle: FontStyle? = null,
148 fontSynthesis: FontSynthesis? = null,
149 fontFamily: FontFamily? = null,
150 fontFeatureSettings: String? = null,
151 letterSpacing: TextUnit = TextUnit.Unspecified,
152 baselineShift: BaselineShift? = null,
153 textGeometricTransform: TextGeometricTransform? = null,
154 localeList: LocaleList? = null,
155 background: Color = Color.Unspecified,
156 textDecoration: TextDecoration? = null,
157 shadow: Shadow? = null
158 ) : this(
159 textForegroundStyle = TextForegroundStyle.from(color),
160 fontSize = fontSize,
161 fontWeight = fontWeight,
162 fontStyle = fontStyle,
163 fontSynthesis = fontSynthesis,
164 fontFamily = fontFamily,
165 fontFeatureSettings = fontFeatureSettings,
166 letterSpacing = letterSpacing,
167 baselineShift = baselineShift,
168 textGeometricTransform = textGeometricTransform,
169 localeList = localeList,
170 background = background,
171 textDecoration = textDecoration,
172 shadow = shadow,
173 platformStyle = null
174 )
175
176 /**
177 * Styling configuration for a text span. This configuration only allows character level
178 * styling, in order to set paragraph level styling such as line height, or text alignment
179 * please see [ParagraphStyle].
180 *
181 * @sample androidx.compose.ui.text.samples.SpanStyleSample
182 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
183 * @param color The color to draw the text.
184 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
185 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
186 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
187 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
188 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
189 * or style cannot be found in the provided font family.
190 * @param fontFamily The font family to be used when rendering the text.
191 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
192 * the same as the CSS font-feature-settings attribute:
193 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
194 * @param letterSpacing The amount of space (in em) to add between each letter.
195 * @param baselineShift The amount by which the text is shifted up from the current baseline.
196 * @param textGeometricTransform The geometric transformation applied the text.
197 * @param localeList The locale list used to select region-specific glyphs.
198 * @param background The background color for the text.
199 * @param textDecoration The decorations to paint on the text (e.g., an underline).
200 * @param shadow The shadow effect applied on the text.
201 * @param platformStyle Platform specific [SpanStyle] parameters.
202 * @see AnnotatedString
203 * @see TextStyle
204 * @see ParagraphStyle
205 */
206 @Deprecated(
207 "SpanStyle constructors that do not take new stable parameters " +
208 "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
209 "constructor.",
210 level = DeprecationLevel.HIDDEN
211 )
212 constructor(
213 color: Color = Color.Unspecified,
214 fontSize: TextUnit = TextUnit.Unspecified,
215 fontWeight: FontWeight? = null,
216 fontStyle: FontStyle? = null,
217 fontSynthesis: FontSynthesis? = null,
218 fontFamily: FontFamily? = null,
219 fontFeatureSettings: String? = null,
220 letterSpacing: TextUnit = TextUnit.Unspecified,
221 baselineShift: BaselineShift? = null,
222 textGeometricTransform: TextGeometricTransform? = null,
223 localeList: LocaleList? = null,
224 background: Color = Color.Unspecified,
225 textDecoration: TextDecoration? = null,
226 shadow: Shadow? = null,
227 platformStyle: PlatformSpanStyle? = null
228 ) : this(
229 textForegroundStyle = TextForegroundStyle.from(color),
230 fontSize = fontSize,
231 fontWeight = fontWeight,
232 fontStyle = fontStyle,
233 fontSynthesis = fontSynthesis,
234 fontFamily = fontFamily,
235 fontFeatureSettings = fontFeatureSettings,
236 letterSpacing = letterSpacing,
237 baselineShift = baselineShift,
238 textGeometricTransform = textGeometricTransform,
239 localeList = localeList,
240 background = background,
241 textDecoration = textDecoration,
242 shadow = shadow,
243 platformStyle = platformStyle
244 )
245
246 /**
247 * Styling configuration for a text span. This configuration only allows character level
248 * styling, in order to set paragraph level styling such as line height, or text alignment
249 * please see [ParagraphStyle].
250 *
251 * @sample androidx.compose.ui.text.samples.SpanStyleSample
252 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
253 * @param color The color to draw the text.
254 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
255 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
256 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
257 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
258 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
259 * or style cannot be found in the provided font family.
260 * @param fontFamily The font family to be used when rendering the text.
261 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
262 * the same as the CSS font-feature-settings attribute:
263 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
264 * @param letterSpacing The amount of space (in em) to add between each letter.
265 * @param baselineShift The amount by which the text is shifted up from the current baseline.
266 * @param textGeometricTransform The geometric transformation applied the text.
267 * @param localeList The locale list used to select region-specific glyphs.
268 * @param background The background color for the text.
269 * @param textDecoration The decorations to paint on the text (e.g., an underline).
270 * @param shadow The shadow effect applied on the text.
271 * @param platformStyle Platform specific [SpanStyle] parameters.
272 * @param drawStyle Drawing style of text, whether fill in the text while drawing or stroke
273 * around the edges.
274 * @see AnnotatedString
275 * @see TextStyle
276 * @see ParagraphStyle
277 */
278 constructor(
279 color: Color = Color.Unspecified,
280 fontSize: TextUnit = TextUnit.Unspecified,
281 fontWeight: FontWeight? = null,
282 fontStyle: FontStyle? = null,
283 fontSynthesis: FontSynthesis? = null,
284 fontFamily: FontFamily? = null,
285 fontFeatureSettings: String? = null,
286 letterSpacing: TextUnit = TextUnit.Unspecified,
287 baselineShift: BaselineShift? = null,
288 textGeometricTransform: TextGeometricTransform? = null,
289 localeList: LocaleList? = null,
290 background: Color = Color.Unspecified,
291 textDecoration: TextDecoration? = null,
292 shadow: Shadow? = null,
293 platformStyle: PlatformSpanStyle? = null,
294 drawStyle: DrawStyle? = null
295 ) : this(
296 textForegroundStyle = TextForegroundStyle.from(color),
297 fontSize = fontSize,
298 fontWeight = fontWeight,
299 fontStyle = fontStyle,
300 fontSynthesis = fontSynthesis,
301 fontFamily = fontFamily,
302 fontFeatureSettings = fontFeatureSettings,
303 letterSpacing = letterSpacing,
304 baselineShift = baselineShift,
305 textGeometricTransform = textGeometricTransform,
306 localeList = localeList,
307 background = background,
308 textDecoration = textDecoration,
309 shadow = shadow,
310 platformStyle = platformStyle,
311 drawStyle = drawStyle
312 )
313
314 /**
315 * Styling configuration for a text span. This configuration only allows character level
316 * styling, in order to set paragraph level styling such as line height, or text alignment
317 * please see [ParagraphStyle].
318 *
319 * @sample androidx.compose.ui.text.samples.SpanStyleBrushSample
320 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
321 * @param brush The brush to use when painting the text. If brush is given as null, it will be
322 * treated as unspecified. It is equivalent to calling the alternative color constructor with
323 * [Color.Unspecified]
324 * @param alpha Opacity to be applied to [brush] from 0.0f to 1.0f representing fully
325 * transparent to fully opaque respectively.
326 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
327 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
328 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
329 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
330 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
331 * or style cannot be found in the provided font family.
332 * @param fontFamily The font family to be used when rendering the text.
333 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
334 * the same as the CSS font-feature-settings attribute:
335 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
336 * @param letterSpacing The amount of space (in em) to add between each letter.
337 * @param baselineShift The amount by which the text is shifted up from the current baseline.
338 * @param textGeometricTransform The geometric transformation applied the text.
339 * @param localeList The locale list used to select region-specific glyphs.
340 * @param background The background color for the text.
341 * @param textDecoration The decorations to paint on the text (e.g., an underline).
342 * @param shadow The shadow effect applied on the text.
343 * @param platformStyle Platform specific [SpanStyle] parameters.
344 * @param drawStyle Drawing style of text, whether fill in the text while drawing or stroke
345 * around the edges.
346 * @see AnnotatedString
347 * @see TextStyle
348 * @see ParagraphStyle
349 */
350 constructor(
351 brush: Brush?,
352 alpha: Float = Float.NaN,
353 fontSize: TextUnit = TextUnit.Unspecified,
354 fontWeight: FontWeight? = null,
355 fontStyle: FontStyle? = null,
356 fontSynthesis: FontSynthesis? = null,
357 fontFamily: FontFamily? = null,
358 fontFeatureSettings: String? = null,
359 letterSpacing: TextUnit = TextUnit.Unspecified,
360 baselineShift: BaselineShift? = null,
361 textGeometricTransform: TextGeometricTransform? = null,
362 localeList: LocaleList? = null,
363 background: Color = Color.Unspecified,
364 textDecoration: TextDecoration? = null,
365 shadow: Shadow? = null,
366 platformStyle: PlatformSpanStyle? = null,
367 drawStyle: DrawStyle? = null
368 ) : this(
369 textForegroundStyle = TextForegroundStyle.from(brush, alpha),
370 fontSize = fontSize,
371 fontWeight = fontWeight,
372 fontStyle = fontStyle,
373 fontSynthesis = fontSynthesis,
374 fontFamily = fontFamily,
375 fontFeatureSettings = fontFeatureSettings,
376 letterSpacing = letterSpacing,
377 baselineShift = baselineShift,
378 textGeometricTransform = textGeometricTransform,
379 localeList = localeList,
380 background = background,
381 textDecoration = textDecoration,
382 shadow = shadow,
383 platformStyle = platformStyle,
384 drawStyle = drawStyle
385 )
386
387 /** Color to draw text. */
388 val color: Color
389 get() = this.textForegroundStyle.color
390
391 /** Brush to draw text. If not null, overrides [color]. */
392 val brush: Brush?
393 get() = this.textForegroundStyle.brush
394
395 /**
396 * Opacity of text. This value is either provided along side Brush, or via alpha channel in
397 * color.
398 */
399 val alpha: Float
400 get() = this.textForegroundStyle.alpha
401
402 /**
403 * Returns a new span style that is a combination of this style and the given [other] style.
404 *
405 * [other] span style's null or inherit properties are replaced with the non-null properties of
406 * this span style. Another way to think of it is that the "missing" properties of the [other]
407 * style are _filled_ by the properties of this style.
408 *
409 * If the given span style is null, returns this span style.
410 */
411 @Stable
mergenull412 fun merge(other: SpanStyle? = null): SpanStyle {
413 if (other == null) return this
414 return fastMerge(
415 color = other.textForegroundStyle.color,
416 brush = other.textForegroundStyle.brush,
417 alpha = other.textForegroundStyle.alpha,
418 fontSize = other.fontSize,
419 fontWeight = other.fontWeight,
420 fontStyle = other.fontStyle,
421 fontSynthesis = other.fontSynthesis,
422 fontFamily = other.fontFamily,
423 fontFeatureSettings = other.fontFeatureSettings,
424 letterSpacing = other.letterSpacing,
425 baselineShift = other.baselineShift,
426 textGeometricTransform = other.textGeometricTransform,
427 localeList = other.localeList,
428 background = other.background,
429 textDecoration = other.textDecoration,
430 shadow = other.shadow,
431 platformStyle = other.platformStyle,
432 drawStyle = other.drawStyle
433 )
434 }
435
436 /** Plus operator overload that applies a [merge]. */
plusnull437 @Stable operator fun plus(other: SpanStyle): SpanStyle = this.merge(other)
438
439 @Deprecated(
440 "SpanStyle copy constructors that do not take new stable parameters " +
441 "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
442 "copy constructor.",
443 level = DeprecationLevel.HIDDEN
444 )
445 fun copy(
446 color: Color = this.color,
447 fontSize: TextUnit = this.fontSize,
448 fontWeight: FontWeight? = this.fontWeight,
449 fontStyle: FontStyle? = this.fontStyle,
450 fontSynthesis: FontSynthesis? = this.fontSynthesis,
451 fontFamily: FontFamily? = this.fontFamily,
452 fontFeatureSettings: String? = this.fontFeatureSettings,
453 letterSpacing: TextUnit = this.letterSpacing,
454 baselineShift: BaselineShift? = this.baselineShift,
455 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
456 localeList: LocaleList? = this.localeList,
457 background: Color = this.background,
458 textDecoration: TextDecoration? = this.textDecoration,
459 shadow: Shadow? = this.shadow
460 ): SpanStyle {
461 return SpanStyle(
462 textForegroundStyle =
463 if (color == this.color) {
464 textForegroundStyle
465 } else {
466 TextForegroundStyle.from(color)
467 },
468 fontSize = fontSize,
469 fontWeight = fontWeight,
470 fontStyle = fontStyle,
471 fontSynthesis = fontSynthesis,
472 fontFamily = fontFamily,
473 fontFeatureSettings = fontFeatureSettings,
474 letterSpacing = letterSpacing,
475 baselineShift = baselineShift,
476 textGeometricTransform = textGeometricTransform,
477 localeList = localeList,
478 background = background,
479 textDecoration = textDecoration,
480 shadow = shadow,
481 platformStyle = this.platformStyle,
482 drawStyle = this.drawStyle
483 )
484 }
485
486 @Deprecated(
487 "SpanStyle copy constructors that do not take new stable parameters " +
488 "like PlatformStyle, DrawStyle are deprecated. Please use the new stable " +
489 "copy constructor.",
490 level = DeprecationLevel.HIDDEN
491 )
copynull492 fun copy(
493 color: Color = this.color,
494 fontSize: TextUnit = this.fontSize,
495 fontWeight: FontWeight? = this.fontWeight,
496 fontStyle: FontStyle? = this.fontStyle,
497 fontSynthesis: FontSynthesis? = this.fontSynthesis,
498 fontFamily: FontFamily? = this.fontFamily,
499 fontFeatureSettings: String? = this.fontFeatureSettings,
500 letterSpacing: TextUnit = this.letterSpacing,
501 baselineShift: BaselineShift? = this.baselineShift,
502 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
503 localeList: LocaleList? = this.localeList,
504 background: Color = this.background,
505 textDecoration: TextDecoration? = this.textDecoration,
506 shadow: Shadow? = this.shadow,
507 platformStyle: PlatformSpanStyle? = this.platformStyle
508 ): SpanStyle {
509 return SpanStyle(
510 textForegroundStyle =
511 if (color == this.color) {
512 textForegroundStyle
513 } else {
514 TextForegroundStyle.from(color)
515 },
516 fontSize = fontSize,
517 fontWeight = fontWeight,
518 fontStyle = fontStyle,
519 fontSynthesis = fontSynthesis,
520 fontFamily = fontFamily,
521 fontFeatureSettings = fontFeatureSettings,
522 letterSpacing = letterSpacing,
523 baselineShift = baselineShift,
524 textGeometricTransform = textGeometricTransform,
525 localeList = localeList,
526 background = background,
527 textDecoration = textDecoration,
528 shadow = shadow,
529 platformStyle = platformStyle
530 )
531 }
532
copynull533 fun copy(
534 color: Color = this.color,
535 fontSize: TextUnit = this.fontSize,
536 fontWeight: FontWeight? = this.fontWeight,
537 fontStyle: FontStyle? = this.fontStyle,
538 fontSynthesis: FontSynthesis? = this.fontSynthesis,
539 fontFamily: FontFamily? = this.fontFamily,
540 fontFeatureSettings: String? = this.fontFeatureSettings,
541 letterSpacing: TextUnit = this.letterSpacing,
542 baselineShift: BaselineShift? = this.baselineShift,
543 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
544 localeList: LocaleList? = this.localeList,
545 background: Color = this.background,
546 textDecoration: TextDecoration? = this.textDecoration,
547 shadow: Shadow? = this.shadow,
548 platformStyle: PlatformSpanStyle? = this.platformStyle,
549 drawStyle: DrawStyle? = this.drawStyle
550 ): SpanStyle {
551 return SpanStyle(
552 textForegroundStyle =
553 if (color == this.color) {
554 textForegroundStyle
555 } else {
556 TextForegroundStyle.from(color)
557 },
558 fontSize = fontSize,
559 fontWeight = fontWeight,
560 fontStyle = fontStyle,
561 fontSynthesis = fontSynthesis,
562 fontFamily = fontFamily,
563 fontFeatureSettings = fontFeatureSettings,
564 letterSpacing = letterSpacing,
565 baselineShift = baselineShift,
566 textGeometricTransform = textGeometricTransform,
567 localeList = localeList,
568 background = background,
569 textDecoration = textDecoration,
570 shadow = shadow,
571 platformStyle = platformStyle,
572 drawStyle = drawStyle
573 )
574 }
575
copynull576 fun copy(
577 brush: Brush?,
578 alpha: Float = this.alpha,
579 fontSize: TextUnit = this.fontSize,
580 fontWeight: FontWeight? = this.fontWeight,
581 fontStyle: FontStyle? = this.fontStyle,
582 fontSynthesis: FontSynthesis? = this.fontSynthesis,
583 fontFamily: FontFamily? = this.fontFamily,
584 fontFeatureSettings: String? = this.fontFeatureSettings,
585 letterSpacing: TextUnit = this.letterSpacing,
586 baselineShift: BaselineShift? = this.baselineShift,
587 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
588 localeList: LocaleList? = this.localeList,
589 background: Color = this.background,
590 textDecoration: TextDecoration? = this.textDecoration,
591 shadow: Shadow? = this.shadow,
592 platformStyle: PlatformSpanStyle? = this.platformStyle,
593 drawStyle: DrawStyle? = this.drawStyle
594 ): SpanStyle {
595 return SpanStyle(
596 textForegroundStyle = TextForegroundStyle.from(brush, alpha),
597 fontSize = fontSize,
598 fontWeight = fontWeight,
599 fontStyle = fontStyle,
600 fontSynthesis = fontSynthesis,
601 fontFamily = fontFamily,
602 fontFeatureSettings = fontFeatureSettings,
603 letterSpacing = letterSpacing,
604 baselineShift = baselineShift,
605 textGeometricTransform = textGeometricTransform,
606 localeList = localeList,
607 background = background,
608 textDecoration = textDecoration,
609 shadow = shadow,
610 platformStyle = platformStyle,
611 drawStyle = drawStyle
612 )
613 }
614
equalsnull615 override fun equals(other: Any?): Boolean {
616 if (this === other) return true
617 if (other !is SpanStyle) return false
618 return hasSameLayoutAffectingAttributes(other) && hasSameNonLayoutAttributes(other)
619 }
620
hasSameLayoutAffectingAttributesnull621 internal fun hasSameLayoutAffectingAttributes(other: SpanStyle): Boolean {
622 if (this === other) return true
623 if (fontSize != other.fontSize) return false
624 if (fontWeight != other.fontWeight) return false
625 if (fontStyle != other.fontStyle) return false
626 if (fontSynthesis != other.fontSynthesis) return false
627 if (fontFamily != other.fontFamily) return false
628 if (fontFeatureSettings != other.fontFeatureSettings) return false
629 if (letterSpacing != other.letterSpacing) return false
630 if (baselineShift != other.baselineShift) return false
631 if (textGeometricTransform != other.textGeometricTransform) return false
632 if (localeList != other.localeList) return false
633 if (background != other.background) return false
634 if (platformStyle != other.platformStyle) return false
635 return true
636 }
637
hasSameNonLayoutAttributesnull638 internal fun hasSameNonLayoutAttributes(other: SpanStyle): Boolean {
639 if (textForegroundStyle != other.textForegroundStyle) return false
640 if (textDecoration != other.textDecoration) return false
641 if (shadow != other.shadow) return false
642 if (drawStyle != other.drawStyle) return false
643 return true
644 }
645
hashCodenull646 override fun hashCode(): Int {
647 var result = color.hashCode()
648 result = 31 * result + brush.hashCode()
649 result = 31 * result + alpha.hashCode()
650 result = 31 * result + fontSize.hashCode()
651 result = 31 * result + (fontWeight?.hashCode() ?: 0)
652 result = 31 * result + (fontStyle?.hashCode() ?: 0)
653 result = 31 * result + (fontSynthesis?.hashCode() ?: 0)
654 result = 31 * result + (fontFamily?.hashCode() ?: 0)
655 result = 31 * result + (fontFeatureSettings?.hashCode() ?: 0)
656 result = 31 * result + letterSpacing.hashCode()
657 result = 31 * result + (baselineShift?.hashCode() ?: 0)
658 result = 31 * result + (textGeometricTransform?.hashCode() ?: 0)
659 result = 31 * result + (localeList?.hashCode() ?: 0)
660 result = 31 * result + background.hashCode()
661 result = 31 * result + (textDecoration?.hashCode() ?: 0)
662 result = 31 * result + (shadow?.hashCode() ?: 0)
663 result = 31 * result + (platformStyle?.hashCode() ?: 0)
664 result = 31 * result + (drawStyle?.hashCode() ?: 0)
665 return result
666 }
667
hashCodeLayoutAffectingAttributesnull668 internal fun hashCodeLayoutAffectingAttributes(): Int {
669 var result = fontSize.hashCode()
670 result = 31 * result + (fontWeight?.hashCode() ?: 0)
671 result = 31 * result + (fontStyle?.hashCode() ?: 0)
672 result = 31 * result + (fontSynthesis?.hashCode() ?: 0)
673 result = 31 * result + (fontFamily?.hashCode() ?: 0)
674 result = 31 * result + (fontFeatureSettings?.hashCode() ?: 0)
675 result = 31 * result + letterSpacing.hashCode()
676 result = 31 * result + (baselineShift?.hashCode() ?: 0)
677 result = 31 * result + (textGeometricTransform?.hashCode() ?: 0)
678 result = 31 * result + (localeList?.hashCode() ?: 0)
679 result = 31 * result + background.hashCode()
680 result = 31 * result + (platformStyle?.hashCode() ?: 0)
681 return result
682 }
683
toStringnull684 override fun toString(): String {
685 return "SpanStyle(" +
686 "color=$color, " +
687 "brush=$brush, " +
688 "alpha=$alpha, " +
689 "fontSize=$fontSize, " +
690 "fontWeight=$fontWeight, " +
691 "fontStyle=$fontStyle, " +
692 "fontSynthesis=$fontSynthesis, " +
693 "fontFamily=$fontFamily, " +
694 "fontFeatureSettings=$fontFeatureSettings, " +
695 "letterSpacing=$letterSpacing, " +
696 "baselineShift=$baselineShift, " +
697 "textGeometricTransform=$textGeometricTransform, " +
698 "localeList=$localeList, " +
699 "background=$background, " +
700 "textDecoration=$textDecoration, " +
701 "shadow=$shadow, " +
702 "platformStyle=$platformStyle, " +
703 "drawStyle=$drawStyle" +
704 ")"
705 }
706 }
707
708 /**
709 * @param a An sp value. Maybe [TextUnit.Unspecified]
710 * @param b An sp value. Maybe [TextUnit.Unspecified]
711 */
lerpTextUnitInheritablenull712 internal fun lerpTextUnitInheritable(a: TextUnit, b: TextUnit, t: Float): TextUnit {
713 if (a.isUnspecified || b.isUnspecified) return lerpDiscrete(a, b, t)
714 return lerp(a, b, t)
715 }
716
717 /**
718 * Lerp between two values that cannot be transitioned. Returns [a] if [fraction] is smaller than
719 * 0.5 otherwise [b].
720 */
lerpDiscretenull721 internal fun <T> lerpDiscrete(a: T, b: T, fraction: Float): T = if (fraction < 0.5) a else b
722
723 /**
724 * Interpolate between two span styles.
725 *
726 * This will not work well if the styles don't set the same fields.
727 *
728 * The [fraction] argument represents position on the timeline, with 0.0 meaning that the
729 * interpolation has not started, returning [start] (or something equivalent to [start]), 1.0
730 * meaning that the interpolation has finished, returning [stop] (or something equivalent to
731 * [stop]), and values in between meaning that the interpolation is at the relevant point on the
732 * timeline between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and 1.0, so
733 * negative values and values greater than 1.0 are valid.
734 */
735 fun lerp(start: SpanStyle, stop: SpanStyle, fraction: Float): SpanStyle {
736 return SpanStyle(
737 textForegroundStyle = lerp(start.textForegroundStyle, stop.textForegroundStyle, fraction),
738 fontFamily = lerpDiscrete(start.fontFamily, stop.fontFamily, fraction),
739 fontSize = lerpTextUnitInheritable(start.fontSize, stop.fontSize, fraction),
740 fontWeight =
741 lerp(
742 start.fontWeight ?: FontWeight.Normal,
743 stop.fontWeight ?: FontWeight.Normal,
744 fraction
745 ),
746 fontStyle = lerpDiscrete(start.fontStyle, stop.fontStyle, fraction),
747 fontSynthesis = lerpDiscrete(start.fontSynthesis, stop.fontSynthesis, fraction),
748 fontFeatureSettings =
749 lerpDiscrete(start.fontFeatureSettings, stop.fontFeatureSettings, fraction),
750 letterSpacing = lerpTextUnitInheritable(start.letterSpacing, stop.letterSpacing, fraction),
751 baselineShift =
752 lerp(
753 start.baselineShift ?: BaselineShift(0f),
754 stop.baselineShift ?: BaselineShift(0f),
755 fraction
756 ),
757 textGeometricTransform =
758 lerp(
759 start.textGeometricTransform ?: TextGeometricTransform.None,
760 stop.textGeometricTransform ?: TextGeometricTransform.None,
761 fraction
762 ),
763 localeList = lerpDiscrete(start.localeList, stop.localeList, fraction),
764 background = lerp(start.background, stop.background, fraction),
765 textDecoration = lerpDiscrete(start.textDecoration, stop.textDecoration, fraction),
766 shadow = lerp(start.shadow ?: Shadow(), stop.shadow ?: Shadow(), fraction),
767 platformStyle = lerpPlatformStyle(start.platformStyle, stop.platformStyle, fraction),
768 drawStyle = lerpDiscrete(start.drawStyle, stop.drawStyle, fraction)
769 )
770 }
771
lerpPlatformStylenull772 private fun lerpPlatformStyle(
773 start: PlatformSpanStyle?,
774 stop: PlatformSpanStyle?,
775 fraction: Float
776 ): PlatformSpanStyle? {
777 if (start == null && stop == null) return null
778 val startNonNull = start ?: PlatformSpanStyle.Default
779 val stopNonNull = stop ?: PlatformSpanStyle.Default
780 return lerp(startNonNull, stopNonNull, fraction)
781 }
782
resolveSpanStyleDefaultsnull783 internal fun resolveSpanStyleDefaults(style: SpanStyle) =
784 SpanStyle(
785 textForegroundStyle = style.textForegroundStyle.takeOrElse { DefaultColorForegroundStyle },
786 fontSize = if (style.fontSize.isUnspecified) DefaultFontSize else style.fontSize,
787 fontWeight = style.fontWeight ?: FontWeight.Normal,
788 fontStyle = style.fontStyle ?: FontStyle.Normal,
789 fontSynthesis = style.fontSynthesis ?: FontSynthesis.All,
790 fontFamily = style.fontFamily ?: FontFamily.Default,
791 fontFeatureSettings = style.fontFeatureSettings ?: "",
792 letterSpacing =
793 if (style.letterSpacing.isUnspecified) {
794 DefaultLetterSpacing
795 } else {
796 style.letterSpacing
797 },
798 baselineShift = style.baselineShift ?: BaselineShift.None,
799 textGeometricTransform = style.textGeometricTransform ?: TextGeometricTransform.None,
800 localeList = style.localeList ?: LocaleList.current,
<lambda>null801 background = style.background.takeOrElse { DefaultBackgroundColor },
802 textDecoration = style.textDecoration ?: TextDecoration.None,
803 shadow = style.shadow ?: Shadow.None,
804 platformStyle = style.platformStyle,
805 drawStyle = style.drawStyle ?: Fill
806 )
807
fastMergenull808 internal fun SpanStyle.fastMerge(
809 color: Color,
810 brush: Brush?,
811 alpha: Float,
812 fontSize: TextUnit,
813 fontWeight: FontWeight?,
814 fontStyle: FontStyle?,
815 fontSynthesis: FontSynthesis?,
816 fontFamily: FontFamily?,
817 fontFeatureSettings: String?,
818 letterSpacing: TextUnit,
819 baselineShift: BaselineShift?,
820 textGeometricTransform: TextGeometricTransform?,
821 localeList: LocaleList?,
822 background: Color,
823 textDecoration: TextDecoration?,
824 shadow: Shadow?,
825 platformStyle: PlatformSpanStyle?,
826 drawStyle: DrawStyle?
827 ): SpanStyle {
828 // prioritize the parameters to Text in diffs here
829 /**
830 * color: Color fontSize: TextUnit fontStyle: FontStyle? fontWeight: FontWeight? fontFamily:
831 * FontFamily? letterSpacing: TextUnit textDecoration: TextDecoration? textAlign: TextAlign?
832 * lineHeight: TextUnit
833 */
834
835 // any new vals should do a pre-merge check here
836 val requiresAlloc =
837 fontSize.isSpecified && fontSize != this.fontSize ||
838 brush == null && color.isSpecified && color != textForegroundStyle.color ||
839 fontStyle != null && fontStyle != this.fontStyle ||
840 fontWeight != null && fontWeight != this.fontWeight ||
841 // ref check for font-family, since we don't want to compare lists in fast path
842 fontFamily != null && fontFamily !== this.fontFamily ||
843 letterSpacing.isSpecified && letterSpacing != this.letterSpacing ||
844 textDecoration != null && textDecoration != this.textDecoration ||
845 // then compare the remaining params, for potential non-Text merges
846 brush != textForegroundStyle.brush ||
847 brush != null && alpha != this.textForegroundStyle.alpha ||
848 fontSynthesis != null && fontSynthesis != this.fontSynthesis ||
849 fontFeatureSettings != null && fontFeatureSettings != this.fontFeatureSettings ||
850 baselineShift != null && baselineShift != this.baselineShift ||
851 textGeometricTransform != null &&
852 textGeometricTransform != this.textGeometricTransform ||
853 localeList != null && localeList != this.localeList ||
854 background.isSpecified && background != this.background ||
855 shadow != null && shadow != this.shadow ||
856 platformStyle != null && platformStyle != this.platformStyle ||
857 drawStyle != null && drawStyle != this.drawStyle
858
859 if (!requiresAlloc) {
860 // we're done
861 return this
862 }
863
864 val otherTextForegroundStyle =
865 if (brush != null) {
866 TextForegroundStyle.from(brush, alpha)
867 } else {
868 TextForegroundStyle.from(color)
869 }
870
871 return SpanStyle(
872 textForegroundStyle = textForegroundStyle.merge(otherTextForegroundStyle),
873 fontFamily = fontFamily ?: this.fontFamily,
874 fontSize = if (!fontSize.isUnspecified) fontSize else this.fontSize,
875 fontWeight = fontWeight ?: this.fontWeight,
876 fontStyle = fontStyle ?: this.fontStyle,
877 fontSynthesis = fontSynthesis ?: this.fontSynthesis,
878 fontFeatureSettings = fontFeatureSettings ?: this.fontFeatureSettings,
879 letterSpacing =
880 if (!letterSpacing.isUnspecified) {
881 letterSpacing
882 } else {
883 this.letterSpacing
884 },
885 baselineShift = baselineShift ?: this.baselineShift,
886 textGeometricTransform = textGeometricTransform ?: this.textGeometricTransform,
887 localeList = localeList ?: this.localeList,
888 background = background.takeOrElse { this.background },
889 textDecoration = textDecoration ?: this.textDecoration,
890 shadow = shadow ?: this.shadow,
891 platformStyle = mergePlatformStyle(platformStyle),
892 drawStyle = drawStyle ?: this.drawStyle
893 )
894 }
895
mergePlatformStylenull896 private fun SpanStyle.mergePlatformStyle(other: PlatformSpanStyle?): PlatformSpanStyle? {
897 if (platformStyle == null) return other
898 if (other == null) return platformStyle
899 return platformStyle.merge(other)
900 }
901