Javafx radio button binding directionally - java

Am still new to javafx and i would like to perform a directional binding to radio buttons
in my fxml i have
<fx:define>
<ToggleGroup fx:id="version_selection" />
</fx:define>
<RadioButton toggleGroup="$version_selection" ............>
<RadioButton toggleGroup="$version_selection" ............>
In my controller i want to bind the value of the selected togglegroup. With textfields its easy as its just
#FXML
TextField name;
#FXML
private ToggleGroup version_selection;
name.textProperty().bindBidirectional(model.field5Property());
where the model is my class with the SimpleStringValue property
How do i bind the togglegroup as it doesnt have a textproperty as a textfield

Even if you had a ObjectProperty<Toggle> in your model, it you couldn't establish a bidirectional binding, since ToggleGroup.selectedToggle is readonly. You need to work around this by adding a listener to both the ToggleGroup.selectedToggle property and the model property and update the property other than the source of the change on a change.
Use a Map<String, Toggle> to convert the text to the toggle to be selected. The following example uses a TextField to determine the property instead of a model property to demonstrate the behaviour: input the text of the RadioButton to select and it'll be selected.
#Override
public void start(Stage primaryStage) throws IOException {
ToggleGroup group = new ToggleGroup();
VBox vbox = new VBox();
for (char c = 'a'; c <= 'f'; c++) {
RadioButton radio = new RadioButton(Character.toString(c));
radio.setToggleGroup(group);
vbox.getChildren().add(radio);
}
TextField input = new TextField();
vbox.getChildren().add(input);
// create map mapping from text to corresponding toggle
Map<String, Toggle> map = new HashMap<>();
for (Toggle t : group.getToggles()) {
map.put(((RadioButton) t).getText(), t);
}
StringProperty property = input.textProperty();
InvalidationListener listener = new InvalidationListener() {
// flag preventing circular updating
boolean updating = false;
#Override
public void invalidated(Observable observable) {
if (!updating) {
updating = true;
if (observable == group.selectedToggleProperty()) {
// change as result of user interaction with RadioButton
// update property
RadioButton selectedToggle = (RadioButton) group.getSelectedToggle();
property.set(selectedToggle == null ? "" : selectedToggle.getText());
} else {
// change as result of updating the property
// select corresponding toggle
Toggle toggleToSelect = map.get(property.get());
group.selectToggle(toggleToSelect);
}
updating = false;
}
}
};
property.addListener(listener);
group.selectedToggleProperty().addListener(listener);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}

Related

How to hide TabPane content on Tab clicked in JavaFX

Here is a code:
package tabpane;
import javafx.application.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class HideShowTabContentOnClicked extends Application {
public static void main(String[] args) {
launch(args);
}
private BorderPane createContent() {
BorderPane borderPane = new BorderPane();
TabPane tabPane = new TabPane();
tabPane.setSide(Side.LEFT);
StackPane stackPane = new StackPane();
stackPane.setStyle("-fx-background-color: lightblue");
stackPane.setMinWidth(200);
Tab firstTab = new Tab("First");
firstTab.setClosable(false);
firstTab.setContent(stackPane);
firstTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
firstTab.setContent(null);
} else {
firstTab.setContent(stackPane);
}
});
Tab secondTab = new Tab("Second");
StackPane stackPane2 = new StackPane();
stackPane2.setStyle("-fx-background-color: yellow");
secondTab.setContent(stackPane2);
secondTab.setClosable(false);
secondTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
secondTab.setContent(null);
} else {
secondTab.setContent(stackPane2);
}
});
StackPane center = new StackPane();
center.setStyle("-fx-background-color: cyan");
borderPane.setCenter(center);
tabPane.getTabs().addAll(firstTab, secondTab);
borderPane.setLeft(tabPane);
return borderPane;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setMaximized(true);
stage.show();
}
}
Here I tried to solve a problem by using selectedProperty() by setting content to null, but it doesn't working, I want to make Tab like toggle button so that when I click on it showed and hide TabPanes content.
Before
And when clicked
As an example I want to implement TabPane like Intellij IDEA Tool Buttons (like "Project", "Structure" Tool Buttons etc).
If you are going to keep your content into StackPane, you can bind stackPane.visibleProperty() with toggleButton.selectedProperty():
stackPane.visibleProperty()
.bind(Bindings.when(toggleButton.selectedProperty())
.then(false)
.otherwise(true)
);
in this exampl: toggleButton.isSelected() --> !stackPane.isVisible() and !toggleButton.isSelected() --> stackPane.isVisible(),
or listen ToggleButton's events:
// toggleButton.setOnAction(e ->{ //new .setOnAction() -> Override previous
toggleButton.addEventHandler(ActionEvent.ACTION, e ->{ //can add any quantity for your needs
if(toggleButton.isSelected())
stackPane.setVisible(false);
else stackPane.setVisible(true);
});
But the problem is instead of toggle button I want to use Tab, so that it behaves like toggle button. i.e. when click "First Tab" in my example code if content visible it should be invisible and vice versa. I mean only tabs should be shown
I found solution.Tab does not have click-handler... but
Tab tab = new Tab();
tab.setContent(stackPane);
Label lable = new Label("Label"); //create Label
tab.setGraphic(lable); //set Lable as Graphic to Tab
lable.setOnMouseClicked(event ->{ //setOnMouseClicked, for example
if(stackPane.isVisible()){
stackPane.setVisible(false);
}else{
stackPane.setVisible(true);
}
});
, you can use Label(for example) as Tab-text and add setOnMouseClicked()-handler to Label. You can use any Node with Handler/ActionListener -> It's up to you.
For example, you can use CheckBox to show/hide StackPane, and Tab text (you can combine FXML and Java-code to produce graphics):
Tab tab = new Tab("Tab2"); //Tab with Text
tab.setContent(stackPane);
CheckBox checkBox = new CheckBox(); //create CheckBox
tab.setGraphic(checkBox); //set CheckBox as Graphic to Tab
stackPane.visibleProperty()
.bind(Bindings.when(checkBox.selectedProperty())
.then(false)
.otherwise(true)
);
or
#FXML
private Tab tab;
// ...
tab.setGraphic(checkBox);
// ...
I have came up with this solution:
AtomicReference<Tab> currentTab = new AtomicReference<>(tabPane.getSelectionModel().getSelectedItem());
AtomicReference<Tab> lastTab = new AtomicReference<>(null);
tabPane.setOnMouseReleased(event -> {
// Check if current node is actually tab
Node n = event.getPickResult().getIntersectedNode();
while (n != null && !(n.getStyleClass().contains("headers-region"))) {
n = n.getParent();
}
if (n == null)
return;
lastTab.set(currentTab.get());
currentTab.set(tabPane.getSelectionModel().getSelectedItem());
if (currentTab.get() == lastTab.get()) {
// Hide
tabPane.setPrefSize(28, 28);
//tabPane.getSelectionModel().clearSelection(); // notify selection model
currentTab.set(null);
} else {
// Show
tabPane.setPrefSize(-1,-1);
currentTab.set(tabPane.getSelectionModel().getSelectedItem());
}
});
First of all, I have added mouse event to the tabPane. Inside this mouse event, check if node under cursor is actually Tab node. If it is, do some logic to identify what user is trying to do: hide or show. Hiding is a bit tricky, so I ended up with setting preferred size of TabPane to 28 px wide.
I have also tried to notify selection model with an empty newValue:
tabPane.getSelectionModel().clearSelection();
But this it is not working properly. Calling select(-1) should call clearSelection(), but behavior is different somehow.
When I select another tab after calling clearSelection(), selection model handler called with oldValue == null, that possibly does not update internal index and tab does not swithes to selected one.

JavaFX: Getting IDs of Dynamic created Button

I'm currently made an Form with JavaFX.
Always i press a Button, i call the "addAnswer()"-Method.
In that I create a RadioButton, a Label and a delete-Button, which i bundle in a HBox. All that HBoxes i pack in a vBox.
The Problem now is the delete-Button. I want to delte just THAT HBox in which the clicked Button is.
Here is my code:
public void addAnswer() {
this.rB = new RadioButton();
checkAnswer.getToggles().add(rB);
hBox = new HBox();
tF = new TextField();
delAnswer = new Button("Löschen");
delAnswer.setId(Integer.toString(counter));
hBox.getChildren().addAll(rB, tF, delAnswer);
hBox.setId(Integer.toString(counter));
delAnswer.setOnAction(e -> delAnswer(Integer.parseInt(hBox.getId())));
System.out.println(delAnswer.getId());
vBox.getChildren().addAll(hBox);
counter++;
}
public void delAnswer(int e){
vBox.getChildren().remove(delAnswer.getId());
}
i tried this one above but i realized, that all the delAnswers-Buttons have the same ID: the number of how often i pressed the add-Button.
Is there any solution where i can just select that one i pressed with that dynamic way? Cause i don't kow how often somebody will press or delete something.
Thanks
hbox is a field and this is why always the HBox last added is used. (hBox is evaluated, when lambda body is executed, not at the time of the lambda creation). This would be different, if you used a (effectively) final local variable:
final HBox hBoxLocal = hBox;
delAnswer.setOnAction(e -> delAnswer(Integer.parseInt(hBoxLocal.getId())));
However I'd like to present a different solution which would allow you to use the same EventHandler<ActionEvent> for all delete Buttons:
You can get the Node that triggered the event using getSource. From this Node you can get the parent, which is the HBox. You can remove this from the VBox using the remove(Object) method
delAnswer.setOnAction(e -> {
// get button
Node source = (Node) e.getSource();
// remove parent of button from VBox
vBox.getChildren().remove(source.getParent());
});
I think your problem is that you give the same event to all your button,Begin by creating a list that stores your buttons and then increments the value of the ID after affecting it to an item :
List<Button> buttons = new ArrayList<>();
/*
Create Button and call IDEvt method to create new event
for each button
*/
private void IDEvt(Button btn){
btn.setId(String.valueOf(IDRank));
btn.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println(btn.getId());
}
});
IDRank++;
}

JavaFX CheckBoxTree in popup of drop-down Button

In order for the end-user to constrain a search to some columns of the main TableView, I needed a treeview with checkboxes.
I decided to embed this TreeView in a popup, showing on click on a custom button.
I have created the following class, inspired from the question:
Java FX8 TreeView in a table cell
public class CustomTreeMenuButton extends MenuButton {
private PopupControl popup = new PopupControl();
private TreeView<? extends Object> tree;
private CustomTreeMenuButton me = this;
public void setTree(TreeView<? extends Object> tree) {
this.tree = tree;
}
public CustomTreeMenuButton() {
super();
this.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (!popup.isShowing()) {
Bounds b = me.localToScreen(me.getBoundsInLocal());
double x = b.getMinX();
double y = b.getMaxY();
popup.setAutoHide(true);
// popup.setAutoFix(true);
popup.setAnchorX(x);
popup.setAnchorY(y);
popup.setSkin(new Skin<Skinnable>() {
#Override
public void dispose() {
}
#Override
public Node getNode() {
return tree;
}
#Override
public Skinnable getSkinnable() {
return null;
}
});
popup.show(me.getScene().getWindow());
}
}
});
}
}
The tree I am working with contains CheckBoxTreeItem objects, and while the popup is working, there is some weird blur on all checkboxes, whenever the focus is not on a checkbox. (See GIF below)
First, I was thinking it was maybe an antialiasing problem, but popup.getScene().getAntiAliasing().toString() returns DISABLED
Then, I saw that non integer anchor points could cause problems. However popup.setAutoFix(true) did nothing, nor did the following:
popup.setAnchorX(new Double(x).intValue());
popup.setAnchorY(new Double(y).intValue());
It might be worth noting that I am working with FXML.
How can I get sharp checkboxes regardless of their focus ?
I would suggest a built-in control, CustomMenuItem, rather than reinventing the wheel:
A MenuItem that allows for arbitrary nodes to be embedded within it,
by assigning a Node to the content property.
An example
// Create the tree
CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<String>("All stuff");
rootItem.setExpanded(true);
final TreeView<String> tree = new TreeView<String>(rootItem);
tree.setEditable(true);
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
for (int i = 0; i < 8; i++) {
final CheckBoxTreeItem<String> checkBoxTreeItem =
new CheckBoxTreeItem<String>("Stuff" + (i+1));
rootItem.getChildren().add(checkBoxTreeItem);
}
tree.setRoot(rootItem);
tree.setShowRoot(true);
// Create a custom menu item
CustomMenuItem customMenuItem = new CustomMenuItem(tree);
customMenuItem.setHideOnClick(false);
// Create the menu button
MenuButton mb = new MenuButton("Stuffs");
mb.getItems().add(customMenuItem);
And the output
Note: It is important to set the hideOnClickProperty to true, to avoid closing when the user clicks in the tree, which can be even done in the contructor, so you can shorten the initialization to:
CustomMenuItem customMenuItem = new CustomMenuItem(tree, false);
If you want to remove the hover glow, you can add the following CSS class:
.menu-item {
-fx-padding: 0;
}

How to get the name of a MenuItem which is clicked in a MenuButton

I'm trying to get the name of a MenuItem and then set a Label text to the name of this clicked MenuItem in a MenuButton. How can i do this? The MenuItems getting the id's from a String in a DB.
Variables:
#FXML
private MenuButton projektAuswahl;
MenuItem item;
Creating new MenuItem:
ResultSet rs = stmt.executeQuery(sqlQuery);
while (rs.next()) {
item = new MenuItem(rs.getString(1));
item.setId(rs.getString(1));
projektAuswahl.getItems().add(item);
}
set the Label text:
private void setProjectLabel() {
//projectnameLabel.setText(item.getText()); //not working..
}
How can i recognize which MenuItem is clicked?
You also need to register a handler for the onAction event. This could be a new one for each MenuItem saving the data about the text each, or you could access the event source to get a reference to the MenuItem:
static MenuItem createMenuItem(String text, EventHandler<ActionEvent> handler) {
MenuItem result = new MenuItem(text);
result.setOnAction(handler);
return result;
}
private Label projectnameLabel;
private void setProjectLabel(ActionEvent event) {
MenuItem source = (MenuItem) event.getSource();
projectnameLabel.setText(source.getText());
}
#Override
public void start(Stage primaryStage) {
EventHandler<ActionEvent> handler = this::setProjectLabel;
MenuButton btn = new MenuButton("menu");
btn.getItems().addAll(createMenuItem("a", handler),
createMenuItem("b", handler),
createMenuItem("c", handler));
projectnameLabel = new Label();
VBox root = new VBox(10, btn, projectnameLabel);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
You can add action listener directly in code like :
menuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
setProjectLabel(); // in your case
}
});
Or I propose you to add a controller to your fxml, then replace all code you displayed in it and bind the setProjectLabel() with the menuitem in SceneBuilder here :

Retrieving ToggleGroup name JavaFX

I'm trying to retrieve the "name" (or any type of unique identifier) of the ToggleGroup of a given ToggleButton, and have been having no success. It is important, if possible, that the different buttons share the event handler. Any help?
//create toggleGroups
final ToggleGroup toggleGroup1 = new ToggleGroup();
final ToggleGroup toggleGroup2 = new ToggleGroup();
//create toggleButtons
final ToggleButton toggleButton1 = new ToggleButton("b1");
final ToggleButton toggleButton2 = new ToggleButton("b2");
//assign toggleButtons to toggleGroups
toggleButton1.setToggleGroup(toggleGroup1);
toggleButton2.setToggleGroup(toggleGroup2);
//assign custom handler to toggleButtons
toggleButton1.setOnAction(new handleEvent());
toggleButton2.setOnAction(new handleEvent());
//handler
class handleEvent implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent e) {
// enable ability to get toggleButton name later
ToggleButton b = (ToggleButton)e.getSource();
//get name of group toggleButton belongs too?
String toggleGroup = b.getToggleGroup().toString();
// do stuff based on name and group
if (toggleGroup == "toggleGroup1") {
//do stuff
} else if (toggleGroup == "toggleGroup2") {
//d other stuff
}
}
}
You can set the name as userData: toggleGroup.setUserData("toggleGroupName")
To retrieve it back you call: String name = (String) toggleGroup.getUserData()

Categories

Resources