Merge "Provide composition local map to layout nodes in backwards compatible way" into androidx-main am: 71d9446cbf
Original change: https://android-review.googlesource.com/c/platform/frameworks/support/+/2506542
Change-Id: I6bf3527db54771b4cd891a16d2adc8eb99348d67
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index 2001639..750e9a7 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -1,3 +1,7 @@
// Baseline format: 1.0
RemovedClass: androidx.compose.ui.platform.AndroidComposeView_androidKt:
Removed class androidx.compose.ui.platform.AndroidComposeView_androidKt
+
+// see b/275084979 for details. API not actually removed, but metalave confused by JvmName + Deprecated
+RemovedMethod: androidx.compose.ui.ComposedModifierKt#materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier):
+ Removed method androidx.compose.ui.ComposedModifierKt.materialize(androidx.compose.runtime.Composer,androidx.compose.ui.Modifier)
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 5a1455d..7d40ab0 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -113,7 +113,8 @@
public final class ComposedModifierKt {
method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method @Deprecated public static androidx.compose.ui.Modifier materializeWithCompositionLocalInjection(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
}
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index fa8d56d..ac7252f 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -117,7 +117,8 @@
method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, Object? key3, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object![]? keys, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method @Deprecated public static androidx.compose.ui.Modifier materializeWithCompositionLocalInjection(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
}
@kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalComposeUiApi {
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index 2001639..ef0ff9c 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -1,3 +1,7 @@
// Baseline format: 1.0
RemovedClass: androidx.compose.ui.platform.AndroidComposeView_androidKt:
Removed class androidx.compose.ui.platform.AndroidComposeView_androidKt
+
+
+RemovedMethod: androidx.compose.ui.ComposedModifierKt#materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier):
+ Removed method androidx.compose.ui.ComposedModifierKt.materialize(androidx.compose.runtime.Composer,androidx.compose.ui.Modifier)
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index c8fa151..60531e4 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -113,7 +113,8 @@
public final class ComposedModifierKt {
method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
- method public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+ method @Deprecated public static androidx.compose.ui.Modifier materializeWithCompositionLocalInjection(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
}
@androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
@@ -1956,7 +1957,8 @@
method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static inline void Layout(java.util.List<? extends kotlin.jvm.functions.Function0<kotlin.Unit>> contents, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MultiContentMeasurePolicy measurePolicy);
method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void MultiMeasureLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
method @kotlin.PublishedApi internal static kotlin.jvm.functions.Function0<kotlin.Unit> combineAsVirtualLayouts(java.util.List<? extends kotlin.jvm.functions.Function0<kotlin.Unit>> contents);
- method @kotlin.PublishedApi internal static kotlin.jvm.functions.Function1<androidx.compose.runtime.SkippableUpdater<androidx.compose.ui.node.ComposeUiNode>,kotlin.Unit> materializerOf(androidx.compose.ui.Modifier modifier);
+ method @Deprecated @kotlin.PublishedApi internal static kotlin.jvm.functions.Function1<androidx.compose.runtime.SkippableUpdater<androidx.compose.ui.node.ComposeUiNode>,kotlin.Unit> materializerOf(androidx.compose.ui.Modifier modifier);
+ method @kotlin.PublishedApi internal static kotlin.jvm.functions.Function1<androidx.compose.runtime.SkippableUpdater<androidx.compose.ui.node.ComposeUiNode>,kotlin.Unit> modifierMaterializerOf(androidx.compose.ui.Modifier modifier);
}
@kotlin.jvm.JvmDefaultWithCompatibility public interface LayoutModifier extends androidx.compose.ui.Modifier.Element {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
new file mode 100644
index 0000000..3526180
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2023 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.ui.modifier
+
+import android.view.View
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Applier
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ReusableComposeNode
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.UiComposable
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.materializerOfWithCompositionLocalInjection
+import androidx.compose.ui.materializeWithCompositionLocalInjectionInternal
+import androidx.compose.ui.node.ComposeUiNode
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverNode
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class CompositionLocalMapInjectionTest {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun consumeInDraw() {
+ // No assertions needed. This not crashing is the test
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides 1) {
+ OldBox(modifierOf { ConsumeInDrawNode() }.size(10.dp))
+ }
+ }
+ }
+
+ @Test
+ fun consumeInLayout() {
+ // No assertions needed. This not crashing is the test
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides 1) {
+ OldBox(modifierOf { ConsumeInLayoutNode() }.size(10.dp))
+ }
+ }
+ }
+
+ @Test
+ fun consumeInAttach() {
+ // No assertions needed. This not crashing is the test
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides 1) {
+ OldBox(modifierOf { ConsumeInAttachNode() }.size(10.dp))
+ }
+ }
+ }
+
+ @Test
+ fun consumeInDrawGetsNotifiedOfChanges() {
+ val node = ConsumeInDrawNode()
+ var state by mutableStateOf(1)
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides state) {
+ OldBox(modifierOf { node }.size(10.dp))
+ }
+ }
+ assertThat(node.int).isEqualTo(state)
+ state = 2
+ rule.runOnIdle {
+ assertThat(node.int).isEqualTo(state)
+ }
+ }
+
+ @Test
+ fun consumeInLayoutGetsNotifiedOfChanges() {
+ val node = ConsumeInLayoutNode()
+ var state by mutableStateOf(1)
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides state) {
+ OldBox(modifierOf { node }.size(10.dp))
+ }
+ }
+ assertThat(node.int).isEqualTo(state)
+ state = 2
+ rule.runOnIdle {
+ assertThat(node.int).isEqualTo(state)
+ }
+ }
+
+ @Test
+ fun consumeInAttachGetsNotifiedOfChanges() {
+ val node = ConsumeInAttachNode()
+ var state by mutableStateOf(1)
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides state) {
+ OldBox(modifierOf { node }.size(10.dp))
+ }
+ }
+ assertThat(node.int).isEqualTo(state)
+ state = 2
+ rule.runOnIdle {
+ assertThat(node.int).isEqualTo(state)
+ }
+ }
+
+ @Test
+ fun consumeInDrawSkippableUpdate() {
+ // No assertions needed. This not crashing is the test
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides 1) {
+ OldBoxSkippableUpdate(modifierOf { ConsumeInDrawNode() }.size(10.dp))
+ }
+ }
+ }
+
+ @Test
+ fun consumeInLayoutSkippableUpdate() {
+ // No assertions needed. This not crashing is the test
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides 1) {
+ OldBoxSkippableUpdate(modifierOf { ConsumeInLayoutNode() }.size(10.dp))
+ }
+ }
+ }
+
+ @Test
+ fun consumeInAttachSkippableUpdate() {
+ // No assertions needed. This not crashing is the test
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides 1) {
+ OldBoxSkippableUpdate(modifierOf { ConsumeInAttachNode() }.size(10.dp))
+ }
+ }
+ }
+
+ @Test
+ fun consumeInDrawGetsNotifiedOfChangesSkippableUpdate() {
+ val node = ConsumeInDrawNode()
+ var state by mutableStateOf(1)
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides state) {
+ OldBoxSkippableUpdate(modifierOf { node }.size(10.dp))
+ }
+ }
+ assertThat(node.int).isEqualTo(state)
+ state = 2
+ rule.runOnIdle {
+ assertThat(node.int).isEqualTo(state)
+ }
+ }
+
+ @Test
+ fun consumeInLayoutGetsNotifiedOfChangesSkippableUpdate() {
+ val node = ConsumeInLayoutNode()
+ var state by mutableStateOf(1)
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides state) {
+ OldBoxSkippableUpdate(modifierOf { node }.size(10.dp))
+ }
+ }
+ assertThat(node.int).isEqualTo(state)
+ state = 2
+ rule.runOnIdle {
+ assertThat(node.int).isEqualTo(state)
+ }
+ }
+
+ @Test
+ fun consumeInAttachGetsNotifiedOfChangesSkippableUpdate() {
+ val node = ConsumeInAttachNode()
+ var state by mutableStateOf(1)
+ rule.setContent {
+ CompositionLocalProvider(SomeLocal provides state) {
+ OldBoxSkippableUpdate(modifierOf { node }.size(10.dp))
+ }
+ }
+ assertThat(node.int).isEqualTo(state)
+ state = 2
+ rule.runOnIdle {
+ assertThat(node.int).isEqualTo(state)
+ }
+ }
+}
+
+val SomeLocal = compositionLocalOf<Int> { error("unprovided value") }
+
+inline fun <reified T : Modifier.Node> modifierOf(crossinline fn: () -> T) =
+ object : ModifierNodeElement<T>() {
+ override fun create() = fn()
+ override fun hashCode() = System.identityHashCode(this)
+ override fun equals(other: Any?) = other === this
+ override fun update(node: T) = node
+ }
+
+class ConsumeInDrawNode : CompositionLocalConsumerModifierNode, DrawModifierNode, Modifier.Node() {
+ var view: View? = null
+ var int: Int? = null
+ override fun ContentDrawScope.draw() {
+ // Consume Static local
+ view = currentValueOf(LocalView)
+ // Consume Freshly Provided Local
+ int = currentValueOf(SomeLocal)
+ }
+}
+
+class ConsumeInLayoutNode :
+ CompositionLocalConsumerModifierNode,
+ LayoutModifierNode,
+ Modifier.Node() {
+ var view: View? = null
+ var int: Int? = null
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ // Consume Static local
+ view = currentValueOf(LocalView)
+ // Consume Freshly Provided Local
+ int = currentValueOf(SomeLocal)
+ val placeable = measurable.measure(constraints)
+ return layout(constraints.minWidth, constraints.maxWidth) {
+ placeable.place(0, 0)
+ }
+ }
+}
+
+class ConsumeInAttachNode : CompositionLocalConsumerModifierNode, ObserverNode, Modifier.Node() {
+ var view: View? = null
+ var int: Int? = null
+ private fun readLocals() {
+ // Consume Static local
+ view = currentValueOf(LocalView)
+ // Consume Freshly Provided Local
+ int = currentValueOf(SomeLocal)
+ }
+ override fun onAttach() {
+ observeReads { readLocals() }
+ }
+ override fun onObservedReadsChanged() {
+ observeReads { readLocals() }
+ }
+}
+
+// This composable is intentionally written to look like the "old" version of Layout, before
+// aosp/2318839. This function allows us to emulate what a module targeting an older version of
+// compose UI would have inlined into their function body. See b/275067189 for more details.
+@UiComposable
+@Composable
+inline fun OldLayoutSkippableUpdate(
+ content: @Composable @UiComposable () -> Unit,
+ modifier: Modifier = Modifier,
+ measurePolicy: MeasurePolicy
+) {
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ val viewConfiguration = LocalViewConfiguration.current
+ @Suppress("DEPRECATION")
+ ReusableComposeNode<ComposeUiNode, Applier<Any>>(
+ factory = ComposeUiNode.Constructor,
+ update = {
+ set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
+ set(density, ComposeUiNode.SetDensity)
+ set(layoutDirection, ComposeUiNode.SetLayoutDirection)
+ set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
+ },
+ // The old version of Layout called a function called "materializerOf". The function below
+ // has the same JVM signature as that function used to have, so the code that this source
+ // generates will be essentially identical to what will have been generated in older versions
+ // of UI, despite this name being different now.
+ skippableUpdate = materializerOfWithCompositionLocalInjection(modifier),
+ content = content
+ )
+}
+
+@Suppress("NOTHING_TO_INLINE")
+@Composable
+@UiComposable
+internal inline fun OldLayout(
+ modifier: Modifier = Modifier,
+ measurePolicy: MeasurePolicy
+) {
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ val viewConfiguration = LocalViewConfiguration.current
+ // The old version of Layout called a function called "materialize". The function below
+ // has the same JVM signature as that function used to have, so the code that this source
+ // generates will be essentially identical to what will have been generated in older versions
+ // of UI, despite this name being different now.
+ val materialized = currentComposer.materializeWithCompositionLocalInjectionInternal(modifier)
+ ReusableComposeNode<ComposeUiNode, Applier<Any>>(
+ factory = ComposeUiNode.Constructor,
+ update = {
+ set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
+ set(density, ComposeUiNode.SetDensity)
+ set(layoutDirection, ComposeUiNode.SetLayoutDirection)
+ set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
+ set(materialized, ComposeUiNode.SetModifier)
+ },
+ )
+}
+private val EmptyBoxMeasurePolicy = MeasurePolicy { _, constraints ->
+ layout(constraints.minWidth, constraints.minHeight) {}
+}
+
+@Composable fun OldBoxSkippableUpdate(modifier: Modifier = Modifier) {
+ OldLayoutSkippableUpdate({ }, modifier, EmptyBoxMeasurePolicy)
+}
+
+@Composable fun OldBox(modifier: Modifier = Modifier) {
+ OldLayout(modifier, EmptyBoxMeasurePolicy)
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
index a06b7ea..a1872aa 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
@@ -18,7 +18,10 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composer
+import androidx.compose.runtime.CompositionLocalMap
import androidx.compose.runtime.Stable
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.requireLayoutNode
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.InspectorValueInfo
import androidx.compose.ui.platform.NoInspectorInfo
@@ -244,6 +247,8 @@
* You almost certainly do not need to call this function directly.
*/
@Suppress("ModifierFactoryExtensionFunction")
+// "materialize" JVM name is taken below to solve a backwards-incompatibility
+@JvmName("materializeModifier")
fun Composer.materialize(modifier: Modifier): Modifier {
if (modifier.all { it !is ComposedModifier }) {
return modifier
@@ -273,3 +278,64 @@
endReplaceableGroup()
return result
}
+
+/**
+ * This class is only used for backwards compatibility purposes to inject the CompositionLocalMap
+ * into LayoutNodes that were created by inlined code of older versions of the Layout composable.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+internal class CompositionLocalMapInjectionNode(map: CompositionLocalMap) : Modifier.Node() {
+ var map: CompositionLocalMap = map
+ set(value) {
+ field = value
+ requireLayoutNode().compositionLocalMap = value
+ }
+ override fun onAttach() {
+ requireLayoutNode().compositionLocalMap = map
+ }
+}
+
+/**
+ * This class is only used for backwards compatibility purposes to inject the CompositionLocalMap
+ * into LayoutNodes that were created by inlined code of older versions of the Layout composable.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+internal class CompositionLocalMapInjectionElement(
+ val map: CompositionLocalMap
+) : ModifierNodeElement<CompositionLocalMapInjectionNode>() {
+ override fun create() = CompositionLocalMapInjectionNode(map)
+ override fun update(node: CompositionLocalMapInjectionNode) = node.also { it.map = map }
+ override fun hashCode(): Int = map.hashCode()
+ override fun equals(other: Any?): Boolean {
+ return other is CompositionLocalMapInjectionElement && other.map == map
+ }
+ override fun InspectorInfo.inspectableProperties() {
+ name = "<Injected CompositionLocalMap>"
+ }
+}
+
+/**
+ * This function exists solely for solving a backwards-incompatibility with older compilations
+ * that used an older version of the `Layout` composable. New code paths should not call this.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+@Suppress("ModifierFactoryExtensionFunction")
+@JvmName("materialize")
+@Deprecated(
+ "Kept for backwards compatibility only. If you are recompiling, use materialize.",
+ ReplaceWith("materialize"),
+ DeprecationLevel.HIDDEN
+)
+fun Composer.materializeWithCompositionLocalInjection(modifier: Modifier): Modifier =
+ materializeWithCompositionLocalInjectionInternal(modifier)
+
+// This method is here to be called from tests since the deprecated hidden API cannot be.
+@Suppress("ModifierFactoryExtensionFunction")
+internal fun Composer.materializeWithCompositionLocalInjectionInternal(
+ modifier: Modifier
+): Modifier {
+ return if (modifier === Modifier)
+ modifier
+ else
+ materialize(CompositionLocalMapInjectionElement(currentCompositionLocalMap).then(modifier))
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
index 001261d..2b45ead 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
@@ -28,6 +28,7 @@
import androidx.compose.ui.UiComposable
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.materialize
+import androidx.compose.ui.materializeWithCompositionLocalInjectionInternal
import androidx.compose.ui.node.ComposeUiNode
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.unit.Constraints
@@ -173,7 +174,13 @@
}
}
+/**
+ * This function uses a JVM-Name because the original name now has a different implementation for
+ * backwards compatibility [materializerOfWithCompositionLocalInjection].
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
@PublishedApi
+@JvmName("modifierMaterializerOf")
internal fun materializerOf(
modifier: Modifier
): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
@@ -183,6 +190,26 @@
}
}
+/**
+ * This function exists solely for solving a backwards-incompatibility with older compilations
+ * that used an older version of the `Layout` composable. New code paths should not call this.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+@JvmName("materializerOf")
+@Deprecated(
+ "Needed only for backwards compatibility. Do not use.",
+ level = DeprecationLevel.WARNING
+)
+@PublishedApi
+internal fun materializerOfWithCompositionLocalInjection(
+ modifier: Modifier
+): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
+ val materialized = currentComposer.materializeWithCompositionLocalInjectionInternal(modifier)
+ update {
+ set(materialized, ComposeUiNode.SetModifier)
+ }
+}
+
@Suppress("ComposableLambdaParameterPosition")
@Composable
@UiComposable