0

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"))
      }
    }
  }
}
richj
  • 7,499
  • 3
  • 32
  • 50
  • Can you add some simple code that illustrates your problem? – Jarek Sep 16 '13 at 23:13
  • It's a ScalaFX control that is based on this article: http://docs.oracle.com/javafx/2/drag_drop/jfxpub-drag_drop.htm for the basic mechanics of Drag and Drop. This article got me started on the Scala syntax for event handling: http://code.google.com/p/scalafx/source/browse/demo/scalafx/scene/input/DragEventTester.scala?r=15a4160e39a66b8c36b928904b24b7ceefd90b3d – richj Sep 17 '13 at 07:49
  • The code is currently over 300 lines so the two articles are easier to follow. The main thing that I want to understand is how the failed drop animation works. It isn't mentioned in the Oracle article and I haven't found it in the JavaFX code so far. – richj Sep 17 '13 at 07:53
  • Added a cut down code sample. – richj Sep 17 '13 at 08:44
  • I tested the sample code on Windows and do not see any animations. There was a problem with application crashing when item was dragged outside of `TreeView`, but was resolved by fix to [Issue 83](https://code.google.com/p/scalafx/issues/detail?id=83&can=7). – Jarek Sep 18 '13 at 03:34
  • I see this on OS/X 10.8.4. By "animation" I mean the document icon flying back from the drop point to the source location of the drag. If I drop in the green area of the test application I see the icon flying back, if I drop in the white area of the control below the list then I don't. I will see if I can try this on a Windows machine. – richj Sep 18 '13 at 08:14
  • On Windows 7 with javafx.runtime.version: 2.2.40-b43, java.runtime.version: 1.7.0_40-b43 and today's scalafx code built from source I see a "no drop" icon (circle with a diagonal line across it) when I drag an item outside of the TreeView. The behaviour is different (as described above) on OS/X with the same versions of Java, Scala and ScalaFX. – richj Sep 18 '13 at 14:16

0 Answers0