Fix drag & drop

This commit is contained in:
2026-09-11 16:42:39 +03:00
Unverified
parent 332c5a6600
commit 8033d53aa2
@@ -69,23 +69,39 @@ object AttachmentDropHighlight {
var activeBridge by mutableStateOf<AttachmentDropBridge?>(null) var activeBridge by mutableStateOf<AttachmentDropBridge?>(null)
private set private set
private val boundsByBridge = mutableMapOf<AttachmentDropBridge, Rect>() private val boundsByKey = mutableMapOf<Any, Pair<AttachmentDropBridge, Rect>>()
private var lastPointer by mutableStateOf<Offset?>(null)
fun register(bridge: AttachmentDropBridge, bounds: Rect) { fun register(key: Any, bridge: AttachmentDropBridge, bounds: Rect) {
boundsByBridge[bridge] = bounds boundsByKey[key] = bridge to bounds
} }
fun unregister(bridge: AttachmentDropBridge) { fun unregister(key: Any) {
boundsByBridge.remove(bridge) boundsByKey.remove(key)
if (activeBridge === bridge) activeBridge = null if (activeBridge != null && boundsByKey.values.none { it.first === activeBridge }) {
activeBridge = hitTest(lastPointer)
}
} }
fun syncFromPointer(windowPoint: Offset) { fun syncFromPointer(windowPoint: Offset) {
lastPointer = windowPoint
activeBridge = hitTest(windowPoint) activeBridge = hitTest(windowPoint)
} }
fun clear() { fun clear() {
activeBridge = null activeBridge = null
lastPointer = null
}
fun isHighlightActive(bridge: AttachmentDropBridge): Boolean {
if (!AttachmentDragSession.isActive) return false
val point = lastPointer
if (point != null) {
return boundsByKey.values.any { (owner, bounds) ->
owner === bridge && bounds.contains(point)
}
}
return activeBridge === bridge
} }
/** Delivers to the currently hovered target. Returns true if handled. */ /** Delivers to the currently hovered target. Returns true if handled. */
@@ -97,10 +113,11 @@ object AttachmentDropHighlight {
return true return true
} }
private fun hitTest(windowPoint: Offset): AttachmentDropBridge? { private fun hitTest(windowPoint: Offset?): AttachmentDropBridge? {
if (windowPoint == null) return null
var best: AttachmentDropBridge? = null var best: AttachmentDropBridge? = null
var bestArea = Float.POSITIVE_INFINITY var bestArea = Float.POSITIVE_INFINITY
boundsByBridge.forEach { (bridge, bounds) -> boundsByKey.values.forEach { (bridge, bounds) ->
if (!bounds.contains(windowPoint)) return@forEach if (!bounds.contains(windowPoint)) return@forEach
val area = bounds.width * bounds.height val area = bounds.width * bounds.height
if (area > 0f && area < bestArea) { if (area > 0f && area < bestArea) {
@@ -169,7 +186,7 @@ fun rememberAttachmentDropBridge(): AttachmentDropBridge = remember { Attachment
/** Whether [bridge] currently owns the exclusive drop highlight. */ /** Whether [bridge] currently owns the exclusive drop highlight. */
@Composable @Composable
fun AttachmentDropBridge.isDropHighlightActive(): Boolean = fun AttachmentDropBridge.isDropHighlightActive(): Boolean =
AttachmentDropHighlight.activeBridge === this AttachmentDropHighlight.isHighlightActive(this)
fun urisToSelectedAttachments( fun urisToSelectedAttachments(
uris: List<String>, uris: List<String>,
@@ -223,8 +240,9 @@ fun Modifier.chatAttachmentDropTarget(
): Modifier = composed { ): Modifier = composed {
if (!enabled) return@composed Modifier if (!enabled) return@composed Modifier
val permissionsHost = rememberAttachmentDropPermissionsHost() val permissionsHost = rememberAttachmentDropPermissionsHost()
DisposableEffect(bridge) { val registrationKey = remember { Any() }
onDispose { AttachmentDropHighlight.unregister(bridge) } DisposableEffect(registrationKey) {
onDispose { AttachmentDropHighlight.unregister(registrationKey) }
} }
val target = remember(bridge, permissionsHost) { val target = remember(bridge, permissionsHost) {
object : DragAndDropTarget { object : DragAndDropTarget {
@@ -260,7 +278,7 @@ fun Modifier.chatAttachmentDropTarget(
} }
} }
onGloballyPositioned { coords -> onGloballyPositioned { coords ->
AttachmentDropHighlight.register(bridge, coords.boundsInRoot()) AttachmentDropHighlight.register(registrationKey, bridge, coords.boundsInRoot())
}.dragAndDropTarget( }.dragAndDropTarget(
shouldStartDragAndDrop = { acceptsAttachmentDrop(it) }, shouldStartDragAndDrop = { acceptsAttachmentDrop(it) },
target = target, target = target,