Unable to show the Tool Tip on the JavaFX TableView cell value - java

In my JavaFX TableView I have one TableColumn on which I have set Cell Factory to render ProgressBar and for other TableColumns I have set Cell Factory to show ToolTip. Like the image below. Second Column is showing Progress Bar and other 3 Columns are render to show Tool tip, that has simple string values to show.
I was getting issue in which the TableView was not displaying/showing updated values in the table i.e UI is not validating/refreshing/painting the TableView elements. If I clicked on ColumnHeader to sort any column then only I can see the TableView updating. Manually sort the table column to refresh the table content is not making sense so I have searched and found solution to show/hide the Table Columns for updating the Table View.
To resolved the issue I have written a code below to solve the TableView Updating/Refreshing issue but due to this code now ToolTip are not getting visible.
Code to Update Table View after each specific interval
class TableProgressBarUpdator implements Runnable {
TableView table;
public TableProgressBarUpdator(TableView fxtable) {
table = fxtable;
}
public void start() {
new Thread(this).start();
}
public void run() {
while (keepUpdating) {
try {
updateProgressbar();
Thread.sleep(1000);
} catch (Exception e) {
LogHandler.doErrorLogging("Error while updating tables cell", e);
}
}
LogHandler.doDebugLogging("Table process repainting is completed.");
}
private void updateProgressbar() throws Exception {
Platform.runLater(new Runnable() {
#Override
public void run() {
((TableColumn) table.getColumns().get(0)).setVisible(false);
((TableColumn) table.getColumns().get(0)).setVisible(true);
}
});
}
}
Start Updating Table View
public void startUpdatingTableProgress() {
keepUpdating = true;
TableProgressBarUpdator tpu = new TableProgressBarUpdator(table);
tpu.start();
}
Stop Updating Table View
public void stopUpdatingTableProgress() {
keepUpdating = false;
}
Adding more code that is showing render classes to show Progress bar and display Tool Tip.
Code to show the Progress Bar Table View.
public static class ProgressBarTableCell<S, T> extends TableCell<S, T> {
private final ProgressBar progressBar;
private ObservableValue<T> ov;
public ProgressBarTableCell() {
this.progressBar = new ProgressBar();
progressBar.setPrefHeight(23);
setAlignment(Pos.CENTER);
}
#Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setGraphic(null);
setText(null);
} else {
if (item.toString().equalsIgnoreCase("Processing")) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (getGraphic() == null) {
setGraphic(progressBar);
progressBar.setProgress(-1);
} else {
ProgressBar objpProgressBar = (ProgressBar) getGraphic();
objpProgressBar.setProgress(-1);
}
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
});
} else {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (getGraphic() == null) {
setGraphic(progressBar);
progressBar.setProgress(0);
} else {
ProgressBar objpProgressBar = (ProgressBar) getGraphic();
objpProgressBar.setProgress(0);
}
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
});
}
}
}
}
Code to Show the Tool Tip
public class ToolTip extends TableCell {
#Override
protected void updateItem(Object object, boolean selected) {
if (object == null) {
setGraphic(null);
setText(null);
}else{
setText(object.toString());
setTooltip(new Tooltip(object.toString()));
}
}
}
Issue -
If I comment-out these two lines from TableProgressBarUpdator Class then I am able to see Tool Tip for each cell values in 1st, 3rd and 4th column but now Table View contents are not updating/refreshing and when I UN-comment these lines I am unable to see the Tool Tip.
((TableColumn) table.getColumns().get(0)).setVisible(false);
((TableColumn) table.getColumns().get(0)).setVisible(true);
In all due to these two lines my Tool Tip Render is not working and If I remove these two lines then Table View Content are not Refreshing/Updating.

You don't need to update TableView manually. may be there are problem in your class associated with that TableView's column.
You have to create class as given below :
public static class Test{
private StringProperty name;
private Test() {
name = new SimpleStringProperty();
}
public Test(String name) {
this.name = new SimpleStringProperty(name);
}
public void setName(String name) {
this.name.set(name);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
}

Are you sure you need the Platform.runLater() call within your ProgressBarTableCell? I would expect it to already be in the Application thread. That could cause the progress bar update to be placed at the end of the queue in the Application thread, after the scheduled table update.
Is the value for your TableCell wrapped in an ObservableProperty (looks like you should have an SimpleStringProperty)? If you did, the table should recognize that it needs a refresh, and you shouldn't have to resort to toggling the column visibility as a hack to force table refreshing.

Related

TreeTableView : setting a row not editable

I want to have control over the styling of some rows of a TreeTableView based on the level in the tree. I used setRowFactory and apply a styling if this row is part of the first level children of the root of the Table. The styling works fine, but I also want to disable clicking on the checkbox for those rows. I am able to setDisable(true) but that also disables the expanding of the TreeItem and SetEditable(false) does not seem to have any effect.
EDIT: What I understand is that the Table must be set editable, then the columns are by default editable. But if I set TreeTableRow.setEditable(true); or TreeTableRow.setEditable(false); I never see any effect. The description seems of setEditable seems exactly what I want but I am unable to use it that way.
void javafx.scene.control.Cell.setEditable(boolean arg0)
setEditable
public final void setEditable(boolean value)
Allows for certain cells to not be able to be edited. This is useful incases >where, say, a List has 'header rows' - it does not make sense forthe header rows >to be editable, so they should have editable set tofalse.
Parameters:value - A boolean representing whether the cell is editable or not.If >true, the cell is editable, and if it is false, the cell can notbe edited.
Main:
public class TreeTableViewRowStyle extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// create the treeTableView and colums
TreeTableView<Person> ttv = new TreeTableView<Person>();
TreeTableColumn<Person, String> colName = new TreeTableColumn<>("Name");
TreeTableColumn<Person, Boolean> colSelected = new TreeTableColumn<>("Selected");
colName.setPrefWidth(100);
ttv.getColumns().add(colName);
ttv.getColumns().add(colSelected);
ttv.setShowRoot(false);
ttv.setEditable(true);
// set the columns
colName.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
colSelected.setCellValueFactory(new TreeItemPropertyValueFactory<>("selected"));
ttv.setRowFactory(table-> {
return new TreeTableRow<Person>(){
#Override
public void updateItem(Person pers, boolean empty) {
super.updateItem(pers, empty);
boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
if (!isEmpty()) {
if(isTopLevel){
setStyle("-fx-background-color:lightgrey;");
setEditable(false); //THIS DOES NOT SEEM TO WORK AS I WANT
//setDisable(true); //this would disable the checkbox but also the expanding of the tree
}else{
setStyle("-fx-background-color:white;");
}
}
}
};
});
// creating treeItems to populate the treetableview
TreeItem<Person> rootTreeItem = new TreeItem<Person>();
TreeItem<Person> parent1 = new TreeItem<Person>(new Person("Parent 1"));
TreeItem<Person> parent2 = new TreeItem<Person>(new Person("Parent 1"));
parent1.getChildren().add(new TreeItem<Person>(new Person("Child 1")));
parent2.getChildren().add(new TreeItem<Person>(new Person("Child 2")));
rootTreeItem.getChildren().addAll(parent1,parent2);
ttv.setRoot(rootTreeItem);
// build and show the window
Group root = new Group();
root.getChildren().add(ttv);
stage.setScene(new Scene(root, 300, 300));
stage.show();
}
}
Model Person :
public class Person {
private StringProperty name;
private BooleanProperty selected;
public Person(String name) {
this.name = new SimpleStringProperty(name);
selected = new SimpleBooleanProperty(false);
}
public StringProperty nameProperty() {
return name;
}
public BooleanProperty selectedProperty() {
return selected;
}
public void setName(String name){
this.name.set(name);
}
public void setSelected(boolean selected){
this.selected.set(selected);
}
}
The base problem is that none of the editable (nor the pseudo-editable like CheckBoxXX) Tree/Table cells respect the editability of the row they are contained in. Which I consider a bug.
To overcome, you have to extend the (pseudo) editable cells and make them respect the row's editable. The exact implementation is different for pseudo- vs. real editing cells. Below are in-line examples, for frequent usage you would make them top-level and re-use.
CheckBoxTreeTableCell: subclass and override updateItem to re-bind its disabled property like
colSelected.setCellFactory(c -> {
TreeTableCell cell = new CheckBoxTreeTableCell() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (getGraphic() != null) {
getGraphic().disableProperty().bind(Bindings
.not(
getTreeTableView().editableProperty()
.and(getTableColumn().editableProperty())
.and(editableProperty())
.and(getTreeTableRow().editableProperty())
));
}
}
};
return cell;
});
For a real editing cell, f.i. TextFieldTreeTableCell: override startEdit and return without calling super if the row isn't editable
colName.setCellFactory(c -> {
TreeTableCell cell = new TextFieldTreeTableCell() {
#Override
public void startEdit() {
if (getTreeTableRow() != null && !getTreeTableRow().isEditable()) return;
super.startEdit();
}
};
return cell;
});
Now you can toggle the row's editability as you do, changed the logic a bit to guarantee full cleanup in all cases:
ttv.setRowFactory(table-> {
return new TreeTableRow<Person>(){
#Override
public void updateItem(Person pers, boolean empty) {
super.updateItem(pers, empty);
// tbd: check for nulls!
boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
if (!isEmpty() && isTopLevel) {
// if(isTopLevel){
setStyle("-fx-background-color:lightgrey;");
setEditable(false);
}else{
setEditable(true);
setStyle("-fx-background-color:white;");
}
}
};
});
Instead of creating a custom TreeTableCell subclass you can use the following utility method that basically installs a new cell-factory on a column that delegates to the original cell-factory but adds the row-editability binding whenever a cell is created.
public <S, T> void bindCellToRowEditability(TreeTableColumn<S, T> treeTableColumn) {
// Keep a handle on the original cell-factory.
Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> callback = treeTableColumn.getCellFactory();
// Install a new cell-factory that performs the delegation.
treeTableColumn.setCellFactory(column -> {
TreeTableCell<S, T> cell = callback.call(column);
// Add a listener so that we pick up when a new row is set for the cell.
cell.tableRowProperty().addListener((observable, oldRow, newRow) -> {
// If the new row is non-null, we proceed.
if (newRow != null) {
// We get the cell and row editable-properties.
BooleanProperty cellEditableProperty = cell.editableProperty();
BooleanProperty rowEditableProperty = newRow.editableProperty();
// Bind the cell's editable-property with its row's property.
cellEditableProperty.bind(rowEditableProperty);
}
});
return cell;
});
}
You can then set this for all columns of your TreeTableView as:
List<TreeTableColumn<S, ?>> columns = treeTableView.getColumns();
columns.forEach(this::bindCellToRowEditability);
You still need the custom TreeTableRow that checks whether it is top-level or not so that the editable value is correctly set for the row itself. However, setting the editable value on the row will now ensure that all cells in that row correctly reflects the row's editable-property.
If you want disable a specific Cell then handle the disable logic in the CellFactory rather than in RowFactory. The static method forTreeTableColumn(..) is a convinient method for quick use. But that is not the only way. You can still create your own factory for CheckBoxTreeTableCell.
So instead of
colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
set the cellfactory as below, and this should work for you.
colSelected.setCellFactory(new Callback<TreeTableColumn<Person, Boolean>, TreeTableCell<Person, Boolean>>() {
#Override
public TreeTableCell<Person, Boolean> call(TreeTableColumn<Person, Boolean> column) {
return new CheckBoxTreeTableCell<Person, Boolean>() {
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
boolean isTopLevel = column.getTreeTableView().getRoot().getChildren().contains(getTreeTableRow().getTreeItem());
setEditable(!isTopLevel);
}
};
}
});

Java TableViewer - change cell image on selection

I have followed Vogella's tutorial on Table Viewer and using the StyledCellLabelProvider presented there. It creates a CHECKED or UNCHECKED icon on the third column
col = createTableViewerColumn(titles[3], bounds[3], 3);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
return null;
}
#Override
public Image getImage(Object element) {
if (((Person) element).isMarried()) {
return CHECKED;
} else {
return UNCHECKED;
}
}
});
col.setEditingSupport(new MarriedEditingSupport(viewer));
The column images get painted accordingly when the table is created but how can I change the icon when the user clicks a cell ? (I want to simulate a check box)
You can use an EditingSupport class based on CheckboxCellEditor for this:
class MarriedEditingSupport extends EditingSupport
{
private final CheckboxCellEditor _editor;
MarriedEditingSupport(TableViewer viewer)
{
super(viewer);
_editor = new CheckboxCellEditor(viewer.getTable());
}
#Override
protected CellEditor getCellEditor(Object element)
{
return _editor;
}
#Override
protected boolean canEdit(Object element)
{
return true;
}
#Override
protected Object getValue(Object element)
{
return Boolean.valueOf(((Person)element).isMarried());
}
#Override
protected void setValue(Object element, Object value)
{
((Person)element).setMarried(((Boolean)value).booleanValue());
getViewer().update(element, null);
}
}

Can we change GWT Bootstrap 3 ButtonCell Icon Dynamically?

I have a GWT bootstrap 3 button as a ButtonCell created with IconType and ButtonType:
public abstract class ButtonColumn<T> extends Column<T, String> {
public ButtonColumn(IconType iconType, ButtonType buttonType) {
this(new ButtonCell(buttonType, iconType));
}
}
So when I create the button, I do
new ButtonColumn<Object>(IconType.PLAY, ButtonType.SUCCESS) {
#Override
public void onClick(Object obj) {
doStuff(obj);
}
};
I want to change my button IconType onClick. Is it possible to achieve it?
And can I create a custom IconType extending the GWT IconType Enum? I wanted to put an animated icon (like a loading icon).
Well, you can not change the button's icon in a row, especially when you create the whole column with an icon already specified. But you can redraw() a row and this could be a way to achieve what you want.
I use AbstractCell to render a button and onBrowserEvent:
first create an AbstractCell with ClickEvent in consumedEvents parameter
in the render() method render a button based on the clicked state
in the onBrowserEvent() method change the clicked state and re-render the row
The clicked state is best to be kept in the table's underlying data type so it is available for each row.
Here is a complete working example code:
final CellTable<TableType> table = new CellTable<TableType>();
AbstractCell<TableType> buttonCell = new AbstractCell<ButtonCellTest.TableType>(ClickEvent.getType().getName()) {
#Override
public void render(Context context, TableType value, SafeHtmlBuilder sb) {
Button button = new Button();
button.setType(ButtonType.SUCCESS);
button.setSize(ButtonSize.SMALL);
button.add(new Icon(value.isClicked() ? IconType.CHECK : IconType.TIMES));
sb.append(SafeHtmlUtils.fromTrustedString(button.toString()));
}
#Override
public void onBrowserEvent(Context context, Element parent, TableType value, NativeEvent event, ValueUpdater<TableType> valueUpdater) {
value.setClicked(!value.isClicked());
// ... do stuff...
table.redrawRow(context.getIndex());
}
};
table.addColumn(new Column<TableType, TableType>(buttonCell) {
#Override
public TableType getValue(TableType object) {
return object;
}
});
ArrayList<TableType> rowData = new ArrayList<TableType>();
rowData.add(new TableType("row 1"));
rowData.add(new TableType("row 2"));
...
table.setRowData(rowData);
And example table's data type keeping the clicked state:
public class TableType {
String text;
boolean clicked = false;
public TableType(String text) {
this.text = text;
}
public String getText() {
return text;
}
public boolean isClicked() {
return clicked;
}
public void setClicked(boolean clicked) {
this.clicked = clicked;
}
}
As for extending the IconType enum - no, you can not extend an enum in Java. See this question for example: Can enums be subclassed to add new elements?.
You could try to add your own CSS class but this should be asked as another question to get precise answers.

JavaFX EventHandler - new Alert if boolean equals true

When the user has reached the end of this Tetris game I want a new alert to be opened.
In the model class I have a boolean that will switch to true if a new Tetris block cannot be spawned.
I'm working with model view presenter, so in the model is the boolean + getter and in the presenter a new alert will be created if the boolean returns true.
The question is how do I add this to the eventHandlers() in the presenter?
public Presenter(Model model, View view) {
this.model = model;
this.view = view;
addEventHandlers();
}
private void addEventHandlers() {
//view.setOnKeyPressed... this is for rotating the blocks to give you an example
}
JavaFX implements observable properties, which are extensions of the Java Bean pattern that support notification for invalidation and for changes to the underlying value. These are fundamental to the JavaFX library: all controls in JavaFX make use of these. So, for example, if you want to respond to changes to the text in a text field, you would do
myTextField.textProperty().addListener((observable, oldText, newText) -> {
// ... do something with newText (and perhaps oldText) here...
});
So you can just achieve this with a BooleanProperty (or similar) in your model class:
private final BooleanProperty gameEnded = new SimpleBooleanProperty();
public BooleanProperty gameEndedProperty() {
return gameEnded ;
}
public final boolean isGameEnded() {
return gameEndedProperty().get();
}
public final void setGameEnded(boolean gameEnded) {
gameEndedProperty().set(gameEnded);
}
Then you can do:
model.gameEndedProperty().addListener((obs, gameWasEnded, gameIsNowEnded) -> {
if (gameIsNowEnded) {
// show alert, etc...
}
});
See "Properties and Bindings" in the Oracle tutorial for more details, including bindings, etc. You might also consider a ReadOnlyBooleanWrapper if you don't want the property to be changed from outside the class it is defined in.
public void setEind() {
boolean oldValue = this.eind;
eind = true;
System.out.println(eind);
firePropertyChange("eind",oldValue,eind);
}
private final List<PropertyChangeListener> listeners = new ArrayList<>();
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.add(listener);
}
public void firePropertyChange(String property, Object oldValue, Object newValue) {
for (PropertyChangeListener listener : listeners) {
listener.propertyChange(new PropertyChangeEvent(this,property,oldValue,newValue));
}
}
spel.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
val.stop();
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Game over!");
alert.setContentText("Enter your name in the next window for the highscores.");
alert.setTitle("End");
alert.show();
}
});

Strange blinking of cell on selection with custom Cell Editor

I am new to working with JTables and having trouble with getting my custom JTable editor to work properly.
I have a number of custom panels with lists and buttons. To renderder them in a cell I am using a custom PanelCellRenderer that has various constructors for each type of the panel.
To make the buttons clickable I have created this simple PanelCellEditor that extends DefaultCellEditor. To access the data stored within cells at the time of editting I pass the reference to the PanelCellRenderer.
The problem I am having is that when I select the cell (by clicking at it), from displaying the list with the button, the cell selected becomes completely blank. When the cell gets deselected the list with data and the button reappear again. Any advice on this will be helpful. Thanks.
public class PanelCellEditor extends DefaultCellEditor {
private PanelCellRenderer pcr;
private Object value;
public PanelCellEditor(final PanelCellRenderer pcr) {
super(new JCheckBox());
this.pcr = pcr;
this.pcr.setOpaque(true);
if (pcr.firstPanel != null) {
pcr.firstPanel.Button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//do something
fireEditingStopped();
}
});
pcr.firstPanel.List.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
String value = (String) ((javax.swing.JList) e.getSource()).getSelectedValue();
//do something
fireEditingStopped();
}
});
}
else if (pcr.secondPanel != null) {
pcr.secondPanel.Button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//do something
fireEditingStopped();
}
});
pcr.secondPanel.List.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
String value = (String) ((javax.swing.JList) e.getSource()).getSelectedValue();
//do something
fireEditingStopped();
}
});
}
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
//// if I comment this whole bit ////
if (isSelected) {
pcr.setForeground(table.getSelectionForeground());
pcr.setBackground(table.getSelectionBackground());
} else {
pcr.setForeground(table.getForeground());
pcr.setBackground(table.getBackground());
}
if (pcr.firstPanel != null)
pcr.firstPanel.list.setListData((String[])value);
else if (pcr.secondPanel != null) {
pcr.secondPanel.list.setListData((String[])value);
}
//////// nothing changes /////////
this.value = value;
return pcr;
}
public Object getCellEditorValue() {
return value;
}
public boolean stopCellEditing() {
return super.stopCellEditing();
}
protected void fireEditingStopped() {
super.fireEditingStopped();
}
}
you could trace the JTable.getTableCellEditor into your objects.
Have you actually registered your editor with the value it should edit with the Jtable?

Categories