如何调整或偏离缺省的JavaFX传输模式行为

sdnqo3pr  于 2023-02-28  发布在  Java
关注(0)|答案(1)|浏览(114)

我已经在JavaFX中成功地实现了拖放,拖动源和目的地都很高兴支持复制和移动传输模式,所以我在两端都使用TransferMode.COPY_OR_MOVE
在这种情况下,我相信JavaFX正确地默认使用复制传输模式作为默认模式。我将这种正确性建立在UX question and answer的基础上,尽管似乎没有绝对的标准。如果按下SHIFT键,JavaFX将切换到移动传输模式。
尽管如此,我还是希望 * move * 是默认设置,而copy only可以通过按键修饰符来使用。我搜索了网页和代码,但是我找不到调整这个默认行为的方法。有人知道一个方法吗?我也尝试过使用单独的传输模式枚举来防止顺序问题,但是那也不起作用。
我还考虑过尝试捕获一个按键事件,然后根据可能按下的修饰符在目标位置简单地接受移动或复制。然而,我关心的是目标位置是否会有焦点,以及按键事件是否会在拖动过程中传播。我将尝试这种方法,但我决定问一下这里是否有人知道一种替代方案(或者甚至可能在Java9中有改变默认设置的能力)。
谢谢!

omjgkv6w

omjgkv6w1#

我非常需要这个东西。大量的本地应用程序已经这样做了:默认情况下,拖动是一个移动操作,按住某个修饰键,无论是在拖动之前还是在拖动过程中,都会将拖动操作更改为复制操作。到JavaFX 19为止,仍然没有办法在本地平台拖放操作进行时以编程方式修改其传输模式。(“本地平台拖放”是指以Node. startDragAndDrops开头的拖放操作。)
但是,您在评论中提到,您的拖放操作仅限于您的应用程序中,这意味着您可以放弃本机平台功能,使用Node.startFullDrag.启动的非本机JavaFX拖放操作,自己完成操作,只需稍微多做一些工作。
(Why JavaFX将本机平台拖放称为“拖放”,而将非本机的、JavaFX实现的拖放称为“完全拖放“,这超出了我的理解范围。我不得不多次阅读javadocs,才能在脑海中记住这些术语。)
本机平台拖放使用DragEvents,而JavaFX实现的拖放使用MouseDragEvents(这显然符合JavaFX中拖放术语的模糊性)。
最大的挑战是找到底层系统的拖放光标:

  • 这篇评论正确地指出了Mac电脑上系统光标(包括拖放光标)的位置。我不知道这一信息是否会长期适用。好消息是,这些光标看起来是普通的.png文件。坏消息是,它们的热点定义在binary plist文件中,这些文件与每个.png文件位于同一目录下。
  • JavaFX for Linux使用的是Gnome/GTK,所以使用Gnome游标是有意义的,它们在活动主题的cursors子目录下,作为Xcursor图像文件;对于下面的代码,我使用GIMP将它们转换为PNG文件(我找不到任何其他能够阅读Xcursor文件的工具)。完整的icon theme spec依赖于XDG base directory specgsettings get org.gnome.desktop.interface gtk-theme这样的命令来查找当前主题的名称。在Java中解析Xcursor图像并非不可能,但我发现它需要相当多的代码。
  • 一个.dll文件遵循Portable Executable。我发现用Java读这个文件相当困难,因为相对偏移的基础没有很好地解释(互联网上的其他人似乎比我更困惑),但我最终还是读到了。哦,每个光标的图像数据是一个BMP/DIB,它缺少14字节的头部;幸运的是,其易于合成。

无论如何,下面是一个使用JavaFX实现的拖放操作的示例,其中不涉及查找系统游标的所有工作:

import java.time.LocalTime;

import java.util.function.Predicate;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.control.Label;
import javafx.scene.layout.TilePane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.image.Image;

public class DragAndDropMoveByDefault
extends Application {
    private Label sourceLabel;
    private Label destLabel;

    private boolean dragging;

    private boolean copyModifierPressed;

    private boolean overPossibleDropTarget;

    private Cursor copyCursor;
    private Cursor moveCursor;
    private Cursor cannotDropCursor;

    private KeyCode copyModifierKey;

    @Override
    public void start(Stage stage) {
        boolean mac = System.getProperty("os.name").contains("Mac");
        copyModifierKey = (mac ? KeyCode.ALT : KeyCode.CONTROL);
        Predicate<MouseEvent> isCopyModifierDown =
            e -> (mac ? e.isAltDown() : e.isControlDown());

        copyCursor = new ImageCursor(new Image(
            getClass().getResource("dnd-copy.png").toString()));
        moveCursor = new ImageCursor(new Image(
            getClass().getResource("dnd-move.png").toString()));
        cannotDropCursor = new ImageCursor(new Image(
            getClass().getResource("dnd-none.png").toString()));

        sourceLabel = new Label(LocalTime.now().toString());
        destLabel = new Label(" ");

        sourceLabel.setStyle("-fx-padding: 100; -fx-border-style: solid;");
        destLabel.setStyle("-fx-padding: 100; -fx-border-style: solid;");

        sourceLabel.setMaxWidth(Double.MAX_VALUE);
        destLabel.setMaxWidth(Double.MAX_VALUE);

        sourceLabel.setOnDragDetected(e -> {
            dragging = true;
            overPossibleDropTarget = false;
            copyModifierPressed = isCopyModifierDown.test(e);
            sourceLabel.startFullDrag();
            updateCursor();
            e.consume();
        });
        destLabel.setOnMouseDragExited(e -> {
            overPossibleDropTarget = false;
            updateCursor();
            e.consume();
        });
        destLabel.setOnMouseDragEntered(e -> {
            copyModifierPressed = isCopyModifierDown.test(e);
            overPossibleDropTarget = true;
            updateCursor();
            e.consume();
        });
        destLabel.setOnMouseDragOver(e -> {
            copyModifierPressed = isCopyModifierDown.test(e);
            overPossibleDropTarget = true;
            updateCursor();
            e.consume();
        });
        destLabel.setOnMouseDragReleased(e -> {
            destLabel.setText(sourceLabel.getText());
            if (!copyModifierPressed) {
                sourceLabel.setText("");
            }
            dragging = false;
            updateCursor();
            e.consume();
        });

        TilePane pane = new TilePane(12, 0, sourceLabel, destLabel);
        pane.setPrefColumns(2);
        pane.setPadding(new Insets(6));

        Scene scene = new Scene(pane);

        scene.setOnKeyPressed(e -> {
            if (dragging && e.getCode() == copyModifierKey) {
                copyModifierPressed = true;
                updateCursor();
            }
        });
        scene.setOnKeyReleased(e -> {
            if (dragging && e.getCode() == copyModifierKey) {
                copyModifierPressed = false;
                updateCursor();
            }
        });

        stage.setScene(scene);
        stage.setTitle("Drag and Drop - Move by Default");
        stage.show();
    }

    private void updateCursor() {
        Cursor cursor;
        if (dragging) {
            if (overPossibleDropTarget) {
                cursor = (copyModifierPressed ? copyCursor : moveCursor);
            } else {
                cursor = cannotDropCursor;
            }
        } else {
            cursor = Cursor.DEFAULT;
        }

        sourceLabel.setCursor(cursor);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

相关问题