I can't find anything on this and i am not sure if this is even possible but i will try. I have a JavaFX application showing a stage when you start typing text (it listen to os inputs). Everything is working fine except when the application is set to full screen on macOS. The stage will not show over the application, even if alwaysontop is true.
Only way i got this to work was using a JDialog in Type.POPUP but this is causing other issues in my code. I tried playing with the window level but it doesn't work.
Here is a test i ran to test the issue. This will go over any application except when it is in full screen.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class ontopTest extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage stage){
Scene scene = new Scene(new Pane(new Label("test application")));
stage.setTitle("test");
stage.initStyle(StageStyle.UNDECORATED);
stage.setWidth(300);
stage.setHeight(300);
stage.setScene(scene);
// stage.setAlwaysOnTop unneeded as com.sun.glass.ui sets window to top
stage.setY(0); // Set Y Axis to 0
// Need to show window first
stage.show();
com.sun.glass.ui.Window.getWindows().get(0).setLevel(3);
// Set window level to 3 (Maximum)
}
}
I want to know if this is possible with JavaFX or any other workaround. I want my view to be over any application, even full screen.
Related
I'm using Eclipse to write javafx code for my university courses. When I run the code either in Eclipse, or after exporting it as a jar file, The program sometimes freezes.
For Example:
Normal box for data entry:
I have this window, where values for a rectangle can be entered.
If you press "submit" without adding both values, a NumberFormatException occurs, which I catch like so:
catch (NumberFormatException ex) {
System.err.println("NumberFormatException: " + ex.getMessage());
Alert alert = new Alert(AlertType.ERROR, "Bitte alle Felder ausfüllen", ButtonType.OK);
alert.showAndWait();
The alert window sometimes appears and works fine. Sometimes (rarely) it does 10 times in a row. Other times I get this:
Error State:
The program is now frozen and nothing is clickable. There is a thin, black line through one of the textfields. The program never recovers and has to be manually terminated through Eclipse or the task manager.
This sometimes happens on the first click on submit. Sometimes on the 5th, or later. But it always eventually happens. The program works fine in all other instances, so long as this alert is not triggered too often.
I have also had it happen in another project, where the trigger was a new window for data entry opening, much like the one I have here, but this time, only the alert freezes the program. The data entry window can be opened as many times as I like.
I'm using (course mandated) JavaFX11 and JavaSE-11 in Eclipse, on Manjaro.
Minimal reproducible example:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.GridPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
GridPane root = new GridPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Button submit = new Button("submit");
Alert alert = new Alert(AlertType.ERROR, "Bitte alle Felder ausfüllen", ButtonType.OK);
submit.setOnAction(e -> alert.show());
root.add(submit,0,0);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Opens a simple GridPane with just the submit button. If I click it, it sometimes shows the alert, but it often freezes as described above. I can see no pattern in the freezes. It's not always the first time, or always the second. Sometimes it's immediate, sometimes it works 3 or 4 times, then freezes on the next submit.
There is no error message in the console.
Edit: Further experimentation has shown, that the window does not freeze. If you hit return, it hits the button in the alert, which closes it and lets me use the rest of the program.
So it seems the error is in the window not being displayed correctly and only showing up as a thin black line. Which is still a problem, because it also happens with windows where I can't just hit enter to close them, which leaves me stuck.
Turns out it was the issue described here: https://github.com/javafxports/openjdk-jfx/issues/222
Plasma-KDE has a bug that prevents these kinds of windows from opening. There's a workaround posted if it's a one time thing.
I fixed it by switching to xfce.
I'm trying to create a simple data entrty application with javafx and ran into a problem when adding a scene control. The display loses it's fill colour even BEFORE i've added the control to the scene! Simply instantiating the control breaks it.
I was running on Oracle java 8 on windows, but I;ve tried OpenJDK 8 on Windows and OpenJDk/OpenJFX 13 on linux and all behave identically. I stripped out the code to the bear minmium to recreate the problem.
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
public class BasicTest extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle r = new Rectangle();
r.setWidth(200);
r.setHeight(50);
r.setFill(Color.BLUE);
r.setStroke(Color.WHITE);
r.setStrokeWidth(2);
Text t = new Text();
t.setText("Confirm");
t.setFill(Color.WHITE);
t.setFont(Font.font("null", 40));
StackPane sp = new StackPane(r);
sp.getChildren().add(t);
sp.setMaxWidth(200);
t.setTranslateY(-2);
Label b = new Label("Click me");//Comment this line out after first run
Scene scene = new Scene(sp, 300, 200);
scene.setFill(Color.BLUE);
primaryStage.setScene(scene);
primaryStage.show();
}
}
With the label commented out the scene background is blue so I get a white "Confirm" with white outline. Just adding the label constructor will make the scene background go grey.
Thanks for all the comments. Many apologies if my wording was confusing. I did add some images to show the difference but they seem to have disappeared. Anyway my thanks to #Matt for their very succinct description (which I wish I'd have thought of!). I like the mouse click idea too.
I was probably being very naughty asking this "question" on SO when I firmly believe this is a bug in javafx. Instantiating an object that goes no where near (yet) the scene graph should have no effect on it in my opinion - CSS or no CSS. I will raise a bug against javafx.
However I knew the power of SO would help me and it has!
Thanks to the clue by #jewelsea I simply replaced my scene.setFill() (which was only a test anyway) with CSS and the problem is circumvented. I can even add the control to the scene now and it works as expected. From the JavaFX CSS Reference:
"The Scene object has no settable CSS properties, nor does it have any pseudo-classes. However, the root node of the scene is assigned the style class "root" (in addition to style classes already assigned to the node). "
So I set up in my css:
.root {
-fx-background-color: blue;
}
Another way to circumvent this is to simply set the background of the StackPane to BLUE:
sp.setBackground(new Background(new BackgroundFill(Color.BLUE, null, null)));
Below is the code sample, with other features left out. The code below encompasses the media player only. It's used on a menu screen for a project I'm working on. My issue is getting the musicButton (which is a toggle button- On/Off) to work properly. Using the following code, when I interact with the music toggle button, the playing music stops. When I click it again to resume playing, it does not resume. It stops once and stops altogether.
You can see I've tried simply using the boolean values of the toggle button in two if statements... If it's off and pressed, pause the music. If its on and pressed, resume the music. The problem is, as stated earlier, pausing the music works but it cannot be resumed. I've tried some combinations with loops, but nothing worked either.
I think if statements are too simple for this. I've scoured the JavaDocs and various online articles but I cannot find anything definitive. I've read a little about listeners, but they seem overly-complex for an on/off switch.
My question:
How do I get the musicButton to pause/play the music, whenver the user clicks it?
Any help is appreciated, thanks.
-Bagger
/* A simple game, the mechanics not yet implemented.
This is simply working on the title screen. */
import java.io.File;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
public class MenuFX extends Application {
#Override
public void start (Stage primaryStage) {
// Make the window a set size...
primaryStage.setResizable(false);
// Create media player
// Rather than inputting the entire absolute URI, which would confine the program
// to the creator's device, we create a new file, grab the URI on whatever machine
// the program is running on and convert it to a string... portability.
Media menuMusic = new Media(new File("music/menu.mp3").toURI().toString());
MediaPlayer menuPlayer = new MediaPlayer(menuMusic);
// Want to see the absolute URI? Uncomment the next line
//System.out.println(new File("music/menu.mp3").toURI().toString());
// Adjust the cycles and volume then start playing menu music
// Lazy, but it will suffice
menuPlayer.setCycleCount(999999999);
menuPlayer.setVolume(0.1);
menuPlayer.setAutoPlay(true);
/*
Need assistance here
*/
// Create music toggle button
ToggleButton musicButton = new ToggleButton("Music On/Off");
if (musicButton.isSelected() == false) {
musicButton.setOnAction(e -> menuPlayer.pause());
}
if (musicButton.isSelected() == true) {
musicButton.setOnAction(e -> menuPlayer.play());
}
// Add all nodes to the vbox pane and center it all
// Must be in order from top to bottom
menuVBox.getChildren().add(musicButton);
menuVBox.setAlignment(Pos.CENTER);
// New scene, place pane in it
Scene scene = new Scene(menuVBox, 630, 730);
// Place scene in stage
primaryStage.setTitle("-tiles-");
primaryStage.setScene(scene);
primaryStage.show();
}
// Needed to run JavaFX w/o the use of the command line
public static void main(String[] args) {
launch(args);
}
}
I think you are over thinking it. You should have an EventListener on your ToggleButton to Pause and Play the music.
musicButton.setOnAction(event -> {
if (musicButton.isSelected()) {
menuPlayer.pause();
}else {
menuPlayer.play();
}
});
This should give you the desired effect.
The reason your code was not working is because the ToggleButton is not selected by default, so the only EventListener that gets associated with it is the menuPlayer.pause();. So when you click on it, it only ever pauses. I have moved your code into one EventListener, and used the approriate if-else.
I have a node I want to implement drag and drop for (this object is the source not the target). I also want the object to move along with the mouse cursor. I managed to do both of these but not at the same time.
It appears that setOnDragDetected and setOnMouseDragged don't work well together. Consider a node with the following handlers:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Example extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle rect = new Rectangle(20, 20);
rect.setOnMousePressed(e -> System.out.println("Pressed"));
rect.setOnMouseDragged(e -> System.out.println("Dragged"));
rect.setOnDragDetected(e -> {
System.out.println("Detected");
ClipboardContent content = new ClipboardContent();
content.putString("something");
Dragboard db = rect.startDragAndDrop(TransferMode.ANY);
db.setContent(content);
});
Group subGroup = new Group(rect);
Scene scene = new Scene(subGroup, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Example.launch(args);
}
}
Now press the mouse on the node and move the mouse. This is the output:
Pressed
Dragged
Dragged
Dragged
Dragged
Dragged
Dragged
Detected
Once the drag is detected the MouseDragged handler stops.
How do I achieve what I described? One thing i noticed was maybe that I can use a onDragOver for the parent but i want the behavior to be in the node because that's where it really should be.
You are mixing two things up here. In short, when you call the startDragAndDrop method the system switches to drag and drop mode and Java stops delivering MouseEvent to rect.
The MouseEvent documentation has a section "Dragging gestures" which explains the three types of dragging gestures. Here is just a short summary:
simple press-drag-release - when a drag is detected Java continues to deliver MouseEvents to the node where the drag was detected.
full press-drag-release - you can call startFullDrag inside the handler you set with setOnDragDetected. Then Java also starts to deliver MouseDragEvents to other nodes (potential gesture targets).
platform-supported drag-and-drop - if you call startDragAndDrop inside the OnDragDetected handler, Java will stop to deliver MouseEvents and start to deliver DragEvents instead. This is used for drag and drop interaction with other applications.
It is not clear to me what you want to achieve, but as long as you do not want to drag something outside of your application, try using startFullDrag instead.
Also, it might be helpful to have a further look at the DragEvent and MouseDragEvent documentation.
I'd like a Stage that is the same size as the screen which is fully transparent and receives mouse events anywhere. In the example below I get mouse events only when the mouse is over the circle. I see this issue on both Windows XP and Windows 7 using Java 8u11
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage ignored) throws Exception {
Stage stage = new Stage(StageStyle.TRANSPARENT);
stage.setTitle("Transparent app test");
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
stage.setX(0);
stage.setY(0);
stage.setWidth(screenBounds.getWidth());
stage.setHeight(screenBounds.getHeight());
Circle circle = new Circle(100);
circle.setFill(Color.RED);
Rectangle rectangle = new Rectangle(screenBounds.getWidth(),
screenBounds.getHeight());
rectangle.setFill(Color.TRANSPARENT);
Scene scene = new Scene(new StackPane(circle, rectangle));
scene.setFill(null);
stage.setScene(scene);
scene.setOnMouseMoved((e) -> {
System.out.println("Mouse over rectangle " + e);
});
stage.show();
}
}
Interestingly if I set the alpha part of the fill color to its absolute minimum then I get mouse events. However I'd prefer not to use this workaround and actually get to the bottom of the issue. My conclusion is somewhere in JavaFX or a Windows library there is some hit-detection code that filters mouse events based on the pixel value of the mouse event.
rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events
rectangle.setFill(Color.rgb(0, 0, 0, 0)); // does not receive mouse events
Research
JavaFx Transparent window - yes please. Mouse transparent - no thanks describes a similar problem, however it does not address the issue of mouse events in completely transparent areas
Debugging - using a breakpoint in the setOnMouseMoved() I've examined the preceding stackframes to try to find the hit-detection code.
Used JNA to test different styles such as WS_EX_TRANSPARENT and WS_EX_LAYERED. Interestingly WS_EX_TRANSPARENT made the window fully mouse transparent - no mouse events over the painted pixels.
Tried putting the mouse listener on the rectangle/StackPane instead - no difference
MSDN article Layered Windows hints at this functionality being part of Windows rather than JavaFX. If this is true is there any workaround?
Hit testing of a layered window is based on the shape and transparency
of the window. This means that the areas of the window that are
color-keyed or whose alpha value is zero will let the mouse messages
through. If the layered window has the WS_EX_TRANSPARENT extended
window style, the shape of the layered window will be ignored and the
mouse events will be passed to the other windows underneath the
layered window.
In summary only known solution is to set the background to be "not quite" transparent to fool JavaFX into sending events.
rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events