1 /* 2 * Copyright (C) 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 com.android.compose.modifiers 18 19 import androidx.compose.ui.Modifier 20 import androidx.compose.ui.layout.LayoutModifier 21 import androidx.compose.ui.layout.Measurable 22 import androidx.compose.ui.layout.MeasureResult 23 import androidx.compose.ui.layout.MeasureScope 24 import androidx.compose.ui.platform.InspectorInfo 25 import androidx.compose.ui.platform.InspectorValueInfo 26 import androidx.compose.ui.platform.debugInspectorInfo 27 import androidx.compose.ui.unit.Constraints 28 import androidx.compose.ui.unit.Density 29 import androidx.compose.ui.unit.constrainHeight 30 import androidx.compose.ui.unit.constrainWidth 31 import androidx.compose.ui.unit.offset 32 33 // This file was mostly copy/pasted from by androidx.compose.foundation.layout.Padding.kt and 34 // contains modifiers with lambda parameters to change the padding of a Composable without 35 // triggering recomposition when the paddings change. 36 // 37 // These should be used instead of the traditional size modifiers when the size changes often, for 38 // instance when it is animated. 39 // 40 // TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations 41 // APIs. 42 43 /** @see androidx.compose.foundation.layout.padding */ 44 fun Modifier.padding( 45 start: Density.() -> Int = PaddingUnspecified, 46 top: Density.() -> Int = PaddingUnspecified, 47 end: Density.() -> Int = PaddingUnspecified, 48 bottom: Density.() -> Int = PaddingUnspecified, 49 ) = 50 this.then( 51 PaddingModifier( 52 start, 53 top, 54 end, 55 bottom, 56 rtlAware = true, 57 inspectorInfo = 58 debugInspectorInfo { 59 name = "padding" 60 properties["start"] = start 61 properties["top"] = top 62 properties["end"] = end 63 properties["bottom"] = bottom 64 } 65 ) 66 ) 67 68 /** @see androidx.compose.foundation.layout.padding */ 69 fun Modifier.padding( 70 horizontal: Density.() -> Int = PaddingUnspecified, 71 vertical: Density.() -> Int = PaddingUnspecified, 72 ): Modifier { 73 return this.then( 74 PaddingModifier( 75 start = horizontal, 76 top = vertical, 77 end = horizontal, 78 bottom = vertical, 79 rtlAware = true, 80 inspectorInfo = 81 debugInspectorInfo { 82 name = "padding" 83 properties["horizontal"] = horizontal 84 properties["vertical"] = vertical 85 } 86 ) 87 ) 88 } 89 90 private val PaddingUnspecified: Density.() -> Int = { 0 } 91 92 private class PaddingModifier( 93 val start: Density.() -> Int, 94 val top: Density.() -> Int, 95 val end: Density.() -> Int, 96 val bottom: Density.() -> Int, 97 val rtlAware: Boolean, 98 inspectorInfo: InspectorInfo.() -> Unit 99 ) : LayoutModifier, InspectorValueInfo(inspectorInfo) { 100 override fun MeasureScope.measure( 101 measurable: Measurable, 102 constraints: Constraints 103 ): MeasureResult { 104 val start = start() 105 val top = top() 106 val end = end() 107 val bottom = bottom() 108 109 val horizontal = start + end 110 val vertical = top + bottom 111 112 val placeable = measurable.measure(constraints.offset(-horizontal, -vertical)) 113 114 val width = constraints.constrainWidth(placeable.width + horizontal) 115 val height = constraints.constrainHeight(placeable.height + vertical) 116 return layout(width, height) { 117 if (rtlAware) { 118 placeable.placeRelative(start, top) 119 } else { 120 placeable.place(start, top) 121 } 122 } 123 } 124 125 override fun hashCode(): Int { 126 var result = start.hashCode() 127 result = 31 * result + top.hashCode() 128 result = 31 * result + end.hashCode() 129 result = 31 * result + bottom.hashCode() 130 result = 31 * result + rtlAware.hashCode() 131 return result 132 } 133 134 override fun equals(other: Any?): Boolean { 135 val otherModifier = other as? PaddingModifier ?: return false 136 return start == otherModifier.start && 137 top == otherModifier.top && 138 end == otherModifier.end && 139 bottom == otherModifier.bottom && 140 rtlAware == otherModifier.rtlAware 141 } 142 } 143