Показать некоторые вкладки вперед от выбранной вкладки в заголовке JavaFX 8 TabPane
Я пытаюсь изменить элемент управления TabPage в JavaFx 8, чтобы он показывал на видовом экране некоторые вкладки впереди (справа) текущего выбранного, или если выбранная вкладка находится в крайнем левом углу заголовка, она показывает соседние вкладки перед текущим.
Как это сейчас:
Как я пытаюсь заставить его вести себя так:
Когда пользователь выбирает вкладку индекса 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 region = (StackPane) tabPane.lookup(".headers-region");
Это работает, но после этого я понятия не имею, как получить доступ к свойствам, реализующим поведение по умолчанию.
Есть предложения?
Спасибо за чтение.
1 ответ:
Наконец - то я это сделал.
Оказалось, что класс, который я искал, был com.солнце.в JavaFX.сцена.контроль.кожа.TabPaneSkin @ jfxrt.jar, у него есть метод, чтобы сделать выбранную вкладку видимой, он запускается каждый раз, когда выбранная вкладка на панели табуляции не полностью видна, я переписал ее.
TabPaneSkin-это кожа TabPane по умолчанию, она применяет некоторые модели поведения к элементу управления TabPane.
/*
* Copyright (c) 2011, 2014, Oracle и/или ее филиалы. Все права защищены.
* ORACLE PROPRIETARY / CONFIDENTIAL. Использование осуществляется в соответствии с условиями лицензии.
*Надеюсь, Оракул не будет возражать...
Выберите свой TabPane, и сделать...
tabPane.setSkin(new TabPaneNewSkin(tabPane));
... чтобы перезаписать TabPaneSkin Oracle по умолчанию с этим, я написал, что показывает соседние вкладки.
Исходный код 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 ближайших вкладки на каждая сторона от выбранной вкладки (если одна из них находится вне экрана и существует), для каждого выбора вкладки.
Так оно и было. ком.солнце.в JavaFX.сцена.контроль.кожа.TabPaneSkin не предполагалось расширять, почти каждый метод является частным, поэтому я сделал его копию и изменил только функцию, упомянутую выше, и переименовал ее в TabPaneNewSkin, и она находится в моем пакете.