JavaFX - Child Nodes Overlapping Parent Node (Parent's width smaller than children) - javafx

Below there is a quick sample of a JavaFX application where the brown region (parent node) contains two child nodes, a red square and a blue circle. When I am reducing the width of the parent node to a size smaller than those of its children, I was expecting the child nodes to be partially visible. However, this is not the case but instead the child nodes are shown fully over parent's region. Any ideas on how to achieve that on the below example?
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Parent Child Relationship!");
ParentNode parentNode = new ParentNode();
StackPane root = new StackPane();
root.getChildren().add(parentNode);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
class ParentNode extends Region {
private Rectangle square = new Rectangle();
private Circle circle = new Circle();;
public ParentNode() {
square.setWidth(40);
square.setHeight(40);
square.setFill(Color.RED);
circle.radiusProperty().bind(square.heightProperty().divide(3));
circle.centerXProperty().bind(circle.radiusProperty());
circle.centerYProperty().bind(square.heightProperty().divide(2));
circle.setFill(Color.BLUE);
circle.setStroke(Color.LIGHTGRAY);
getChildren().addAll(square, circle);
setBackground(new Background(new BackgroundFill(Color.CHOCOLATE, null, null)));
this.setMaxHeight(100);
this.setMaxWidth(200);
this.setMinHeight(0);
this.setMinWidth(0);
this.setOnMousePressed((e) -> this.setMaxWidth(20));
}
}

The only way i can think of, would be using a rectangle as the clip for the parentNode and binding its height and width to the parentNode's width and height properties, here is a working example:
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Parent Child Relationship!");
ParentNode parentNode = new ParentNode();
parentNode.maxWidthProperty().bind(primaryStage.widthProperty().subtract(200));
Rectangle clip = new Rectangle();
clip.widthProperty().bind(parentNode.widthProperty());
clip.heightProperty().bind(parentNode.heightProperty());
parentNode.setClip(clip);
StackPane root = new StackPane();
root.getChildren().add(parentNode);
primaryStage.setScene(new Scene(root, 400, 250));
primaryStage.show();
}
(i bound the parentNode's width to the windows width just so you see it working while you resize the window)

Related

Frosted Glass Clipping in JavaFX

I'm trying to achieve this effect here but on a static background (without the scrolling). I'm getting this weird clip on my results though (without the frost effect). I think I know where the problem is in my code but I'm not sure how to solve it.
public class App extends Application {
private static final double BLUR_AMOUNT = 80;
private static final Effect frostEffect = new BoxBlur(BLUR_AMOUNT, BLUR_AMOUNT, 3);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
ImageView background = new ImageView(bgImage);
StackPane centerBlurredPane = (StackPane) frozenCenterUI();
BorderPane centerContent = new BorderPane();
centerContent.setCenter(new Text("Center"));
centerContent.setTop(new Text("Top"));
centerContent.setLeft(new Text("Left"));
centerContent.setRight(new Text("Right"));
centerBlurredPane.getChildren.add(centerContent);
Scene scene = new Scene(new StackPane(background,centerBlurredPane),414, 849);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node frozenCenterUI() {
Image frostImage = background.snapshot(new SnapshotParameters(), null);
ImageView frost = new ImageView(frostImage);
Rectangle filler = new Rectangle(24, 762, 366, 696);
filler.setArcHeight(50);
filler.setArcWidth(50);
filler.setFill(Color.AZURE);
Pane frostPane = new Pane(frost);
frostPane.setEffect(frostEffect);
StackPane frostView = new StackPane(filler, frostPane);
Rectangle clipShape = new Rectangle(24, 762, 366, 696);
frostView.setClip(clipShape);
return frostView;
}
}
This is what i'm getting, however. I want to apply frost on the white area here.
What should my clip shape be?
Here's my background image, backgroundImage
Edit: I found that if I change my clipShape to have the same dimensions as the scene, then I get the desired effect. However, the BorderPane I added to the frozen pane is not constrained to it, but actually stretches and fills the entire window.

How to make a GridPane right of BorderPane closeable?

I have code in the form:
<BorderPane>
...
<right>
<GridPane>
...
</GridPane>
</right>
...
</BorderPane>
Obviously, now the GridPane takes a big space right of my BorderPane. What I'd like to do is add a button (or another element) that minimizes and maximizes the GridPane, so it's only fully in the view of the user when it is really needed. How can I easily achieve this?
You can do what you want by setting the Visible and Managed properties of your GridPane off and on. The centre of the BorderPane will automatically expand to take over the entire width of the BorderPane. "Managed" controls whether or not the layout manager will leave space for the node, so if you just turn Visible off, then you'll have an unused area the size of your GridPane on the right. The following code demonstrates it, I put the buttons in a VBox with a border around it so that you can see how it expands:
public class ResizeRight extends BorderPane {
public ResizeRight() {
Button openButton = new Button("Open");
Button closeButton = new Button("Close");
GridPane gridPane = new GridPane();
gridPane.addRow(0, new Text("This is just some text"));
gridPane.addRow(1, new Text("This is just some more text"));
VBox vbox = new VBox(10, openButton, closeButton);
vbox.setBorder(new Border(new BorderStroke(Color.BLACK,
BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
setCenter(vbox);
setRight(gridPane);
setPadding(new Insets(10));
openButton.setOnAction(evt -> {
gridPane.setVisible(true);
gridPane.setManaged(true);
});
closeButton.setOnAction(evt -> {
gridPane.setVisible(false);
gridPane.setManaged(false);
});
}
}
Run it from something like this:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new ResizeRight(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}

JavaFX listener to a visible part of VBox

I have a JavaFX VBox inside a BorderPane (central). The content of the VBox is calculated using some business logic and it depends on the height of the visible part of the vbox.
So basically I need a listener watching changes of the visible height of the vbox = height of the central part of the border pane.
The following code demonstrates what I have tried:
public class HelloFX extends Application {
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
vbox.boundsInParentProperty()
.addListener((obs, oldValue, newValue) ->
System.out.println(newValue.getHeight()));
Button button = new Button("ADD LINE");
button.setPrefHeight(25);
button.setOnAction(event ->
vbox.getChildren().add(new Label("line")));
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
borderPane.setTop(button);
Scene scene = new Scene(borderPane, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
BorderPane with simple button on the top position and VBox on the central. The button click adds one line to vBox. Total scene height is 100, 25 is the button height and the rest (75) is the vBox.
I'm looking for some listener to report changes of the height of the central part of border pane. So in my example it should always print "75" no matter how many lines I have added to the vBox. The only event changing the value should be resizing the whole window. In reality once the vBox is filled my listener reports increasing height values. Apparently the height property includes the invisible part of the vbox.
EDIT
Finally I've found some solution - placing the vBox in the ScrollPane with disabled scrollbars. Then I can simply listen on the height property of the scrollpane and everything works as expected.
public class HelloFX extends Application {
#Override
public void start(Stage primaryStage) {
VBox vbox = new VBox();
ScrollPane scrollPane = new ScrollPane();
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.setContent(vbox);
scrollPane.heightProperty()
.addListener((obs, oldValue, newValue) ->
System.out.println(newValue));
Button button = new Button("ADD LINE");
button.setPrefHeight(25);
button.setOnAction(event ->
vbox.getChildren().add(new Label("line")));
BorderPane borderPane = new BorderPane();
borderPane.setCenter(scrollPane);
borderPane.setTop(button);
Scene scene = new Scene(borderPane, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}

How to make pane and elements in it resizable when stage size is changing

Can you please tell me how can I realize that the whole content of a pane will be resized while the stage is resized with mousedragg. Here is my code:
public class fab extends Application {
private Stage stage;
private Pane pane;
private Scene scene;
#Override
public void start(Stage stage) throws Exception {
this.stage = stage;
Button button = new Button("Button");
pane = new Pane();
pane.getChildren().add(button);
stage.setTitle("Test");
scene = new Scene(pane, 640, 640);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
I think there is the idea of binding. But I don't know how to use that, in order to make all nodes of a pane resizable, when the stage size is changing.
I'm searching a solution without Fxml or sceneBuilder.
Thank you in advance.
If you insists to use the Pane container then after the line scene = new Scene(pane, 640, 640); add this:
scene.widthProperty().addListener((c,o,n)->button.setPrefWidth((Double)n));
scene.heightProperty().addListener((c,o,n)->button.setPrefHeight((Double)n));
and after the line stage.setScene(scene); add this:
button.setPrefSize(scene.getWidth(), scene.getHeight());
This works fine with Pane and do your required thing.
But I prefer using an AnchorPane container and set the Top, Right,Bottom and Left anchors to 0 .
Here is the solution if you wish to bind the width of the button to you scene width
button.minWidthProperty().bind(scene.widthProperty());
You can also modify this +/- whatever you want for ex
button.minWidthProperty().bind(scene.widthProperty().subtract(20));
and you can do the same for the height
button.minHeightProperty().bind(scene.heightProperty().subtract(200));

How to keep angle of DropShadow when node is rotated

When you apply a DropShadow on a node which is rotated, then the DropShadow rotates with it. Is there a simple way to keep the DropShadow angle where it is, e. g. bottom right even when the node is rotated?
I know that it would work if I put all the nodes into a group and apply the shadow on the group, but that's unfortunately not an option in my case.
Sample image:
left rectangle with drop shadow
right rectangle with same drop shadow, but rotated by 180 degrees
You see, it looks wrong with the shadows being in opposite directions.
Code
public class HelloEffects extends Application {
Stage stage;
Scene scene;
#Override
public void start(Stage stage) {
Group group = new Group();
DropShadow ds1 = new DropShadow();
ds1.setOffsetY(4.0f);
ds1.setOffsetX(4.0f);
ds1.setColor(Color.BLACK);
Rectangle rect1 = new Rectangle( 100, 200);
rect1.relocate(100, 100);
rect1.setEffect(ds1);
rect1.setFill(Color.RED);
Rectangle rect2 = new Rectangle( 100, 200);
rect2.relocate(300, 100);
rect2.setEffect(ds1);
rect2.setFill(Color.RED);
rect2.setRotate(180);
group.getChildren().addAll(rect1, rect2);
scene = new Scene( group, 840, 680);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
You should add a container to rect2 and apply the effect on the container, the container can be either Pane or Group:
Group rect2Container = new Group(rect2);
rect2Container.setEffect(ds1);
group.getChildren().addAll(rect1, rect2Container);

Resources