diff options
author | Louis Pullen-Freilich <lpf@google.com> | 2019-10-15 17:53:42 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-10-15 17:53:42 +0000 |
commit | 20763dfd0e797ce83dd5d266b50388a6f4472f41 (patch) | |
tree | e47a1387e5f0123185fca502a1094c586b4dff20 | |
parent | 6edbab822b449f0c92f609ecf4cf051d43e7eec2 (diff) | |
parent | 82bd4697f619a644e7713e72c57935ac99488ab7 (diff) | |
download | support-20763dfd0e797ce83dd5d266b50388a6f4472f41.tar.gz |
Merge "Adds @sample for places in compose-runtime where we had inline docs" into androidx-master-dev
16 files changed, 509 insertions, 183 deletions
diff --git a/compose/compose-runtime/integration-tests/samples/build.gradle b/compose/compose-runtime/integration-tests/samples/build.gradle index 0789dfdd37f..0f0590adf3e 100644 --- a/compose/compose-runtime/integration-tests/samples/build.gradle +++ b/compose/compose-runtime/integration-tests/samples/build.gradle @@ -30,5 +30,7 @@ dependencies { implementation project(":annotation:annotation-sampled") implementation project(":compose:compose-runtime") - implementation project(":ui:ui-android-view-non-ir") + implementation project(":ui:ui-framework") + implementation project(":ui:ui-layout") + implementation project(":ui:ui-material") } diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/AmbientSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/AmbientSamples.kt new file mode 100644 index 00000000000..a62635e14e4 --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/AmbientSamples.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE") + +package androidx.compose.samples + +import androidx.annotation.Sampled +import androidx.compose.Ambient +import androidx.compose.Composable +import androidx.compose.ambient +import androidx.compose.unaryPlus + +@Sampled +fun createAmbient() { + val ActiveUser = Ambient.of<User> { error("No active user found!") } +} + +@Sampled +@Composable +fun ambientProvider() { + @Composable + fun App(user: User) { + ActiveUser.Provider(value = user) { + SomeScreen() + } + } +} + +@Sampled +@Composable +fun someScreenSample() { + @Composable + fun SomeScreen() { + UserPhoto() + } +} + +@Sampled +@Composable +fun consumeAmbient() { + @Composable + fun UserPhoto() { + val user = +ambient(ActiveUser) + ProfileIcon(src = user.profilePhotoUrl) + } +} + +private val ActiveUser = Ambient.of<User> { error("No active user found!") } + +@Composable private fun SomeScreen() {} + +@Composable private fun UserPhoto() {} diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/EffectSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/EffectSamples.kt index 6be15e9d7bc..c8fa7717c98 100644 --- a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/EffectSamples.kt +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/EffectSamples.kt @@ -16,8 +16,6 @@ package androidx.compose.samples -import android.widget.Button -import android.widget.TextView import androidx.annotation.Sampled import androidx.compose.Composable import androidx.compose.State @@ -27,7 +25,8 @@ import androidx.compose.onCommit import androidx.compose.state import androidx.compose.stateFor import androidx.compose.unaryPlus -import androidx.ui.androidview.adapters.setOnClick +import androidx.ui.core.Text +import androidx.ui.material.Button @Suppress("unused") @Sampled @@ -70,7 +69,7 @@ fun twoInputsKeySample() { fun SimpleStateSample() { val count = +state { 0 } - TextView(text = "You clicked ${count.value} times") + Text(text = "You clicked ${count.value} times") Button(text = "Click me", onClick = { count.value++ }) } @@ -79,7 +78,7 @@ fun SimpleStateSample() { fun DestructuredStateSample() { val (count, setCount) = +state { 0 } - TextView(text = "You clicked $count times") + Text(text = "You clicked $count times") Button(text = "Click me", onClick = { setCount(count + 1) }) } @@ -91,11 +90,10 @@ fun DestructuredStateSample() { fun DelegatedStateSample() { var count by +state { 0 } - TextView(text = "You clicked $count times") + Text(text = "You clicked $count times") Button(text = "Click me", onClick = { count = count + 1 }) } -private class User private class Subscription { fun unsubscribe() {} } @@ -112,6 +110,7 @@ private val elements = listOf<Element>() private class Element(val id: Int) @Suppress("UNUSED_PARAMETER") +@Composable private fun ListItem(item: Any, selected: Boolean) {} private const val parentId = 0 diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/KeySamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/KeySamples.kt new file mode 100644 index 00000000000..e2ec9f895c0 --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/KeySamples.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE") + +package androidx.compose.samples + +import androidx.annotation.Sampled +import androidx.compose.Composable +import androidx.compose.Key +import androidx.compose.unaryPlus + +@Sampled +@Composable +fun locallyUniqueKeys() { + for (user in users) { + Key(user.id) { UserPreview(user = user) } + } + + for (user in users.filter { isAdmin }) { + Key(user.id) { Friend(friend = user) } + } +} + +@Sampled +@Composable +fun notAlwaysUniqueKeys() { + for ((child, parent) in relationships) { + Key(parent.id) { + User(user = child) + User(user = parent) + } + } +} + +@Sampled +@Composable +fun moreCorrectUniqueKeys() { + for ((child, parent) in relationships) { + Key(parent.id to child.id) { + User(user = child) + User(user = parent) + } + } +} + +@Composable private fun User(user: User) {} + +@Composable private fun UserPreview(user: User) {} + +@Composable private fun Friend(friend: User) {} + +private const val isAdmin = true + +private val users = listOf<User>() +private val relationships = mapOf<User, User>() +private val child = User() diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/ModelSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/ModelSamples.kt new file mode 100644 index 00000000000..745488f8ceb --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/ModelSamples.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.compose.samples + +import androidx.compose.Composable +import androidx.compose.Model +import androidx.compose.memo +import androidx.compose.unaryPlus +import androidx.ui.core.TextField +import androidx.ui.material.Button + +@Composable +fun modelSample() { + @Model + class LoginState(var username: String, var password: String) { + fun login() = Api.login(username, password) + } + + @Composable + fun LoginScreen() { + val model = +memo { LoginState("user", "pass") } + + TextField( + value = model.username, + onValueChange = { model.username = it } + ) + TextField( + value = model.password, + onValueChange = { model.password = it } + ) + Button(text = "Login", onClick = { model.login() }) + } +} diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/PivotalSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/PivotalSamples.kt new file mode 100644 index 00000000000..542156225f4 --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/PivotalSamples.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused", "UNUSED_PARAMETER", "UNUSED_VARIABLE") + +package androidx.compose.samples + +import androidx.annotation.Sampled +import androidx.compose.Composable +import androidx.compose.Pivotal +import androidx.compose.onActive +import androidx.compose.onCommit +import androidx.compose.onDispose +import androidx.compose.state +import androidx.compose.unaryPlus +import androidx.ui.core.Text + +@Sampled +@Composable +fun incorrectUserOrdering() { + @Composable + fun UserList(userIds: List<Int>) { + for (id in userIds) { + UserRow(userId = id) + } + } + + @Composable + fun UserRow(userId: Int) { + var user by +state<User?> { null } + +onActive { + val dispose = Api.getUserAsync(userId) { user = it } + onDispose { dispose() } + } + + if (user == null) { + LoadingIndicator() + return + } + ProfileIcon(src = user!!.profilePhotoUrl) + Text(text = user!!.name) + } +} + +@Sampled +@Composable +fun expensiveApiCalls() { + @Composable + fun UserRow(userId: Int) { + var user by +state<User?> { null } + +onCommit(userId) { + val dispose = Api.getUserAsync(userId) { user = it } + onDispose { dispose() } + } + + if (user == null) { + LoadingIndicator() + return + } + ProfileIcon(src = user!!.profilePhotoUrl) + Text(text = user!!.name) + } +} + +@Sampled +@Composable +fun pivotalUsage() { + @Composable + fun UserRow(@Pivotal userId: Int) { + var user by +state<User?> { null } + +onActive { + val dispose = Api.getUserAsync(userId) { user = it } + onDispose { dispose() } + } + + if (user == null) { + LoadingIndicator() + return + } + ProfileIcon(src = user!!.profilePhotoUrl) + Text(text = user!!.name) + } +} + +@Composable private fun LoadingIndicator() {} + +@Composable private fun UserRow(userId: Int) {} diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/RecomposeSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/RecomposeSamples.kt new file mode 100644 index 00000000000..7eb34014c14 --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/RecomposeSamples.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.compose.samples + +import androidx.compose.Composable +import androidx.compose.Recompose +import androidx.compose.memo +import androidx.compose.unaryPlus +import androidx.ui.core.TextField +import androidx.ui.material.Button + +@Composable +fun recomposeSample() { + class LoginState(var username: String, var password: String) { + fun login() = Api.login(username, password) + } + + @Composable + fun LoginScreen() { + val model = +memo { LoginState("user", "pass") } + + Recompose { recompose -> + TextField( + value = model.username, + onValueChange = { + model.username = it + recompose() + } + ) + TextField( + value = model.password, + onValueChange = { + model.password = it + recompose() + } + ) + Button(text = "Login", onClick = { model.login() }) + } + } +} diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/SharedSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/SharedSamples.kt new file mode 100644 index 00000000000..f2270c3e2b6 --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/SharedSamples.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UNUSED_PARAMETER", "unused") + +package androidx.compose.samples + +import androidx.compose.Composable + +@Composable +internal fun ProfileIcon(src: String) {} + +internal class User { + val id = 0 + val name = "" + val profilePhotoUrl = "" +} + +internal object Api { + fun login(username: String, password: String) {} + fun getUserAsync(id: Int, user: (User) -> Unit): () -> Unit = {} +} diff --git a/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/SlotTableSamples.kt b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/SlotTableSamples.kt new file mode 100644 index 00000000000..eaaab06c8aa --- /dev/null +++ b/compose/compose-runtime/integration-tests/samples/src/main/java/androidx/compose/samples/SlotTableSamples.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused", "UNUSED_PARAMETER") + +package androidx.compose.samples + +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.Sampled +import androidx.compose.Composable + +@Sampled +@Composable +fun initialGroup() { + LinearLayout { + Contact(contact = jim) + Contact(contact = bob) + } +} + +@Sampled +@Composable +fun reorderedGroup() { + LinearLayout { + Contact(contact = bob) + Contact(contact = jim) + } +} + +@Sampled +@Composable +private fun contactSample() { + @Composable + fun Contact(contact: Contact) { + TextView(text = contact.name) + TextView(text = contact.email) + } +} + +@Suppress("UNUSED_PARAMETER") +@Composable +private fun Contact(contact: Contact) {} + +private data class Contact(val name: String, val email: String) + +private val jim = Contact("", "") +private val bob = Contact("", "")
\ No newline at end of file diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt index 96ec9a14173..9c25e117473 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Ambient.kt @@ -29,41 +29,30 @@ package androidx.compose * [Ambient]s by their nature are hierarchical. They make sense when the value of the ambient needs * to be scoped to a particular sub-hierarchy of the composition. * - * ``` - * // One must create an Ambient instance, which can be referenced by the consumers - * // statically. Ambient instances themselves hold no data, and can be thought of as a - * // type-safe identifier for the data being passed down a tree. The Ambient constructor takes a - * // single parameter, a factory to create a default value in cases where an ambient is - * // used without a Provider. If this is a situation you would rather not handle, you - * // can throw an error in this factory - * val ActiveUser = Ambient.of<User> { error("No active user found!") } + * One must create an Ambient instance, which can be referenced by the consumers statically. Ambient + * instances themselves hold no data, and can be thought of as a type-safe identifier for the data + * being passed down a tree. The Ambient constructor takes a single parameter, a factory to create a + * default value in cases where an ambient is used without a Provider. If this is a situation you + * would rather not handle, you can throw an error in this factory * - * // Somewhere up the tree, a "Provider" component can be used, which has a required - * // "value" parameter. This would often be at the "root" of a tree, but could be - * // anywhere, and can also be used in multiple places to override the provided value - * // for a sub-tree. - * @Composable fun App(user: User) { - * ActiveUser.Provider(value=user) { - * SomeScreen() - * } - * } + * @sample androidx.compose.samples.createAmbient * - * // intermediate components do not need to know about the ambient value, and can have - * // zero dependencies on it - * @Composable fun SomeScreen() { - * UserPhoto() - * } + * Somewhere up the tree, a [Provider] component can be used, which has a required `value` + * parameter. This would often be at the "root" of a tree, but could be anywhere, and can also be + * used in multiple places to override the provided value for a sub-tree. * - * // A component that wishes to consume the ambient value can use the corresponding - * // "ambient" effect, which returns the current value of the ambient, and subscribes the component - * // to changes of it - * @Composable fun UserPhoto() { - * val user = +ambient(ActiveUser) - * ImageView(src=user.profilePhotoUrl) - * } - * ``` + * @sample androidx.compose.samples.ambientProvider * - * @see [ambient] + * Intermediate components do not need to know about the ambient value, and can have zero + * dependencies on it. For example, `SomeScreen` might look like this: + * + * @sample androidx.compose.samples.someScreenSample + * + * Finally, a component that wishes to consume the ambient value can use the corresponding + * [ambient] effect, which returns the current value of the ambient, and subscribes the component + * to changes of it. + * + * @sample androidx.compose.samples.consumeAmbient */ class Ambient<T> @PublishedApi @@ -105,7 +94,7 @@ internal constructor(private val key: String, private val defaultFactory: (() -> * * @param value The current value of the ambient. * @param children Everything composed inside this block will get [value] when consuming this - * ambient + * ambient * * @see ambient */ diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt index 15d0b18c398..a7a827719e9 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Effects.kt @@ -214,7 +214,8 @@ internal class PostCommitScopeImpl( * * @sample androidx.compose.samples.oneInputKeySample * - * @param v1 The value to use as the key. This will be compared to its previous value using `Object.equals` + * @param v1 The value to use as the key. This will be compared to its previous value using + * [Any.equals] * @param block The block to execute other effects in */ @CheckResult("+") @@ -231,8 +232,10 @@ fun <T, V1> key(v1: V1, block: Effect<T>.() -> T) = * * @sample androidx.compose.samples.twoInputsKeySample * - * @param v1 The first value to use as a key. This will be compared to its previous value using `Object.equals` - * @param v2 The second value to use as a key. This will be compared to its previous value using `Object.equals` + * @param v1 The first value to use as a key. This will be compared to its previous value using + * [Any.equals] + * @param v2 The second value to use as a key. This will be compared to its previous value using + * [Any.equals] * @param block The block to execute other effects in */ @CheckResult("+") @@ -247,7 +250,8 @@ fun <T, V1, V2> key(v1: V1, v2: V2, block: Effect<T>.() -> T) = * * @sample androidx.compose.samples.twoInputsKeySample * - * @param inputs The set of values to be used to create a compound key. This will be compared to its previous value using `Object.equals` + * @param inputs The set of values to be used to create a compound key. This will be compared to + * its previous value using [Any.equals] * @param block The block to execute other effects in */ @CheckResult("+") diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt index 41e9c569412..88e04ad2f06 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Key.kt @@ -26,13 +26,7 @@ package androidx.compose * * For instance, consider the following example: * - * for (user in users) { - * Key(user.id) { UserPreview(user=child) } - * } - * - * for (user in users.filter { isAdmin }) { - * Key(user.id) { Friend(friend=user) } - * } + * @sample androidx.compose.samples.locallyUniqueKeys * * Even though there are users with the same id composed in both the top and the bottom loop, * because they are different calls to Key, there is no need to create compound keys. @@ -42,27 +36,16 @@ package androidx.compose * * For instance, consider the following example: * - * for ((child, parent) in relationships) { - * Key(parent.id) { - * User(user=child) - * User(user=parent) - * } - * } + * @sample androidx.compose.samples.notAlwaysUniqueKeys * * This example assumes that `parent.id` is a unique key for each item in the collection, * but this is only true if it is fair to assume that a parent will only ever have a single child, * which may not be the case. Instead, it may be more correct to do the following: * - * for ((child, parent) in relationships) { - * Key(parent.id to child.id) { - * User(user=child) - * User(user=parent) - * } - * } - * + * @sample androidx.compose.samples.moreCorrectUniqueKeys * * @param key The value used to identify this group. The value will be compared for equality - * using [Object.equals] and hashed using [Object.hashCode]. + * using [Any.equals] and hashed using [Any.hashCode]. * @param children The composable children for this group. * * @see [androidx.compose.key] diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt index 9682b693529..0c167b3240b 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt @@ -28,24 +28,11 @@ package androidx.compose * * Consider the following example: * - * @Model - * class LoginState(var username: String, var password: String) { - * val valid: Boolean get() = username.length > 0 && password.length > 0 - * fun login() = Api.login(username, password) - * } - * - * @Composable - * fun LoginScreen() { - * val model = +memo { LoginState() } - * - * EditText(text=model.username, onTextChange={ model.username = it }) - * EditText(text=model.password, onTextChange={ model.password = it }) - * Button(text="Login", enabled=model.valid, onPress={ model.login() }) - * } + * @sample androidx.compose.samples.modelSample * * In this example, `LoginScreen` is recomposed every time the username and password of the - * model updates, keeping the UI synchronized with the model. There is no need to call `recompose` - * manually. + * model updates, keeping the UI synchronized with the model. There is no need to call + * [androidx.compose.Recompose] manually. * * @see FrameManager * @see Observe diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Pivotal.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Pivotal.kt index b4620be326e..a94e9ae3e0c 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Pivotal.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Pivotal.kt @@ -17,15 +17,14 @@ package androidx.compose /** - * @Pivotal can be applied to the parameters of a composable to indicate that - * the parameter contributes to the "identity" of the composable. Pivotal - * parameters are used when calculating the composable's `key`, and the - * composable's key is used to determine whether or not the previous results and state of the - * composable have moved or can be reused. + * [Pivotal] can be applied to the parameters of a composable to indicate that the parameter + * contributes to the "identity" of the composable. Pivotal parameters are used when calculating + * the composable's `key`, and the composable's key is used to determine whether or not the previous + * results and state of the composable have moved or can be reused. * * By setting a parameter to a composable function as [Pivotal], you are ensuring that for the - * life time of that composable in the composition, that parameter will remain unchanged. If it does - * change (as in, the previous and current values passed into [Object.equals] evaluate to false), + * lifetime of that composable in the composition, that parameter will remain unchanged. If it does + * change (as in, the previous and current values passed into [Any.equals] evaluate to false), * then the composable will start a new life time, as if it had been removed and recreated. * * As a result, the [Pivotal] annotation can be used to simplify component logic, as well as improve @@ -34,49 +33,16 @@ package androidx.compose * Let's consider the following example, where we have a list of users being displayed from a list * of user ids: * - * @Composable - * fun UserList(userIds: List<Int>) { - * for (id in userIds) { - * UserRow(userId=id) - * } - * } - * - * @Composable - * fun UserRow(userId: Int) { - * val user = +state<User?> { null } - * +onActive { - * val dispose = Api.getUserAsync(userId) { user = it } - * onDispose { dispose() } - * } - * if (user == null) { - * LoadingIndicator() - * return - * } - * Image(src=user.profileImage) - * Text(text=user.name) - * } + * @sample androidx.compose.samples.incorrectUserOrdering * * This example has a bug in it. If the list of user ids is reordered in any way, Compose will reuse - * previous instances of the UserRow(...) that were created with previous ids. This means that user + * previous instances of the `UserRow` that were created with previous ids. This means that user * requests that had previously come in will show up in the wrong position. Semantically, the author * of this code had intended `UserRow` to move with the user id, but had not written it to do so. * * One way to fix this would be to change `onActive` to `onCommit(userId)`: * - * @Composable - * fun UserRow(userId: Int) { - * val user = +state<User?> { null } - * +onCommit(userId) { - * val dispose = Api.getUserAsync(userId) { user = it } - * onDispose { dispose() } - * } - * if (user == null) { - * LoadingIndicator() - * return - * } - * Image(src=user.profileImage) - * Text(text=user.name) - * } + * @sample androidx.compose.samples.expensiveApiCalls * * In this rendition, the proper users will show up in the proper places, however, in the case where * the list of user Ids is shuffled, it is likely that the program will have to execute every API @@ -88,20 +54,7 @@ package androidx.compose * * The ideal and correct implementation of the above `UserRow` component is thus as follows: * - * @Composable - * fun UserRow(@Pivotal userId: Int) { - * val user = +state<User?> { null } - * +onActive { - * val dispose = Api.getUserAsync(userId) { user = it } - * onDispose { dispose() } - * } - * if (user == null) { - * LoadingIndicator() - * return - * } - * Image(src=user.profileImage) - * Text(text=user.name) - * } + * @sample androidx.compose.samples.pivotalUsage * * @see Key * @see key diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt index a53cc256243..2d61183ab11 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt @@ -37,20 +37,7 @@ private class RecomposeHelper : Function0<Unit> { * * Example: * - * class LoginState(var username: String, var password: String) { - * val valid: Boolean get() = username.length > 0 && password.length > 0 - * fun login() = Api.login(username, password) - * } - * - * @Composable - * fun LoginScreen() { - * val model = +memo { LoginState() } - * Recompose { recompose -> - * EditText(text=model.username, onTextChange={ model.username = it; recompose() }) - * EditText(text=model.password, onTextChange={ model.password = it; recompose() }) - * Button(text="Login", enabled=model.valid, onPress={ model.login() }) - * } - * } + * @sample androidx.compose.samples.recomposeSample * * Note: The above example can be done without [Recompose] by annotating `LoginState` with [Model]. * diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt index 5ef7ceb5576..0eda66d4670 100644 --- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt +++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/SlotTable.kt @@ -33,46 +33,35 @@ package androidx.compose * only counts as one node in its group regardless of the number of nodes it contains. * * ASIDE: - * The intent is for groups are to represent memoized function calls and nodes represent views. For - * example, + * The intent is that groups represent memoized function calls and nodes represent views. For + * example: * - * LinearLayout { - * Contact(contact=jim) - * Contact(contact=bob) - * } + * @sample androidx.compose.samples.initialGroup * - * the <LinearLayout> tag here would be a node (the linear layout view). The node contains the - * groups for the child views of the linear layout. + * the `LinearLayout` here would be a node (the linear layout view). The node contains the + * groups for the child views of the linear layout. * - * If contact's composition looks like: + * If contact's composition looks like: * - * @Composable - * fun Contact(contact: Contact) { - * TextView(text=contact.name) - * TextView(text=contact.email) - * } + * @sample androidx.compose.samples.contactSample * - * then composing contact into the linear layout would add two views to the linear layout's - * children. The composition of contact creates groups, one for each text view. The groups for each - * contact would be able to report that it produces two views (that is the group created for - * Contact has two nodes). Summing the nodes in the group produces the number of views (as - * each node corresponds to a view). + * then composing contact into the linear layout would add two views to the linear layout's + * children. The composition of contact creates groups, one for each text view. The groups for each + * contact would be able to report that it produces two views (that is the group created for + * Contact has two nodes). Summing the nodes in the group produces the number of views (as + * each node corresponds to a view). * - * If the order that jim and bob change above, + * If the order of jim and bob change: * - * LinearLayout { - * Contact(contact=bob) - * Contact(contact=jim) - * } + * @sample androidx.compose.samples.reorderedGroup * - * the previous result can be reused by moving the views generated bob's group before jim's (or vis - * versa). A composition algorithm could use the key information for each group to determine if they - * can be switched. For example, since the first contact's group has two nodes the composition - * algorithm can infer that the beginning of jim's views starts at 2 and contains 2 view. To move - * jim in front of bob, move the 2 views from offset 2 to offset 0. If contact is immutable, for - * example, Contact would only need to be recomposed if the value of jim or bob change. + * the previous result can be reused by moving the views generated bob's group before jim's (or vis + * versa). A composition algorithm could use the key information for each group to determine if they + * can be switched. For example, since the first contact's group has two nodes the composition + * algorithm can infer that the beginning of jim's views starts at 2 and contains 2 view. To move + * jim in front of bob, move the 2 views from offset 2 to offset 0. If contact is immutable, for + * example, Contact would only need to be recomposed if the value of jim or bob change. */ - open class SlotEditor internal constructor(val table: SlotTable) { var current = 0 internal val slots get() = table.slots |