I am trying to figure out how to change a scrollpanes scrollbar size to make it wider in javafx 2.1.
The ScrollBar width is based on the font size of the ScrollPane.
Set the font size of the ScrollPane to something big and (if needed) sent the font size of your the ScrollPane's content node back to something normal.
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(content);
scrollPane.setStyle("-fx-font-size: 40px;"); // set the font size to something big.
content.setStyle("-fx-font-size: 11px;"); // reset the region's font size to the default.
Here is a complete executable example based on my answer to a previous forum question on the same topic.
import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.*;
import javafx.stage.Stage;
import javafx.scene.chart.*;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Region;
public class BigScrollBars extends Application {
#Override public void start(Stage stage) {
// create a chart.
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", 13),
new PieChart.Data("Oranges", 25),
new PieChart.Data("Plums", 10),
new PieChart.Data("Pears", 22),
new PieChart.Data("Apples", 30)
);
final PieChart chart = new PieChart(pieChartData);
chart.setTitle("Imported Fruits");
chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
chart.setPrefSize(800,600);
// create a scrollpane.
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(chart);
scrollPane.setStyle("-fx-font-size: 40px;"); // set the font size to something big.
chart.setStyle("-fx-font-size: 11px;"); // reset the region's font size to the default.
// show the scene.
stage.setScene(new Scene(scrollPane, 400, 300));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
The following solution worked for me:
#FXML
private ScrollPane myScrollPane;
// Where you need in your code do the following:
Set<Node> nodes = myScrollPane.lookupAll(".scroll-bar");
for (final Node node : nodes) {
if (node instanceof ScrollBar) {
ScrollBar sb = (ScrollBar) node;
if (sb.getOrientation() == Orientation.VERTICAL) { // HORIZONTAL is another option.
sb.setPrefWidth(40); // You can define your preferred width here.
}
}
}
Related
Is it possible with Popup opacity mask top and bottom JavaFX? I have TextField autocomplete with Popup. So the idea is to put an opacity mask.
Below is another way you can give a try, for getting the opacity masked effect. Though it is not exactly the same implementation, I took some ideas from the link you provided :).
I created a small utility where you can pass the Popup instance. The utility builds the mask panes and include to the root node of the Popup.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class PopupOpacityMaskDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
root.setStyle("-fx-background-color:grey;");
root.setOnMouseClicked(e -> {
ListView<String> content = new ListView<>();
content.getItems().addAll(IntStream.range(100, 200).mapToObj(i -> i + "").collect(Collectors.toList()));
content.setPrefSize(200, 250);
Popup popup = new Popup();
popup.setAutoHide(true);
popup.getContent().add(content);
popup.setX(e.getScreenX());
popup.setY(e.getScreenY());
popup.show(root.getScene().getWindow());
MaskUtil.applyMask(popup);
});
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Demo");
primaryStage.show();
}
static class MaskUtil{
static void applyMask(Popup popup) {
double fadeSize = 70;
Pane pane = (Pane)popup.getScene().getRoot();
// Build the mask panes
Pane topMask = buildMaskPane(pane, fadeSize, false);
Pane bottomMask = buildMaskPane(pane, fadeSize, true);
// Just ensuring to remove any masks (if you are reusing the Popup)
pane.getChildren().removeAll(pane.lookupAll(".mask"));
pane.getChildren().addAll(topMask, bottomMask);
// Update the bottom mask position by listening to height of pane
pane.heightProperty().addListener((obs, old, h) -> bottomMask.setLayoutY(h.doubleValue() - fadeSize));
if (pane.getHeight() > 0) {
bottomMask.setLayoutY(pane.getHeight() - fadeSize);
}
}
private static Pane buildMaskPane(Pane pane, double fadeSize, boolean isBottom) {
Pane mask = new Pane();
mask.setMouseTransparent(true); // Turn this to 'false' if you don't want to interact over mask
mask.setPrefHeight(fadeSize);
mask.prefWidthProperty().bind(pane.widthProperty());
mask.maxHeightProperty().bind(mask.prefHeightProperty());
mask.minHeightProperty().bind(mask.prefHeightProperty());
mask.getStyleClass().add("mask");
mask.setStyle(String.format("-fx-background-color:linear-gradient(to %s, #555555, transparent)", isBottom ? "top" : "bottom"));
return mask;
}
}
}
I currently have a ScrollPane with a FlowPane as content. The FlowPane currently initializes with no children nodes, a fixed width and a pref/min height (but no max height).
While adding items to the FlowPane at runtime (I click some UI element and something is added to the FlowPane), the ScrollPane should adjust its height in the case that the addition to the FlowPane no longer fits.
I don't understand how to set the height of the flowPane and ScrollPane so that this works - if that's the problem to begin with. At the moment, whenever the addition to the FlowPane doesn't fit its initial height, the content is added, but not visible. The scrollbar belonging to the ScrollPane never adjusts its height - if it did, I could just scroll further down and see the content.
Let's say I have a ScrollPane with some width and height, some viewport width/height, and a FlowPane with some width/height - What should my settings be for the min/pref/max sizes? How can I make a scrollPane adjust its scrollbar behaviour or make the content visible?
The ScrollPane's setFitToHeight is already set to true, which didn't seem to change anything.
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class FlowPaneTest extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception
{
// borderPane rootPane
BorderPane borderPane = new BorderPane();
borderPane.setMinSize(600, 600);
borderPane.setPrefSize(600, 600);
borderPane.setMaxSize(600, 600);
// container for the two scrollPanes below
FlowPane flow = new FlowPane();
borderPane.setRight(flow);
// two scrollPanes, each should resize it's height (width should be fixed) if
// children are added beyond it's current height
ScrollPane top = new ScrollPane();
ScrollPane bottom = new ScrollPane();
FlowPane scrollPaneContent = new FlowPane();
top.setContent(scrollPaneContent);
bottom.setContent(scrollPaneContent);
flow.getChildren().add(top);
flow.getChildren().add(bottom);
borderPane.setOnMouseClicked(new EventHandler<Event>()
{
#Override
public void handle(Event event)
{
Label l = new Label("test");
l.setMinSize(100, 100);
l.setPrefSize(100, 100);
l.setMaxSize(100, 100);
scrollPaneContent.getChildren().add(l);
}
});
// size settings
int width = 300, height = 300;
top.setHvalue(0.5);
top.setMinViewportHeight(height);
top.setPrefViewportHeight(height);
top.setMinViewportWidth(width);
top.setPrefViewportWidth(width);
top.setHbarPolicy(ScrollBarPolicy.ALWAYS);
top.setFitToHeight(true);
top.setMinSize(width, height);
top.setPrefSize(width, height);
top.setMaxWidth(width);
scrollPaneContent.setMinSize(width, height);
scrollPaneContent.setPrefSize(width, height);
scrollPaneContent.setMaxWidth(width);
scrollPaneContent.setPrefHeight(height);
bottom.setMinSize(width, height);
bottom.setPrefSize(width, height);
bottom.setMaxWidth(width);
bottom.setHvalue(0.5);
bottom.setMinViewportHeight(height);
bottom.setPrefViewportHeight(height);
bottom.setMinViewportWidth(width);
bottom.setPrefViewportWidth(width);
bottom.setHbarPolicy(ScrollBarPolicy.ALWAYS);
top.setFitToHeight(true);
bottom.setFitToHeight(true);
// stage
Scene scene = new Scene(borderPane, 600.0, 600.0);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Try to give the ScrollPane pref height and width and add this line
scrollPane.setFitToWidth(true);
Ending up with something similar to this ugly bit of code - It listens to the number of children in the pane and increases the size every time something is added to the list of children:
topSubPane.getChildren().addListener(new ListChangeListener()
{
#Override
public void onChanged(Change c)
{
c.next();
topSubPane.setPrefHeight(topSubPane.getHeight() + 50);
}
});
Works, but feels like an unorthodox hack. Is there really no regular way of doing this?
I'm having some difficulty with ScrollPane in JavaFX 8 showing the scrollbar as needed. What I'm currently doing is simply creating a FlowPane with x number of elements, and setting that as the content of the ScrollPane.
The problem happens when I shrink down perpendicular to the orientation of the FlowPane. When elements begin to wrap and go out of bounds, the scrollbar does not appear. This does not happen when I shrink parallel to the orientation. I have a small Java program to exemplify the issue.
Start
Shrinking Parallel
Shrinking Perpendicular
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane();
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
scroll.setStyle("-fx-border-color: green");
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for(int i = 0; i < panes; i++) {
StackPane filler = new StackPane();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
Have a look at the code below and tell me if that's what you want to achieve. I am still not sure what cause the problem, I will have to look the documentation of ScrollPane to find out. My suspicion is at setFitToWidth & setFitToHeight methods. Although I still believe it's not a bug.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane();
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
scroll.setStyle("-fx-border-color: green");
// Apparently this cause the issue here.
// scroll.setFitToHeight(true);
// scroll.setFitToWidth(true);
// Instead just make the flow pane take the dimensions of the ScrollPane
// the -5 is to not show the Bars when both of panes have the same dimensions
flow.prefWidthProperty().bind(Bindings.add(-5, scroll.widthProperty()));
flow.prefHeightProperty().bind(Bindings.add(-5, scroll.heightProperty()));
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for (int i = 0; i < panes; i++) {
HBox filler = new HBox();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
Looking documentation of the ScrollPane, and in specific the setFitToHeight you will find that :
Property description:
If true and if the contained node is a
Resizable, then the node will be kept resized to match the height of
the ScrollPane's viewport. If the contained node is not a Resizable,
this value is ignored.
And because the node inside the ScrollPane will be kept resized to match the width and height of the ScrollPane's viewport thats why the Vertical ScrollBar will never appear.
You can add the code below to always show your vertical scrollbar.
scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
When the required height of the FlowPane inside the ScrollPane is calculated a width value of -1 is passed. The flow pane will then report the height required when all its content fits into a single line.
As a workaround you could pass the width from the last layout calculation in this case.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flow = new FlowPane() {
#Override protected double computeMinHeight(double width) {
double minHeight = super.computeMinHeight(width != -1 ? width :
/* When no width is specified, use the current contol size*/
getWidth());
return minHeight;
}
};
flow.setStyle("-fx-border-color: red");
addPanes(flow, 16);
ScrollPane scroll = new ScrollPane(flow);
flow.maxWidthProperty().bind(scroll.widthProperty());
scroll.widthProperty().addListener((observable, oldValue, newValue)->{
/* clearSizeCache */
flow.requestLayout();
});
scroll.setStyle("-fx-border-color: green");
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
Scene scene = new Scene(scroll, 450, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
public void addPanes(FlowPane root, int panes) {
for(int i = 0; i < panes; i++) {
StackPane filler = new StackPane();
filler.setStyle("-fx-border-color: black");
filler.setPrefSize(100, 100);
root.getChildren().add(filler);
}
}
}
I'm writing a GUI application with a ScrollPane, but had some issues with resizing. I extracted the essential code in the following example:
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import javafx.scene.layout.VBox;
import javafx.scene.control.ScrollPane;
public class JavaFXExample extends Application {
final int width = 300;
final int height = 300;
#Override
public void start(Stage primaryStage) {
Button b = new Button("This should be at the bottom!");
//this vbox goes inside the scrollpane
VBox boxInScrollPane = new VBox(10);
boxInScrollPane.setAlignment(Pos.BOTTOM_CENTER);
boxInScrollPane.getChildren().add(b);
//main content
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(boxInScrollPane);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
//Doesn't do anything!
scrollPane.setPrefSize(100, 100);
scrollPane.setMaxSize(100, 100);
scrollPane.setMinSize(100, 100);
Scene scene = new Scene(scrollPane, width, height);
primaryStage.setScene(scene);
primaryStage.show();
//set size of boxInScrollPane to be equal to the viewport
Bounds viewportBounds = scrollPane.getViewportBounds();
double innerWidth = viewportBounds.getMaxX() - viewportBounds.getMinX();
double innerHeight = viewportBounds.getMaxY() - viewportBounds.getMinY();
System.out.println(innerWidth + " " + innerHeight);
boxInScrollPane.setPrefSize(innerWidth, innerHeight);
}
public static void main(String[] args) {
launch(args);
}
}
So I have a window, which contains a ScrollPane, which contains a VBox, which contains a button. The example here, where I resize the scrollpane to be 100x100px in a 300x300px window, is arbitrary. What's important is that when I run this code, I get a scrollpane that fills the entire window! Here's my output:
What's going on here?
The root of the scene is sized to fill the entire scene, irrespective of its min/pref/max size. If you want the ScrollPane to remain 100 pixels wide and 100 pixels high, wrap it in another container (pretty much any container will do); the container will then be resized, but the ScrollPane will respect its layout sizes:
Scene scene = new Scene(new StackPane(scrollPane), width, height);
By default a StackPane centers its content, so this results in
Solution:
Pane pane = new Pane(scrollPane);
Scene scene = new Scene(pane, width, height);
primaryStage.setScene(scene);
primaryStage.show();
From Scene constuctor doc:
Creates a Scene for a specific root Node with a specific size.
Setting ScrollPane as root node will make it resize to given size in constructor so the previous settings will not work.
Solution will be to make a simple pane that will be resized so ScrollPane will be on his own rules.
I have a JavaFx VBox inside of a ScrollPane:
VBox container = new VBox();
container.setAlignment(Pos.CENTER);
...
scrollPane.setContent(container);
scrollPane.setFitToWidth(true);
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setMinWidth(150);
scrollPane.setPannable(true);
the size of this VBox never change, inside i have some labels, one label has an user image
like the next image(A)
the image is resized to some Height, but i don't know the size of this image, so if the width of the image is bigger than width of the VBox, this happen(part of the image hidden)(B)
but i don't want this, i want something like the following image:(the sides of the image hidden if the image width is bigger than the VBox width)(C)
http://i.stack.imgur.com/B3DOK.png
How i can do this?
I tried to put a rectangle as clip, in this rectangle i wanna show the center of the image, but the same happens.
imageView.setClip(new Rectangle(centerX - recSize, centerY - recSize, recSize*2, recSize*2));
---------------with the clip----------------
red = original image
blue = part of the image that is visible
http://i.stack.imgur.com/mYbyF.png
Nice(D)
Not nice:(E)(labels not centered correctly because of the image.)
Sorry by the links, i can't put the images directly
Solution
Set a viewport on the image not a clip.
imageView.setViewport(
new Rectangle2D(500, 320, 420, 300)
);
Sample
Here is a sample. It's not going to exactly match what you are asking for because even with the linked images in your question, I can't quite understand what you are trying to do. But I think it should give you enough background info that you can learn to accomplish what you want.
The sample creates an image view as a graphic in a scroll pane. The image view applies a viewport to the image and scales the viewport with preserved ratio. This allows a scaled portion of the much larger image to be displayed. It's kind of like a thumbnail clip (click on the thumbnail to display the full image).
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
import javafx.stage.*;
// display a captioned image in a viewport.
// click the image to get an expanded view.
public class LabelWithImage extends Application {
private static final double IMAGE_WIDTH = 150;
#Override
public void start(Stage stage) {
Image image = new Image(IMAGE_LOC);
ImageView imageView = new ImageView(
image
);
imageView.setViewport(
new Rectangle2D(500, 320, 420, 300)
);
imageView.setFitWidth(IMAGE_WIDTH);
imageView.setPreserveRatio(true);
Label labeledImage = createCaptionedImage(
imageView,
"Village Home"
);
addGlowOnMouseOver(labeledImage);
labeledImage.setOnMouseClicked(event -> {
displayFullImage(stage, image);
});
VBox vbox = new VBox( // vbox just there to mimic question askers structure.
labeledImage
);
vbox.setPadding(new Insets(10));
ScrollPane scrollPane = makeScrollable(vbox);
Scene scene = new Scene(
scrollPane
);
stage.setScene(scene);
stage.show();
stage.setMaxWidth(stage.getWidth());
stage.setMaxHeight(stage.getHeight());
}
private void displayFullImage(Stage stage, Image image) {
Stage displayStage = new Stage();
displayStage.initStyle(StageStyle.UTILITY);
displayStage.initModality(Modality.APPLICATION_MODAL);
displayStage.initOwner(stage);
displayStage.setScene(
new Scene(
new Group(
new ImageView(
image
)
)
)
);
displayStage.show();
}
private void addGlowOnMouseOver(Node node) {
Glow glow = new Glow();
DropShadow shadow = new DropShadow(20, Color.GOLD);
glow.setInput(shadow);
node.setOnMousePressed(event -> node.setEffect(null));
node.setOnMouseEntered(event -> node.setEffect(glow));
node.setOnMouseExited(event -> node.setEffect(null));
}
private ScrollPane makeScrollable(Node node) {
ScrollPane scrollPane = new ScrollPane(node);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setPannable(true);
return scrollPane;
}
private Label createCaptionedImage(ImageView imageView, String caption) {
Label labeledImage = new Label(caption);
labeledImage.setFont(Font.font("Athelas", FontPosture.ITALIC, 20));
labeledImage.setStyle("-fx-background-color: cornsilk");
labeledImage.setPadding(new Insets(0, 0, 5, 0));
labeledImage.setGraphic(
imageView
);
labeledImage.setContentDisplay(ContentDisplay.TOP);
return labeledImage;
}
public static void main(String[] args) {
launch(args);
}
private static final String IMAGE_LOC =
"http://www.imgion.com/images/01/beautiful-village-home.jpg";
// image courtesy of http://www.imgion.com which provides
// "free images on large topics to share with your friends and on your blogs."
}