I have a context menu which activated on right click of a node in my JavaFX application. Originally I just had one menu item, but I decided to add another. When I added another, the second menu item worked but the first menu item stopped working. Here was my code:
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
MenuItem hyperlink = new MenuItem("Hyperlink...");
contextMenu.getItems().addAll(delete, hyperlink);
//handles deletion
webView.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
//handles hyperlink
webView.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
}
I assumed this was because the listeners were overriding each other, so I changed it to add event handlers. Here's how I changed the code:
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
MenuItem hyperlink = new MenuItem("Hyperlink...");
contextMenu.getItems().addAll(delete, hyperlink);
//handles deletion
webView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
//handles hyperlink
webView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
hyperlink.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
}
}
});
Now, NOTHING is happening when I right-click the node. Can someone explain why, and what my solution might be?
There is a number of things:
Yes, in your first version, the handlers were overriding each other.
Direct translation to addEventHandler style would use event type MouseEvent.MOUSE_PRESSED, not MOUSE_CLICKED, since MOUSE_CLICKED occurs only after the mouse button has been released, thus the isSecondaryButtonDown() test always returns false—that's why nothing is happening.
Instead of detecting mouse presses, you can listen to CONTEXT_MENU_REQUESTED events:
webView.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override
public void handle(ContextMenuEvent e) {
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
}
}
You should set the menu item's action right after its creation, not on each right click:
MenuItem delete = new MenuItem("Delete");
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
//do stuff
}
});
The only thing you need to do on right click is show the context menu, as shown above.
Why are you duplicating code. Just follow your first approach and do both things inside the same EventHandler.
One more note: Define your ActionEvents for the MenuItems outside of the webView handlers. What you are doing now is every time you click a menuItem you are resetting the ActionEvents. From your code, I can only see that you need to detect the MouseEvent just to show the Context Menu. Which can be done in one webView.setOnMousePressed(...).
Related
Is it possible to simply check if a key is currently pressed in JavaFX without listening for a KeyEvent? In my case I want to check if a key is down while a button is being pressed.
button.setOnAction(e -> {
// This is where I want to check if a specific key is currently down.
if(keyIsDown) {
// do something
} else {
// do something else
}
});
I can think of a couple of workarounds. I'm just wondering if there is a right way to do this.
Here you go this code works like a charm with me when I'm pressing any button on keyboard then click a button prints key down
SimpleBooleanProperty simpleBooleanProperty = new SimpleBooleanProperty();
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
simpleBooleanProperty.setValue(true);
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
simpleBooleanProperty.setValue(false);
}
});
done.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if(simpleBooleanProperty.get()){
System.out.println("Key Down");
}else { //key released
System.out.println("Key Up");
}
}
});
I need to disable the close event using Alt + F4 keyboard shortcut. For now, I'm trying to filter the events in my Scene for this keypress and consume it, but didn't have any success, the close event happens anyway. Follows bellow part of my code:
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.isAltDown() && event.getCode().equals(KeyCode.F4)) {
event.consume();
}
});
primaryStage.setOnCloseRequest((ev) -> System.exit(0));
You can try to disable the implicit exit:
Platform.setImplicitExit(false);
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
event.consume();
}
});
and then create a button which will close the app on click:
Button btn = new Button();
btn.setText("Close");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.exit(0);
}
});
Suppose I want to have my program to react same way, say, navigate to next record, in response to different events, including pressing a key, clicking GUI button, selecting menu item and so on.
This was done with "actions" in Swing.
Can I materialize this concept in some program object in JavaFX?
Or I should make a porridge of interacting objects?
Action is still there in JavaFX. Example belows how to create an action, bind it to a keyboard shortcut and share between two different elements.
Button go = new Button("Go");
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
browser.load(location.getText(), new Runnable() {
#Override
public void run() {
System.out.println("---------------");
System.out.println(browser.getHTML());
}
});
}
};
...
MenuItem menuItem = new MenuItem("Go!");
menuItem.setAccelerator(new KeyCodeCombination(KeyCode.G, KeyCombination.CONTROL_DOWN));
go.setOnAction(goAction);
menuItem.setOnAction(goAction);
JavaFX provides many events. You also do this with setOn() method:
button.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
// code here
}
});
my layout like :
i want focus at Company Name Texfield when i press TAB button at first time , but right now i get focus at Add Button how can i manage it ?
i try code like
ChangeFocus(mTextFieldCompanyName, mTextAreaAboutUs);
ChangeFocus(mTextAreaAboutUs, mTextAreaContactUs);
ChangeFocus(mTextAreaContactUs, mButtonVideo);
ChangeFocus(mButtonVideo, mButtonImage);
ChangeFocus(mButtonImage, mButtonSave);
public void ChangeFocus(Control mControlFrom,final Control mControlTo)
{
mControlFrom.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.TAB)
{
System.out.println("TAB pressed");
mControlTo.requestFocus();
event.consume(); // do nothing
}
}
});
}
Try wrapping your mTextFieldCompanyName inside Platform.runLater()
Platform.runLater(new Runnable() {
#Override
public void run() {
mTextFieldCompanyName.requestFocus();
}
});
Hope it helps :)
I have a ListView with table names in it and an add button.
tablesListView.setEditable(true);
tablesListView.setCellFactory(TextFieldListCell.forListView());
tablesListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
addTableButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent evt) {
tablesListView.getItems().add("");
tablesListView.getSelectionModel().selectLast();
tablesListView.edit(tablesListView.getSelectionModel().getSelectedIndex());
}
});
So when i click on some cell in the list it switches its state to the TextField, but that's not working with the button, the cell becomes selected, but not switched. Any help?
Try
tablesListView.setEditable(true);
tablesListView.setCellFactory(TextFieldListCell.forListView());
tablesListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(.1),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
tablesListView.edit(tablesListView.getSelectionModel().getSelectedIndex());
}
}));
animation.setCycleCount(1);
addTableButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent evt) {
tablesListView.getItems().add("");
tablesListView.getSelectionModel().selectLast();
animation.play();
}
});
Actually Platform.runLater() works, but only occasionally, cause it runs the runnable in "some unspecified time in the future". So you need to specify the time when the edit command should run by for instance the timeline in my code example. The edit command postponed for 0.1 sec.