JavaFX Tab when drag and drop

I have a tab with multiple tabs. I want to move tabs by simply dragging them to a specific position (just like we can organize tabs in a browser). Is there any way I can achieve this?

+4
source share
5 answers

We achieved this a little differently. Instead of the drag and drop function, we provided the ability to move left / right in the context menu of the tab, which in turn moves the tab. We wanted this feature to be a priority, so it is implemented with this workaround. enter image description here

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); } } 
+5
source

I implemented a class that handles both draggable and removable tabs - more info here . The implementation is not the most accurate, not the most stable, but it works very well for me in the simple cases that I have tried so far. I deliberately kept everything in one class to make it easier for others to copy / use / modify as I see fit.

The main concept that I use (maybe Iโ€™m using it incorrectly) is that the graphics that you can set on the tab can be any node, not just ImageView (or similar.) Therefore, instead of using setText() on Tab directly , I am not adding any text at all, just setting the graphic as a Label containing the desired text. Now that the label is present in the title of the tab (and is largely the title of the tab spatially), it makes it much easier (and not dependent on the skin) to capture the global coordinates of each tab title in the panel. From now on, it's just the case of some relatively simple positioning logic to figure out when to separate tabs in a new window, when to add them again, and when to reorder them.

Of course, this is not an ideal solution, but, unfortunately, I have not seen this anymore!

 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 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; } } } 
+5
source

February 2016 update

There is an open function request that you can use to track implementation:

A function request is currently planned for implementation in Java 9. Patches for getting drag and drop functions are tied to a function request.


Drag and drop for tab headers is not implemented on the underlying JavaFX 2.2 platform.

Until this is implemented in the standard JDK, you will need to implement this function yourself using the JavaFX drag and drop function . A similar function is implemented for dragging the headings of table columns, so perhaps you can look at the code of TableColumnHeader.java for inspiration when implementing your function.

If you implement it (if you want), you can make changes to OpenJFX using patches on the TabSkin.java tab .

+4
source

You can find a very descriptive answer, where you can create your own tabs for them:

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

A complete JavaFX solution is difficult to find as a secure blog for the same states that such functions are not available for tabs, and they plan to enable them later.

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

+1
source

The following code shows how to solve the problem very simply without tricks.

  ..... ..... Tab tab1 = new Tab("Tab1"); Tab tab2 = new Tab("Tab21"); TabPane tabPane = new TabPane(tab1, tab21); root.getChildren().add(tabPane); .... .... System.out.println("Tabs size()= " + tabPane.lookupAll(".tab").size()); tabPane.lookupAll(".tab").forEach(t -> { System.err.println("tab.bounds = " + t.getLayoutBounds()); }); 

You can access other areas of TabPane using style classes such as tab-content-area, tab-header-area, tab-header-background, headers-region, control-buttons-tab. Just use lookup or lookup All TabPane methods

0
source

Source: https://habr.com/ru/post/1479702/


All Articles