I would like to change the cursor to use the drag and drop "move" and "move and copy" cursors in my application on a panel when I perform certain actions. The area is using swing inside of a swing node so a swing (Apply to panel) or JavaFx (Applying to swing node) solution would work.
I did find swing cursors DragSource.DefaultMoveDrop and DragSource.DefaultCopyDrop but when I apply them my cursor doesn't change. (And I know I am applying the cursor correctly as applying other cursors work fine)
EDIT: Note that when not nested in a SwingNode and in a JFrame instead the cursor does change to what I want. I have amended the code example to emulate the environment I am in where it doesn't work:
public class Temp extends Application {
public static void main(String[] args) {
Application.launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
SwingNode swingNode = new SwingNode();
SwingUtilities.invokeLater(() -> {
JPanel panel = new JPanel();
panel.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); //Change this
swingNode.setContent(panel);
});
Scene scene = new Scene(new BorderPane(swingNode));
primaryStage.setScene(scene);
primaryStage.show();
}
}
Ok turns out it works DragSource.DefaultMoveDrop and DragSource.DefaultCopyDrop actually work in a normal swing application, just not on JPanel inside a SwingNode which the environment I am working in uses. (I can't change this)
Therefore if you run into this issue and you are NOT mixing the frameworks this forms an answer.
Related
I created a swing GUI, and a graph using JavaFX. I would like to open the graph by clicking a button on the swing GUI. My Code is below. Thank you for your help.
This is what I have to open the FX GUI from the swing GUI.
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Test Graph = new Test();
Graph.GrpahScreen();
}
});
FX class
public class Test extends Application {
#Override
public void start(Stage stage) {
// TODO
}
public static void main(String args []) {
launch(args);
}
}
The Application class represents a JavaFX Application. Since you are writing a Swing application (with JavaFX content embedded in it), it makes no sense to create an Application subclass.
Additionally, it is highly recommended not to use both JavaFX Stages and Swing JFrames in the same application. You should place the JavaFX content in a JFXPanel and display the JFXPanel in a JFrame. You need to be careful to obey the threading restrictions of both toolkits: as usual, Swing components must be created on the AWT event dispatch thread, and JavaFX components must be created on the JavaFX application Thread. This is all covered in detail in the JFXPanel documentation, but in essence:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JFrame frame = new JFrame();
JFXPanel jfxPanel = new JFXPanel();
Platform.runLater(() -> {
Parent root = ... ; // create JavaFX content, can be in a separate class
Scene scene = new Scene(root);
jfxPanel.setScene(scene);
});
frame.add(jfxPanel);
frame.setSize(...);
frame.setVisible(true);
}
});
My title is badly worded because my problem is very hard to describe, so I drew an image for it:
I have an ImageView object which represents a pile of cards (not poker cards but just used them as an example). When this image view is clicked, I need a window to popup that features a ScrollPane and shows them all the card objects that are in the linked list. When the user clicks anywhere outside of the window (and later on, any right mouse button click) the scrollpane window needs to close.
Ways that I have already tried:
Scene with APPLICATION_MODAL. Then did Scene.showAndWait(). I didn't like this method because it made another application on the user's taskbar. It also felt clunky and just bad.
Changed my root pane to a StackPane, then added this Scrollpane to the stackpane when the user clicked on the deck. This for some reason was really slow and seemed really obtrusive. It was also annoying because my alternate class needed to have access to the root pane (since when it closes, it needs to go to the root StackPane and call .remove() on itself).
Are there any other better ways to accomplish this? My application is going to have many of these piles and so this framework needs to work very well.
I would still propose to open a new Stage with some restrictions to solve your issues with this approach.
You can use the initOwner property of the Stage to have another Stage as owner, therefore no other icon will appear on the taskbar.
You can use the initStyle property with TRANSPARENT or UNDECORATED StageStlye, this will ensure that only the content is visible.
And in the end you can use the focusedProperty to check whether the Stage lost focus to close it.
Example
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
Scene sc = new Scene(new ScrollPane(), 300, 300);
popupStage.setScene(sc);
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
Boolean newValue) {
if(!newValue)
popupStage.close();
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
It is also possible of course to not open it in a new Stage, but draw a ScrollPane inside the current Stage which overlaps the content of the Stage using for example an AnchorPane or Group as root, but the first solution has the advantage that you are not bound to the dimensions of main Stage (the popup can have any size that you want).
You can achieve this with a low level system hook that catches the mouse events.
http://kra.lc/blog/2016/02/java-global-system-hook/ or https://github.com/kwhat/jnativehook/releases
I hope that is what you needed, otherwise i got your question wrong.
I have a Java project that plays a .flv media file through JavaFX Media Player, and it's working fine. Recently, I've been wanting to experiment by adding GUI components to this Project (JPanel, JLabel). However, I've failed in all my attempts and after doing some research turns out it's not as simple as i first thought.. I've tried borderPane.setTop(JLabel) but I get a "Cannot convert Jlabel to Node" error.. I feel that I'm missing something
If anyone has any idea why this isnt working for me, I would greatly appreciate any form of explanation or examples.. :)
Here is the code if it might be of use to you!
#Override
public void start(Stage stage){
String path = "Data/Video/Clip.flv";
Media media = new Media(new File(path).toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(media);
MediaView mediaView = new MediaView(mediaPlayer);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(mediaView);
//borderPane.add(logoPanel); <<<<<<< Error
Scene scene = new Scene(borderPane, 1024, 800);
scene.setFill(javafx.scene.paint.Color.BLACK);
stage.setTitle("Media Player");
stage.setScene(scene);
stage.show();
mediaPlayer.setAutoPlay(true);
BorderPane is a JavaFX Node whereas JPanel is a Java Swing Component.
You cannot add a JPanel to a BorderPane, what you are looking for instead is the JavaFX equivalent of the JPanel which is the Pane class.
If you are developing using JavaFX it is easier to just use JavaFX components. If you must use Swing components then you can use the SwingNode class.
What you basically want to achieve: add some Swing components to an JavaFX application.
You have to use SwingNode class to "wrap" a JComponent.
SwingNode Class
JavaFX 8 introduces the SwingNode class, which is located in the
javafx.embed.swing package. This class enables you to embed Swing
content in a JavaFX application. To specify the content of the
SwingNode object, call the setContent method, which accepts an
instance of the javax.swing.JComponent class.
You can check this tutorial how embed Swing component into JavaFX application.
Small example to put a Swing Button into the center of a BorderPane:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
SwingNode swingNode = new SwingNode();
JButton jButton = new JButton("I am a Swing button");
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Message from Swing");
}
});
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
swingNode.setContent(jButton);
}
});
root.setCenter(swingNode);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
By the way, BorderPane has no method "add" (as in your commented line):
You can use setCenter, setTop, setBottom, setLeft and setRight to add Nodes into it (as you used to fill the center).
If your goal was not to embed Swing into JavaFX, just to use JavaFX controls, you can check this article what controls JavaFX has by default.
I tried to write my 'own' log in a tab and I experienced problems with updating the label with a naive solution that I had.
So after I googled I checked out this solution here:
Displaying changing values in JavaFx Label
I don't know if I did everything right but unfortunately this solution doesn't work for me.
final static Label logLabel = new Label();
final static SimpleStringProperty logString = new SimpleStringProperty("test");
...
...
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.getTabs().add(createSettingsTab());
tabPane.getTabs().add(createParticipantTab());
tabPane.getTabs().add(createSpectatorTab());
tabPane.getTabs().add(createOverviewTab());
tabPane.getTabs().add(createTournamentTab());
tabPane.getTabs().add(createLogTab());
tabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
Scene scene = new Scene(tabPane, 1200, 800);
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
private Tab createLogTab() {
// TODO Auto-generated method stub
logLabel.textProperty().bind(logString);
Tab tab = new Tab("Log");
tab.setContent(logLabel);
return tab;
}
I got this lines for initializing the Label and for setting the new text I do this:
logString.set(logString.get() + "foo");
The log tab keeps being blank...
I'd appreciate any help! Thanks!
edit// That's the only useful mcve I can think of. As I said the other create methods to create the other tabs are not making use of the label or the SimpleStringProperty
This is the button that doesn't work as supposed concerning the label.
buttonLoadConfig.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("test");
logString.set(logString.get() + "\ttest");
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose Config");
File config = fileChooser.showOpenDialog(new Stage());
}
});
Use of static members is often not a good idea and is probably contributing to your issue.
Your code as it is currently written can have many tabs but only one label. You are trying to add the same label to multiple tabs. But, when you do that, the label will be removed from the previous tabs because of rules on how the JavaFX Scenegraph works.
See the Node documentation:
If a program adds a child node to a Parent (including Group, Region, etc) and that node is already a child of a different Parent or the root of a Scene, the node is automatically (and silently) removed from its former parent.
So the binding is working, but, due to the above rule and the way you have written the rest of your code, the program as a whole is likely not working as you expected or wished.
If you are still having issues, update your question with an mcve.
I've been struggling for weeks to resolve the memory leaks within our JavaFX application, and thought today I was at a complete loss with it so decided to write the simplest application application I could think of to prove JavaFX could in fact release memory and therefore proving I was doing something wrong. To my surprise this seems to leak memory too.
Can anyone advise why the following application still has a javafx.scene.control.Label in the heap after the button is clicked? The way I checked it was there was with jprofiler.
public class MemoryTestApplication extends Application {
#Override
public void start(Stage primaryStage) {
//root pane
final VBox root = new VBox();
//label to remove when button is clicked
final Label label = new Label("Remove Me");
root.getChildren().add(label);
//button to remove label
Button btn = new Button("Remove label");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().remove(label);
}
});
root.getChildren().add(btn);
//create scene and stage
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Your anonymous inner class for the event handler is holding a reference to the label, and your button is holding a reference to the event handler.
The anonymous inner class is going to have two synthetically generated fields, created by the compiler, like:
final Label val$label;
final MemoryTestApplication this$0;
Since these are final, you can't clear label after using it by label = null;, so I think your best bet is to change the event handler to a normal, named class, pass the label reference in to its constructor, and then clear it after using it to remove the label from the layout. (You would want to test that it was not null before removing, so that it would only be removed the first time the button is pressed).