1 /*
2  * Copyright (C) 2021 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.constraintlayout.compose
18 
19 import android.util.Log
20 import androidx.compose.ui.layout.FirstBaseline
21 import androidx.compose.ui.unit.Dp
22 import androidx.compose.ui.unit.dp
23 import androidx.constraintlayout.core.parser.CLArray
24 import androidx.constraintlayout.core.parser.CLNumber
25 import androidx.constraintlayout.core.parser.CLObject
26 import androidx.constraintlayout.core.parser.CLString
27 
28 @JvmDefaultWithCompatibility
29 /**
30  * Represents a vertical side of a layout (i.e start and end) that can be anchored using [linkTo] in
31  * their `Modifier.constrainAs` blocks.
32  */
33 interface VerticalAnchorable {
34     /** Adds a link towards a [ConstraintLayoutBaseScope.VerticalAnchor]. */
linkTonull35     fun linkTo(
36         anchor: ConstraintLayoutBaseScope.VerticalAnchor,
37         margin: Dp = 0.dp,
38         goneMargin: Dp = 0.dp
39     )
40 }
41 
42 @JvmDefaultWithCompatibility
43 /**
44  * Represents a horizontal side of a layout (i.e top and bottom) that can be anchored using [linkTo]
45  * in their `Modifier.constrainAs` blocks.
46  */
47 interface HorizontalAnchorable {
48     /** Adds a link towards a [ConstraintLayoutBaseScope.HorizontalAnchor]. */
49     fun linkTo(
50         anchor: ConstraintLayoutBaseScope.HorizontalAnchor,
51         margin: Dp = 0.dp,
52         goneMargin: Dp = 0.dp
53     )
54 
55     /** Adds a link towards a [ConstraintLayoutBaseScope.BaselineAnchor]. */
56     fun linkTo(
57         anchor: ConstraintLayoutBaseScope.BaselineAnchor,
58         margin: Dp = 0.dp,
59         goneMargin: Dp = 0.dp
60     )
61 }
62 
63 @JvmDefaultWithCompatibility
64 /**
65  * Represents the [FirstBaseline] of a layout that can be anchored using [linkTo] in their
66  * `Modifier.constrainAs` blocks.
67  */
68 interface BaselineAnchorable {
69     /** Adds a link towards a [ConstraintLayoutBaseScope.BaselineAnchor]. */
linkTonull70     fun linkTo(
71         anchor: ConstraintLayoutBaseScope.BaselineAnchor,
72         margin: Dp = 0.dp,
73         goneMargin: Dp = 0.dp
74     )
75 
76     /** Adds a link towards a [ConstraintLayoutBaseScope.HorizontalAnchor]. */
77     fun linkTo(
78         anchor: ConstraintLayoutBaseScope.HorizontalAnchor,
79         margin: Dp = 0.dp,
80         goneMargin: Dp = 0.dp
81     )
82 }
83 
84 internal abstract class BaseVerticalAnchorable(private val containerObject: CLObject, index: Int) :
85     VerticalAnchorable {
86     private val anchorName: String = AnchorFunctions.verticalAnchorIndexToAnchorName(index)
87 
88     final override fun linkTo(
89         anchor: ConstraintLayoutBaseScope.VerticalAnchor,
90         margin: Dp,
91         goneMargin: Dp
92     ) {
93         val targetAnchorName = AnchorFunctions.verticalAnchorIndexToAnchorName(anchor.index)
94         val constraintArray =
95             CLArray(charArrayOf()).apply {
96                 add(CLString.from(anchor.id.toString()))
97                 add(CLString.from(targetAnchorName))
98                 add(CLNumber(margin.value))
99                 add(CLNumber(goneMargin.value))
100             }
101         containerObject.put(anchorName, constraintArray)
102     }
103 }
104 
105 internal abstract class BaseHorizontalAnchorable(
106     private val containerObject: CLObject,
107     index: Int
108 ) : HorizontalAnchorable {
109     private val anchorName: String = AnchorFunctions.horizontalAnchorIndexToAnchorName(index)
110 
linkTonull111     final override fun linkTo(
112         anchor: ConstraintLayoutBaseScope.HorizontalAnchor,
113         margin: Dp,
114         goneMargin: Dp
115     ) {
116         val targetAnchorName = AnchorFunctions.horizontalAnchorIndexToAnchorName(anchor.index)
117         val constraintArray =
118             CLArray(charArrayOf()).apply {
119                 add(CLString.from(anchor.id.toString()))
120                 add(CLString.from(targetAnchorName))
121                 add(CLNumber(margin.value))
122                 add(CLNumber(goneMargin.value))
123             }
124         containerObject.put(anchorName, constraintArray)
125     }
126 
linkTonull127     final override fun linkTo(
128         anchor: ConstraintLayoutBaseScope.BaselineAnchor,
129         margin: Dp,
130         goneMargin: Dp
131     ) {
132         val constraintArray =
133             CLArray(charArrayOf()).apply {
134                 add(CLString.from(anchor.id.toString()))
135                 add(CLString.from("baseline"))
136                 add(CLNumber(margin.value))
137                 add(CLNumber(goneMargin.value))
138             }
139         containerObject.put(anchorName, constraintArray)
140     }
141 }
142 
143 internal object AnchorFunctions {
144 
horizontalAnchorIndexToAnchorNamenull145     fun horizontalAnchorIndexToAnchorName(index: Int): String =
146         when (index) {
147             0 -> "top"
148             1 -> "bottom"
149             else -> {
150                 Log.e("CCL", "horizontalAnchorIndexToAnchorName: Unknown horizontal index")
151                 "top"
152             }
153         }
154 
verticalAnchorIndexToAnchorNamenull155     fun verticalAnchorIndexToAnchorName(index: Int): String =
156         when (index) {
157             -2 -> "start"
158             -1 -> "end"
159             0 -> "left"
160             1 -> "right"
161             else -> {
162                 Log.e("CCL", "verticalAnchorIndexToAnchorName: Unknown vertical index")
163                 "start"
164             }
165         }
166 }
167