By PrinterJob of JavaFx can call the Print Dialog. My problem is that the dialog when calling does not come to the fore.
Here is my example:
import javafx.application.Application;
import javafx.print.Printer;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Printexample extends Application
{
#Override
public void start( final Stage primaryStage )
{
final PrinterJob job = PrinterJob.createPrinterJob( Printer.getDefaultPrinter() );
final Button b = new Button( "Print Dialog" );
b.setOnAction( event -> job.showPrintDialog( primaryStage ) );
final BorderPane pane = new BorderPane( b );
primaryStage.setMinWidth( 400 );
primaryStage.setMinHeight( 300 );
primaryStage.setTitle( "Print" );
final Scene scene = new Scene( pane );
primaryStage.setScene( scene );
primaryStage.centerOnScreen();
primaryStage.addEventFilter( KeyEvent.KEY_PRESSED, event ->
{
if ( event.getCode().equals( KeyCode.ESCAPE ) )
{
primaryStage.close();
}
} );
primaryStage.show();
}
public static void main( final String[] args )
{
launch( args );
}
}
The second problem: The frame is not modal, therefore it can lead to errors.
Information: I use Java 8_92.
Probably a current limitation of JavaFX as described by JDK-8088395.
So you have these options:
Wait this to eventually be fixed in an update or JavaFX 9.
Write yourself a custom dialog and then communicate with the print APIs to populate it, as suggested in JDK-8098009.
Block your scene using a overlay, show the print dialog and then remove the overlay. You'll also need to prevent the window closing while the scene is blocked.
Use AWT Print Dialog (kludge, you've been warned), e.g.:
java.awt.print.PrinterJob printJob = PrinterJob.getPrinterJob();
Button b = new Button("Print Dialog");
b.setOnAction(event -> {
JFrame f = new JFrame();
printJob.printDialog();
// Stage will be blocked(non responsive) until the printDialog returns
});
These Problem happen to me today with a spring boot application and the same Code under Windows 10.
I write here the solution, that for me works, because it was the same issue.
Spring is setting by default the java.awt.headless Property with true.
In the Implementation of showPrintDialog Method there is a bug checking about headless returning true but not showing the dialog.
The solution for me was setting the headless property in Spring as false as described Run Spring Boot Headless by Efe Kahraman
I think you might be missing a peice of code to send the stage to the front.
Try adding: stage.toFront();
Source: http://www.java2s.com/Code/Java/JavaFX/Movingstagewindowtofront.htm
Related
I have a JavaFX application and I need simple to avoid user from opening the same window inside the application more than once.
I tried to find some solution, but nothing get applicable.
As a sample... I have a window that give me payments options, its not a modal, it's a new stage. While I click the button to open that window, it's open, doesn't matter if there is another instance of this same stage running, simple open new windows every click. I want to avoid this. Like switch to the already opened stage window when click the button, or simply miss the click if that window is already opened.
You just need to keep track of the stage and only open a new one if its not already shown. You could also choose to disable the Button if the new window is showing, but I prefer to have the new window simply brought back in front so the user knows it's there.
You can do this by creating a reference to your Stage and then checking if it is null or showing within the button's event handler.
Here is an MCVE to demonstrate:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
/**
* Reference to the new Window that will allow only one instance at a time.
*/
private Stage newWindowStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button openWindow = new Button("Open Window");
// **********************************************************************************************
// Set the button to open the new Window Stage
// **********************************************************************************************
openWindow.setOnAction(event -> {
// **********************************************************************************************
// Check if the Stage is already showing.
// **********************************************************************************************
if (newWindowStage == null || !newWindowStage.isShowing()) {
// **********************************************************************************************
// The new window is not currently open, so create/show it
// **********************************************************************************************
newWindowStage = new Stage();
newWindowStage.setWidth(300);
newWindowStage.setHeight(300);
newWindowStage.setScene(new Scene(
new VBox(
new Label("New Window!")
)
));
newWindowStage.show();
} else {
// **********************************************************************************************
// The window is already open, so bring it to the front of focus
// **********************************************************************************************
newWindowStage.toFront();
}
});
root.getChildren().add(openWindow);
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(200);
primaryStage.setHeight(200);
primaryStage.setTitle("Test Application");
primaryStage.show();
}
}
Overview
Using FlyingSaucer within a JavaFX application, to avoid WebView for various reasons:
doesn't provide direct API access to its scrollbars for synchronous behaviour;
bundles JavaScript, which is a huge bloat for my use case; and
failed to run on Windows.
FlyingSaucer uses Swing, which requires wrapping its XHTMLPanel (a subclass of JPanel) in a SwingNode to use alongside JavaFX. Everything works great, the application renders Markdown in real-time, and is responsive. Here's a demo video of the application running on Linux.
Problem
The text rendering on Windows is blurry. When running in a JFrame, not wrapped by a SwingNode, but still part of the same application shown in the video, the quality of the text is flawless. The screen capture shows the application's main window (bottom), which includes the SwingNode along with the aforementioned JFrame (top). You may have to zoom into the straight edge of the "l" or "k" to see why one is sharp and the other blurry:
This only happens on Windows. When viewing the font on Windows through the system's font preview program, the fonts are antialiased using LCD colours. The application uses grayscale. I suspect that if there is a way to force the rendering to use colour for antialiasing instead of grayscale, the problem may disappear. Then again, when running within its own JFrame, there is no problem and LCD colours are not used.
Code
Here's the code for the JFrame that has a perfect render:
private static class Flawless {
private final XHTMLPanel panel = new XHTMLPanel();
private final JFrame frame = new JFrame( "Single Page Demo" );
private Flawless() {
frame.getContentPane().add( new JScrollPane( panel ) );
frame.pack();
frame.setSize( 1024, 768 );
}
private void update( final org.w3c.dom.Document html ) {
frame.setVisible( true );
try {
panel.setDocument( html );
} catch( Exception ignored ) {
}
}
}
The code for the blurry SwingNode is a little more involved (see full listing), but here are some relevant snippets (note that HTMLPanel extends from XHTMLPanel only to suppress some undesired autoscrolling during updates):
private final HTMLPanel mHtmlRenderer = new HTMLPanel();
private final SwingNode mSwingNode = new SwingNode();
private final JScrollPane mScrollPane = new JScrollPane( mHtmlRenderer );
// ...
final var context = getSharedContext();
final var textRenderer = context.getTextRenderer();
textRenderer.setSmoothingThreshold( 0 );
mSwingNode.setContent( mScrollPane );
// ...
// The "preview pane" contains the SwingNode.
final SplitPane splitPane = new SplitPane(
getDefinitionPane().getNode(),
getFileEditorPane().getNode(),
getPreviewPane().getNode() );
Minimal Working Example
Here's a fairly minimal self-contained example:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.xhtmlrenderer.simple.XHTMLPanel;
import javax.swing.*;
import static javax.swing.SwingUtilities.invokeLater;
import static javax.swing.UIManager.getSystemLookAndFeelClassName;
import static javax.swing.UIManager.setLookAndFeel;
public class FlyingSourceTest extends Application {
private final static String HTML = "<!DOCTYPE html><html><head" +
"><style type='text/css'>body{font-family:serif; background-color: " +
"#fff; color:#454545;}</style></head><body><p style=\"font-size: " +
"300px\">TEST</p></body></html>";
public static void main( String[] args ) {
Application.launch( args );
}
#Override
public void start( Stage primaryStage ) {
invokeLater( () -> {
try {
setLookAndFeel( getSystemLookAndFeelClassName() );
} catch( Exception ignored ) {
}
primaryStage.setTitle( "Hello World!" );
final var renderer = new XHTMLPanel();
renderer.getSharedContext().getTextRenderer().setSmoothingThreshold( 0 );
renderer.setDocument( new W3CDom().fromJsoup( Jsoup.parse( HTML ) ) );
final var swingNode = new SwingNode();
swingNode.setContent( new JScrollPane( renderer ) );
final var root = new SplitPane( swingNode, swingNode );
// ----------
// Here be dragons? Using a StackPane, instead of a SplitPane, works.
// ----------
//StackPane root = new StackPane();
//root.getChildren().add( mSwingNode );
Platform.runLater( () -> {
primaryStage.setScene( new Scene( root, 300, 250 ) );
primaryStage.show();
} );
} );
}
}
Blurry capture from the minimal working example;
zooming in reveals letter edges are heavily antialiased rather than sharp contrasts:
Using a JLabel also exhibits the same fuzzy render:
final var label = new JLabel( "TEST" );
label.setFont( label.getFont().deriveFont( Font.BOLD, 128f ) );
final var swingNode = new SwingNode();
swingNode.setContent( label );
Attempts
Here are most of the ways I've tried to remove the blur.
Java
On the Java side, someone suggested to run the application using:
-Dawt.useSystemAAFontSettings=off
-Dswing.aatext=false
None of the text rendering hints have helped.
Setting the content of the SwingNode within SwingUtilities.invokeLater has no effect.
JavaFX
Someone else mentioned that turning caching off helped, but that was for a JavaFX ScrollPane, not one within a SwingNode. It didn't work.
The JScrollPane contained by the SwingNode has its alignment X and alignment Y set to 0.5 and 0.5, respectively. Ensuring a half-pixel offset is recommended elsewhere. I cannot imagine that setting the Scene to use StrokeType.INSIDE would make any difference, although I did try using a stroke width of 1 to no avail.
FlyingSaucer
FlyingSaucer has a number of configuration options. Various combinations of settings include:
java -Dxr.text.fractional-font-metrics=true \
-Dxr.text.aa-smoothing-level=0 \
-Dxr.image.render-quality=java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC
-Dxr.image.scale=HIGH \
-Dxr.text.aa-rendering-hint=VALUE_TEXT_ANTIALIAS_GASP -jar ...
The xr.image. settings only affect images rendered by FlyingSaucer, rather than how the output from FlyingSaucer is rendered by JavaFX within the SwingNode.
The CSS uses points for the font sizes.
Research
https://stackoverflow.com/a/26227562/59087 -- looks like a few solutions may be helpful.
https://bugs.openjdk.java.net/browse/JDK-8089499 -- doesn't seem to apply because this is using SwingNode and JScrollPane.
https://stackoverflow.com/a/24124020/59087 -- probably not relevant because there is no XML scene builder in use.
https://www.cs.mcgill.ca/media/tech_reports/42_Lessons_Learned_in_Migrating_from_Swing_to_JavaFX_LzXl9Xv.pdf -- page 8 describes shifting by 0.5 pixels, but how?
https://dlsc.com/2014/07/17/javafx-tip-9-do-not-mix-swing-javafx/ -- suggests not mixing JavaFX and Swing, but moving to pure Swing isn't an option: I'd sooner rewrite the app in another language.
Accepted as a bug against OpenJDK/JavaFX:
https://bugs.openjdk.java.net/browse/JDK-8252255
JDK & JRE
Using Bellsoft's OpenJDK with JavaFX bundled. To my knowledge, the OpenJDK has had Freetype support for a while now. Also, the font looks great on Linux, so it's probably not the JDK.
Screen
The following screen specifications exhibit the problem, but other people (viewing on different monitors and resolutions, undoubtedly) have mentioned the issue.
15.6" 4:3 HD (1366x768)
Full HD (1920x1080)
Wide View Angle LED Backlight
ASUS n56v
Question
Why does FlyingSaucer's XHTMLPanel when wrapped within SwingNode become blurry on Windows, and yet displaying the same XHTMLPanel in a JFrame running in the same JavaFX application appears crisp? How can the problem be fixed?
The problem involves SplitPane.
There are a few options that you might try although I have to admit that I do not know FlyingSaucer and its API.
FlyingSaucer has different renderers. Thus it might be possible to avoid the Swing/AWT rendering completely by using this library instead in order to do all the rendering directly in JavaFX. https://github.com/jfree/fxgraphics2d
Another possibility is to let FlyingSaucer render into an image which can the be displayed in JavaFX very efficiently via direct buffers. See the AWTImage code in my repository here: https://github.com/mipastgt/JFXToolsAndDemos
I wasn't able to reproduce the issue myself, so there may be some issue in the combination of JDK/JavaFX version you are using. It's also possible that the issue only arises with a specific combination of display size and screen scaling.
My setup is the following:
JavaFX 14
OpenJDK 14
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.jsoup.nodes.Document;
import org.xhtmlrenderer.simple.XHTMLPanel;
import javax.swing.*;
public class FlyingSourceTest extends Application {
private final static String HTML_PREFIX = "<!DOCTYPE html>\n"
+ "<html>\n"
+ "<body>\n";
private static final String HTML_CONTENT =
"<p style=\"font-size:500px\">TEST</p>";
private final static String HTML_SUFFIX = "<p style='height=2em'> </p></body></html>";
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
primaryStage.setTitle("Hello World!");
XHTMLPanel mHtmlRenderer = new XHTMLPanel();
mHtmlRenderer.getSharedContext().getTextRenderer().setSmoothingThreshold(0);
SwingNode mSwingNode = new SwingNode();
JScrollPane mScrollPane = new JScrollPane(mHtmlRenderer);
String htmlContent = HTML_PREFIX + HTML_CONTENT + HTML_SUFFIX;
Document jsoupDoc = Jsoup.parse(htmlContent);
org.w3c.dom.Document w3cDoc = new W3CDom().fromJsoup(jsoupDoc);
mHtmlRenderer.setDocument(w3cDoc);
mSwingNode.setContent(mScrollPane);
// AnchorPane anchorPane = new AnchorPane();
// anchorPane.getChildren().add(mSwingNode);
// AnchorPane.setTopAnchor(mSwingNode, 0.5);
// AnchorPane.setLeftAnchor(mSwingNode, 0.5);
// mSwingNode.setTranslateX(0.5);
// mSwingNode.setTranslateY(0.5);
StackPane root = new StackPane();
root.getChildren().add(mSwingNode);
Platform.runLater(() -> {
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
});
});
}
}
The issue has been accepted as a bug against OpenJDK/JavaFX:
https://bugs.openjdk.java.net/browse/JDK-8252255
Neither of Mipa's suggestions would work in practice. FlyingSaucer is tightly integrated with a JScrollPane, which precludes the possibility of forcing FlyingSaucer to render onto a JavaFX-based panel.
Another possibility is to go the opposite direction: create a Swing application and embed JavaFX controls, such as using a JFXPanel; however, it would seem more prudent to accept the blurry behaviour until the bug is bashed.
really liking JavaFX but have come across this problem and wondered if it was a bug.
The ScrollBar.setOnMousePressed() doesn't seem to fire when it has been initialised with a handler. The code below demonstrates the problem:-
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Play extends Application {
public static void main(String[] args) {
launch(args);
}
private static int cnt;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Bug?");
Button btn = new Button("This text will get replaced by the event handlers");
ScrollBar scrollBar = new ScrollBar();
// When pressing and releasing the ScrollBar thumb, we only get decrements
// If you replace the ScrollBar with say a Button, then the code below works as you might expect.
scrollBar.setOnMousePressed( event -> btn.setText("X" + cnt++));
scrollBar.setOnMouseReleased( event -> btn.setText("X" + cnt--));
VBox root = new VBox();
root.getChildren().add(btn);
root.getChildren().add(scrollBar);
primaryStage.setScene(new Scene(root, 350, 250));
primaryStage.show();
}
}
Note, Im running on JDK 1.8.0_66 64 Bit on Microsoft Windows 10.
A simple workaround, as suggested by James_D, is to use EventFilters instead of setOnMousePressed(), as follows:-
So,
scrollBar.addEventFilter(MouseEvent.MOUSE_PRESSED,
event -> btn.setText("X" + cnt++));
instead of
scrollBar.setOnMousePressed( event -> btn.setText("X" + cnt++));
I believe .setOnMousePressed() should work, but doesn't because of a bug in the library. I've raised with oracle and will update the answer once oracle clarifies.
I have trouble when closing a window in JavaFX.
I define my setOnCloseRequest as I wanted and it works when I click the x in the window. However, I also need a button to close the window and this onCloseRequest has to work, the problem is it does not. The event does not fire at all.
I am using JavaFX 2.2 (Java 7) and I notice that the reference for setOnCloseRequest says close the window on external request
Solution
Fire an event from your internal close request (on the button push), so that the application thinks it received an external close request. Then your close request logic can be identical whether the request came from an external event or an internal one.
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
// close event handling logic.
// consume the event if you wish to cancel the close operation.
}
...
stage.setOnCloseRequest(confirmCloseEventHandler);
Button closeButton = new Button("Close Application");
closeButton.setOnAction(event ->
stage.fireEvent(
new WindowEvent(
stage,
WindowEvent.WINDOW_CLOSE_REQUEST
)
)
);
Note
This is a Java 8+ solution, for JavaFX 2, you will need to convert the lambda functions in anonymous inner classes and will be unable to use the Alert dialog box but will need to provide your own alert dialog system as JavaFX 2 does not feature an in-built one. I strongly recommend upgrading to Java 8+ rather than staying with JavaFX 2.
Sample UI
Sample Code
The sample code will show the user a close confirmation alert and cancel the close request if the user does not confirm the close.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.*;
import javafx.stage.WindowEvent;
import java.util.Optional;
public class CloseConfirm extends Application {
private Stage mainStage;
#Override
public void start(Stage stage) throws Exception {
this.mainStage = stage;
stage.setOnCloseRequest(confirmCloseEventHandler);
Button closeButton = new Button("Close Application");
closeButton.setOnAction(event ->
stage.fireEvent(
new WindowEvent(
stage,
WindowEvent.WINDOW_CLOSE_REQUEST
)
)
);
StackPane layout = new StackPane(closeButton);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
Alert closeConfirmation = new Alert(
Alert.AlertType.CONFIRMATION,
"Are you sure you want to exit?"
);
Button exitButton = (Button) closeConfirmation.getDialogPane().lookupButton(
ButtonType.OK
);
exitButton.setText("Exit");
closeConfirmation.setHeaderText("Confirm Exit");
closeConfirmation.initModality(Modality.APPLICATION_MODAL);
closeConfirmation.initOwner(mainStage);
// normally, you would just use the default alert positioning,
// but for this simple sample the main stage is small,
// so explicitly position the alert so that the main window can still be seen.
closeConfirmation.setX(mainStage.getX());
closeConfirmation.setY(mainStage.getY() + mainStage.getHeight());
Optional<ButtonType> closeResponse = closeConfirmation.showAndWait();
if (!ButtonType.OK.equals(closeResponse.get())) {
event.consume();
}
};
public static void main(String[] args) {
launch(args);
}
}
In Swing I was to add JTextArea into JScrollPane in order JTextArea to have scroll bars. When I do the same in JavaFX, the behavior is different.
This example
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
public class SlideshowForSlipryPage2 extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SlideshowForSlipryPage");
primaryStage.setScene(
new Scene(
new ScrollPane(
new TextArea() {{
setPromptText("[PROMPT 1]");
}}
)
, 300, 250
)
);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
gives
i.e. text area is wider than a window and shorter than it.
Why and how to fix?
This issue has nothing to do with the pref width of the text area.
You must set the wrap property to true so that the text will wrap onto two lines when the cursor hits the edge of the container. 2 ways:
1) Using scene builder is the most simple way. Simple click "Wrap Text" in the properties of the TextArea.
2) Call the following method of the TextArea:
textArea.setWrapText(true);
You have to call setFitToWidth(true) and setFitToHeight(true) on the ScrollPane, you can set those properties in the scene builder Layout tab after selecting the ScrollPane.
Try this:
textArea.setPrefSize( Double.MAX_VALUE, Double.MAX_VALUE );
and set scrollpane size same as parent like this:
scrollPane.prefWidthProperty().bind(<parentControl>.prefWidthProperty());
scrollPane.prefHeightProperty().bind(<parentConrol>.prefHeightProperty());
I hope it resolves your query !!!