From 372600f3296c7f4d044a1e97f4c612c2e12a6c83 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Wed, 2 Oct 2024 16:53:54 -0400 Subject: [PATCH] Apply modifiers as they change in ViewFlexContainer (#2357) * Apply modifiers as they change in ViewFlexContainer This reduces the amount of preparation required to measure a layout. * Spotless * apiDump --- .../redwood/layout/view/ViewFlexContainer.kt | 23 ++++++++++++------- .../cash/redwood/layout/view/YogaLayout.kt | 10 +------- redwood-widget/api/android/redwood-widget.api | 4 ++-- .../cash/redwood/widget/ViewGroupChildren.kt | 18 +++++++-------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewFlexContainer.kt b/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewFlexContainer.kt index 6a461f155b..933ed8303d 100644 --- a/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewFlexContainer.kt +++ b/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/ViewFlexContainer.kt @@ -45,12 +45,7 @@ internal class ViewFlexContainer( private val direction: FlexDirection, ) : YogaFlexContainer, ChangeListener { - private val yogaLayout: YogaLayout = YogaLayout( - context, - applyModifier = { node, index -> - node.applyModifier(children.widgets[index].modifier, density) - }, - ) + private val yogaLayout: YogaLayout = YogaLayout(context) override val rootNode: Node get() = yogaLayout.rootNode override val density = Density(context.resources) @@ -59,14 +54,26 @@ internal class ViewFlexContainer( override val children = ViewGroupChildren( yogaLayout, - insert = { index, view -> - yogaLayout.rootNode.children.add(index, view.asNode()) + insert = { index, widget -> + val view = widget.value + + val node = view.asNode() + yogaLayout.rootNode.children.add(index, node) + + // Always apply changes *after* adding a node to its parent. + node.applyModifier(widget.modifier, density) + yogaLayout.addView(view, index) }, remove = { index, count -> yogaLayout.rootNode.children.remove(index, count) yogaLayout.removeViews(index, count) }, + onModifierUpdated = { index, widget -> + val node = yogaLayout.rootNode.children[index] + node.applyModifier(widget.modifier, density) + yogaLayout.requestLayout() + }, ) private var onScroll: ((Px) -> Unit)? = null diff --git a/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/YogaLayout.kt b/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/YogaLayout.kt index cb95114814..52a8c54a48 100644 --- a/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/YogaLayout.kt +++ b/redwood-layout-view/src/main/kotlin/app/cash/redwood/layout/view/YogaLayout.kt @@ -15,10 +15,7 @@ import app.cash.redwood.yoga.Size import kotlin.math.roundToInt @SuppressLint("ViewConstructor") -internal class YogaLayout( - context: Context, - private val applyModifier: (Node, Int) -> Unit, -) : ViewGroup(context) { +internal class YogaLayout(context: Context) : ViewGroup(context) { val rootNode = Node() private fun applyLayout(node: Node, xOffset: Float, yOffset: Float) { @@ -85,11 +82,6 @@ internal class YogaLayout( MeasureSpec.UNSPECIFIED -> {} } - // This must be applied immediately before measure. - for ((index, node) in rootNode.children.withIndex()) { - applyModifier(node, index) - } - // Sync widget layout requests to the Yoga node tree. for (node in rootNode.children) { if (node.view?.isLayoutRequested == true) { diff --git a/redwood-widget/api/android/redwood-widget.api b/redwood-widget/api/android/redwood-widget.api index 4af43b0f07..6ea1de52c4 100644 --- a/redwood-widget/api/android/redwood-widget.api +++ b/redwood-widget/api/android/redwood-widget.api @@ -75,8 +75,8 @@ public abstract interface class app/cash/redwood/widget/SavedStateRegistry { } public final class app/cash/redwood/widget/ViewGroupChildren : app/cash/redwood/widget/Widget$Children { - public fun (Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V - public synthetic fun (Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V + public synthetic fun (Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun detach ()V public fun getWidgets ()Ljava/util/List; public fun insert (ILapp/cash/redwood/widget/Widget;)V diff --git a/redwood-widget/src/androidMain/kotlin/app/cash/redwood/widget/ViewGroupChildren.kt b/redwood-widget/src/androidMain/kotlin/app/cash/redwood/widget/ViewGroupChildren.kt index 7f5a14e60b..1ffc9fb0ea 100644 --- a/redwood-widget/src/androidMain/kotlin/app/cash/redwood/widget/ViewGroupChildren.kt +++ b/redwood-widget/src/androidMain/kotlin/app/cash/redwood/widget/ViewGroupChildren.kt @@ -20,27 +20,27 @@ import android.view.ViewGroup public class ViewGroupChildren( private val container: ViewGroup, - private val insert: (index: Int, view: View) -> Unit = { index, view -> - container.addView(view, index) + private val insert: (index: Int, widget: Widget) -> Unit = { index, widget -> + container.addView(widget.value, index) }, private val remove: (index: Int, count: Int) -> Unit = { index, count -> container.removeViews(index, count) }, + private val onModifierUpdated: (index: Int, widget: Widget) -> Unit = { _, _ -> + container.requestLayout() + }, ) : Widget.Children { private val _widgets = ArrayList>() override val widgets: List> get() = _widgets override fun insert(index: Int, widget: Widget) { _widgets.add(index, widget) - insert(index, widget.value) + insert.invoke(index, widget) } override fun move(fromIndex: Int, toIndex: Int, count: Int) { _widgets.move(fromIndex, toIndex, count) - val views = Array(count) { offset -> - container.getChildAt(fromIndex + offset) - } remove.invoke(fromIndex, count) val newIndex = if (toIndex > fromIndex) { @@ -48,8 +48,8 @@ public class ViewGroupChildren( } else { toIndex } - views.forEachIndexed { offset, view -> - insert(newIndex + offset, view) + for (i in newIndex until newIndex + count) { + insert.invoke(i, _widgets[i]) } } @@ -59,7 +59,7 @@ public class ViewGroupChildren( } override fun onModifierUpdated(index: Int, widget: Widget) { - container.requestLayout() + onModifierUpdated.invoke(index, widget) } override fun detach() {