Different events for CheckBox's box and label?(JavaFX) - checkbox

I want my CheckBox to become selected only when box is clicked. If CheckBox's label is clicked I want to perform different action with this CheckBox being not selected. How is it possible to achieve this?
I made separate checkbox and label. But I can't change label's pseudo class through checkbox. They are al in listview. Here is a part of the code:
HBox todoHBox=new HBox(0);
CheckBox todoCheckBox=new CheckBox();
Label todoLabel=new Label(item.getName());
Label timeLabel=new Label();
Region rSpring = new Region();
todoHBox.setPrefWidth(300);
todoHBox.setHgrow(rSpring, Priority.ALWAYS);
todoHBox.setHgrow(timeLabel, Priority.ALWAYS);
todoHBox.setHgrow(todoCheckBox, Priority.NEVER);
timeLabel.setMinWidth(60);
timeLabel.getStyleClass().add("time-label");
todoLabel.getStyleClass().add("todo-label");
todoHBox.getChildren().addAll(todoCheckBox,todoLabel,rSpring,timeLabel);
todoHBox.setMargin(timeLabel, new Insets(3,0,0,0));
PseudoClass pseudo = PseudoClass.getPseudoClass("task-done");
todoCheckBox.selectedProperty().addListener(e->{
if(todoCheckBox.isSelected()){
todoLabel.pseudoClassStateChanged(pseudo, true);
}else{
todoLabel.pseudoClassStateChanged(pseudo, false);
}});
setGraphic(todoHBox);

Just use two controls: a CheckBox with no text and a Label. Call setOnMouseClicked(...) on the Label with a handler that does whatever you need.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CheckBoxWithLabelExample extends Application {
#Override
public void start(Stage primaryStage) {
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().addListener((obs, wasSelected, isNowSelected) ->
System.out.println("Check box is now "+(wasSelected ? "not ":"") + "selected"));
Label label = new Label("Make Task");
label.setOnMouseClicked(e -> System.out.println("Text clicked"));
HBox control = new HBox(checkBox, label);
Scene scene = new Scene(new StackPane(control), 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Related

How to implement a constantly active loop to check for changes?

I created an empty TextField test and an empty TextField test2 in the "start" method
public void start(Stage primaryStage) throws Exception {}
Now I want to check constantly if test.getText().equals(""). If so, test2.setEditable(false), otherwise test2.setEditable(true).
I don't know how to implement it, as it needs to get checked constantly.
I already tried to implement an if-Statement in the start method and it actually set test2 on notEditable at the beginning, astest is empty but when test.getText().equals("") changed to !test.getText().equals("") test2 was still not editable.
You can do this by binding the editable property of test2 to the text property of test.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
TextField test = new TextField();
TextField test2 = new TextField();
// do the binding
test2.editableProperty().bind(test.textProperty().isEmpty().not());
VBox root = new VBox(20, test, test2);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(50));
primaryStage.setScene(new Scene(root, 300, 150));
primaryStage.setTitle("Example");
primaryStage.show();
}
}
The text property of TextField is a StringProperty. This class has the method isEmpty (inherited by StringExpression) that returns a BooleanBinding that will hold true if the StringProperty's value is empty or null. The not() call negates the value of the BooleanBinding which means test2 will only be editable if test's text is not empty.
Instead of checking all the time you can just listen on changes like this:
test.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.equals("")) {
test2.setEditable(false);
}
);

CheckBox inside TableCell breaks the traversal order

Consider the following example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TestCheckBoxTab extends Application {
public void start (Stage stage) {
HBox root = new HBox();
root.getChildren().addAll(new TextField(), new TextField(), new CheckBox(), new TextField());
stage.setScene(new Scene(root));
stage.show();
}
public static void main (String[] args) {
launch();
}
}
Here you can easily use the TAB and SHIFT+TAB commands to traverse through the different controls, and it works properly.
But if you have the same controls inside table cells in a TableView, the CheckBox breaks the traversal order. This is demonstrated in the following example:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class AlwaysEditableTable extends Application {
public void start(Stage stage) {
TableView<ObservableList<StringProperty>> table = new TableView<>();
table.setEditable(true);
table.getSelectionModel().setCellSelectionEnabled(true);
table.setPrefWidth(505);
// Dummy columns
ObservableList<String> columns = FXCollections.observableArrayList("Column1", "Column2", "Column3", "Column4",
"Column5");
// Dummy data
ObservableList<StringProperty> row1 = FXCollections.observableArrayList(new SimpleStringProperty("Cell1"),
new SimpleStringProperty("Cell2"), new SimpleStringProperty("0"), new SimpleStringProperty("Cell4"),
new SimpleStringProperty("1"));
ObservableList<ObservableList<StringProperty>> data = FXCollections.observableArrayList();
data.add(row1);
for (int i = 0; i < columns.size(); i++) {
final int j = i;
TableColumn<ObservableList<StringProperty>, String> col = new TableColumn<>(columns.get(i));
col.setCellValueFactory(param -> param.getValue().get(j));
col.setPrefWidth(100);
if (i == 2 || i == 4) {
col.setCellFactory(e -> new CheckBoxCell(j));
} else {
col.setCellFactory(e -> new AlwaysEditingCell(j));
}
table.getColumns().add(col);
}
table.setItems(data);
Scene scene = new Scene(table);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
/**
* A cell that contains a text field that is always shown. The text of the
* text field is bound to the underlying data.
*/
public static class AlwaysEditingCell extends TableCell<ObservableList<StringProperty>, String> {
private final TextField textField;
public AlwaysEditingCell(int columnIndex) {
textField = new TextField();
this.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
setGraphic(null);
} else {
setGraphic(textField);
}
});
// The index is not changed until tableData is instantiated, so this
// ensure the we wont get a NullPointerException when we do the
// binding.
this.indexProperty().addListener((obs, oldValue, newValue) -> {
ObservableList<ObservableList<StringProperty>> tableData = getTableView().getItems();
int oldIndex = oldValue.intValue();
if (oldIndex >= 0 && oldIndex < tableData.size()) {
textField.textProperty().unbindBidirectional(tableData.get(oldIndex).get(columnIndex));
}
int newIndex = newValue.intValue();
if (newIndex >= 0 && newIndex < tableData.size()) {
textField.textProperty().bindBidirectional(tableData.get(newIndex).get(columnIndex));
setGraphic(textField);
} else {
setGraphic(null);
}
});
}
}
/**
* A cell containing a checkbox. The checkbox represent the underlying value
* in the cell. If the cell value is 0, the checkbox is unchecked. Checking
* or unchecking the checkbox will change the underlying value.
*/
public static class CheckBoxCell extends TableCell<ObservableList<StringProperty>, String> {
private final CheckBox box;
private ObservableList<ObservableList<StringProperty>> tableData;
public CheckBoxCell(int columnIndex) {
this.box = new CheckBox();
this.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
setGraphic(null);
} else {
setGraphic(box);
}
});
this.indexProperty().addListener((obs, oldValue, newValue) -> {
tableData = getTableView().getItems();
int newIndex = newValue.intValue();
if (newIndex >= 0 && newIndex < tableData.size()) {
// If active value is "1", the check box will be set to
// selected.
box.setSelected(tableData.get(getIndex()).get(columnIndex).equals("1"));
// We add a listener to the selected property. This will
// allow us to execute code every time the check box is
// selected or deselected.
box.selectedProperty().addListener((observable, oldVal, newVal) -> {
if (newVal) {
// If newValue is true the checkBox is selected, and
// we set the corresponding cell value to "1".
tableData.get(getIndex()).get(columnIndex).set("1");
} else {
// Otherwise we set it to "0".
tableData.get(getIndex()).get(columnIndex).set("0");
}
});
setGraphic(box);
} else {
setGraphic(null);
}
});
}
}
}
When pressing TAB while focusing a TextField inside a TableCell, the focus is moved to the next control properly. But if a CheckBox inside a TableCell is focused, the focus is moved to the first Control in the TableView instead of to the next Control. SHIFT+TAB while focusing a CheckBoxwill move the focus to the last control in the TableView. If I add a TextField outside the TableView, SHIFT+TAB while focusing a CheckBox will actually focus that TextField, while TAB behaviour still at least keeps the focus inside the TableView. The CheckBox somehow breaks the traversal order.
This is strange to me since TextField and CheckBox seems to have the same TAB functionality implemented, due to the fact the traversal order is correct in the first example. Something I guess is inherited from the Control class.
Does anybody know anything about this? I tried to look for some kind of EventFilter or listener for the TAB and SHIFT+TAB commands inside the source code of Control, TextField, CheckBox, TextInputControl and even in the Scene source, but I couldn't find it anywhere.
I also tried implementing my own TAB functionality for the CheckBox cell, which ended up raising another issue.
I was struggling a lot with the same issue. I finally avoided it by setting:
checkBox.setFocusTraversable(false);
and
this.setFocusTraversable(false);
on my own implementation of BooleanCell.
The table traverses over editable fields and the Checkbox is always enabled in my implementation. But setting the Checkbox disabled means you have to double click on it to change the value. That would be weird behaviour.

MouseListener on editable JavaFX ComboBox

MouseEvent is not working for JavaFX ComboBox when it is set to editable. It is working well on Non editable ComboBox. Is there any way to set MouseEvent on editable ComboBox?
package editablecombobox;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class EditableComboBox extends Application {
#Override
public void start(Stage primaryStage) {
ComboBox combo = new ComboBox();
combo.setEditable(true);
combo.setOnMousePressed(new EventHandler<MouseEvent>() { //adding MouseEvent on ComboBox
#Override
public void handle(MouseEvent event) {
System.out.println("MOUSE PRESSED!!!");
}
});
StackPane root = new StackPane();
root.getChildren().add(combo);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
setOnMousePressed is not working.
I guess you are just interested in the Editable TextField area of the ComboBox. You need to add the MouseListener to the editor(TextField) of the ComboBox. Try using :
combo.getEditor().setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("MOUSE PRESSED!!!");
}
});

Miglayout aligning components to top of cell

I have a group of buttons that will just stay in the middle of the panel. I am trying to place them at the top of the panel in a flowy layout constraint. Any help in achieving this is greatly appreciated. I have tried dock and align but didn't do anything.
private class ProductPanel extends JPanel {
private JLabel lblProd;
private JButton butAdd;
private JButton butRemove;
private JButton butEdit;
private Product_Table_Model ptm;
private JScrollPane scroll;
private JPanel buttonPanel;
private JTable table;
ProductPanel() {
setLayout(new MigLayout("debug"));
ptm = new Product_Table_Model(
(ArrayList<Product>) client
.receiveObject("Get_Product_Data"));
initComponents();
}
public void initComponents() {
lblProd = new JLabel ("Product List: ");
buttonPanel = new JPanel (new MigLayout());
butAdd = new JButton ("Add");
butRemove = new JButton ("Remove");
butEdit = new JButton ("Edit");
buttonPanel.add(butAdd, "cell 0 0");
buttonPanel.add(butRemove, "cell 0 1");
buttonPanel.add(butEdit, "cell 0 2");
butAdd.setPreferredSize(new Dimension(40, 50));
add(lblProd, "wrap");
table = new JTable(ptm);
table.setFillsViewportHeight(true);
scroll = new JScrollPane(table);
add(scroll);
add(buttonPanel);
}
}
The buttons are aligned to the top, as expected. It is the panel itself that
was not aligned within its cell. So the following will fix the issue:
add(buttonPanel, "top");
You should not be setting the preffered size with a setPrefferedSize() method.
Instead, use size groups.
MigLayout is very powerful layout manager, you don't need to create two
panels with two layout managers. It is possible to create your layout much easier.
The following example is one such solution:
package com.zetcode;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import net.miginfocom.swing.MigLayout;
public class ProductPanel2 extends JPanel {
private JLabel lblProd;
private JButton butAdd;
private JButton butRemove;
private JButton butEdit;
private JScrollPane scroll;
private JTable table;
public ProductPanel2() {
initComponents();
}
private void initComponents() {
setLayout(new MigLayout());
lblProd = new JLabel("Product List: ");
butAdd = new JButton("Add");
butRemove = new JButton("Remove");
butEdit = new JButton("Edit");
table = new JTable();
table.setFillsViewportHeight(true);
scroll = new JScrollPane(table);
add(lblProd, "wrap");
add(scroll);
add(butAdd, "split 3, flowy, top, sgx");
add(butRemove, "sgx");
add(butEdit, "sgx");
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
ProductPanel2 pane = new ProductPanel2();
frame.setContentPane(pane);
frame.setSize(350, 250);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
The cell next to the table is split into three subcells. Three buttons are
inserted into these subcells. A vertical flow mode is set with the flowy
constraint. The top constraint aligns the buttons to the top. Finally, the
sgx constraint makes the button the same size.

AWT repaint issues

I am trying to make some small additions to some old java code that does not support swing. I need to add a small dialog that contains a panel which has a checkbox and a couple text fields. When the user clicks on the checkbox I want to disable or enable the checkboxes. This part seems to work well but the text fields are not properly getting redrawn. When I click the checkbox the fields do not appear to become enabled but if I then click on the panel or the text field you see that they are enabled (the opposite is also true, when I un-check the checkbox the fields still look enabled until you try and click on them and they become ghosted and do not become selected). I use the setEnabled(boolean) to set the status of the fields. I have tried calling repaint and validate on both the fields and the panel after changing the status and this does not seem to work. I have also tried to have the fields request focus and this did not work. Anyone have any other ideas?
//The class that contains all of this is of type Window
//Declaration of the components
private Panel _inputPanel;
private TextField min , max;
//This method adds to two text fields
public void addMinMaxtextFields(String min, String max) {
TextField minField = new TextField(min);
TextField maxField = new TextField(max);
this.min = minField;
this.max = maxField;
this.min.setEnabled(false);
this.max.setEnabled(false);
_inputPanel.add(minField);
_inputPanel.add(maxField);
}
//listener for the checkbox
public void itemStateChanged(ItemEvent e) {
Component[] components = _inputPanel.getComponents();
min.setEnabled(!min.isEnabled());
min.setVisible(true);
min.validate();
min.repaint();
_inputPanel.validate();
_inputPanel.repaint();
this.pack();
this.setSize(this.getWidth(), this.getHeight());
this.validate();
this.repaint();
/* do nothing */
}
You will need to call update(Graphics g) on Panel after setEnabled(boolean) is called.
check :
http://download-llnw.oracle.com/javase/1.4.2/docs/api/java/awt/Container.html#update(java.awt.Graphics)
I tried following code (built from code you provided), Its working fine.
import java.awt.Checkbox;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class CheckUI extends Dialog implements ItemListener {
// The class that contains all of this is of type Window
// Declaration of the components
private Panel _inputPanel;
private TextField min, max;
private Checkbox cb;
public CheckUI(Frame owner, boolean modal) {
super(owner, modal);
_inputPanel = new Panel();
this.add(_inputPanel);
addMinMaxtextFields("min", "max");
}
// This method adds to two text fields
public void addMinMaxtextFields(String min, String max) {
cb = new Checkbox();
cb.addItemListener(this);
TextField minField = new TextField(min);
TextField maxField = new TextField(max);
this.min = minField;
this.max = maxField;
this.min.setEnabled(false);
this.max.setEnabled(false);
_inputPanel.add(minField);
_inputPanel.add(maxField);
_inputPanel.add(cb);
}
// listener for the checkbox
public void itemStateChanged(ItemEvent e) {
Component[] components = _inputPanel.getComponents();
min.setEnabled(!min.isEnabled());
min.setVisible(true);
min.validate();
min.repaint();
_inputPanel.validate();
_inputPanel.repaint();
this.pack();
this.setSize(this.getWidth(), this.getHeight());
this.validate();
this.repaint();
/* do nothing */
}
/**
* #param args
*/
public static void main(String[] args) {
Frame parent = new Frame();
parent.setVisible(true);
parent.setExtendedState(Frame.MAXIMIZED_BOTH);
parent.pack();
CheckUI ui = new CheckUI(parent, true);
ui.pack();
ui.setVisible(true);
}
}

Resources