2013-05-08 182 views
4

我有一個帶有多個選項卡的Tabpane。 我想通過在特定位置拖動標籤來重新定位標籤(就像我們能夠在瀏覽器中安排標籤的方式一樣)。 我有什麼辦法可以實現它嗎?JavaFX標籤定位鼠標拖放

回答

5

我們以略微不同的方式實現了它。取代拖放功能,我們在選項卡上下文菜單中提供了左移/右移功能,然後輪流移動選項卡。 我們希望優先考慮這個功能,以便現在使用此解決方案來實現它。對於MoveRight的 enter image description here

代碼片段:

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); 
    } 
} 
4

更新2016年2月

有一個開放的功能要求,你可以用它來追蹤落實:

功能請求當前計劃在Java 9中實現。用於獲取拖放功能的修補程序附加到功能請求。


拖放進行標籤標題是不是在基本的JavaFX 2.2平臺上實現。

在標準JDK中實現之前,您需要使用JavaFX的Drag and Drop functionality自己實現該功能。一個類似的功能是實現拖動表格列標題,所以也許你可以看看TableColumnHeader.java代碼靈感實現你的功能。

如果你實施它(如果你願意的話),你可以通過TabSkin.java源代碼補丁將修改內容返回OpenJFX

+0

@Anshul,請創建一個描述你如何解決這個問題的答案。 – jewelsea 2013-05-10 06:22:41

+0

我想現在你不知道在這方面做更好的做法嗎?我沒有發現任何東西,因此創建了我自己的基本解決方案(請參閱我的答案),但我知道它肯定有它的缺陷,所以想知道是否會遇到任何其他外部庫這樣做! – berry120 2014-02-01 01:55:17

+1

對不起貝瑞,還沒有見過什麼。這將是一個很好的功能進入核心平臺。 – jewelsea 2014-02-01 03:23:13

5

,我實現了同時處理拖動和可拆卸的標籤一類 - 更多詳情here。這個實現並不是最簡單的,也不是最有彈性的,但對於我迄今爲止嘗試過的簡單案例來說,這種實現非常有效。我故意將所有內容都放在一個類中,以便其他人能夠在他們認爲合適的情況下複製/使用/修改。

我使用的基本概念(可以說是誤用)是,您可以在選項卡上設置的圖形可以是任何節點,而不僅僅是一個ImageView(或類似的)。)因此,我不直接使用Tab上的setText(),而是根本不添加任何文本,只是將圖形設置爲包含所需文本的Label。現在標籤出現在標籤頁頭中(幾乎是空間上的標籤頁頭),這使得它更容易(和皮膚無關)獲取窗格中每個標籤頁頭的全局座標。從那時起,只需要一些相對簡單的定位邏輯來確定何時將標籤分離到新窗口,何時重新添加標籤以及何時對其重新排序。

當然,這不是一個理想的解決方案,但不幸的是我還沒有看到這方面的其他內容!

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; 
     } 

    } 
} 
0

以下代碼顯示瞭如何以非常簡單的方式解決問題而無需技巧。

..... 
    ..... 
    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的其他地區的訪問。只需使用lookup或lookup TabPane的所有方法