Selim
Selim

Reputation: 1132

Weird ScrollBar behavior when bound to ScrollPane ScrollBar

I have built a control that provides Twitter Bootstrap navbar-like behaviour for JavaFX. It basically consists of a StackPane with a ScrollPane on the back, a BorderPane with Top, Bottom bars on top and finally a BorderPane with a ScrollBar on the right on the topmost layer. This is supposed to support following scenarios:

Hence the StackPane layout.

The layout part works fine and does what I want. My ScrollBar behaves very weirdly though. When content is scrollable the native ScrollBar (the one managed by the ScrollPane) looks like this:

native ScrollBar

The red lines indicate an estimation of the maximum size the thumb could have. So with scrollable content, the ScrollBar is not as high as possible which is correct.

Now my implementation behaves differently with scrollable content. There are two behaviors I can observe:

enter image description here

The ScrollBar thumb is tiny eventhough the native one has the correct size. Or:

enter image description here

The thumb has the maximum size eventhough the content is scrollable. While scrolling works, both of these examples are clearly wrong regarding visuals. My code that binds the two ScrollBars properties looks like following:

vScrollBar.valueProperty().bindBidirectional(scrollPane.vvalueProperty());
vScrollBar.maxProperty().bind(scrollPane.vmaxProperty());
vScrollBar.minProperty().bind(scrollPane.vminProperty());
nodeListChangeListener = c -> {
    ScrollBar hiddenScrollBar = getScrollBarFromScrollPane(scrollPane, Orientation.VERTICAL);
    if (hiddenScrollBar != null) {
        vScrollBar.visibleAmountProperty().bind(hiddenScrollBar.visibleAmountProperty());
        vScrollBar.blockIncrementProperty().bind(hiddenScrollBar.blockIncrementProperty());
        vScrollBar.unitIncrementProperty().bind(hiddenScrollBar.unitIncrementProperty());
        scrollPane.getChildrenUnmodifiable().removeListener(nodeListChangeListener);
    }
};
scrollPane.getChildrenUnmodifiable().addListener(nodeListChangeListener);

Another thing worth mentioning is that in almost every other place I am using this control it behaves correctly. There are only a few areas where this occurs but I don't understand how it is still possible.


So finally: What property am I missing here? Shouldn't both ScrollBars behave identically given that I am binding all these properties from the ScrollPane ScrollBar to my custom overlay ScrollBar (vScrollBar)?

Upvotes: 4

Views: 476

Answers (1)

Edwardth
Edwardth

Reputation: 707

(I assume that getScrollBarFromScrollPane(scrollPane, Orientation.VERTICAL); invokes (ScrollBar)scrollPane.queryAccessibleAttribute(AccessibleAttribute.VERTICAL_SCROLLBAR);)

I don't know how you create you component, but it's very likely that hiddenScrollBar is just null and the visible ScrollBar is never bounded to the ScrollBar of the ScrollPane.

This happens because nodeListChangeListener is notified during the creation of ScrollPaneSkin but before the property skin is set, therefor it's not possible to access the scrollbar.

To correctly bind the scrollbar you can add a listener to skinProperty:

scrollPane.skinProperty().addListener(c -> {
    ScrollBar hiddenScrollBar = (ScrollBar)scrollPane.queryAccessibleAttribute(AccessibleAttribute.VERTICAL_SCROLLBAR);
    if (hiddenScrollBar != null) {
        bar.visibleAmountProperty().bind(hiddenScrollBar.visibleAmountProperty());
        bar.blockIncrementProperty().bind(hiddenScrollBar.blockIncrementProperty());
        bar.unitIncrementProperty().bind(hiddenScrollBar.unitIncrementProperty());
    }
});

Upvotes: 1

Related Questions