mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-04 09:49:32 +00:00
Fix wrong selection behaviour
This commit is contained in:
+85
-77
@@ -110,83 +110,6 @@ fun EmojiItem(
|
|||||||
.background(backgroundColor, CircleShape)
|
.background(backgroundColor, CircleShape)
|
||||||
.indication(interactionSource, ripple())
|
.indication(interactionSource, ripple())
|
||||||
.pointerInput(item) {
|
.pointerInput(item) {
|
||||||
// Only detect drag after long press for those items which have a skin tone picker
|
|
||||||
if (hasSkinTones) {
|
|
||||||
val slotWidthPx = with(density) { SkinToneSlotSize.toPx() }
|
|
||||||
val spacingPx = with(density) { SkinToneSlotSpacing.toPx() }
|
|
||||||
val paddingPx = with(density) { SkinTonePadding.toPx() }
|
|
||||||
var startOffset = Offset.Zero
|
|
||||||
detectDragGesturesAfterLongPress(
|
|
||||||
onDragStart = { position ->
|
|
||||||
if (onLongPress != null) {
|
|
||||||
startOffset = position
|
|
||||||
dismissed = false
|
|
||||||
onLongPress(item)
|
|
||||||
hoveredIndex = -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDrag = { change, _ ->
|
|
||||||
if (!dismissed) {
|
|
||||||
change.consume()
|
|
||||||
// It's a valid drag event if it's within ~2 items height above, so, on top of the current one or the skin tone selector,
|
|
||||||
// or 1 item below, which should be far enough to not be triggered by mistake
|
|
||||||
val isValidDrag = if (change.position.y < startOffset.y) {
|
|
||||||
startOffset.y - change.position.y <= itemSize.height * 2f
|
|
||||||
} else {
|
|
||||||
change.position.y - startOffset.y <= itemSize.height
|
|
||||||
}
|
|
||||||
if (!isValidDrag) {
|
|
||||||
dismissed = true
|
|
||||||
hoveredIndex = -1
|
|
||||||
onDismissSkinPicker?.invoke()
|
|
||||||
} else {
|
|
||||||
val skinItemsCount = item.skins!!.size
|
|
||||||
// Original + variants
|
|
||||||
val totalSlots = 1 + skinItemsCount
|
|
||||||
// Calculate the whole size of the skin tone picker
|
|
||||||
val pickerWidthPx = 2 * paddingPx + totalSlots * slotWidthPx + (totalSlots - 1) * spacingPx
|
|
||||||
// Calculate the initial offset inside the picker, given the relative position of the item inside its parent
|
|
||||||
val initialOffset = pickerWidthPx * itemOffsetPercent
|
|
||||||
val xInPicker = initialOffset + change.position.x
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
(xInPicker / slotWidthPx).toInt().coerceIn(0, totalSlots - 1)
|
|
||||||
} else {
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
hoveredIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDragEnd = {
|
|
||||||
if (!dismissed) {
|
|
||||||
val idx = hoveredIndex
|
|
||||||
val skinCount = item.skins?.size ?: 0
|
|
||||||
if (idx in 0..skinCount) {
|
|
||||||
if (idx == 0) {
|
|
||||||
onSelectEmoji(item)
|
|
||||||
} else {
|
|
||||||
val skin = item.skins?.getOrNull(idx - 1)
|
|
||||||
if (skin != null) {
|
|
||||||
onSelectEmoji(item.copy(unicode = skin.unicode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (idx < 0) {
|
|
||||||
onSelectEmoji(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dismissed = false
|
|
||||||
hoveredIndex = -1
|
|
||||||
onDismissSkinPicker?.invoke()
|
|
||||||
},
|
|
||||||
onDragCancel = {
|
|
||||||
dismissed = false
|
|
||||||
hoveredIndex = -1
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always detect long press and tap gestures
|
// Always detect long press and tap gestures
|
||||||
detectTapGestures(
|
detectTapGestures(
|
||||||
onPress = { pressOffset ->
|
onPress = { pressOffset ->
|
||||||
@@ -194,6 +117,9 @@ fun EmojiItem(
|
|||||||
interactionSource.emit(press)
|
interactionSource.emit(press)
|
||||||
if (tryAwaitRelease()) {
|
if (tryAwaitRelease()) {
|
||||||
interactionSource.emit(PressInteraction.Release(press))
|
interactionSource.emit(PressInteraction.Release(press))
|
||||||
|
if (hasSkinTones) {
|
||||||
|
onSelectEmoji(item)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
interactionSource.emit(PressInteraction.Cancel(press))
|
interactionSource.emit(PressInteraction.Cancel(press))
|
||||||
}
|
}
|
||||||
@@ -201,6 +127,88 @@ fun EmojiItem(
|
|||||||
onTap = { onSelectEmoji(item) },
|
onTap = { onSelectEmoji(item) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.then(
|
||||||
|
// Only detect drag after long press for those items which have a skin tone picker
|
||||||
|
if (hasSkinTones) {
|
||||||
|
Modifier.pointerInput(item) {
|
||||||
|
val slotWidthPx = with(density) { SkinToneSlotSize.toPx() }
|
||||||
|
val spacingPx = with(density) { SkinToneSlotSpacing.toPx() }
|
||||||
|
val paddingPx = with(density) { SkinTonePadding.toPx() }
|
||||||
|
var startOffset = Offset.Zero
|
||||||
|
detectDragGesturesAfterLongPress(
|
||||||
|
onDragStart = { position ->
|
||||||
|
if (onLongPress != null) {
|
||||||
|
startOffset = position
|
||||||
|
dismissed = false
|
||||||
|
onLongPress(item)
|
||||||
|
hoveredIndex = -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDrag = { change, _ ->
|
||||||
|
if (!dismissed) {
|
||||||
|
change.consume()
|
||||||
|
// It's a valid drag event if it's within ~2 items height above, so, on top of the current one or the skin tone selector,
|
||||||
|
// or 1 item below, which should be far enough to not be triggered by mistake
|
||||||
|
val isValidDrag = if (change.position.y < startOffset.y) {
|
||||||
|
startOffset.y - change.position.y <= itemSize.height * 2f
|
||||||
|
} else {
|
||||||
|
change.position.y - startOffset.y <= itemSize.height
|
||||||
|
}
|
||||||
|
if (!isValidDrag) {
|
||||||
|
dismissed = true
|
||||||
|
hoveredIndex = -1
|
||||||
|
onDismissSkinPicker?.invoke()
|
||||||
|
} else {
|
||||||
|
val skinItemsCount = item.skins!!.size
|
||||||
|
// Original + variants
|
||||||
|
val totalSlots = 1 + skinItemsCount
|
||||||
|
// Calculate the whole size of the skin tone picker
|
||||||
|
val pickerWidthPx = 2 * paddingPx + totalSlots * slotWidthPx + (totalSlots - 1) * spacingPx
|
||||||
|
// Calculate the initial offset inside the picker, given the relative position of the item inside its parent
|
||||||
|
val initialOffset = pickerWidthPx * itemOffsetPercent
|
||||||
|
val xInPicker = initialOffset + change.position.x
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
(xInPicker / slotWidthPx).toInt().coerceIn(0, totalSlots - 1)
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
hoveredIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
if (!dismissed) {
|
||||||
|
val idx = hoveredIndex
|
||||||
|
val skinCount = item.skins?.size ?: 0
|
||||||
|
if (idx in 0..skinCount) {
|
||||||
|
if (idx == 0) {
|
||||||
|
onSelectEmoji(item)
|
||||||
|
} else {
|
||||||
|
val skin = item.skins?.getOrNull(idx - 1)
|
||||||
|
if (skin != null) {
|
||||||
|
onSelectEmoji(item.copy(unicode = skin.unicode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (idx < 0) {
|
||||||
|
onSelectEmoji(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dismissed = false
|
||||||
|
hoveredIndex = -1
|
||||||
|
onDismissSkinPicker?.invoke()
|
||||||
|
},
|
||||||
|
onDragCancel = {
|
||||||
|
dismissed = false
|
||||||
|
hoveredIndex = -1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
)
|
||||||
.clearAndSetSemantics {
|
.clearAndSetSemantics {
|
||||||
contentDescription = description
|
contentDescription = description
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user