In ScalaFX if I drop a TreeCell onto a control other than the containing TreeView then a short animation is played showing the dragged item returning to its original position.
The same happens if I drop the TreeCell outside of the application - e.g. onto the desktop.
If I drop the TreeCell onto the containing TreeView in a location that shouldn't be accepted I want the same animation to play, but setting:
event.dropCompleted = false
isn't enough to produce this effect.
Is there a way to play the drop failed animation from inside the TreeCell's onDragDropped event handler?
Edit
Added code sample as requested. Drag an entry and drop it outside the control and you will see the drop failed animation. Drag an entry and drop it below the list and the animation doesn't play. The question is how to make the animation play in the second case.
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.paint.Color
import scalafx.scene.layout.Pane
import scalafx.Includes._
import scalafx.scene.input.{TransferMode, DataFormat, DragEvent, MouseEvent}
import scalafx.scene.control._
import javafx.scene.control.{TreeItem => jfxTreeItem}
import java.nio.ByteBuffer
import java.util
import scalafx.beans.value.ObservableValue
class StringRootItem(text: String) extends TreeItem[String](text) {
override def toString() = text
}
class StringTreeItem(text: String) extends TreeItem[String](text) {
override def toString() = text
}
object DragAndDropControl {
def apply(rootNodeName: String, entries: List[String]): DragAndDropControl = {
val rootItem = new StringRootItem(rootNodeName)
rootItem.setExpanded(true)
val children = rootItem.getChildren
entries.foreach(entry => {
children.add(new StringTreeItem(entry))
})
new DragAndDropControl(rootItem)
}
}
class DragAndDropControl(rootItem: StringRootItem) extends TreeView[String](rootItem) {
showRoot = false
cellFactory = (tree: TreeView[String]) => {
new StringDragAndDropCell(tree)
}
object StringDragAndDropCell {
val customFormat: DataFormat = new DataFormat("string.entry.custom")
var transferItem: TreeItem[String] = null
def toBuffer(entry: String): ByteBuffer = ByteBuffer.allocate(10)
def fromBuffer(buffer: ByteBuffer): String = null
}
class StringDragAndDropCell(tree: TreeView[String]) extends TreeCell[String] {
import StringDragAndDropCell._
private var stringEntry: String = null
onDragDetected = (event: MouseEvent) => {
val db = startDragAndDrop(TransferMode.MOVE)
import scala.collection.JavaConversions._
if ((treeItem ne null) && (treeItem.value ne null) && (treeItem.value.getValue ne null)) {
transferItem = treeItem.value
val content: Map[javafx.scene.input.DataFormat, AnyRef] = Map(customFormat.delegate -> toBuffer(transferItem.value()))
val contentMap: util.Map[javafx.scene.input.DataFormat, AnyRef] = mapAsJavaMap(content)
db.setContent(contentMap)
}
event.consume()
}
onDragOver = (event: DragEvent) => {
val db = event.getDragboard
if (db.hasContent(customFormat))
event.acceptTransferModes(TransferMode.MOVE)
event.consume()
}
onDragDone = (event: DragEvent) => {
if (event.transferMode == TransferMode.MOVE)
event.dragboard.clear()
event.consume()
}
onDragDropped = (event: DragEvent) => {
val db = event.getDragboard
var success = false
if (db.hasContent(customFormat))
success = stringEntry ne null
if (success)
if (event.gestureSource != event.delegate.getGestureTarget) {
treeItem().getParent.getChildren.removeAll(transferItem)
treeItem().getParent.getChildren.add(index(), transferItem)
}
event.dropCompleted = success
event.consume()
}
/*
* Change handler for the treeItem property.
*
* The treeItem property is set by JavaFX after construction and at any time when the cell is recycled for UI virtualization.
*
* The text property must be updated when the treeItem is set in order for the cell to render correctly (without this the cell will appear blank.)
*/
super.treeItem.onChange((observable: ObservableValue[jfxTreeItem[String], jfxTreeItem[String]], oldValue: jfxTreeItem[String], newValue: jfxTreeItem[String]) => {
if (newValue ne null) {
stringEntry = newValue.getValue
text = stringEntry
}
else
stringEntry = null
})
override def toString(): String = {
if (stringEntry == null)
null
else
s"ScalaFX string tree cell ${stringEntry.toString}"
}
}
}
object TestDragAndDrop extends JFXApp
{
println("javafx.runtime.version: " + System.getProperties.get("javafx.runtime.version"))
println("java.runtime.version: " + System.getProperties.get("java.runtime.version"))
stage = new JFXApp.PrimaryStage {
title = "Test Drag and Drop"
width = 600
height = 472
scene = new Scene {
fill = Color.LIGHTGREEN
root = new Pane {
content = DragAndDropControl("Numbers", List("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"))
}
}
}
}