Is it possible to use a Node as a mouse cursor? I'm thinking in a ProgressIndicator. For example a determinate one, letting the user know how much percentage of the current task is done.
Probably the most reliable way to do this is to set the cursor to Cursor.NONE, and have a label with the progress indicator as its graphic, which tracks the mouse coordinates.
I tried using an ImageCursor which updated, but nothing appeared: I am guessing the images couldn't be computed quickly enough.
Here's an SSCCE of the first technique:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ProgressIndicatorAsCursor extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Start");
Service<Void> service = new Service<Void>() {
#Override
public Task<Void> createTask() {
return new Task<Void>() {
#Override
public Void call() throws Exception {
for (int i = 1 ; i <= 1000; i++) {
Thread.sleep(10);
updateProgress(i, 1000);
}
return null ;
}
};
}
};
button.disableProperty().bind(service.runningProperty());
button.setOnAction(e -> service.restart());
ProgressIndicator pi = new ProgressIndicator();
pi.progressProperty().bind(service.progressProperty());
Pane pane = new Pane();
// fill pane with rectangle as task progresses:
Rectangle rectangle = new Rectangle();
rectangle.setFill(Color.CORNFLOWERBLUE);
rectangle.setX(0);
rectangle.widthProperty().bind(pane.widthProperty());
rectangle.heightProperty().bind(pane.heightProperty().multiply(service.progressProperty()));
rectangle.yProperty().bind(pane.heightProperty().subtract(rectangle.heightProperty()));
pane.getChildren().add(rectangle);
Label label = new Label();
label.graphicProperty().bind(
Bindings.when(service.runningProperty())
.then(pi)
.otherwise((ProgressIndicator)null));
pane.setOnMouseEntered(e ->
pane.getChildren().add(label));
pane.setOnMouseExited(e ->
pane.getChildren().remove(label));
pane.setOnMouseMoved(e -> label.relocate(e.getX(), e.getY()));
pane.cursorProperty().bind(
Bindings.when(service.runningProperty())
.then(Cursor.NONE)
.otherwise(Cursor.DEFAULT));
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
BorderPane root = new BorderPane(pane, new Rectangle(0,0,0,20), null, button, null);
primaryStage.setScene(new Scene(root, 400, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Sees it's impossible, but you could obtain the cursor property and keep updating it with an image as your desire.
Related
I am writing a program that uses a lot of data to do several calculations and it takes quite a while to finish. This is why I want to update and inform the user on the current percentage of the program being done. However I don't know how I can update the stage within the calculation method.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
public void calculate() {
//Here I want to change the "percentage text"
//and update the stage.
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Text percentage = new Text();
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
calculate();
}
});
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(btn, 0, 0);
GridPane.setConstraints(percentage, 1, 0);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().addAll(btn, percentage);
final Pane rootGroup = new VBox(12);
rootGroup.setPrefWidth(400);
rootGroup.setPrefHeight(225);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12,12,12,12));
primaryStage.setScene(new Scene(rootGroup));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This is what my code roughly looks like. Thank you in advance.
JavaFx provides a class called "Task" which can be used for background calculations while keeping the progression syncronised!
I would suggest doing it that way:
Create a second class extending Task for doing the heavy calculation:
import javafx.concurrent.Task;
public class CalculationTask extends Task<String> {
#Override
protected String call() throws Exception {
for (int i = 0; i < 100000000;i++) {
updateProgress(i, 100000000);
}
succeeded();
return "This is the computed result... Datatype can be changed by generics if you are calculation numbers";
}
}
This is one way how you could use it in your application class:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
Task<String> calculationTask = new CalculationTask();
ProgressBar progressBar = new ProgressBar(); //ProgressIndicator could also be used
progressBar.progressProperty().bind(calculationTask.progressProperty());
progressBar.visibleProperty().bind(calculationTask.runningProperty());
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
calculationTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent workerStateEvent) {
new Alert(Alert.AlertType.CONFIRMATION, calculationTask.getValue()).show();
}
});
new Thread(calculationTask).start();
}
});
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(btn, 0, 0);
GridPane.setConstraints(progressBar, 1, 0);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().addAll(btn, progressBar);
final Pane rootGroup = new VBox(12);
rootGroup.setPrefWidth(400);
rootGroup.setPrefHeight(225);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12,12,12,12));
primaryStage.setScene(new Scene(rootGroup));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I hope that helped, otherwise feel free to ask questions!
I am trying to make an application where an object moves across the screen, only changing its x value. I am not getting any errors but it is not working when I run it. the objects are showing but the rectangle is not animating. I am not sure what to do
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private BorderPane root;
private Stage stage;
private Scene scene;
private AnchorPane anchorPane;
private Rectangle player;
private Circle circles;
private Button up, down;
private Timeline timer;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
root = new BorderPane();
scene = new Scene(root, 600, 500); //width and height of application
stage.setScene(scene);
stage.setTitle("Hoop runner"); //text for the title bar of the window
scene.getStylesheets().add("hoop.css");
anchorPane = new AnchorPane();
anchorPane.setMaxHeight(500);
anchorPane.setMinHeight(500);
anchorPane.setMinWidth(600);
anchorPane.setMaxWidth(600);
player = new Rectangle();
circles = new Circle();
up = new Button("˄");
down = new Button("˅");
root.setCenter(new VBox(new VBox(circles),
new HBox(player)));
root.setCenter(anchorPane);
player.setX(5);
player.setY(380);
player.setHeight(80);
player.setWidth(120);
anchorPane.getChildren().add(player);
timer = new Timeline(Animation.INDEFINITE);
timer.setCycleCount(Animation.INDEFINITE);
timer.getKeyFrames().add(new KeyFrame(Duration.INDEFINITE, event -> maverick()));
timer.play();
stage.show();
}
private void maverick() {
timer.play();
System.out.println("hi");
player.setX(player.getX() + 2);
timer.play();
}
public static void main(String[] args) {
launch(args);
}
}
Use setTranslateX instead of setX if you want to position or animate Nodes.
Change timer = new Timeline(Animation.INDEFINITE) to timer = new Timeline().
Change new KeyFrame(Duration.INDEFINITE, event -> maverick()) to new KeyFrame(Duration.millis(500), event -> maverick()) (for example).
A KeyFrame has to have a point in time. If, like the example, you use 500ms, then the KeyFrame's EventHandler will get called after 500ms. Since your TimeLine has a cycleCount of INDEFINITE, it will loop and execute the KeyFrame every 500ms.
Is there any way to translate somethings like pop up windows(that are not Node) in java FX ?
for example fade transition , translate transition or any timeline transition .... .
Thank's
Create a property and use a Timeline to "animate" the property. Register a listener with the property and update the window when its value changes.
For example:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TranslateWindowExample extends Application {
#Override
public void start(Stage primaryStage) {
Button moveButton = new Button("Move");
moveButton.setOnAction(event -> {
double currentX = primaryStage.getX() ;
DoubleProperty x = new SimpleDoubleProperty(currentX);
x.addListener((obs, oldX, newX) -> primaryStage.setX(newX.doubleValue()));
KeyFrame keyFrame = new KeyFrame(Duration.seconds(1), new KeyValue(x, currentX + 100));
Timeline animation = new Timeline(keyFrame);
animation.play();
});
StackPane root = new StackPane(moveButton);
Scene scene = new Scene(root, 250, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Can you give an example of what do you call popup window ?
For instance, you can create a new stage on the top of your main stage.
You can also use some Tooltip class to add some text over the top of your window.
If you want more classical windows, you should have a look to Alert class.
Anthony
BorderPane in JavaFx application does not show bottom region Node unless the window is maximized when the scene is switched using Button event. If the scenes are switched one after another its arranged perfectly. Do I have bugs in my code or is this the default behaviour? Thanks.
System : Windows XP
Java version : 7
My SSCE:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
public class Main extends Application {
#Override
public void start(final Stage stage) {
try {
///// 2nd scene
BorderPane root2 = new BorderPane();
root2.setPrefSize(stage.getWidth(),stage.getHeight());
HBox buttons2=new HBox(50);
buttons2.getChildren().addAll(new Button("Button1"),new Button("Button2"));
buttons2.setAlignment(Pos.BOTTOM_CENTER);
root2.setBottom(buttons2);
final Scene scene2 = new Scene(root2,stage.getWidth(),stage.getHeight());
///// 1st scene
VBox buttons1=new VBox();
buttons1.setPrefSize(stage.getWidth(),stage.getHeight());
Button nextSceneBtn=new Button("NEXT");
buttons1.getChildren().add(nextSceneBtn);
buttons1.setAlignment(Pos.CENTER);
Scene scene1=new Scene(buttons1,stage.getWidth(),stage.getHeight());
////action event
nextSceneBtn.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
stage.setScene(scene2);
}
});
///stage
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
stage.setX(0);
stage.setY(0);
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.setScene(scene1); //if it's #setScene(scene2) at the beginning, it's ok
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
That looks like a bug, which seems to have been fixed in JavaFX 8. Obviously if you're running on Windows XP, that's of limited use.
A possible workaround is to switch the root of the scene, instead of the scene itself:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
public class Main extends Application {
#Override
public void start(final Stage stage) {
try {
///// 2nd scene
final BorderPane root2 = new BorderPane();
root2.setPrefSize(stage.getWidth(),stage.getHeight());
HBox buttons2=new HBox(50);
buttons2.getChildren().addAll(new Button("Button1"),new Button("Button2"));
buttons2.setAlignment(Pos.BOTTOM_CENTER);
root2.setBottom(buttons2);
// final Scene scene2 = new Scene(root2,stage.getWidth(),stage.getHeight());
///// 1st scene
VBox buttons1=new VBox();
buttons1.setPrefSize(stage.getWidth(),stage.getHeight());
Button nextSceneBtn=new Button("NEXT");
buttons1.getChildren().add(nextSceneBtn);
buttons1.setAlignment(Pos.CENTER);
final Scene scene1=new Scene(buttons1,stage.getWidth(),stage.getHeight());
////action event
nextSceneBtn.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
// stage.setScene(scene2);
scene1.setRoot(root2);
}
});
///stage
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
stage.setX(0);
stage.setY(0);
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.setScene(scene1); //if it's #setScene(scene2) at the beginning, it's ok
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
When there is no record in any table it shows a message 'No content in table', which is by default functionality of TableView in JavaFx.
So here my question is, does the same can be possible with ListView in JavaFx ? Like, if there is no item in any ListView then it will show a message same as TableView, instead of a blank/empty fields.
You have to try this:-
listView.setPlaceholder(new Label("No Content In List"));
its 100% working....
JavaFX8 has a setPlaceholder(...) method for ListView.
In earlier versions, you need to roll your own somehow. This is a bit of a hack: it wraps the ListView in a stack pane, with a white rectangle and the placeholder displayed over the top of the list view. The placeholder and rectangle have their visible property bound, so they are only visible if the list is empty.
There may be easier ways that I'm not seeing right away...
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ListViewPlaceholderTest extends Application {
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<>();
final IntegerProperty counter = new SimpleIntegerProperty();
final Button addButton = new Button("Add item");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
counter.set(counter.get()+1);
listView.getItems().add("Item "+counter.get());
}
});
final Button removeButton = new Button("Remove");
removeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
listView.getItems().remove(listView.getSelectionModel().getSelectedIndex());
}
});
removeButton.disableProperty().bind(Bindings.equal(listView.getSelectionModel().selectedIndexProperty(), -1));
final HBox buttons = new HBox(5);
buttons.setPadding(new Insets(10));
buttons.getChildren().addAll(addButton, removeButton);
final BorderPane root = new BorderPane();
root.setCenter(createPlaceholderForListView(listView, new Label("No content in List")));
root.setBottom(buttons);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private Node createPlaceholderForListView(ListView<?> listView, Node placeholder) {
final StackPane pane = new StackPane();
final Rectangle rect = new Rectangle(0, 0, Color.WHITE);
rect.widthProperty().bind(listView.widthProperty());
rect.heightProperty().bind(listView.heightProperty());
pane.getChildren().addAll(listView, rect, placeholder);
placeholder.visibleProperty().bind(Bindings.isEmpty(listView.getItems()));
rect.visibleProperty().bind(placeholder.visibleProperty());
rect.setMouseTransparent(true);
return pane ;
}
}
With fxml:
<ListView fx:id="foundContentList">
<placeholder>
<Label text="Nothing found" />
</placeholder>
</ListView>
Not entirely sure but I don't think there is a setPlaceholder method(to set the default message when no content in table) for ListView.
The workaround that I use is to create an Object in the list that indicate "No content" and show that on the listview and also disable it.
For example:
ObservableList noContent= FXCollections.observableArrayList("No content found");
ListView listView = new ListView(noContent);
listView.setDisable(true);