简体   繁体   English

在JavaFx 8 TabPane标题中的选定选项卡前面显示一些选项卡

[英]Show some tabs ahead from selected tab in a JavaFx 8 TabPane header

I'm trying to modify the TabPage control in JavaFx 8, to make it reveal to the viewport some tabs ahead (to the right) of the current selected one, or if the selected tab is at the extreme left of the header, it shows the nearby tabs before the current one. 我正在尝试修改JavaFx 8中的TabPage控件,使其向视口显示当前所选选项卡前面(右侧)的一些选项卡,或者如果所选选项卡位于标题的最左侧,则显示在当前标签之前的附近标签。

How it is now: 现在怎么样:

默认行为

How I'm trying to make it behave like: 我是如何尝试使它表现得像:

当用户选择索引X的选项卡时,选项卡窗格标题会显示另外2或3个附近的选项卡。

When the user selects the tab of index X, the tab pane header reveals another 2 or 3 nearby tabs. 当用户选择索引X的选项卡时,选项卡窗格标题会显示另外2或3个附近的选项卡。

This is what I tried so far, with no success, apparently the code bellow is too fast to make the interface thread sync the tab selections on time (the idea was to select a tab ahead and then fallback to the one selected by the user, making the header reveal the tabs after the selected tab): 这是我到目前为止所尝试的,没有成功,显然代码下面太快,无法使接口线程按时同步选项卡选项(想法是提前选择一个选项卡,然后回退到用户选择的选项卡,使标题显示选定选项卡后的选项卡):

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

The tabPane calls it for every tab selection: tabPane为每个选项卡选择调用它:

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

I have been reading some topics here, and it appears to me that the header is actually a StackPane, and can be obtained by executing: 我一直在阅读这里的一些主题,在我看来,标题实际上是一个StackPane,可以通过执行:

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

It works, but after that I have no idea how to access the properties that implements the default behaviour. 它可以工作,但之后我不知道如何访问实现默认行为的属性。

Any suggestions? 有什么建议?

Thanks for reading. 谢谢阅读。

I finally did it. 我终于做到了。

Found out the class I was looking for was com.sun.javafx.scene.control.skin.TabPaneSkin @ jfxrt.jar, it has a method to make the selected tab visible, it runs everytime a selected tab at a TabPane is not fully visible, I overwrote it. 发现我正在寻找的类是com.sun.javafx.scene.control.skin.TabPaneSkin @jfxrt.jar,它有一个方法可以使选定的选项卡可见,它每次TabPane上的选定选项卡都不完整时运行可见,我覆盖了它。

TabPaneSkin is the default Skin of TabPane, it applies some behaviours to the TabPane control. TabPaneSkin是TabPane的默认外观,它将一些行为应用于TabPane控件。

/* / *
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. *版权所有(c)2011,2014,Oracle和/或其附属公司。 All rights reserved. 版权所有。
* ORACLE PROPRIETARY/CONFIDENTIAL. * ORACLE所有权/机密。 Use is subject to license terms. 使用受许可条款的约束。
* *

Hopefully Oracle will not mind... 希望Oracle不介意......

Pick your TabPane, and make... 选择你的TabPane,然后......

tabPane.setSkin(new TabPaneNewSkin(tabPane));

... to overwrite Oracle's default TabPaneSkin with this one I wrote that shows nearby tabs. ...用我编写的这个显示附近标签的那个来覆盖Oracle的默认TabPaneSkin。

Original Oracle's code for repositioning tabs when one is selected to make it visible: 原始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);
            }
        }

Code I wrote into my custom TabPane skin: 代码我写入我的自定义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);
        }
    }

Code above reveals the 3 nearest tabs at each side from the selected tab (if one of those is out of the screen and exists), for every tab selection. 对于每个选项卡选项,上面的代码显示所选选项卡每侧的3个最近选项卡(如果其中一个选项卡位于屏幕外并且存在)。

So that was it. 就是这样。 com.sun.javafx.scene.control.skin.TabPaneSkin was not supposed to be extended, almost every method is private, so I made a copy of it and changed only the function mentioned above, and renamed it to TabPaneNewSkin, and it is at my package. com.sun.javafx.scene.control.skin.TabPaneSkin不应该被扩展,几乎每个方法都是私有的,所以我复制了它并只更改了上面提到的函数,并将其重命名为TabPaneNewSkin,它是在我的包裹。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM