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.
Related
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(...).
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
}
});
I have a JavaFX application, and I would like to add an event handler for a mouse click anywhere within the scene. The following approach works ok, but not exactly in the way I want to. Here is a sample to illustrate the problem:
public void start(Stage primaryStage) {
root = new AnchorPane();
scene = new Scene(root,500,200);
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse click detected! "+event.getSource());
}
});
Button button = new Button("click here");
root.getChildren().add(button);
primaryStage.setScene(scene);
primaryStage.show();
}
If I click anywhere in empty space, the EventHandler invokes the handle() method, but if i click the button, the handle() method is not invoked. There are many buttons and other interactive elements in my application, so I need an approach to catch clicks on those elements as well without having to manually add a new handler for every single element.
You can add an event filter to the scene with addEventFilter(). This will be called before the event is consumed by any child controls. Here's what the code for the event filter looks like.
scene.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("mouse click detected! " + mouseEvent.getSource());
}
});
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'm making a level editor for my game. I have a property panel where I can modify the selected object its properties. I also have a Save button to write the level xml.
A field-edit is submitted(*) when the editor component lost the focus or Enter is pressed. This is working great, but the only problem is that when I have this sequence of actions:
Edit a field
Press the save button
Because, what happens is this:
I edit the field
I press the save button
The level is saved
The field lost the focus
The edit is submitted
As you can see, this is the wrong order. Of course I want the field to lose its focus, which causes the submit and then save the level.
Is there a trick, hack or workaround to make the field first lose the focus and then perform the action listener of the save button?
Thanks in advance.
(* submit = the edit to the field is also made in the object property)
EDIT: For the field I'm using a FocusAdapter with focusLost:
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusLost(FocusEvent e)
{
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
}
};
And for the button a simple ActionListener with actionPerformed`.
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Save the level
}
});
Hmm ... can't reproduce: in the snippet below the lost is always notified before the actionPerfomed, independent on whether I click the button or use the mnemonic:
final JTextField field = new JTextField("some text to change");
FocusAdapter focus = new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
LOG.info("lost: " + field.getText());
}
};
field.addFocusListener(focus);
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
LOG.info("save: " + field.getText());
}
};
save.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
JButton button = new JButton(save);
JComponent box = Box.createHorizontalBox();
box.add(field);
box.add(button);
On the other hand, focus is a tricky property to rely on, the ordering might be system-dependent (mine is win vista). Check how the snippet behave on yours.
If you see the same sequence as I do, the problem is somewhere else
if you get the save before the lost, try to wrap the the save action into invokeLater (which puts it at the end of the EventQueue, so it's executed after all pending events)
Action save = new AbstractAction("save") {
#Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
LOG.info("save: " + field.getText());
}
});
}
};
Normally, wrapping your save code into an SwingUtilities.invokeLater() should do the trick. As you already mentioned, this doesn't work? Try this:
private boolean editFocus = false;
FocusAdapter focusAdapter = new FocusAdapter()
{
#Override
public void focusGained(FocusEvent e){
editFocus = true;
}
#Override
public void focusLost(FocusEvent e){
compProperties.setProperty(i, getColor());
record(); // For undo-redo mechanism
editFocus = false;
if (saveRequested){
save();
}
}
};
and for your button:
private boolean saveRequested = false;
btnSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
if (editFocus){
saveRequested = true;
return;
} else {
save();
}
}
});
and then your save method:
private void save(){
// do your saving work
saveRequested = false;
}
This only works when your focusLost gets called after your button's action. If suddenly the order is correct, this code will get save() called twice.
But again, wrapping your save() code in your original approach should work, because the save code will execute after processing all events. That is after processing your button click and your focusLost events. Because your focusLost code executes immediately (it's not wrapped in an invokeLater()), the focusLost code should be executed always before your save code. This does not mean that the event order will be correct! But the code associated to the events will executed in the right order.