1 /* 2 * Copyright 2022 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.style 18 19 import androidx.compose.ui.text.PlatformParagraphStyle 20 import androidx.compose.ui.text.internal.checkPrecondition 21 import kotlin.jvm.JvmInline 22 23 /** 24 * The configuration for line height such as alignment of the line in the provided line height, 25 * whether to apply additional space as a result of line height to top of first line top and bottom 26 * of last line. 27 * 28 * The configuration is applied only when a line height is defined on the text. 29 * 30 * [trim] feature is available only when [PlatformParagraphStyle.includeFontPadding] is false. 31 * 32 * Please check [Trim] and [Alignment] for more description. 33 * 34 * @param alignment defines how to align the line in the space provided by the line height. 35 * @param trim defines whether the space that would be added to the top of first line, and bottom of 36 * the last line should be trimmed or not. This feature is available only when 37 * [PlatformParagraphStyle.includeFontPadding] is false. 38 * @param mode defines the behavior when the specified line height is smaller than system preferred 39 * line height. By specifying [Mode.Fixed], the line height is always set to the specified value. 40 * This is the default value. By specifying [Mode.Minimum], the specified line height is smaller 41 * than the system preferred value, the system preferred one is used instead. 42 */ 43 class LineHeightStyle(val alignment: Alignment, val trim: Trim, val mode: Mode) { 44 45 constructor(alignment: Alignment, trim: Trim) : this(alignment, trim, Mode.Fixed) 46 47 companion object { 48 /** 49 * The default configuration for [LineHeightStyle]: 50 * - alignment = [Alignment.Proportional] 51 * - trim = [Trim.Both] 52 * - mode = [Mode.Fixed] 53 */ 54 val Default = 55 LineHeightStyle(alignment = Alignment.Proportional, trim = Trim.Both, mode = Mode.Fixed) 56 } 57 58 /** Returns a copy of this [LineHeightStyle], optionally overriding some of the values. */ copynull59 fun copy( 60 alignment: Alignment = this.alignment, 61 trim: Trim = this.trim, 62 mode: Mode = this.mode, 63 ) = LineHeightStyle(alignment, trim, mode) 64 65 override fun equals(other: Any?): Boolean { 66 if (this === other) return true 67 if (other !is LineHeightStyle) return false 68 69 if (alignment != other.alignment) return false 70 if (trim != other.trim) return false 71 if (mode != other.mode) return false 72 73 return true 74 } 75 hashCodenull76 override fun hashCode(): Int { 77 var result = alignment.hashCode() 78 result = 31 * result + trim.hashCode() 79 result = 31 * result + mode.hashCode() 80 return result 81 } 82 toStringnull83 override fun toString(): String { 84 return "LineHeightStyle(" + "alignment=$alignment, " + "trim=$trim," + "mode=$mode" + ")" 85 } 86 87 /** 88 * Defines whether the space that would be added to the top of first line, and bottom of the 89 * last line should be trimmed or not. This feature is available only when 90 * [PlatformParagraphStyle.includeFontPadding] is false. 91 */ 92 @kotlin.jvm.JvmInline 93 value class Trim private constructor(private val value: Int) { 94 toStringnull95 override fun toString(): String { 96 return when (value) { 97 FirstLineTop.value -> "LineHeightStyle.Trim.FirstLineTop" 98 LastLineBottom.value -> "LineHeightStyle.Trim.LastLineBottom" 99 Both.value -> "LineHeightStyle.Trim.Both" 100 None.value -> "LineHeightStyle.Trim.None" 101 else -> "Invalid" 102 } 103 } 104 105 companion object { 106 private const val FlagTrimTop = 0x00000001 107 private const val FlagTrimBottom = 0x00000010 108 109 /** 110 * Trim the space that would be added to the top of the first line as a result of the 111 * line height. Single line text is both the first and last line. This feature is 112 * available only when [PlatformParagraphStyle.includeFontPadding] is false. 113 * 114 * For example, when line height is 3.em, and [Alignment] is [Alignment.Center], the 115 * first line has 2.em height and the height from first line baseline to second line 116 * baseline is still 3.em: 117 * <pre> 118 * +--------+ 119 * | Line1 | 120 * | | 121 * |--------| 122 * | | 123 * | Line2 | 124 * | | 125 * +--------+ 126 * </pre> 127 */ 128 val FirstLineTop = Trim(FlagTrimTop) 129 130 /** 131 * Trim the space that would be added to the bottom of the last line as a result of the 132 * line height. Single line text is both the first and last line. This feature is 133 * available only when [PlatformParagraphStyle.includeFontPadding] is false. 134 * 135 * For example, when line height is 3.em, and [Alignment] is [Alignment.Center], the 136 * last line has 2.em height and the height from first line baseline to second line 137 * baseline is still 3.em: 138 * <pre> 139 * +--------+ 140 * | | 141 * | Line1 | 142 * | | 143 * |--------| 144 * | | 145 * | Line2 | 146 * +--------+ 147 * </pre> 148 */ 149 val LastLineBottom = Trim(FlagTrimBottom) 150 151 /** 152 * Trim the space that would be added to the top of the first line and bottom of the 153 * last line as a result of the line height. This feature is available only when 154 * [PlatformParagraphStyle.includeFontPadding] is false. 155 * 156 * For example, when line height is 3.em, and [Alignment] is [Alignment.Center], the 157 * first and last line has 2.em height and the height from first line baseline to second 158 * line baseline is still 3.em: 159 * <pre> 160 * +--------+ 161 * | Line1 | 162 * | | 163 * |--------| 164 * | | 165 * | Line2 | 166 * +--------+ 167 * </pre> 168 */ 169 val Both = Trim(FlagTrimTop or FlagTrimBottom) 170 171 /** 172 * Do not trim first line top or last line bottom. 173 * 174 * For example, when line height is 3.em, and [Alignment] is [Alignment.Center], the 175 * first line height, last line height and the height from first line baseline to second 176 * line baseline are 3.em: 177 * <pre> 178 * +--------+ 179 * | | 180 * | Line1 | 181 * | | 182 * |--------| 183 * | | 184 * | Line2 | 185 * | | 186 * +--------+ 187 * </pre> 188 */ 189 val None = Trim(0) 190 } 191 isTrimFirstLineTopnull192 internal fun isTrimFirstLineTop(): Boolean { 193 return value and FlagTrimTop > 0 194 } 195 isTrimLastLineBottomnull196 internal fun isTrimLastLineBottom(): Boolean { 197 return value and FlagTrimBottom > 0 198 } 199 } 200 201 /** 202 * Defines how to align the line in the space provided by the line height. 203 * 204 * @param topRatio the ratio of ascent to ascent+descent in percentage. Valid values are between 205 * 0f (inclusive) and 1f (inclusive). 206 */ 207 @kotlin.jvm.JvmInline 208 value class Alignment constructor(internal val topRatio: Float) { 209 <lambda>null210 init { 211 checkPrecondition(topRatio in 0f..1f || topRatio == -1f) { 212 "topRatio should be in [0..1] range or -1" 213 } 214 } 215 toStringnull216 override fun toString(): String { 217 return when (topRatio) { 218 Top.topRatio -> "LineHeightStyle.Alignment.Top" 219 Center.topRatio -> "LineHeightStyle.Alignment.Center" 220 Proportional.topRatio -> "LineHeightStyle.Alignment.Proportional" 221 Bottom.topRatio -> "LineHeightStyle.Alignment.Bottom" 222 else -> "LineHeightStyle.Alignment(topPercentage = $topRatio)" 223 } 224 } 225 226 companion object { 227 /** 228 * Align the line to the top of the space reserved for that line. This means that all 229 * extra space as a result of line height is applied to the bottom of the line. When the 230 * provided line height value is smaller than the actual line height, the line will 231 * still be aligned to the top, therefore the required difference will be subtracted 232 * from the bottom of the line. 233 * 234 * For example, when line height is 3.em, the lines are aligned to the top of 3.em 235 * height: 236 * <pre> 237 * +--------+ 238 * | Line1 | 239 * | | 240 * | | 241 * |--------| 242 * | Line2 | 243 * | | 244 * | | 245 * +--------+ 246 * </pre> 247 */ 248 val Top = Alignment(topRatio = 0f) 249 250 /** 251 * Align the line to the center of the space reserved for the line. This configuration 252 * distributes additional space evenly between top and bottom of the line. 253 * 254 * For example, when line height is 3.em, the lines are aligned to the center of 3.em 255 * height: 256 * <pre> 257 * +--------+ 258 * | | 259 * | Line1 | 260 * | | 261 * |--------| 262 * | | 263 * | Line2 | 264 * | | 265 * +--------+ 266 * </pre> 267 */ 268 val Center = Alignment(topRatio = 0.5f) 269 270 /** 271 * Align the line proportional to the ascent and descent values of the line. For example 272 * if ascent is 8 units of length, and descent is 2 units; an additional space of 10 273 * units will be distributed as 8 units to top, and 2 units to the bottom of the line. 274 * This is the default behavior. 275 */ 276 val Proportional = Alignment(topRatio = -1f) 277 278 /** 279 * Align the line to the bottom of the space reserved for that line. This means that all 280 * extra space as a result of line height is applied to the top of the line. When the 281 * provided line height value is smaller than the actual line height, the line will 282 * still be aligned to the bottom, therefore the required difference will be subtracted 283 * from the top of the line. 284 * 285 * For example, when line height is 3.em, the lines are aligned to the bottom of 3.em 286 * height: 287 * <pre> 288 * +--------+ 289 * | | 290 * | | 291 * | Line1 | 292 * |--------| 293 * | | 294 * | | 295 * | Line2 | 296 * +--------+ 297 * </pre> 298 */ 299 val Bottom = Alignment(topRatio = 1f) 300 } 301 } 302 303 /** 304 * Defines if the specified line height value should be enforced. 305 * 306 * The line height is determined by the font file used in the text. So, sometimes the specified 307 * text height can be too tight to show the given text. By using `Adjustment.Minimum` the line 308 * height can be adjusted to the system provided value if the specified line height is too 309 * tight. This is useful for supporting languages that use tall glyphs, e.g. Arabic, Myanmar, 310 * etc. 311 */ 312 @JvmInline 313 value class Mode private constructor(private val value: Int) { 314 companion object { 315 /** 316 * Always use the specified line height. Even if the system preferred line height is 317 * larger than specified one, the specified line height is used. 318 */ 319 val Fixed = Mode(0) 320 321 /** 322 * By specifying [Mode.Minimum], when the specified line height is smaller than the 323 * system preferred value, the system preferred one is used instead. 324 */ 325 val Minimum = Mode(1) 326 } 327 } 328 } 329