Javafx 2 鼠标拖放时的JavaFX选项卡定位
我有一个包含多个选项卡的选项卡窗格。 我想通过将选项卡拖动到特定位置来重新定位它们(就像我们在浏览器中排列选项卡的方式一样)Javafx 2 鼠标拖放时的JavaFX选项卡定位,javafx-2,Javafx 2,我有一个包含多个选项卡的选项卡窗格。 我想通过将选项卡拖动到特定位置来重新定位它们(就像我们在浏览器中排列选项卡的方式一样) 有什么方法可以实现吗?可以找到一个非常描述性的答案,您可以在这里为相同的内容创建自定义选项卡: javafxcooked解决方案作为devblog很难找到,因为在相同的状态下,选项卡不存在这样的功能,他们计划稍后合并 2016年2月更新 有一个打开的功能请求可用于跟踪实施情况: 该功能请求目前计划在Java9中实现。用于获取拖放功能的修补程序附加到功能请求 基
有什么方法可以实现吗?可以找到一个非常描述性的答案,您可以在这里为相同的内容创建自定义选项卡: javafxcooked解决方案作为devblog很难找到,因为在相同的状态下,选项卡不存在这样的功能,他们计划稍后合并 2016年2月更新 有一个打开的功能请求可用于跟踪实施情况:
基本JavaFX2.2平台中未实现选项卡标题的拖放 在标准JDK中实现该功能之前,您需要自己使用JavaFX实现该功能。为拖动表列标题实现了一个类似的功能,因此您可以从代码中获得实现该功能的灵感
如果您实现了它(如果您愿意),您可以通过补丁将修改返回到源代码。我们以一种稍微不同的方式实现了它。我们在选项卡上下文菜单上提供了“向左移动/向右移动”功能,而不是拖放功能,从而依次移动选项卡。 我们希望优先使用这个特性,所以现在就用这个解决方案来实现它。 MoveRight的代码段:
我已经实现了一个处理可拖动和可分离选项卡的类-更多详细信息。这个实现不是最整洁的,也不是最有弹性的,但在我迄今为止尝试过的简单案例中,它对我来说非常有效。我故意将所有内容都保存在同一个类中,以便于其他人根据自己的需要进行复制/使用/修改 我使用的基本概念(可以说是误用)是,可以在选项卡上设置的图形可以是任何节点,而不仅仅是
ImageView
(或类似)因此,我不直接使用tab
上的setText(),只需将图形设置为包含所需文本的标签
。现在标签出现在选项卡标题中(特别是选项卡标题),这使得在窗格中获取每个选项卡标题的全局坐标变得更加容易(与皮肤无关)。从那时起,它只是一些相对简单的定位逻辑的一个例子,以确定何时将选项卡分离到一个新窗口中,何时重新添加它们,以及何时重新排序它们
当然,这不是一个理想的解决方案,但不幸的是,我在这个问题上没有看到太多其他的东西
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;
}
}
}
import java.util.HashSet;
导入java.util.Set;
导入javafx.collections.ListChangeListener;
导入javafx.event.EventHandler;
导入javafx.geometry.Point2D;
导入javafx.geometry.Pos;
导入javafx.geometry.Rectangle2D;
导入javafx.scene.scene;
导入javafx.scene.control.control;
导入javafx.scene.control.Label;
导入javafx.scene.control.Tab;
导入javafx.scene.control.TabPane;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.layout.StackPane;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.scene.text.text;
导入javafx.stage.stage;
导入javafx.stage.StageStyle;
导入javafx.stage.WindowEvent;
/**
*一个可拖动的选项卡,可以选择从其选项卡窗格中分离并显示
*在一个单独的窗口。这可以添加到任何普通选项卡窗格中,但是
*具有可拖动选项卡的选项卡窗格必须*仅*具有可拖动选项卡、普通选项卡和
*DrraTableTabs混合将导致问题!
*
*@作者迈克尔·贝瑞
*/
公共类DragTableTab扩展选项卡{
private static final Set tabPanes=new HashSet();
自有品牌;
私有文本dragText;
私有静态最后阶段标记阶段;
私人舞台;
私有布尔可分离;
静止的{
markerStage=新阶段();
markerStage.initStyle(舞台风格.未装饰);
矩形虚拟=新矩形(3,10,Color.web(“#555555”);
StackPane markerStack=新的StackPane();
markerStack.getChildren().add(dummy);
markerStage.setScene(新场景(markerStack));
}
/**
*创建一个新的可拖动选项卡。此选项卡可以添加到任何普通选项卡窗格,
*但是,具有可拖动选项卡的选项卡窗格必须*仅*具有可拖动选项卡,
*普通选项卡和DrraTableTabs混合会导致问题!
*
*@param text显示在标记标签上的文本。
*/
公共DragTableTab(字符串文本){
名称标签=新标签(文本);
设置图形(名称标签);
可分离=真;
dragStage=新阶段();
dragStage.initStyle(舞台风格.未装饰);
StackPane dragStagePane=新的StackPane();
dragStagePane.setStyle(“-fx背景色:#DDDDDD;”);
dragText=新文本(文本);
StackPane.setAlignment(绘图文本,位置中心);
dragStagePane.getChildren().add(dragText);
dragStage.setScene(新场景(dragStagePane));
nameLabel.setOnMouseDrawed(新的EventHandler(){
@凌驾
公共无效句柄(MouseEvent t){
dragStage.setWidth(namelab.getWidth()+10);
dragStage.setHeight(namelab.getHeight()+10);
setX(t.getScreenX());
setY(t.getScreenY());
dragStage.show();
Point2D screenPoint=新的Point2D(t.getScreenX(),t.getScreenY());
add(getTabPane());
InsertData=getInsertData(屏幕点);
if(data==null | | data.getInsertPane().getTabs().isEmpty()){
markerStage.hide();
}
否则{
int index=data.getIndex();
布尔端
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;
}
}
}
.....
.....
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());
});
tabPane.tabDragPolicy = TabPane.TabDragPolicy.REORDER