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.carousel
18 
19 import androidx.compose.foundation.BorderStroke
20 import androidx.compose.foundation.border
21 import androidx.compose.foundation.shape.GenericShape
22 import androidx.compose.material3.ExperimentalMaterial3Api
23 import androidx.compose.runtime.Composable
24 import androidx.compose.runtime.remember
25 import androidx.compose.ui.Modifier
26 import androidx.compose.ui.draw.clip
27 import androidx.compose.ui.geometry.Offset
28 import androidx.compose.ui.geometry.toRect
29 import androidx.compose.ui.graphics.Shape
30 import androidx.compose.ui.graphics.addOutline
31 import androidx.compose.ui.platform.LocalDensity
32 
33 /** Receiver scope for [Carousel] item content. */
34 @ExperimentalMaterial3Api
35 sealed interface CarouselItemScope {
36     /**
37      * Information regarding the carousel item, such as its minimum and maximum size.
38      *
39      * The item information is updated after every scroll. If you use it in a composable function,
40      * it will be recomposed on every change causing potential performance issues. Avoid using it in
41      * the composition.
42      */
43     val carouselItemDrawInfo: CarouselItemDrawInfo
44 
45     /**
46      * Clips the composable to the given [shape], taking into account the item's size in the cross
47      * axis and mask in the main axis.
48      *
49      * @param shape the shape to be applied to the composable
50      */
51     @Composable fun Modifier.maskClip(shape: Shape): Modifier
52 
53     /**
54      * Draw a border on the composable using the given [shape], taking into account the item's size
55      * in the cross axis and mask in the main axis.
56      *
57      * @param border the border to be drawn around the composable
58      * @param shape the shape of the border
59      */
60     @Composable fun Modifier.maskBorder(border: BorderStroke, shape: Shape): Modifier
61 
62     /**
63      * Converts and remembers [shape] into a [GenericShape] that uses the intersection of the
64      * carousel item's mask Rect and Size as the final shape's bounds.
65      *
66      * This method is useful if using a [Shape] in a Modifier other than [maskClip] and [maskBorder]
67      * where the shape should follow the changes in the item's mask size.
68      *
69      * @param shape The shape that will be converted and remembered and react to changes in the
70      *   item's mask.
71      */
72     @Composable fun rememberMaskShape(shape: Shape): GenericShape
73 }
74 
75 @ExperimentalMaterial3Api
76 internal class CarouselItemScopeImpl(private val itemInfo: CarouselItemDrawInfo) :
77     CarouselItemScope {
78     override val carouselItemDrawInfo: CarouselItemDrawInfo
79         get() = itemInfo
80 
81     @Composable
maskClipnull82     override fun Modifier.maskClip(shape: Shape): Modifier = clip(rememberMaskShape(shape = shape))
83 
84     @Composable
85     override fun Modifier.maskBorder(border: BorderStroke, shape: Shape): Modifier =
86         border(border, rememberMaskShape(shape = shape))
87 
88     @Composable
89     override fun rememberMaskShape(shape: Shape): GenericShape {
90         val density = LocalDensity.current
91         return remember(carouselItemDrawInfo, density) {
92             GenericShape { size, direction ->
93                 val rect = carouselItemDrawInfo.maskRect.intersect(size.toRect())
94                 addOutline(shape.createOutline(rect.size, direction, density))
95                 translate(Offset(rect.left, rect.top))
96             }
97         }
98     }
99 }
100