Removing JavaFX TreeCell formating added with PseudoClass - css

I am using a PseudoClass to change the formating of a JavaFx TreeCell depending on a condition given by the data in the tree cell. The following code works:
public class EmfTreeCellImpl extends TreeCell<EmfTreeNode> {
PseudoClass AMBIGUOUS_FEATURE = PseudoClass.getPseudoClass("ambiguous-feature");
public EmfTreeCellImpl() {
}
#Override
protected void updateItem(EmfTreeNode item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
setText(item.getLabel());
setEditable(false);
if (item instanceof EmfTreeNode.SingleAttributeNode) {
EmfTreeNode.SingleAttributeNode san = (EmfTreeNode.SingleAttributeNode) item;
//pseudoClassStateChanged(AMBIGUOUS_FEATURE, san.isAmbiguous());
pseudoClassStateChanged(AMBIGUOUS_FEATURE, san.isAmbiguous());
}
}
}
The cells get highlighted correctly. The issue is that the highlight is "persisted" in the specific tree cell in the GUI. That is, if I collapse or expand other tree branches the highlight stays "in place" highlighting other cells that do not meet the condition. I thought the setGraphic(null) would remove the formatting.
To solve this I added an extra line to the updateItem to clear the formatting:
if (empty || item == null) {
setText(null);
setGraphic(null);
pseudoClassStateChanged(AMBIGUOUS_FEATURE, false);
return;
}
But somehow it feels I should only be either removing the format from the ones that have been formatted or using a more "global" clear.

As stated in the comments, the solution proposed in the answer itself is perfectly valid, however a more concise way of handling it may be making use of the built-in filled pseudo-class of Cell which is applied automatically to any non-empty cell:
:filled:ambiguous-feature {
/* css-styling of non-empty ambiguous cells */
}

Related

TableView modify style per cell only [duplicate]

This question already has an answer here:
How to get row value inside updateItem() of CellFactory
(1 answer)
Closed 4 years ago.
Assume a:
TableView<ResultType, String> table = new TableView<> ();
table.setItems(myItems);
In TableColumn we have the setCellValueFactory method which very nicely gives you access to the ResultType object of the respective cell value. So you can use it to extract the values like that:
aColumnFromTableView.setCellValueFactory(data -> new SimpleStringProperty(data.getValue));
Now each cell from aColumnFromTableView will be populated with a value from all ResultType objects which are set as items for the table.
The question is: can we also change the cell's style in a similar way? I had a look at the setCellFactory method, but it does not seem as friendly as setCellValueFactory though (= it does not provide me the respective ResultType).
Here's what you can do with setCellFactory:
aColumnFromTableView.setCellValueFactory(data -> ???? ); // data is actually aColumnFromTableView itself??
So I am wondering of a way to set the cell style individually similar to what I described with "setCellValueFactory". I hope it exists.
Note: I also tried
aColumnFromTableView.setCellValueFactory(data -> {
aColumnFromTableView.setStyle("my style");
return new SimpleStringProperty(data.getValue);
})
But that sets it for the entire column and not individually.
Thanks!!!!
If you want to customize the style of the TableCell based on the value of the cell you'll need to use a cellFactory and return your own TableCell.
For instance, if you wanted a TableCell<?, Double> that displayed the number in red if it was negative you could do:
column.setCellFactory(col -> new TableCell<>() {
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
if (item < 0.0) {
setTextFill(Color.RED); // or use setStyle(String)
} else {
setTextFill(Color.BLACK); // or use setStyle(String)
}
}
}
});
When creating a custom TableCell you'll more than likely want to override the updateItem(Object,boolean) method. It's important you override it correctly, however, if you want it to work right. Read the javadoc for information:
The updateItem method should not be called by developers, but it is the best method for developers to override to allow for them to customise the visuals of the cell. To clarify, developers should never call this method in their code (they should leave it up to the UI control, such as the ListView control) to call this method. However, the purpose of having the updateItem method is so that developers, when specifying custom cell factories (again, like the ListView cell factory), the updateItem method can be overridden to allow for complete customisation of the cell.
It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
Note in this code sample two important points:
We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues.
We test for the empty condition, and if true, we set the text and graphic properties to null. If we do not do this, it is almost guaranteed that end users will see graphical artifacts in cells unexpectedly.
Instead of setting properties or calling setStyle you could use things like PseudoClass states to make it easier to style from an external CSS stylesheet.
import javafx.css.PseudoClass;
import javafx.scene.control.TableCell;
public class CustomCell<S> extends TableCell<S, Double> {
private static final PseudoClass POSITIVE = PseudoClass.getPseudoClass("positive");
private static final PseudoClass NEGATIVE = PseudoClass.getPseudoClass("negative");
public CustomCell() {
getStyleClass().add("custom-cell");
}
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
pseudoClassStateChanged(POSITIVE, false);
pseudoClassStateChanged(NEGATIVE, false);
} else {
setText(item.toString()); // you might want to format the number for display
pseudoClassStateChanged(POSITIVE, item >= 0.0);
pseudoClassStateChanged(NEGATIVE, item < 0.0);
}
}
}
Then use:
column.setCellFactory(col -> new CustomCell<>());
And in a stylesheet:
.custom-cell:positive {
-fx-text-fill: black;
}
.custom-cell:negative {
-fx-text-fill: red;
}

JavaFX : color individual TreeItems using css

I want to be able to color individual Tree Item of treeView based on some condition.
This answer seems good but I am unable to implement it.
https://stackoverflow.com/a/10931896/6653207
I am unable to understand how to use setCellFactory method to format individual TreeItems.
I have a class
public class Bag {
public String caption,assignment="";
Boolean eval;
public Set<Vertex> Nodes = new HashSet<Vertex>();
public Vector<Bag> ChildBags = new Vector<Bag>();
#Override
public String toString()
{
return assignment+ " " +caption;
}
}
Here's my css file:
.true{
-fx-text-fill:#33cc00 ;
}
.assignment{
-fx-text-fill: #0033cc
}
So I want to color to green the caption ( the toString() method returns ) of all those nodes whose eval property is true.
and assignment string which toString() method returns for all the nodes should be blue.
How can I do so?
Thanks.
By overriding the updateItem method of the TreeCell, you can adjust the TreeCell's properties based on the value of the TreeItem the cell contains.
In the following example a pseudoclass is assigned to all cells that contain a value with the prefix "child" and all empty cells get a black background.
TreeView<String> treeView = ...
PseudoClass childPseudoClass = PseudoClass.getPseudoClass("child");
treeView.setCellFactory(tv -> new TreeCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
// update for empty cell / cell containing null
pseudoClassStateChanged(childPseudoClass, false);
setText("");
setStyle("-fx-background-color: black;");
} else {
// update for filled cell
pseudoClassStateChanged(childPseudoClass, item.startsWith("child"));
setText(item);
setStyle(null);
}
}
});
CSS Stylesheet
.tree-cell:child {
-fx-background-color: red;
}
The updateItem method is called by the TreeView every time the value changes, e.g. if a new TreeItem is associated with the cell or the value property of a TreeItem is modified.
You could also use the factory to add listeners to the TreeCell, before it's returned, in case you prefer this and e.g. want to change the cell based on the treeItem property.
EDIT: To apply different colors to the text, you need to use different Nodes for the text parts.
treeView.setCellFactory(tv -> new TreeCell<Bag>() {
private final Text assignment;
private final Text caption;
private final Node graphic;
{
assignment = new Text();
caption = new Text();
assignment.getStyleClass().add("assignment");
graphic = new HBox(4, assignment, caption);
setGraphic(graphic);
}
#Override
protected void updateItem(Bag item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(graphic);
assignment.setText(item.assignment);
caption.setText(item.caption);
caption.getStyleClass().remove("true");
if (item.eval) {
caption.getStyleClass().add("true");
}
}
}
});
To color the text you need to use the -fx-fill property instead of the -fx-text-fill property.

JavaFX - bug in cell render after hiding columns

First of all sorry for my English:)
I have a strange bug... I'd like to hide some columns in JavaFX TableView after pressing the button. The code is as simple as possible:
column8.visibleProperty().set(false);
column9.visibleProperty().set(false);
(I also tested with remove with the same result).
The problem is that in "neighboring cell" I have "separator" from somewhere after this action.
I use Cellfactories is my code. For one of the hidden Cell it is
column8.setCellFactory(column -> {
return new TableCell<Anfrage, Mandant>() {
#Override
protected void updateItem(Mandant item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getNameMandant());
}
}
};
});
And for cell, where I have "separator" after hiding:
//Store
column10.setCellFactory(column -> {
return new TableCell<Anfrage, Terminals>() {
#Override
protected void updateItem(Terminals item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item.getStore());
}
}
};
});
Some pics to explain what is actually wrong.
If i set visible to "true" again everythings works as expected - all columns are in there without any "separators" anywhere.
Thanks a lot for any advice!
So, finally tableview refreshing is resolved in JavaFX 8u60. Now "native" table.refresh() do the trick.

How to add a custom css style for specific item in a combo/choice box in JavaFx?

I am using javaFx, and I want to mimic this control. Specificly the link in the bottom of the menu !!
is it possible to do it with css only or without creating a custom control?
There is no need to implement a custom control, but you do need a cell factory to do this. The basic idea is to add a style class to the cell if it requires it, and remove it if not. So you can do, for example:
final String cellStyleClass = "my-combo-box-cell" ;
ComboBox<String> combo = new ComboBox<>();
combo.setCellFactory(listView -> new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty) ;
getStyleClass().remove(cellStyleClass);
if (empty) {
setText(null);
} else {
setText(item);
if (/* needs style class */) {
getStyleClass().add(cellStyleClass);
}
}
}
});
The test to see if you need to add the style class can obviously reference the item, etc.
Now in your CSS file you can style the cell as you need it:
.my-combo-box-cell {
/* specific styles here... */
}

Colouring table row in JavaFX

This question is related to this. Now I want to colour the row where field value equals to some value.
#FXML
private TableView<FaDeal> tv_mm_view;
#FXML
private TableColumn<FaDeal, String> tc_inst;
tc_inst.setCellValueFactory(cellData -> new SimpleStringProperty(""+cellData.getValue().getInstrumentId()));
tc_inst.setCellFactory(column -> new TableCell<FaDeal, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else {
setText(item);
// Style row where balance < 0 with a different color.
TableRow currentRow = getTableRow();
if (item.equals("1070")) {
currentRow.setStyle("-fx-background-color: tomato;");
} else currentRow.setStyle("");
}
}
});
The problem is I don't want to show tc_inst in my table. For this reason I set visible checkbox in SceneBuilder to false. In this case colouring part doesn't work at all. How can hide tc_inst so that colouring works?
Use a row factory, instead of a cell factory, if you want to change the color of the whole row:
tv_mm_view.setRowFactory(tv -> new TableRow<FaDeal>() {
#Override
public void updateItem(FaDeal item, boolean empty) {
super.updateItem(item, empty) ;
if (item == null) {
setStyle("");
} else if (item.getInstrumentId().equals("1070")) {
setStyle("-fx-background-color: tomato;");
} else {
setStyle("");
}
}
});
Note that if the value of instrumentId changes while the row is displayed, then the color will not change automatically with the above code, unless you do some additional work. The simplest way to make that happen would be to construct your items list with an extractor which returned the instrumentIdProperty() (assuming you are using the JavaFX property pattern in FaDeal).

Resources