我已经实现了与How to drag and drop tab nodes between tab panes非常相似的拖放功能,到目前为止工作正常,但如果选项卡被拖到外面,我还会添加一个新的Stage(下面的SideStage)主窗口/阶段。问题是,Tab只是在我将一个新Tab拖到TabPane或者我调整窗口大小后才显示。拖动动作的主要部分发生在EventHandler中的GuiPartFactory中,它被分配给被拖动的Tab的Label。我尝试了各种大小的分配,requestLayout等等。
Sidestage 类:
private TabPane tabPane;
public SideStage(double xPos, double yPos) {
tabPane = initTabArea();
VBox root = new VBox(0,GuiPartFactory.initMenuAndTitle(this),tabPane);
Scene scene = new Scene(root, 800, 600);
setScene(scene);
scene.setOnDragExited(new DnDExitAndEntryHandler());
scene.setOnDragEntered(new DnDExitAndEntryHandler());
setX(xPos);
setY(yPos);
;
initCloseWindowHandler();
}
private void initCloseWindowHandler() {
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
System.out.println("closing this sidestage");
MainApp.sideStages.remove(SideStage.this);
}
});
}
private TabPane initTabArea() {
final TabPane tabPane = GuiPartFactory.createTabPane();
tabPane.addEventHandler(Tab.TAB_CLOSE_REQUEST_EVENT, new EventHandler<Event>() {
@Override
public void handle(Event event) {
System.out.println("tab got closed mon!");
if (tabPane.getTabs().isEmpty()) {
SideStage.this.close();
}
}
});
VBox.setVgrow(tabPane, Priority.ALWAYS);
return tabPane;
}
public void addTab(Tab tab) {
tabPane.getTabs().add(tab);
}
GuiPartFactory 类:
public static ResourceBundle BUNDLE = ResourceBundle.getBundle("locales/Bundle", new Locale("en", "GB"));
public static TabPane createTabPane() {
final TabPane tabPane = new TabPane();
//This event gets fired when the cursor is holding a draggable object over this tabpane
tabPane.setOnDragOver(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
final Dragboard dragboard = event.getDragboard();
if (dragboard.hasString() && dragboard.getString().equals("tab") && ((Tab) MainApp.dndTemp).getTabPane() != tabPane) {
event.acceptTransferModes(TransferMode.MOVE);
event.consume();
}
}
});
//This event gets fired when the cursor is releasing a draggable object over this tabpane (this gets only called if it has been accepted in the previos dragover event!)
tabPane.setOnDragDropped(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
System.out.println("dropped");
Tab tab = (Tab) MainApp.dndTemp;
MainApp.dndHandled = true;
tab.getTabPane().getTabs().remove(tab);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
event.setDropCompleted(true);
event.consume();
}
});
return tabPane;
}
public static Tab createTab(String text) {
final Tab tab = new Tab();
final Label label = new Label("Tab" + text);
tab.setGraphic(label);
StackPane pane = new StackPane();
//need to set a real size here
pane.setPrefSize(500, 500);
tab.setContent(pane);
tab.getContent().setOnDragOver(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
final Dragboard dragboard = event.getDragboard();
if (dragboard.hasString() && dragboard.getString().equals("tab") && ((Tab) MainApp.dndTemp).getTabPane() != tab.getTabPane()) {
event.acceptTransferModes(TransferMode.MOVE);
event.consume();
}
}
});
tab.getContent().setOnDragDropped(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
System.out.println("dropped");
Tab transferTab = (Tab) MainApp.dndTemp;
MainApp.dndHandled = true;
transferTab.getTabPane().getTabs().remove(transferTab);
tab.getTabPane().getTabs().add(transferTab);
tab.getTabPane().getSelectionModel().select(transferTab);
event.setDropCompleted(true);
event.consume();
}
});
label.setOnDragDetected(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
Dragboard dragboard = label.startDragAndDrop(TransferMode.MOVE);
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString("tab");
MainApp.dndTemp = tab;
MainApp.dndHandled = false;
dragboard.setContent(clipboardContent);
dragboard.setDragView(new Image("img/dragcursor.png"));
event.consume();
}
});
label.setOnDragDone(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
//our dragndrop failed
if (!MainApp.dndHandled) {
//lets see if it failed because we dropped outside of our java-windows
if (!MainApp.dndInside) {
if (!MainApp.dndTemp.equals(tab)) {
System.out.println("something is wrong here");
}
System.out.println("gotta make a new window!");
SideStage sideStage = new SideStage(event.getScreenX(), event.getScreenY());
MainApp.sideStages.add(sideStage);
//just a check to make sure we don't access a null variable
if (tab.getTabPane() != null) {
tab.getTabPane().getTabs().remove(tab);
}
sideStage.addTab(tab);
sideStage.sizeToScene();
tab.getTabPane().getSelectionModel().select(tab);
System.out.println("width: " + ((StackPane) tab.getContent()).getWidth());
System.out.println("height: " + ((StackPane) tab.getContent()).getHeight());
System.out.println("width: " + tab.getTabPane().getWidth());
System.out.println("height: " + tab.getTabPane().getHeight());
sideStage.show();
}
}
}
});
return tab;
}
public static Tab createTabRandomColor(String text) {
Random rng = new Random();
Tab tab = GuiPartFactory.createTab(text);
StackPane pane = (StackPane) tab.getContent();
int red = rng.nextInt(256);
int green = rng.nextInt(256);
int blue = rng.nextInt(256);
String style = String.format("-fx-background-color: rgb(%d, %d, %d);", red, green, blue);
pane.setStyle(style);
Label label = new Label("This is tab " + text);
label.setStyle(String.format("-fx-text-fill: rgb(%d, %d, %d);", 256 - red, 256 - green, 256 - blue));
pane.getChildren().add(label);
return tab;
}
public static MenuBar initMenuAndTitle(Stage stage) {
//setting up a window title
stage.setTitle(BUNDLE.getString("title.name"));
//adding the menubar
MenuBar menuBar = new MenuBar();
//adding the base menu entry
Menu menuStart = new Menu(BUNDLE.getString("menu.start"));
Menu menuView = new Menu(BUNDLE.getString("menu.view"));
Menu menuHelp = new Menu(BUNDLE.getString("menu.help"));
menuBar.getMenus().addAll(menuStart, menuView, menuHelp);
//adding the menuitems inside the menus
MenuItem aboutItem = new MenuItem(BUNDLE.getString("menu.about"));
aboutItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Dialogs popup = Dialogs.create();
popup.title(BUNDLE.getString("menu.about.title"));
popup.message(BUNDLE.getString("menu.about.message"));
popup.showInformation();
}
});
menuHelp.getItems().add(aboutItem);
MenuItem exitItem = new MenuItem(BUNDLE.getString("menu.exit"));
exitItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Dialogs popup = Dialogs.create();
popup.title(BUNDLE.getString("menu.exit.title"));
popup.message(BUNDLE.getString("menu.exit.message"));
popup.actions(new Action[]{YES, NO});
Action response = popup.showConfirm();
if(response.equals(YES)) {
System.exit(0);
}
}
});
menuStart.getItems().add(exitItem);
return menuBar;
}
最后是 MainApp 类:
//for handling tag drag'n'drop outside of java windows
public static Object dndTemp = null;
public static boolean dndHandled = true;
public static boolean dndInside = true;
public static Set<SideStage> sideStages = new HashSet<>();
//holding the tabs (doe)
private TabPane tabPane;
@Override
public void start(Stage stage) {
tabPane = initTabs();
VBox root = new VBox(0, GuiPartFactory.initMenuAndTitle(stage), tabPane);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
scene.setOnDragExited(new DnDExitAndEntryHandler());
scene.setOnDragEntered(new DnDExitAndEntryHandler());
}
private TabPane initTabs() {
TabPane tabPane = GuiPartFactory.createTabPane();
Tab bedraggin = GuiPartFactory.createTab("be draggin");
Tab beraggin = GuiPartFactory.createTab("be raggin");
tabPane.getTabs().add(bedraggin);
tabPane.getTabs().add(beraggin);
for (int i = 0; i < 3; i++) {
Tab tab = GuiPartFactory.createTabRandomColor("" + i);
tabPane.getTabs().add(tab);
}
VBox.setVgrow(tabPane, Priority.ALWAYS);
return tabPane;
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public boolean removeSideStage(SideStage sideStage) {
return sideStages.remove(sideStage);
}
public void addTab(Tab tab) {
tabPane.getTabs().add(tab);
}
public void removeTab(Tab tab) {
tabPane.getTabs().remove(tab);
}
编辑:临时解决方法是将实际添加的标签添加到新的TabPane中,并将其添加到runnable中。但我认为这是一个相当糟糕的解决方案:
Platform.runLater(new Runnable() {
@Override
public void run() {
sideStage.addTab(tab);
tab.getTabPane().getSelectionModel().select(tab);
}
});