如何调整或偏离默认的 JavaFX 传输模式行为

rya*_*000 5 java javafx

我已经在 J​​avaFX 中成功实现了拖放。拖动源和目标都乐意支持复制和移动传输模式,因此我TransferMode.COPY_OR_MOVE在两端都使用。

在这种情况下,我相信 JavaFX正确地默认使用复制传输模式作为默认值。我的这种正确性基于用户体验问题和答案,尽管似乎很明显可能没有绝对的标准。如果按下 SHIFT 键,JavaFX 将切换到移动传输模式。

尽管如此,我更希望将移动设置为默认设置,并且仅通过按键修饰符进行复制。我在网络和代码中进行了搜索,但找不到调整此默认行为的方法。有谁知道办法吗?我还尝试使用单独的传输模式枚举以防顺序问题,但这也不起作用。

我进一步考虑尝试捕获关键事件,然后根据可能关闭的修改器在目的地简单地接受移动或复制。然而,我担心的是目的地是否会获得焦点以及关键事件是否会在拖动过程中传播。我将尝试这种方法,但决定也询问这里是否有人知道替代方案(或者即使 Java 9 中可能有更改默认值的能力)。

谢谢!

VGR*_*VGR 0

我非常需要这个东西。许多本机应用程序已经以这种方式运行:默认情况下,拖动是移动操作,并且无论在拖动之前还是拖动过程中按住某个修改键,都会将拖动更改为复制操作。从 JavaFX 19 开始,一旦进行\xe2\x80\x99s,仍然无法以编程方式修改本机平台拖放的传输模式。(通过 \xe2\x80\x9cnative 平台拖放\xe2\x80\x9d 我的意思是从Node.startDragAndDrop开始拖放。)

\n

但是,您在评论中提到您的拖放操作仅在您的应用程序内进行。这意味着您可以放弃本机平台功能,并使用由Node.startFullDrag启动的非本机 JavaFX 拖放功能自行完成,只需多做一点工作。

\n

(为什么 JavaFX 仅调用本机平台拖放 \xe2\x80\x9cdrag 和 drop\xe2\x80\x9d 并调用非本机、JavaFX 实现的拖放 \xe2\x80\x9cfull 拖放而 drop\xe2\x80\x9d 超出了我的能力范围。在我能够将术语直接记在脑子里之前,我必须多次阅读 javadocs。)

\n

虽然本机平台拖放使用DragEvents,但 JavaFX 实现的拖放使用MouseDragEvents。(这显然符合 JavaFX 中拖放操作时混淆术语的传统。)

\n

最大的挑战是找到底层系统\xe2\x80\x99s拖放光标:

\n
    \n
  • 此评论正确地指出了 Mac 上系统光标(包括拖放光标)的位置。我不知道这些信息是否长期有效。好消息是,光标看起来是普通的 .png 文件。坏消息是,它们的热点是在与每个 .png 文件位于同一目录中的二进制 plist文件中定义的。
  • \n
  • JavaFX for Linux 使用 Gnome/GTK,因此使用 Gnome 游标是有意义的。它们位于cursors活动主题的子目录中,作为Xcursor图像文件;对于下面的代码,我使用GIMP将它们转换为 PNG 文件。(我无法\xe2\x80\x99找到任何其他能够读取Xcursor文件的工具。)查找当前的Gnome主题有点复杂;完整的图标主题规范取决于XDG 基本目录规范gsettings get org.gnome.desktop.interface gtk-theme和类似查找当前主题\xe2\x80\x99s 名称的命令。在 Java 中解析 Xcursor 图像并非不可能,但我发现它需要大量代码。
  • \n
  • Windows 拖放光标隐藏在 ole32.dll 中。.dll 文件遵循可移植可执行格式。我发现使用Java读取该文件相当困难,因为相对偏移\xe2\x80\x99基数\xe2\x80\x99没有得到很好的解释(互联网的其余部分似乎比我更困惑),但我最终确实到达了那里。哦,每个cursor\xe2\x80\x99s图像数据都是一个BMP/DIB,缺少14字节头;幸运的是,这很容易合成。
  • \n
\n

无论如何,这里是一个使用 JavaFX 实现的拖放操作的示例,无需执行查找 system\xe2\x80\x99s 游标所需的所有工作:

\n
import java.time.LocalTime;\n\nimport java.util.function.Predicate;\n\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.stage.Stage;\nimport javafx.scene.Scene;\nimport javafx.scene.Cursor;\nimport javafx.scene.ImageCursor;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.TilePane;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.image.Image;\n\npublic class DragAndDropMoveByDefault\nextends Application {\n    private Label sourceLabel;\n    private Label destLabel;\n\n    private boolean dragging;\n\n    private boolean copyModifierPressed;\n\n    private boolean overPossibleDropTarget;\n\n    private Cursor copyCursor;\n    private Cursor moveCursor;\n    private Cursor cannotDropCursor;\n\n    private KeyCode copyModifierKey;\n\n    @Override\n    public void start(Stage stage) {\n        boolean mac = System.getProperty("os.name").contains("Mac");\n        copyModifierKey = (mac ? KeyCode.ALT : KeyCode.CONTROL);\n        Predicate<MouseEvent> isCopyModifierDown =\n            e -> (mac ? e.isAltDown() : e.isControlDown());\n\n        copyCursor = new ImageCursor(new Image(\n            getClass().getResource("dnd-copy.png").toString()));\n        moveCursor = new ImageCursor(new Image(\n            getClass().getResource("dnd-move.png").toString()));\n        cannotDropCursor = new ImageCursor(new Image(\n            getClass().getResource("dnd-none.png").toString()));\n\n        sourceLabel = new Label(LocalTime.now().toString());\n        destLabel = new Label(" ");\n\n        sourceLabel.setStyle("-fx-padding: 100; -fx-border-style: solid;");\n        destLabel.setStyle("-fx-padding: 100; -fx-border-style: solid;");\n\n        sourceLabel.setMaxWidth(Double.MAX_VALUE);\n        destLabel.setMaxWidth(Double.MAX_VALUE);\n\n        sourceLabel.setOnDragDetected(e -> {\n            dragging = true;\n            overPossibleDropTarget = false;\n            copyModifierPressed = isCopyModifierDown.test(e);\n            sourceLabel.startFullDrag();\n            updateCursor();\n            e.consume();\n        });\n        destLabel.setOnMouseDragExited(e -> {\n            overPossibleDropTarget = false;\n            updateCursor();\n            e.consume();\n        });\n        destLabel.setOnMouseDragEntered(e -> {\n            copyModifierPressed = isCopyModifierDown.test(e);\n            overPossibleDropTarget = true;\n            updateCursor();\n            e.consume();\n        });\n        destLabel.setOnMouseDragOver(e -> {\n            copyModifierPressed = isCopyModifierDown.test(e);\n            overPossibleDropTarget = true;\n            updateCursor();\n            e.consume();\n        });\n        destLabel.setOnMouseDragReleased(e -> {\n            destLabel.setText(sourceLabel.getText());\n            if (!copyModifierPressed) {\n                sourceLabel.setText("");\n            }\n            dragging = false;\n            updateCursor();\n            e.consume();\n        });\n\n        TilePane pane = new TilePane(12, 0, sourceLabel, destLabel);\n        pane.setPrefColumns(2);\n        pane.setPadding(new Insets(6));\n\n        Scene scene = new Scene(pane);\n\n        scene.setOnKeyPressed(e -> {\n            if (dragging && e.getCode() == copyModifierKey) {\n                copyModifierPressed = true;\n                updateCursor();\n            }\n        });\n        scene.setOnKeyReleased(e -> {\n            if (dragging && e.getCode() == copyModifierKey) {\n                copyModifierPressed = false;\n                updateCursor();\n            }\n        });\n\n        stage.setScene(scene);\n        stage.setTitle("Drag and Drop - Move by Default");\n        stage.show();\n    }\n\n    private void updateCursor() {\n        Cursor cursor;\n        if (dragging) {\n            if (overPossibleDropTarget) {\n                cursor = (copyModifierPressed ? copyCursor : moveCursor);\n            } else {\n                cursor = cannotDropCursor;\n            }\n        } else {\n            cursor = Cursor.DEFAULT;\n        }\n\n        sourceLabel.setCursor(cursor);\n    }\n\n    public static void main(String[] args) {\n        Application.launch(args);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n