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 */
paddingnull44 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 */
paddingnull69 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
<lambda>null90 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) {
measurenull100 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
hashCodenull125 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
equalsnull134 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