5

I realize that I can't be the first person who desires to keep the content of a ScrollPane within the borders of the ScrollPanes viewport, yet after several hours of searching, I cannot find an answer to my question.

I've scoured Google, you.com and duckduckgo with various search terms such as

  • JavaFX ScrollPane viewport restrict contents to inside borders
  • JavaFX ScrollPane viewport keep contents inside viewport borders

And other variations of the wording, and I have not found an answer.

I have created basic reproducible code that accurately models the data I am presenting in my actual program. It's actually quite simple:

  • I have multiple VBoxes, each with three labels and a Separator.
  • Each VBox gets added to a parent VBox.
  • That parent VBox gets added to a ScrollPane.
  • ScrollPane has its scroll bars removed with some light styling.

The problem: When scrolling, the contents of the ScrollPane overlap the borders of the ScrollPanes viewport, and I cannot figure out how to tell the ScrollPane to keep the contents of the viewport within its borders.

If there is another way of doing this so that the scrollable contents stay within the borders of the viewport, I do not know of it and would appreciate any guidance towards that end.

My assumption is that the actual border of the viewport is something that can obviously be defined as to its width and color etc. but that it happens to be "painted" within the same space as the actual contents of the viewport. So that it should not be assumed that the border is any kind of an expected boundary by which the contents would then be bound... and if that is true, I accept that of course, but that doesn't help me achieve the behavior I'm after, which is to make sure that the contents do not show over the viewports borders.

Here, you can see what I'm talking about

enter image description here

Here is the code that will show the behavior:

import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import java.util.Random;

public class Main extends Application {

    @Override public void start(Stage stage) throws Exception {
        random = new Random(System.currentTimeMillis());
        makeScrollPane();
        Scene scene = new Scene(scrollPane);
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.centerOnScreen();
        stage.setWidth(width);
        stage.setHeight(height);
        stage.show();

    }
    private       Random     random;
    private final double     width       = 300;
    private final double     height      = 500;
    private       ScrollPane scrollPane;
    private final String     scrollStyle = Main.class.getResource("scrollpane.css").toExternalForm();
    private final String     labelStyle  = Main.class.getResource("label.css").toExternalForm();

    private void makeScrollPane() {
        VBox[] contentArray = new VBox[15];
        for (int x = 0; x < 15; x++) {
            String boxIndex = String.valueOf(random());
            String boxNum = String.valueOf(random());
            String lbl1   = "Title " + boxIndex;
            String lbl2   = "Item " + boxIndex;
            contentArray[x] = getVBox(lbl1, lbl2, boxNum);
        }
        VBox vBoxContent = new VBox(contentArray);
        scrollPane = new ScrollPane(vBoxContent);
        scrollPane.fitToWidthProperty().set(true);
        scrollPane.fitToHeightProperty().set(true);
        scrollPane.getStylesheets().add(scrollStyle);
        scrollPane.hbarPolicyProperty().set(ScrollPane.ScrollBarPolicy.NEVER);
        scrollPane.vbarPolicyProperty().set(ScrollPane.ScrollBarPolicy.NEVER);
    }

    private VBox getVBox(String label1, String label2, String label3) {
        Label lbl1 = newLabel(label1);
        Label lbl2 = newLabel(label2);
        Label lbl3 = newLabel(label3);
        lbl3.setId("big");
        Separator separator = new Separator();
        separator.setMaxWidth(250);
        separator.setHalignment(HPos.CENTER);
        separator.setPadding(new Insets(0, 0, 25, 0));
        VBox vBox = new VBox(20, lbl1, lbl2, lbl3, separator);
        vBox.setPrefWidth(width * .9);
        vBox.setPrefHeight(100);
        vBox.setAlignment(Pos.CENTER);
        return vBox;
    }

    private int random() {
        return random.nextInt(1000, 9999);
    }

    private Label newLabel(String content) {
        Label label = new Label(content);
        label.getStylesheets().add(labelStyle);
        label.setPrefWidth(width * .9);
        label.setPrefHeight(25);
        label.setAlignment(Pos.CENTER);
        return label;
    }
};

And the two stylesheets to go with it:

label.css

.label {
    -fx-font-size: 13pt;
    -fx-background-color: transparent;
    -fx-font-family: "Arial";
    -fx-text-fill: ghostwhite;
}

#big {
    -fx-font-size: 40;
    -fx-text-fill: yellow;
}

scrollpane.css

.scroll-pane .viewport {
    -fx-background-color: black;
    -fx-border-color: darkorange;
    -fx-border-width: .7em;
}

.root {
    -fx-background-color: black;
}

I appreciate any guidance in finding a way to accomplish what I'm after here.

Thank you

Michael Sims
  • 2,360
  • 1
  • 16
  • 29
  • 3
    Interesting. It seems the viewport doesn't respect padding, which would be my first approach here. Would a solution that puts the border on the scroll pane itself, with the black background in the viewport, be usable? I think that would work in the demo application you provided. – James_D Nov 18 '22 at 15:19
  • 3
    looks like a bug to me: viewport is specified as substructure of ScrollPane, so should be fully styleable and skin should respect those styles. Styling the scrollPane itself (as suggested by @James_D) seems to work in this case. – kleopatra Nov 18 '22 at 15:38
  • @James_D - I also tried padding of the VBox in the scrollPane, but as I'm sure you realized, the padding would only respect the edges of the VBox which the top and bottom would be outside of the viewports borders. I changed the stylesheet.css to apply the border only to the scroll-pane itself which does work fine. Wondering if this should be submitted as a bug report? – Michael Sims Nov 18 '22 at 18:36
  • You can check to see if a [bug report already exists](https://bugs.openjdk.org/browse/JDK-8297130?jql=project%20%3D%20JDK%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20javafx%20ORDER%20BY%20priority%20DESC%2C%20updated%20DESC). Otherwise, I would just submit the bug and see what they say. – Slaw Nov 18 '22 at 20:04

1 Answers1

3

The solution in this case, which is technically a workaround I suppose, was to only style the ScrollPane itself with the border and leave the viewport with a black background as suggested by James_D.

Simply changing scrollpane.css did the trick:

.scroll-pane {
    -fx-background-color: black;
    -fx-border-color: darkorange;
    -fx-border-width: .7em;
}

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

.root {
    -fx-background-color: black;
}
Michael Sims
  • 2,360
  • 1
  • 16
  • 29