How to show and hide Images on check/unchecking of a Checkbox? - javafx

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.

Related

First Button in a ButtonBar is always focused?

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();
}
}

JavaFX TabPane width and Button width donĀ“t match

While I was trying to use the TabPane container in JavaFX I noticed, that if I give the Tabs in the TabPane a specific width and another element e.g. a Button the exact same width, they show up on the screen with different sizes.
Here is an example: As you can see in this image, the Button is smaller than the width of the tap
Here is the Code I for this specific image:
package stackOverflow;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TabPaneMystery extends Application {
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
StackPane stackPane = new StackPane();
Scene scene = new Scene(stackPane, 600, 400);
stage.setScene(scene);
stage.show();
TabPane tabPane = new TabPane();
stackPane.getChildren().add(tabPane);
Tab tab1 = new Tab();
tabPane.setTabMaxWidth(160); //The width of the tab is '160'
tabPane.setTabMinWidth(160);
tabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
tab1.setText("Tab");
tabPane.getTabs().add(tab1);
Button b = new Button();
b.setText("Button");
b.setPrefWidth(160); //The width of the button is '160'
b.setTranslateX(6);
tab1.setContent(b);
}
}
in Line 33 and 34 (where the first comment is) I set the width of the tapPane to be 160 and in Line 42 (where the second comment is) I set the exact same width
This results in the shown image.
My question is:
Have I made an obvious mistake or is it some kind of bug or does the compiler interpret the width for every node somewhat different?
I suspect you're seeing some padding in the Tab. The actual width of the Tab comes out to 170. You can confirm this by changing the width of the Tab to 150; it will then be the exact same size as the Button:
You could also use CSS to remove the padding:
tab1.setStyle("-fx-padding: 0");
Although, as you can see below, it isn't an exact match and there may be other CSS properties to look into. Hope this gets you closer to your goal, though:

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.

Create a draggable selection box for a sketching program in JavaFX

I'm trying to create a draggable selection box for a sketching program in JavaFX, one like this:
I'm only not sure how to do it. I initially wanted to do it like this: capture the mouse coordinates when the mouse is pressed and do it again at the end of a drag, then calculate the height and width and make a transparent button with a black border with these properties.
But, then I realized that when I do it like this, it is not possible to see the button while you are scaling the plane, unless you draw and delete a lot of buttons.
So, I wondered if there is a better way to do something like this or is my reasoning above right? Thanks
I would use a Rectangle instead of a Button. Just do what you describe, but update the size (and position) of the rectangle on mouse drag, instead of only adding it when the mouse is released.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class SelectionRectangle extends Application {
private double mouseDownX ;
private double mouseDownY ;
#Override
public void start(Stage primaryStage) {
Rectangle selectionRectangle = new Rectangle();
selectionRectangle.setStroke(Color.BLACK);
selectionRectangle.setFill(Color.TRANSPARENT);
selectionRectangle.getStrokeDashArray().addAll(5.0, 5.0);
Pane pane = new Pane();
pane.setMinSize(600, 600);
pane.getChildren().add(selectionRectangle);
pane.setOnMousePressed(e -> {
mouseDownX = e.getX();
mouseDownY = e.getY();
selectionRectangle.setX(mouseDownX);
selectionRectangle.setY(mouseDownY);
selectionRectangle.setWidth(0);
selectionRectangle.setHeight(0);
});
pane.setOnMouseDragged(e -> {
selectionRectangle.setX(Math.min(e.getX(), mouseDownX));
selectionRectangle.setWidth(Math.abs(e.getX() - mouseDownX));
selectionRectangle.setY(Math.min(e.getY(), mouseDownY));
selectionRectangle.setHeight(Math.abs(e.getY() - mouseDownY));
});
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You can use a mouse released handler to figure out what's selected, by looking at the x, y, width, and height properties of the rectangle, as needed.

remove default focus from TextField 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.

Resources