remove default focus from TextField JavaFX - javafx

I have designed some TextField in a form by SceneBuilder, when I run the code, one of the TextFields has been clicked by default, I want when I run the code, none of the TextFields get selected by default and user select a TextFiled.
UPDATE: As you see in this image I want to make the first field like other two field when code runs(no curser in field)
How can I do this?

In my case the accepted answer is not working. But this worked:
i requested focus for parent wrapped in runLater.
#FXML
public void initialize() {
//unfocus pathField
Platform.runLater( () -> root.requestFocus() );
}
Direct call of requestFocuss does not work.

Had the same problem with a non - editable TextField, which got selected and highlighted every time.
Solved it by setting myTextField.setFocusTraversable(false);
Note that in this case the focus goes to the next UI element an you must set every single element you don't want focused. You lose also the ability to select the element via the Tab key.
The ApiDoc states that setFocusTraversable() is false by default, but that seems not to work, unless explicitly called.

As there is no public method to achieve this, there is no direct way. Though, you can use a trick to do it. You can have a BooleanProperty just to check when the control is focused for the first time. Listen to focusProperty() of the control and when it is focused for the first time, delegate the focus to its container. For the rest of the focus, it will work as it should.
Example:
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final BooleanProperty firstTime = new SimpleBooleanProperty(true); // Variable to store the focus on stage load
VBox vBox = new VBox(10);
vBox.setPadding(new Insets(20));
TextField t1 = new TextField();
TextField t2 = new TextField();
TextField t3 = new TextField();
t1.setPromptText("FirstName");
t2.setPromptText("LastName");
t3.setPromptText("Email");
vBox.getChildren().addAll(new HBox(t1, t2), t3);
primaryStage.setScene(new Scene(vBox, 300, 300));
primaryStage.show();
t1.focusedProperty().addListener((observable, oldValue, newValue) -> {
if(newValue && firstTime.get()){
vBox.requestFocus(); // Delegate the focus to container
firstTime.setValue(false); // Variable value changed for future references
}
});
}
public static void main(String[] args) {
launch(args);
}
}
On initial screen load :

You can simply set the focus traversable to false in the initialize method. Here is an example:
#Override
public void initialize(URL location, ResourceBundle resources) {
yourTextField.setFocusTraversable(false);
}

You can simply use Bindings
TextField textField = new TextField();
textField.styleProperty().bind(
Bindings
.when(textField.focusedProperty())
.then("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);")
.otherwise("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);"));
This will keep your prompt text visible even when the TextField is focused, as long it's empty.

Related

JavaFx 11 ListView consumes ESCAPE key pressed event even if is not in editing state

I have a problem with JavaFx ListView component. I'm using popup with TextField and ListView inside of VBox. When TextField is in focus, I can normally close this popup window pressing the Esc key on the keyboard, but when ListView item is in focus popup stays open, nothing happens.
Minimal reproducible example:
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
MenuItem rightClickItem = new MenuItem("CLICK!");
rightClickItem.setOnAction(a -> showdialog());
ContextMenu menu = new ContextMenu(rightClickItem);
Label text = new Label("Right Click on me");
text.setContextMenu(menu);
StackPane root = new StackPane(text);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("RightClick MenuItem And Dialog");
primaryStage.setScene(scene);
primaryStage.show();
}
private void showdialog() {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
VBox vBox = new VBox();
ListView listView = new ListView();
listView.getItems().add("Item 1");
listView.getItems().add("Item 2");
vBox.getChildren().add(new TextField());
vBox.getChildren().add(listView);
vBox.addEventHandler(KeyEvent.KEY_PRESSED, keyEvent -> System.err.println("Key pressed: " + keyEvent.getCode()));
dialog.getDialogPane().setContent(vBox);
dialog.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
It seems to me that Esc key is consumed in ListView, and this cause a problem with closing a popup.
Just to mention, I'm using zulu-11.0.8 JDKFx version.
It seems to me that Esc key is consumed in ListView, and this cause a problem with closing a popup.
That's indeed the problem - happens with all controls that have a consuming KeyMapping to ESCAPE added by their respective Behavior (f.i. also for a TextField with TextFormatter).
There is no clean way to interfere with it (Behavior and InputMap didn't yet make to move into public api). The way to hack around is to remove the KeyMapping from the Behavior's inputMap. Beware: you must be allowed to go dirty, that is use internal api and use reflection!
The steps:
grab the control's skin (available after the control is added to the scenegraph)
reflectively access the skin's behavior
remove the keyMapping from the behavior's inputMap
Example code snippet:
private void tweakInputMap(ListView listView) {
ListViewSkin<?> skin = (ListViewSkin<?>) listView.getSkin();
// use your favorite utility method to reflectively access the private field
ListViewBehavior<?> listBehavior = (ListViewBehavior<?>) FXUtils.invokeGetFieldValue(
ListViewSkin.class, skin, "behavior");
InputMap<?> map = listBehavior.getInputMap();
Optional<Mapping<?>> mapping = map.lookupMapping(new KeyBinding(KeyCode.ESCAPE));
map.getMappings().remove(mapping.get());
}
It's usage:
listView.skinProperty().addListener(ov -> {
tweakInputMap(listView);
});
To avoid using private API, you can use an event filter that, if the ListView is not editing, copies the Escape key event and fires it on the parent. From there, the copied event can propagate to be useful in other handlers such as closing a popup.
Also, if you need this on all ListViews in your application, you can do it in a derived class of ListViewSkin and set that as the -fx-skin for .list-view in your CSS file.
listView.addEventFilter( KeyEvent.KEY_PRESSED, keyEvent -> {
if( keyEvent.getCode() == KeyCode.ESCAPE && !keyEvent.isAltDown() && !keyEvent.isControlDown()
&& !keyEvent.isMetaDown() && !keyEvent.isShiftDown()
) {
if( listView.getEditingIndex() == -1 ) {
// Not editing.
final Parent parent = listView.getParent();
parent.fireEvent( keyEvent.copyFor( parent, parent ) );
}
keyEvent.consume();
}
} );

How to bind a List's size property to a Node's disabled property?

Suppose I have an ObservableList and a Button in the same controller class:
private ObservableList<NameItem> _selectedList = _database.getONameList();
#FXML
private Button nextButton;
How do I make it so that the Button is only enabled while the ObservableList is not empty? Is there a binding property I can use to set this?
This can be done fairly easily with just a couple of simple Bindings, actually.
First, you want to create an IntegerBinding that is bound to the size of your ObservableList:
IntegerBinding listSize = Bindings.size(_selectedList);
Then create a new BooleanBinding that is bound to whether or not the listSize binding is greater than 0:
BooleanBinding listPopulated = listSize.greaterThan(0);
Now, all you need to do is bind the button's disableProperty to the opposite of the listPopulated property using the not() method (since listPopulated will be true if items are in the list, you want to actually pass false to the button's disableProperty):
button.disableProperty().bind(listPopulated.not());
Here is a quick MCVE to demonstrate:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.IntegerBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Create an empty ObservableList
ObservableList<String> list = FXCollections.observableArrayList();
// Create a binding to extract the list's size to a property
IntegerBinding listSizeProperty = Bindings.size(list);
// Create a boolean binding that will return true only if the list has 1 or more elements
BooleanBinding listPopulatedProperty = listSizeProperty.greaterThan(0);
// Create the button we want to enable/disable
Button button = new Button("Submit");
// Bind the button's disableProperty to the opposite of the listPopulateProperty
// If listPopulateProperty is false, the button will be disabled, and vice versa
button.disableProperty().bind(listPopulatedProperty.not());
// Create another button to add an item to the list, just to demonstrate the concept
Button btnAddItem = new Button("Add Item");
btnAddItem.setOnAction(event -> {
list.add("New Item");
System.out.println(list.size());
});
// Add the buttons to the layout
root.getChildren().addAll(btnAddItem, button);
// Show the Stage
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
In the above example, the "Submit" button is disabled until you add an item to the ObservableList using the "Add Item" button.
EDIT: As Lukas excellently points out in the comments below, these Bindings can also all be chained together to simplify things (either method is equally valid; it just depends on which you find more readable, really):
button.disableProperty().bind(Bindings.size(list).greaterThan(0).not())
Another Method
Another way to do this is with a ListChangeListener that enables or disables the button any time the list changes:
list.addListener((ListChangeListener<String>) c -> {
// If the size of the list is less than 1, disable the button; otherwise enable it
button.setDisable(c.getList().size() < 1);
});
This will essentially do exactly the same thing as the first method, but you'll need to set the initial state of the button yourself before the listener can keep it updated for you.

How to remove selection on input in editable ComboBox

Yes, there are earlier threads and guides on the issue. And they tell me that either setValue(null) or getSelectionModel().clearSelection() should be the answer. But doing any of these gives me a java.lang.IndexOutOfBoundsException.
What I want to do is to clear the selection everytime something is being written into the combo box. This is because it causes problems and looks weird when you write something in the combo box and something else remains selected in the combo box popup.
Here's an SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
public class SSCCE extends Application {
#Override
public void start(Stage stage) {
HBox root = new HBox();
ComboBox<Integer> cb = new ComboBox<Integer>();
cb.setEditable(true);
cb.getItems().addAll(1, 2, 6, 7, 9);
cb.setConverter(new IntegerStringConverter());
cb.getEditor().textProperty()
.addListener((obs, oldValue, newValue) -> {
// Using any of these will give me a IndexOutOfBoundsException
// Using any of these will give me a IndexOutOfBoundsException
//cb.setValue(null);
//cb.getSelectionModel().clearSelection();
});
root.getChildren().addAll(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
You are running into this JavaFX ComboBox change value causes IndexOutOfBoundsException issue, which is causing the IndexOutOfBoundsException. These are kind of a pain.
There is a bit of a logical issue with your attempts anyway: clearing the selected value will cause the editor to update its text, so even if this worked it would make it impossible for the user to type. So you want to check that the changed value isn't the one typed in. This seems to fix both issues:
cb.getEditor().textProperty()
.addListener((obs, oldValue, newValue) -> {
if (cb.getValue() != null && ! cb.getValue().toString().equals(newValue)) {
cb.getSelectionModel().clearSelection();
}
});
You may need to change the toString() call, depending on the exact converter you are using. In this case, it will work.

ChangeListener stops being trigger even though changes continue to happen

I've run into a situation that seems very strange indeed.
Below is a simple javaFX app that has a TextFlow container contained in a ScollPane. In order to send the ScrollBar to the bottom of the ScrollPane as Text is added to the TextFlow container a DoubleProperty binding is made to the heightProperty to the TextFlow container (which changes after the TextFlow container has rendered after the latest addition of Text), with a ChangeListener added to move the vValue of the ScollPane to vMax (the bottom).
The Add button simply tests adding Text to the TextFlow container.
Now the strange part - the ChangeListener works perfectly for the first 9 to 11 Text additions after which it appears heightProperty changes are not being fired through the bind (well, at least the ChangeListener no longer executes). Whether it happens after 9, 10, or 11 times seems to vary with the speed of hitting the Add button.
Now the even stranger part - the ChangeListener works perfectly when the app is running in debug (no breakpoints needed)!
I am using Netbeans 8 with JDK1.8.0 on a Windows 8.1 laptop, all x64.
I'm hoping someone can replicate this and give me a clue as to why the ChangeListener is not executing after x Text adds (and why it does work in debug mode).
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class TextFlowTest extends Application {
final GridPane grid = new GridPane();
final Button addbtn = new Button();
final Button scrollbtn = new Button();
final TextFlow textflow = new TextFlow();
final ScrollPane textflowSP = new ScrollPane();
int counter = 0;
#Override
public void start(Stage primaryStage) {
// Attach a listener to the TextFlow component height property which
// is set after component has been rendered so we can set scroll bar to
// bottom.
DoubleProperty hProperty = new SimpleDoubleProperty();
hProperty.bind(textflow.heightProperty());
hProperty.addListener(new ChangeListener() {
#Override
public void changed(ObservableValue ov, Object t, Object t1) {
System.out.println("In listener " + ++counter + " with height of "
+ Double.toString((Double)t1));
textflowSP.setVvalue(textflowSP.getVmax());
}
}) ;
// Create Scroll Pane for TextFlow component
textflowSP.setPrefHeight(200);
textflowSP.setPrefWidth(200);
textflowSP.setFitToHeight(true);
textflowSP.setFitToWidth(true);
textflowSP.setContent(textflow);
grid.add(textflowSP,0,1);
// Button to add Text to TextFlow component
addbtn.setText("Add Text");
addbtn.setOnAction((ActionEvent e) -> {
textflow.getChildren().add(new Text("Line 1\nLine 2\nLine 3\n-\n"));
// Print shows that height property has not changed after adding Text
// but before rendering.
System.out.println(Double.toString(textflow.heightProperty().get()));
});
// Button to scoll to bottom of Scroll Pane
scrollbtn.setText("Scroll to Bottom");
scrollbtn.setOnAction((ActionEvent e) -> {
textflowSP.setVvalue(textflowSP.getVmax());
});
VBox btnVB = new VBox();
btnVB.getChildren().addAll(addbtn,scrollbtn);
grid.add(btnVB,1,1);
Scene scene = new Scene(grid, 330, 210);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Thanks in advance for any assistance offered.
Have a look at the docs from the bind function:
Note that JavaFX has all the bind calls implemented through weak listeners. This means the bound property can be garbage collected and stopped from being updated.
The binding between hProperty and textflow.heightProperty is done with weak listeners. So nothing keeps the hProperty from beeing garbage collected.
What happens in details:
Your program starts with the main function
Your program starts JavaFX by calling launch
Within the launch function the start-method will be called
hProperty will be filled with a new Reference to SimpleDoubleProperty
The start-method returns and JavaFX enters the Main-loop. When a function has returned all local variables of that function will be freed. At this time no hard reference exist for your hProperty. Only a weak reference left within the bindings-list of the heightProperty.
hProperty becomes garbage collected
The listener which were registered for the binding on textflow.heightProperty recognize the garbage collection action and remove them self from textflow.heightProperty
This is why your binding works until the next GC-cycle.
To prevent this you have to store a reference to your hProperty somewhere outside the local function scope, like in the scope of TextFlowTest. Or just register your listeners directly on the height-property:
textflow.heightProperty().addListener((obs, ov, nv) -> { ... });
I was having a similar problem. I solved it by declaring the property to be bound (in this case DoubleProperty hProperty = new SimpleDoubleProperty();) outside of any method so that it is not merely a local variable.

Using JavaFX 2.2 Mnemonic (and accelerators)

I'm trying to make JavaFX Mnemonic work. I have some button on scene and what I want to achieve is to fire this button event by pressing Ctrl+S.
Here is a code sceleton:
#FXML
public Button btnFirst;
btnFirst.getScene().addMnemonic(new Mnemonic(btnFirst,
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)));
Button's mnemonicParsing is false. (Well, while trying to make this work I've tried to set it to true, but no result). JavaFX documentation states that when a Mnemonic is registered on a Scene, and the KeyCombination reaches the Scene unconsumed, then the target Node will be sent an ActionEvent. But this doesn't work, probably, I'm doing wrong...
I can use the standard button's mnemonic (by setting mnemonicParsing to true and prefix 'F' letter by underscore character). But this way user have to use Alt key, that brings some strange behaviour on browsers with menu bar (if application is embedded into web page than browser's menu activated after firing button event by pressing Alt+S).
Besides, standard way makes it impossible to make shortcuts like Ctrl+Shift+F3 and so on.
So, if there some way to make this work?
For your use case, I think you actually want to use an accelerator rather than a mnemonic.
button.getScene().getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#Override public void run() {
button.fire();
}
}
);
In most cases it is recommended that you use KeyCombination.SHORTCUT_DOWN as the modifier specifier, as in the code above. A good explanation of this is in the KeyCombination documentation:
The shortcut modifier is used to represent the modifier key which is
used commonly in keyboard shortcuts on the host platform. This is for
example control on Windows and meta (command key) on Mac. By using
shortcut key modifier developers can create platform independent
shortcuts. So the "Shortcut+C" key combination is handled internally
as "Ctrl+C" on Windows and "Meta+C" on Mac.
If you wanted to specifically code to only handle a Ctrl+S key combination, they you could use:
new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)
Here is an executable example:
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SaveMe extends Application {
#Override public void start(final Stage stage) throws Exception {
final Label response = new Label();
final ImageView imageView = new ImageView(
new Image("http://icons.iconarchive.com/icons/gianni-polito/colobrush/128/software-emule-icon.png")
);
final Button button = new Button("Save Me", imageView);
button.setStyle("-fx-base: burlywood;");
button.setContentDisplay(ContentDisplay.TOP);
displayFlashMessageOnAction(button, response, "You have been saved!");
layoutScene(button, response, stage);
stage.show();
setSaveAccelerator(button);
}
// sets the save accelerator for a button to the Ctrl+S key combination.
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#Override public void run() {
fireButton(button);
}
}
);
}
// fires a button from code, providing visual feedback that the button is firing.
private void fireButton(final Button button) {
button.arm();
PauseTransition pt = new PauseTransition(Duration.millis(300));
pt.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
button.fire();
button.disarm();
}
});
pt.play();
}
// displays a temporary message in a label when a button is pressed,
// and gradually fades the label away after the message has been displayed.
private void displayFlashMessageOnAction(final Button button, final Label label, final String message) {
final FadeTransition ft = new FadeTransition(Duration.seconds(3), label);
ft.setInterpolator(Interpolator.EASE_BOTH);
ft.setFromValue(1);
ft.setToValue(0);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
label.setText(message);
label.setStyle("-fx-text-fill: forestgreen;");
ft.playFromStart();
}
});
}
private void layoutScene(final Button button, final Label response, final Stage stage) {
final VBox layout = new VBox(10);
layout.setPrefWidth(300);
layout.setAlignment(Pos.CENTER);
layout.getChildren().addAll(button, response);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
}
public static void main(String[] args) { launch(args); }
}
// icon license: (creative commons with attribution) http://creativecommons.org/licenses/by-nc-nd/3.0/
// icon artist attribution page: (eponas-deeway) http://eponas-deeway.deviantart.com/gallery/#/d1s7uih
Sample output:
Update Jan 2020, using the same accelerator for multiple controls
One caveat for accelerators in current and previous implementations (JavaFX 13 and prior), is that you cannot, out of the box, define the same accelerator key combination for use on multiple menus or controls within a single application.
For more information see:
JavaFX ContextMenu accelerator firing from wrong tab
and the related JDK-8088068 issue report.
The linked issue report includes a work-around you can use to allow you define and use the same accelerator within multiple places within an application (for example on two different menu items in different context menus).
Note that this only applies to trying to use the same accelerator in multiple places within an application, if you don't need try to do that, then you can ignore this information.

Resources