I am having the following code to display a PopOver (Custom PopUp by ControlsFX - mvn repo)
public class JavaFXApplication35 extends Application {
#Override
public void start(Stage primaryStage) {
try {
Label lblName = new Label("Tetsing name");
Label lblStreet = new Label("Some street name");
Label lblCityStateZip = new Label("Some city, 111111");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
PopOver popOver = new PopOver(vBox);
Label label = new Label("Mouse mouse over me");
label.setOnMouseEntered(mouseEvent -> {
popOver.show(label, -3);
});
label.setOnMouseExited(mouseEvent -> {
if (popOver.isShowing()) {
popOver.hide();
}
});
StackPane root = new StackPane();
root.getChildren().add(label);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest((WindowEvent event) -> {
System.exit(0);
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The problem is ,
I want the pop-up to be displayed when mouse entered the Label - works fine.
I want the pop-up to be hidden when user exits mouse from Label but not if he enters mouse in to the pop-up window.
I have added MouseEntered and MouseExited actions on Label but how can i handle the another scenario where i don't want to hide the pop-up if user enters mouse in to pop-up.
I ran into the same problem. Here is my solution. Just pass your label (or other node) and PopOver's content node as arguments to this method.
public static void addAutoHidingPopOver(Node hoverableNode, Node contentNode) {
//Creating PopOver
PopOver popOver = new PopOver(hoverableNode);
popOver.setContentNode(contentNode);
//Here you can set custom parameters of your PopOver
//...
//Mouse Actions handling
final Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(1000)));
timeline.setOnFinished(finishEvent -> {
if (hoverableNode.isHover() || contentNode.isHover()) timeline.play();
else popOver.hide();
});
hoverableNode.setOnMouseEntered(mouseEvent -> {if (!popOver.isShowing()) popOver.show(hoverableNode);});
hoverableNode.setOnMouseExited(mouseEvent -> timeline.play());
}
PopOver will be hidden after 1 sec after mouse leave hoverableNode or contentNode. Use it like this:
addAutoHidingPopOver(someLabel, someContentNode);
Note, that your content node should take all visible space of PopOver for comfort use.
That could be expected behavior. I am not sure, but here is a workaround. You can use a ToggleButton.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.PopOver;
public class App extends Application
{
#Override
public void start(Stage primaryStage)
{
//Build PopOver look and feel
Label lblName = new Label("John Doe");
Label lblStreet = new Label("123 Hello Street");
Button lblCityStateZip = new Button("MadeUpCity, XX 55555");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
//Create PopOver and add look and feel
PopOver popOver = new PopOver(vBox);
ToggleButton toggleButton = new ToggleButton("Click me!");
toggleButton.selectedProperty().addListener((obs, oldValue, newValue) -> {
if (newValue) {
popOver.show(toggleButton);
}
else {
popOver.hide();
}
});
;
StackPane root = new StackPane();
root.getChildren().add(toggleButton);
var scene = new Scene(root, 500, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Related
I am creating a JavaFx dialog box and I have written to a large extent the code. My problem is how to display the error message if a user enters the invalid input. I know I have to use a while loop somewhere but not sure where because of the structure of JavaFx dialog box. Second problem is if the user enters the right input, say 1 for yes, I would want to call a function to carry out a task.
The code I have written brings up the pop up box and prints the consequence of the user input to the console.
public static void AnotherMatch() {
//creates a popUp window
Stage popUp = new Stage();
// makes sure no changes are made in the Main window while this window is open
popUp.initModality(Modality.APPLICATION_MODAL);
popUp.setTitle("New Game");
popUp.setMinWidth(400);
popUp.setHeight(200);
TextPanel textPanel2 = new TextPanel();
TextField nameInput = new TextField();
Button button = new Button("Enter");
//label explains how the game works
Label displayLabel = new Label();
displayLabel.setText("Do you want to play another match: Yes: 1 -- No: 2");
button.setOnAction(e -> isChoice(nameInput, nameInput.getText()));
//vbox stores label and is set in centre
VBox windowDisplay = new VBox();
windowDisplay.setStyle("-fx-background-color:Wheat"); //background colour is set
windowDisplay.getChildren().addAll(displayLabel,nameInput, button);
windowDisplay.setAlignment(Pos.CENTER);
Scene scene = new Scene(windowDisplay);
popUp.setScene(scene);
popUp.showAndWait(); }
Code for isChoice function
private static boolean isChoice(TextField nameInput, String message) {
// TODO Auto-generated method stub
try {
int choice = Integer.parseInt(nameInput.getText());
if(choice == 1) {
System.out.println("I want to play game again");
return true;
}
else if (choice == 2){
System.out.println("I want to stop playing");
return false;
}
else {
System.out.println("Invalid entry");
return false;
}
}
catch(NumberFormatException e){
System.out.println(message + " Invalid .Enter 1 for yes and 2 for no");
return false;
}
}
The user should be asked to enter yes or no. If the user invalid input, an error message should be displayed to the user and the answer asked again until they answer yes or no.
One way you can do is using Bindings to disable the Button unless the TextField contains Yes or No(ignore case).
Demo App using Bindings.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication357 extends Application
{
#Override
public void start(Stage primaryStage)
{
TextField textField = new TextField();
Button btn = new Button();
btn.disableProperty().bind(Bindings.notEqualIgnoreCase("yes", textField.textProperty()).and(Bindings.notEqualIgnoreCase("no", textField.textProperty())));
btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane(new VBox(textField, btn));
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
I'm new with Java, I would like to know how to prevent MenuButton popup from closing when I click on the item, I tried the following function, which I found on this site, but does not seem to do anything. I need this in order to make a clone of my C# application whit the purpose of learning Java.
#FXML
private MenuButton menuButton;
#FXML
void initialize() {
CheckMenuItem menuButtonItem1 = new CheckMenuItem("Item 1");
CheckMenuItem menuButtonItem2 = new CheckMenuItem("Item 2");
CheckMenuItem menuButtonItem3 = new CheckMenuItem("Item 3");
menuButtonItem1.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
e.consume();
}
});
menuButtonItem2.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
e.consume();
}
});
menuButtonItem3.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
e.consume();
}
});
}
The CustomMenuItem class provides a setHideOnClick() method that will handle this. In order to use it, you'll wrap a standard Node in this CustomMenuItem:
CheckBox checkBox = new CheckBox("Item 1");
CustomMenuItem customMenuItem = new CustomMenuItem(checkBox);
customMenuItem.setHideOnClick(false);
Below is a full example for you to try:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CheckMenuKeepOpen extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Create the MenuButton
MenuButton menuButton = new MenuButton("Click Me");
for (int i = 0; i < 5; i++) {
// In order to keep the menu open when selecting a CheckBox, we need to wrap it in a CustomMenuItem
CustomMenuItem menuItem = new CustomMenuItem(new CheckBox("Item #" + i));
// This method is pretty obvious; it keeps the menu open when selecting this item.
menuItem.setHideOnClick(false);
menuButton.getItems().add(menuItem);
}
root.getChildren().add(menuButton);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
We found using custom menu item would lose the styling of normal menu items. Instead we solved by using a Menu (i.e. a sub menu) with no items in which other than arrows to the right looks identical to a normal menu item but does not close when clicked. Then finally used css to remove the arrow to the right.
I would like to intercept mouse events on canvas only where I've drawn some shape, but in all other transparent area I would like to have behaviour like with property mouseTransparent true.
I can achieve that with ImageView, transparent areas doesn't intercept mouse events.
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Drawing Operations Test");
Pane root = new Pane();
root.setStyle("-fx-background-color: #C9E3AF");
root.setMinSize(1000, 1000);
root.setOnMouseClicked(event -> {
System.out.println("Clicked on root pane");
});
Canvas canvas1 = new Canvas(512, 512);
canvas1.getGraphicsContext2D().setFill(Color.BLACK);
canvas1.getGraphicsContext2D().fillRect(250, 250, 250, 250);
canvas1.setOnMouseClicked(event -> {
System.out.println("Clicked on canvas1");
});
canvas1.setPickOnBounds(false);
Canvas canvas2 = new Canvas(512, 512);
canvas2.getGraphicsContext2D().setFill(Color.RED);
canvas2.getGraphicsContext2D().fillRect(200, 200, 250, 250);
canvas2.setOnMouseClicked(event -> {
System.out.println("Clicked on canvas2");
});
canvas2.setPickOnBounds(false);
SnapshotParameters param1 = new SnapshotParameters();
param1.setFill(Color.TRANSPARENT);
WritableImage image1 = canvas1.snapshot(param1, new WritableImage(512, 512));
SnapshotParameters param2 = new SnapshotParameters();
param2.setFill(Color.TRANSPARENT);
WritableImage image2 = canvas2.snapshot(param2, new WritableImage(512, 512));
ImageView view1 = new ImageView(image1);
view1.setOnMouseClicked(event -> {
System.out.println("Clicked on view1");
});
view1.setPickOnBounds(false);
ImageView view2 = new ImageView(image2);
view2.setOnMouseClicked(event -> {
System.out.println("Clicked on view2");
});
view2.setPickOnBounds(false);
// ImageView test
// root.getChildren().addAll(view1, view2);
// Canvas test
root.getChildren().addAll(canvas1, canvas2);
Scene sc = new Scene(root);
primaryStage.setScene(sc);
primaryStage.setX(0);
primaryStage.setY(0);
primaryStage.show();
}
Is it even possible with Canvas?
As far as I'm concerned it is impossible to achieve using Canvas, but using Group and Shapes within gives you all the features of Canvas plus behaviour you expect.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class ShapesApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Circle circle = new Circle(100);
circle.setFill(Color.BLUE);
Group group = new Group(circle);
group.setOnMouseMoved(System.out::println);
StackPane stackPane = new StackPane(group);
stackPane.setPrefSize(400, 400);
stage.setScene(new Scene(stackPane));
stage.show();
}
}
This question already has answers here:
Center stage on parent stage
(2 answers)
Closed 4 years ago.
I am trying to pop-up the box in the center of the application when I resize the application or move it.
I tried with css alignment and also using Java. Is it possible if I don't use pane and directly add box in the scene?
Here is my code:
public Boolean call(String question) {
final Stage dialogStage = new Stage(StageStyle.UNDECORATED);
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(owner);
dialogStage.setTitle("ConfirmTitle"); // WIP, waiting for the strings&trans
final Button ok = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.ok")); // WIP,
// waiting
// for
// the
// strings&trans
ok.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Button cancel = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.cancel")); // WIP,
// waiting
// for the
// strings&trans
cancel.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Text text = new Text(question);
text.getStyleClass().add(HTML_POPUP_STYLE);
final Insets ins = new Insets(10);
final VBox box = new VBox();
box.setAlignment(Pos.BOTTOM_CENTER);
box.setSpacing(10);
box.setPadding(ins);
final HBox buttons = new HBox(10);
buttons.getChildren().addAll(ok, cancel);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(ins);
box.getChildren().addAll(text, buttons);
box.getStyleClass().add(HTML_POPUP_STYLE);
StackPane pane = new StackPane();
pane.setAlignment(box, Pos.CENTER);
pane.getChildren().add(box);
Scene scene = new Scene(pane);
try {
URL javafxCss = nmsGuiContainer.getBundleContext().getBundle()
.getResource(NmsGuiContainer.JAVAFX_CSS_URL);
scene.getStylesheets().add(javafxCss.toExternalForm());
} catch (Exception e) {
LOGGER.error("Cannot load the CSS file for JavaFX components ", e);
}
dialogStage.setScene(scene);
ok.setCancelButton(false);
final boolean[] res = new boolean[1];
ok.setOnAction(new CloseDialogHandler(dialogStage, res));
cancel.setCancelButton(true);
cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
dialogStage.centerOnScreen();
nmsGuiContainer.fadeContainer();
dialogStage.showAndWait();
nmsGuiContainer.unfadeContainer();
return res[0];
}
Here is a screenshot of the alertbox:
The Stage.initOwner() method does exactly what you need. While you do call it in your example code, I do not know what owner you are passing to it.
Here is a sample that demonstrates how to do this.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button btnShowAlert = new Button("Show Alert!");
// Set the action to show the alert
btnShowAlert.setOnAction(e -> {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setHeaderText("This is centered over the main window!");
alert.setContentText("Move the main window and show the alert again!");
alert.initOwner(primaryStage);
alert.showAndWait();
});
root.getChildren().add(btnShowAlert);
primaryStage.setTitle("Centered Alerts");
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(500);
primaryStage.setHeight(300);
primaryStage.show();
}
}
I have a javafx application with multiple textboxes that the user can enter information in. I also have a keyboard built into the application that when pressed adds that text to the textbox.
My issue is that since I have multiple textboxes, I don't know which one to add the buttons text to. Is there a way in javafx to check if a user has clicked on a certain textbox so I can check which one has been selected and add the text there?
You can use the Scene.focusOwner property of the active scene to get the focused node. Check, if it's a TextInputControl and call the appropriate method for the button clicked. Note that clicking a button may move the focus, if focusTraversable is true for that button. (By default this is the case.)
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
final Scene scene = new Scene(grid);
for (int i = 0; i < 4; i++) {
grid.add(new TextField(), 0, i);
final String buttonValue = Character.toString((char) ('a'+i));
Button button = new Button(buttonValue);
button.setFocusTraversable(false); // prevent buttons from stealing focus
button.setOnAction(evt -> {
Node fo = scene.getFocusOwner();
if (fo instanceof TextInputControl) {
((TextInputControl) fo).replaceSelection(buttonValue);
}
});
grid.add(button, 1, i);
}
primaryStage.setScene(scene);
primaryStage.show();
}
You should create a listener for each TextField's focusProperty and set an instance variable.
Once you have a global reference to the currently focused TextField, you can do any processing on it that you choose.
Here is a quick application to demonstrate. I've included a couple extra details in the code itself:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
// Instance variable to hold the currently-selected TextField
private TextField selectedTextField;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Create TextFields
TextField txt1 = new TextField();
TextField txt2 = new TextField();
TextField txt3 = new TextField();
TextField txt4 = new TextField();
// This method sets the same change listener on each textfield
installListener(txt1, txt2, txt3, txt4);
VBox pane = new VBox(5);
pane.setPadding(new Insets(5));
// Add the TextFields to the layout
pane.getChildren().addAll(
new HBox(5, new Label("Txt1: "), txt1),
new HBox(5, new Label("Txt2: "), txt2),
new HBox(5, new Label("Txt3: "), txt3),
new HBox(5, new Label("Txt4: "), txt4)
);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
// Accepts multiple TextFields
private void installListener(TextField... textFields) {
// Install the same listener on all of them
for (TextField textField : textFields) {
textField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
// Set the selectedTextField to null whenever focus is lost. This accounts for the
// TextField losing focus to another control that is NOT a TextField
selectedTextField = null;
if (newValue) {
// The new textfield is focused, so set the global reference
selectedTextField = textField;
System.out.println("Selected Text: " + selectedTextField.getText());
}
});
}
}
}