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.adaptive.layout
18
19 import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
20 import androidx.compose.runtime.Composable
21 import androidx.compose.runtime.Immutable
22 import androidx.compose.runtime.remember
23 import androidx.compose.ui.unit.IntSize
24
25 /**
26 * The class that provides motion settings for three pane scaffolds like [ListDetailPaneScaffold]
27 * and [SupportingPaneScaffold].
28 */
29 @ExperimentalMaterial3AdaptiveApi
30 @Immutable
31 class ThreePaneMotion
32 internal constructor(
33 private val primaryPaneMotion: PaneMotion,
34 private val secondaryPaneMotion: PaneMotion,
35 private val tertiaryPaneMotion: PaneMotion,
36 ) {
37 /**
38 * Gets the specified [PaneMotion] of a given pane role.
39 *
40 * @param role the specified role of the pane, see [ListDetailPaneScaffoldRole] and
41 * [SupportingPaneScaffoldRole].
42 */
43 operator fun get(role: ThreePaneScaffoldRole): PaneMotion =
44 when (role) {
45 ThreePaneScaffoldRole.Primary -> primaryPaneMotion
46 ThreePaneScaffoldRole.Secondary -> secondaryPaneMotion
47 ThreePaneScaffoldRole.Tertiary -> tertiaryPaneMotion
48 }
49
50 override fun equals(other: Any?): Boolean {
51 if (this === other) return true
52 if (other !is ThreePaneMotion) return false
53 if (primaryPaneMotion != other.primaryPaneMotion) return false
54 if (secondaryPaneMotion != other.secondaryPaneMotion) return false
55 if (tertiaryPaneMotion != other.tertiaryPaneMotion) return false
56 return true
57 }
58
59 override fun hashCode(): Int {
60 var result = primaryPaneMotion.hashCode()
61 result = 31 * result + secondaryPaneMotion.hashCode()
62 result = 31 * result + tertiaryPaneMotion.hashCode()
63 return result
64 }
65
66 override fun toString(): String {
67 return "ThreePaneMotion(" +
68 "primaryPaneMotion=$primaryPaneMotion, " +
69 "secondaryPaneMotion=$secondaryPaneMotion, " +
70 "tertiaryPaneMotion=$tertiaryPaneMotion)"
71 }
72
73 companion object {
74 /** A default [ThreePaneMotion] instance that specifies no motions. */
75 val NoMotion =
76 ThreePaneMotion(PaneMotion.NoMotion, PaneMotion.NoMotion, PaneMotion.NoMotion)
77 }
78 }
79
80 @ExperimentalMaterial3AdaptiveApi
81 @Composable
calculateThreePaneMotionnull82 internal fun ThreePaneScaffoldState.calculateThreePaneMotion(
83 ltrPaneOrder: ThreePaneScaffoldHorizontalOrder
84 ): ThreePaneMotion {
85 class ThreePaneMotionHolder(var value: ThreePaneMotion)
86
87 val resultHolder = remember { ThreePaneMotionHolder(ThreePaneMotion.NoMotion) }
88 if (currentState != targetState) {
89 // Only update motions when the state changes to prevent unnecessary recomposition at the
90 // end of state transitions.
91 val paneMotions = calculatePaneMotion(currentState, targetState, ltrPaneOrder)
92 resultHolder.value =
93 ThreePaneMotion(
94 paneMotions[ltrPaneOrder.indexOf(ThreePaneScaffoldRole.Primary)],
95 paneMotions[ltrPaneOrder.indexOf(ThreePaneScaffoldRole.Secondary)],
96 paneMotions[ltrPaneOrder.indexOf(ThreePaneScaffoldRole.Tertiary)]
97 )
98 }
99 return resultHolder.value
100 }
101
102 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
103 @Suppress("PrimitiveInCollection") // No way to get underlying Long of IntSize or IntOffset
104 internal class ThreePaneScaffoldMotionDataProvider :
105 PaneScaffoldMotionDataProvider<ThreePaneScaffoldRole> {
106 private lateinit var ltrOrder: ThreePaneScaffoldHorizontalOrder
107
108 private val primary = PaneMotionData()
109 private val secondary = PaneMotionData()
110 private val tertiary = PaneMotionData()
111
112 override var scaffoldSize: IntSize = IntSize.Zero
113
114 override val count: Int = 3
115
getRoleAtnull116 override fun getRoleAt(index: Int): ThreePaneScaffoldRole = ltrOrder[index]
117
118 override fun get(role: ThreePaneScaffoldRole): PaneMotionData =
119 when (role) {
120 ThreePaneScaffoldRole.Primary -> primary
121 ThreePaneScaffoldRole.Secondary -> secondary
122 ThreePaneScaffoldRole.Tertiary -> tertiary
123 }
124
getnull125 override fun get(index: Int): PaneMotionData = get(getRoleAt(index))
126
127 internal fun update(
128 threePaneMotion: ThreePaneMotion,
129 ltrOrder: ThreePaneScaffoldHorizontalOrder
130 ) {
131 this.ltrOrder = ltrOrder
132 forEach { role, it ->
133 it.motion = threePaneMotion[role]
134 it.isOriginSizeAndPositionSet = false
135 }
136 }
137 }
138