I have read the questions asked similar problem but it doesn't help me.My programs are all up to date and running with SDK 12 and Java Language Level 11.
I created an extended Jpanel and i want to add it to Gui form with Non-Palette but when i choose the class the name becomes red and nothing happens.
here is my extended Jpanel:
public class Panelback extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = null;
try {
image = ImageIO.read(new File("C:....bg.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
g.drawImage(image, 0, 0, null);
}
}
when i try to add it to GUI form and click ok nothing happens.enter image description here
Related
My question is how to display more than one user interface screen per program. I'm sure this question has been asked before, but I haven't found a solution that works for me (or should I say that I understand). There isn't anything exotic about the scenarios I'm talking about. The first is simply validating inputs from a screen and re-displaying the same screen in the case of errors.
I'll pose the question in terms of the second more complicated scenario: displaying an input data screen, processing the inputs; and then displaying the outputs. This complicated somewhat by the fact that the first, a simple screen with 5 text boxes and a command button, uses an FXML file, whereas the second, a multi-select list box does not. The flow is:
1. Main program calls
2. A loader program which loads the FXML and somehow or another calls
3. A controller which receives the inputs and processes them to produce output.
The final step is to display the output in the form of a multi-select list box. Note that the first GUI employs a controller, which is a separate file, to process the inputs, whereas the second uses an event handler, which is in the same file as the screen definition, to make the selection(s) when the user clicks a command button.
Various SO posts have said that the way to go is to not shut down the application once the first GUI has completed via but Keep the JavaFX run time going in the background with
Platform.setImplicitExit(false);
and to define each GUI and simply switch scenes to the one you want to display. But where, given the scenario I described do you put the code? The second GUI has three pieces: screen definition, event handler(s), and scene switching code. Where do you put each? #2 or #3. If you put some in #2 and some in #3, how does #3 know what you did in #2?
The code for #2 the FMXL loader:
public class inputData extends Application {
public static void load() {
launch();
}
#Override
public void start(Stage stage) throws Exception {
GridPane inpRoot = FXMLLoader.load(getClass().getResource("inputData.fxml"));
Scene inpScene = new Scene(inpRoot, 300, 275);
stage.setTitle("Amsnag 2.1 - Query Input");
stage.setScene(inpScene);
stage.show();
}
}
Code for #3, the list box definition and handlers, which worked fine running separately. It's only when I tried to incorporate it with the rest of the program that it failed.
public class multiList extends Application {
public static void load() {
launch();
}
public static final ObservableList options = FXCollections.observableArrayList();
#Override
public void start(final Stage stage) {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// lcreate temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
} ); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
layout.getChildren().setAll(listView, controls);
layout.setPrefWidth(320);``enter code here
Scene scene = new Scene(layout);
// stage.setScene(new Scene(layout));
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
public static void main(String[] args) { launch(args); }
}
You can't reuse an Application subclass in a different application.
The Application class represents an entire application, or perhaps more specifically its lifecycle. So it has methods such as init(), start(), and stop() which are invoked by the FX Application Toolkit at the appropriate moments in the lifecycle of the application.
The layout for your multiList (aside: please use proper naming conventions) class is performed in the start() method, so it can only happen at the start of the application. By putting the layout code here, you make it impossible to reuse so that it is performed at a later point in a different application.
So move the layout for MultiList to a separate class:
public class MultiList {
public static final ObservableList options = FXCollections.observableArrayList();
private final VBox view ;
public MultiList() {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// create temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
} catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
}); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
view = new VBox(10);
view.setAlignment(Pos.CENTER);
view.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
view.getChildren().setAll(listView, controls);
view.setPrefWidth(320);
}
public Parent getView() {
return view ;
}
}
Now if you want to test this out on its own, you can write an application for it:
public class MultiListApp extends Application {
#Override
public void start(Stage primaryStage) {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
primaryStage.setScene(scene);
primarStage.setTitle("Select one or more options");
primaryStage.show();
}
}
Or in the controller class for InputData.fxml, you can do the same thing:
public class InputDataController {
#FXML
private void someEventHandler() {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
}
When opening a new dialog, while its loading, you click couple of times on parent shell, apparently the new dialog does not display correctly.
Please see the example below:
Examples
https://i.stack.imgur.com/ZovxE.png (eclipse IDE example)
https://i.stack.imgur.com/5zVar.png
https://i.stack.imgur.com/u86b9.png
https://i.stack.imgur.com/FGaAr.png
Initially I encountered the problem in december 2014, and back then also reported by vaious in house devlopers which were using different development systems and then same problem has been reported by our several customers.
This behavior can be reproduced using following environment:
Windows Version: 7 Pro 64 Bit - 6.1.7601
Java Version: RE 1.8.0_121_b13
SWT Versions
3.8.2
4.6.2
4.7M6
I20170319-2000
I could only reproduce the problem on Windows 7 with the windows basic theme/design/style (not with classic or aero).
On windows 10 its not reproducible.
reproduce
code to reproduce
package test;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class Main {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = createShell(display);
createButton(shell);
shell.open();
eventLoop(display, shell);
display.dispose();
}
private static Shell createShell(Display display) {
final Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
shell.setSize(500, 200);
return shell;
}
private static void createButton(final Shell shell) {
final Button openDialog = new Button(shell, SWT.PUSH);
openDialog.setText("Click here to open Dialog ...");
openDialog.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
TestDialog inputDialog = new TestDialog(shell);
inputDialog.open();
}
});
}
private static void eventLoop(Display display, final Shell shell) {
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
class TestDialog extends Dialog {
public TestDialog(Shell parent) {
super(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.MAX | SWT.RESIZE);
setText("Dialog");
}
public void open() {
Shell shell = new Shell(getParent(), getStyle());
shell.setText(getText());
createContents(shell);
shell.pack();
initializeBounds(shell);
shell.open();
eventLoop(shell);
}
private void createContents(final Shell shell) {
shell.setLayout(new GridLayout(2, true));
Label label = new Label(shell, SWT.NONE);
label.setText("Some Label text ...");
final Text text = new Text(shell, SWT.BORDER);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
text.setLayoutData(data);
createCloseButton(shell);
/* time for the user to create the misbehavior */
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void createCloseButton(final Shell shell) {
Button closeButton = new Button(shell, SWT.PUSH);
closeButton.setText("Close");
GridData data = new GridData(GridData.FILL_HORIZONTAL);
closeButton.setLayoutData(data);
closeButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
shell.close();
}
});
shell.setDefaultButton(closeButton);
}
private void initializeBounds(Shell shell) {
Rectangle bounds = shell.getBounds();
Rectangle parentBounds = getParent().getBounds();
bounds.x = parentBounds.x;
bounds.y = parentBounds.y;
shell.setBounds(bounds);
}
private void eventLoop(Shell shell) {
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
steps to reproduce
Start the application
it should look like: https://i.stack.imgur.com/dMJ9e.png
Click on the button.
Keep continuously clicking on right bottom corner of the parent shell (avoid hitting the new opening dialog), till mouse cursor changes to wait icon and parent shell changes its color.
it should look as following: https://i.stack.imgur.com/c1Ikp.png
Wait until the new dialog appears.
it looks likes as following: https://i.stack.imgur.com/kTDgQ.png (incorrectly displayed)
instead: https://i.stack.imgur.com/cHVjn.png (correctly displayed)
steps to reproduce done in video
https://youtu.be/7ukhloCPf0k
When you mouse hover some of the UI elements (the originally not correctly drawn), you can notice some of them to be get painted (e.g. table rows).
https://i.stack.imgur.com/kkMKn.png (before opening the dialog)
https://i.stack.imgur.com/ZXIKc.png (after opening the dialog)
https://i.stack.imgur.com/25M7S.jpg (after mouse over)
Even calling Shell.update() or Shell.redraw() after the Dialog opened does not fix it.
In Windows Performance Options -> Visual Effects -> disable "Use visual styles on windows and buttons" is the only option I found which provides a workaround,
which seems to be the same as changing the design/theme/style to classic.
https://www.sevenforums.com/tutorials/1908-visual-effects-settings-change.html (How to Change Windows Visual Effects)
In the end, I have following questions:
Is it a SWT or Windows problem?
Is there any related topic in bug entries for Windows or in Eclipse Bugzilla?
Is there someone else who experienced the same problem? please share the experience.
Is there any settings in SWT or Windows which could affect its look n feel and fix the problem?
In the end, I have following questions: Is it a SWT or Windows problem?
Neither. As others have mentioned, you certainly should not tie up the UI thread with any long-running task. That work belongs in a background thread.
In regards to using a background thread, there are several ways you could go about this depending on how you want your Dialog to behave.
One option would be to kick off the background thread and then open the dialog when the task is done. I personally don't care for this because while the task is running, a user may think that nothing is happening.
Another option would be to open the dialog but display a "Loading" message, or something to that effect to give meaningful feedback and let a user know that the application isn't frozen (like how it looks/responds in your example).
The strategy would be to:
Create the dialog
Start the long task on a background thread and register a callback
Open the dialog with a "Loading" message
When the task is complete, the dialog will be updated from the callback
If you search around a bit on using Executors, you should find some far better examples and detail on how to use them.
Here's a brief example to illustrate what that might look like:
(Note: There are definitely a few issues with this code, but for the sake of brevity and illustrating the point I opted for a slightly naive solution. Also there are Java 8-esque ways that would be a bit shorter, but again, this illustrates the idea behind using a background thread; the same concepts apply)
Given a Callable (or Runnable if you don't need a return value),
public class LongTask implements Callable<String> {
#Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
You can use the Executors class to create a thread pool, and then an ExecutorService to submit the Callable for execution. Then, using Futures.addCallback(), you can register a callback which will execute one of two methods depending on whether the task was successful or failed.
final ExecutorService threadPool = Executors.newFixedThreadPool(1);
final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(threadPool);
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new FutureCallback(){...});
In this case I used the Google Guava implementation ListeningExecutorService which makes things a bit cleaner and simpler, in my opinion. But again, you may not even need this if you opt for a more "Java 8" approach.
As for the callback, when the task is successful, we update the Dialog with the results. If it fails, we can update it with something to indicate failure:
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
#Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus(result);
}
});
}
#Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
In this case I opted for the Callable to return a String, thus the FutureCallback should be parameterized with String. You may want to use some other class that you created, which will work just as well.
Notice that we use the Display.asyncExec() method to ensure that the code which updates the UI runs on the UI thread, because the callback may execute on the background thread.
Like I said, there are still a few issues here, including what happens when you click the cancel button before the task completes, etc. But hopefully this helps illustrate an approach for handling long-running background tasks without blocking the UI thread.
Full example code:
public class DialogTaskExample {
private final Display display;
private final Shell shell;
private final ListeningExecutorService executorService;
public DialogTaskExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new GridLayout());
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
final Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
button.setText("Start");
button.addSelectionListener(new SelectionAdapter() {
#SuppressWarnings("synthetic-access")
#Override
public void widgetSelected(final SelectionEvent e) {
final MyDialog dialog = new MyDialog(shell);
dialog.setBlockOnOpen(false);
dialog.open();
dialog.setStatus("Doing stuff...");
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new DialogCallback(dialog));
}
});
}
public void run() {
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
executorService.shutdown();
display.dispose();
}
public static void main(final String... args) {
new DialogTaskExample().run();
}
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
#Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus(result);
}
});
}
#Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
public static class LongTask implements Callable<String> {
/**
* {#inheritDoc}
*/
#Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
public static class MyDialog extends Dialog {
private Composite baseComposite;
private Label label;
/**
* #param parentShell
*/
protected MyDialog(final Shell parentShell) {
super(parentShell);
}
/**
* {#inheritDoc}
*/
#Override
protected Control createDialogArea(final Composite parent) {
baseComposite = (Composite) super.createDialogArea(parent);
label = new Label(baseComposite, SWT.NONE);
return baseComposite;
}
public void setStatus(final String text) {
label.setText(text);
baseComposite.layout();
}
}
}
The code seems to be straight forward, only that you are making the main Thread sleep for 15secs hence the delay. If not required remove the sleep or reduce the time for sleep to 5secs or so.
I'm trying to use EasyUploads-addon for Vaadin, but can't get the image shown.
When I click addon's "Choose File"-button, it will ask me to open an file. I choose some .png -file from my images and addon shows the information of the picture beneath it.
Then I press my "Upload"-button, which is currently needed just to show the uploaded image at first.
private void showUploadedImage() {
Object value = upload.getValue();
byte[] data = (byte[]) value;
StreamResource resource = new StreamResource(
new StreamResource.StreamSource() {
private static final long serialVersionUID = 1L;
#Override
public InputStream getStream() {
return new ByteArrayInputStream(data);
}
}, "filename.png");
image.setVisible(true);
image.setCaption("Uploaded image");
image.setSource(resource);
}
upload = EasyUpload's component which is used to choose file
image = Embedded component that I have drawn with the designer to my layout.
But when I look the page with browser the image is not shown. The image is just shown as there is no image at all, just caption will be shown.
HTML-code of the image from the page's source:
<img src="null">
This might be really trivial case, but all the examples that I found was over 3-4 years old and didn't look helpful.
Can someone tell me, how it should be done?
You need to call a setter on UploadField class:
uploadField.setFieldType(FieldType.BYTE_ARRAY);
Working SSCCE:
#Override
protected void init(VaadinRequest request) {
setContent(layout);
final UploadField uploadField = new UploadField();
uploadField.setAcceptFilter(".png");
uploadField.setFieldType(FieldType.BYTE_ARRAY);
uploadField.addListener(new Listener(){
#Override
public void componentEvent(Event event)
{
showUploadedImage(uploadField);
}
});
layout.addComponent(uploadField);
layout.addComponent(image);
}
private void showUploadedImage(UploadField upload) {
Object value = upload.getValue();
final byte[] data = (byte[]) value;
StreamResource resource = new StreamResource(
new StreamResource.StreamSource() {
#Override
public InputStream getStream() {
return new ByteArrayInputStream(data);
}
}, "filename.png");
image.setSource(resource);
}
Produces (after choosing a .png file):
Of course you may want to change type of uploadField listener to more specific one.
Tested on Java 8, Vaadin 7.4.1, Eclipse Luna.
I made a JavaFx application that has a main screen. And that main screen contains buttons, each button shows different screens. Now I want, when I press a button, my current screen to FadeOut and new screen to FadeIn.
public class GuiPractice extends Application {
private Stage stage;
public static void main(String[] args) {
Application.launch(GuiPractice.class, (java.lang.String[])null);
}
#Override
public void start(Stage primaryStage) throws Exception {
try{
stage = primaryStage;
stage.setTitle("HOME SCREEN");
gotoWelcome();
stage.show();
} catch(Exception ex){
Logger.getLogger(GuiPractice.class.getName()).log(Level.SEVERE,null,ex);
}
}
private void gotoWelcome(){
try{
WelcomePageController wc = (WelcomePageController) replaceScene("WelcomePage.fxml"); // Check 'replaceScene' Below
wc.setApp(this);
} catch (Exception ex){
Logger.getLogger(GuiPractice.class.getName()).log(Level.SEVERE,null,ex);
}
}
// When I Press Button Action Handler Calls This Method..
// This Method is Working Fine, Except For the FandIn Portion
private void gotoSignin(){
try{
SigninPageController wc = (SigninPageController) replaceScene("SigninPage.fxml"); // Check 'replaceScene' Below
wc.setApp(this);
/// THIS PORTION IS FADE TRANSITION, IT IS NOT WORKING
FadeTransition ft = new FadeTransition(Duration.millis(3000));
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
} catch (Exception ex){
Logger.getLogger(GuiPractice.class.getName()).log(Level.SEVERE,null,ex);
}
}
private Initializable replaceScene(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream ins = GuiPractice.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(GuiPractice.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(ins);
} finally {
ins.close();
}
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
return (Initializable) loader.getController();
}
}
You need to pass the node on which you want to apply the Transition to the constructor of the FadeTransition.
Approach 1
So you can take the following lines and put them inside your replaceScene() before scene = new Scene(page)
page = (AnchorPane) loader.load(ins);
FadeTransition ft = new FadeTransition(Duration.millis(3000), page);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
Scene scene = new Scene(page);
Though, there will be a Fading Effect on the welcome screen as well and you will have to apply some logic to avid it !
Approach 2
Load the fxml inside the gotoSignin()/ Get the Anchorpane reference and pass it to the controller. Though, I believe this will bring a lot of changes for your current design ! You can take a call and decide !
Hope it helps !
Yeah, of course, you could have an fade out effect - just inverse values from and to, of your FadeTransition, like:
FadeTransition ft = new FadeTransition(Duration.millis(3000), page);
ft.setFromValue(1);
ft.setToValue(0);
ft.play();
JavaFx is supposed to be easily integrated in an SWT application (see here: http://docs.oracle.com/javafx/2/swt_interoperability/jfxpub-swt_interoperability.htm) and both toolkits use the same threading model.
However things get strange, when I open a dialog containing an FxCanvas which contains a JavaFx ComboBox. If I open the combo box popup menu and then close the dialog, the popup menu stays open. If I now move the mouse onto the popup a null pointer exception is thrown within javafx. When doing this within a larger application all JavaFx GUIs remain broken until the application is restarted.
Any ways to work around this?
Example code below: Close the dialog with 'Ok' or the window close button. Exit the application with 'Cancel'
package test;
import javafx.embed.swt.FXCanvas;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.StackPane;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class TestFx {
static class MyDialog extends Dialog {
Parent w;
public MyDialog(Shell parent,Parent n) {
super(parent);
this.w = n;
setShellStyle(SWT.RESIZE| SWT.BORDER | SWT.TITLE |SWT.CLOSE );
}
#Override
public void cancelPressed() {
System.exit(0);
}
#Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
container.setLayout(new FillLayout());
FXCanvas fxCanvas = new FXCanvas(container, SWT.NONE);
Scene scene = new Scene(w);
fxCanvas.setScene(scene);
return container;
}
}
private static Parent createScene() {
StackPane pane = new StackPane();
pane.setPadding(new Insets(10));
ComboBox<String> c = new ComboBox<String>();
c.getItems().addAll("Test1","Test2");
pane.getChildren().add(c);
return pane;
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
while (true) {
MyDialog d = new MyDialog(shell,createScene());
d.open();
}
}
}
Exception:
java.lang.NullPointerException
at com.sun.javafx.tk.quantum.GlassScene.sceneChanged(GlassScene.java:290)
at com.sun.javafx.tk.quantum.ViewScene.sceneChanged(ViewScene.java:156)
at com.sun.javafx.tk.quantum.PopupScene.sceneChanged(PopupScene.java:30)
at com.sun.javafx.tk.quantum.GlassScene.markDirty(GlassScene.java:157)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2214)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:363)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
at com.sun.javafx.tk.quantum.QuantumToolkit$9.run(QuantumToolkit.java:329)
at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method)
at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2546)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3756)
at org.eclipse.jface.window.Window.runEventLoop(Window.java:825)
at org.eclipse.jface.window.Window.open(Window.java:801)
at test.TestFx.main(TestFx.java:55)
At work we're developing some applications using JavaFX, on top of and old Swing platform and we also have found this issue.
Apparently it is caused by some issues on JFXPanel which is not correctly propagating some window events (focus, iconifying, etc) to the FX framework. The issue affects not only the ComboBox component, but every component that uses a PopupWindow (Menu, Tooltip, etc), specially when using Swing's JInternalFrame.
So, when a Popup is displaying and the window is minimized or closed, the Popup does not hide, causing the FX thread to crash if you try subsequently to interact with it.
The workaround mentioned above works, but only for ComboBox, as Menu and Tooltip does not inherit from the Node class, so didn't work for us :(
I developed another workaround which resolved the problem for all components that display popups, which basically forces all popups to close whenever a JFXPanel loses focus:
private static void initFX(final JFXPanel jfxPanel) {
final TestFxPanel parent = new TestFxPanel();
final Scene scene = new Scene(parent);
jfxPanel.setScene(scene);
jfxPanel.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(final FocusEvent e) {
System.out.println(jfxPanel.getName() + ": FocusLost");
runFocusPatch(scene);
}
});
}
static void runFocusPatch(final Scene scene) {
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println("Running patch");
final Iterator<Window> winIter = scene.getWindow().impl_getWindows();
while (winIter.hasNext()) {
final Window t = winIter.next();
if (t instanceof PopupWindow) {
System.out.println("Got a popup");
Platform.runLater(new Runnable() {
#Override
public void run() {
((PopupWindow) t).hide();
}
});
}
}
}
});
}
I confirm that the issue is NOT present in 8.0. Sadly we are not allowed to java 8 in production software as its still in beta stage.
best regards.
I found a workaround when using Java7: Override the close method in Dialog to hide the combo box popups:
#Override
public boolean close() {
Set<Node> nodes = w.lookupAll("#");
for (Node n : nodes)
if (n instanceof ComboBox)
((ComboBox)n).hide();
return super.close();
}
The trouble is discussed here : javafx-jira.kenai.com/browse/RT-30991
Developer has said, that the issue is fixed in JavaFX-8