So once again we are in the process of converting our existing Java application that was using entirely Swing to using JavaFX. However, the application will not be using JavaFX entirely. This seems to be causing some issues with Alerts/Dialogs and modality. We are currently using Java 8u40.
The main application is basically in a JFrame that has a Menu. The main content pane is JDesktopPane and clicking a MenuItem opens new JInternalFrames within the DeskopPane. Screens we are converting to JavaFX are basically JFXPanels within a JInternalFrame at the moment. Any Alerts/Dialogs that are opened from the JFXPanels are modal to the panel itself, but not to the JInternalFrame, DeskopPane, Menu, etc.
I read in the DialogPane documentation that they are planning to introduce some lightweight dialogs and even possibly InternalFrames in future releases of JavaFX, so maybe we'll just have to wait it out a little longer for this functionality. But, ideally when opening a new Alert/Dialog it would be modal to the entire Application.
EDIT:
Currently doing the following for modal dialogs:
((Stage)getDialogPane().getScene().getWindow()).setAlwaysOnTop(true);
This makes the dialog always appear on top, however the dialog also remains on top of other applications even if our main application is minimized. It also does not block input to any Swing components in the frame.
You can use the following work-around which creates an invisible JDialog when the Alert is shown and disposes the JDialog when the Alert is closed. This approach extends the modality to the whole application, including the Swing part.
// create Alert
Alert alert = new Alert(AlertType.INFORMATION, "Hello");
// create invisible JDialog and "show" it
JDialog dialog = new JDialog();
dialog.setModal(true);
dialog.setUndecorated(true);
dialog.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
SwingUtilities.invokeLater(() -> dialog.setVisible(true));
// show Alert
alert.showAndWait();
// close JDialog after Alert is closed
dialog.dispose();
I don't think i understand your question completely. But here is my guess - You are trying to make an alert window from some JFXPanel that will be modal (i.e. user will not be able to click in your application until she closes that alert window) to your entire application which is written partially using swing components.
If your application would be written in purely JavaFX then you would do something like (Assuming you have created a button somewhere in your JFXPanel)
button.setOnAction(evt -> {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.initModality(Modality.APPLICATION_MODAL);
// This will not work in your code
alert.initOwner(button.getScene().getWindow());
alert.show();
});
but since initOwner requires a javafx.stage.window object passing a swing component won't work in your code. As of Java 8u40 i don't think there is a right way(i.e. not hacks) to set ownership of Alert objects to swing component. Not surprisingly such questions has already been asked here and not answered as of writing this.
For your requirements you can use JOptionPane.showMessageDialog method and its look alike as workaround.
button.setOnAction(evt -> {
JOptionPane.showMessageDialog(desktopPane,"My message");
});
These dialog boxes are modal by default so no work is necessary. You can call these from any event handler methods of JavaFX components.
I've done a little workaround with a small interface which is implemented in my JavaFXFrame:
public interface DialogParent {
void setOnFocusGained(EventHandler<FocusEvent> focusHandler);
void setOnCloseRequest(EventHandler<WindowEvent> closeHandler);
}
And my JavaFXFrame implementation
public class JavaFXFrame implements DialogParent {
private JFrame frame;
private EventHandler<ch.irix.sumadmin.util.FocusEvent> focusGainedHandler;
private EventHandler<javafx.stage.WindowEvent> windowClosingHandler;
public void JavaFXFrame() {
final JFXPanel fxPanel = new JFXPanel();
frame = new JFrame();
frame.add(fxPanel);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
tryClosing(this);
}
});
frame.addWindowFocusListener(new WindowAdapter() {
#Override
public void windowGainedFocus(WindowEvent e) {
if (focusGainedHandler != null) {
focusGainedHandler.handle(new FocusEvent());
}
}
});
}
public void setVisible(boolean visible) {
frame.setVisible(visible);
}
private void tryClosing(WindowListener listener) {
javafx.stage.WindowEvent windowEvent = new javafx.stage.WindowEvent(null, javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST);
if (windowClosingHandler != null) {
windowClosingHandler.handle(windowEvent);
}
if (!windowEvent.isConsumed()) {
frame.setVisible(false);
}
}
#Override
public void setOnFocusGained(EventHandler<ch.irix.sumadmin.util.FocusEvent> focusGainedHandler) {
this.focusGainedHandler = focusGainedHandler;
}
#Override
public void setOnCloseRequest(EventHandler<javafx.stage.WindowEvent> windowClosingHandler) {
this.windowClosingHandler = windowClosingHandler;
}
}
And showing an Alert:
public static void showAlert(Alert alert) {
DialogPane dialogPane = alert.getDialogPane();
final Stage stage = new Stage();
stage.setScene(dialogPane.getScene());
List<ButtonType> buttonTypes = dialogPane.getButtonTypes();
for (ButtonType buttonType : buttonTypes) {
ButtonBase button = (ButtonBase) dialogPane.lookupButton(buttonType);
button.setOnAction(evt -> {
dialogPane.setUserData(buttonType);
stage.close();
});
}
dialogParent.setOnFocusGained(event -> {
stage.toFront();
});
dialogParent.setOnCloseRequest(Event::consume);
stage.setOnCloseRequest(event -> {
dialogParent.setOnFocusGained(null);
dialogParent.setOnCloseRequest(null);
});
stage.show();
}
Hope this will help you
Related
The title seems a little bit confusing, but I'll explain everything.
I'm developing a project where I show the image captured by a webcam in a JPanel, Java Swing. Now I have to integrate this with JavaFx.
I have a controller where I have the method startRecording, that would initialize the cameraThread and tell the class Camera to startRecording, inside Camera class a have a method DrawFrame(BufferedImage, JPanel panel) where I call the function drawImage from OpenCV to draw in the Panel:
Controller:
public void startRecording(){
cameraInstance.setCameraRGBPanel(windowsInstance.getCameraRGBPanel());
cameraInstance.setCameraHSVPanel(windowsInstance.getCameraHSVPanel());
cameraInstance.setCameraThresholdPanel(windowsInstance.getCameraThresholdPanel());
cameraInstance.setRecord(true);
cameraThread = new Thread(cameraInstance);
cameraThread.start();
}
Class camera:
private void drawFrame(BufferedImage buff, JPanel pane){
pane.getGraphics().drawImage(buff, 0, 0, null);
}
To start with, JavaFX has no JPanel and the Pane (an option) has no getGraphics, I've tried to use a SwingNode, add the JPanel and then do everything as usual, but the image simply won't be shown.
The following code was a test, that's why it seems to be so 'bad'.
public void start(Stage stage) throws Exception {
stage.setTitle("Tela Teste");
pCamera = new Pane();
SwingNode swing = new SwingNode();
pCamera.getChildren().add(swing);
createAndSetSeingContent(swing);
Group root = new Group();
root.getChildren().add(pCamera);
stage.setScene(new Scene(root , 500, 500));
stage.setResizable(true);
stage.show();
}
private void createAndSetSeingContent(SwingNode swing) {
ControllerCamera control = new ControllerCamera();
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
JPanel panel = new JPanel();
JLabel label = new JLabel("Abc");
//panel.add(label);
swing.setContent(panel);
Button teste = new Button("A");
pCamera.getChildren().add(teste);
teste.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
control.startRecording(panel);
System.out.println("abc");
}
});
}
I changed to method startRecording to something like:
public void startRecording(JPanel panel){
cameraInstance.setCameraRGBPanel(panel);
cameraInstance.setRecord(true);
cameraThread = new Thread(cameraInstance);
cameraThread.start();
}
Still nothing appears in the panel, but if I add a label or button, then it appear and works as intended to. The "abc" is always shown in the console.
I think that's all the code related to the problem. Something else I want to say is that yesterday was the first day I was dealing with FX, let's say the project is divided, the other guy is also working on the problem, but we haven't gotten anywhere so far, that's why I decided to ask you here.
Edit 1: everything was working perfectly before all this situation (everything works with Swing, but not in FX).
The simplest way to display an Image in JavaFX is with an ImageView. You can create a single ImageView and update its image by calling setImage(...), passing in a javafx.scene.image.Image. I don't know the camera API you are working with: you might be able to generate a JavaFX image directly, in which case your draw frame method looks as simple as:
private void drawFrame(Image image, ImageView imageView) {
imageView.setImage(image);
}
If you can only generate BufferedImages, you can do
private void drawFrame(BufferedImage buff, ImageView imageView) {
imageView.setImage(SwingFXUtils.toFXImage(buff, null));
}
In either case, you can just create the ImageView, put it in a Pane subclass of some kind, put the Pane in a scene and display it in the Stage:
public void start(Stage stage) throws Exception {
stage.setTitle("Tela Teste");
pCamera = new Pane();
ImageView imageView = new ImageView();
pCamera.getChildren().add(imageView);
Group root = new Group();
root.getChildren().add(pCamera);
stage.setScene(new Scene(root , 500, 500));
stage.setResizable(true);
stage.show();
}
Then just pass the imageView to your drawFrame method as needed.
I am having trouble getting the hardware back button to do what I would like it to do for the Windows Phone 8. The app is strictly just webview, so as of now when a back (hardware) button is clicked it closes the app but i want to back just previous page. I put the the URL in constructor like below
namespace Masala
{
public partial class Entertainment : PhoneApplicationPage
{
public Entertainment()
{
InitializeComponent();
var targetUri = new Uri("http://mobile-masala.com");
WebBrowser.Navigate(targetUri);
}
}
}
Add following code in your page.xam.cs to handle back button press...
protected override void OnBackKeyPress(CancelEventArgs e)
{
if(WebBrowser.CanGoBack)// your code... check the web view that you can go back or it is the main page.
{
WebBrowser.GoBack();
e.Cancel = true;
}
}
I want to preface this question by stating that the following code works perfectly fine in JDK 1.7. The goal here is to create a tab pane with a tab at the end (with text set to "+") so that whenever this tab is selected, the program creates a new tab in the tab pane. This functionality works fine. The problem is that when you close the New Tab via the X, it switches to the "Add Tab," creates a new tab, then throws the following NullPointerException in some JDK code (and the app now shows TWO new tabs which are the same exact object):
Executing C:\Users\XXXXXX\Documents\NetBeansProjects\TestJavaFx\dist\run2082574567\TestJavaFx.jar using platform C:\Program Files\Java\jdk1.8.0\jre/bin/java
java.lang.NullPointerException
at com.sun.javafx.scene.control.skin.TabPaneSkin$TabHeaderSkin.access$302(TabPaneSkin.java:1040)
....
I have cut down the trouble code to bare minimums to display the issue, and it is as follows:
public class TestTabApp extends Application {
private TabPane tabPane;
private Tab addTab;
private Tab currentTab;
#Override
public void start(Stage primaryStage) {
//Create the tab pane and the 'addTab' for adding new tabs.
tabPane = new TabPane();
tabPane.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB);
addTab = new Tab("+");
addTab.setClosable(false);
tabPane.getTabs().add(addTab);
//Add a listener to listen for changes to tab selection.
tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
#Override
public void changed(ObservableValue<? extends Tab> observable, Tab oldSelectedTab, Tab newSelectedTab) {
//If we change to the addTab create a
//new tab and change selection.
if (newSelectedTab == addTab) {
//Create the new tab.
createNewTab();
} else {
currentTab = newSelectedTab;
}
}
});
//Create a new tab for initial load of the app
createNewTab();
StackPane root = new StackPane();
root.getChildren().add(tabPane);
Scene scene = new Scene(root, 500, 500);
primaryStage.setTitle("Tab Test");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private Tab createNewTab() {
Tab newTab = new Tab("New Tab");
newTab.setClosable(true);
tabPane.getTabs().add(tabPane.getTabs().size() - 1, newTab);
tabPane.getSelectionModel().select(newTab);
return newTab;
}
}
Does anyone have any thoughts on why this is happening now in JDK 1.8, but not in 1.7? Is there a bug in 1.8?
I also posted this on the Oracle forums and received a response from a David Grieve:
This is clearly a bug in the TabPaneSkin code. What seems to be happening is that the tab is removed before the tab removal animation completes. The problem may be exacerbated by the code automatically adding a tab if the last tab is removed, but the core code shouldn't fall over like that.
As a workaround, turn the tab close animation off with the following bit of CSS.
tabPane.setStyle("-fx-close-tab-animation: none;");
I have created https://javafx-jira.kenai.com/browse/RT-36443 to track the issue.
I am working on a Windows Phone 8 App which should be protected with a passcode. What is the best way to show the passcode screen everytime the app is lauchend or activated?
I think the central point of action shoule be the App.xaml.cs with its Launch and Activation event handlers. But how exactly can I show the passcode screen?
The problem is, that one never know which pages will be displayed when the app launches or is reactivated. It is either the main page or any other page which was last displayed when the app was deactivated.
I tried to intercept the navigation to the first page, cancel it and show the passcode page instead:
// App.xaml.cs
private void InitializePhoneApplication() {
...
RootFrame.Navigating += HandleFirstNavigation;
...
}
private void HandleFirstNavigation(object sender, NavigatingCancelEventArgs e) {
RootFrame.Navigating -= HandleFirstNavigation;
e.Cancel = true;
RootFrame.Dispatcher.BeginInvoke(new Action(this.OpenPasscodePage));
}
private void OpenPasscodePage() {
RootFrame.Navigate(PasscodePageUri);
}
This works, but only when the app lauchend. When the app reactivated (dormant or tombstoned) the e.Cancel is irgnored. Although the navigation to the passcode page is called the original page is shown.
Moving the navigation the the passcode page from Navigating to Navigated does not worth either:
private void InitializePhoneApplication() {
...
RootFrame.Navigated += PasscodePageAfterFirstNavigation;
...
}
private void PasscodePageAfterFirstNavigation(object sender, EventArgs e) {
RootFrame.Navigated-= PasscodePageAfterFirstNavigation;
RootFrame.Navigate(PasscodePageUri);
}
This seems to be some kind of race condition: Sometimes the passcode page is shown, sometimes the original page. Even if the passcode pages comes up this looks bad because one first see the original page for the fraction of a second before the app navigates further to the passcode page.
Both solution do not work. Any idea what is the right way to implement this?
EDIT: Meanwhile I tried a third solution which does not work either. This solution uses the Uri Mapper:
App.xaml.cs
public bool PasscodeWasConfirmed; private void Application_Launching(object sender, LaunchingEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
private void Application_Activated(object sender, ActivatedEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
public Uri InitialPageUri;
public bool ShouldRedirectToPasscodePage(Uri uri) {
if (PasswordWasConfirmend == false) {
InitialPageUri = uri;
return true;
}
return false;
}
UriMapper
public class AppUriMapper : UriMapperBase {
public override Uri MapUri(Uri uri) {
App app = (Application.Current as App);
if (app != null) {
if (app.ShouldRedirectToPasscodePage(uri))
return PasscodeQueryPage.PageUri;
}
// default
return uri;
}
}
PasscodePage
public partial class PasscodePage : PhoneApplicationPage {
...
private void PasscodeConfirmed() {
App app = (Application.Current as App);
app.PasscodeWasConfirmed = true;
NavigationService.Navigate(app.InitialPageUri);
}
}
The Logic is working without any problem, but the app does not navigate to InitialPageUri after the passcode was confirmed. The Uri Mapper is called and correctly and returns the InitialPageUri (no redirect any more). But no navigation happens...
There are no errors, exceptions or debug output. simply nothing happes...
Biggest problem when using Uri Mapper:
When the app is reactivated from Dormant state there is no navigation which could be mapped or redirected...
(I've edited previous answer instead of adding a new one)
I've spend a little time trying to find a solution, and I don't see why your code doesn't run.
In my case it's enough if I do such a change in App.xaml:
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
This works on my example which is under the link http://sdrv.ms/1ajH40E
But - I cannot prevent user from seeing last screen when he holds back buton and is chosing to which app return, and then for a blink he can see the last page before leaving the app. I don't know if it is possible to change this behaviour after clicking MS Button:
windows phone change deactivated app image
Second edit
Ok - maybe I've found solution why it sometiems work and sometimes not in your code. After pressing the Start or Search buton the App can go to two cases: Tombstone and non-tombsone. After return different events happen. Code above works with Tombstone case but not with non-tombstone. To work it with the second you need to add (because page is not initialized again) - (of course it can be different solution):
bool afterActivation = false;
private void Application_Activated(object sender, ActivatedEventArgs e)
{
afterActivation = true;
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
if (afterActivation)
{
afterActivation = false;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
}
Please also ensure of your debug properties in VS: Project->Properties->Debug->Tombstone upon deactiovation checkbox.
You can also find some information here (if you haven't seen it before):
http://blogs.msdn.com/b/ptorr/archive/2010/12/11/how-to-correctly-handle-application-deactivation-and-reactivation.aspx
I used the SimpleSwingBrowser example (http://docs.oracle.com/javafx/2/swing/SimpleSwingBrowser.java.htm) and added some code of my own for log tailing.
I wanted to add a search bar ability to it (Search and Highlight text).
After googling for hours and self experiments, I didn't find a way to do it.
Can someone give me a kick-off direction for writing such ability.
Suggestions for a JavaScript based solution
Use an existing JavaScript highlighting library such as jQuery highlight or hilitor.js.
Suggestions for a Java based solution
Use the Java w3c DOM API to perform operations on the WebEngine document object after the document has been loaded.
To get a Search API in the JavaFX WebView core implementation
I created feature request RT-23383 Text search support for WebView. The feature request is currently open and unactioned - you can create an account in the issue tracker and vote for or comment on the feature request.
Sample
This sample uses jQuery highlight. The user types the word to be highlighted into the text field, then presses the highlight button to highlight all occurrences of the word in the page or to remove highlight button to clear all marked highlights. You could modify the sample to allow further jQuery based searches to scroll to a next and previously highlighted word.
I tried to get it to work with any arbitrary web page, but that logic defeated me. If you control the source of the page you want to search and can add the reference to the jQuery highlight plugin and it's style class to your page, something like this sample program might be an option.
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.Stage;
public class WebViewSearch extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final WebView webView = new WebView();
final WebEngine engine = webView.getEngine();
engine.load("http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html");
final TextField searchField = new TextField("light");
searchField.setPromptText("Enter the text you would like to highlight and press ENTER to highlight");
searchField.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
if (engine.getDocument() != null) {
highlight(
engine,
searchField.getText()
);
}
}
});
final Button highlightButton = new Button("Highlight");
highlightButton.setDefaultButton(true);
highlightButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
searchField.fireEvent(new ActionEvent());
}
});
final Button removeHighlightButton = new Button("Remove Highlight");
removeHighlightButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
removeHighlight(
engine
);
}
});
removeHighlightButton.setCancelButton(true);
HBox controls = new HBox(10);
controls.getChildren().setAll(
highlightButton,
removeHighlightButton
);
VBox layout = new VBox(10);
layout.getChildren().setAll(searchField, controls, webView);
searchField.setMinHeight(Control.USE_PREF_SIZE);
controls.setMinHeight(Control.USE_PREF_SIZE);
controls.disableProperty().bind(webView.getEngine().getLoadWorker().runningProperty());
searchField.disableProperty().bind(webView.getEngine().getLoadWorker().runningProperty());
primaryStage.setScene(new Scene(layout));
primaryStage.show();
webView.requestFocus();
}
private void highlight(WebEngine engine, String text) {
engine.executeScript("$('body').removeHighlight().highlight('" + text + "')");
}
private void removeHighlight(WebEngine engine) {
engine.executeScript("$('body').removeHighlight()");
}
}