I have discovered how to insert a button inside a row's TableView but I'm not sure how could I add different values for that button:
column
.setCellFactory(new Callback<TableColumn<GuiObject, Boolean>, TableCell<GuiObject, Boolean>>() {
#Override
public TableCell<GuiObject, Boolean> call(
TableColumn<GuiObject, Boolean> p) {
return new ButtonCell();
}
});
Where ButtonCell is self implemented button for TableCell<GuiObject, Boolean>, but I want to be able to dynamically insert different buttons dependent on the row I am inserting.
If you access the TableView from java like this:
#FXML
TableView myTable;
#Override
public void initialize(URL url, ResourceBundle rb) {
TableColumn<Item, String> firstColumn = new TableColumn<>("First Column");
firstColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Item, String> p) {
return new ReadOnlyObjectWrapper(p.getValue().column1);
}
});
TableColumn<Item, String> secondColumn = new TableColumn<>("Second Column");
secondColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Item, String> p) {
return new ReadOnlyObjectWrapper(p.getValue().column2);
}
});
TableColumn<Item, Button> buttonCol = new TableColumn<>("ButtonColumn");
buttonCol.setSortable(false);
buttonCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item, Button>, ObservableValue<Button>>() {
#Override
public ObservableValue<Button> call(TableColumn.CellDataFeatures<Item, Button> features) {
return new ReadOnlyObjectWrapper(features.getValue().button);
}
});
myTable.getColumns().add(buttonCol);
myTable.getColumns().add(firstColumn);
myTable.getColumns().add(secondColumn);
myTable.getItems().add(new Item("Test 1", "Test 1", new Button("Test 1"), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
//ON ACTION CODE HERE
System.out.println("TEST 1 CLICKED!");
}
}));
myTable.getItems().add(new Item("Test 2", "Test 2", new Button("Test 2"), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
//ON ACTION CODE HERE
System.out.println("TEST 2 BUTTON CLICKED");
}
}));
}
And the Item class:
public class Item {
public String column1, column2;
public Button button;
public Item(String column1, String column2, Button b) {
this.column1 = column1;
this.column2 = column2;
button = b;
}
}
Proof it works:
I fixed my final issue thanks to the code from Cobbles. I simply added a method addAddButton() which adds a button for new row and provides the functionality for the existing objects of type Item:
private void addAddButton() {
Button b = new Button(" + ");
final Item item = new Item(="<new>",
"<new>", b);
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
Button deleteButton = new Button(" - ");
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
selectedItems.remove(item); // a predefined list of selected items
myTable.getItems().clear();
for (Item current : selectedItems) {
myTable.getItems().add(current);
}
addAddButton();
}
});
item.setButton(deleteButton);
selectedItems.add(item);
myTable.getItems().clear();
for (Item current : selectedItems) {
myTable.getItems().add(current);
}
addAddButton();
}
});
tableAvailabilites.getItems().add(ga1);
}
It is not super efficient yet, but it is working!
Related
I have JFXTreeTableView which consist of 5 columnsx In that first 2 columns have Delete & Edit Buttons for each cell. After populating table
I want first columns should disable on save Button click.
If above case is not possible then delete Buttons inside first column's cells should be disabled on Save button click.
I did like this but dont know how to disable column or buttons inside cells.
Controller Class
public class FinanceActionsController implements Initializable {
#FXML
private JFXTreeTableView<InvoiceItems> tblInvoiceItemsView;
private JFXButton btnSave;
#FXML
private HBox hbBottonBtnBar;
ObservableList<InvoiceItems> invoiceItems = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
tableStructure();
btnSave.setOnAction((ActionEvent event) -> {
if (invoiceItems.isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Please add Atleast one Invoice Item");
alert.showAndWait();
} else {
onClickBtnSaveInvoice();
disableAndAddControlsOnSave();
//tblInvoiceItemsView.setDisable(true);
}
});
}
private void tableStructure() {
JFXTreeTableColumn<InvoiceItems, Boolean> delItem = new JFXTreeTableColumn<>("Delete");
JFXTreeTableColumn<InvoiceItems, String> editItem = new JFXTreeTableColumn<>("Edit");
JFXTreeTableColumn<InvoiceItems, String> billItem = new JFXTreeTableColumn<>("Billable Head");
delItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, Boolean> param) -> param.getValue().getValue().getBtnFlag());
delItem.setCellFactory(new Callback<TreeTableColumn<InvoiceItems, Boolean>, TreeTableCell<InvoiceItems, Boolean>>() {
#Override
public TreeTableCell<InvoiceItems, Boolean> call(TreeTableColumn<InvoiceItems, Boolean> param) {
final TreeTableCell<InvoiceItems, Boolean> cell = new TreeTableCell<InvoiceItems, Boolean>() {
MaterialIconView del = new MaterialIconView(MaterialIcon.DELETE_FOREVER, "1.5em");
final JFXButton btnDel = new JFXButton("", del);
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
btnDel.disableProperty().bind(txtN.disableProperty());
del.setFill(Color.RED);
btnDel.setButtonType(JFXButton.ButtonType.RAISED);
btnDel.setOnAction(event -> {
});
setGraphic(btnDel);
setText(null);
}
}
};
return cell;
}
});
billItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, String> param) -> param.getValue().getValue().getBillItemDesc());
final TreeItem<InvoiceItems> root = new RecursiveTreeItem<>(invoiceItems, RecursiveTreeObject::getChildren);
tblInvoiceItemsView.getColumns().setAll(delItem, editItem, billItem);
tblInvoiceItemsView.setRoot(root);
tblInvoiceItemsView.setShowRoot(false);
}
Class InvoiceItems -
class InvoiceItems extends RecursiveTreeObject<InvoiceItems> {
StringProperty billItemDesc;
BooleanProperty btnFlag;
public InvoiceItems(String billItemDesc) {
this.billItemDesc = new SimpleStringProperty(billItemDesc);
}
public StringProperty getBillItemDesc() {
return billItemDesc;
}
public BooleanProperty getBtnFlag() {
return btnFlag;
}
public void setBtnFlag(Boolean btnFlag) {
this.btnFlag = new SimpleBooleanProperty(btnFlag);
}
}
I have tried to pass InvoiceItems setBtnFlag as True in Observable list to work in setCellFactory's updateItem method but not working. Please help any help will be appreciable, Thank You.
I have a TableView and the columns for it I create like this:
TableColumn<Foo,String> fieldColumn=new TableColumn("field");
fieldColumn.setCellValueFactory(data->data.getValue().getFieldProperty());
tableView.add(fieldColumn);
Now I want to destroy my tableView but I want to continue to use all foos. That's why I want to unbind foos' properties from table/column. How to do it?
If you remove the TableColumns, the listeners will be removed during the next layout pass. This allows you to remove the listeners added by the TableView by clearing the columns and calling layout():
Item class for allowing to get the number of listeners to the property
public class Item {
private final Set<Object> listeners = new HashSet<>();
public Item(String value) {
this.value.set(value);
}
private final StringProperty value = new SimpleStringProperty() {
#Override
public void removeListener(ChangeListener<? super String> listener) {
super.removeListener(listener);
listeners.remove(listener);
}
#Override
public void addListener(ChangeListener<? super String> listener) {
super.addListener(listener);
listeners.add(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
super.removeListener(listener);
listeners.remove(listener);
}
#Override
public void addListener(InvalidationListener listener) {
super.addListener(listener);
listeners.add(listener);
}
};
public final StringProperty valueProperty() {
return this.value;
}
public int getListenerCount() {
return listeners.size();
}
}
Text Application
private void printListenerCount(String message) {
System.out.println(message + tableView.getItems().stream().mapToInt(Item::getListenerCount).sum());
}
private TableView<Item> tableView;
#Override
public void start(Stage primaryStage) {
tableView = new TableView<>();
tableView.getItems().addAll(new Item("a"), new Item("b"), new Item("c"));
TableColumn<Item, String> column = new TableColumn<>();
column.setCellValueFactory(cd -> cd.getValue().valueProperty());
tableView.getColumns().add(column);
Button btn = new Button("print listener count");
btn.setOnAction((ActionEvent event) -> {
printListenerCount("listeners: ");
});
Button btn2 = new Button("clear columns");
btn2.setOnAction(evt -> {
tableView.getColumns().clear();
// do layout to remove the listeners added for the columns
tableView.layout();
printListenerCount("after clear columns: ");
});
Scene scene = new Scene(new VBox(tableView, btn, btn2));
primaryStage.setScene(scene);
primaryStage.show();
}
Pressing the print listener count and then the clear columns buttons will result in the following output:
listeners: 3
after clear columns: 0
Is there a default way to allow the users deselect a selected row?
If not, I want to make the row to be deselected on another click. How can I achieve it?
In the bellow I attached the code piece for my tree grid tg
// ---------------------- set up columns ------------------------ \\
// set up name column
ColumnConfig<ProductMapping, String> cc1 = new ColumnConfig<ProductMapping, String>(new ValueProvider<ProductMapping, String>(){
#Override
public String getValue(ProductMapping object) {
return object.getName();
}
#Override
public void setValue(ProductMapping object, String value) {
object.setName(value);
}
#Override
public String getPath() {
return "name";
}
});
cc1.setWidth(200);
cc1.setHeader("Name");
// setup solution column
ColumnConfig<ProductMapping, String> cc2 = new ColumnConfig<ProductMapping, String>(new ValueProvider<ProductMapping, String>() {
#Override
public String getValue(ProductMapping object) {
return object.getSolution();
}
#Override
public void setValue(ProductMapping object, String value) {
object.setSolution(value);
}
#Override
public String getPath() {
return "solution";
}
});
cc2.setHeader("Solution");
cc2.setWidth(200);
// setup condition column
ColumnConfig<ProductMapping, String> cc3 = new ColumnConfig<ProductMapping, String>(new ValueProvider<ProductMapping, String>() {
#Override
public String getValue(ProductMapping object) {
return object.getCondition();
}
#Override
public void setValue(ProductMapping object, String value) {
object.setCondition(value);
}
#Override
public String getPath() {
return "condition";
}
});
cc3.setHeader("Condition");
cc3.setWidth(200);
// create column model
List<ColumnConfig<ProductMapping,?>> ccl = new LinkedList<ColumnConfig<ProductMapping,?>>();
ccl.add( cc1);
ccl.add( cc2);
ccl.add( cc3);
ColumnModel<ProductMapping> cm = new ColumnModel<ProductMapping>(ccl);
// Create the tree grid using the store, column model and column config for the tree column
tg = new TreeGrid<ProductMapping>(treeStore, cm, ccl.get(0));
// tg.getSelectionModel().get
tg.addRowClickHandler(new RowClickEvent.RowClickHandler(){
#Override
public void onRowClick(RowClickEvent event) {
tgRowClicked(event);
}
});
I would like to modify the right click context menu for some some SWT Text boxes.
I would like to still have some of the default options like Copy, Cut, Paste, but would also like to have a custom action 'Generate Random' to fill the text box with a UUID.
How can I add such a menu to the control?
here's what I came up with to add some of the standard functions (cut, copy, paste, select all) as well as a custom action (generate UUID)
public static void addContextMenuWithUUID(final Text control)
{
Menu menu = new Menu(control);
MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText("Cut");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.cut();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Copy");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.copy();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Paste");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.paste();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Select All");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.selectAll();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Generate UUID");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.setText(UUID.randomUUID().toString());
}
});
control.setMenu(menu);
}
When I had to do something similar a while ago, this is what I did,
I adopted the TextActionHandler class provided by eclipse and modified the code to suit my needs.
public final class TextActionHandler {
enum TextAction {
CUT (WorkbenchMessages.Workbench_cut, IWorkbenchCommandConstants.EDIT_CUT),
COPY (WorkbenchMessages.Workbench_copy, IWorkbenchCommandConstants.EDIT_COPY),
PASTE (WorkbenchMessages.Workbench_paste, IWorkbenchCommandConstants.EDIT_PASTE),
DELETE (WorkbenchMessages.Workbench_delete, null),
SELECT_ALL(WorkbenchMessages.Workbench_selectAll, WorkbenchCommandConstants.EDIT_SELECT_ALL);
private String text;
private String commandId;
private TextAction(String text, String commandId ) {
this.text = text;
this.commandId = commandId;
}
public String getCommandId() {
return commandId;
}
public String getText() {
return text;
}
}
public TextActionHandler(Text text) {
addText(text);
}
public TextActionHandler() {
super();
}
public void addText(Text textControl) {
if (textControl == null) {
return;
}
textControl.addDisposeListener(new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent e) {
removeText(activeTextControl);
}
});
textControl.addListener(SWT.Activate, textControlListener);
textControl.addListener(SWT.Deactivate, textControlListener);
textControl.addKeyListener(keyAdapter);
textControl.addMouseListener(mouseAdapter);
activeTextControl = textControl;
updateActionsEnableState();
}
public void hookContextMenu() {
final MenuManager menuMgr = new MenuManager("#PMPopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
addContextMenuOptions(menuMgr);
}
});
Menu menu = menuMgr.createContextMenu(activeTextControl);
activeTextControl.setMenu(menu);
}
private void addContextMenuOptions(MenuManager manager) {
manager.removeAll();
manager.add(textCutAction);
manager.add(textCopyAction);
manager.add(textPasteAction);
manager.add(textDeleteAction);
manager.add(new Separator());
manager.add(textSelectAllAction);
// add your own action handlers here
}
...
// example.
private final class CutActionHandler extends Action {
private CutActionHandler() {
setProperties(this, TextAction.CUT);
setEnabled(false);
}
#Override
public void runWithEvent(Event event) {
if (activeTextControl != null && !activeTextControl.isDisposed()) {
activeTextControl.cut();
updateActionsEnableState();
}
}
#Override
public boolean isEnabled() {
return activeTextControl != null && !activeTextControl.isDisposed()
&& activeTextControl.getEditable()
&& activeTextControl.getSelectionCount() > 0;
}
public void updateEnabledState() {
setEnabled(isEnabled());
}
}
private void setProperties(Action action, TextAction actionEnum){
action.setText(actionEnum.getText());
action.setActionDefinitionId(actionEnum.getCommandId());
action.setImageDescriptor(getImageDescriptor(actionEnum));
action.setDisabledImageDescriptor(getDisabledImageDescriptor(actionEnum));
}
}
Likewise, you can have your own ActionHandlers added. e.g, RandomGeneratorHandler.
To hook this to your textboxes, do
Text text = new Text(parent, SWT.NONE);
...
TextActionHandler handler = new TextActionHandler();
handler.addText(text);
handler.hookContextMenu();
Note - I have not provided the complete class here, for other actions like copy, paste, delete and select all etc, you will have to do something similar as Cut. I have used the same code defined in the TextActionHandler class.
I am trying to detect a doubleclick on a random cell of a tableview.
The detection of the doubleclick is not a problem but rather which cell has been doubleclicked.
table.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Cell text: " + c.getText());
}
}
});
This is how I'm building my table:
private void BuildTable() throws Exception
{
/*Some initialisations etc*/
for(int i=0; i<result.getMetaData().getColumnCount();i++)
{
final int j = i;
TableColumn col = new TableColumn(result.getMetaData().getColumnName(i+1));
col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList,String>,ObservableValue<String>>(){
public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param)
{
return new SimpleStringProperty(param.getValue().get(j).toString());
}
});
table.getColumns().addAll(col);
}
while(result.next()){
ObservableList<String> row = FXCollections.observableArrayList();
for(int i = 1; i<=result.getMetaData().getColumnCount();i++){
row.add(result.getString(i));
}
data.add(row);
}
table.setItems(data);
}catch(Exception e){
e.printStackTrace();
}
}
The real problem here is that I can't just typecast into a TableCell.
Can someone help me out? I would be very grateful.
Instead of registering a handler with the table, you need to register the handler with the table cells themselves. To do this, use a cell factory on the appropriate TableColumn(s).
As an example, add the following code to the standard table example (listing 13.6).
firstNameCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> col) {
final TableCell<Person, String> cell = new TableCell<Person, String>() {
#Override
public void updateItem(String firstName, boolean empty) {
super.updateItem(firstName, empty);
if (empty) {
setText(null);
} else {
setText(firstName);
}
}
};
cell.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double click on "+cell.getItem());
}
}
});
return cell ;
}
});
You need set the tableview's selection mode in cell, defined a custom type cell where you catch the keypress event or double clic, for an example you can review this page
https://gist.github.com/james-d/be5bbd6255a4640a5357