blob: a9c1504af66af123023eb2a289eb55a366daa5eb [file] [log] [blame]
Siyamed Sinirf447f2a2019-12-03 15:44:51 -08001/*
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
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010017package androidx.compose.ui.text
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080018
Louis Pullen-Freilich1f10a592020-07-24 16:35:14 +010019import androidx.compose.runtime.Immutable
20import androidx.compose.runtime.Stable
Halil Ozercan06becff2022-04-13 01:38:27 +000021import androidx.compose.ui.graphics.Brush
Louis Pullen-Freilich4dc4dac2020-07-22 14:39:14 +010022import androidx.compose.ui.graphics.Color
23import androidx.compose.ui.graphics.Shadow
24import androidx.compose.ui.graphics.lerp
George Mountc6549b92020-12-14 10:49:53 -080025import androidx.compose.ui.graphics.takeOrElse
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010026import androidx.compose.ui.text.font.FontFamily
27import androidx.compose.ui.text.font.FontStyle
28import androidx.compose.ui.text.font.FontSynthesis
29import androidx.compose.ui.text.font.FontWeight
30import androidx.compose.ui.text.font.lerp
Louis Pullen-Freilich534385a2020-08-14 14:10:12 +010031import androidx.compose.ui.text.intl.LocaleList
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010032import androidx.compose.ui.text.style.BaselineShift
33import androidx.compose.ui.text.style.TextDecoration
Halil Ozercan06e71162022-07-18 17:24:24 +010034import androidx.compose.ui.text.style.TextForegroundStyle
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010035import androidx.compose.ui.text.style.TextGeometricTransform
36import androidx.compose.ui.text.style.lerp
Louis Pullen-Freilicha7eeb102020-07-22 17:54:24 +010037import androidx.compose.ui.unit.TextUnit
George Mountcdd20c92020-12-10 20:03:46 +000038import androidx.compose.ui.unit.isUnspecified
Louis Pullen-Freilicha7eeb102020-07-22 17:54:24 +010039import androidx.compose.ui.unit.lerp
Siyamed Sinir0ac70682022-03-27 12:07:36 -070040import androidx.compose.ui.unit.sp
41
42/** The default font size if none is specified. */
43private val DefaultFontSize = 14.sp
44private val DefaultLetterSpacing = 0.sp
45private val DefaultBackgroundColor = Color.Transparent
46// TODO(nona): Introduce TextUnit.Original for representing "do not change the original result".
47// Need to distinguish from Inherit.
48private val DefaultColor = Color.Black
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080049
50/**
Siyamed Sinirfaa4ac42019-12-04 09:02:31 -080051 * Styling configuration for a text span. This configuration only allows character level styling,
52 * in order to set paragraph level styling such as line height, or text alignment please see
53 * [ParagraphStyle].
54 *
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010055 * @sample androidx.compose.ui.text.samples.SpanStyleSample
Siyamed Sinirfaa4ac42019-12-04 09:02:31 -080056 *
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010057 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080058 *
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080059 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
haoyudf2e07c2020-11-09 17:36:11 -080060 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080061 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
62 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
63 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight or
Siyamed Sinir0ac70682022-03-27 12:07:36 -070064 * style cannot be found in the provided font family.
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080065 * @param fontFamily The font family to be used when rendering the text.
66 * @param fontFeatureSettings The advanced typography settings provided by font. The format is the
67 * same as the CSS font-feature-settings attribute:
68 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
69 * @param letterSpacing The amount of space (in em) to add between each letter.
70 * @param baselineShift The amount by which the text is shifted up from the current baseline.
71 * @param textGeometricTransform The geometric transformation applied the text.
72 * @param localeList The locale list used to select region-specific glyphs.
73 * @param background The background color for the text.
Siyamed Sinir32fd1062019-12-10 10:54:47 -080074 * @param textDecoration The decorations to paint on the text (e.g., an underline).
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080075 * @param shadow The shadow effect applied on the text.
Siyamed Sinir8e2ae722022-03-10 08:23:44 -080076 * @param platformStyle Platform specific [SpanStyle] parameters.
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080077 *
Siyamed Sinirc8b01322019-12-05 09:41:00 -080078 * @see AnnotatedString
79 * @see TextStyle
80 * @see ParagraphStyle
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080081 */
82@Immutable
siyamedbd272552022-07-29 12:40:43 -070083class SpanStyle internal constructor(
Halil Ozercan06becff2022-04-13 01:38:27 +000084 // The fill to draw text, a unified representation of Color and Brush.
Halil Ozercan06e71162022-07-18 17:24:24 +010085 internal val textForegroundStyle: TextForegroundStyle,
haoyudf2e07c2020-11-09 17:36:11 -080086 val fontSize: TextUnit = TextUnit.Unspecified,
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080087 val fontWeight: FontWeight? = null,
88 val fontStyle: FontStyle? = null,
89 val fontSynthesis: FontSynthesis? = null,
90 val fontFamily: FontFamily? = null,
91 val fontFeatureSettings: String? = null,
haoyudf2e07c2020-11-09 17:36:11 -080092 val letterSpacing: TextUnit = TextUnit.Unspecified,
Siyamed Sinirf447f2a2019-12-03 15:44:51 -080093 val baselineShift: BaselineShift? = null,
94 val textGeometricTransform: TextGeometricTransform? = null,
95 val localeList: LocaleList? = null,
Nader Jawadcd46cc32020-09-30 19:03:45 -070096 val background: Color = Color.Unspecified,
Siyamed Sinir32fd1062019-12-10 10:54:47 -080097 val textDecoration: TextDecoration? = null,
Siyamed Sinir8e2ae722022-03-10 08:23:44 -080098 val shadow: Shadow? = null,
siyamedbd272552022-07-29 12:40:43 -070099 val platformStyle: PlatformSpanStyle? = null
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800100) {
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800101
Siyamed Sinir0ac70682022-03-27 12:07:36 -0700102 /**
103 * Styling configuration for a text span. This configuration only allows character level styling,
104 * in order to set paragraph level styling such as line height, or text alignment please see
105 * [ParagraphStyle].
106 *
107 * @sample androidx.compose.ui.text.samples.SpanStyleSample
108 *
109 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
110 *
111 * @param color The text color.
112 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
113 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
114 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
115 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
116 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
117 * or style cannot be found in the provided font family.
118 * @param fontFamily The font family to be used when rendering the text.
119 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
120 * the same as the CSS font-feature-settings attribute:
121 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
122 * @param letterSpacing The amount of space (in em) to add between each letter.
123 * @param baselineShift The amount by which the text is shifted up from the current baseline.
124 * @param textGeometricTransform The geometric transformation applied the text.
125 * @param localeList The locale list used to select region-specific glyphs.
126 * @param background The background color for the text.
127 * @param textDecoration The decorations to paint on the text (e.g., an underline).
128 * @param shadow The shadow effect applied on the text.
129 *
130 * @see AnnotatedString
131 * @see TextStyle
132 * @see ParagraphStyle
133 */
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800134 constructor(
135 color: Color = Color.Unspecified,
136 fontSize: TextUnit = TextUnit.Unspecified,
137 fontWeight: FontWeight? = null,
138 fontStyle: FontStyle? = null,
139 fontSynthesis: FontSynthesis? = null,
140 fontFamily: FontFamily? = null,
141 fontFeatureSettings: String? = null,
142 letterSpacing: TextUnit = TextUnit.Unspecified,
143 baselineShift: BaselineShift? = null,
144 textGeometricTransform: TextGeometricTransform? = null,
145 localeList: LocaleList? = null,
146 background: Color = Color.Unspecified,
147 textDecoration: TextDecoration? = null,
148 shadow: Shadow? = null
149 ) : this(
Halil Ozercan06e71162022-07-18 17:24:24 +0100150 textForegroundStyle = TextForegroundStyle.from(color),
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800151 fontSize = fontSize,
152 fontWeight = fontWeight,
153 fontStyle = fontStyle,
154 fontSynthesis = fontSynthesis,
155 fontFamily = fontFamily,
156 fontFeatureSettings = fontFeatureSettings,
157 letterSpacing = letterSpacing,
158 baselineShift = baselineShift,
159 textGeometricTransform = textGeometricTransform,
160 localeList = localeList,
161 background = background,
162 textDecoration = textDecoration,
163 shadow = shadow,
164 platformStyle = null
165 )
166
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800167 /**
Halil Ozercan06becff2022-04-13 01:38:27 +0000168 * Styling configuration for a text span. This configuration only allows character level styling,
169 * in order to set paragraph level styling such as line height, or text alignment please see
170 * [ParagraphStyle].
171 *
172 * @sample androidx.compose.ui.text.samples.SpanStyleSample
173 *
174 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
175 *
176 * @param color The color to draw the text.
177 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
178 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
179 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
180 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
181 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
182 * or style cannot be found in the provided font family.
183 * @param fontFamily The font family to be used when rendering the text.
184 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
185 * the same as the CSS font-feature-settings attribute:
186 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
187 * @param letterSpacing The amount of space (in em) to add between each letter.
188 * @param baselineShift The amount by which the text is shifted up from the current baseline.
189 * @param textGeometricTransform The geometric transformation applied the text.
190 * @param localeList The locale list used to select region-specific glyphs.
191 * @param background The background color for the text.
192 * @param textDecoration The decorations to paint on the text (e.g., an underline).
193 * @param shadow The shadow effect applied on the text.
194 * @param platformStyle Platform specific [SpanStyle] parameters.
195 *
196 * @see AnnotatedString
197 * @see TextStyle
198 * @see ParagraphStyle
199 */
Halil Ozercan06becff2022-04-13 01:38:27 +0000200 constructor(
201 color: Color = Color.Unspecified,
202 fontSize: TextUnit = TextUnit.Unspecified,
203 fontWeight: FontWeight? = null,
204 fontStyle: FontStyle? = null,
205 fontSynthesis: FontSynthesis? = null,
206 fontFamily: FontFamily? = null,
207 fontFeatureSettings: String? = null,
208 letterSpacing: TextUnit = TextUnit.Unspecified,
209 baselineShift: BaselineShift? = null,
210 textGeometricTransform: TextGeometricTransform? = null,
211 localeList: LocaleList? = null,
212 background: Color = Color.Unspecified,
213 textDecoration: TextDecoration? = null,
214 shadow: Shadow? = null,
215 platformStyle: PlatformSpanStyle? = null
216 ) : this(
Halil Ozercan06e71162022-07-18 17:24:24 +0100217 textForegroundStyle = TextForegroundStyle.from(color),
Halil Ozercan06becff2022-04-13 01:38:27 +0000218 fontSize = fontSize,
219 fontWeight = fontWeight,
220 fontStyle = fontStyle,
221 fontSynthesis = fontSynthesis,
222 fontFamily = fontFamily,
223 fontFeatureSettings = fontFeatureSettings,
224 letterSpacing = letterSpacing,
225 baselineShift = baselineShift,
226 textGeometricTransform = textGeometricTransform,
227 localeList = localeList,
228 background = background,
229 textDecoration = textDecoration,
230 shadow = shadow,
231 platformStyle = platformStyle
232 )
233
234 /**
235 * Styling configuration for a text span. This configuration only allows character level styling,
236 * in order to set paragraph level styling such as line height, or text alignment please see
237 * [ParagraphStyle].
238 *
Halil Ozercana8686042022-06-01 15:20:20 +0100239 * @sample androidx.compose.ui.text.samples.SpanStyleBrushSample
Halil Ozercan06becff2022-04-13 01:38:27 +0000240 *
241 * @sample androidx.compose.ui.text.samples.AnnotatedStringBuilderSample
242 *
243 * @param brush The brush to use when painting the text. If brush is given as null, it will be
244 * treated as unspecified. It is equivalent to calling the alternative color constructor with
245 * [Color.Unspecified]
Halil Ozercana8686042022-06-01 15:20:20 +0100246 * @param alpha Opacity to be applied to [brush] from 0.0f to 1.0f representing fully
247 * transparent to fully opaque respectively.
Halil Ozercan06becff2022-04-13 01:38:27 +0000248 * @param fontSize The size of glyphs (in logical pixels) to use when painting the text. This
249 * may be [TextUnit.Unspecified] for inheriting from another [SpanStyle].
250 * @param fontWeight The typeface thickness to use when painting the text (e.g., bold).
251 * @param fontStyle The typeface variant to use when drawing the letters (e.g., italic).
252 * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight
253 * or style cannot be found in the provided font family.
254 * @param fontFamily The font family to be used when rendering the text.
255 * @param fontFeatureSettings The advanced typography settings provided by font. The format is
256 * the same as the CSS font-feature-settings attribute:
257 * https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
258 * @param letterSpacing The amount of space (in em) to add between each letter.
259 * @param baselineShift The amount by which the text is shifted up from the current baseline.
260 * @param textGeometricTransform The geometric transformation applied the text.
261 * @param localeList The locale list used to select region-specific glyphs.
262 * @param background The background color for the text.
263 * @param textDecoration The decorations to paint on the text (e.g., an underline).
264 * @param shadow The shadow effect applied on the text.
265 * @param platformStyle Platform specific [SpanStyle] parameters.
266 *
267 * @see AnnotatedString
268 * @see TextStyle
269 * @see ParagraphStyle
270 */
271 @ExperimentalTextApi
272 constructor(
273 brush: Brush?,
Halil Ozercana8686042022-06-01 15:20:20 +0100274 alpha: Float = Float.NaN,
Halil Ozercan06becff2022-04-13 01:38:27 +0000275 fontSize: TextUnit = TextUnit.Unspecified,
276 fontWeight: FontWeight? = null,
277 fontStyle: FontStyle? = null,
278 fontSynthesis: FontSynthesis? = null,
279 fontFamily: FontFamily? = null,
280 fontFeatureSettings: String? = null,
281 letterSpacing: TextUnit = TextUnit.Unspecified,
282 baselineShift: BaselineShift? = null,
283 textGeometricTransform: TextGeometricTransform? = null,
284 localeList: LocaleList? = null,
285 background: Color = Color.Unspecified,
286 textDecoration: TextDecoration? = null,
287 shadow: Shadow? = null,
288 platformStyle: PlatformSpanStyle? = null
289 ) : this(
Halil Ozercan06e71162022-07-18 17:24:24 +0100290 textForegroundStyle = TextForegroundStyle.from(brush, alpha),
Halil Ozercan06becff2022-04-13 01:38:27 +0000291 fontSize = fontSize,
292 fontWeight = fontWeight,
293 fontStyle = fontStyle,
294 fontSynthesis = fontSynthesis,
295 fontFamily = fontFamily,
296 fontFeatureSettings = fontFeatureSettings,
297 letterSpacing = letterSpacing,
298 baselineShift = baselineShift,
299 textGeometricTransform = textGeometricTransform,
300 localeList = localeList,
301 background = background,
302 textDecoration = textDecoration,
303 shadow = shadow,
304 platformStyle = platformStyle
305 )
306
307 /**
308 * Color to draw text.
309 */
Halil Ozercan06e71162022-07-18 17:24:24 +0100310 val color: Color get() = this.textForegroundStyle.color
Halil Ozercan06becff2022-04-13 01:38:27 +0000311
312 /**
313 * Brush to draw text. If not null, overrides [color].
314 */
George Mountaea96e12022-06-15 16:00:57 -0700315 @ExperimentalTextApi
Halil Ozercan06becff2022-04-13 01:38:27 +0000316 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
317 @get:ExperimentalTextApi
Halil Ozercan06e71162022-07-18 17:24:24 +0100318 val brush: Brush? get() = this.textForegroundStyle.brush
Halil Ozercan06becff2022-04-13 01:38:27 +0000319
320 /**
Halil Ozercana8686042022-06-01 15:20:20 +0100321 * Opacity of text. This value is either provided along side Brush, or via alpha channel in
322 * color.
323 */
George Mountaea96e12022-06-15 16:00:57 -0700324 @ExperimentalTextApi
Halil Ozercana8686042022-06-01 15:20:20 +0100325 @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
326 @get:ExperimentalTextApi
Halil Ozercan06e71162022-07-18 17:24:24 +0100327 val alpha: Float get() = this.textForegroundStyle.alpha
Halil Ozercana8686042022-06-01 15:20:20 +0100328
329 /**
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800330 * Returns a new span style that is a combination of this style and the given [other] style.
331 *
332 * [other] span style's null or inherit properties are replaced with the non-null properties of
333 * this span style. Another way to think of it is that the "missing" properties of the [other]
334 * style are _filled_ by the properties of this style.
335 *
336 * If the given span style is null, returns this span style.
337 */
Leland Richardson47df77f2020-05-21 09:15:40 -0700338 @Stable
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800339 fun merge(other: SpanStyle? = null): SpanStyle {
340 if (other == null) return this
341
342 return SpanStyle(
Halil Ozercan06e71162022-07-18 17:24:24 +0100343 textForegroundStyle = textForegroundStyle.merge(other.textForegroundStyle),
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800344 fontFamily = other.fontFamily ?: this.fontFamily,
haoyudf2e07c2020-11-09 17:36:11 -0800345 fontSize = if (!other.fontSize.isUnspecified) other.fontSize else this.fontSize,
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800346 fontWeight = other.fontWeight ?: this.fontWeight,
347 fontStyle = other.fontStyle ?: this.fontStyle,
348 fontSynthesis = other.fontSynthesis ?: this.fontSynthesis,
349 fontFeatureSettings = other.fontFeatureSettings ?: this.fontFeatureSettings,
haoyudf2e07c2020-11-09 17:36:11 -0800350 letterSpacing = if (!other.letterSpacing.isUnspecified) {
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800351 other.letterSpacing
352 } else {
353 this.letterSpacing
354 },
355 baselineShift = other.baselineShift ?: this.baselineShift,
356 textGeometricTransform = other.textGeometricTransform ?: this.textGeometricTransform,
357 localeList = other.localeList ?: this.localeList,
George Mountc6549b92020-12-14 10:49:53 -0800358 background = other.background.takeOrElse { this.background },
Siyamed Sinir32fd1062019-12-10 10:54:47 -0800359 textDecoration = other.textDecoration ?: this.textDecoration,
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800360 shadow = other.shadow ?: this.shadow,
361 platformStyle = mergePlatformStyle(other.platformStyle)
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800362 )
363 }
Siyamed Sinirbc9d8822020-04-21 18:20:59 -0700364
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800365 private fun mergePlatformStyle(other: PlatformSpanStyle?): PlatformSpanStyle? {
366 if (platformStyle == null) return other
367 if (other == null) return platformStyle
368 return platformStyle.merge(other)
369 }
370
Siyamed Sinirbc9d8822020-04-21 18:20:59 -0700371 /**
372 * Plus operator overload that applies a [merge].
373 */
Leland Richardson47df77f2020-05-21 09:15:40 -0700374 @Stable
Siyamed Sinirbc9d8822020-04-21 18:20:59 -0700375 operator fun plus(other: SpanStyle): SpanStyle = this.merge(other)
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800376
377 fun copy(
378 color: Color = this.color,
379 fontSize: TextUnit = this.fontSize,
380 fontWeight: FontWeight? = this.fontWeight,
381 fontStyle: FontStyle? = this.fontStyle,
382 fontSynthesis: FontSynthesis? = this.fontSynthesis,
383 fontFamily: FontFamily? = this.fontFamily,
384 fontFeatureSettings: String? = this.fontFeatureSettings,
385 letterSpacing: TextUnit = this.letterSpacing,
386 baselineShift: BaselineShift? = this.baselineShift,
387 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
388 localeList: LocaleList? = this.localeList,
Andrey Rudenkob3660012021-01-18 15:01:15 +0100389 background: Color = this.background,
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800390 textDecoration: TextDecoration? = this.textDecoration,
391 shadow: Shadow? = this.shadow
392 ): SpanStyle {
393 return SpanStyle(
Halil Ozercan06e71162022-07-18 17:24:24 +0100394 textForegroundStyle = if (color == this.color) {
395 textForegroundStyle
396 } else {
397 TextForegroundStyle.from(color)
398 },
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800399 fontSize = fontSize,
400 fontWeight = fontWeight,
401 fontStyle = fontStyle,
402 fontSynthesis = fontSynthesis,
403 fontFamily = fontFamily,
404 fontFeatureSettings = fontFeatureSettings,
405 letterSpacing = letterSpacing,
406 baselineShift = baselineShift,
407 textGeometricTransform = textGeometricTransform,
408 localeList = localeList,
409 background = background,
410 textDecoration = textDecoration,
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800411 shadow = shadow,
Halil Ozercan06becff2022-04-13 01:38:27 +0000412 platformStyle = this.platformStyle
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800413 )
414 }
415
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800416 fun copy(
417 color: Color = this.color,
418 fontSize: TextUnit = this.fontSize,
419 fontWeight: FontWeight? = this.fontWeight,
420 fontStyle: FontStyle? = this.fontStyle,
421 fontSynthesis: FontSynthesis? = this.fontSynthesis,
422 fontFamily: FontFamily? = this.fontFamily,
423 fontFeatureSettings: String? = this.fontFeatureSettings,
424 letterSpacing: TextUnit = this.letterSpacing,
425 baselineShift: BaselineShift? = this.baselineShift,
426 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
427 localeList: LocaleList? = this.localeList,
428 background: Color = this.background,
429 textDecoration: TextDecoration? = this.textDecoration,
430 shadow: Shadow? = this.shadow,
431 platformStyle: PlatformSpanStyle? = this.platformStyle
432 ): SpanStyle {
433 return SpanStyle(
Halil Ozercan06e71162022-07-18 17:24:24 +0100434 textForegroundStyle = if (color == this.color) {
435 textForegroundStyle
436 } else {
437 TextForegroundStyle.from(color)
438 },
Halil Ozercan06becff2022-04-13 01:38:27 +0000439 fontSize = fontSize,
440 fontWeight = fontWeight,
441 fontStyle = fontStyle,
442 fontSynthesis = fontSynthesis,
443 fontFamily = fontFamily,
444 fontFeatureSettings = fontFeatureSettings,
445 letterSpacing = letterSpacing,
446 baselineShift = baselineShift,
447 textGeometricTransform = textGeometricTransform,
448 localeList = localeList,
449 background = background,
450 textDecoration = textDecoration,
451 shadow = shadow,
452 platformStyle = platformStyle
453 )
454 }
455
456 @ExperimentalTextApi
457 fun copy(
458 brush: Brush?,
Halil Ozercana8686042022-06-01 15:20:20 +0100459 alpha: Float = this.alpha,
Halil Ozercan06becff2022-04-13 01:38:27 +0000460 fontSize: TextUnit = this.fontSize,
461 fontWeight: FontWeight? = this.fontWeight,
462 fontStyle: FontStyle? = this.fontStyle,
463 fontSynthesis: FontSynthesis? = this.fontSynthesis,
464 fontFamily: FontFamily? = this.fontFamily,
465 fontFeatureSettings: String? = this.fontFeatureSettings,
466 letterSpacing: TextUnit = this.letterSpacing,
467 baselineShift: BaselineShift? = this.baselineShift,
468 textGeometricTransform: TextGeometricTransform? = this.textGeometricTransform,
469 localeList: LocaleList? = this.localeList,
470 background: Color = this.background,
471 textDecoration: TextDecoration? = this.textDecoration,
472 shadow: Shadow? = this.shadow,
473 platformStyle: PlatformSpanStyle? = this.platformStyle
474 ): SpanStyle {
475 return SpanStyle(
Halil Ozercan06e71162022-07-18 17:24:24 +0100476 textForegroundStyle = TextForegroundStyle.from(brush, alpha),
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800477 fontSize = fontSize,
478 fontWeight = fontWeight,
479 fontStyle = fontStyle,
480 fontSynthesis = fontSynthesis,
481 fontFamily = fontFamily,
482 fontFeatureSettings = fontFeatureSettings,
483 letterSpacing = letterSpacing,
484 baselineShift = baselineShift,
485 textGeometricTransform = textGeometricTransform,
486 localeList = localeList,
487 background = background,
488 textDecoration = textDecoration,
489 shadow = shadow,
490 platformStyle = platformStyle
491 )
492 }
493
Sean McQuillancb6d1002022-08-08 15:39:42 -0700494 override fun equals(other: Any?): Boolean {
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800495 if (this === other) return true
496 if (other !is SpanStyle) return false
Siyamed Sinirda5769c2022-04-13 23:35:28 -0700497 return hasSameLayoutAffectingAttributes(other) &&
498 hasSameNonLayoutAttributes(other)
499 }
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800500
Siyamed Sinirda5769c2022-04-13 23:35:28 -0700501 internal fun hasSameLayoutAffectingAttributes(other: SpanStyle): Boolean {
502 if (this === other) return true
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800503 if (fontSize != other.fontSize) return false
504 if (fontWeight != other.fontWeight) return false
505 if (fontStyle != other.fontStyle) return false
506 if (fontSynthesis != other.fontSynthesis) return false
507 if (fontFamily != other.fontFamily) return false
508 if (fontFeatureSettings != other.fontFeatureSettings) return false
509 if (letterSpacing != other.letterSpacing) return false
510 if (baselineShift != other.baselineShift) return false
511 if (textGeometricTransform != other.textGeometricTransform) return false
512 if (localeList != other.localeList) return false
513 if (background != other.background) return false
Siyamed Sinirda5769c2022-04-13 23:35:28 -0700514 if (platformStyle != other.platformStyle) return false
515 return true
516 }
517
518 private fun hasSameNonLayoutAttributes(other: SpanStyle): Boolean {
Halil Ozercan06e71162022-07-18 17:24:24 +0100519 if (textForegroundStyle != other.textForegroundStyle) return false
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800520 if (textDecoration != other.textDecoration) return false
521 if (shadow != other.shadow) return false
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800522 return true
523 }
524
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800525 @OptIn(ExperimentalTextApi::class)
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800526 override fun hashCode(): Int {
527 var result = color.hashCode()
Halil Ozercan06becff2022-04-13 01:38:27 +0000528 result = 31 * result + brush.hashCode()
Halil Ozercana8686042022-06-01 15:20:20 +0100529 result = 31 * result + alpha.hashCode()
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800530 result = 31 * result + fontSize.hashCode()
531 result = 31 * result + (fontWeight?.hashCode() ?: 0)
532 result = 31 * result + (fontStyle?.hashCode() ?: 0)
533 result = 31 * result + (fontSynthesis?.hashCode() ?: 0)
534 result = 31 * result + (fontFamily?.hashCode() ?: 0)
535 result = 31 * result + (fontFeatureSettings?.hashCode() ?: 0)
536 result = 31 * result + letterSpacing.hashCode()
537 result = 31 * result + (baselineShift?.hashCode() ?: 0)
538 result = 31 * result + (textGeometricTransform?.hashCode() ?: 0)
539 result = 31 * result + (localeList?.hashCode() ?: 0)
540 result = 31 * result + background.hashCode()
541 result = 31 * result + (textDecoration?.hashCode() ?: 0)
542 result = 31 * result + (shadow?.hashCode() ?: 0)
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800543 result = 31 * result + (platformStyle?.hashCode() ?: 0)
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800544 return result
545 }
546
Halil Ozercana8836782022-06-23 17:17:19 +0100547 internal fun hashCodeLayoutAffectingAttributes(): Int {
548 var result = fontSize.hashCode()
549 result = 31 * result + (fontWeight?.hashCode() ?: 0)
550 result = 31 * result + (fontStyle?.hashCode() ?: 0)
551 result = 31 * result + (fontSynthesis?.hashCode() ?: 0)
552 result = 31 * result + (fontFamily?.hashCode() ?: 0)
553 result = 31 * result + (fontFeatureSettings?.hashCode() ?: 0)
554 result = 31 * result + letterSpacing.hashCode()
555 result = 31 * result + (baselineShift?.hashCode() ?: 0)
556 result = 31 * result + (textGeometricTransform?.hashCode() ?: 0)
557 result = 31 * result + (localeList?.hashCode() ?: 0)
558 result = 31 * result + background.hashCode()
559 result = 31 * result + (platformStyle?.hashCode() ?: 0)
560 return result
561 }
562
563 @OptIn(ExperimentalTextApi::class)
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800564 override fun toString(): String {
565 return "SpanStyle(" +
566 "color=$color, " +
Halil Ozercan06becff2022-04-13 01:38:27 +0000567 "brush=$brush, " +
Halil Ozercana8686042022-06-01 15:20:20 +0100568 "alpha=$alpha, " +
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800569 "fontSize=$fontSize, " +
570 "fontWeight=$fontWeight, " +
571 "fontStyle=$fontStyle, " +
572 "fontSynthesis=$fontSynthesis, " +
573 "fontFamily=$fontFamily, " +
574 "fontFeatureSettings=$fontFeatureSettings, " +
575 "letterSpacing=$letterSpacing, " +
576 "baselineShift=$baselineShift, " +
577 "textGeometricTransform=$textGeometricTransform, " +
578 "localeList=$localeList, " +
579 "background=$background, " +
580 "textDecoration=$textDecoration, " +
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800581 "shadow=$shadow, " +
582 "platformStyle=$platformStyle" +
Siyamed Sinirda765cc2021-01-09 10:17:35 -0800583 ")"
584 }
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800585}
Siyamed Sinirbc9d8822020-04-21 18:20:59 -0700586
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800587/**
haoyudf2e07c2020-11-09 17:36:11 -0800588 * @param a An sp value. Maybe [TextUnit.Unspecified]
589 * @param b An sp value. Maybe [TextUnit.Unspecified]
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800590 */
Siyamed Sinirc8b01322019-12-05 09:41:00 -0800591internal fun lerpTextUnitInheritable(a: TextUnit, b: TextUnit, t: Float): TextUnit {
haoyudf2e07c2020-11-09 17:36:11 -0800592 if (a.isUnspecified || b.isUnspecified) return lerpDiscrete(a, b, t)
Siyamed Sinirc8b01322019-12-05 09:41:00 -0800593 return lerp(a, b, t)
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800594}
595
Siyamed Sinirc8b01322019-12-05 09:41:00 -0800596/**
597 * Lerp between two values that cannot be transitioned. Returns [a] if [fraction] is smaller than
598 * 0.5 otherwise [b].
599 */
600internal fun <T> lerpDiscrete(a: T, b: T, fraction: Float): T = if (fraction < 0.5) a else b
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800601
602/**
603 * Interpolate between two span styles.
604 *
605 * This will not work well if the styles don't set the same fields.
606 *
607 * The [fraction] argument represents position on the timeline, with 0.0 meaning
608 * that the interpolation has not started, returning [start] (or something
609 * equivalent to [start]), 1.0 meaning that the interpolation has finished,
610 * returning [stop] (or something equivalent to [stop]), and values in between
611 * meaning that the interpolation is at the relevant point on the timeline
612 * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
613 * 1.0, so negative values and values greater than 1.0 are valid.
614 */
615fun lerp(start: SpanStyle, stop: SpanStyle, fraction: Float): SpanStyle {
616 return SpanStyle(
Halil Ozercan06e71162022-07-18 17:24:24 +0100617 textForegroundStyle = lerp(start.textForegroundStyle, stop.textForegroundStyle, fraction),
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800618 fontFamily = lerpDiscrete(
619 start.fontFamily,
620 stop.fontFamily,
621 fraction
622 ),
623 fontSize = lerpTextUnitInheritable(start.fontSize, stop.fontSize, fraction),
624 fontWeight = lerp(
625 start.fontWeight ?: FontWeight.Normal,
626 stop.fontWeight ?: FontWeight.Normal,
627 fraction
628 ),
629 fontStyle = lerpDiscrete(
630 start.fontStyle,
631 stop.fontStyle,
632 fraction
633 ),
634 fontSynthesis = lerpDiscrete(
635 start.fontSynthesis,
636 stop.fontSynthesis,
637 fraction
638 ),
639 fontFeatureSettings = lerpDiscrete(
640 start.fontFeatureSettings,
641 stop.fontFeatureSettings,
642 fraction
643 ),
644 letterSpacing = lerpTextUnitInheritable(
645 start.letterSpacing,
646 stop.letterSpacing,
647 fraction
648 ),
649 baselineShift = lerp(
650 start.baselineShift ?: BaselineShift(0f),
651 stop.baselineShift ?: BaselineShift(0f),
652 fraction
653 ),
654 textGeometricTransform = lerp(
655 start.textGeometricTransform ?: TextGeometricTransform.None,
656 stop.textGeometricTransform ?: TextGeometricTransform.None,
657 fraction
658 ),
659 localeList = lerpDiscrete(start.localeList, stop.localeList, fraction),
Siyamed Sinird4b6f662019-12-04 10:07:56 -0800660 background = lerp(
Siyamed Sinir36b1ec92020-04-21 15:58:30 -0700661 start.background,
662 stop.background,
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800663 fraction
664 ),
Siyamed Sinir32fd1062019-12-10 10:54:47 -0800665 textDecoration = lerpDiscrete(
666 start.textDecoration,
667 stop.textDecoration,
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800668 fraction
669 ),
670 shadow = lerp(
671 start.shadow ?: Shadow(),
672 stop.shadow ?: Shadow(),
673 fraction
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800674 ),
675 platformStyle = lerpPlatformStyle(start.platformStyle, stop.platformStyle, fraction)
Siyamed Sinirf447f2a2019-12-03 15:44:51 -0800676 )
Siyamed Sinirbc9d8822020-04-21 18:20:59 -0700677}
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800678
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800679private fun lerpPlatformStyle(
680 start: PlatformSpanStyle?,
681 stop: PlatformSpanStyle?,
682 fraction: Float
683): PlatformSpanStyle? {
684 if (start == null && stop == null) return null
685 val startNonNull = start ?: PlatformSpanStyle.Default
686 val stopNonNull = stop ?: PlatformSpanStyle.Default
Siyamed Sinire5b3f742022-04-15 10:14:40 -0700687 return lerp(startNonNull, stopNonNull, fraction)
Siyamed Sinir8e2ae722022-03-10 08:23:44 -0800688}
Siyamed Sinir0ac70682022-03-27 12:07:36 -0700689
Siyamed Sinir0ac70682022-03-27 12:07:36 -0700690internal fun resolveSpanStyleDefaults(style: SpanStyle) = SpanStyle(
Halil Ozercan06e71162022-07-18 17:24:24 +0100691 textForegroundStyle = style.textForegroundStyle.takeOrElse {
692 TextForegroundStyle.from(DefaultColor)
693 },
Siyamed Sinir0ac70682022-03-27 12:07:36 -0700694 fontSize = if (style.fontSize.isUnspecified) DefaultFontSize else style.fontSize,
695 fontWeight = style.fontWeight ?: FontWeight.Normal,
696 fontStyle = style.fontStyle ?: FontStyle.Normal,
697 fontSynthesis = style.fontSynthesis ?: FontSynthesis.All,
698 fontFamily = style.fontFamily ?: FontFamily.Default,
699 fontFeatureSettings = style.fontFeatureSettings ?: "",
700 letterSpacing = if (style.letterSpacing.isUnspecified) {
701 DefaultLetterSpacing
702 } else {
703 style.letterSpacing
704 },
705 baselineShift = style.baselineShift ?: BaselineShift.None,
706 textGeometricTransform = style.textGeometricTransform ?: TextGeometricTransform.None,
707 localeList = style.localeList ?: LocaleList.current,
708 background = style.background.takeOrElse { DefaultBackgroundColor },
709 textDecoration = style.textDecoration ?: TextDecoration.None,
710 shadow = style.shadow ?: Shadow.None,
711 platformStyle = style.platformStyle
712)