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