Javafx darken background - javafx

I have FXML application with 10 circles in AnchorPane. I want to hover mouse on one circle and make other 9 and background to darken.
The best I could do was some basic FadeTransition, which only made them disappear, not darken, plus I cant figure out how to select all children of node except one that I have mouse on. Selecting all children except one manually seems not really efficient for more objects.
I tried to google it up, but I just cant find anything.
Please, post a link to thread related to similar problem or sample code. Any help would be really appreciated.

You can use the following sample. Please note that there are some assumptions made, such as every node in the scene graph is a Shape object and that every shape has a Color object associated with the fill. The sample code is sufficient to derive other solutions related specifically to your use case.
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class SelectionApp extends Application {
private Pane root = new Pane();
private Parent createContent() {
root.setPrefSize(800, 600);
root.getChildren().add(new Rectangle(800, 600, Color.AQUA));
for (int i = 0; i < 10; i++) {
Circle circle = new Circle(25, 25, 25, Color.GREEN);
// just place them randomly
circle.setTranslateX(Math.random() * 700);
circle.setTranslateY(Math.random() * 500);
circle.setOnMouseEntered(e -> select(circle));
circle.setOnMouseExited(e -> deselect(circle));
root.getChildren().add(circle);
}
return root;
}
private void select(Shape node) {
root.getChildren()
.stream()
.filter(n -> n != node)
.map(n -> (Shape) n)
.forEach(n -> n.setFill(darker(n.getFill())));
}
private void deselect(Shape node) {
root.getChildren()
.stream()
.filter(n -> n != node)
.map(n -> (Shape) n)
.forEach(n -> n.setFill(brighter(n.getFill())));
}
private Color darker(Paint c) {
return ((Color) c).darker().darker();
}
private Color brighter(Paint c) {
return ((Color) c).brighter().brighter();
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
primaryStage.setTitle("Darken");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Related

JavaFX Alert with multiple colors

I have a program that at some point (may) displays two warnings - one about errors - those are in red, and one about warnings - those are in orange.
I wonder however if there is a way - using css - to have just one warning with some text red and some text orange.
Here is an example of what I want to achieve (the two can be separated into "sections"):
RED ERROR1
RED ERROR2
RED ERROR3
ORANGE WARNING1
ORANGE WARNING2
I've seen some answers pointing to RichTextFX like this one, however I don't see (or don't know) how that could apply to generic Alerts. Is that even possible, without writing some custom ExpandedAlert class?
The Alert class inherits from Dialog, which provides a pretty rich API and allows arbitrarily complex scene graphs to be set via the content property.
If you just want static text with different colors, the simplest approach is probably to add labels to a VBox; though you could also use more complex structures such as TextFlow or the third-party RichTextFX mentioned in the question if you need.
A simple example is:
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
private final Random rng = new Random();
private void showErrorAlert(Stage stage) {
Alert alert = new Alert(Alert.AlertType.ERROR);
int numErrors = 2 + rng.nextInt(3);
int numWarnings = 2 + rng.nextInt(3);
VBox errorList = new VBox();
for (int i = 1 ; i <= numErrors ; i++) {
Label label = new Label("Error "+i);
label.setStyle("-fx-text-fill: red; ");
errorList.getChildren().add(label);
}
for (int i = 1 ; i <= numWarnings ; i++) {
Label label = new Label("Warning "+i);
label.setStyle("-fx-text-fill: orange; ");
errorList.getChildren().add(label);
}
alert.getDialogPane().setContent(errorList);
alert.initOwner(stage);
alert.show();
}
#Override
public void start(Stage stage) {
Button showErrors = new Button("Show Errors");
showErrors.setOnAction(e -> showErrorAlert(stage));
BorderPane root = new BorderPane(showErrors);
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
which gives this result:

JavaFx Overlapping mouse events

I’m trying to build a board game interface where the user can switch between multiple eras, each one with its own board. To do so, I’m creating 4 different board, each within its own pane, and I’m toggling the nodes Visibility and disabling the nodes that aren’t being used. The problem I have is the mouse event handlers I’m using to see where the user is clicking only work on the top layer, the last one that was rendered. The event Handlers underneath don’t work even if they are enabled.
Here’s what I wrote:
static EventHandler<MouseEvent> eventMouseClickRoad = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
final Shape innerShape = (Shape) (e.getTarget());
System.out.println("click");
Color color = (Color) innerShape.getFill();
if(color.getOpacity() != 1)
{
innerShape.setFill(Color.RED);
//and do the data treatment
}
}
};
public void boardControler(Vector2DList sideList,PointList hexEdge,Pane groupPane,float scaleX, float scaleY, float buttonSize)
{
//set road button
for(Vector2D v : sideList.getVectorList()){
Path mypath = new Path(new MoveTo(v.getP1().getX(),v.getP1().getY()),new LineTo(v.getP2().getX(),v.getP2().getY()));
groupPane.getChildren().add(mypath);
}
for(Vector2D v : sideList.getVectorList()){
float midX=(v.getP1().getX()+v.getP2().getX())/2;
float diffY=v.getP1().getY()-v.getP2().getY();
float diffX=v.getP1().getX()-v.getP2().getX();
Rectangle rectangle = new Rectangle(midX-buttonSize/2,midY-Math.abs(diffY)+buttonSize+(Math.abs(diffY)-scaleY/4),buttonSize,(scaleY/2)-(buttonSize*2));
rectangle.setRotate(Math.toDegrees(Math.atan(diffY/diffX))+90);
rectangle.setFill(Color.TRANSPARENT);
rectangle.addEventFilter(MouseEvent.MOUSE_ENTERED, Event.eventMouseEntered);
rectangle.addEventFilter(MouseEvent.MOUSE_EXITED, Event.eventMouseExit);
rectangle.addEventFilter(MouseEvent.MOUSE_CLICKED, Event.eventMouseClickRoad);
groupPane.getChildren().add(rectangle);
}
}
And this is what i use to toggle the board that's being used:
to disable
for(Node n : groupPane2.getChildren())
{
n.setDisable(true);
n.setManaged(false);
n.setVisible(false);
}
to enable
for(Node n : groupPane2.getChildren())
{
n.setDisable(false);
n.setManaged(true);
n.setVisible(true);
}
Perhaps using a StackPane would be the solution here. Your question doesn't include much code to show all of your context, but the MCVE below may help to demonstrate the idea.
Basically, we create a StackPane as our root display container for all of your boards. Your "boards" can be anything, a Pane, another StackPane, or a VBox like in my example. This should allow you to continue using whatever layout system you currently are.
One thing to note, it appears that each board will need to have a background set, or the lower boards will show through and may accept mouse events.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StackPaneSample extends Application {
public static void main(String[] args) {
launch(args);
}
private static StackPane stackPane = new StackPane();
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Create our StackPane
stackPane.setStyle("-fx-border-color: black");
VBox.setVgrow(stackPane, Priority.ALWAYS);
// Let's create 3 "boards" for our StackPane. A background color seems necessary to hide layers below the top one
VBox board1 = new VBox() {{
setStyle("-fx-background-color: whitesmoke");
setAlignment(Pos.CENTER);
setUserData("Board #1");
getChildren().add(new Label((String) getUserData()));
}};
VBox board2 = new VBox() {{
setStyle("-fx-background-color: whitesmoke");
setAlignment(Pos.CENTER);
setUserData("Board #2");
getChildren().add(new Label((String) getUserData()));
}};
VBox board3 = new VBox() {{
setStyle("-fx-background-color: whitesmoke");
setAlignment(Pos.CENTER);
setUserData("Board #3");
getChildren().add(new Label((String) getUserData()));
}};
stackPane.getChildren().add(board1);
stackPane.getChildren().add(board2);
stackPane.getChildren().add(board3);
// Create three buttons that will switch between the boards
Button btnBoard1 = new Button("Board #1");
Button btnBoard2 = new Button("Board #2");
Button btnBoard3 = new Button("Board #3");
HBox hbButtons = new HBox(20) {{
setAlignment(Pos.CENTER);
setPadding(new Insets(5));
getChildren().addAll(btnBoard1, btnBoard2, btnBoard3);
}};
// Finish out layout
root.getChildren().addAll(
stackPane,
new Separator(Orientation.HORIZONTAL),
hbButtons
);
// ** Now let's add our functionality **
// Print out which board has been clicked upon
// We need to first cast our List to VBox
for (Node vbox : stackPane.getChildren()) {
vbox.setOnMouseClicked(event -> System.out.println("Clicked on " + vbox.getUserData()));
}
// Set the buttons to set the top board
btnBoard1.setOnAction(event -> selectBoard(board1));
btnBoard2.setOnAction(event -> selectBoard(board2));
btnBoard3.setOnAction(event -> selectBoard(board3));
// Show the Stage
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
// Method to remove the board and readd it, placing it on top of all others.
private static void selectBoard(VBox board) {
stackPane.getChildren().remove(board);
stackPane.getChildren().add(board);
}
}
The Result:
I am, admittedly, not familiar with the Cartesian coordinates you mention in your comment, so perhaps this won't work for you. Adding more code/context to your question might help us narrow down the issue better.

JavaFX 8 Changing the opacity of the stage does not work with StageStyle.TRANSPARENT (bug or my fault?)

I'm trying to build a FadeIn/Out animation on a window (stage). If the mouse moves into the stage it should fade in and if the mouse leaves it should fade out.
I created a Timeline that modifies the stage.opacityProperty() to achieve this. I ran into problems when I set the stage style transparent like this stage.initStyle(StageStyle.TRANSPARENT);. If I do so, the fading will not be visible. The Timeline plays the animation, but the opacity change will not be rendered by JavaFX. When setting the stageStyle to default, everything works fine and the window plus its decoration will fade in and out.
I want this effect to work in TRANSPARENT stage style so i tried the following:
I put a label onto the scene and change its textproperty in another Timeline. I now update the label text every 400msecs. If i do so, the opacity change will be rendered on every label-change.
This brings me to the conclusion, that modifying the opacity in TRANSPARENT stage style, will not result in a repaint of the stage.
Modifying the label text will result in repaint. Does this mean, that i cannot fade a stage in TRANSPARENT stage style, if the content does not change?
Is this a bug or am I doing something wrong?
I've made an SSCCE that reproduces the problem. If you remove the line stage.initStyle(StageStyle.TRANSPARENT); the fadeIn/out animation will run smoothly.
package de.schuette.jfx.stage_opacity_bug;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class FadeApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
private Label label;
#Override
public void start(Stage stage) {
if (stage == null)
throw new IllegalArgumentException("No stage was set.");
this.label = new Label("HALLO WELT");
Scene scene = new Scene(label, 300, 300);
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
stage.setScene(scene);
stage.setOpacity(1);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setTitle("Opacity change does result in repaint when stage style is transparent.");
stage.setAlwaysOnTop(true);
stage.show();
Platform.runLater(() -> {
Timeline t = new Timeline(new KeyFrame(Duration.millis(0),
new KeyValue(stage.opacityProperty(), 1)), new KeyFrame(
Duration.millis(500), new KeyValue(stage.opacityProperty(),
0)));
t.setAutoReverse(true);
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
});
Platform.runLater(() -> {
Timeline t = new Timeline(new KeyFrame(Duration.millis(400), e -> {
label.textProperty().set(String.valueOf(Math.random()));
}));
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
});
}
}
I'm currtently working with
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Windows 7 x64 Professional
With the help of the JavaFX developer team I was able to find a workaround for this problem. Using a custom linear interpolator that changes the scene's fill property and immediately change it back to its original value will cause a repaint on the stage. This is done by the "bugFixInterpolator" in the code below:
package de.schuette.jfx.stage_opacity_bug;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class FadeApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
private Label label;
/*
* (non-Javadoc)
*
* #see javafx.application.Application#start(javafx.stage.Stage)
*/
#Override
public void start(Stage stage) {
if (stage == null)
throw new IllegalArgumentException("No stage was set.");
this.label = new Label("HELLO WORLD");
Scene scene = new Scene(label, 300, 300);
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
stage.setScene(scene);
stage.setOpacity(1);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setTitle("Opacity change does result in repaint when stage style is transparent.");
stage.setAlwaysOnTop(true);
stage.show();
Interpolator bugFixInterpolator = new Interpolator() {
#Override
protected double curve(double t) {
Paint fill = scene.getFill();
scene.setFill(Color.RED);
scene.setFill(fill);
return t;
}
#Override
public String toString() {
return "Interpolator.LINEAR";
}
};
Timeline t = new Timeline(new KeyFrame(Duration.millis(0),
new KeyValue(stage.opacityProperty(), 1, bugFixInterpolator)),
new KeyFrame(Duration.millis(500), new KeyValue(stage
.opacityProperty(), 0, bugFixInterpolator)));
t.setAutoReverse(true);
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
t = new Timeline(new KeyFrame(Duration.millis(400), e -> {
label.textProperty().set(String.valueOf(Math.random()));
}));
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
}
}

Make JavaFX wait and continue with code

Basically I am trying to make a short effect using JavaFX. I have the shape of a heart (added together from two circles and a polygon) that I can vary in size using the double value p. "Standart Size" would be p = 1.0;.
I am trying to add a pumping effect to the heart. I have the method pumpOnce():
public void pumpOnce(){
p = p + 1;
initHeart();
//Here goes what ever it takes to make stuff working!!
p = p - 1;
initHeart();
}
initHeart() draws the heart based on p.
I have found out that Thread.sleep(); or similar methods will not work due to the thread philosophy in JavaFX.
But what can I use instead?
The JavaFX animations are probably the way to go, but the "thread philosophy" in JavaFX isn't hard to work with if you want to roll your own, or do other, more complicated things in background threads.
The following code will pause and change the value in a label (full disclosure, I'm reusing code I wrote for another question):
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.xml.datatype.Duration;
public class DelayWithTask extends Application {
private static Label label;
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
label = new Label();
label.setText("Waiting...");
StackPane root = new StackPane();
root.getChildren().add(label);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
delay(5000, () -> label.setText("Hello World"));
}
public static void delay(long millis, Runnable continuation) {
Task<Void> sleeper = new Task<Void>() {
#Override
protected Void call() throws Exception {
try { Thread.sleep(millis); }
catch (InterruptedException e) { }
return null;
}
};
sleeper.setOnSucceeded(event -> continuation.run());
new Thread(sleeper).start();
}
}
The basic JavaFX background tool is the Task, any JavaFX application that actually does anything will probably be littered with these all over. Learn how to use them.
Dave's solution is great for general purpose off thread based work in JavaFX.
If you wish to use the animation facilities of JavaFX, the solutions below demonstrate this using a Timeline or a ScaleTransition. The timeline implements a discrete scale of the UI element, so every quarter of a second the UI element is scaled larger or back to it's original size. The scale transition implements a smooth scale of the UI element, so the UI element gradually gets larger then smaller using an interpolated scale factor with the default easing interpolator.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BeatingHeart extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
ImageView heart = new ImageView(HEART_IMAGE_LOC);
animateUsingTimeline(heart);
// animateUsingScaleTransition(heart);
StackPane layout = new StackPane(heart);
layout.setPrefWidth(heart.getImage().getWidth() * 2);
layout.setPrefHeight(heart.getImage().getHeight() * 2);
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private void animateUsingTimeline(ImageView heart) {
DoubleProperty scale = new SimpleDoubleProperty(1);
heart.scaleXProperty().bind(scale);
heart.scaleYProperty().bind(scale);
Timeline beat = new Timeline(
new KeyFrame(Duration.ZERO, event -> scale.setValue(1)),
new KeyFrame(Duration.seconds(0.5), event -> scale.setValue(1.1))
);
beat.setAutoReverse(true);
beat.setCycleCount(Timeline.INDEFINITE);
beat.play();
}
private void animateUsingScaleTransition(ImageView heart) {
ScaleTransition scaleTransition = new ScaleTransition(
Duration.seconds(1), heart
);
scaleTransition.setFromX(1);
scaleTransition.setFromY(1);
scaleTransition.setFromZ(1);
scaleTransition.setToX(1.1);
scaleTransition.setToY(1.1);
scaleTransition.setToZ(1.1);
scaleTransition.setAutoReverse(true);
scaleTransition.setCycleCount(Animation.INDEFINITE);
scaleTransition.play();
}
private static final String HEART_IMAGE_LOC =
"http://icons.iconarchive.com/icons/mirella-gabriele/valentine/128/Heart-red-icon.png";
// icon obtained from: http://www.iconarchive.com/show/valentine-icons-by-mirella-gabriele/Heart-red-icon.html
// icon license: Free for non-commercial use, commercial use not allowed.
}

How to avoid a 'machine gun' effect to the bullets in my game?

Some days ago i asked this:
How to have multiple instances on the screen of the same sprite at the same time with javafx2
and partially solved the question elaborating the suggestion of jewelsea.
I have this obstacle now: when a key is pressed to 'fire' bullets, weapon shoot bullets as fast as a machine gun..
I would like to limit the amount of bullets that the weapon of the hero of my game can shoot..for example to decide to shoot a bullet every 0.5 secs or just when a key is pressed and not to have always a machine gun effect...
In my game the part of program that controls the 'fire' effect is like this:
scene.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event2) {
if (event2.getCode()==KeyCode.F); { .........
Before i've tried also using setOnKeyPressed and setOnKeyReleased with the same results..
So what could i try to shoot just a bullet also keeping press the 'F' key or to limit the bullets in number?
Thank you in advance and good bye!
I've done this by using a Timeline as a timer and starting it and stopping it on key pressed and key released:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class KeyEventTest extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Scene scene = new Scene(root, 400, 400);
Duration firingInterval = Duration.millis(500);
Timeline firing = new Timeline(
new KeyFrame(Duration.ZERO, event -> fire()),
new KeyFrame(firingInterval));
firing.setCycleCount(Animation.INDEFINITE);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.F && firing.getStatus() != Animation.Status.RUNNING) {
firing.playFromStart();
}
});
scene.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.F) {
firing.stop();
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void fire() {
// dummy implementation:
System.out.println("Fire!");
}
public static void main(String[] args) {
launch(args);
}
}
It's fairly easy to adapt this to additionally limit the number of bullets on the screen at any time, etc.

Resources