0

How can you change the width of a ScrollBar from a ScrollPane to a certain value of px? I looked at JavaFX 2.1 Change ScrollPane Scrollbar size but simply setting the font size to X px doesn't seem to work properly. I did some tests with big values and the width wasn't what I set it to be (ex: for a value of 50px the ScrollBar had width of 56px, for a value of 88px it had width of 96px, for a value of 40px it had width of 44px, for a value of 30px it had width of 33px, so no relation between then).

I need a solution that works from java code and not from a CSS file as I need to be able to dynamically set the value.

There were other suggestions to use .lookupAll but from what I understand you should avoid using that.

Code for setting width and colors:

scrollPane.setStyle("colorScrollBar: rgba(" + A function that returns RBGA + ");"
        + " colorScrollBarButton: rgba(" + A function that returns RBGA + ");" 
        + " -fx-font-size: " + 30 + "px;");

Here is the CSS file I used to change the ScrollBar (to make it transparent, to change the colors and remove some margins):

.scroll-bar {
    -fx-background-color: colorScrollBar;
    -fx-background-insets: 0;
    -fx-padding: 0;
}

.scroll-bar .thumb {
    -fx-background-color: colorScrollBarButton;
}

.scroll-bar > .increment-button > .increment-arrow,
.scroll-bar > .decrement-button > .decrement-arrow {
    -fx-background-color: colorScrollBarButton;
}

.scroll-bar > .increment-button:hover,
.scroll-bar > .decrement-button:hover {
    -fx-background-color: colorScrollBar;
}

.scroll-pane > .viewport {
   -fx-background-color: transparent;
}

.scroll-pane {
    -fx-background-color: transparent;
    -fx-background-insets: 0;
    -fx-padding: 0;
}

.scroll-pane:focused {
    -fx-background-insets: 0;
    -fx-padding: 0;
}

.scroll-pane .corner {
    -fx-background-insets: 0;
    -fx-padding: 0;
}
Devorious
  • 107
  • 1
  • 8
  • It is not really a big problem that you can't do that to a `ScrollPane` but good to know in the future for other nodes as you said. Then I guess I'll just have to create a custom `ScrollBar`, set the pref width to what I need, and find out how to bind the scrolling of the bar to `ScrollPane`. Like I said, `CSS` doesn't help me, that is why I'll go with the custom one. – Devorious Dec 04 '21 at 11:51
  • don't understand your comment .. what has "bind scrolling of the bar" to do with its width? Please provide a [mcve] demonstrating what you are after and how it doesn't work (when setting the bar's pref) – kleopatra Dec 04 '21 at 11:54
  • I'm goin to create a custom `ScrollBar` and use `.setPrefWidth(Value)` to set it's width, and bind the scrolling of the custom `ScrollBar` to the `ScrollPane`. When the custom `ScrollBar` is being scrolled down let's say, then the `ScrollPane` will be scrolled down for the same value. With this I don't need the `ScrollBar` from the `ScrollPane`, the one I can't change the width dynamically. – Devorious Dec 04 '21 at 11:59
  • 1
    @kleopatra you can set a `-fx-font-size` property on any node, including regions, layout panes, controls and the .root class. Doing so will effect anything measured in em units, which is most things in modena.css except padding values and background insets (which are pixel point measurements). That allows all of the standard controls to scale with font size and still look good. There is some info on em sizing and in the comments at the top of modena.css. A [scale by font size](https://stackoverflow.com/questions/23229149/javafx-automatic-resizing-and-button-padding) example. – jewelsea Dec 04 '21 at 16:11
  • The likely reason your font size based scaling didn’t provide the exact pixel widths you expected is probably because of info [documented in modena](https://gist.github.com/maxd/63691840fc372f22f470#file-modena-css-L72): “ Not all sizes are scaled with em units only padding. All borders and background insets are still in pixels. Also any padding where it has to match up is being used to size a border should also be in pixels.” So if you make the font size 50% bigger it doesn’t mean a control will be exactly 50% bigger. – jewelsea Dec 04 '21 at 16:18
  • @jewelsea then I was wrong - thanks for the heads up :) – kleopatra Dec 04 '21 at 16:20
  • “I need a solution that works from java code and not from a CSS file as I need to be able to dynamically set the value.” You can use a data url for css, the value for the data url can be in Java code, it does not need to be in a file. If you don’t understand that but would like to, ask a specific new question explicitly about that (and only that and nothing about scroll css, to keep it focused) – jewelsea Dec 04 '21 at 16:24
  • @jewelsea for this project I solved the problem, but I'll still look for more info about that, maybe I'll need it in the future, thanks! – Devorious Dec 05 '21 at 09:37

4 Answers4

2

This works for me.


    private ScrollPane scrollPane;

    @Override
    public void start(Stage primaryStage) {
        BorderPane root = new BorderPane();

        Circle content = new Circle(2000);
        scrollPane = new ScrollPane(content);
        
        Slider sizeSldr = new Slider(8, 80, 14);
        root.setTop(sizeSldr);
        root.setCenter(scrollPane);

        sizeSldr.valueProperty().addListener((observable,  oldValue,  newValue) -> {
            setScrollBarWidth(newValue.doubleValue());
        });

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setTitle("ScrollBar Width");
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    void setScrollBarWidth(double width) {
        scrollPane.setStyle("-fx-font-size: %3.3fpx".formatted(width));
    }

    public static void main(String[] args) {
        launch(args);
    }
}
swpalmer
  • 3,890
  • 2
  • 23
  • 31
  • Using this you ran into the same problem I mentioned in the main post, I changed your code to use instead of `newValue.doubleValue()`, a constant value of 60, and the `ScrollBar` has a total width of 66px. @jewelsea explained the reason in a comment on the main post. – Devorious Dec 05 '21 at 09:29
  • if you target to exact pixel sizes, you are doing something wrong .. doing so will break the layout for changed screen sizes/resolutions – kleopatra Dec 06 '21 at 11:12
0

The solution I came with is to create a custom ScrollBar and use that to scroll the ScrollPane. Here is an example how to create one and bind it to the ScrollPane:

ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(paneElements); //The pane that will have the nodes
scrollPane.setLayoutX(20);
scrollPane.setLayoutY(20);
scrollPane.setPrefWidth(stage.getWidth() - 40); //This was my use case, do what you need here
scrollPane.setPrefHeight(stage.getHeight() - 66); //This was my use case, do what you need here
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setStyle("colorScrollBar: rgba(" + I am using a function that returns RGBA + ");"
        + " colorScrollBarButton: rgba(" + I am using a function that returns RGBA + ");");
pane.getChildren().add(scrollPane);

ScrollBar scrollBarVertical = new ScrollBar();
scrollBarVertical.setLayoutX(stage.getWidth() - 20)); //Use what you need
scrollBarVertical.setLayoutY(20); //Use what you need
scrollBarVertical.setPrefHeight(stage.getHeight() - 66); //Use what you need
scrollBarVertical.setMinWidth(0); //I set it to 0 because if you need to use low values, it wasn't using values lower then 16 in prefWidth in my case
scrollBarVertical.setPrefWidth(prefWidth * 0.75)); //Use what you need
scrollBarVertical.setOrientation(Orientation.VERTICAL);
scrollBarVertical.setMin(scrollPane.getVmin());
scrollBarVertical.setMax(scrollPane.getVmax());
scrollBarVertical.valueProperty().bindBidirectional(scrollPane.vvalueProperty());
scrollBarVertical.setStyle("colorScrollBar: rgba(" + I am using a function that returns RGBA + ");"
        + " colorScrollBarButton: rgba(" + I am using a function that returns RGBA + ");");
pane.getChildren().add(scrollBarVertical);
Devorious
  • 107
  • 1
  • 8
0

Once in the Scene, you can query the ScrollBars using:

ScrollBar vsb = (ScrollBar) scrollPane.queryAccessibleAttribute(
        AccessibleAttribute.VERTICAL_SCROLLBAR);
ScrollBar hsb = (ScrollBar) scrollPane.queryAccessibleAttribute(
        AccessibleAttribute.HORIZONTAL_SCROLLBAR);

vsb.setPrefWidth(40);
hsb.setPrefHeight(40);
Oboe
  • 2,643
  • 2
  • 9
  • 17
  • I think for this to work safely, you might also need to implement a handler for [notifyAccessibleAttributeChanged](https://openjfx.io/javadoc/17/javafx.graphics/javafx/scene/Node.html#notifyAccessibleAttributeChanged(javafx.scene.AccessibleAttribute)), which is called "When the value of an attribute is changed by a node, it must notify the assistive technology using" because scroll panes will be added and removed depending on scroll pane policy changes whether or not the content fits without scrolling. – jewelsea Dec 04 '21 at 23:03
  • @jewelsea I'm not sure how to implement a handler for `notifyAccessibleAttributeChanged` without extending the class. Any guidance? – Oboe Dec 04 '21 at 23:31
  • I don’t know either :-). It is not an API that I have ever used. There is a (long) [tutorial](https://m.youtube.com/watch?v=iUPPEkD9H1I) on it by the developer who created it, perhaps that contains some info. – jewelsea Dec 04 '21 at 23:47
  • CSS has the advantage that it is automatically applied dynamically, so it doesn’t have the same kind of issues as these sorts of lookups. – jewelsea Dec 04 '21 at 23:48
  • It might not be necessary to implement a handler, but in the past when using lookups, which are similar, I ran into issues with different instances of scroll bars being created in listViews as the size of the list grew or shrank. I don’t know if the same thing would happen with scroll panes. – jewelsea Dec 04 '21 at 23:49
  • @jewelsea Thanks for noticing ;). I'll take a look at the tutorial and update the answer if I figure it out. – Oboe Dec 04 '21 at 23:50
  • @jewelsea _I ran into issues with different instances of scroll bars being created in listViews as the size of the list grew or shrank_ hmm ... sounds unusual: the scrollbars are created once (by the skin's virtualFlow), added to the scenegraph and shown/hidden as needed. So the only way to get different bars on different calls to lookup is if the skin changed in between. If it happens somehow else, it might be a bug. – kleopatra Dec 05 '21 at 11:41
0

Been struggling with this problem for some time, found an answer

You can add listener to your scrollPane's children, and set the widths to your scrollBars as they appear:

ScrollPane scrollPane = new ScrollPane(pane);
scrollPane.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() {
            @Override
            public void onChanged(Change<? extends Node> c) {
                while (c.next()) {
                    if (c.wasAdded()) {
                        c.getAddedSubList().forEach(n -> {
                            if (n instanceof ScrollBar) {
                                ScrollBar bar = (ScrollBar) n;
                                bar.setPrefWidth(30); //set what you need
                            }
                        });
                    }
                }
            }
        });

PS Sorry for necroposting

vit
  • 21
  • 1