Skip to content

Commit

Permalink
Fix the tap callback for squoosh component instance (#1777)
Browse files Browse the repository at this point in the history
Fixes: #1709
  • Loading branch information
yiqunw700 authored Nov 12, 2024
1 parent 6a52933 commit d7a865a
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import com.android.designcompose.serdegen.ColorOrVarType
import com.android.designcompose.serdegen.ComponentInfo
import com.android.designcompose.serdegen.Dimension
import com.android.designcompose.serdegen.NodeQuery
import com.android.designcompose.serdegen.View
import java.util.Optional
import kotlin.jvm.optionals.getOrNull

// Node data associated with a Figma node that may be a variant. This is used for grid layouts to
// determine the span of an item in the grid layout.
Expand Down Expand Up @@ -371,6 +373,17 @@ fun CustomizationContext.getTapCallback(nodeName: String): TapCallback? {
return null
}

fun CustomizationContext.getTapCallback(view: View): TapCallback? {
var tapCallback = getTapCallback(view.name)
// If no tap callback was found but this is a variant of a component set,
// look for a tap callback in the component set
val componentSetName = view.component_info.getOrNull()?.component_set_name
if (tapCallback == null && !componentSetName.isNullOrBlank()) {
tapCallback = getTapCallback(componentSetName)
}
return tapCallback
}

fun CustomizationContext.getContent(nodeName: String): ReplacementContent? {
val c = cs[nodeName] ?: return null
if (c.content.isPresent) return c.content.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ internal fun Modifier.squooshInteraction(
): Modifier {
val node = childComposable.node
val maybeReactions = node.view.reactions
if (!maybeReactions.isPresent) return this
val reactions = maybeReactions.get()
val reactions = maybeReactions.getOrNull()

val tapCallback = customizations.getTapCallback(node.view)

return this.then(
Modifier.pointerInput(reactions) {
Modifier.pointerInput(reactions, tapCallback) {
// Use the interaction scope so that we don't have our event handler removed when our
// Modifier node is removed from the tree (allowing interactions like "close the overlay
// while pressed" applied to an overlay to actually receive the touch release event and
Expand All @@ -87,8 +88,8 @@ internal fun Modifier.squooshInteraction(
onPress = {
// Set the "pressed" state.
reactions
.filter { r -> r.trigger.type is TriggerType.Press }
.forEach {
?.filter { r -> r.trigger.type is TriggerType.Press }
?.forEach {
interactionState.dispatch(
it.action.get(),
findTargetInstanceId(
Expand All @@ -104,8 +105,8 @@ internal fun Modifier.squooshInteraction(

// Clear the "pressed" state.
reactions
.filter { r -> r.trigger.type is TriggerType.Press }
.forEach {
?.filter { r -> r.trigger.type is TriggerType.Press }
?.forEach {
interactionState.undoDispatch(
findTargetInstanceId(
document,
Expand All @@ -121,8 +122,8 @@ internal fun Modifier.squooshInteraction(
// of us, etc) then we can run the action.
if (dispatchClickEvent) {
reactions
.filter { r -> r.trigger.type is TriggerType.Click }
.forEach {
?.filter { r -> r.trigger.type is TriggerType.Click }
?.forEach {
interactionState.dispatch(
it.action.get(),
findTargetInstanceId(
Expand All @@ -135,8 +136,8 @@ internal fun Modifier.squooshInteraction(
)
}
}
// Invoke the tap callback customization if one exists on this node
customizations.getTapCallback(node.view.name)?.invoke()

tapCallback?.invoke()
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ internal fun resolveVariantsRecursively(
}
}
}
if (customizations.getTapCallback(view.name) != null) hasSupportedInteraction = true
val tapCallback = customizations.getTapCallback(view)
if (tapCallback != null) hasSupportedInteraction = true

// If this node has a content customization, then we make a special record of it so that we can
// zip through all of them after layout and render them in the right location.
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ val EXAMPLES: ArrayList<Triple<String, @Composable () -> Unit, String?>> =
Triple("Battleship", { BattleshipTest() }, BattleshipDoc.javaClass.name),
Triple("Blend Modes", { BlendModeTest() }, BlendModeTestDoc.javaClass.name),
Triple("Component Replace", { ComponentReplaceTest() }, ComponentReplaceDoc.javaClass.name),
Triple(
"ComponentTapCallback",
{ ComponentTapCallbackTest() },
ComponentTapCallbackDoc.javaClass.name,
),
Triple("Custom Brush", { CustomBrushTest() }, CustomBrushTestDoc.javaClass.name),
// Dials gauges and progress vectors
Triple("Dials Gauges", { DialsGaugesTest() }, DialsGaugesTestDoc.javaClass.name),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2024 Google LLC
*
* 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 com.android.designcompose.testapp.validation.examples

import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.android.designcompose.ComponentReplacementContext
import com.android.designcompose.TapCallback
import com.android.designcompose.annotation.Design
import com.android.designcompose.annotation.DesignComponent
import com.android.designcompose.annotation.DesignDoc
import com.android.designcompose.annotation.DesignVariant

enum class DrivingState {
P,
R,
D,
}

@DesignDoc(id = "1jeKYynjk1nqYblZ66QDDK")
interface ComponentTapCallback {
@DesignComponent(node = "#Main", isRoot = true)
fun mainFrame(
@Design(node = "CompTest")
drivingStateIndicator: @Composable (ComponentReplacementContext) -> Unit
)

@DesignComponent(node = "CompTest")
fun drivingStateIndicator(
@DesignVariant(property = "prnd") state: DrivingState,
@Design(node = "CompTest") onPress: TapCallback,
)
}

@Composable
fun ComponentTapCallbackTest() {
val drivingState = remember { mutableStateOf(DrivingState.P) }
ComponentTapCallbackDoc.mainFrame(
drivingStateIndicator = {
ComponentTapCallbackDoc.drivingStateIndicator(
state = drivingState.value,
onPress = {
drivingState.value =
when (drivingState.value) {
DrivingState.P -> DrivingState.R
DrivingState.R -> DrivingState.D
DrivingState.D -> DrivingState.P
}
},
)
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2024 Google LLC
*
* 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 com.android.designcompose.testapp.validation

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.designcompose.TestUtils
import com.android.designcompose.test.internal.captureRootRoboImage
import com.android.designcompose.test.internal.designComposeRoborazziRule
import com.android.designcompose.testapp.common.InterFontTestRule
import com.android.designcompose.testapp.validation.examples.ComponentTapCallbackTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode

@RunWith(AndroidJUnit4::class)
@Config(qualifiers = "w1920dp-h1500dp-xlarge-long-notround-any-xhdpi-keyshidden-nonav")
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class TapCallbackTest {
@get:Rule val clearStateTestRule = TestUtils.ClearStateTestRule()
@get:Rule val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@get:Rule val roborazziRule = designComposeRoborazziRule(javaClass.simpleName)
@get:Rule val interFontTestRule = InterFontTestRule()

@Test
fun componentInstanceTapCallbackTest() {
with(composeTestRule) {
setContent { ComponentTapCallbackTest() }
onNodeWithText("Parked").assertExists()
onNodeWithText("Parked").performClick()
onNodeWithText("Reverse").assertExists()
captureRootRoboImage("ComponentTapCallbackTest-tapped")
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d7a865a

Please sign in to comment.