I am currently developing a JavaFX application and there I used a ButtonBar with some Buttons. When I run the program, the first Button is always focused. How can I disable that setting?
P.S.: I used some CSS code to change the default appearance and that brown box conveys that the button is focused.
You can take away the focus from the button with otherNode.requestFocus():
Requests that this Node get the input focus, and that this Node's
top-level ancestor become the focused window. To be eligible to
receive the focus, the node must be part of a scene, it and all of its
ancestors must be visible, and it must not be disabled. If this node
is eligible, this function will cause it to become this Scene's "focus
owner". Each scene has at most one focus owner node. The focus owner
will not actually have the input focus, however, unless the scene
belongs to a Stage that is both visible and active (docs).
Like in this example:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class App extends Application {
#Override
public void start(Stage stage) {
// Create controls:
ButtonBar buttonBar = new ButtonBar();
IntStream.range(0, 3).forEach(i -> buttonBar.getButtons().add(new Button("Button " + i)));
// This takes away the focus from the button:
Platform.runLater(buttonBar::requestFocus); // (Does not have to be the buttonBar itself)
// Prepare and show stage:
stage.setScene(new Scene(buttonBar));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Related
Lets say I have some TextField which is placed deep in the panes layout structure. I want add listener or recognize in some way that TextField changed its position (x, y) in Scene. The question is - how can I achive it in a proper, reusable way?
I provide some test code. To recreate please drag stage border, which will cause TextField position change.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
TextField textField = new TextField();
Button button = new Button("Button");
HBox hBox = new HBox(textField, button);
hBox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
StackPane stackPane = new StackPane(hBox);
stackPane.setPrefSize(600., 400.);
Scene scene = new Scene(stackPane);
stage.setScene(scene);
stage.show();
}
}
Add a listener to the localToSceneTransform property of the node.
node.localToSceneTransformProperty().addListener((o, oldValue, newValue) -> {
System.out.println("transform may have changed");
});
Note that this
Yields some "false positives", i.e. it may notify you, if the position hasn't actually changed.
Registering too many of those listeners to nodes may decrease the performance of your application, since doing so involves listening to changes for all nodes in the hierarchy up to the root node.
In addition to this a listener to the boundsInLocal property may be needed, if you also want to be notified to the size of the node itself changing.
I am trying to make a application where a user clicks on the screen and we add a circle with a number inside of the circle where the user clicked.
The way I want my application to work is: if I click the screen a pop up comes up asking the user to enter a number. When they press OK, a circle with the number inside of the circle is added to the screen where the user last clicked.
At the moment, I have got the pop up to work and when the user clicks OK a circle is added to the screen but it is not added where the user last clicked. I am not sure how to add the circle where the user last clicked. How would I do this?
As Slaw pointed out in his comment, you can get the X/Y coordinates of a mouse click from the MouseEvent.
So create an onMouseClicked() method for whichever node you allow the user to click on.
The following MCVE will demonstrate by printing out the X/Y coordinates whenever you click on the root VBox container:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
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);
// Add a listener to catch MouseEvents when clicking on the root VBox
root.setOnMouseClicked(mouseEvent -> {
System.out.println("X: " + mouseEvent.getX());
System.out.println("Y: " + mouseEvent.getY());
});
// Show the Stage
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
I'm trying to have a few CheckBoxes visualize ingredients on a pizza in this javafx app. The Pizza is a ImageView. But I don't know how I will go about adding ingredients. Lets talk about salami for a second!
My first idea was to do this on my setOnAction of my CheckBox salami: (gc being my graphics context)
Image salami1 = new Image("salami.png");
gc.setFill(new ImagePattern(salami1);
gc.fillOval(250, 200, 60, 60);
(I tried just adding another ImageView on top instead, but even though It was a .png which transparent background the background would still show. So I tried this instead. Since I will only go for cheese, salami this would be fine too. This is very basic and supposed to be just for practice on my side.)
However, How do I make the salami disappear again once I uncheck the box? I'm aware of gc.clearRect() but that's it. I'm clueless as on how to do this upon unchecking the box. Thanks in advance
You can do this pretty simply by binding the ImageView's visible property to the selected property of the appropriate CheckBox.
Here's a quick sample:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.image.ImageView;
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) {
// Just creating a sample interface
VBox root = new VBox(5);
root.setPadding(new Insets(5));
CheckBox chkSalami = new CheckBox("Salami");
ImageView imgSalami = new ImageView("salami.png");
// Bind the salami image's "visible" property to the checkbox's "selected" property
imgSalami.visibleProperty().bind(chkSalami.selectedProperty());
root.getChildren().addAll(chkSalami, imgSalami);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
The important part is the binding line. This is the basic logic that this line performs:
Always set the imgSalami object's visibility to match whether chkSalami is selected.
This means you do not need to mess around with adding any loops or ChangeListeners to the CheckBox; just bind each image to the matching checkbox.
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.
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.