mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-04 10:32:34 +00:00
Fix skin tone picker always selecting the middle item by default instead of based on the current pointer position
This commit is contained in:
+29
-10
@@ -25,6 +25,8 @@ import androidx.compose.material3.LocalTextStyle
|
|||||||
import androidx.compose.material3.ripple
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -35,7 +37,9 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.Path
|
import androidx.compose.ui.graphics.Path
|
||||||
import androidx.compose.ui.graphics.drawscope.Fill
|
import androidx.compose.ui.graphics.drawscope.Fill
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.layout.onPlaced
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.layout.positionInParent
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
@@ -77,7 +81,8 @@ fun EmojiItem(
|
|||||||
}
|
}
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
var itemSize by remember { mutableStateOf(IntSize.Zero) }
|
var itemSize by remember { mutableStateOf(IntSize.Zero) }
|
||||||
var hoveredIndex by remember { mutableStateOf(-1) }
|
var itemOffsetPercent by remember { mutableFloatStateOf(0f) }
|
||||||
|
var hoveredIndex by remember { mutableIntStateOf(-1) }
|
||||||
var dismissed by remember { mutableStateOf(false) }
|
var dismissed by remember { mutableStateOf(false) }
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val description = a11yReactionAction(
|
val description = a11yReactionAction(
|
||||||
@@ -87,6 +92,17 @@ fun EmojiItem(
|
|||||||
val hasSkinTones = !item.skins.isNullOrEmpty()
|
val hasSkinTones = !item.skins.isNullOrEmpty()
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.onPlaced { coordinates ->
|
||||||
|
val position = coordinates.positionInParent()
|
||||||
|
val parentBounds = coordinates.parentLayoutCoordinates?.size ?: return@onPlaced
|
||||||
|
// Calculate the offset of the item inside its parent as a percentage, to be able to set the initial offset
|
||||||
|
// for the selection inside the skin tone picker relative to its size.
|
||||||
|
itemOffsetPercent = if (parentBounds.width > 0) {
|
||||||
|
position.x / parentBounds.width
|
||||||
|
} else {
|
||||||
|
0f
|
||||||
|
}
|
||||||
|
}
|
||||||
.sizeIn(minWidth = 40.dp, minHeight = 40.dp)
|
.sizeIn(minWidth = 40.dp, minHeight = 40.dp)
|
||||||
.onSizeChanged { itemSize = it }
|
.onSizeChanged { itemSize = it }
|
||||||
.background(backgroundColor, CircleShape)
|
.background(backgroundColor, CircleShape)
|
||||||
@@ -94,6 +110,9 @@ fun EmojiItem(
|
|||||||
.pointerInput(item) {
|
.pointerInput(item) {
|
||||||
// Only detect drag after long press for those items which have a skin tone picker
|
// Only detect drag after long press for those items which have a skin tone picker
|
||||||
if (hasSkinTones) {
|
if (hasSkinTones) {
|
||||||
|
val slotWidthPx = with(density) { SkinToneSlotSize.toPx() }
|
||||||
|
val spacingPx = with(density) { SkinToneSlotSpacing.toPx() }
|
||||||
|
val paddingPx = with(density) { SkinTonePadding.toPx() }
|
||||||
var startOffset = Offset.Zero
|
var startOffset = Offset.Zero
|
||||||
detectDragGesturesAfterLongPress(
|
detectDragGesturesAfterLongPress(
|
||||||
onDragStart = { position ->
|
onDragStart = { position ->
|
||||||
@@ -119,16 +138,16 @@ fun EmojiItem(
|
|||||||
hoveredIndex = -1
|
hoveredIndex = -1
|
||||||
onDismissSkinPicker?.invoke()
|
onDismissSkinPicker?.invoke()
|
||||||
} else {
|
} else {
|
||||||
val skinCount = item.skins!!.size
|
val skinItemsCount = item.skins!!.size
|
||||||
val slotWidthPx = with(density) { SkinToneSlotSize.toPx() }
|
// Original + variants
|
||||||
val spacingPx = with(density) { SkinToneSlotSpacing.toPx() }
|
val totalSlots = 1 + skinItemsCount
|
||||||
val paddingPx = with(density) { SkinTonePadding.toPx() }
|
// Calculate the whole size of the skin tone picker
|
||||||
val totalSlots = 1 + skinCount
|
|
||||||
val pickerWidthPx = 2 * paddingPx + totalSlots * slotWidthPx + (totalSlots - 1) * spacingPx
|
val pickerWidthPx = 2 * paddingPx + totalSlots * slotWidthPx + (totalSlots - 1) * spacingPx
|
||||||
val pickerHalfWidthPx = pickerWidthPx / 2f
|
// Calculate the initial offset inside the picker, given the relative position of the item inside its parent
|
||||||
val centerXPx = itemSize.width / 2f
|
val initialOffset = pickerWidthPx * itemOffsetPercent
|
||||||
val pickerLeftPx = centerXPx - pickerHalfWidthPx
|
val xInPicker = initialOffset + change.position.x
|
||||||
val xInPicker = change.position.x - pickerLeftPx
|
|
||||||
|
// If it's a valid offset, calculate the hovered index, otherwise, set it to -1 to indicate that no item is hovered
|
||||||
val index = if (xInPicker in 0f..pickerWidthPx) {
|
val index = if (xInPicker in 0f..pickerWidthPx) {
|
||||||
(xInPicker / slotWidthPx).toInt().coerceIn(0, totalSlots - 1)
|
(xInPicker / slotWidthPx).toInt().coerceIn(0, totalSlots - 1)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user