JavaFX Tab positioning on mouse drag/drop

I have a Tabpane with multiple tabs. I want to re-position tabs by just dragging them at a particular position(just like the way we are able to arrange tabs in browser.) Is there any way i can achieve it?


We achieved it in a slightly different way.Instead of drag/drop feature we provided the move left/move right functionality on tab context menu which in turns moves the tab. We wanted to have this feature on priority so implemented it with this workaround for now.

Code snippet for MoveRight:
public void moveRight() {
    protected TabPane workBook;
    int cTabIndex = bem.workBook.getTabs().indexOf(bem.activeSheet);
    int tabCount = workBook.getTabs().size();

    if (tabCount > 1 && cTabIndex > 0) {
        workBook.getTabs().remove(bem.activeSheet);
        workBook.getTabs().add(cTabIndex - 1, bem.activeSheet);
    }
}

How to drag and drop tabs of the SAME tabpane?, tabs, etc, and you are not relying on the interplay between mouse drag actions If you want to be able to only drag within each tab pane, create a Label; import javafx.scene.control. TabPane; import javafx.scene.layout. Drag and Drop for tab headers is not implemented in the base JavaFX 2.2 platform. Until that is implemented in the standard JDK, you will need to implement the feature yourself using JavaFX's Drag and Drop functionality.


I've implemented a class that handles both draggable and detachable tabs - more details here. The implementation is not the tidiest, nor the most resilient but works pretty well for me in the simple cases I've tried so far. I've deliberately kept everything in the one class to make it easier for others to copy / use / modify as they see fit.

The basic concept that I'm using (arguably mis-using) is that the graphic you can set on a tab can be any node, not just an ImageView (or similar.) So instead of using the setText() on Tab directly, I'm not adding any text at all, just setting the graphic to be a Label containing the desired text. Now that the label is present in the tab header (and is pretty much the tab header spacially), that makes it much easier (and skin-independant) to grab the global co-ordinates of each tab header in the pane. From then it's just a case of some relatively simple positioning logic to work out when to detach tabs into a new window, when to re-add them and when to reorder them.

Of course, this isn't an ideal solution but unfortunately I haven't seen much else on the subject!

import java.util.HashSet;
import java.util.Set;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;

/**
 * A draggable tab that can optionally be detached from its tab pane and shown
 * in a separate window. This can be added to any normal TabPane, however a
 * TabPane with draggable tabs must *only* have DraggableTabs, normal tabs and
 * DrragableTabs mixed will cause issues!
 * <p>
 * @author Michael Berry
 */
public class DraggableTab extends Tab {

    private static final Set<TabPane> tabPanes = new HashSet<>();
    private Label nameLabel;
    private Text dragText;
    private static final Stage markerStage;
    private Stage dragStage;
    private boolean detachable;

    static {
        markerStage = new Stage();
        markerStage.initStyle(StageStyle.UNDECORATED);
        Rectangle dummy = new Rectangle(3, 10, Color.web("#555555"));
        StackPane markerStack = new StackPane();
        markerStack.getChildren().add(dummy);
        markerStage.setScene(new Scene(markerStack));
    }

    /**
     * Create a new draggable tab. This can be added to any normal TabPane,
     * however a TabPane with draggable tabs must *only* have DraggableTabs,
     * normal tabs and DrragableTabs mixed will cause issues!
     * <p>
     * @param text the text to appear on the tag label.
     */
    public DraggableTab(String text) {
        nameLabel = new Label(text);
        setGraphic(nameLabel);
        detachable = true;
        dragStage = new Stage();
        dragStage.initStyle(StageStyle.UNDECORATED);
        StackPane dragStagePane = new StackPane();
        dragStagePane.setStyle("-fx-background-color:#DDDDDD;");
        dragText = new Text(text);
        StackPane.setAlignment(dragText, Pos.CENTER);
        dragStagePane.getChildren().add(dragText);
        dragStage.setScene(new Scene(dragStagePane));
        nameLabel.setOnMouseDragged(new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent t) {
                dragStage.setWidth(nameLabel.getWidth() + 10);
                dragStage.setHeight(nameLabel.getHeight() + 10);
                dragStage.setX(t.getScreenX());
                dragStage.setY(t.getScreenY());
                dragStage.show();
                Point2D screenPoint = new Point2D(t.getScreenX(), t.getScreenY());
                tabPanes.add(getTabPane());
                InsertData data = getInsertData(screenPoint);
                if(data == null || data.getInsertPane().getTabs().isEmpty()) {
                    markerStage.hide();
                }
                else {
                    int index = data.getIndex();
                    boolean end = false;
                    if(index == data.getInsertPane().getTabs().size()) {
                        end = true;
                        index--;
                    }
                    Rectangle2D rect = getAbsoluteRect(data.getInsertPane().getTabs().get(index));
                    if(end) {
                        markerStage.setX(rect.getMaxX() + 13);
                    }
                    else {
                        markerStage.setX(rect.getMinX());
                    }
                    markerStage.setY(rect.getMaxY() + 10);
                    markerStage.show();
                }
            }
        });
        nameLabel.setOnMouseReleased(new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent t) {
                markerStage.hide();
                dragStage.hide();
                if(!t.isStillSincePress()) {
                    Point2D screenPoint = new Point2D(t.getScreenX(), t.getScreenY());
                    TabPane oldTabPane = getTabPane();
                    int oldIndex = oldTabPane.getTabs().indexOf(DraggableTab.this);
                    tabPanes.add(oldTabPane);
                    InsertData insertData = getInsertData(screenPoint);
                    if(insertData != null) {
                        int addIndex = insertData.getIndex();
                        if(oldTabPane == insertData.getInsertPane() && oldTabPane.getTabs().size() == 1) {
                            return;
                        }
                        oldTabPane.getTabs().remove(DraggableTab.this);
                        if(oldIndex < addIndex && oldTabPane == insertData.getInsertPane()) {
                            addIndex--;
                        }
                        if(addIndex > insertData.getInsertPane().getTabs().size()) {
                            addIndex = insertData.getInsertPane().getTabs().size();
                        }
                        insertData.getInsertPane().getTabs().add(addIndex, DraggableTab.this);
                        insertData.getInsertPane().selectionModelProperty().get().select(addIndex);
                        return;
                    }
                    if(!detachable) {
                        return;
                    }
                    final Stage newStage = new Stage();
                    final TabPane pane = new TabPane();
                    tabPanes.add(pane);
                    newStage.setOnHiding(new EventHandler<WindowEvent>() {

                        @Override
                        public void handle(WindowEvent t) {
                            tabPanes.remove(pane);
                        }
                    });
                    getTabPane().getTabs().remove(DraggableTab.this);
                    pane.getTabs().add(DraggableTab.this);
                    pane.getTabs().addListener(new ListChangeListener<Tab>() {

                        @Override
                        public void onChanged(ListChangeListener.Change<? extends Tab> change) {
                            if(pane.getTabs().isEmpty()) {
                                newStage.hide();
                            }
                        }
                    });
                    newStage.setScene(new Scene(pane));
                    newStage.initStyle(StageStyle.UTILITY);
                    newStage.setX(t.getScreenX());
                    newStage.setY(t.getScreenY());
                    newStage.show();
                    pane.requestLayout();
                    pane.requestFocus();
                }
            }

        });
    }

    /**
     * Set whether it's possible to detach the tab from its pane and move it to
     * another pane or another window. Defaults to true.
     * <p>
     * @param detachable true if the tab should be detachable, false otherwise.
     */
    public void setDetachable(boolean detachable) {
        this.detachable = detachable;
    }

    /**
     * Set the label text on this draggable tab. This must be used instead of
     * setText() to set the label, otherwise weird side effects will result!
     * <p>
     * @param text the label text for this tab.
     */
    public void setLabelText(String text) {
        nameLabel.setText(text);
        dragText.setText(text);
    }

    private InsertData getInsertData(Point2D screenPoint) {
        for(TabPane tabPane : tabPanes) {
            Rectangle2D tabAbsolute = getAbsoluteRect(tabPane);
            if(tabAbsolute.contains(screenPoint)) {
                int tabInsertIndex = 0;
                if(!tabPane.getTabs().isEmpty()) {
                    Rectangle2D firstTabRect = getAbsoluteRect(tabPane.getTabs().get(0));
                    if(firstTabRect.getMaxY()+60 < screenPoint.getY() || firstTabRect.getMinY() > screenPoint.getY()) {
                        return null;
                    }
                    Rectangle2D lastTabRect = getAbsoluteRect(tabPane.getTabs().get(tabPane.getTabs().size() - 1));
                    if(screenPoint.getX() < (firstTabRect.getMinX() + firstTabRect.getWidth() / 2)) {
                        tabInsertIndex = 0;
                    }
                    else if(screenPoint.getX() > (lastTabRect.getMaxX() - lastTabRect.getWidth() / 2)) {
                        tabInsertIndex = tabPane.getTabs().size();
                    }
                    else {
                        for(int i = 0; i < tabPane.getTabs().size() - 1; i++) {
                            Tab leftTab = tabPane.getTabs().get(i);
                            Tab rightTab = tabPane.getTabs().get(i + 1);
                            if(leftTab instanceof DraggableTab && rightTab instanceof DraggableTab) {
                                Rectangle2D leftTabRect = getAbsoluteRect(leftTab);
                                Rectangle2D rightTabRect = getAbsoluteRect(rightTab);
                                if(betweenX(leftTabRect, rightTabRect, screenPoint.getX())) {
                                    tabInsertIndex = i + 1;
                                    break;
                                }
                            }
                        }
                    }
                }
                return new InsertData(tabInsertIndex, tabPane);
            }
        }
        return null;
    }

    private Rectangle2D getAbsoluteRect(Control node) {
        return new Rectangle2D(node.localToScene(node.getLayoutBounds().getMinX(), node.getLayoutBounds().getMinY()).getX() + node.getScene().getWindow().getX(),
                node.localToScene(node.getLayoutBounds().getMinX(), node.getLayoutBounds().getMinY()).getY() + node.getScene().getWindow().getY(),
                node.getWidth(),
                node.getHeight());
    }

    private Rectangle2D getAbsoluteRect(Tab tab) {
        Control node = ((DraggableTab) tab).getLabel();
        return getAbsoluteRect(node);
    }

    private Label getLabel() {
        return nameLabel;
    }

    private boolean betweenX(Rectangle2D r1, Rectangle2D r2, double xPoint) {
        double lowerBound = r1.getMinX() + r1.getWidth() / 2;
        double upperBound = r2.getMaxX() - r2.getWidth() / 2;
        return xPoint >= lowerBound && xPoint <= upperBound;
    }

    private static class InsertData {

        private final int index;
        private final TabPane insertPane;

        public InsertData(int index, TabPane insertPane) {
            this.index = index;
            this.insertPane = insertPane;
        }

        public int getIndex() {
            return index;
        }

        public TabPane getInsertPane() {
            return insertPane;
        }

    }
}

JavaFX Tab positioning on mouse drag/drop - javafx-2 - html, I have a Tabpane with multiple tabs. I want to re-position tabs by just dragging them at a particular position(just like the way we are able to arrange tabs in  Stack Overflow Public questions and answers; Teams Private questions and answers for your team; Enterprise Private self-hosted questions and answers for your enterprise; Talent Hire technical talent


How to drag and drop tab nodes between tab panes, Based on the tutorial I want to drag tabs between two tabs. So far I Ref javafx 2 - How to drag and drop tab nodes between tab panes - Stack Overflow. 2495 Views; Tags: StackPane;; import javafx.scene.layout.VBox Thank you for the reply but I don't get any result when I drag the tab with the mouse. I need additional information how to implement tab drag and drop into TabPane in order to change tab order. I found this simple implementation JavaFX Tab positioning on mouse drag/drop - Stack Overflow But I want to implement advanced drag and drop with the mouse.


I just found out that this has been implemented in JavaFX 10.

tabPane.tabDragPolicy = TabPane.TabDragPolicy.REORDER

...does the trick.

A DraggablePanelsExample.java (Release 8), package draggablepanelsexample; import javafx.application. Label; import javafx.scene.control. setTitle("Draggable Panels Example"); stage.show(); } public static void main(final String[] args) if (dragModeActiveProperty.get()) { // remember initial mouse cursor coordinates // and node position dragContext. Moreover, drag-and-drop can be implemented between a JavaFX application and a third-party (native) application such as Windows Explorer or a desktop. A drag-and-drop gesture happens as follows: The user click a mouse button on a gesture source, drags the mouse, and releases the mouse button on a gesture target.


A very descriptive answer can be found where you can create custom tabs for the same:

http://0divides0.wordpress.com/2010/10/21/movable-tabbed-panes-in-javafx/

A JavaFX cooked solution is hard to find as dev blog for the same states that such functionality is not present for Tabs and they plan to incorporate later.

http://grokbase.com/p/openjdk/openjfx-dev/123fq9k310/draggable-tabs

TabPane (JavaFX 12), Tabs in a TabPane can be positioned at any of the four sides by specifying the Side . A TabPane has This enum specifies drag policies for tabs in a TabPane. Part II Drag-and-Drop Feature in JavaFX Applications. 7 Drag-and-Drop Operation. Objects and Data Types for Drag-and-Drop Operation. Transfer Modes; Implementing a Basic Drag-and-Drop Gesture. Starting the Drag-and-Drop Gesture on a Source; Handling a DRAG_OVER Event on a Target; Providing Visual Feedback by a Gesture Target; Handling a DRAG_DROPPED Event on a Target; Handling a DRAG_DONE Event on a Source


Draggable Node 01 (make JavaFX nodes draggable) · GitHub, // we use a default pane without layout such as HBox, VBox etc. final Pane root = new Pane();. final Scene  I wanted to play around drag and drop API in JavaFX. Here is a sample application implemented using DnD API. This example comprises 2 Listviews i.e. Players and Team. Players are dragged from source list view and dropped inside target list view. I have come across a couple of issues while working with DnD API:…


[JDK-8102073] Allow user to drag-and-drop Tab header areas in , In JavaFX, tab functionality is handled by two classes As the user then drags, the coordinates in the mouse drag events are position remains that projected by the current mouse position until the mouse button is released. MOUSE_DRAG_EXITED public static final EventType<MouseDragEvent> MOUSE_DRAG_EXITED This event occurs when the gesture exits a node. This event type is delivered only to the exited node, if parents want to filter it or get the bubbling event, they need to use MOUSE_DRAG_EXITED_TARGET.


JavaFX Drag and Drop Example, A drag-and-drop gesture is a specific type of a press-drag-release gesture. import javafx.scene.control.Label;. import javafx.scene.control. import javafx.​scene.layout. writelog( "Event on Target: mouse drag released" );. Drag and drop gesture can be started by calling startDragAndDrop() (on a node or scene) inside of a DRAG_DETECTED event handler. The data to be transfered to drop target are placed to a dragBoard at this moment. Drag entered/exited events behave similarly to mouse entered/exited events, please see MouseEvent overview.