Skip to content

Commit

Permalink
Report platform-independent drop-target events to ComposeScene and …
Browse files Browse the repository at this point in the history
…`OwnerDragAndDropManager` instead of the platform-specific drag-and-drop manager.
  • Loading branch information
m-sasha committed Aug 19, 2024
1 parent 96def38 commit 12f07d8
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 283 deletions.
20 changes: 11 additions & 9 deletions compose/ui/ui/api/desktop/ui.api
Original file line number Diff line number Diff line change
Expand Up @@ -3286,7 +3286,7 @@ public abstract interface class androidx/compose/ui/platform/PlatformContext {
public fun convertLocalToWindowPosition-MK-Hz9U (J)J
public fun convertScreenToLocalPosition-MK-Hz9U (J)J
public fun convertWindowToLocalPosition-MK-Hz9U (J)J
public fun createDragAndDropManager ()Landroidx/compose/ui/platform/PlatformDragAndDropManager;
public fun drag-12SF9DM (Landroidx/compose/ui/draganddrop/DragAndDropTransferData;JLkotlin/jvm/functions/Function1;)Z
public abstract fun getInputModeManager ()Landroidx/compose/ui/input/InputModeManager;
public fun getMeasureDrawLayerBounds ()Z
public fun getParentFocusManager ()Landroidx/compose/ui/focus/FocusManager;
Expand Down Expand Up @@ -3317,13 +3317,6 @@ public abstract interface class androidx/compose/ui/platform/PlatformContext$Sem
public abstract fun onSemanticsOwnerRemoved (Landroidx/compose/ui/semantics/SemanticsOwner;)V
}

public abstract interface class androidx/compose/ui/platform/PlatformDragAndDropManager {
public abstract fun drag-12SF9DM (Landroidx/compose/ui/draganddrop/DragAndDropTransferData;JLkotlin/jvm/functions/Function1;)Z
public abstract fun getModifier ()Landroidx/compose/ui/Modifier;
public abstract fun isInterestedNode (Landroidx/compose/ui/draganddrop/DragAndDropModifierNode;)Z
public abstract fun registerNodeInterest (Landroidx/compose/ui/draganddrop/DragAndDropModifierNode;)V
}

public final class androidx/compose/ui/platform/PlatformInsets {
public static final field $stable I
public static final field Companion Landroidx/compose/ui/platform/PlatformInsets$Companion;
Expand Down Expand Up @@ -3517,12 +3510,12 @@ public final class androidx/compose/ui/scene/CanvasLayersComposeScene_skikoKt {
}

public abstract interface class androidx/compose/ui/scene/ComposeScene {
public abstract fun activeDragAndDropManager ()Landroidx/compose/ui/platform/PlatformDragAndDropManager;
public abstract fun calculateContentSize-YbymL2g ()J
public abstract fun close ()V
public abstract fun createLayer (Landroidx/compose/ui/unit/Density;Landroidx/compose/ui/unit/LayoutDirection;ZLandroidx/compose/runtime/CompositionContext;)Landroidx/compose/ui/scene/ComposeSceneLayer;
public abstract fun getCompositionLocalContext ()Landroidx/compose/runtime/CompositionLocalContext;
public abstract fun getDensity ()Landroidx/compose/ui/unit/Density;
public abstract fun getDropTarget ()Landroidx/compose/ui/scene/ComposeSceneDropTarget;
public abstract fun getFocusManager ()Landroidx/compose/ui/scene/ComposeSceneFocusManager;
public abstract fun getLayoutDirection ()Landroidx/compose/ui/unit/LayoutDirection;
public abstract fun getSize-bOM6tXw ()Landroidx/compose/ui/unit/IntSize;
Expand Down Expand Up @@ -3552,6 +3545,15 @@ public final class androidx/compose/ui/scene/ComposeSceneContext$Companion {
public final fun getEmpty ()Landroidx/compose/ui/scene/ComposeSceneContext;
}

public final class androidx/compose/ui/scene/ComposeSceneDropTarget {
public static final field $stable I
public final fun onChanged (Landroidx/compose/ui/draganddrop/DragAndDropEvent;)V
public final fun onDrop (Landroidx/compose/ui/draganddrop/DragAndDropEvent;)Z
public final fun onEntered (Landroidx/compose/ui/draganddrop/DragAndDropEvent;)Z
public final fun onExited (Landroidx/compose/ui/draganddrop/DragAndDropEvent;)V
public final fun onMoved (Landroidx/compose/ui/draganddrop/DragAndDropEvent;)V
}

public final class androidx/compose/ui/scene/ComposeSceneFocusManager {
public static final field $stable I
public final fun getFocusRect ()Landroidx/compose/ui/geometry/Rect;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@

package androidx.compose.ui.platform

import androidx.collection.ArraySet
import androidx.compose.ui.Modifier
import androidx.compose.ui.draganddrop.AwtDragAndDropTransferable
import androidx.compose.ui.draganddrop.DragAndDropEvent
import androidx.compose.ui.draganddrop.DragAndDropManager
import androidx.compose.ui.draganddrop.DragAndDropModifierNode
import androidx.compose.ui.draganddrop.DragAndDropNode
import androidx.compose.ui.draganddrop.DragAndDropTransferAction
import androidx.compose.ui.draganddrop.DragAndDropTransferAction.Companion.Copy
import androidx.compose.ui.draganddrop.DragAndDropTransferAction.Companion.Link
Expand All @@ -35,8 +31,7 @@ import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.toAwtImage
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.scene.ComposeScene
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
Expand Down Expand Up @@ -82,32 +77,20 @@ internal fun DragAndDropTransferAction.Companion.fromAwtAction(
}

/**
* Implements [PlatformDragAndDropManager] via the AWT drag-and-drop system.
* A drag-and-drop implementation via the AWT drag-and-drop system.
*/
internal class AwtDragAndDropManager(
private val rootContainer: JComponent,
): PlatformDragAndDropManager {

private val rootDragAndDropNode = DragAndDropNode { null }

private var transferHandler: ComposeTransferHandler? = null

private val interestedNodes = ArraySet<DragAndDropModifierNode>()
private val getScene: () -> ComposeScene
) {
private val scene get() = getScene()

private val density: Density
get() = rootContainer.density

private val scale: Float
get() = density.density

override val modifier: Modifier
get() = Modifier
.then(DragAndDropModifier(rootDragAndDropNode))
.onPlaced {
transferHandler = (rootContainer.transferHandler as? ComposeTransferHandler)
?: error("No ${ComposeTransferHandler::class.simpleName} installed in the root container")
}

private fun Point.toOffset(): Offset {
val scale = this@AwtDragAndDropManager.scale
return Offset(
Expand All @@ -116,7 +99,7 @@ internal class AwtDragAndDropManager(
)
}

override fun drag(
fun drag(
transferData: DragAndDropTransferData,
decorationSize: Size,
drawDragDecoration: DrawScope.() -> Unit
Expand All @@ -127,7 +110,7 @@ internal class AwtDragAndDropManager(
val density = this@AwtDragAndDropManager.density
val layoutDirection = layoutDirectionFor(rootContainer)

transferHandler?.startOutgoingTransfer(
transferHandler.startOutgoingTransfer(
transferData = transferData,
dragImage = renderDragImage(
size = decorationSize,
Expand All @@ -141,14 +124,6 @@ internal class AwtDragAndDropManager(
return true
}

override fun registerNodeInterest(node: DragAndDropModifierNode) {
interestedNodes.add(node)
}

override fun isInterestedNode(node: DragAndDropModifierNode): Boolean {
return interestedNodes.contains(node)
}

/**
* Renders the image to represent the dragged object for AWT.
*/
Expand All @@ -173,45 +148,33 @@ internal class AwtDragAndDropManager(
}

/**
* Receives and processes events from the [ComposeDropTarget] installed in the root component.
* Receives and processes events from the [DropTarget] installed in the root component.
*/
val dropTargetListener = object : DropTargetListener {
private val dropTargetListener = object : DropTargetListener {
override fun dragEnter(dtde: DropTargetDragEvent) {
val event = DragAndDropEvent(dtde)

// There's no drag-start event in AWT, so start in dragEnter, and stop in dragExit
val accepted = rootDragAndDropNode.acceptDragAndDropTransfer(event)
interestedNodes.forEach { it.onStarted(event) }
rootDragAndDropNode.onEntered(event)
val accepted = scene.dropTarget.onEntered(DragAndDropEvent(dtde))
if (!accepted) {
dtde.rejectDrag()
}
}

override fun dragExit(dte: DropTargetEvent) {
val event = DragAndDropEvent(dte)
rootDragAndDropNode.onExited(event)
endDrag(event)
scene.dropTarget.onExited(DragAndDropEvent(dte))
}

override fun dragOver(dtde: DropTargetDragEvent) {
rootDragAndDropNode.onMoved(DragAndDropEvent(dtde))
scene.dropTarget.onMoved(DragAndDropEvent(dtde))
}

override fun dropActionChanged(dtde: DropTargetDragEvent) {
rootDragAndDropNode.onChanged(DragAndDropEvent(dtde))
scene.dropTarget.onChanged(DragAndDropEvent(dtde))
}

override fun drop(dtde: DropTargetDropEvent) {
val event = DragAndDropEvent(dtde)
val accepted = scene.dropTarget.onDrop(DragAndDropEvent(dtde))
dtde.acceptDrop(dtde.dropAction)
dtde.dropComplete(rootDragAndDropNode.onDrop(event))
endDrag(event)
}

private fun endDrag(event: DragAndDropEvent) {
rootDragAndDropNode.onEnded(event)
interestedNodes.clear()
dtde.dropComplete(accepted)
}

private fun DragAndDropEvent(dragEvent: DropTargetDragEvent) = DragAndDropEvent(
Expand All @@ -233,6 +196,22 @@ internal class AwtDragAndDropManager(
)
}

/**
* The [TransferHandler] installed as the root container's [JComponent.setTransferHandler] in
* order to implement drop-source functionality.
*/
val transferHandler = ComposeTransferHandler(rootContainer)

/**
* The AWT [DropTarget] installed as the root container's [JComponent.dropTarget] in order to
* implement drop-target functionality.
*/
val dropTarget = DropTarget(
rootContainer,
DnDConstants.ACTION_MOVE or DnDConstants.ACTION_COPY or DnDConstants.ACTION_LINK,
dropTargetListener,
true
)
}

/**
Expand Down Expand Up @@ -313,57 +292,6 @@ private class OutgoingTransfer(
val dragImageOffset: Point
)

/**
* The AWT [DropTarget] we install in the root container in order to implement drop-target
* functionality.
*/
internal class ComposeDropTarget(
rootContainer: JComponent,
private val dropTargetListener: () -> DropTargetListener?
) : DropTarget(
rootContainer,
DnDConstants.ACTION_MOVE or DnDConstants.ACTION_COPY or DnDConstants.ACTION_LINK,
null,
true
) {

override fun dragEnter(dtde: DropTargetDragEvent) {
dropTargetListener()?.dragEnter(dtde)
}

override fun dragOver(dtde: DropTargetDragEvent) {
dropTargetListener()?.dragOver(dtde)
}

override fun dropActionChanged(dtde: DropTargetDragEvent) {
dropTargetListener()?.dropActionChanged(dtde)
}

override fun dragExit(dte: DropTargetEvent) {
dropTargetListener()?.dragExit(dte)
}

override fun drop(dtde: DropTargetDropEvent) {
dropTargetListener()?.drop(dtde)
}
}

private class DragAndDropModifier(
val dragAndDropNode: DragAndDropNode
) : ModifierNodeElement<DragAndDropNode>() {
override fun create() = dragAndDropNode

override fun update(node: DragAndDropNode) = Unit

override fun InspectorInfo.inspectableProperties() {
name = "RootDragAndDropNode"
}

override fun hashCode(): Int = dragAndDropNode.hashCode()

override fun equals(other: Any?) = other === this
}

/**
* The AWT drag-and-drop seems to have some differences between the various OSes. This
* interface encapsulates the adaptations to these differences for each OS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ import androidx.compose.ui.awt.AwtEventListeners
import androidx.compose.ui.awt.OnlyValidPrimaryMouseButtonFilter
import androidx.compose.ui.awt.isFocusGainedHandledBySwingPanel
import androidx.compose.ui.awt.runOnEDTThread
import androidx.compose.ui.draganddrop.DragAndDropTransferData
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.asComposeCanvas
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.input.key.internal
import androidx.compose.ui.input.key.toComposeEvent
import androidx.compose.ui.input.pointer.AwtCursor
Expand All @@ -40,8 +43,6 @@ import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.PointerKeyboardModifiers
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.AwtDragAndDropManager
import androidx.compose.ui.platform.ComposeDropTarget
import androidx.compose.ui.platform.ComposeTransferHandler
import androidx.compose.ui.platform.DelegateRootForTestListener
import androidx.compose.ui.platform.DesktopTextInputService
import androidx.compose.ui.platform.EmptyViewConfiguration
Expand Down Expand Up @@ -331,6 +332,8 @@ internal class ComposeSceneMediator(
*/
private var keyboardModifiersRequireUpdate = false

private val dragAndDropManager = AwtDragAndDropManager(container, getScene = { scene })

init {
// Transparency is used during redrawer creation that triggered by [addNotify], so
// it must be set to correct value before adding to the hierarchy to handle cases
Expand All @@ -345,13 +348,8 @@ internal class ComposeSceneMediator(
container.addContainerListener(containerListener)

// AwtDragAndDropManager support
container.transferHandler = ComposeTransferHandler(container)
container.dropTarget = ComposeDropTarget(
rootContainer = container,
dropTargetListener = {
(scene.activeDragAndDropManager() as? AwtDragAndDropManager)?.dropTargetListener
}
)
container.transferHandler = dragAndDropManager.transferHandler
container.dropTarget = dragAndDropManager.dropTarget

// It will be enabled dynamically. See DesktopPlatformComponent
contentComponent.enableInputMethods(false)
Expand Down Expand Up @@ -697,12 +695,18 @@ internal class ComposeSceneMediator(
return true
}

override fun drag(
transferData: DragAndDropTransferData,
decorationSize: Size,
drawDragDecoration: DrawScope.() -> Unit
) = dragAndDropManager.drag(
transferData, decorationSize, drawDragDecoration
)

override val rootForTestListener
get() = this@ComposeSceneMediator.rootForTestListener
override val semanticsOwnerListener
get() = this@ComposeSceneMediator.semanticsOwnerListener

override fun createDragAndDropManager() = AwtDragAndDropManager(container)
}

private inner class DesktopPlatformComponent : PlatformComponent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.Autofill
import androidx.compose.ui.autofill.AutofillTree
import androidx.compose.ui.draganddrop.DragAndDropManager
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusOwner
import androidx.compose.ui.focus.FocusOwnerImpl
Expand Down Expand Up @@ -62,14 +61,13 @@ import androidx.compose.ui.platform.DefaultHapticFeedback
import androidx.compose.ui.platform.DelegatingSoftwareKeyboardController
import androidx.compose.ui.platform.PlatformClipboardManager
import androidx.compose.ui.platform.PlatformContext
import androidx.compose.ui.platform.PlatformDragAndDropManager
import androidx.compose.ui.platform.PlatformRootForTest
import androidx.compose.ui.platform.PlatformTextInputSessionScope
import androidx.compose.ui.platform.RenderNodeLayer
import androidx.compose.ui.platform.asDragAndDropManager
import androidx.compose.ui.scene.ComposeScene
import androidx.compose.ui.scene.ComposeSceneInputHandler
import androidx.compose.ui.scene.ComposeScenePointer
import androidx.compose.ui.scene.OwnerDragAndDropManager
import androidx.compose.ui.semantics.EmptySemanticsElement
import androidx.compose.ui.semantics.EmptySemanticsModifier
import androidx.compose.ui.semantics.SemanticsOwner
Expand Down Expand Up @@ -124,7 +122,9 @@ internal class RootNodeOwner(
platformContext.parentFocusManager.clearFocus(true)
},
)
val dragAndDropManager: PlatformDragAndDropManager = platformContext.createDragAndDropManager()

val dragAndDropManager = OwnerDragAndDropManager(platformContext)

private val rootSemanticsNode = EmptySemanticsModifier()

private val rootModifier = EmptySemanticsElement(rootSemanticsNode)
Expand Down Expand Up @@ -330,8 +330,7 @@ internal class RootNodeOwner(
): Nothing {
awaitCancellation()
}
override val dragAndDropManager: DragAndDropManager =
this@RootNodeOwner.dragAndDropManager.asDragAndDropManager()
override val dragAndDropManager = this@RootNodeOwner.dragAndDropManager
override val pointerIconService = PointerIconServiceImpl()
override val focusOwner get() = this@RootNodeOwner.focusOwner
override val windowInfo get() = platformContext.windowInfo
Expand Down
Loading

0 comments on commit 12f07d8

Please sign in to comment.