Skip to content

Commit

Permalink
Send widget tag with property change
Browse files Browse the repository at this point in the history
This will enable a future change to perform property value deserialization on the Zipline thread. Previously only the ID of the widget was available, and we can only resolve that to a widget tag on the main thread.
  • Loading branch information
JakeWharton committed Oct 2, 2024
1 parent bd063c6 commit abcbf03
Show file tree
Hide file tree
Showing 16 changed files with 117 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,28 +86,31 @@ public class DefaultGuestProtocolAdapter(

public override fun <T> appendPropertyChange(
id: Id,
tag: PropertyTag,
widgetTag: WidgetTag,
propertyTag: PropertyTag,
serializer: KSerializer<T>,
value: T,
) {
changes.add(PropertyChange(id, tag, json.encodeToJsonElement(serializer, value)))
changes.add(PropertyChange(id, widgetTag, propertyTag, json.encodeToJsonElement(serializer, value)))
}

public override fun appendPropertyChange(
id: Id,
tag: PropertyTag,
widgetTag: WidgetTag,
propertyTag: PropertyTag,
value: Boolean,
) {
changes.add(PropertyChange(id, tag, JsonPrimitive(value)))
changes.add(PropertyChange(id, widgetTag, propertyTag, JsonPrimitive(value)))
}

@OptIn(ExperimentalSerializationApi::class)
override fun appendPropertyChange(
id: Id,
tag: PropertyTag,
widgetTag: WidgetTag,
propertyTag: PropertyTag,
value: UInt,
) {
changes.add(PropertyChange(id, tag, JsonPrimitive(value)))
changes.add(PropertyChange(id, widgetTag, propertyTag, JsonPrimitive(value)))
}

override fun appendModifierChange(id: Id, value: Modifier) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,17 @@ public abstract class GuestProtocolAdapter(
@RedwoodCodegenApi
public abstract fun <T> appendPropertyChange(
id: Id,
tag: PropertyTag,
widgetTag: WidgetTag,
propertyTag: PropertyTag,
serializer: KSerializer<T>,
value: T,
)

@RedwoodCodegenApi
public abstract fun appendPropertyChange(
id: Id,
tag: PropertyTag,
widgetTag: WidgetTag,
propertyTag: PropertyTag,
value: Boolean,
)

Expand All @@ -102,7 +104,8 @@ public abstract class GuestProtocolAdapter(
@RedwoodCodegenApi
public abstract fun appendPropertyChange(
id: Id,
tag: PropertyTag,
widgetTag: WidgetTag,
propertyTag: PropertyTag,
value: UInt,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class GuestProtocolAdapterTest {

val expected = listOf(
Create(Id(1), WidgetTag(5)),
PropertyChange(Id(1), PropertyTag(2), JsonPrimitive("PT10S")),
PropertyChange(Id(1), WidgetTag(5), PropertyTag(2), JsonPrimitive("PT10S")),
)
assertThat(guestAdapter.takeChanges()).isEqualTo(expected)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class ProtocolTest {
// Text
Create(Id(2), WidgetTag(3)),
// text
PropertyChange(Id(2), PropertyTag(1), JsonPrimitive("hey")),
PropertyChange(Id(2), WidgetTag(3), PropertyTag(1), JsonPrimitive("hey")),
ModifierChange(Id(2)),
ChildrenChange.Add(Id(1), ChildrenTag(1), Id(2), 0),
// Row
Expand All @@ -129,7 +129,7 @@ class ProtocolTest {
// Text
Create(Id(4), WidgetTag(3)),
// text
PropertyChange(Id(4), PropertyTag(1), JsonPrimitive("hello")),
PropertyChange(Id(4), WidgetTag(3), PropertyTag(1), JsonPrimitive("hello")),
ModifierChange(Id(4)),
ChildrenChange.Add(Id(3), ChildrenTag(1), Id(4), 0),
ChildrenChange.Add(Id(1), ChildrenTag(1), Id(3), 1),
Expand All @@ -151,29 +151,29 @@ class ProtocolTest {
// Button
Create(Id(1), WidgetTag(4)),
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("hi")),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(1), JsonPrimitive("hi")),
// onClick
PropertyChange(Id(1), PropertyTag(2), JsonPrimitive(false)),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(2), JsonPrimitive(false)),
// color
PropertyChange(Id(1), PropertyTag(3), JsonPrimitive(0u)),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(3), JsonPrimitive(0u)),
ModifierChange(Id(1)),
ChildrenChange.Add(Id.Root, ChildrenTag.Root, Id(1), 0),
// Button
Create(Id(2), WidgetTag(4)),
// text
PropertyChange(Id(2), PropertyTag(1), JsonPrimitive("hi")),
PropertyChange(Id(2), WidgetTag(4), PropertyTag(1), JsonPrimitive("hi")),
// onClick
PropertyChange(Id(2), PropertyTag(2), JsonPrimitive(true)),
PropertyChange(Id(2), WidgetTag(4), PropertyTag(2), JsonPrimitive(true)),
// color
PropertyChange(Id(2), PropertyTag(3), JsonPrimitive(0u)),
PropertyChange(Id(2), WidgetTag(4), PropertyTag(3), JsonPrimitive(0u)),
ModifierChange(Id(2)),
ChildrenChange.Add(Id.Root, ChildrenTag.Root, Id(2), 1),
// Button2
Create(Id(3), WidgetTag(7)),
// text
PropertyChange(Id(3), PropertyTag(1), JsonPrimitive("hi")),
PropertyChange(Id(3), WidgetTag(7), PropertyTag(1), JsonPrimitive("hi")),
// onClick
PropertyChange(Id(3), PropertyTag(2), JsonPrimitive(true)),
PropertyChange(Id(3), WidgetTag(7), PropertyTag(2), JsonPrimitive(true)),
ModifierChange(Id(3)),
ChildrenChange.Add(Id.Root, ChildrenTag.Root, Id(3), 2),
),
Expand Down Expand Up @@ -214,11 +214,11 @@ class ProtocolTest {
// Button
Create(Id(1), WidgetTag(4)),
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("state: 0")),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(1), JsonPrimitive("state: 0")),
// onClick
PropertyChange(Id(1), PropertyTag(2), JsonPrimitive(true)),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(2), JsonPrimitive(true)),
// color
PropertyChange(Id(1), PropertyTag(3), JsonPrimitive(0u)),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(3), JsonPrimitive(0u)),
ModifierChange(Id(1)),
ChildrenChange.Add(Id.Root, ChildrenTag.Root, Id(1), 0),
),
Expand All @@ -230,7 +230,7 @@ class ProtocolTest {
assertThat(composition.awaitSnapshot()).isEqualTo(
listOf(
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("state: 1")),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(1), JsonPrimitive("state: 1")),
),
)

Expand All @@ -240,9 +240,9 @@ class ProtocolTest {
assertThat(composition.awaitSnapshot()).isEqualTo(
listOf(
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("state: 2")),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(1), JsonPrimitive("state: 2")),
// text
PropertyChange(Id(1), PropertyTag(2), JsonPrimitive(false)),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(2), JsonPrimitive(false)),
),
)

Expand All @@ -252,7 +252,7 @@ class ProtocolTest {
assertThat(composition.awaitSnapshot()).isEqualTo(
listOf(
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("state: 3")),
PropertyChange(Id(1), WidgetTag(4), PropertyTag(1), JsonPrimitive("state: 3")),
),
)
}
Expand Down Expand Up @@ -283,9 +283,9 @@ class ProtocolTest {
// Button2
Create(Id(1), WidgetTag(7)),
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("state: 0")),
PropertyChange(Id(1), WidgetTag(7), PropertyTag(1), JsonPrimitive("state: 0")),
// onClick
PropertyChange(Id(1), PropertyTag(2), JsonPrimitive(true)),
PropertyChange(Id(1), WidgetTag(7), PropertyTag(2), JsonPrimitive(true)),
ModifierChange(Id(1)),
ChildrenChange.Add(Id.Root, ChildrenTag.Root, Id(1), 0),
),
Expand All @@ -297,7 +297,7 @@ class ProtocolTest {
assertThat(composition.awaitSnapshot()).isEqualTo(
listOf(
// text
PropertyChange(Id(1), PropertyTag(1), JsonPrimitive("state: 1")),
PropertyChange(Id(1), WidgetTag(7), PropertyTag(1), JsonPrimitive("state: 1")),
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ class HostProtocolAdapterTest {
// Set Button's required color property.
PropertyChange(
id = Id(1),
tag = PropertyTag(3),
widgetTag = WidgetTag(4),
propertyTag = PropertyTag(3),
value = JsonPrimitive(0),
),
Add(
Expand Down Expand Up @@ -155,8 +156,9 @@ class HostProtocolAdapterTest {
val updateButtonText = listOf(
PropertyChange(
id = Id(1),
widgetTag = WidgetTag(4),
// text
tag = PropertyTag(1),
propertyTag = PropertyTag(1),
value = JsonPrimitive("hello"),
),
)
Expand Down Expand Up @@ -230,7 +232,7 @@ class HostProtocolAdapterTest {
ModifierChange(Id(2)),
// Text
Create(Id(3), WidgetTag(3)),
PropertyChange(Id(3), PropertyTag(1), JsonPrimitive("hello")),
PropertyChange(Id(3), WidgetTag(3), PropertyTag(1), JsonPrimitive("hello")),
ModifierChange(Id(3)),
Add(Id(2), ChildrenTag(1), Id(3), 0),
Add(Id(1), ChildrenTag(1), Id(2), 0),
Expand All @@ -241,7 +243,7 @@ class HostProtocolAdapterTest {
// Validate we're tracking ID=3.
host.sendChanges(
listOf(
PropertyChange(Id(3), PropertyTag(1), JsonPrimitive("hey")),
PropertyChange(Id(3), WidgetTag(3), PropertyTag(1), JsonPrimitive("hey")),
),
)

Expand All @@ -255,7 +257,7 @@ class HostProtocolAdapterTest {
assertFailure {
host.sendChanges(
listOf(
PropertyChange(Id(3), PropertyTag(1), JsonPrimitive("sup")),
PropertyChange(Id(3), WidgetTag(3), PropertyTag(1), JsonPrimitive("sup")),
),
)
}.isInstanceOf<IllegalStateException>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ class ProtocolFactoryTest {
val textInput = factory.widget(WidgetTag(5))!!.createNode(Id(1))

val throwingEventSink = UiEventSink { error(it) }
textInput.apply(PropertyChange(Id(1), PropertyTag(2), JsonPrimitive("PT10S")), throwingEventSink)
textInput.apply(PropertyChange(Id(1), WidgetTag(5), PropertyTag(2), JsonPrimitive("PT10S")), throwingEventSink)

assertThat((textInput.widget.value as TextInputValue).customType).isEqualTo(10.seconds)
}
Expand All @@ -272,7 +272,7 @@ class ProtocolFactoryTest {
)
val button = factory.widget(WidgetTag(4))!!.createNode(Id(1))

val change = PropertyChange(Id(1), PropertyTag(345432))
val change = PropertyChange(Id(1), WidgetTag(4), PropertyTag(345432))
val eventSink = UiEventSink { throw UnsupportedOperationException() }
val t = assertFailsWith<IllegalArgumentException> {
button.apply(change, eventSink)
Expand All @@ -292,7 +292,7 @@ class ProtocolFactoryTest {
)
val button = factory.widget(WidgetTag(4))!!.createNode(Id(1))

button.apply(PropertyChange(Id(1), PropertyTag(345432))) { throw UnsupportedOperationException() }
button.apply(PropertyChange(Id(1), WidgetTag(4), PropertyTag(345432))) { throw UnsupportedOperationException() }

assertThat(handler.events.single()).isEqualTo("Unknown property 345432 for 4")
}
Expand All @@ -314,7 +314,7 @@ class ProtocolFactoryTest {
val textInput = factory.widget(WidgetTag(5))!!.createNode(Id(1))

val eventSink = RecordingUiEventSink()
textInput.apply(PropertyChange(Id(1), PropertyTag(4), JsonPrimitive(true)), eventSink)
textInput.apply(PropertyChange(Id(1), WidgetTag(5), PropertyTag(4), JsonPrimitive(true)), eventSink)

(textInput.widget.value as TextInputValue).onChangeCustomType!!.invoke(10.seconds)

Expand Down
9 changes: 5 additions & 4 deletions redwood-protocol/api/redwood-protocol.api
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,12 @@ public final class app/cash/redwood/protocol/ModifierTag$Companion {

public final class app/cash/redwood/protocol/PropertyChange : app/cash/redwood/protocol/ValueChange {
public static final field Companion Lapp/cash/redwood/protocol/PropertyChange$Companion;
public synthetic fun <init> (IILkotlinx/serialization/json/JsonElement;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (IIILkotlinx/serialization/json/JsonElement;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public fun getId-0HhLjSo ()I
public final fun getTag-VGAaYLs ()I
public final fun getPropertyTag-VGAaYLs ()I
public final fun getValue ()Lkotlinx/serialization/json/JsonElement;
public final fun getWidgetTag-BlhN7y0 ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand All @@ -347,8 +348,8 @@ public synthetic class app/cash/redwood/protocol/PropertyChange$$serializer : ko
}

public final class app/cash/redwood/protocol/PropertyChange$Companion {
public final fun invoke-e3iP1vo (IILkotlinx/serialization/json/JsonElement;)Lapp/cash/redwood/protocol/PropertyChange;
public static synthetic fun invoke-e3iP1vo$default (Lapp/cash/redwood/protocol/PropertyChange$Companion;IILkotlinx/serialization/json/JsonElement;ILjava/lang/Object;)Lapp/cash/redwood/protocol/PropertyChange;
public final fun invoke-ITsWdOQ (IIILkotlinx/serialization/json/JsonElement;)Lapp/cash/redwood/protocol/PropertyChange;
public static synthetic fun invoke-ITsWdOQ$default (Lapp/cash/redwood/protocol/PropertyChange$Companion;IIILkotlinx/serialization/json/JsonElement;ILjava/lang/Object;)Lapp/cash/redwood/protocol/PropertyChange;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

Expand Down
8 changes: 5 additions & 3 deletions redwood-protocol/api/redwood-protocol.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,12 @@ final class app.cash.redwood.protocol/ModifierElement { // app.cash.redwood.prot
final class app.cash.redwood.protocol/PropertyChange : app.cash.redwood.protocol/ValueChange { // app.cash.redwood.protocol/PropertyChange|null[0]
final val id // app.cash.redwood.protocol/PropertyChange.id|{}id[0]
final fun <get-id>(): app.cash.redwood.protocol/Id // app.cash.redwood.protocol/PropertyChange.id.<get-id>|<get-id>(){}[0]
final val tag // app.cash.redwood.protocol/PropertyChange.tag|{}tag[0]
final fun <get-tag>(): app.cash.redwood.protocol/PropertyTag // app.cash.redwood.protocol/PropertyChange.tag.<get-tag>|<get-tag>(){}[0]
final val propertyTag // app.cash.redwood.protocol/PropertyChange.propertyTag|{}propertyTag[0]
final fun <get-propertyTag>(): app.cash.redwood.protocol/PropertyTag // app.cash.redwood.protocol/PropertyChange.propertyTag.<get-propertyTag>|<get-propertyTag>(){}[0]
final val value // app.cash.redwood.protocol/PropertyChange.value|{}value[0]
final fun <get-value>(): kotlinx.serialization.json/JsonElement // app.cash.redwood.protocol/PropertyChange.value.<get-value>|<get-value>(){}[0]
final val widgetTag // app.cash.redwood.protocol/PropertyChange.widgetTag|{}widgetTag[0]
final fun <get-widgetTag>(): app.cash.redwood.protocol/WidgetTag // app.cash.redwood.protocol/PropertyChange.widgetTag.<get-widgetTag>|<get-widgetTag>(){}[0]

final fun equals(kotlin/Any?): kotlin/Boolean // app.cash.redwood.protocol/PropertyChange.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // app.cash.redwood.protocol/PropertyChange.hashCode|hashCode(){}[0]
Expand All @@ -245,7 +247,7 @@ final class app.cash.redwood.protocol/PropertyChange : app.cash.redwood.protocol
}

final object Companion { // app.cash.redwood.protocol/PropertyChange.Companion|null[0]
final fun invoke(app.cash.redwood.protocol/Id, app.cash.redwood.protocol/PropertyTag, kotlinx.serialization.json/JsonElement = ...): app.cash.redwood.protocol/PropertyChange // app.cash.redwood.protocol/PropertyChange.Companion.invoke|invoke(app.cash.redwood.protocol.Id;app.cash.redwood.protocol.PropertyTag;kotlinx.serialization.json.JsonElement){}[0]
final fun invoke(app.cash.redwood.protocol/Id, app.cash.redwood.protocol/WidgetTag, app.cash.redwood.protocol/PropertyTag, kotlinx.serialization.json/JsonElement = ...): app.cash.redwood.protocol/PropertyChange // app.cash.redwood.protocol/PropertyChange.Companion.invoke|invoke(app.cash.redwood.protocol.Id;app.cash.redwood.protocol.WidgetTag;app.cash.redwood.protocol.PropertyTag;kotlinx.serialization.json.JsonElement){}[0]
final fun serializer(): kotlinx.serialization/KSerializer<app.cash.redwood.protocol/PropertyChange> // app.cash.redwood.protocol/PropertyChange.Companion.serializer|serializer(){}[0]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,29 @@ public sealed interface ValueChange : Change
public class PropertyChange private constructor(
@SerialName("id")
private val _id: Int,
@SerialName("widget")
private val _widgetTag: Int,
@SerialName("tag")
private val _tag: Int,
public val value: JsonElement = JsonNull,
) : ValueChange {
override val id: Id get() = Id(_id)

/** Identifies which property changed on the widget with [id]. */
public val tag: PropertyTag get() = PropertyTag(_tag)
/** Identifies the widget on which the property changed. */
public val widgetTag: WidgetTag get() = WidgetTag(_widgetTag)

/** Identifies which property changed on the widget. */
public val propertyTag: PropertyTag get() = PropertyTag(_tag)

public companion object {
public operator fun invoke(
id: Id,
/** Identifies which property changed on the widget with [id]. */
tag: PropertyTag,
/** Identifies the widget on which the property changed. */
widgetTag: WidgetTag,
/** Identifies which property changed on the widget. */
propertyTag: PropertyTag,
value: JsonElement = JsonNull,
): PropertyChange = PropertyChange(id.value, tag.value, value)
): PropertyChange = PropertyChange(id.value, widgetTag.value, propertyTag.value, value)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ class ProtocolTest {
),
),
),
PropertyChange(Id(1), PropertyTag(2), JsonPrimitive("hello")),
PropertyChange(Id(1), PropertyTag(2), JsonNull),
PropertyChange(Id(1), WidgetTag(2), PropertyTag(2), JsonPrimitive("hello")),
PropertyChange(Id(1), WidgetTag(2), PropertyTag(2), JsonNull),
)
val json = "" +
"[" +
Expand All @@ -95,8 +95,8 @@ class ProtocolTest {
"""["remove",{"id":4,"tag":3,"index":2,"count":1}],""" +
"""["remove",{"id":1,"tag":2,"index":3,"count":4,"removedIds":[5,6,7,8]}],""" +
"""["modifier",{"id":1,"elements":[[1,{}],[2,3],[3,[]],[4],[5]]}],""" +
"""["property",{"id":1,"tag":2,"value":"hello"}],""" +
"""["property",{"id":1,"tag":2}]""" +
"""["property",{"id":1,"widget":2,"tag":2,"value":"hello"}],""" +
"""["property",{"id":1,"widget":2,"tag":2}]""" +
"]"
assertJsonRoundtrip(ListSerializer(Change.serializer()), changes, json)
}
Expand Down
Loading

0 comments on commit abcbf03

Please sign in to comment.