2013-05-08 5 views
4

У меня есть вкладка с несколькими вкладками. Я хочу переместить вкладки, просто перетащив их в определенную позицию (точно так же, как мы можем организовать вкладки в браузере.) Есть ли способ, которым я могу это достичь?JavaFX Позиционирование вкладок на перетаскивание мышью

ответ

5

Мы достигли этого несколько иначе. Вместо функции перетаскивания мы предоставили возможность перемещения влево/вправо в контекстном меню вкладки, которое по очереди перемещает вкладку. Мы хотели, чтобы эта функция была приоритетной, поэтому она реализована с этим обходным решением. enter image description here

Фрагмент кода для 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); 
    } 
} 
1

очень описательный ответ можно найти, где вы можете создавать собственные вкладки для того же:

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

JavaFX приготовленный раствор трудно найти, как девблоге для одних и тех же государств, таких функций нет для вкладок, и они планируют включить их позже.

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

4

Обновление Февраль 2016

Существует запрос открытой функции, которую можно использовать для отслеживания реализации:

Запрос функции в настоящее время запланирован для реализации в Java 9. Патчи для получения функций перетаскивания привязаны к запросу функции.


Перетаскивание заголовков вкладок не реализовано на базовой платформе JavaFX 2.2.

До тех пор, пока это не будет реализовано в стандартном JDK, вам нужно будет реализовать эту функцию самостоятельно, используя Drag and Drop functionality JavaFX. Аналогичная функция реализована для перетаскивания заголовков столбцов таблицы, поэтому, возможно, вы можете посмотреть код TableColumnHeader.java для вдохновения при реализации вашей функции.

Если вы его реализуете (если хотите), вы можете внести изменения в OpenJFX через патчи к источнику TabSkin.java.

+0

@Anshul, пожалуйста, создайте ответ, который описывает, как вы это решили. – jewelsea

+0

Я не думаю, что вы знаете, как лучше сделать что-то в этом отношении сейчас? Я не нашел ничего такого, что создало бы мое рудиментарное решение (см. Мой ответ), но я знаю, что у него определенно есть подводные камни, поэтому задавался вопросом, не попадаешь ли вы в какую-либо другую внешнюю библиотеку, которая это делает! – berry120

+1

Извините Берри, ничего не видел. Было бы неплохой возможностью попасть в основную платформу. – jewelsea

5

Я реализовал класс, который обрабатывает как перетаскивать и съемные вкладки - больше деталей here. Реализация не самая аккуратная, не самая устойчивая, но очень хорошо работает для меня в простых случаях, которые я пробовал до сих пор. Я преднамеренно сохранил все в одном классе, чтобы другим было легче копировать/использовать/изменять по своему усмотрению.

Основная концепция, которую я использую (возможно, неправильно использую), заключается в том, что графикой, которую вы можете установить на вкладке, может быть любой узел, а не только ImageView (или аналогичный.) Поэтому вместо того, чтобы напрямую использовать setText() на Tab, я не добавляю никакого текста вообще, просто устанавливая графику как 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, используя классы стилей, такие как вкладки-содержимой область, вкладки-заголовок-область, вкладки-заголовок-фон, заголовки-область, управления-кнопка-закладка , Просто используйте поиск или поиск. Все методы TabPane

Смежные вопросы