如何拖放同一选项卡的选项卡?

问题描述 投票:0回答:2

我已经看过很多关于如何将一个选项卡的选项卡拖动到另一个选项卡的内容,顺便说一句,我没有明白这一点,我想它在某种程度上可能有用,但最自然的应用应该是重新排序选项卡同样的 tabpane,似乎没有人在互联网上谈论的事情。

我想要几乎所有浏览器都实现的行为。

我尝试过一些方法,但它没有达到我想要的效果。主要问题来自于

Tab
不能与
setOnDragDetected
setOnDragOver
等方法绑定(出于某种原因,顺便说一下……),这导致了过于复杂的算法来弥补这个弱点图书馆。

所以这是我的代码,它没有做我想要的事情,因为(这只是一种解决方法)我只在放置后检测“放置目标”,从而使其毫无用处。是的,出于某种原因,在按住单击按钮的同时悬停对象不会导致

mouseover
事件...

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class DragAndDrop extends Application
{
    public void start(Stage primaryStage)
    {
        TabPane tabPane = new TabPane();

        final int[] target = {0};

        for(int i = 0 ; i < 2 ; i ++)
        {
            Tab tab = new Tab();
            tab.setClosable(false);

            int index[] = {i};

            tabPane.getTabs().add(tab);

            Label label = new Label("Tab " + (i + 1), new Rectangle(16, 16, Color.TRANSPARENT));
            tab.setGraphic(label);

            tab.getGraphic().setOnMouseEntered(event ->
            {
                target[0] = index[0];
                System.out.println("target : " + target[0]);
            });
        }

        tabPane.setOnDragDetected(event ->
        {
            Dragboard dragboard = tabPane.startDragAndDrop(TransferMode.ANY);

            ClipboardContent clipboardContent = new ClipboardContent();
            clipboardContent.putString(Integer.toString(tabPane.getSelectionModel().getSelectedIndex()));
            System.out.println("source : " + tabPane.getSelectionModel().getSelectedIndex());
            dragboard.setContent(clipboardContent);

            event.consume();
        });

        tabPane.setOnDragOver(event ->
        {
            if(event.getDragboard().hasString())
            {
                event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
            }

            event.consume();
        });

        tabPane.setOnDragDropped(event ->
        {
            Dragboard dragboard = event.getDragboard();
            boolean success = false;

            if(dragboard.hasString())
            {
                System.out.println("from " + dragboard.getString() + " to " + target[0]);

                success = true;
            }

            event.setDropCompleted(success);
            event.consume();
        });

        tabPane.setOnDragDone(event ->
        {
            if(event.getTransferMode() == TransferMode.MOVE)
            {
                // tabPane.setText("");
            }
            event.consume();
        });

        primaryStage.setScene(new Scene(tabPane, 300, 200));
        primaryStage.show();
    }

    public static void main(String[] args)
    {
        launch(args);
    }
}
java events javafx tabs drag-and-drop
2个回答
12
投票

我认为解决此问题的最简单方法是将拖动处理程序注册到与选项卡关联的图形,而不是选项卡窗格。这使得管理选项卡的索引等变得更容易,并且您不依赖于鼠标拖动操作和选择之间的相互作用(您真的不知道这些操作会以什么顺序发生)。这种方法的缺点是您必须以某种方式确保每个选项卡一个图形(您可以使用标签作为图形来显示文本)。

这是一个通用的“选项卡窗格拖动支持”类。无论如何,它并不意味着生产质量,但基本功能似乎有效。这里的想法是,您创建一个

DraggingTabPaneSupport
实例并使用它为一个或多个
TabPane
添加拖动支持。然后可以在任何这些窗格内或之间拖动选项卡。如果您希望只能在每个选项卡窗格中拖动,请为每个选项卡窗格创建一个
DraggingTabPaneSupport
实例。

import java.util.concurrent.atomic.AtomicLong;

import javafx.collections.ListChangeListener.Change;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

public class DraggingTabPaneSupport {

    private Tab currentDraggingTab ;

    private static final AtomicLong idGenerator = new AtomicLong();

    private final String draggingID = "DraggingTabPaneSupport-"+idGenerator.incrementAndGet() ;

    public void addSupport(TabPane tabPane) {
        tabPane.getTabs().forEach(this::addDragHandlers);
        tabPane.getTabs().addListener((Change<? extends Tab> c) -> {
            while (c.next()) {
                if (c.wasAdded()) {
                    c.getAddedSubList().forEach(this::addDragHandlers);
                }
                if (c.wasRemoved()) {
                    c.getRemoved().forEach(this::removeDragHandlers);
                }
            }
        });

        // if we drag onto a tab pane (but not onto the tab graphic), add the tab to the end of the list of tabs:
        tabPane.setOnDragOver(e -> {
            if (draggingID.equals(e.getDragboard().getString()) && 
                    currentDraggingTab != null &&
                    currentDraggingTab.getTabPane() != tabPane) {
                e.acceptTransferModes(TransferMode.MOVE);
            }
        });
        tabPane.setOnDragDropped(e -> {
            if (draggingID.equals(e.getDragboard().getString()) && 
                    currentDraggingTab != null &&
                    currentDraggingTab.getTabPane() != tabPane) {

                currentDraggingTab.getTabPane().getTabs().remove(currentDraggingTab);
                tabPane.getTabs().add(currentDraggingTab);
                currentDraggingTab.getTabPane().getSelectionModel().select(currentDraggingTab);
            }
        });
    }

    private void addDragHandlers(Tab tab) {

        // move text to label graphic:
        if (tab.getText() != null && ! tab.getText().isEmpty()) {
            Label label = new Label(tab.getText(), tab.getGraphic());
            tab.setText(null);
            tab.setGraphic(label);
        }

        Node graphic = tab.getGraphic();
        graphic.setOnDragDetected(e -> {
            Dragboard dragboard = graphic.startDragAndDrop(TransferMode.MOVE);
            ClipboardContent content = new ClipboardContent();
            // dragboard must have some content, but we need it to be a Tab, which isn't supported
            // So we store it in a local variable and just put arbitrary content in the dragbaord:
            content.putString(draggingID);
            dragboard.setContent(content);
            dragboard.setDragView(graphic.snapshot(null, null));
            currentDraggingTab = tab ;
        });
        graphic.setOnDragOver(e -> {
            if (draggingID.equals(e.getDragboard().getString()) && 
                    currentDraggingTab != null &&
                    currentDraggingTab.getGraphic() != graphic) {
                e.acceptTransferModes(TransferMode.MOVE);
            }
        });
        graphic.setOnDragDropped(e -> {
            if (draggingID.equals(e.getDragboard().getString()) && 
                    currentDraggingTab != null &&
                    currentDraggingTab.getGraphic() != graphic) {

                int index = tab.getTabPane().getTabs().indexOf(tab) ;
                currentDraggingTab.getTabPane().getTabs().remove(currentDraggingTab);
                tab.getTabPane().getTabs().add(index, currentDraggingTab);
                currentDraggingTab.getTabPane().getSelectionModel().select(currentDraggingTab);
            }
        });
        graphic.setOnDragDone(e -> currentDraggingTab = null);
    }

    private void removeDragHandlers(Tab tab) {
        tab.getGraphic().setOnDragDetected(null);
        tab.getGraphic().setOnDragOver(null);
        tab.getGraphic().setOnDragDropped(null);
        tab.getGraphic().setOnDragDone(null);
    }
}

这是一个使用它的快速示例。在此示例中,我创建了三个选项卡窗格。我为前两个窗格创建一个拖动支持实例,因此可以在这两个窗格之间拖动选项卡,并为第三个窗格创建第二个拖动支持实例,因此在第三个窗格中,选项卡可以通过拖动重新排序,但不能拖动到另外两个窗格。

import java.util.Random;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class DraggingTabPaneExample extends Application {

    private final Random rng = new Random();

    @Override
    public void start(Stage primaryStage) {
        TabPane[] panes = new TabPane[] {new TabPane(), new TabPane(), new TabPane() };
        VBox root = new VBox(10, panes);
        for (int i = 1 ; i <= 15; i++) {
            Tab tab = new Tab("Tab "+i);
            tab.setGraphic(new Rectangle(16, 16, randomColor()));
            Region region = new Region();
            region.setMinSize(100, 150);
            tab.setContent(region);
            panes[(i-1) % panes.length].getTabs().add(tab);
        }

        DraggingTabPaneSupport support1 = new DraggingTabPaneSupport();
        support1.addSupport(panes[0]);
        support1.addSupport(panes[1]);
        DraggingTabPaneSupport support2 = new DraggingTabPaneSupport();
        support2.addSupport(panes[2]);

        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Color randomColor() {
        return Color.rgb(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
    }

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

初始截图:

在窗格内拖动后:

您还可以在前两个窗格之间拖动(但不能在第三个窗格和任何其他窗格之间拖动):


0
投票

从 Java 10 开始:myTabPane.setTabDragPolicy(TabDragPolicy.REORDER);

© www.soinside.com 2019 - 2024. All rights reserved.