3

I have a BorderPane inside a Tabpane inside a ScrollPane. The ScrollPane.ScrollBarPolicy.AS_NEEDED does work if i remove the TabPane and put the BorderPane as Content of the ScrollPane. How do i get this to work with the TabPane?

Somehow the BorderPane is able to tell the ScrollPane when to display Scrollbars and the TabPane unable to do so. I looked through the avaible Methods for the Tabpane but couldn't find any for this resizing.

Working Example:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class FXApplication extends Application {

    private BorderPane border;
    private GridPane inner;
    private TabPane tabPane;

    @Override
    public void start(Stage primaryStage) {

        tabPane = new TabPane();
        Tab tab = new Tab("test");
        tabPane.getTabs().add(tab);

        border = new BorderPane();
        border.setCenter(innerGrid());
        tab.setContent(border);

        ScrollPane scp = new ScrollPane();
        scp.setFitToHeight(true);
        scp.setFitToWidth(true);
        scp.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
        scp.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);

//        scp.setContent(border);  // this works
        scp.setContent(tabPane);   // this doesnt

        Scene s = new Scene(scp);
        primaryStage.setScene(s);
        primaryStage.show();
    }

    private GridPane innerGrid() {
        inner = new GridPane();

        for(int i=0; i<11 ;i++) {
            ColumnConstraints columnConstraints = new ColumnConstraints();
            columnConstraints.setHgrow(Priority.SOMETIMES);
            inner.getColumnConstraints().add(columnConstraints);

            RowConstraints rowConstraints = new RowConstraints();
            rowConstraints.setVgrow(Priority.SOMETIMES);
            inner.getRowConstraints().add(rowConstraints);
        }

        for(int i=0; i<100 ;i++) {
            inner.add(new Button("Button " + i), i/10, i%10);
        }

        return inner;
    }

    public static void main(String[] args) {
        FXApplication.launch(args);
    }

}
stefan
  • 73
  • 3
  • good starter - but not complete (and with it probably not minimal :) CustomGridPane is missing, but you can probably replace it with some plain content – kleopatra Mar 27 '19 at 11:11
  • thx - removed CustomGridPane and made it "close to minimal" – stefan Mar 27 '19 at 12:28
  • good :) just nit-picking: you removed it from the method signature, but not from instantiation :) – kleopatra Mar 27 '19 at 12:40

1 Answers1

1

Astonishingly, the exact behavior of AS_NEEDED is unspecified. All we have is the ScrollPaneSkin to look at. The decision whether or not to show the (f.i.) horizontal bar happens in its private method determineHorizontalSBVisible()

private boolean determineHorizontalSBVisible() {
    final ScrollPane sp = getSkinnable();

    if (Properties.IS_TOUCH_SUPPORTED) {
        return (tempVisibility && (nodeWidth > contentWidth));
    }
    else {
        // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default
        ScrollBarPolicy hbarPolicy = sp.getHbarPolicy();
        return (ScrollBarPolicy.NEVER == hbarPolicy) ? false :
               ((ScrollBarPolicy.ALWAYS == hbarPolicy) ? true :
               ((sp.isFitToWidth() && scrollNode != null ? scrollNode.isResizable() : false) ?
               (nodeWidth > contentWidth && scrollNode.minWidth(-1) > contentWidth) : (nodeWidth > contentWidth)));
    }
}

Here nodeWidth is the actual width of the content node - has been calculated previously, respecting the node's min/max widths - and contentWidth is the width available for laying out the content.

Unreadable code (for me ;) In the case of resizable content and fitting into scrollPane's content area boils down to returning true if both content's actual and min width are greater than the available width.

The minWidth makes the difference in your context: BorderPane has a min > 0, TabPane has a min == 0, so the method above always returns false.

The other way round: to allow the hbar being visible with the TabPane it needs a min, f.i. by relating it to its pref:

tabPane.setMinWidth(Region.USE_PREF_SIZE);
kleopatra
  • 51,061
  • 28
  • 99
  • 211