Fix long messages not being clickable (#6356)
* Fix long messages not being clickable As @bmarty found out, `clip = true` causes the click event to be ignored in some cases. Since we have the shape we want to draw and we're using a custom `onDraw` modifier anyway to cut-out part of the path, we can just draw everything using the modifier and avoid using `clip = true`. This seems to fix the issue. * Fix clipping of images or other items that cover the bubble * Fix borders being displayed for contents * Extract the layer drawing logic into `drawInLayer` to simplify the inlined code. Remove redundant code, those changes are now in the `drawInLayer` block * Workaround for lint issue: it seems like detekt can't properly detect usages in content receivers * Update screenshots --------- Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
committed by
GitHub
parent
13bbd24df1
commit
a2d9f241dd
@@ -22,14 +22,12 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.graphics.layer.CompositingStrategy
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -49,6 +47,7 @@ import io.element.android.libraries.designsystem.theme.messageFromMeBackground
|
||||
import io.element.android.libraries.designsystem.theme.messageFromOtherBackground
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.utils.graphics.drawInLayer
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
|
||||
private val BUBBLE_RADIUS = 12.dp
|
||||
@@ -78,32 +77,45 @@ fun MessageEventBubble(
|
||||
.onKeyboardContextMenuAction(onLongClick)
|
||||
}
|
||||
|
||||
val cutTopStart = state.cutTopStart
|
||||
// Ignore state.isHighlighted for now, we need a design decision on it.
|
||||
val backgroundBubbleColor = MessageEventBubbleDefaults.backgroundBubbleColor(state.isMine)
|
||||
val bubbleShape = remember(state) { MessageEventBubbleDefaults.shape(state.cutTopStart, state.groupPosition, state.isMine) }
|
||||
val radiusPx = (avatarRadius + SENDER_AVATAR_BORDER_WIDTH).toPx()
|
||||
val yOffsetPx = -(NEGATIVE_MARGIN_FOR_BUBBLE + avatarRadius).toPx()
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
.graphicsLayer {
|
||||
shape = bubbleShape
|
||||
clip = true
|
||||
compositingStrategy = CompositingStrategy.Offscreen
|
||||
}
|
||||
.drawWithContent {
|
||||
drawRect(backgroundBubbleColor)
|
||||
drawContent()
|
||||
if (state.cutTopStart) {
|
||||
drawCircle(
|
||||
color = Color.Black,
|
||||
center = Offset(
|
||||
x = if (isRtl) size.width else 0f,
|
||||
y = yOffsetPx,
|
||||
),
|
||||
radius = radiusPx,
|
||||
blendMode = BlendMode.Clear,
|
||||
)
|
||||
.drawWithCache {
|
||||
// Calculate the outline of the background and cache it
|
||||
val outline = bubbleShape.createOutline(size, layoutDirection, this)
|
||||
|
||||
onDrawWithContent {
|
||||
// Draw the contents in a layer to be able to clip them with the same outline
|
||||
// For some reason, doing this clipping outside a layer messes up with the touch events
|
||||
drawInLayer(
|
||||
composingStrategy = CompositingStrategy.Offscreen,
|
||||
outline = outline,
|
||||
clip = true,
|
||||
) {
|
||||
// Draw the background first, so that it's behind the content
|
||||
drawRect(backgroundBubbleColor)
|
||||
|
||||
// Then draw the content on top of it
|
||||
drawContent()
|
||||
|
||||
// And then clip the top start corner if needed to make room for the avatar
|
||||
if (cutTopStart) {
|
||||
drawCircle(
|
||||
color = Color.Black,
|
||||
center = Offset(
|
||||
x = if (layoutDirection == LayoutDirection.Rtl) size.width else 0f,
|
||||
y = yOffsetPx,
|
||||
),
|
||||
radius = radiusPx,
|
||||
blendMode = BlendMode.Clear,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Need to set the contentAlignment again (it's already set in TimelineItemEventRow), for the case
|
||||
|
||||
Reference in New Issue
Block a user