1 /*
<lambda>null2  * Copyright 2024 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.material3
18 
19 import androidx.compose.foundation.shape.CornerBasedShape
20 import androidx.compose.ui.Modifier
21 import androidx.compose.ui.geometry.Size
22 import androidx.compose.ui.graphics.Shape
23 import androidx.compose.ui.layout.layout
24 import androidx.compose.ui.unit.Dp
25 import androidx.compose.ui.unit.dp
26 import kotlin.math.roundToInt
27 
28 /**
29  * [Modifier] that centers the content horizontally depending on the [CornerBasedShape] provided. It
30  * will increase or decrease the start padding to better center the content depending on the corner
31  * radii of the provided shape. This is meant to be used with asymmetric shapes, the modifier will
32  * not do anything to the content if the shape provided is symmetric.
33  *
34  * @param shape the [CornerBasedShape] that the content should be adjusted to so that the content is
35  *   more centered within the shape.
36  * @param maxStartOffset the maximum start offset that the content can be adjusted before it starts
37  *   clipping the content
38  * @param maxEndOffset the maximum end offset that the content can be adjusted before it starts
39  *   clipping the content
40  */
41 @ExperimentalMaterial3ExpressiveApi
42 internal fun Modifier.horizontalCenterOptically(
43     shape: CornerBasedShape,
44     maxStartOffset: Dp = 0.dp,
45     maxEndOffset: Dp = 0.dp
46 ) =
47     this.layout { measureable, constraints ->
48         val placeable = measureable.measure(constraints)
49         val width = placeable.width
50         val height = placeable.height
51         val size = Size(width = width.toFloat(), height = height.toFloat())
52         val density = this@layout
53         val maxStartOffsetPx = -maxStartOffset.toPx()
54         val maxEndOffsetPx = maxEndOffset.toPx()
55 
56         val topStart = shape.topStart.toPx(shapeSize = size, density = density)
57         val topEnd = shape.topEnd.toPx(shapeSize = size, density = density)
58         val bottomStart = shape.bottomStart.toPx(shapeSize = size, density = density)
59         val bottomEnd = shape.bottomEnd.toPx(shapeSize = size, density = density)
60         val avgStart = (topStart + bottomStart) / 2
61         val avgEnd = (topEnd + bottomEnd) / 2
62         val paddingCorrection = CenterOpticallyCoefficient * (avgStart - avgEnd)
63         layout(width, height) {
64             val coercedCorrection = paddingCorrection.coerceIn(maxStartOffsetPx, maxEndOffsetPx)
65             placeable.place(coercedCorrection.roundToInt(), 0)
66         }
67     }
68 
69 @ExperimentalMaterial3ExpressiveApi
horizontalCenterOpticallynull70 internal fun Modifier.horizontalCenterOptically(
71     shape: ShapeWithHorizontalCenterOptically,
72     maxStartOffset: Dp = 0.dp,
73     maxEndOffset: Dp = 0.dp
74 ) =
75     this.layout { measurable, constraints ->
76         val placeable = measurable.measure(constraints)
77         val width = placeable.width
78         val height = placeable.height
79         val maxStartOffsetPx = -maxStartOffset.toPx()
80         val maxEndOffsetPx = maxEndOffset.toPx()
81         layout(width, height) {
82             val coercedOffset = shape.offset().coerceIn(maxStartOffsetPx, maxEndOffsetPx)
83             placeable.place(coercedOffset.roundToInt(), 0)
84         }
85     }
86 
87 internal interface ShapeWithHorizontalCenterOptically : Shape {
offsetnull88     fun offset(): Float
89 }
90 
91 internal const val CenterOpticallyCoefficient = 0.11f
92