2015-07-30 4 views
4

Я пытаюсь изменить элемент управления TabPage в JavaFx 8, чтобы он показывал в окне просмотра некоторые вкладки вперед (справа) текущего выбранного, или если выбранная вкладка находится в крайнем левом углу заголовка, она показывает близлежащие вкладки перед текущим.Показывать некоторые вкладки впереди из выбранной вкладки в заголовке Tab Tab JavaFx 8

Как сейчас:

Default behaviour

Как я пытаюсь заставить его вести себя как:

When the user selects the tab of index X, the tab pane header reveals another 2 or 3 nearby tabs.

Когда пользователь выбирает вкладку индекса X, вкладку заголовок панели показывает еще 2 или 3 соседних вкладки.

Это то, что я пробовал до сих пор, без каких-либо успехов, по-видимому, слишком короткий код, чтобы поток интерфейса синхронизировал выбор вкладок вовремя (идея состояла в том, чтобы выбрать вкладку вперед, а затем вернуться к выбранной пользователем, что делает заголовок выявить вкладки после выбранной вкладки):

import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.scene.control.Tab; 
import javafx.scene.control.TabPane; 

public class TabSelectionListener implements ChangeListener<Tab> { 

    protected TabPane owner; 
    protected boolean lock; 
    protected int nextItems; 

    TabSelectionListener(TabPane listenTo){ 
     owner = listenTo; 
     lock = false; 
     nextItems = 2; 
    } 

    TabSelectionListener(TabPane listenTo, int minimalInFront){ 
     owner = listenTo; 
     lock = false; 
     nextItems = minimalInFront; 
    } 

    @Override 
    public void changed(ObservableValue<? extends Tab> list, Tab old, Tab newT) { 
     int maxTab; 
     int curTab; 
     int i; 

     // Locks this listener, because the selections owner.getSelectionModel().select(X) 
     // will call this listener again, and we are calling those from here. 
     if(!lock){ 
      lock = true; 
      maxTab = owner.getTabs().size() - 1; 
      curTab = owner.getSelectionModel().getSelectedIndex(); 

      for(i = 0; i < nextItems && curTab + i < maxTab; i++); 
      owner.getSelectionModel().select(i); // int overload 
      owner.getSelectionModel().select(newT); 

      lock = false; 
     } 
    } 
} 

tabPane называет его для каждого выбора вкладки:

tabPane.getSelectionModel().selectedItemProperty().addListener(new TabSelectionListener(tabPane,3)); 

Я читал некоторые темы здесь, и появляется для меня, что заголовок на самом деле является StackPane, и может можно получить, выполнив:

StackPane region = (StackPane) tabPane.lookup(".headers-region"); 

Он работает, но после того, что я не знаю, как получить доступ к свойствам, которые реализуют поведение по умолчанию.

Любые предложения?

Спасибо за чтение.

ответ

3

Я, наконец, сделал это.

Обнаружено, что класс, который я искал, был com.sun.javafx.scene.control.skin.TabPaneSkin @ jfxrt.jar, он имеет способ сделать выбранную вкладку видимой, она запускается каждый раз, когда выбранная вкладка TabPane не полностью виден, я перезаписал его.

TabPaneSkin - это скин по умолчанию в TabPane, он применяет некоторые действия к элементу управления TabPane.

/*
* Copyright (с) 2011, 2014, Oracle и/или ее дочерних компаний. Все права защищены.
* ORACLE PROPRIETARY/CONFIDENTIAL. Использование зависит от условий лицензии.
*

Надеюсь, Oracle не будет возражать ...

Выберите свой TabPane и сделать ...

tabPane.setSkin(new TabPaneNewSkin(tabPane)); 

... перезаписать Oracle по умолчанию TabPaneSkin с этим я написал который показывает соседние вкладки.

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

private void ensureSelectedTabIsVisible() { 
      // work out the visible width of the tab header 
      double tabPaneWidth = snapSize(isHorizontal() ? getSkinnable().getWidth() : getSkinnable().getHeight()); 
      double controlTabWidth = snapSize(controlButtons.getWidth()); 
      double visibleWidth = tabPaneWidth - controlTabWidth - firstTabIndent() - SPACER; 

      // and get where the selected tab is in the header area 
      double offset = 0.0; 
      double selectedTabOffset = 0.0; 
      double selectedTabWidth = 0.0; 
      for (Node node : headersRegion.getChildren()) { 
       TabHeaderSkin tabHeader = (TabHeaderSkin)node; 

       double tabHeaderPrefWidth = snapSize(tabHeader.prefWidth(-1)); 

       if (selectedTab != null && selectedTab.equals(tabHeader.getTab())) { 
        selectedTabOffset = offset; 
        selectedTabWidth = tabHeaderPrefWidth; 
       } 
       offset += tabHeaderPrefWidth; 
      } 

      final double scrollOffset = getScrollOffset(); 
      final double selectedTabStartX = selectedTabOffset; 
      final double selectedTabEndX = selectedTabOffset + selectedTabWidth; 

      final double visibleAreaEndX = visibleWidth; 

      if (selectedTabStartX < -scrollOffset) { 
       setScrollOffset(-selectedTabStartX); 
      } else if (selectedTabEndX > (visibleAreaEndX - scrollOffset)) { 
       setScrollOffset(visibleAreaEndX - selectedTabEndX); 
      } 
     } 

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

// This function was overwritten 
    private void ensureSelectedTabIsVisible() { 
     // work out the visible width of the tab header 
     double tabPaneWidth = snapSize(isHorizontal() ? getSkinnable().getWidth() : getSkinnable().getHeight()); 
     double controlTabWidth = snapSize(controlButtons.getWidth()); 
     double visibleWidth = tabPaneWidth - controlTabWidth - firstTabIndent() - SPACER; 


     // and get where the selected tab is in the header area 
     double offset = 0.0; 
     double selectedTabOffset = 0.0; 
     double selectedTabWidth = 0.0; 

     // OVERWRITE 
     // Makes the nearby 3 tabs for each side of the selected tab visible. 
     ObservableList<Node> headersRegionChildren = headersRegion.getChildren(); 
     boolean nextTabs = false; 
     int nextTabsCount = 0; 
     int current = 0; 
     int numOfTabsToShowNext = 3; 
     int numOfTabsToShowBefore = 3; 
     double tabHeaderPrefWidth;  
     TabHeaderSkin tabHeader; 

     for (Node node : headersRegionChildren) { 
      tabHeader = (TabHeaderSkin)node; 

      tabHeaderPrefWidth = snapSize(tabHeader.prefWidth(-1)); 

      if (selectedTab != null && selectedTab.equals(tabHeader.getTab())) { 
        selectedTabWidth = tabHeaderPrefWidth; 

       // OVERWRITE: Finds the offset of the first tab in the limit numOfTabsToShowBefore before the selected one to be shown 
       for(int i = current - 1; i >= 0 && numOfTabsToShowBefore > 1; i--, numOfTabsToShowBefore--){ 
        tabHeader = (TabHeaderSkin)headersRegionChildren.get(i); 
        tabHeaderPrefWidth = snapSize(tabHeader.prefWidth(-1)); 
        offset -= tabHeaderPrefWidth; 
        selectedTabWidth += tabHeaderPrefWidth; 
       } 

       selectedTabOffset = offset; 
       // OVERWRITE: Sets the flag to start counting in the next 3 nearby tabs. 
       nextTabs = true; 
      } 
      // OVERWRITE: Sums the width of the next nearby tabs with the 
      // width of the selected tab, so it will scroll enough to show 
      // them too. 
      if(nextTabs && nextTabsCount < numOfTabsToShowNext){ 
       selectedTabWidth += tabHeaderPrefWidth; 
       nextTabsCount++; 
      }else if(nextTabsCount == numOfTabsToShowNext){ 
       break; 
      } 

      offset += tabHeaderPrefWidth; 
      current++; 
     } 
     // END OVERWRITE 

     final double scrollOffset = getScrollOffset(); 
     final double selectedTabStartX = selectedTabOffset; 
     final double selectedTabEndX = selectedTabOffset + selectedTabWidth; 

     final double visibleAreaEndX = visibleWidth; 

     if (selectedTabStartX < -scrollOffset) { 
      setScrollOffset(-selectedTabStartX); 
     } else if (selectedTabEndX > (visibleAreaEndX - scrollOffset)) { 
      setScrollOffset(visibleAreaEndX - selectedTabEndX); 
     } 
    } 

Код выше вскрывает 3 ближайших вкладок с каждой стороны с выбранной вкладки (если один из них выходит за пределы экрана и существует) для каждого выбора вкладки.

Так оно и было. com.sun.javafx.scene.control.skin.TabPaneSkin не должен расширяться, почти каждый метод является приватным, поэтому я сделал его копию и изменил только функцию, упомянутую выше, и переименовал ее в TabPaneNewSkin, и это на мой пакет.

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