TextField in PopOver with strange behavior - javafx

I have a PopOver with a TextField with a strange behavior, this PopOver it's owned by other TextField because when I type the word 'Fernández' all keys are processed by the internal TextField except when I type a stressed vowel like 'á' that it's collected by the external TextField.
PopOver owned by TextField
But when i show the same PopOver owned by a button works fine and the internal TextField receives the letter 'á'
PopOver owned by Button
I would appreciate any help to solve it.
EDIT: Here you can see an example code to show this.
public class PopOverTest extends Application {
#Override
public void start(Stage primaryStage) {
CustomTextField externo = new CustomTextField();
ImageView imgView = new ImageView(new Image("test/image.png"));
externo.setLeft(imgView);
CustomTextField interno = new CustomTextField();
PopOver popOver = new PopOver();
popOver.setContentNode(interno);
popOver.stArrowLocation(PopOver.ArrowLocation.TOP_LEFT);
imgView.setOnMouseClicked(e -> {
popOver.show(imgView);
});
StackPane root = new StackPane();
root.getChildren().add(externo);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

I found a solution for this.
Changing the external Textfield EventDispatcher and the problem is resolved
EventDispatcher dispatcher = externalTextField.getEventDispatcher();
then on focus of the internal TextField
externalTextField.setEventDispatcher(interntalTextField.getEventDispatcher());
and when lost focus restore the EventDispatcher
externalTextField.setEventDispatcher(dispatcher);
That's all folks!

Related

Javafx Popup can't get focus if other application gains focus

I use a JavaFX Popup in my application to change the Shipping Address of a customer. That often requires copy pasting the Address from the web browser. If I click on the Popup after the application lost focus it will not regain focus. I first need to click the parent window behind the popup and then click the Popup again to be able to interact with it.
Is there any way to solve that problem, besides using an undecorated stage instead of a Popup?
EDIT:
Here I made a minimal example:
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
Scene scene = new Scene(new VBox(), 600, 600);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
Popup popup = new Popup();
Button btn = new Button("This is a very nice button!");
btn.setOnAction(actionEvent -> System.out.println("button clicked"));
TextField textField = new TextField();
textField.setOnMouseClicked(mouseEvent -> System.out.println("textField clicked"));
VBox popupVBox = new VBox(btn, textField);
popupVBox.setStyle("-fx-background-color: white");
popupVBox.setPadding(new Insets(10));
popup.getContent().add(popupVBox);
popup.show(scene.getWindow());
}
public static void main(String[] args) {
launch();
}
}
If you click outside of the application (for example the web browser, to copy some value) then you can not enter Text into the TextField anymore. The button action listener and even the TextField mouse Listener still work and output "button clicked" and "textField clicked" but the blue halo around them does not apppear and you can not enter (or paste) text into the TextField. After you click somewhere on the main Stage of the application it works again.
I tried adding textField.requestFocus();
to the mouse Listener of the textfield but that does not do anything.
EDIT 2 after the comment from kleopatra:
I tried adding the following code:
popupVBox.setOnMouseEntered(mouseEvent -> {
popup.getOwnerWindow().requestFocus();
parentVBox.toFront(); //This is created above and passed to the new Scene instead of diectly passing new VBox() as in the code snippet posted above
});
With that code added, popup and its children seem to have focus if the mouse is hoverd over it after an other application had focus, i.e. if clicked with the mouse the blue border appears around the button and textField, and if there is any text in the textField I can move the cursor with the mouse and select the text with the mouse, buy any keyboard inputs are ignored. Actually they are executed in the other application which had focus before. If I ctrl+c in Intellij and try to ctrl+v into the textfield it will be pasted to the Intellij editor. Also the arrow keys move the cursor in the intellij window instead of the textField.
The only way to be able to type anything in the TextField is to click the parent window first.
Also
textField.setOnMouseClicked(mouseEvent -> {
textField.requestFocus();
});
does not do anything
not an answer, just to clarify the comment (will delete if seen by OP :)
public class PopupFocus extends Application {
#Override
public void start(Stage stage) throws IOException {
Scene scene = new Scene(new VBox(), 300, 300);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.setX(10);
stage.show();
Popup popup = new Popup();
Button btn = new Button("This is a very nice button!");
btn.setOnAction(actionEvent -> System.out.println("button clicked"));
TextField textField = new TextField();
textField.setOnMouseClicked(mouseEvent -> {
System.out.println("textField clicked");
stage.toFront();
});
VBox popupVBox = new VBox(btn, textField);
popupVBox.setStyle("-fx-background-color: white");
popupVBox.setPadding(new Insets(10));
popup.getContent().add(popupVBox);
// original
popup.show(scene.getWindow());
}
public static void main(String[] args) {
launch();
}
}

How to disable right click on a menu in javafx

In javaFX code, a menu can popup by left click or right click. How to disable right click?
public void start(Stage primaryStage)
{
BorderPane root = new BorderPane();
MenuBar menuBar = new MenuBar();
Menu hello = new Menu("hello");
menuBar.getMenus().addAll(hello);
Menu world = new Menu("world");
menuBar.getMenus().addAll(world);
root.setCenter(menuBar);
MenuItem item = new MenuItem("laugh");
hello.getItems().add(item);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
When I right click the "hello" menu, it will popup menuitem "laugh".
The basic approach is to register a eventFilter on the MenuBar that consumes the events that should not be delivered to the children.
Doing so manually in your application code:
public class DisableRightClickOpenMenu extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
MenuBar menuBar = new MenuBar();
menuBar.addEventFilter(MouseEvent.MOUSE_PRESSED, ev -> {
if (ev.getButton() == MouseButton.SECONDARY) {
ev.consume();
}
});
Menu hello = new Menu("hello");
menuBar.getMenus().addAll(hello);
Menu world = new Menu("world");
menuBar.getMenus().addAll(world);
root.setCenter(menuBar);
MenuItem item = new MenuItem("laugh");
hello.getItems().add(item);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you want this behaviour across all your applications, you can implement a custom menuBarSkin that registers the filter and install the custom skin via a stylesheet.
The skin:
public class ExMenuBarSkin extends MenuBarSkin {
/**
* Instantiates a skin for the given MenuBar. Registers an
* event filter that consumes right mouse press.
*
* #param menuBar
*/
public ExMenuBarSkin(MenuBar menuBar) {
super(menuBar);
menuBar.addEventFilter(MouseEvent.MOUSE_PRESSED, ev -> {
if (ev.getButton() == MouseButton.SECONDARY) {
ev.consume();
}
});
}
}
In your stylesheet (replace with your fully qualified class name):
.menu-bar {
-fx-skin: "de.swingempire.fx.event.ExMenuBarSkin";
}
Its usage (replace the name with your stylesheet file name):
URL uri = getClass().getResource("contextskin.css");
primaryStage.getScene().getStylesheets().add(uri.toExternalForm());
This is usual behavior of menu in many programs. I don't think you can change it. However, you can use some other controls and simulate menu. (Like HBox and Labels).
I agree as far as I know there's no a standard way to do this, but you may want to consider the following workaround.
It is replacing the Menu node with a Menu object composed by an HBox and a Label: an EventHandler is added to the HBox and by checking the mouse button pressed we add/remove on the fly the MenuItem to its parent.
#Override
public void start(final Stage primaryStage) {
final BorderPane root = new BorderPane();
final MenuBar menuBar = new MenuBar();
final Menu menuHello = new Menu();
final Menu menuWorld = new Menu("world");
final MenuItem menuitem = new MenuItem("laugh");
final HBox hbox = new HBox();
menuBar.getMenus().addAll(menuHello, menuWorld);
root.setCenter(menuBar);
hbox.setPrefWidth(30);
hbox.getChildren().add(new Label("hello"));
menuHello.setGraphic(hbox);
menuHello.getItems().add(menuitem);
hbox.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
System.out.println("Right click");
menuHello.getItems().remove(menuitem);
} else {
System.out.println("Left click");
if (!menuHello.getItems().contains(menuitem)) {
menuHello.getItems().add(menuitem);
menuHello.show(); // The .show method prevent 'losing' the current click }
}
}
});
final Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
This will produce the following result - preview
Note that I've used an HBox just for habit, there's no a particular reason.
While using a workaround like this, my suggestion would be to fill all the Menus with the same 'pattern', such as the HBox + Label combo in my example, and stylize them via css/code (width/height, background/fill/hover... colors etc.) in order to have them as uniform as possible and avoid creating graphic inconsistencies due to have different nodes types in the same menubar.

How to create own window rather then use default window

I've been working on javafx and i want to remove default windows and create a window with my style
It's quite easy to create a window in javafx. To create your own window you need to modify the style of your stage which can be done using initStyle() method.
public class Test extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createParent(), Color.TRANSPARENT);
primaryStage.initStyle(StageStyle.TRANSPARENT);
// primaryStage.initStyle(StageStyle.UNDECORATED);
// primaryStage.initStyle(StageStyle.DECORATED);
primaryStage.setTitle("My Own Window");
primaryStage.setScene(scene);
primaryStage.show();
}
private Parent createParent() {
Pane rootPane = new Pane();
rootPane.setPrefSize(1000,400);
Button btn = new Button("RandomButton");
btn.setOnAction(e -> Platform.exit());
rootPane.getChildren().add(btn);
return rootPane;
}
}

TextField ESCAPE Keycode not caught

I'm trying to catch an ESCAPE key press in a textfield.
I was expecting to put a simple event listener (or even an event filter) to the textfield. But nothing works.
It seems all key events are caught, except the ESCAPE key I 'm waiting for.
Can you provide any help please,
Thanks in advance
PS: the following bunch of code explains how I proceed.
public class KeyEventSample extends Application {
/**
* Constructor
*/
public KeyEventSample() {super();}
#Override
public void start(final Stage primaryStage) throws Exception {
final TextField textField = new TextField();
final TextArea textArea = new TextArea();
textField.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ESCAPE)
textArea.appendText("ESCAPE");
else if (e.getCode() == KeyCode.ENTER)
textArea.appendText("ENTER");
else
textArea.appendText("->"+e.getCode().toString());
});
final BorderPane borderPane = new BorderPane();
borderPane.setCenter(textField);
borderPane.setBottom(textArea);
final Scene scene = new Scene(borderPane, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
}

How to create a modal window in JavaFX 2.1

I can't figure out how to create a modal window in JavaFX. Basically I have file chooser and I want to ask the user a question when they select a file. I need this information in order to parse the file, so the execution needs to wait for the answer.
I've seen this question but I've not been able to find out how to implement this behavior.
In my opinion this is not good solution, because parent window is all time active.
For example if You want open window as modal after click button...
private void clickShow(ActionEvent event) {
Stage stage = new Stage();
Parent root = FXMLLoader.load(
YourClassController.class.getResource("YourClass.fxml"));
stage.setScene(new Scene(root));
stage.setTitle("My modal window");
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(
((Node)event.getSource()).getScene().getWindow() );
stage.show();
}
Now Your new window is REALY modal - parent is block.
also You can use
Modality.APPLICATION_MODAL
Here is link to a solution I created earlier for modal dialogs in JavaFX 2.1
The solution creates a modal stage on top of the current stage and takes action on the dialog results via event handlers for the dialog controls.
JavaFX 8+
The prior linked solution uses a dated event handler approach to take action after a dialog was dismissed. That approach was valid for pre-JavaFX 2.2 implementations. For JavaFX 8+ there is no need for event handers, instead, use the new Stage showAndWait() method. For example:
Stage dialog = new Stage();
// populate dialog with controls.
...
dialog.initOwner(parentStage);
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.showAndWait();
// process result of dialog operation.
...
Note that, in order for things to work as expected, it is important to initialize the owner of the Stage and to initialize the modality of the Stage to either WINDOW_MODAL or APPLICATION_MODAL.
There are some high quality standard UI dialogs in JavaFX 8 and ControlsFX, if they fit your requirements, I advise using those rather than developing your own. Those in-built JavaFX Dialog and Alert classes also have initOwner and initModality and showAndWait methods, so that you can set the modality for them as you wish (note that, by default, the in-built dialogs are application modal).
You can create application like my sample. This is only single file JavaFX application.
public class JavaFXApplication1 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage stage;
stage = new Stage();
final SwingNode swingNode = new SwingNode();
createSwingContent(swingNode);
StackPane pane = new StackPane();
pane.getChildren().add(swingNode);
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle("Swing in JavaFX");
stage.setScene(new Scene(pane, 250, 150));
stage.show();
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void createSwingContent(final SwingNode swingNode) {
SwingUtilities.invokeLater(() -> {
try {
Path currentRelativePath = Paths.get("");
String s = currentRelativePath.toAbsolutePath().toString();
JasperDesign jasperDesign = JRXmlLoader.load(s + "/src/reports/report1.jrxml");
String query = "SELECT * FROM `accounttype`";
JRDesignQuery jrquery = new JRDesignQuery();
jrquery.setText(query);
jasperDesign.setQuery(jrquery);
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint JasperPrint = JasperFillManager.fillReport(jasperReport, null, c);
//JRViewer viewer = new JRViewer(JasperPrint);
swingNode.setContent(new JRViewer(JasperPrint));
} catch (JRException ex) {
Logger.getLogger(AccountTypeController.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

Resources