1 /*
2 * 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.gestures.snapping.SnapPosition
20 import kotlin.math.max
21 import kotlin.math.min
22 import kotlin.math.roundToInt
23
24 /**
25 * Calculates the offset from the beginning of the carousel container needed to snap to the item at
26 * [itemIndex].
27 *
28 * This method takes into account the correct keyline list needed to allow the item to be fully
29 * visible and located at a focal position.
30 */
getSnapPositionOffsetnull31 internal fun getSnapPositionOffset(strategy: Strategy, itemIndex: Int, itemCount: Int): Int {
32 if (!strategy.isValid) return 0
33
34 val numOfFocalKeylines =
35 strategy.defaultKeylines.lastFocalIndex - strategy.defaultKeylines.firstFocalIndex
36 val startStepsSize = strategy.startKeylineSteps.size + numOfFocalKeylines
37 val endStepsSize = strategy.endKeylineSteps.size + numOfFocalKeylines
38
39 var offset =
40 (strategy.defaultKeylines.firstFocal.unadjustedOffset - strategy.itemMainAxisSize / 2F)
41 .roundToInt()
42
43 if (itemIndex < startStepsSize) {
44 var startIndex = max(0, startStepsSize - 1 - itemIndex)
45 startIndex = min(strategy.startKeylineSteps.size - 1, startIndex)
46 val startKeylines = strategy.startKeylineSteps[startIndex]
47 offset =
48 (startKeylines.firstFocal.unadjustedOffset - strategy.itemMainAxisSize / 2f)
49 .roundToInt()
50 }
51 if (itemCount > numOfFocalKeylines + 1 && itemIndex >= itemCount - endStepsSize) {
52 var endIndex = max(0, itemIndex - itemCount + endStepsSize)
53 endIndex = min(strategy.endKeylineSteps.size - 1, endIndex)
54 val endKeylines = strategy.endKeylineSteps[endIndex]
55 offset =
56 (endKeylines.firstFocal.unadjustedOffset - strategy.itemMainAxisSize / 2f).roundToInt()
57 }
58
59 return offset
60 }
61
KeylineSnapPositionnull62 internal fun KeylineSnapPosition(pageSize: CarouselPageSize): SnapPosition =
63 object : SnapPosition {
64 override fun position(
65 layoutSize: Int,
66 itemSize: Int,
67 beforeContentPadding: Int,
68 afterContentPadding: Int,
69 itemIndex: Int,
70 itemCount: Int
71 ): Int {
72 return getSnapPositionOffset(pageSize.strategy, itemIndex, itemCount)
73 }
74 }
75