I am trying to make a little game and in the most games the mouse gets locked in the center of the screen. So, is it possible to lock the mouse in the center of the screen or set the position of the mouse in JavaFX? I know that it is possible to do, and I also know some samples written in LWJGL or just with the AWT/SWING package.
Thanks for help.
Update 11/27/2019
From now you can use also JavaFX Robot API:
https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/robot/Robot.html
Here is the code you need:
import java.awt.AWTException;
import java.awt.Robot;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class MoveCursor extends Application {
Scene scene;
VBox container;
Button moveMouse;
Button showHideCursor;
public static int screenWidth = (int) Screen.getPrimary().getBounds().getWidth();
public static int screenHeight = (int) Screen.getPrimary().getBounds().getHeight();
#Override
public void start(Stage stage) throws Exception {
// MoveMouse Button
moveMouse = new Button("Move Cursor to the center of Screen");
moveMouse.setOnAction(m -> {
moveCursor(screenWidth/2, screenHeight/2);
});
// ShowHide Cursor
showHideCursor = new Button("Show/Hide Cursor");
showHideCursor.setCursor(Cursor.HAND);
showHideCursor.setOnAction(m -> {
if (scene.getCursor() != Cursor.NONE)
scene.setCursor(Cursor.NONE);
else
scene.setCursor(Cursor.DEFAULT);
});
// Container
container = new VBox();
container.getChildren().addAll(moveMouse, showHideCursor);
// Scene
scene = new Scene(container, 500, 500);
stage.setScene(scene);
stage.show();
}
/**
* Move the mouse to the specific screen position
*
* #param x
* #param y
*/
public void moveCursor(int screenX, int screenY) {
Platform.runLater(() -> {
try {
Robot robot = new Robot();
robot.mouseMove(screenX, screenY);
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Related
According to this code, button("enlarge") will trigger an action which is calling the method enlarge(), which will do: circle.setRadius(circle.getRadius() + 2);
which is just merely changing the radius value. what I don't understand is that how merely changing the radius will somehow make the program redraw the entire circle.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ControlCircle extends Application {
private CirclePane1 circlePane = new CirclePane1();
#Override
public void start(Stage primaryStage) {
HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.setAlignment(Pos.CENTER);
Button btEnlarge = new Button("Enlarge");
Button btShrink = new Button("Shrink");
hBox.getChildren().add(btEnlarge);
hBox.getChildren().add(btShrink);
btEnlarge.setOnAction(new EnlargeHandler());
btShrink.setOnAction(new ShrinkHandler());
BorderPane borderPane = new BorderPane();
borderPane.setCenter(circlePane);
borderPane.setBottom(hBox);
BorderPane.setAlignment(hBox, Pos.CENTER);
Scene scene = new Scene(borderPane, 200, 150);
primaryStage.setTitle("ControlCircle");
primaryStage.setScene(scene);
primaryStage.show();
}
class EnlargeHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent e) {
circlePane.enlarge();
}
}
class ShrinkHandler implements EventHandler<ActionEvent>{
#Override
public void handle(ActionEvent e) {
circlePane.shrink();
}
}
}
class CirclePane1 extends StackPane{
private Circle circle = new Circle(50);
public CirclePane1() {
getChildren().add(circle);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
}
public void enlarge() {
circle.setRadius(circle.getRadius() + 2);
}
public void shrink() {
circle.setRadius(circle.getRadius() > 2 ? circle.getRadius() - 2
: circle.getRadius());
}
}
Looking at the source code of Circle you can see how the radius property is defined:
private final DoubleProperty radius = new DoublePropertyBase() {
#Override
public void invalidated() {
NodeHelper.markDirty(Circle.this, DirtyBits.NODE_GEOMETRY);
NodeHelper.geomChanged(Circle.this);
}
#Override
public Object getBean() {
return Circle.this;
}
#Override
public String getName() {
return "radius";
}
};
When you change the value of the radius property its invalidated() method is called. And this is where the "magic" happens, it calls these two methods:
NodeHelper.markDirty(Circle.this, DirtyBits.NODE_GEOMETRY);
NodeHelper.geomChanged(Circle.this);
Now, I don't know what these methods do specifically - nor do I have the time/desire to study it - but they tell the JavaFX runtime that the Circle needs to be redrawn. This means that the next time a rendering pulse occurs, which may be triggered by these methods, the Circle will be drawn with its new radius.
This is all very optimized and will cause a redraw only when applicable (such as only when part of ascene-graph).
I have made a progress bar in javafx. There is a cancel Button by default. I just want to disable this cancel button when my task got completed.
jobProgressView.setGraphicFactory(task -> {
return new Button("save");
});
Without more code, I'm only able to make a guess. Even your added code isn't enough to know all things from your implementation.
So this solution assumes, that you have a Task that is running and showing it's progress on a Progressbar. The Task here is wrapped in a service, which can be restarted (maybe you also need this?).
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CancelButtonDemo extends Application {
Service<Integer> service = new MyService();
#Override
public void start(Stage primaryStage) {
Button start = new Button();
Button cancel = new Button();
ProgressBar progress = new ProgressBar(0);
start.setText("Run Task");
start.setOnAction((ActionEvent event) -> {
if (!(service.getState().equals(Worker.State.READY))) {
service.reset();
}
progress.progressProperty().bind(service.progressProperty());
service.start();
});
start.disableProperty().bind(service.runningProperty());
cancel.setText("Cancel Task");
cancel.setOnAction((ActionEvent event) -> {
service.cancel();
progress.progressProperty().unbind();
progress.setProgress(0);
});
cancel.disableProperty().bind(Bindings.not(service.runningProperty()));
VBox root = new VBox(20);
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(start, progress, cancel);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Cancel Button Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class MyService extends Service<Integer> {
#Override
protected Task<Integer> createTask() {
return new Task<Integer>() {
#Override
protected Integer call() throws Exception {
int iterations;
for (iterations = 0; iterations < 10000000; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
updateMessage("Iteration " + iterations);
updateProgress(iterations, 10000000);
}
return iterations;
}
};
}
}
}
The above application looks like this:
I am just starting with JavaFX. I want to have a BorderPane with controls on top, left, and right, and an image in the center. I want the center pane to resize as you resize the window, but to always be able to see all left, right, and top controls.
With the code below, I can show a button in the left, top, and right. And I can display an image in the center.
But the image expands beyond center bounds and hides the right button.
Oddly, if I set a clipping rectangle on the imageview in the center pane (uncomment lines 67 & 68), it does in fact only draw the clipped region, but the rest of the layout behaves as if it were drawing the whole picture. That is, the UNDRAWN part of the image still obscures the button on the right.
Any help would be much appreciated.
Thanks in advance and apologies if it's simple.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ImageApp extends Application {
private BorderPane root;
private Rectangle clipRect;
private ImageView iv;
private StackPane leftPane;
private StackPane rightPane;
private Button topButton;
private Button leftButton;
private Button rightButton;
#Override
public void start(Stage primaryStage) {
root = new BorderPane();
Scene primaryScene = new Scene(root, 900, 800);
initializePrimaryStage(primaryStage, primaryScene);
initializeFrameContent(root, topButton, leftButton);
initializeContent(root);
primaryStage.show();
}
private void initializeFrameContent(BorderPane root, Button topButton, Button leftButton) {
topButton = new Button("TOP");
leftButton = new Button("LEFT");
rightButton = new Button("RIGHT");
leftPane = new StackPane(leftButton);
leftPane.setAlignment(Pos.TOP_LEFT);
rightPane = new StackPane(rightButton);
rightPane.setAlignment(Pos.TOP_RIGHT);
root.setLeft(leftPane);
root.setTop(topButton);
root.setRight(rightButton);
}
private void initializePrimaryStage(Stage primaryStage, Scene primaryScene) {
primaryStage.setTitle("Image Clip Test");
primaryStage.setScene(primaryScene);
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.minWidthProperty().setValue(400);
primaryStage.minHeightProperty().setValue(300);
}
public static void main(String[] args) {
launch(args);
}
private void initializeContent(BorderPane root) {
Image image = new Image(
"http://www.ciee.org/study-abroad/images/cities/0020/headers/desktop/big-ben-london-traffic-trafalgar-abroad-studies.jpg"
);
iv = new ImageView(image);
root.setCenter(iv);
//clipRect = new Rectangle(400,200);
//root.getCenter().setClip(clipRect);
}
}
You don't specify what you intend to do. Why would you want to clip the content? The way you describe it all you want is some background that's getting clipped. You can do that with various mechanisms, e. g. css.
Or you could use a proper parent, e. g. a ScrollPane in order to limit the region or e. g. an ImageViewPane in order to stretch to fit:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ImageApp extends Application {
private BorderPane root;
private Rectangle clipRect;
private ImageView iv;
private StackPane leftPane;
private StackPane rightPane;
private Button topButton;
private Button leftButton;
private Button rightButton;
#Override
public void start(Stage primaryStage) {
root = new BorderPane();
Scene primaryScene = new Scene(root, 900, 800);
initializePrimaryStage(primaryStage, primaryScene);
initializeFrameContent(root, topButton, leftButton);
initializeContent(root);
primaryStage.show();
}
private void initializeFrameContent(BorderPane root, Button topButton, Button leftButton) {
topButton = new Button("TOP");
leftButton = new Button("LEFT");
rightButton = new Button("RIGHT");
leftPane = new StackPane(leftButton);
leftPane.setAlignment(Pos.TOP_LEFT);
rightPane = new StackPane(rightButton);
rightPane.setAlignment(Pos.TOP_RIGHT);
root.setLeft(leftPane);
root.setTop(topButton);
root.setRight(rightButton);
}
private void initializePrimaryStage(Stage primaryStage, Scene primaryScene) {
primaryStage.setTitle("Image Clip Test");
primaryStage.setScene(primaryScene);
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.minWidthProperty().setValue(400);
primaryStage.minHeightProperty().setValue(300);
}
public static void main(String[] args) {
launch(args);
}
private void initializeContent(BorderPane root) {
Image image = new Image(
"http://www.ciee.org/study-abroad/images/cities/0020/headers/desktop/big-ben-london-traffic-trafalgar-abroad-studies.jpg"
);
iv = new ImageView(image);
// ImageViewPane content = new ImageViewPane( iv);
ScrollPane content = new ScrollPane( imageView);
// hide scrollbars
content.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
content.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
content.setPadding(Insets.EMPTY);
root.setCenter(content);
}
// code from here: https://stackoverflow.com/questions/22993550/how-to-resize-an-image-when-resizing-the-window-in-javafx
class ImageViewPane extends Region {
private ObjectProperty<ImageView> imageViewProperty = new SimpleObjectProperty<ImageView>();
public ObjectProperty<ImageView> imageViewProperty() {
return imageViewProperty;
}
public ImageView getImageView() {
return imageViewProperty.get();
}
public void setImageView(ImageView imageView) {
this.imageViewProperty.set(imageView);
}
public ImageViewPane() {
this(new ImageView());
}
#Override
protected void layoutChildren() {
ImageView imageView = imageViewProperty.get();
if (imageView != null) {
imageView.setFitWidth(getWidth());
imageView.setFitHeight(getHeight());
layoutInArea(imageView, 0, 0, getWidth(), getHeight(), 0, HPos.CENTER, VPos.CENTER);
}
super.layoutChildren();
}
public ImageViewPane(ImageView imageView) {
imageViewProperty.addListener(new ChangeListener<ImageView>() {
#Override
public void changed(ObservableValue<? extends ImageView> arg0, ImageView oldIV, ImageView newIV) {
if (oldIV != null) {
getChildren().remove(oldIV);
}
if (newIV != null) {
getChildren().add(newIV);
}
}
});
this.imageViewProperty.set(imageView);
}
}
}
EDIT:
I have an alert box that pops up if the user clicks "Delete" for removing an item in a ListView. It works, but I would like it to pop over the original stage. It showed up in my first monitor. Is there any way to set the position of the alert when it's shown?
Note, the "owner" is in a different class, and I created everything with Scenebuilder/FXML. I cannot figure out how to get initOwner() to work. Here is the "Main" class:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Assignment_5 extends Application {
public Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Assignment_5.fxml"));
primaryStage.setTitle("Plant Pack");
primaryStage.setScene(new Scene(root, 1200, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is the working code within the Controller class. It's not necessary to implement the modality of this alert, but it would be a nice addition to make it more convenient. I simply don't know how to pass the main Window from the Main class to this:
protected void handleDeleteButtonClick(ActionEvent event) {
Alert alertBox = new Alert(Alert.AlertType.CONFIRMATION, "Confirm Delete", ButtonType.OK, ButtonType.CANCEL);
alertBox.setContentText("Are you sure you want to delete this " + plantType.getValue().toString().toLowerCase() + "?");
alertBox.showAndWait();
if(alertBox.getResult() == ButtonType.OK) {
int selectedPlant = plantList.getSelectionModel().getSelectedIndex();
observablePlantList.remove(selectedPlant);
}
else {
alertBox.close();
}
}
I understand this is fairly new, so it's difficult to find many resources. If anyone knows any info I may have missed, please let me know. Thanks for any help offered.
I am using Java 8 with IntelliJ 14.1.5.
As #jewelsea suggests, setting the modality and owner for the alert box will assure that the alert will appear over the stage, even if the stage is moved.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class DeleteAlertDemo extends Application {
Stage owner;
ObservableList<String> observablePlantList;
ListView<String> plantList;
protected void handleDeleteButtonClick(ActionEvent event) {
String item = plantList.getSelectionModel().getSelectedItem();
Alert alertBox = new Alert(Alert.AlertType.CONFIRMATION, "Confirm Delete",
ButtonType.OK, ButtonType.CANCEL);
alertBox.setContentText("Are you sure you want to delete this "
+ item.toLowerCase() + "?");
alertBox.initModality(Modality.APPLICATION_MODAL); /* *** */
alertBox.initOwner(owner); /* *** */
alertBox.showAndWait();
if (alertBox.getResult() == ButtonType.OK) {
int selectedPlant = plantList.getSelectionModel().getSelectedIndex();
observablePlantList.remove(selectedPlant);
} else {
alertBox.close();
}
}
#Override
public void start(Stage primaryStage) {
owner = primaryStage; /* *** */
Button deleteBtn = new Button();
deleteBtn.setText("Delete");
deleteBtn.setOnAction(this::handleDeleteButtonClick);
observablePlantList = FXCollections.observableArrayList("Begonia",
"Peony", "Rose", "Lilly", "Chrysanthemum", "Hosta");
plantList = new ListView<>(observablePlantList);
plantList.getSelectionModel().select(0);
BorderPane root = new BorderPane();
root.setCenter(plantList);
root.setRight(deleteBtn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Delete Alert Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I have a problem with my button size in JavaFX. I want to have fixed size buttons but when I change text on the buttons, changes button size aswell.
I have 5 buttons and 5 random numbers between 1 - 20. Buttons with single digit is smaller then buttons with two digits. I want both same size.
What can I do?
Here is one way to do this. The buttons go in a TilePane, the TilePane goes in a group so that everything in it remains at it's preferred size. A preferred size is set on each button.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import java.util.Random;
public class FixedSizes extends Application {
private static final int NUM_BUTTONS = 5;
private static final int MAX_BUTTON_VALUE = 20;
private static final Random random = new Random(42);
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(generateButtonLayout()));
stage.show();
}
private Parent generateButtonLayout() {
TilePane layout = new TilePane();
layout.setHgap(10);
layout.setPrefColumns(NUM_BUTTONS);
layout.getChildren().setAll(createButtons());
layout.setPadding(new Insets(10));
return new Group(layout);
}
private Button[] createButtons() {
Button[] buttons = new Button[NUM_BUTTONS];
for (int i = 0; i < buttons.length; i++) {
buttons[i] = createButton();
}
return buttons;
}
private Button createButton() {
Button button = new Button(generateButtonText());
button.setOnAction(event -> button.setText(generateButtonText()));
button.setPrefWidth(50);
return button;
}
private String generateButtonText() {
return "" + (random.nextInt(MAX_BUTTON_VALUE) + 1);
}
public static void main(String[] args) {
launch(args);
}
}