Skip to content

Commit 710d8ed

Browse files
committed
Support shape overrides and nested component overrides
1 parent e369f90 commit 710d8ed

File tree

74 files changed

+332
-104
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+332
-104
lines changed

crates/dc_bundle/src/definition/view.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,18 @@ impl ViewData {
334334
return Some(other.clone());
335335
}
336336
}
337+
if let Some(View_data_type::Container { 0: Container { shape, .. } }) =
338+
other.view_data_type.as_ref()
339+
{
340+
let other_shape = shape;
341+
if let Some(View_data_type::Container { 0: Container { shape, .. } }) =
342+
self.view_data_type.as_ref()
343+
{
344+
if other_shape != shape {
345+
return Some(other.clone());
346+
}
347+
}
348+
}
337349
return None;
338350
}
339351
}
@@ -484,6 +496,12 @@ impl View {
484496
pub fn find_view_by_id(&self, view_id: &String) -> Option<&View> {
485497
if view_id.as_str() == self.id {
486498
return Some(&self);
499+
} else if self.id.starts_with("I") {
500+
// If this id starts with I, meaning it is a neseted instance in this component.
501+
let stripped_id = &self.id[1..];
502+
if view_id.ends_with(stripped_id) {
503+
return Some(&self);
504+
}
487505
} else if let Some(id) = view_id.split(";").last() {
488506
// If this is a descendent node of an instance, the last section is the node id
489507
// of the view in the component. Example: I70:17;29:15

crates/dc_bundle/src/proto/definition/view/view.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ message ComponentOverrides {
115115
reserved 2 to 4;
116116
ViewStyle style = 1;
117117
ViewData view_data = 5;
118+
// This applys to the case when a nested instance was changed to another variant.
119+
optional string variant_id = 6;
118120
}
119121

120122
// Details on the Figma component that this view is an instance of.

crates/figma_import/src/document.rs

Lines changed: 234 additions & 83 deletions
Large diffs are not rendered by default.
0 Bytes
Binary file not shown.
Binary file not shown.

designcompose/src/main/java/com/android/designcompose/InteractionState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ internal fun InteractionState.changeTo(
353353
undoInstanceId: String?,
354354
) {
355355
val varKey = getInstanceIdWithKey(instanceNodeId, key)
356+
System.err.println("changeTo $varKey $newVariantId")
356357
val previousVariant = this.variantMemory.put(varKey, newVariantId)
357358
if (undoInstanceId != null) {
358359
val undoKey = getInstanceIdWithKey(undoInstanceId, key)
@@ -532,7 +533,7 @@ internal fun InteractionState.invalAnimations() {
532533
/// using the query, it may still be present but indexed using the variant map if the node is a
533534
/// variant, or by ID (if queried by name) or name (if queried by ID). So we do a linear search if
534535
/// the hash lookup and variant lookup fails.
535-
private fun searchNodes(
536+
internal fun searchNodes(
536537
q: NodeQuery,
537538
nodes: Map<NodeQuery, View>,
538539
parentViewMap: HashMap<String, HashMap<String, View>>,

designcompose/src/main/java/com/android/designcompose/squoosh/SquooshAnimate.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ private fun SquooshResolvedNode.cloneSelf(parent: SquooshResolvedNode?): Squoosh
606606
layoutId = this.layoutId,
607607
textInfo = this.textInfo,
608608
unresolvedNodeId = this.unresolvedNodeId,
609+
overrideViewData = this.overrideViewData,
609610
layoutNode = this,
610611
parent = parent,
611612
computedLayout = this.computedLayout,

designcompose/src/main/java/com/android/designcompose/squoosh/SquooshRender.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ internal fun Modifier.squooshRender(
145145
childRenderSelector.selectedRenderChild = null
146146
} else {
147147
val shape =
148-
if (node.view.data.hasContainer()) {
148+
if (node.overrideViewData?.hasContainer() == true) {
149+
node.overrideViewData!!.container.shape
150+
} else if (node.view.data.hasContainer()) {
149151
node.view.data.container.shape
150152
} else {
151153
// If this is text, just render the text and return

designcompose/src/main/java/com/android/designcompose/squoosh/SquooshResolvedNode.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.android.designcompose.squoosh
1919
import android.graphics.PointF
2020
import com.android.designcompose.TextMeasureData
2121
import com.android.designcompose.definition.view.View
22+
import com.android.designcompose.definition.view.ViewData
2223
import com.android.designcompose.definition.view.ViewStyle
2324
import com.android.designcompose.layout_interface.Layout
2425

@@ -38,6 +39,7 @@ internal class SquooshResolvedNode(
3839
// The node we look at for layout info, if we're in a tree that was derived
3940
// from other trees (i.e.: the tree that combines the base and transition
4041
// trees).
42+
val overrideViewData: ViewData?,
4143
val layoutNode: SquooshResolvedNode? = null,
4244
var firstChild: SquooshResolvedNode? = null,
4345
var nextSibling: SquooshResolvedNode? = null,

designcompose/src/main/java/com/android/designcompose/squoosh/SquooshTreeBuilder.kt

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import com.android.designcompose.KeyAction
3030
import com.android.designcompose.KeyEventTracker
3131
import com.android.designcompose.VariableState
3232
import com.android.designcompose.common.NodeQuery
33+
import com.android.designcompose.common.views
3334
import com.android.designcompose.definition.element.FontStyle
3435
import com.android.designcompose.definition.element.StrokeAlign
3536
import com.android.designcompose.definition.element.TextDecoration
@@ -88,6 +89,7 @@ import com.android.designcompose.getTapCallback
8889
import com.android.designcompose.getVisible
8990
import com.android.designcompose.getVisibleState
9091
import com.android.designcompose.isPressed
92+
import com.android.designcompose.searchNodes
9193
import com.android.designcompose.squooshNodeVariant
9294
import com.android.designcompose.squooshRootNode
9395
import com.android.designcompose.utils.hasScrolling
@@ -143,14 +145,17 @@ internal class SquooshOverlayComposable(val nodeId: String, val extras: FrameExt
143145
internal class ParentComponentData(
144146
val parent: ParentComponentData?,
145147
val instanceId: String,
148+
private val instanceName: String,
146149
val componentInfo: ComponentInfo,
147150
) {
148151
private val preComputedHashCode: Int =
149152
if (parent != null) {
150-
(parent.preComputedHashCode * 31 + instanceId.hashCode()) * 31 +
151-
componentInfo.id.hashCode()
153+
((parent.preComputedHashCode * 31 + instanceId.hashCode()) * 31 +
154+
instanceName.hashCode()) * 31
155+
componentInfo.id.hashCode()
152156
} else {
153-
instanceId.hashCode() * 31 + componentInfo.id.hashCode()
157+
(instanceId.hashCode() * 31 + instanceName.hashCode()) * 31 +
158+
componentInfo.id.hashCode()
154159
}
155160

156161
override fun hashCode(): Int {
@@ -166,10 +171,40 @@ internal class ParentComponentData(
166171
if (preComputedHashCode != other.preComputedHashCode) return false
167172
if (parent != other.parent) return false
168173
if (instanceId != other.instanceId) return false
174+
if (instanceName != other.instanceName) return false
169175
if (componentInfo != other.componentInfo) return false
170176

171177
return true
172178
}
179+
180+
fun getOverrideStyle(viewName: String, isComponentRoot: Boolean = false): ViewStyle? {
181+
if (isComponentRoot && parent == null) {
182+
return componentInfo.overridesTableMap[componentInfo.componentSetName]?.styleOrNull
183+
}
184+
val overrideStyle = componentInfo.overridesTableMap[viewName]?.styleOrNull
185+
val parentOverride = parent?.getOverrideStyle("$instanceName:$viewName")
186+
return parentOverride ?: overrideStyle
187+
}
188+
189+
fun getOverrideData(viewName: String, isComponentRoot: Boolean = false): ViewData? {
190+
if (isComponentRoot && parent == null) {
191+
return componentInfo.overridesTableMap[componentInfo.componentSetName]?.viewDataOrNull
192+
}
193+
val overrideData = componentInfo.overridesTableMap[viewName]?.viewDataOrNull
194+
val parentOverride = parent?.getOverrideData("$instanceName:$viewName")
195+
return parentOverride ?: overrideData
196+
}
197+
198+
fun getOverrideVariant(viewName: String, isComponentRoot: Boolean = false): String? {
199+
if (isComponentRoot) {
200+
return parent?.getOverrideVariant(instanceName)
201+
} else {
202+
val overrideVariant =
203+
componentInfo.overridesTableMap[viewName]?.takeIf { it.hasVariantId() }?.variantId
204+
val parentOverride = parent?.getOverrideVariant("$instanceName:$viewName")
205+
return parentOverride ?: overrideVariant
206+
}
207+
}
173208
}
174209

175210
/// Iterate over a given view tree recursively, applying customizations that will select
@@ -216,16 +251,15 @@ internal fun resolveVariantsRecursively(
216251
// need to get a different layout id.
217252
if (viewFromTree.hasComponentInfo()) {
218253
parentComps =
219-
ParentComponentData(parentComponents, viewFromTree.id, viewFromTree.componentInfo)
254+
ParentComponentData(
255+
parentComponents,
256+
viewFromTree.id,
257+
viewFromTree.name,
258+
viewFromTree.componentInfo,
259+
)
220260

221-
overrideStyle =
222-
viewFromTree.componentInfo.overridesTableMap[
223-
viewFromTree.componentInfo.componentSetName]
224-
?.styleOrNull
225-
overrideViewData =
226-
viewFromTree.componentInfo.overridesTableMap[
227-
viewFromTree.componentInfo.componentSetName]
228-
?.viewDataOrNull
261+
overrideStyle = parentComps.getOverrideStyle(viewFromTree.name, isComponentRoot = true)
262+
overrideViewData = parentComps.getOverrideData(viewFromTree.name, isComponentRoot = true)
229263

230264
// Ensure that the children of this component get unique layout ids, even though there
231265
// may be multiple instances of the same component in one tree.
@@ -240,6 +274,17 @@ internal fun resolveVariantsRecursively(
240274
.squooshNodeVariant(viewFromTree.id, customizations.getKey(), document)
241275
?.let { view = it }
242276

277+
parentComps.getOverrideVariant(viewFromTree.name, isComponentRoot = true)?.let {
278+
searchNodes(
279+
NodeQuery.NodeId(it),
280+
document.c.document.views(),
281+
document.c.variantViewMap,
282+
document.c.variantPropertyMap,
283+
document.c.nodeIdMap,
284+
)
285+
?.let { v -> view = v }
286+
}
287+
243288
// If we didn't replace the component because of an interaction, we might want to replace it
244289
// because of a variant customization.
245290
if (
@@ -281,10 +326,8 @@ internal fun resolveVariantsRecursively(
281326
variantTransition.selectedVariant(viewFromTree, view, customVariantTransition)
282327
}
283328
} else {
284-
overrideViewData =
285-
parentComps?.componentInfo?.overridesTableMap?.get(viewFromTree.name)?.viewDataOrNull
286-
overrideStyle =
287-
parentComps?.componentInfo?.overridesTableMap?.get(viewFromTree.name)?.styleOrNull
329+
overrideViewData = parentComps?.getOverrideData(viewFromTree.name)
330+
overrideStyle = parentComps?.getOverrideStyle(viewFromTree.name)
288331
}
289332

290333
// Calculate the style we're going to use. If we have an override style then we have to apply
@@ -356,7 +399,15 @@ internal fun resolveVariantsRecursively(
356399

357400
// If this is a text node being replaced, don't store textInfo into resolvedView
358401
val resolvedTextInfo = if (replacementComponent != null) null else textInfo
359-
val resolvedView = SquooshResolvedNode(view, style, layoutId, resolvedTextInfo, viewFromTree.id)
402+
val resolvedView =
403+
SquooshResolvedNode(
404+
view,
405+
style,
406+
layoutId,
407+
resolvedTextInfo,
408+
viewFromTree.id,
409+
overrideViewData,
410+
)
360411

361412
var skipChildren = false // Set to true for customizations that replace children
362413
var skipComposableList =
@@ -619,6 +670,7 @@ internal fun generateReplacementListChildNode(
619670
layoutId = layoutId,
620671
textInfo = null,
621672
unresolvedNodeId = "list-child-${node.unresolvedNodeId}-${childIdx}",
673+
overrideViewData = null,
622674
firstChild = null,
623675
nextSibling = null,
624676
parent = node,

integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/MainActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ const val TAG = "DesignCompose"
5757
class MainActivity : ComponentActivity() {
5858
private lateinit var currentDisplay:
5959
MutableState<Triple<String, @Composable () -> Unit, String?>>
60-
private lateinit var enableRendererToggle: MutableState<Boolean>
6160

6261
override fun onCreate(savedInstanceState: Bundle?) {
6362
super.onCreate(savedInstanceState)

integration-tests/validation/src/testDebug/kotlin/com/android/designcompose/testapp/validation/ComponentOverrides.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class ComponentOverrides {
133133
fun testComponent_overrideTextProperty() {
134134
with(composeTestRule) {
135135
with(onDCDocAnyNode(ComponentOverridesTestDoc)) {
136-
onNodeWithTag("#button").performTouchInput { down(center) }
136+
onNodeWithTag("#ok_button").performTouchInput { down(center) }
137137
captureRootRoboImage("button_overrideTextProperty")
138138
}
139139
}

0 commit comments

Comments
 (0)