1 /*
2  * Copyright 2020 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.layout
18 
19 import androidx.compose.runtime.Immutable
20 import kotlin.math.max
21 import kotlin.math.min
22 
23 /**
24  * Defines an offset line that can be used by parent layouts to align and position their children.
25  * Text baselines are representative examples of [AlignmentLine]s. For example, they can be used by
26  * `Row`, to align its children by baseline, or by `paddingFrom` to achieve a layout with a specific
27  * distance from the top to the baseline of the text content. [AlignmentLine]s can be understood as
28  * an abstraction over text baselines.
29  *
30  * When a layout provides a value for a particular [AlignmentLine], this can be read by the parents
31  * of the layout after measuring, using the [Placeable.get] operator on the corresponding
32  * [Placeable] instance. Based on the position of the [AlignmentLine], the parents can then decide
33  * the positioning of the children.
34  *
35  * Note that when a layout provides a value for an [AlignmentLine], this will be automatically
36  * inherited by the layout's parent, which will offset the value by the position of the child within
37  * itself. This way, nested layout hierarchies are able to preserve the [AlignmentLine]s defined for
38  * deeply nested children, making it possible for non-direct parents to use these for positioning
39  * and alignment. When a layout inherits multiple values for the same [AlignmentLine] from different
40  * children, the position of the line within the layout will be computed by merging the children
41  * values using the provided [merger]. If a layout provides a value for an [AlignmentLine], this
42  * will always be the position of the line, regardless of the values provided by children for the
43  * same line.
44  *
45  * [AlignmentLine]s cannot be created directly, please create [VerticalAlignmentLine] or
46  * [HorizontalAlignmentLine] instances instead.
47  *
48  * @sample androidx.compose.ui.samples.AlignmentLineSample
49  * @see VerticalAlignmentLine
50  * @see HorizontalAlignmentLine
51  */
52 @Immutable
53 sealed class AlignmentLine(internal val merger: (Int, Int) -> Int) {
54     companion object {
55         /** Constant representing that an [AlignmentLine] has not been provided. */
56         const val Unspecified = Int.MIN_VALUE
57     }
58 }
59 
60 /**
61  * Merges two values of the current [alignment line][AlignmentLine]. This is used when a layout
62  * inherits multiple values for the same [AlignmentLine] from different children, so the position of
63  * the line within the layout will be computed by merging the children values using the provided
64  * [AlignmentLine.merger].
65  */
mergenull66 internal fun AlignmentLine.merge(position1: Int, position2: Int) = merger(position1, position2)
67 
68 /**
69  * A vertical [AlignmentLine]. Defines a vertical offset line that can be used by parent layouts
70  * usually to align or position their children horizontally. The positions of the alignment lines
71  * will be automatically inherited by parent layouts from their content, and the [merger] will be
72  * used to merge multiple line positions when more than one child provides a specific
73  * [AlignmentLine]. See [AlignmentLine] for more details.
74  *
75  * @param merger How to merge two alignment line values defined by different children
76  */
77 class VerticalAlignmentLine(merger: (Int, Int) -> Int) : AlignmentLine(merger)
78 
79 /**
80  * A horizontal [AlignmentLine]. Defines an horizontal offset line that can be used by parent
81  * layouts usually to align or position their children vertically. Text baselines (`FirstBaseline`
82  * and `LastBaseline`) are representative examples of [HorizontalAlignmentLine]s. For example, they
83  * can be used by `Row`, to align its children by baseline, or by `paddingFrom` to achieve a layout
84  * with a specific from the top to the baseline of the text content. The positions of the alignment
85  * lines will be automatically inherited by parent layouts from their content, and the [merger] will
86  * be used to merge multiple line positions when more than one child provides a specific
87  * [HorizontalAlignmentLine]. See [AlignmentLine] for more details.
88  *
89  * @param merger How to merge two alignment line values defined by different children
90  */
91 class HorizontalAlignmentLine(merger: (Int, Int) -> Int) : AlignmentLine(merger)
92 
93 /**
94  * [AlignmentLine] defined by the baseline of a first line of a
95  * [androidx.compose.foundation.text.BasicText]
96  */
97 val FirstBaseline = HorizontalAlignmentLine(::min)
98 
99 /**
100  * [AlignmentLine] defined by the baseline of the last line of a
101  * [androidx.compose.foundation.text.BasicText]
102  */
103 val LastBaseline = HorizontalAlignmentLine(::max)
104