Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Will support drag and drop file into widget? #222

Closed
gtf35 opened this issue Dec 27, 2020 · 31 comments · Fixed by JetBrains/compose-multiplatform-core#391
Closed

Will support drag and drop file into widget? #222

gtf35 opened this issue Dec 27, 2020 · 31 comments · Fixed by JetBrains/compose-multiplatform-core#391
Assignees
Labels

Comments

@gtf35
Copy link

gtf35 commented Dec 27, 2020

Will compose support drag and drop file into widget?
Sometimes useful,for example, drag and drop an apk to the window to install it to phone.😉

@Dominaezzz
Copy link
Contributor

#176

@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

Thanks,but it will open a dialog.
I mean we can drag and drop files directly from the Explorer to the window。🤔

@Dominaezzz
Copy link
Contributor

Oh sorry. Compose itself won't support that. You can register for drap and drop events with DropTargetListener on AppWindowAmbient.current.

@Dominaezzz
Copy link
Contributor

Dominaezzz commented Dec 27, 2020

Here's a more concrete example https://twitter.com/tkuenneth/status/1331993022955917312?s=20 .

EDIT: https://dev.to/tkuenneth/from-swing-to-jetpack-compose-desktop-2-4a4h

@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

🤔 It is based on the window.
If I want to trigger in a specific area, I guess, this way could not be possible

By the way, thanks again

@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

This link looks like what I want, thank you so much

@gtf35 gtf35 closed this as completed Dec 27, 2020
@Dominaezzz
Copy link
Contributor

There isn't an immediately obvious way to limit it to a specific area. I think it's worth leaving this issue open.

@Dominaezzz
Copy link
Contributor

Maybe you can limit it with the new SwingPanel.

@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

Yeah, you are right.
I only watch the video, he drop it in the widget, actually he doesnt limit it.

@gtf35 gtf35 reopened this Dec 27, 2020
@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

I thought about that idea, but in the latest「0.3.0-build136」
image

@Dominaezzz
Copy link
Contributor

I think you need to use this version 0.0.0-unmerged-build21.

@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

Yeah, I see this version in issues, but I dont known how to find unmerged version.😉

by the way, this version ...
But this is another issues
image

@Dominaezzz
Copy link
Contributor

You have to update the version in build.gradle

@gtf35
Copy link
Author

gtf35 commented Dec 27, 2020

Yes, you are right🙌
Thanks a looooot

@olonho
Copy link
Contributor

olonho commented Jan 13, 2021

Guess we could close it, once SwingPanel support merged.

@gtf35
Copy link
Author

gtf35 commented Jan 13, 2021

But wouldn’t it be better to let compose-jb support it?

@illuminator3
Copy link

bump still no support for file drag and drop on a single element instead of the entire window?

@akurasov akurasov added desktop enhancement New feature or request labels Jul 16, 2021
@felixdivo
Copy link
Contributor

You can also embed a androidx.compose.ui.awt.SwingPanel an have it accept drops, but that's also inconvenient to work with (and changes the style).

@gtf35
Copy link
Author

gtf35 commented Aug 16, 2021

@felixdivo Yeah, compatibility with Swing is not compose's own behavior

@felixdivo
Copy link
Contributor

You can also embed a androidx.compose.ui.awt.SwingPanel an have it accept drops

That also does not work nicely due to #1062 and (very importantly!) #1202. I thought about putting a compose UI in the drop traget by nesting a ComposePanel in a SwingPanel, but that also does not work due to #1060 and #1061.

@igordmn igordmn removed the interop label Nov 24, 2021
@tunjid
Copy link

tunjid commented Mar 18, 2022

You can do this with a custom modifier. Here's source for one here

Here's a desktop implementation.

And an Android implementation.

Here's a gif of both side by side

DnD

@hakanai
Copy link

hakanai commented Aug 27, 2022

I have a number of use cases for this:

  • Drag selected text from text views out into other apps
  • Drag text from other apps into text views
  • Drag images from other apps into text views
  • Drag images from other apps into specific drop areas (similar to what previous example showed)
  • Drag files from other apps (including file explorers) into specific drop areas
  • Drag things between two slots in the same app (probably possible without using real dnd)
  • Drag nodes from my app out into files

Walingar added a commit to Walingar/androidx that referenced this issue Jan 31, 2023
Walingar added a commit to Walingar/androidx that referenced this issue Mar 2, 2023
Walingar added a commit to Walingar/androidx that referenced this issue Mar 2, 2023
@Walingar
Copy link
Collaborator

Hello everyone, thank you for your request and use cases! Experimental drag and drop functionality for desktop has been merged and will be available in the next dev builds. It allows moving files/images/text inside your Compose desktop application out of the box!

@Walingar Walingar assigned Walingar and unassigned Rsedaikin Mar 13, 2023
@mgroth0
Copy link

mgroth0 commented Apr 29, 2023

@Walingar this is great news! Is it available in 1.4.0?

@hakanai
Copy link

hakanai commented Apr 30, 2023

Sample code for a text field receiving dropped text replacing the selection, working in desktop on v1.4.0.

Still missing the equivalent behaviour in the other direction.

var isDroppable by remember { mutableStateOf(false) }
val (textFieldValue, setTextFieldValue) = remember { mutableStateOf(TextFieldValue()) }

TextField(
    value = textFieldValue,
    onValueChange = setTextFieldValue,
    modifier = Modifier
        .border(4.dp, if (isDroppable) Color.Green else Color.Red)
        .onExternalDrag(
            onDragStart = { externalDragValue ->
                isDroppable = externalDragValue.dragData is androidx.compose.ui.DragData.Text
            },
        onDragExit = {
            isDroppable = false
        },
        onDrop = { externalDragValue ->
            isDroppable = false
            val dragData = externalDragValue.dragData
            if (dragData is androidx.compose.ui.DragData.Text) {
                setTextFieldValue(textFieldValue.copy(
                    text = textFieldValue.text.substring(0, textFieldValue.selection.start) +
                        dragData.readText() +
                        textFieldValue.text.substring(textFieldValue.selection.end),
                    selection = TextRange(textFieldValue.selection.end)
                ))
            }
        },
    )
)

@mgroth0
Copy link

mgroth0 commented Apr 30, 2023

It works perfectly! Thanks @hakanai

@Walingar
Copy link
Collaborator

Walingar commented May 1, 2023

@hakanai thank you for your example!
Indeed, it is available in 1.4.0 as an experimental feature. So, please try it out and provide us feedback!

Also, @hakanai can you please describe your use case for DnD from Compose app. I will be glad if you can create a separate issue for that and describe it there!

@June6-android
Copy link

What if I want to drop an image and send it somewhere as a file? How can I transform a Painter object to a file object?

`.onExternalDrag(
onDragStart = {
isDroppable = true
},
onDragExit = {
isDroppable = false
},
onDrop = { externalDragValue ->
isDroppable = false
val dragData = externalDragValue.dragData
if (dragData is androidx.compose.ui.DragData.Image) {
val img = dragData.readImage()

        }
    }
)`

@dima-avdeev-jb
Copy link
Contributor

@June6-android You can try this code:

fun savePainterToFile(painter: Painter, file: File) {
    val image: Image = painter.toAwtImage(Density(1f), LayoutDirection.Ltr)
    val bufferedImage = BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB)
    bufferedImage.graphics.drawImage(image, 0, 0, null);
    ImageIO.write(bufferedImage, "png", file)
}

@SinaMegapolis
Copy link

Currently im trying to disable/enable drag and drop for a widget using this modifier, but on runtime the component never gets recomposed to check for the drag data type
Is there any better way to do this?
kotlin var isDroppable by remember { mutableStateOf(false) } val dragModifier: Modifier = Modifier.onExternalDrag ( enabled = isDroppable, onDragStart = { val data = it.dragData isDroppable = data is DragData.FilesList } )

@okushnikov
Copy link

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.