I created a set of JavaFX components in a Scene which I'd like to display in a resizable modal dialog in a swing applications. The JavaFX components include ImageViews for scanned images which can get quite big dependening on the zoom level, so precise layouting in an issue. My options are afaik
displaying a JavaFX Dialog with showAndWait in a Platform.runLater and stop the Swing EDT with an invisible JDialog. That apparently causes deadlocks and is quite unelegant.
put the JavaFX components in a JFXPanel and display it in a JDialog. That works in terms of modality, but I have no idea how to layout components in the JFXPanel since in a GroupLayout the panel simply grows infinitely (JavaFX ScrollPane don't have any effect).
For example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class NewMain1 extends Application {
private final ImageView imageView;
public NewMain1() {
this.imageView = new ImageView(NewMain.class.getResource("/File_CC-BY-SA_3_icon_88x31.png").toString());
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
BorderPane borderPane = new BorderPane();
Button bottomButton = new Button("Some button");
ScrollPane imageViewScrollPane = new ScrollPane(imageView);
borderPane.setCenter(imageViewScrollPane);
borderPane.setBottom(bottomButton);
imageView.setSmooth(true);
imageView.setFitHeight(400);
StackPane root = new StackPane();
root.getChildren().add(borderPane);
stage.setScene(new Scene(root, 800, 600));
stage.show();
}
}
shows a well working ScrollPane for the ImageView whereas in a JFXPanel in a JDialog the scrolling/layout doesn't work:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class NewMain extends JFrame {
private static final long serialVersionUID = 1L;
private final JFXPanel mainPanel = new JFXPanel();
private final ImageView imageView;
private final JButton closeButton = new JButton("Close");
public NewMain() throws HeadlessException {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setBounds(0, 0, 800, 600);
setPreferredSize(new Dimension(800, 600));
GroupLayout layout = new GroupLayout(this.getContentPane());
this.getContentPane().setLayout(layout);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
this.imageView = new ImageView(NewMain.class.getResource("/File_CC-BY-SA_3_icon_88x31.png").toString());
Platform.runLater(() -> {
BorderPane borderPane = new BorderPane();
Button bottomButton = new Button("Some button");
ScrollPane imageViewScrollPane = new ScrollPane(imageView);
borderPane.setCenter(imageViewScrollPane);
borderPane.setBottom(bottomButton);
imageView.setSmooth(true);
imageView.setFitHeight(400);
Group root = new Group();
Scene scene = new Scene(root, Color.ALICEBLUE);
root.getChildren().add(borderPane);
mainPanel.setScene(scene);
});
closeButton.addActionListener((event) -> {
setVisible(false);
});
layout.setHorizontalGroup(layout.createParallelGroup()
.addComponent(mainPanel)
.addComponent(closeButton));
layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(mainPanel)
.addComponent(closeButton));
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new NewMain().setVisible(true);
});
}
}
Don't make the root of the Scene a Group, Groups are not resizable.
Just remove the group and use a resizable layout for the scene root (you already have a resizable layout in your sample code, it's the BorderPane, so you can just use that).
Instead of:
Group root = new Group();
Scene scene = new Scene(root, Color.ALICEBLUE);
root.getChildren().add(borderPane);
Write:
Scene scene = new Scene(borderPane, Color.ALICEBLUE);
ScrollPane inside a JavaFX scene inside a JFXPanel inside a Swing JFrame.
Note, that in your pure JavaFX NewMain1 application, you use are already using a resizable pane as a root (a StackPane), so that is the reason for the discrepancy that you observed between the pure JavaFX version and the Swing embedded version.
Related
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.awt.*;
public class Main extends Application {
Stage window;
Scene scene1, scene2;
Button button1, button2;
ImageView iv = new ImageView();
#Override
public void start(Stage primaryStage) throws Exception{
window = primaryStage;
Label label1 = new Label("First stage");
Button button1 = new Button("PLAY");
//Layout 1
VBox layout1 = new VBox(20);
scene1 = new Scene(layout1, 1280, 720);
layout1.getChildren().add(changeBackground("file:menuBackground.png"));
layout1.getChildren().addAll(label1, button1);
//defines dimensions of vbox so button and label can be centered
layout1.prefWidthProperty().bind(window.widthProperty());
layout1.prefHeightProperty().bind(window.heightProperty());
layout1.setAlignment(Pos.CENTER);
button1.setOnAction(event -> window.setScene(scene2));
//Button 2
Button button2 = new Button("This scene sucks, go back to scene 1");
button2.setOnAction(event -> window.setScene(scene1));
//Layout 2
StackPane layout2 = new StackPane();
layout2.getChildren().add(button2);
scene2 = new Scene(layout2, 1280, 720);
layout2.getChildren().add(changeBackground("file:gameBackground.png"));
window.setScene(scene1);
window.setTitle("Scramblo");
window.show();
}
public static void main(String[] args) {
launch(args);
}
public Node changeBackground(String imageFile){
ImageView iv = new ImageView();
Image image = new Image(imageFile);
iv.setImage(image);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getWidth();
iv.setFitHeight(height);
iv.setFitHeight(width);
iv.setPreserveRatio(true);
return iv;
}
}
So what I want is for the buttons to overlap the background images. I want to see the buttons and be able to interact with them with just having the background chillin' there. Right now when I add the background it completely removes the buttons/labels and anything else I put on there. I'm confused on how to go about this! Thanks!
Make layout1 a StackPane and not a VBox. Use a VBox as the container for label1 and button1 and add the VBox to layout1.
A StackPane places nodes added to it one on top of the other. The first node added is the lowest. Hence you want to first add the background image to the StackPane and then the button. That way the button appears on top of the image.
Similarly for layout2, because you first added button2 and after that added the image, the image sits on top of the button and therefore hides the button. You just need to swap them, i.e. add the image first and then add the button.
Note that the URL for the image, in your code, looks incorrect to me. I simply put the image in the same folder as the compiled Main.class file and used method getResource() to get the image file.
Also, I don't think it is a good idea to mix AWT and JavaFX. JavaFX has class Screen that will provide you with the screen dimensions.
Here is your code with my fixes. Compare it with your code to see the differences.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.net.URL;
public class Main extends Application {
Stage window;
Scene scene1, scene2;
Button button1, button2;
ImageView iv = new ImageView();
#Override
public void start(Stage primaryStage) throws Exception {
Screen screen = Screen.getPrimary();
Rectangle2D screenBounds = screen.getBounds();
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
window = primaryStage;
Label label1 = new Label("First stage");
Button button1 = new Button("PLAY");
VBox vBox = new VBox(20);
vBox.getChildren().addAll(label1, button1);
vBox.setAlignment(Pos.CENTER);
// Layout 1
StackPane layout1 = new StackPane();
scene1 = new Scene(layout1, 1280, 720);
layout1.getChildren().addAll(changeBackground("snowymountains.png"), vBox);
// defines dimensions of vbox so button and label can be centered
layout1.prefWidthProperty().bind(window.widthProperty());
layout1.prefHeightProperty().bind(window.heightProperty());
layout1.setAlignment(Pos.CENTER);
button1.setOnAction(event -> window.setScene(scene2));
// Button 2
Button button2 = new Button("This scene sucks, go back to scene 1");
button2.setOnAction(event -> window.setScene(scene1));
// Layout 2
StackPane layout2 = new StackPane();
scene2 = new Scene(layout2, 1280, 720);
layout2.getChildren().addAll(changeBackground("gameBackground.jpg"), button2);
window.setScene(scene1);
window.setTitle("Scramblo");
window.show();
}
public static void main(String[] args) {
launch(args);
}
public Node changeBackground(String imageFile) {
URL url = getClass().getResource(imageFile);
ImageView iv = new ImageView();
Image image = new Image(url.toExternalForm());
iv.setImage(image);
iv.setFitHeight(window.getHeight());
iv.setFitHeight(window.getWidth());
iv.setPreserveRatio(true);
return iv;
}
}
How can I create a popover like in the DatePicker class from javafx.scene.control.DatePicker as seen here:
The popover should when displayed be on top of all the other components as seen here (the popover is above the TextField):
Found a pretty simple solution to my problem, here is a code snippet in case people encounter the same problem
package main;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane rootPane = new BorderPane();
MenuButton openButton = new MenuButton("Open Context Menu");
BorderPane contentPane = new BorderPane();
CustomMenuItem item = new CustomMenuItem(contentPane);
openButton.setStyle("-fx-selection-bar: transparent;"); //this is optional. it makes the blue background that appears when something is focused transparent
contentPane.setPrefSize(300, 300);
Label text = new Label("The ContextMenu will only close when you click the\nbutton below OR click outside of the ContextMenu.\nHow neat is that?");
text.setStyle(" -fx-text-fill: -fx-text-base-color;"); //needs to bet set if you want the selection-bar to be transparent. if not set the text will become invisible
contentPane.setTop(text);
Button closeButton = new Button("Close this popover");
closeButton.setOnAction(x -> {
openButton.hide();
});
contentPane.setBottom(closeButton);
item.setHideOnClick(false); // this will stop the ContextMenu from being hidden when clicking inside of it.
openButton.getItems().add(item);
rootPane.setCenter(openButton);
Scene scene = new Scene(rootPane, 550, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I just placed a Pane with all of my content inside of a CustomMenuItem and added that CustomMenuItem to my MenuButton.
I have a Pane Object where users can drag and drop various ImageViews. For this, I used
pane.getChildren(imageViewObject)
method
Now, after replacing Pane with ScrollPane, it does not have this method. So I don't know how to get arrount this issue.
Thank you in advance
you can specify only one node with ScrollPane. To create a scroll view with more than one component, use layout containers or the Group class.
Pane pane = ...;
ScrollPane sp = new ScrollPane();
sp.setContent(pane);
Example:
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author kachna
*/
public class Test extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.getChildren().addAll(new Button("button1"), new Button("button2"), new Button("button3"));
root.setSpacing(10);
root.setPadding(new Insets(10));
ScrollPane sp = new ScrollPane();
sp.setContent(root);
sp.setPannable(true); // it means that the user should be able to pan the viewport by using the mouse.
Scene scene = new Scene(sp, 100, 100);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
package application;
import java.awt.Button;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
public class Main extends Application {
Button button; //Declare Button
public static void main(String[] args) {
launch(args); //calls application
}
#Override
public void start(Stage primaryStage) throws Exception {
//comes from application pack
primaryStage.setTitle("Title Of Window"); //Main Stage
button = new Button (); //Creates Button
button.setLabel("Click Me");
StackPane layout = new StackPane();
layout.getChildren().add(button); //Button in Scene
Scene scene = new Scene (layout, 300, 250); //Sets Scene
primaryStage.setScene(scene); //Stage and Scene
primaryStage.show();
}
Hey guys, so this is first time I am creating something in JavaFX and I was wondering why doesn't my button in Scene add in Layout.GetChildren() line, it keeps on displaying red line underneath add. I am using Eclipse IDE.
you have imported import java.awt.Button, you must import Button class in the javafx package.
HiEveryone,
I'm new Javafx and trying to change the float of this button as to Right side from the current Left side.. How to do that?
I'm using simply Pane as a container at the moment.
Have a look into this screenshot:
How to do that? ..pls help
Thanks in advance!
EDITED
Source code:
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.scene.control.TextField;
import javafx.scene.control.Button;
public class Main extends Application {
#Override
public void start (Stage primaryStage){
primaryStage.setTitle("Modern web browser made by Rajendra arora.");
WebView wv = new WebView();
wv.setLayoutX(0.0);
wv.setLayoutY(40.0);
wv.setPrefHeight(Double.MAX_EXPONENT);
wv.setPrefWidth(Double.MAX_EXPONENT);
WebEngine we = wv.getEngine();
we.load("http://www.google.com/");
Button btn=new Button("Go");
TextField tf = new TextField("http://www.google.com/");
tf.setLayoutX(1.0);
tf.setPrefWidth(Double.MAX_EXPONENT);
tf.setPrefHeight(25.0);
Pane sp = new Pane();
sp.getChildren().add(tf);
sp.getChildren().add(btn);
sp.getChildren().add(wv);
Scene scene = new Scene(sp, 600, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Use a HBox to hold the textfield and button, later add it to the Pane
HBox hbox = new HBox();
hbox.getChildren().addAll(tf, btn);
Pane sp = new Pane();
sp.getChildren().add(hbox);
sp.getChildren().add(wv);