Repository: cashapp/redwood
Pull request #594 (2022-12-06 00:42:46)

Remove FlexItem.visible.
This isn't part of the flexbox spec and we aren't likely to use it given we remove items from the tree instead of hiding them.


Patch:
diff --git a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexContainer.kt b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexContainer.kt
index b96fb05ea..2dbd0b5b9 100644
--- a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexContainer.kt
+++ b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexContainer.kt
@@ -116,14 +116,11 @@ public class FlexContainer {
     determineMainSize(flexLines, widthSpec, heightSpec)
 
     if (alignItems == AlignItems.Baseline) {
-      for (flexLine in flexLines) {
+      flexLines.forEachIndices { flexLine ->
         // The largest height value that also take the baseline shift into account
         var largestHeightInLine = Double.MIN_VALUE
-        for (i in flexLine.firstIndex..flexLine.lastIndex) {
-          val child = items.getOrNull(i)
-          if (child == null || !child.visible) {
-            continue
-          }
+        for (i in flexLine.indices) {
+          val child = items[i]
           val heightInLine = if (flexWrap != FlexWrap.WrapReverse) {
             val marginTop = maxOf(flexLine.maxBaseline - child.baseline, child.margin.top)
             child.height + marginTop + child.margin.bottom
@@ -191,20 +188,7 @@ public class FlexContainer {
     var flexLine = FlexLine()
     flexLine.mainSize = orientation.mainPadding(padding)
     for (i in 0 until items.size) {
-      val item = items.getOrNull(i)
-      if (item == null) {
-        if (isLastFlexItem(i, items.size, flexLine)) {
-          addFlexLine(flexLines, flexLine, i, sumCrossSize)
-        }
-        continue
-      } else if (!item.visible) {
-        flexLine.invisibleItemCount++
-        flexLine.itemCount++
-        if (isLastFlexItem(i, items.size, flexLine)) {
-          addFlexLine(flexLines, flexLine, i, sumCrossSize)
-        }
-        continue
-      }
+      val item = items[i]
       val childMainSize = if (item.flexBasisPercent != DefaultFlexBasisPercent && mainMode == MeasureSpecMode.Exactly) {
         roundIfEnabled(item.flexBasisPercent * mainSize)
       } else {
@@ -237,8 +221,9 @@ public class FlexContainer {
           flexLinesSize = flexLines.size,
         )
       ) {
-        if (flexLine.itemCountVisible > 0) {
-          addFlexLine(flexLines, flexLine, maxOf(i - 1, 0), sumCrossSize)
+        if (flexLine.itemCount > 0) {
+          flexLine.sumCrossSizeBefore = sumCrossSize
+          flexLines += flexLine
           sumCrossSize += flexLine.crossSize
         }
         val crossSize = orientation.crossSize(item)
@@ -290,7 +275,8 @@ public class FlexContainer {
       }
 
       if (isLastFlexItem(i, items.size, flexLine)) {
-        addFlexLine(flexLines, flexLine, i, sumCrossSize)
+        flexLine.sumCrossSizeBefore = sumCrossSize
+        flexLines += flexLine
         sumCrossSize += flexLine.crossSize
       }
     }
@@ -338,18 +324,7 @@ public class FlexContainer {
     childCount: Int,
     flexLine: FlexLine,
   ): Boolean {
-    return childIndex == childCount - 1 && flexLine.itemCountVisible > 0
-  }
-
-  private fun addFlexLine(
-    flexLines: MutableList<FlexLine>,
-    flexLine: FlexLine,
-    itemIndex: Int,
-    usedCrossSizeSoFar: Double,
-  ) {
-    flexLine.sumCrossSizeBefore = usedCrossSizeSoFar
-    flexLine.lastIndex = itemIndex
-    flexLines += flexLine
+    return childIndex == childCount - 1 && flexLine.itemCount > 0
   }
 
   /**
@@ -383,7 +358,7 @@ public class FlexContainer {
       }
       paddingAlongMainAxis = padding.top + padding.bottom
     }
-    for (flexLine in flexLines) {
+    flexLines.forEachIndices { flexLine ->
       if (flexLine.mainSize < mainSize && flexLine.anyItemsHaveFlexGrow) {
         expandFlexItems(
           childrenFrozen = childrenFrozen,
@@ -449,18 +424,14 @@ public class FlexContainer {
       flexLine.crossSize = Double.MIN_VALUE
     }
     var accumulatedRoundError = 0.0
-    for (i in 0 until flexLine.itemCount) {
-      val index = flexLine.firstIndex + i
-      val child = items.getOrNull(index)
-      if (child == null || !child.visible) {
-        continue
-      }
+    for (i in flexLine.indices) {
+      val child = items[i]
       val measurable = child.measurable
       if (flexDirection.isHorizontal) {
         // The direction of the main axis is horizontal
-        if (!childrenFrozen[index] && child.flexGrow > 0) {
+        if (!childrenFrozen[i] && child.flexGrow > 0) {
           var rawCalculatedWidth = (child.width + unitSpace * child.flexGrow)
-          if (i == flexLine.itemCount - 1) {
+          if (i == flexLine.lastIndex) {
             rawCalculatedWidth += accumulatedRoundError
             accumulatedRoundError = 0.0
           }
@@ -473,7 +444,7 @@ public class FlexContainer {
             // (children items). In that case, invoke this method again with the same fromIndex.
             needsReexpand = true
             newWidth = maxWidth
-            childrenFrozen[index] = true
+            childrenFrozen[i] = true
             flexLine.totalFlexGrow -= child.flexGrow
           } else {
             accumulatedRoundError += rawCalculatedWidth - newWidth
@@ -502,9 +473,9 @@ public class FlexContainer {
         flexLine.mainSize += (child.width + child.margin.start + child.margin.end)
       } else {
         // The direction of the main axis is vertical
-        if (!childrenFrozen[index] && child.flexGrow > 0) {
+        if (!childrenFrozen[i] && child.flexGrow > 0) {
           var rawCalculatedHeight = (child.height + unitSpace * child.flexGrow)
-          if (i == flexLine.itemCount - 1) {
+          if (i == flexLine.lastIndex) {
             rawCalculatedHeight += accumulatedRoundError
             accumulatedRoundError = 0.0
           }
@@ -517,7 +488,7 @@ public class FlexContainer {
             // (children items). In that case, invoke this method again with the same fromIndex.
             needsReexpand = true
             newHeight = maxHeight
-            childrenFrozen[index] = true
+            childrenFrozen[i] = true
             flexLine.totalFlexGrow -= child.flexGrow
           } else {
             accumulatedRoundError += rawCalculatedHeight - newHeight
@@ -603,18 +574,14 @@ public class FlexContainer {
     if (!calledRecursively) {
       flexLine.crossSize = Double.MIN_VALUE
     }
-    for (i in 0 until flexLine.itemCount) {
-      val index = flexLine.firstIndex + i
-      val child = items.getOrNull(index)
-      if (child == null || !child.visible) {
-        continue
-      }
+    for (i in flexLine.indices) {
+      val child = items[i]
       val measurable = child.measurable
       if (flexDirection.isHorizontal) {
         // The direction of main axis is horizontal
-        if (!childrenFrozen[index] && child.flexShrink > 0) {
+        if (!childrenFrozen[i] && child.flexShrink > 0) {
           var rawCalculatedWidth = child.width - unitShrink * child.flexShrink
-          if (i == flexLine.itemCount - 1) {
+          if (i == flexLine.lastIndex) {
             rawCalculatedWidth += accumulatedRoundError
             accumulatedRoundError = 0.0
           }
@@ -627,7 +594,7 @@ public class FlexContainer {
             // items). In that case, invoke this method again with the same fromIndex.
             needsReshrink = true
             newWidth = minWidth
-            childrenFrozen[index] = true
+            childrenFrozen[i] = true
             flexLine.totalFlexShrink -= child.flexShrink
           } else {
             accumulatedRoundError += rawCalculatedWidth - newWidth
@@ -656,9 +623,9 @@ public class FlexContainer {
         flexLine.mainSize += child.width + child.margin.start + child.margin.end
       } else {
         // The direction of main axis is vertical
-        if (!childrenFrozen[index] && child.flexShrink > 0) {
+        if (!childrenFrozen[i] && child.flexShrink > 0) {
           var rawCalculatedHeight = child.height - unitShrink * child.flexShrink
-          if (i == flexLine.itemCount - 1) {
+          if (i == flexLine.lastIndex) {
             rawCalculatedHeight += accumulatedRoundError
             accumulatedRoundError = 0.0
           }
@@ -668,7 +635,7 @@ public class FlexContainer {
             // Need to invoke this method again like the case flex direction is vertical
             needsReshrink = true
             newHeight = minHeight
-            childrenFrozen[index] = true
+            childrenFrozen[i] = true
             flexLine.totalFlexShrink -= child.flexShrink
           } else {
             accumulatedRoundError += rawCalculatedHeight - newHeight
@@ -796,11 +763,10 @@ public class FlexContainer {
             val freeSpaceUnit = (size - totalCrossSize) / flexLines.size
             var accumulatedError = 0.0
             var i = 0
-            val flexLinesSize = flexLines.size
-            while (i < flexLinesSize) {
+            while (i < flexLines.size) {
               val flexLine = flexLines[i]
               var newCrossSizeAsFloat = flexLine.crossSize + freeSpaceUnit
-              if (i == flexLines.size - 1) {
+              if (i == flexLines.lastIndex) {
                 newCrossSizeAsFloat += accumulatedError
                 accumulatedError = 0.0
               }
@@ -838,10 +804,10 @@ public class FlexContainer {
             val newFlexLines = ArrayList<FlexLine>()
             val dummySpaceFlexLine = FlexLine()
             dummySpaceFlexLine.crossSize = spaceTopAndBottom
-            for (flexLine in flexLines) {
-              newFlexLines.add(dummySpaceFlexLine)
-              newFlexLines.add(flexLine)
-              newFlexLines.add(dummySpaceFlexLine)
+            flexLines.forEachIndices { flexLine ->
+              newFlexLines += dummySpaceFlexLine
+              newFlexLines += flexLine
+              newFlexLines += dummySpaceFlexLine
             }
             flexLines.clear()
             flexLines += newFlexLines
@@ -852,16 +818,15 @@ public class FlexContainer {
             }
             // The value of free space along the cross axis between each flex line.
             var spaceBetweenFlexLine = size - totalCrossSize
-            val numberOfSpaces = flexLines.size - 1
+            val numberOfSpaces = flexLines.lastIndex
             spaceBetweenFlexLine /= numberOfSpaces
             var accumulatedError = 0.0
             val newFlexLines = ArrayList<FlexLine>()
             var i = 0
-            val flexLineSize = flexLines.size
-            while (i < flexLineSize) {
+            while (i < flexLines.size) {
               val flexLine = flexLines[i]
-              newFlexLines.add(flexLine)
-              if (i != flexLines.size - 1) {
+              newFlexLines += flexLine
+              if (i != flexLines.lastIndex) {
                 val dummySpaceFlexLine = FlexLine()
                 if (i == flexLines.size - 2) {
                   // The last dummy space block in the flex container.
@@ -879,7 +844,7 @@ public class FlexContainer {
                   dummySpaceFlexLine.crossSize -= 1
                   accumulatedError += 1
                 }
-                newFlexLines.add(dummySpaceFlexLine)
+                newFlexLines += dummySpaceFlexLine
               }
               i++
             }
@@ -916,12 +881,12 @@ public class FlexContainer {
     dummySpaceFlexLine.crossSize = spaceAboveAndBottom
     for (i in flexLines.indices) {
       if (i == 0) {
-        newFlexLines.add(dummySpaceFlexLine)
+        newFlexLines += dummySpaceFlexLine
       }
       val flexLine = flexLines[i]
-      newFlexLines.add(flexLine)
-      if (i == flexLines.size - 1) {
-        newFlexLines.add(dummySpaceFlexLine)
+      newFlexLines += flexLine
+      if (i == flexLines.lastIndex) {
+        newFlexLines += dummySpaceFlexLine
       }
     }
     return newFlexLines
@@ -934,15 +899,8 @@ public class FlexContainer {
   private fun stretchChildren(flexLines: List<FlexLine>) {
     if (alignItems == AlignItems.Stretch) {
       for (flexLine in flexLines) {
-        for (i in 0 until flexLine.itemCount) {
-          val itemIndex = flexLine.firstIndex + i
-          if (i >= items.size) {
-            continue
-          }
-          val item = items.getOrNull(itemIndex)
-          if (item == null || !item.visible) {
-            continue
-          }
+        for (i in flexLine.indices) {
+          val item = items[i]
           if (item.alignSelf != AlignSelf.Auto && item.alignSelf != AlignSelf.Stretch) {
             continue
           }
@@ -954,8 +912,8 @@ public class FlexContainer {
         }
       }
     } else {
-      for (flexLine in flexLines) {
-        for (index in flexLine.firstIndex..flexLine.lastIndex) {
+      flexLines.forEachIndices { flexLine ->
+        for (index in flexLine.indices) {
           val item = items[index]
           if (item.alignSelf == AlignSelf.Stretch) {
             if (flexDirection.isHorizontal) {
@@ -1096,7 +1054,7 @@ public class FlexContainer {
     var childTop = padding.top
     var childLeft: Double
     var childRight: Double
-    for (flexLine in flexLines) {
+    flexLines.forEachIndices { flexLine ->
       var spaceBetweenItem = 0.0
       when (justifyContent) {
         JustifyContent.FlexStart -> {
@@ -1112,24 +1070,21 @@ public class FlexContainer {
           childRight = width - paddingRight - (width - flexLine.mainSize) / 2
         }
         JustifyContent.SpaceAround -> {
-          val visibleCount = flexLine.itemCountVisible
-          if (visibleCount != 0) {
-            spaceBetweenItem = ((width - flexLine.mainSize) / visibleCount)
+          if (flexLine.itemCount != 0) {
+            spaceBetweenItem = ((width - flexLine.mainSize) / flexLine.itemCount)
           }
           childLeft = paddingLeft + spaceBetweenItem / 2
           childRight = width - paddingRight - spaceBetweenItem / 2
         }
         JustifyContent.SpaceBetween -> {
           childLeft = paddingLeft
-          val visibleCount = flexLine.itemCountVisible
-          val denominator = if (visibleCount != 1) visibleCount - 1 else 1
+          val denominator = if (flexLine.itemCount != 1) flexLine.itemCount - 1 else 1
           spaceBetweenItem = (width - flexLine.mainSize) / denominator
           childRight = width - paddingRight
         }
         JustifyContent.SpaceEvenly -> {
-          val visibleCount = flexLine.itemCountVisible
-          if (visibleCount != 0) {
-            spaceBetweenItem = (width - flexLine.mainSize) / (visibleCount + 1)
+          if (flexLine.itemCount != 0) {
+            spaceBetweenItem = (width - flexLine.mainSize) / (flexLine.itemCount + 1)
           }
           childLeft = paddingLeft + spaceBetweenItem
           childRight = width - paddingRight - spaceBetweenItem
@@ -1137,12 +1092,8 @@ public class FlexContainer {
         else -> throw AssertionError()
       }
       spaceBetweenItem = maxOf(spaceBetweenItem, 0.0)
-      for (i in 0 until flexLine.itemCount) {
-        val index = flexLine.firstIndex + i
-        val child = items.getOrNull(index)
-        if (child == null || !child.visible) {
-          continue
-        }
+      for (i in flexLine.indices) {
+        val child = items[i]
         childLeft += child.margin.start
         childRight -= child.margin.end
         if (flexWrap == FlexWrap.WrapReverse) {
@@ -1209,7 +1160,7 @@ public class FlexContainer {
     var childRight = width - paddingRight
     var childTop: Double
     var childBottom: Double
-    for (flexLine in flexLines) {
+    flexLines.forEachIndices { flexLine ->
       var spaceBetweenItem = 0.0
       when (justifyContent) {
         JustifyContent.FlexStart -> {
@@ -1225,24 +1176,21 @@ public class FlexContainer {
           childBottom = height - paddingBottom - (height - flexLine.mainSize) / 2
         }
         JustifyContent.SpaceAround -> {
-          val visibleCount = flexLine.itemCountVisible
-          if (visibleCount != 0) {
-            spaceBetweenItem = ((height - flexLine.mainSize) / visibleCount)
+          if (flexLine.itemCount != 0) {
+            spaceBetweenItem = ((height - flexLine.mainSize) / flexLine.itemCount)
           }
           childTop = paddingTop + spaceBetweenItem / 2
           childBottom = height - paddingBottom - spaceBetweenItem / 2
         }
         JustifyContent.SpaceBetween -> {
           childTop = paddingTop
-          val visibleCount = flexLine.itemCountVisible
-          val denominator = if (visibleCount != 1) visibleCount - 1 else 1
+          val denominator = if (flexLine.itemCount != 1) flexLine.itemCount - 1 else 1
           spaceBetweenItem = (height - flexLine.mainSize) / denominator
           childBottom = height - paddingBottom
         }
         JustifyContent.SpaceEvenly -> {
-          val visibleCount = flexLine.itemCountVisible
-          if (visibleCount != 0) {
-            spaceBetweenItem = ((height - flexLine.mainSize) / (visibleCount + 1))
+          if (flexLine.itemCount != 0) {
+            spaceBetweenItem = ((height - flexLine.mainSize) / (flexLine.itemCount + 1))
           }
           childTop = paddingTop + spaceBetweenItem
           childBottom = height - paddingBottom - spaceBetweenItem
@@ -1250,12 +1198,8 @@ public class FlexContainer {
         else -> throw AssertionError()
       }
       spaceBetweenItem = maxOf(spaceBetweenItem, 0.0)
-      for (i in 0 until flexLine.itemCount) {
-        val index = flexLine.firstIndex + i
-        val child = items.getOrNull(index)
-        if (child == null || !child.visible) {
-          continue
-        }
+      for (i in flexLine.indices) {
+        val child = items[i]
         childTop += child.margin.top
         childBottom -= child.margin.bottom
         if (rightToLeft) {
diff --git a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexItem.kt b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexItem.kt
index 2d80fd26d..48d957735 100644
--- a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexItem.kt
+++ b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexItem.kt
@@ -19,11 +19,6 @@ package app.cash.redwood.flexbox
  * An item with properties that can be measured and laid out inside a [FlexContainer].
  */
 public class FlexItem(
-  /**
-   * True if this item is visible and should be laid out.
-   */
-  public val visible: Boolean = true,
-
   /**
    * The baseline used for [AlignItems.Baseline] and [AlignSelf.Baseline].
    */
diff --git a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexLine.kt b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexLine.kt
index 6ca81c2df..79ffb1ea3 100644
--- a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexLine.kt
+++ b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/FlexLine.kt
@@ -37,11 +37,6 @@ internal class FlexLine {
    */
   var itemCount = 0
 
-  /**
-   * The number of items who are invisible (i.e. aren't included in measure or layout).
-   */
-  var invisibleItemCount = 0
-
   /**
    * The sum of the flexGrow properties of the children included in this flex line.
    */
@@ -63,15 +58,10 @@ internal class FlexLine {
   var sumCrossSizeBefore = 0.0
 
   /**
-   * The index of the first child included in this flex line (inclusive).
+   * The index of the first item included in this flex line.
    */
   var firstIndex = 0
 
-  /**
-   * The index of the last child included in this flex line (inclusive).
-   */
-  var lastIndex = 0
-
   /**
    * True if any [FlexItem]s in this line have [FlexItem.flexGrow] attributes set
    * (i.e. have a value other than [DefaultFlexGrow]).
diff --git a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/utils.kt b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/utils.kt
index fb249f757..ffbdf5f34 100644
--- a/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/utils.kt
+++ b/redwood-flex-container/src/commonMain/kotlin/app/cash/redwood/flexbox/utils.kt
@@ -40,9 +40,17 @@ internal fun FlexItem.layout(left: Double, top: Double, right: Double, bottom: D
   this.bottom = bottom
 }
 
-/** The number of items who are not invisible in this flex line. */
-internal val FlexLine.itemCountVisible: Int
-  get() = itemCount - invisibleItemCount
+internal inline fun <T> List<T>.forEachIndices(block: (T) -> Unit) {
+  for (index in indices) block(get(index))
+}
+
+/** The index of the last child included in this flex line or -1 if the flex line is empty. */
+internal val FlexLine.lastIndex: Int
+  get() = if (itemCount > 0) firstIndex + itemCount - 1 else -1
+
+/** The range of item indices covered by this flex line. */
+internal inline val FlexLine.indices: IntRange
+  get() = firstIndex..lastIndex
 
 /** The largest main size of all flex lines. */
 internal fun List<FlexLine>.getLargestMainSize(): Double {
diff --git a/redwood-protocol-compose/src/commonMain/kotlin/app/cash/redwood/protocol/compose/ProtocolBridge.kt b/redwood-protocol-compose/src/commonMain/kotlin/app/cash/redwood/protocol/compose/ProtocolBridge.kt
index dcc3340af..2681439d8 100644
--- a/redwood-protocol-compose/src/commonMain/kotlin/app/cash/redwood/protocol/compose/ProtocolBridge.kt
+++ b/redwood-protocol-compose/src/commonMain/kotlin/app/cash/redwood/protocol/compose/ProtocolBridge.kt
@@ -25,7 +25,7 @@ import app.cash.redwood.protocol.PropertyDiff
 import app.cash.redwood.widget.Widget
 
 public class ProtocolBridge : EventSink {
-  private var nextValue = Id.Root.value + 1U
+  private var nextValue = Id.Root.value + 1L
   private val nodes = mutableMapOf<Id, DiffProducingWidget>()
 
   private var childrenDiffs = mutableListOf<ChildrenDiff>()
@@ -34,7 +34,7 @@ public class ProtocolBridge : EventSink {
 
   public fun nextId(): Id {
     val value = nextValue
-    nextValue = value + 1U
+    nextValue = value + 1L
     return Id(value)
   }
 
diff --git a/redwood-protocol/src/commonMain/kotlin/app/cash/redwood/protocol/protocol.kt b/redwood-protocol/src/commonMain/kotlin/app/cash/redwood/protocol/protocol.kt
index 6b73c1ab2..c5e332448 100644
--- a/redwood-protocol/src/commonMain/kotlin/app/cash/redwood/protocol/protocol.kt
+++ b/redwood-protocol/src/commonMain/kotlin/app/cash/redwood/protocol/protocol.kt
@@ -33,9 +33,9 @@ public fun interface DiffSink {
 /** Identifies a widget instance. */
 @JvmInline
 @Serializable
-public value class Id(public val value: ULong) {
+public value class Id(public val value: Long) {
   public companion object {
-    public val Root: Id = Id(0UL)
+    public val Root: Id = Id(0L)
   }
 }
 


Test patch:
diff --git a/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/DiffProducingWidgetFactoryTest.kt b/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/DiffProducingWidgetFactoryTest.kt
index a5f202d01..842c4de22 100644
--- a/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/DiffProducingWidgetFactoryTest.kt
+++ b/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/DiffProducingWidgetFactoryTest.kt
@@ -49,7 +49,7 @@ class DiffProducingWidgetFactoryTest {
 
     val expected = Diff(
       propertyDiffs = listOf(
-        PropertyDiff(Id(1U), 2U, JsonPrimitive("PT10S")),
+        PropertyDiff(Id(1), 2U, JsonPrimitive("PT10S")),
       ),
     )
     assertEquals(expected, bridge.createDiffOrNull())
@@ -72,7 +72,7 @@ class DiffProducingWidgetFactoryTest {
     val expected = Diff(
       layoutModifiers = listOf(
         LayoutModifiers(
-          Id(1U),
+          Id(1),
           buildJsonArray {
             add(
               buildJsonArray {
@@ -108,7 +108,7 @@ class DiffProducingWidgetFactoryTest {
     val expected = Diff(
       layoutModifiers = listOf(
         LayoutModifiers(
-          Id(1U),
+          Id(1),
           buildJsonArray {
             add(
               buildJsonArray {
@@ -143,7 +143,7 @@ class DiffProducingWidgetFactoryTest {
       argument = it
     }
 
-    diffProducingWidget.sendEvent(Event(Id(1U), 4U, JsonPrimitive("PT10S")))
+    diffProducingWidget.sendEvent(Event(Id(1), 4U, JsonPrimitive("PT10S")))
 
     assertEquals(10.seconds, argument)
   }
@@ -152,7 +152,7 @@ class DiffProducingWidgetFactoryTest {
     val factory = DiffProducingExampleSchemaWidgetFactory(ProtocolBridge())
     val button = factory.Button() as DiffProducingWidget
 
-    val event = Event(Id(1U), 3456543U)
+    val event = Event(Id(1), 3456543U)
     val t = assertFailsWith<IllegalArgumentException> {
       button.sendEvent(event)
     }
@@ -165,7 +165,7 @@ class DiffProducingWidgetFactoryTest {
     val factory = DiffProducingExampleSchemaWidgetFactory(ProtocolBridge(), mismatchHandler = handler)
     val button = factory.Button() as DiffProducingWidget
 
-    button.sendEvent(Event(Id(1U), 3456543U))
+    button.sendEvent(Event(Id(1), 3456543U))
 
     assertEquals("Unknown event 3456543 for 4", handler.events.single())
   }
diff --git a/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt b/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt
index 5de2dd34a..e377bc0d1 100644
--- a/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt
+++ b/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt
@@ -84,20 +84,20 @@ class ProtocolTest {
     assertEquals(
       Diff(
         childrenDiffs = listOf(
-          ChildrenDiff.Insert(Id.Root, RootChildrenTag, Id(1U), 1 /* row */, 0),
-          ChildrenDiff.Insert(Id(1U), 1U, Id(2U), 3 /* text */, 0),
-          ChildrenDiff.Insert(Id(1U), 1U, Id(3U), 1 /* row */, 1),
-          ChildrenDiff.Insert(Id(3U), 1U, Id(4U), 3 /* text */, 0),
+          ChildrenDiff.Insert(Id.Root, RootChildrenTag, Id(1), 1 /* row */, 0),
+          ChildrenDiff.Insert(Id(1), 1U, Id(2), 3 /* text */, 0),
+          ChildrenDiff.Insert(Id(1), 1U, Id(3), 1 /* row */, 1),
+          ChildrenDiff.Insert(Id(3), 1U, Id(4), 3 /* text */, 0),
         ),
         layoutModifiers = listOf(
-          LayoutModifiers(Id(1U), JsonArray(listOf())),
-          LayoutModifiers(Id(2U), JsonArray(listOf())),
-          LayoutModifiers(Id(3U), JsonArray(listOf())),
-          LayoutModifiers(Id(4U), JsonArray(listOf())),
+          LayoutModifiers(Id(1), JsonArray(listOf())),
+          LayoutModifiers(Id(2), JsonArray(listOf())),
+          LayoutModifiers(Id(3), JsonArray(listOf())),
+          LayoutModifiers(Id(4), JsonArray(listOf())),
         ),
         propertyDiffs = listOf(
-          PropertyDiff(Id(2U), 1U /* text */, JsonPrimitive("hey")),
-          PropertyDiff(Id(4U), 1U /* text */, JsonPrimitive("hello")),
+          PropertyDiff(Id(2), 1U /* text */, JsonPrimitive("hey")),
+          PropertyDiff(Id(4), 1U /* text */, JsonPrimitive("hello")),
         ),
       ),
       diffs.removeFirst(),
@@ -135,43 +135,43 @@ class ProtocolTest {
     assertEquals(
       Diff(
         childrenDiffs = listOf(
-          ChildrenDiff.Insert(Id.Root, RootChildrenTag, Id(1U), 4 /* button */, 0),
+          ChildrenDiff.Insert(Id.Root, RootChildrenTag, Id(1), 4 /* button */, 0),
         ),
         layoutModifiers = listOf(
-          LayoutModifiers(Id(1U), JsonArray(listOf())),
+          LayoutModifiers(Id(1), JsonArray(listOf())),
         ),
         propertyDiffs = listOf(
-          PropertyDiff(Id(1U), 1U /* text */, JsonPrimitive("state: 0")),
-          PropertyDiff(Id(1U), 2U /* onClick */, JsonPrimitive(true)),
+          PropertyDiff(Id(1), 1U /* text */, JsonPrimitive("state: 0")),
+          PropertyDiff(Id(1), 2U /* onClick */, JsonPrimitive(true)),
         ),
       ),
       diffs.removeFirst(),
     )
 
     // Invoke the onClick lambda to move the state from 0 to 1.
-    bridge.sendEvent(Event(Id(1U), 2U))
+    bridge.sendEvent(Event(Id(1), 2U))
     yield() // Allow state change to be handled.
 
     clock.awaitFrame()
     assertEquals(
       Diff(
         propertyDiffs = listOf(
-          PropertyDiff(Id(1U), 1U /* text */, JsonPrimitive("state: 1")),
+          PropertyDiff(Id(1), 1U /* text */, JsonPrimitive("state: 1")),
         ),
       ),
       diffs.removeFirst(),
     )
 
     // Invoke the onClick lambda to move the state from 1 to 2.
-    bridge.sendEvent(Event(Id(1U), 2U))
+    bridge.sendEvent(Event(Id(1), 2U))
     yield() // Allow state change to be handled.
 
     clock.awaitFrame()
     assertEquals(
       Diff(
         propertyDiffs = listOf(
-          PropertyDiff(Id(1U), 1U /* text */, JsonPrimitive("state: 2")),
-          PropertyDiff(Id(1U), 2U /* text */, JsonPrimitive(false)),
+          PropertyDiff(Id(1), 1U /* text */, JsonPrimitive("state: 2")),
+          PropertyDiff(Id(1), 2U /* text */, JsonPrimitive(false)),
         ),
       ),
       diffs.removeFirst(),
@@ -185,7 +185,7 @@ class ProtocolTest {
     assertEquals(
       Diff(
         propertyDiffs = listOf(
-          PropertyDiff(Id(1U), 1U /* text */, JsonPrimitive("state: 3")),
+          PropertyDiff(Id(1), 1U /* text */, JsonPrimitive("state: 3")),
         ),
       ),
       diffs.removeFirst(),
diff --git a/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingWidgetFactoryTest.kt b/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingWidgetFactoryTest.kt
index 4af336df6..0d3ac43a0 100644
--- a/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingWidgetFactoryTest.kt
+++ b/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/DiffConsumingWidgetFactoryTest.kt
@@ -229,7 +229,7 @@ class DiffConsumingWidgetFactoryTest {
     val textInput = factory.create(Id.Root, ThrowingWidgetChildren(), 5)!!
 
     val throwingEventSink = EventSink { error(it) }
-    textInput.apply(PropertyDiff(Id(1U), 2U, JsonPrimitive("PT10S")), throwingEventSink)
+    textInput.apply(PropertyDiff(Id(1), 2U, JsonPrimitive("PT10S")), throwingEventSink)
 
     assertEquals(10.seconds, recordingTextInput.customType)
   }
@@ -238,7 +238,7 @@ class DiffConsumingWidgetFactoryTest {
     val factory = DiffConsumingExampleSchemaWidgetFactory(EmptyExampleSchemaWidgetFactory())
     val button = factory.create(Id.Root, ThrowingWidgetChildren(), 4)!!
 
-    val diff = PropertyDiff(Id(1U), 345432U)
+    val diff = PropertyDiff(Id(1), 345432U)
     val eventSink = EventSink { throw UnsupportedOperationException() }
     val t = assertFailsWith<IllegalArgumentException> {
       button.apply(diff, eventSink)
@@ -254,7 +254,7 @@ class DiffConsumingWidgetFactoryTest {
     )
     val button = factory.create(Id.Root, ThrowingWidgetChildren(), 4)!!
 
-    button.apply(PropertyDiff(Id(1U), 345432U)) { throw UnsupportedOperationException() }
+    button.apply(PropertyDiff(Id(1), 345432U)) { throw UnsupportedOperationException() }
 
     assertEquals("Unknown property 345432 for 4", handler.events.single())
   }
@@ -275,11 +275,11 @@ class DiffConsumingWidgetFactoryTest {
     val textInput = factory.create(Id.Root, ThrowingWidgetChildren(), 5)!!
 
     val eventSink = RecordingEventSink()
-    textInput.apply(PropertyDiff(Id(1U), 4U, JsonPrimitive(true)), eventSink)
+    textInput.apply(PropertyDiff(Id(1), 4U, JsonPrimitive(true)), eventSink)
 
     recordingTextInput.onChangeCustomType!!.invoke(10.seconds)
 
-    assertEquals(Event(Id(1U), 4U, JsonPrimitive("PT10S")), eventSink.events.single())
+    assertEquals(Event(Id(1), 4U, JsonPrimitive("PT10S")), eventSink.events.single())
   }
 
   class RecordingTextInput : TextInput<Nothing> {
diff --git a/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/ProtocolBridgeTest.kt b/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/ProtocolBridgeTest.kt
index 504fe697d..852cd5f85 100644
--- a/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/ProtocolBridgeTest.kt
+++ b/redwood-protocol-widget/src/commonTest/kotlin/app/cash/redwood/protocol/widget/ProtocolBridgeTest.kt
@@ -60,7 +60,7 @@ class ProtocolBridgeTest {
         ChildrenDiff.Insert(
           id = Id.Root,
           tag = RootChildrenTag,
-          childId = Id(1U),
+          childId = Id(1),
           kind = 4 /* button */,
           index = 0,
         ),
diff --git a/redwood-protocol/src/commonTest/kotlin/app/cash/redwood/protocol/ProtocolTest.kt b/redwood-protocol/src/commonTest/kotlin/app/cash/redwood/protocol/ProtocolTest.kt
index 84297272f..d21f8ff74 100644
--- a/redwood-protocol/src/commonTest/kotlin/app/cash/redwood/protocol/ProtocolTest.kt
+++ b/redwood-protocol/src/commonTest/kotlin/app/cash/redwood/protocol/ProtocolTest.kt
@@ -30,13 +30,13 @@ class ProtocolTest {
   }
 
   @Test fun eventNonNullValue() {
-    val model = Event(Id(1U), 2U, JsonPrimitive("Hello"))
+    val model = Event(Id(1), 2U, JsonPrimitive("Hello"))
     val json = """{"id":1,"tag":2,"value":"Hello"}"""
     assertJsonRoundtrip(Event.serializer(), model, json)
   }
 
   @Test fun eventNullValue() {
-    val model = Event(Id(1U), 2U)
+    val model = Event(Id(1), 2U)
     val json = """{"id":1,"tag":2}"""
     assertJsonRoundtrip(Event.serializer(), model, json)
   }
@@ -44,13 +44,13 @@ class ProtocolTest {
   @Test fun diff() {
     val model = Diff(
       childrenDiffs = listOf(
-        ChildrenDiff.Insert(Id(1U), 2U, Id(3U), 4, 5),
-        ChildrenDiff.Move(Id(1U), 2U, 3, 4, 5),
-        ChildrenDiff.Remove(Id(1U), 2U, 3, 4),
+        ChildrenDiff.Insert(Id(1), 2U, Id(3), 4, 5),
+        ChildrenDiff.Move(Id(1), 2U, 3, 4, 5),
+        ChildrenDiff.Remove(Id(1), 2U, 3, 4),
       ),
       layoutModifiers = listOf(
         LayoutModifiers(
-          Id(1U),
+          Id(1),
           buildJsonArray {
             add(
               buildJsonArray {
@@ -62,8 +62,8 @@ class ProtocolTest {
         ),
       ),
       propertyDiffs = listOf(
-        PropertyDiff(Id(1U), 2U, JsonPrimitive("Hello")),
-        PropertyDiff(Id(1U), 2U, JsonNull),
+        PropertyDiff(Id(1), 2U, JsonPrimitive("Hello")),
+        PropertyDiff(Id(1), 2U, JsonNull),
       ),
     )
     val json = "" +