blob: 6f06bd16fa5cb0b91529a4d49fdeefb503a56951 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.text
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.text.style.lerp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.isUnspecified
/**
* Paragraph styling configuration for a paragraph. The difference between [SpanStyle] and
* `ParagraphStyle` is that, `ParagraphStyle` can be applied to a whole [Paragraph] while
* [SpanStyle] can be applied at the character level.
* Once a portion of the text is marked with a `ParagraphStyle`, that portion will be separated from
* the remaining as if a line feed character was added.
*
* @sample androidx.compose.ui.text.samples.ParagraphStyleSample
* @sample androidx.compose.ui.text.samples.ParagraphStyleAnnotatedStringsSample
*
* @param textAlign The alignment of the text within the lines of the paragraph.
* @param textDirection The algorithm to be used to resolve the final text direction:
* Left To Right or Right To Left.
* @param textIndent The indentation of the paragraph.
* @param lineHeight Line height for the [Paragraph] in [TextUnit] unit, e.g. SP or EM.
*
* @see Paragraph
* @see AnnotatedString
* @see SpanStyle
* @see TextStyle
*/
@Immutable
class ParagraphStyle constructor(
val textAlign: TextAlign? = null,
val textDirection: TextDirection? = null,
val lineHeight: TextUnit = TextUnit.Unspecified,
val textIndent: TextIndent? = null
) {
init {
if (lineHeight != TextUnit.Unspecified) {
// Since we are checking if it's negative, no need to convert Sp into Px at this point.
check(lineHeight.value >= 0f) {
"lineHeight can't be negative (${lineHeight.value})"
}
}
}
/**
* Returns a new paragraph style that is a combination of this style and the given [other]
* style.
*
* If the given paragraph style is null, returns this paragraph style.
*/
@Stable
fun merge(other: ParagraphStyle? = null): ParagraphStyle {
if (other == null) return this
return ParagraphStyle(
lineHeight = if (other.lineHeight.isUnspecified) {
this.lineHeight
} else {
other.lineHeight
},
textIndent = other.textIndent ?: this.textIndent,
textAlign = other.textAlign ?: this.textAlign,
textDirection = other.textDirection ?: this.textDirection
)
}
/**
* Plus operator overload that applies a [merge].
*/
@Stable
operator fun plus(other: ParagraphStyle): ParagraphStyle = this.merge(other)
fun copy(
textAlign: TextAlign? = this.textAlign,
textDirection: TextDirection? = this.textDirection,
lineHeight: TextUnit = this.lineHeight,
textIndent: TextIndent? = this.textIndent
): ParagraphStyle {
return ParagraphStyle(
textAlign = textAlign,
textDirection = textDirection,
lineHeight = lineHeight,
textIndent = textIndent
)
}
override operator fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ParagraphStyle) return false
if (textAlign != other.textAlign) return false
if (textDirection != other.textDirection) return false
if (lineHeight != other.lineHeight) return false
if (textIndent != other.textIndent) return false
return true
}
override fun hashCode(): Int {
var result = textAlign?.hashCode() ?: 0
result = 31 * result + (textDirection?.hashCode() ?: 0)
result = 31 * result + lineHeight.hashCode()
result = 31 * result + (textIndent?.hashCode() ?: 0)
return result
}
override fun toString(): String {
return "ParagraphStyle(" +
"textAlign=$textAlign, " +
"textDirection=$textDirection, " +
"lineHeight=$lineHeight, " +
"textIndent=$textIndent" +
")"
}
}
/**
* Interpolate between two [ParagraphStyle]s.
*
* This will not work well if the styles don't set the same fields.
*
* The [fraction] argument represents position on the timeline, with 0.0 meaning
* that the interpolation has not started, returning [start] (or something
* equivalent to [start]), 1.0 meaning that the interpolation has finished,
* returning [stop] (or something equivalent to [stop]), and values in between
* meaning that the interpolation is at the relevant point on the timeline
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
@Stable
fun lerp(start: ParagraphStyle, stop: ParagraphStyle, fraction: Float): ParagraphStyle {
return ParagraphStyle(
textAlign = lerpDiscrete(start.textAlign, stop.textAlign, fraction),
textDirection = lerpDiscrete(
start.textDirection,
stop.textDirection,
fraction
),
lineHeight = lerpTextUnitInheritable(start.lineHeight, stop.lineHeight, fraction),
textIndent = lerp(
start.textIndent ?: TextIndent(),
stop.textIndent ?: TextIndent(),
fraction
)
)
}