1 /*
<lambda>null2  * Copyright 2023 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 @file:JvmName("PrimitiveSnapshotStateKt")
18 @file:JvmMultifileClass
19 
20 package androidx.compose.runtime
21 
22 import androidx.compose.runtime.internal.JvmDefaultWithCompatibility
23 import androidx.compose.runtime.internal.equalsWithNanFix
24 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
25 import androidx.compose.runtime.snapshots.GlobalSnapshot
26 import androidx.compose.runtime.snapshots.Snapshot
27 import androidx.compose.runtime.snapshots.SnapshotId
28 import androidx.compose.runtime.snapshots.SnapshotMutableState
29 import androidx.compose.runtime.snapshots.StateFactoryMarker
30 import androidx.compose.runtime.snapshots.StateObjectImpl
31 import androidx.compose.runtime.snapshots.StateRecord
32 import androidx.compose.runtime.snapshots.currentSnapshot
33 import androidx.compose.runtime.snapshots.overwritable
34 import androidx.compose.runtime.snapshots.readable
35 import androidx.compose.runtime.snapshots.toSnapshotId
36 import androidx.compose.runtime.snapshots.withCurrent
37 import kotlin.jvm.JvmMultifileClass
38 import kotlin.jvm.JvmName
39 import kotlin.reflect.KProperty
40 
41 /**
42  * Return a new [MutableFloatState] initialized with the passed in [value]
43  *
44  * The MutableFloatState class is a single value holder whose reads and writes are observed by
45  * Compose. Additionally, writes to it are transacted as part of the [Snapshot] system. On the JVM,
46  * values are stored in memory as the primitive `float` type, avoiding the autoboxing that occurs
47  * when using `MutableState<Float>`.
48  *
49  * @param value the initial value for the [MutableFloatState]
50  * @see FloatState
51  * @see MutableFloatState
52  * @see mutableStateOf
53  * @see mutableIntStateOf
54  * @see mutableLongStateOf
55  * @see mutableDoubleStateOf
56  */
57 @StateFactoryMarker
58 fun mutableFloatStateOf(value: Float): MutableFloatState = createSnapshotMutableFloatState(value)
59 
60 /**
61  * A value holder where reads to the [floatValue] property during the execution of a [Composable]
62  * function cause the current [RecomposeScope] to subscribe to changes of that value.
63  *
64  * @see MutableFloatState
65  * @see mutableDoubleStateOf
66  */
67 @Stable
68 @JvmDefaultWithCompatibility
69 interface FloatState : State<Float> {
70     @get:AutoboxingStateValueProperty("floatValue")
71     override val value: Float
72         @Suppress("AutoBoxing") get() = floatValue
73 
74     val floatValue: Float
75 }
76 
77 /** Permits property delegation of `val`s using `by` for [FloatState]. */
78 @Suppress("NOTHING_TO_INLINE")
getValuenull79 inline operator fun FloatState.getValue(thisObj: Any?, property: KProperty<*>): Float = floatValue
80 
81 /**
82  * A value holder where reads to the [floatValue] property during the execution of a [Composable]
83  * function cause the current [RecomposeScope] to subscribe to changes of that value. When the
84  * [floatValue] property is written to and changed, a recomposition of any subscribed
85  * [RecomposeScope]s will be scheduled. If [floatValue] is written to with the same value, no
86  * recompositions will be scheduled.
87  *
88  * @see [FloatState]
89  * @see [mutableDoubleStateOf]
90  */
91 @Stable
92 @JvmDefaultWithCompatibility
93 interface MutableFloatState : FloatState, MutableState<Float> {
94     @get:AutoboxingStateValueProperty("floatValue")
95     @set:AutoboxingStateValueProperty("floatValue")
96     override var value: Float
97         @Suppress("AutoBoxing") get() = floatValue
98         set(value) {
99             floatValue = value
100         }
101 
102     override var floatValue: Float
103 }
104 
105 /** Permits property delegation of `var`s using `by` for [MutableFloatState]. */
106 @Suppress("NOTHING_TO_INLINE")
setValuenull107 inline operator fun MutableFloatState.setValue(
108     thisObj: Any?,
109     property: KProperty<*>,
110     value: Float
111 ) {
112     this.floatValue = value
113 }
114 
createSnapshotMutableFloatStatenull115 internal expect fun createSnapshotMutableFloatState(value: Float): MutableFloatState
116 
117 /**
118  * A single value holder whose reads and writes are observed by Compose.
119  *
120  * Additionally, writes to it are transacted as part of the [Snapshot] system.
121  *
122  * @param value the wrapped value
123  * @see [mutableDoubleStateOf]
124  */
125 internal open class SnapshotMutableFloatStateImpl(value: Float) :
126     StateObjectImpl(), MutableFloatState, SnapshotMutableState<Float> {
127 
128     private var next =
129         currentSnapshot().let { snapshot ->
130             FloatStateStateRecord(snapshot.snapshotId, value).also {
131                 if (snapshot !is GlobalSnapshot) {
132                     it.next =
133                         FloatStateStateRecord(Snapshot.PreexistingSnapshotId.toSnapshotId(), value)
134                 }
135             }
136         }
137 
138     override val firstStateRecord: StateRecord
139         get() = next
140 
141     override var floatValue: Float
142         get() = next.readable(this).value
143         set(value) =
144             next.withCurrent {
145                 if (!it.value.equalsWithNanFix(value)) {
146                     next.overwritable(this, it) { this.value = value }
147                 }
148             }
149 
150     // Arbitrary policies are not allowed. The underlying `==` implementation
151     // for primitive types corresponds to structural equality
152     override val policy: SnapshotMutationPolicy<Float>
153         get() = structuralEqualityPolicy()
154 
155     override fun component1(): Float = floatValue
156 
157     override fun component2(): (Float) -> Unit = { floatValue = it }
158 
159     override fun prependStateRecord(value: StateRecord) {
160         next = value as FloatStateStateRecord
161     }
162 
163     override fun mergeRecords(
164         previous: StateRecord,
165         current: StateRecord,
166         applied: StateRecord
167     ): StateRecord? {
168         val currentRecord = current as FloatStateStateRecord
169         val appliedRecord = applied as FloatStateStateRecord
170         return if (currentRecord.value.equalsWithNanFix(appliedRecord.value)) {
171             current
172         } else {
173             null
174         }
175     }
176 
177     override fun toString(): String =
178         next.withCurrent { "MutableFloatState(value=${it.value})@${hashCode()}" }
179 
180     private class FloatStateStateRecord(snapshotId: SnapshotId, var value: Float) :
181         StateRecord(snapshotId) {
182         override fun assign(value: StateRecord) {
183             this.value = (value as FloatStateStateRecord).value
184         }
185 
186         override fun create(): StateRecord = create(currentSnapshot().snapshotId)
187 
188         override fun create(snapshotId: SnapshotId): StateRecord =
189             FloatStateStateRecord(snapshotId, value)
190     }
191 }
192