JavaFX SetText CSS options - css

I'm using the Jfoenix libary, and I have gotten it to show both the day of the year, and day of the month (after asking here for the first hint). They are both inside the setText line. I'm trying to figure out if I can add CSS to them individually so I can make the Day of the Year appear smaller, in the right corner and maybe a different color. I've googled quite a bit but not getting the answers I'm looking for. Thank you.
endDate.setDayCellFactory(p -> new DateCell() {
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setText("");
} else {
setText(Integer.toString(item.getDayOfYear()) + "\r\n" + Integer.toString(item.getDayOfMonth()));
}
}
});

You cannot style the text in the same Labeled differently, but you can use the graphic property instead to display the date:
DatePicker datePicker = new DatePicker();
datePicker.setDayCellFactory(p -> new DateCell() {
private final Text doy = new Text();
private final Text dom = new Text();
private final VBox graphic = new VBox(doy, dom);
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
doy.setFont(Font.font(8));
dom.setFont(Font.font(15));
VBox.setMargin(dom, new Insets(0, 0, 0, 10));
}
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setGraphic(null);
} else {
setGraphic(graphic);
doy.setText(Integer.toString(item.getDayOfYear()));
dom.setText(Integer.toString(item.getDayOfMonth()));
}
}
});
BTW: For java internal string handling usually \r is not required. This is only necessary, if you write the string to a file or use a class that depends on the line separator of the OS; only \n works just fine for the purpose you're using it for here. If you need to use different line separation on different OS, hardcoding \r\n is not a good idea; use System.lineSeparator() instead.

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 JFXDatePicker set color for specific dates

I am able to change the color of a JFXDatePicker from the JFoenix library for certain dates via the code below.
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
#Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if(!empty) {
if(listRegisteredTOTDays.contains(item)) {
setStyle("-fx-background-color: #99e699;");
}
}
}
};
}
};
The problem occurs when I have hovered over the item with a changed background. It changes the background colour to the default one after hovering and not the set one via de code above.
I do not want to disable the cell as the user still must be able to click on it!
The exercise is to inform the user which dates already has data. So nothing can be disabled.
How to overcome this? I just want after hovering it gets back the color set as above.
Before hovering:
and after hovering
While JFXDatePicker looks beautiful, it takes some liberties that make DateCells hard to customize. Instead of using CSS to style the cells it sets the background from code which has a higher precedence than any CSS, even inline CSS styles.
You can see this in the createDayCells() method of com.jfoenix.skins.JFXDatePickerContent.
This means without modifying the skins your only chance of fixing this is to add your own event handler and use Platform.runLater to ensure it runs after the event handler added in createDayCells():
final Background markedBackground = new Background(new BackgroundFill(Color.rgb(0x99, 0xE6, 0x99),
CornerRadii.EMPTY,
Insets.EMPTY));
picker.setDayCellFactory(dp -> new DateCell() {
{
addEventHandler(MouseEvent.MOUSE_EXITED, evt -> {
if (listRegisteredTOTDays.contains(getItem())) {
// override background property of marked cells after
// JFXDatePicker modifies it
Platform.runLater(() -> {
setBackground(markedBackground);
});
}
});
}
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (!empty && listRegisteredTOTDays.contains(item)) {
setBackground(markedBackground);
}
}
});

Javafx listview dynamically changing the color of listview cells

Hello I'm currently building project that uses javaFx and was wondering if it's possible to dynamically change the color of a listview objects cells at run-time. Each cell of the listview represents an object which has a color associated with it. The issue I'm having is that each object has a color that's generated at run time and the number of objects won't necessarily remain constant.
The underlying GUI that the javaFX code base is working with is fxml. I've tried looking into using CSS but since the amount of objects and color associated with those objects is generated at run-time I can't hard code it into a style-sheet. I've also been looking to using cell factories to create the desired cell for each cell, but I need to color to be passed into the cell factory and it has to be in the RGB format. Does anyone have any experience with a problem such as this one?
for(int i =0; i< mice.mice.size();i++){
selectedMiceListView.setCellFactory(new Callback<ListView<Object>,
ListCell<Object>>() {
// #Override
public ListCell<Object> call(final ListView<Object> param) {
final ListCell<Object> cell = new ListCell<Object>() {
#Override
protected void updateItem(final Object item, final boolean empty) {
super.updateItem(item, empty);
setStyle("-fx-background-color: rgb(" + mice.mice.get(i).red + "," + mice.mice.get(i).green + ", " + mice.mice.get(i).blue + ");");
}
};
return cell;
}
});
}
Below is what I've tried so far, it says that inner classes must effectively have final variables. Is there a way to modify it so that it accepts the rgb value i'm trying to set it too?
Try this, the below code paints every cell in the listview with black
this.listView
.setCellFactory(new Callback<ListView<Object>, ListCell<Object>>() {
#Override
public ListCell<Object> call(final ListView<Object> param) {
final ListCell<Object> cell = new ListCell<Object>() {
#Override
protected void updateItem(final Object item, final boolean empty) {
super.updateItem(item, empty);
setBackground(
new Background(new BackgroundFill(Color.rgb(100, 150, 200), CornerRadii.EMPTY, Insets.EMPTY)));
}
};
return cell;
}
});

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.

TextField inside TableView gone after scroll JavaFX

I have a table view and inside it, there is one column filled with TextField.
There is no problem when I have few data and my table do not have scroll bar, all TextFields appears.
The problem is, when I scroll down my table and then goes up again, some TextFields are missing.
Here is my code for the column filled with TextField:
purchaseQtyCol.setCellFactory(
new Callback<TableColumn< TransactionModel, TextField>, TableCell< TransactionModel, TextField>>() {
#Override
public TableCell< TransactionModel, TextField> call(final TableColumn< TransactionModel, TextField> p) {
TableCell<TransactionModel, TextField> cell = new TableCell<TransactionModel, TextField>() {
#Override
public void updateItem(TextField item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
/**
* for(CheckBox cbb : canceledCB) {
* if(item.equals(cbb))
* System.out.println("aa" +
* this.indexProperty().getValue() + " " +
* item.isSelected() ); }*
*/
this.setGraphic(item);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
purchaseQtyCol.setCellValueFactory(new Callback<CellDataFeatures<TransactionModel, TextField>, ObservableValue<TextField>>() {
#Override
public ObservableValue<TextField> call(final CellDataFeatures<TransactionModel, TextField> p) {
System.out.println("new textfield");
final TextField qtyField = new TextField() {
#Override
public void replaceText(int start, int end, String text) {
if (text.matches("[0-9]") || text.equals("")) {
super.replaceText(start, end, text);
if (this.getText().isEmpty()) {
p.getValue().setPurchaseQty(0);
p.getValue().setTotalPrice(0);
} else {
p.getValue().setPurchaseQty(Integer.parseInt(this.getText()));
p.getValue().setTotalPrice(p.getValue().purchaseQtyProperty().intValue() * p.getValue().basePriceProperty().intValue());
}
recountTotals();
}
}
#Override
public void replaceSelection(String text) {
if (text.matches("[0-9]") || text.equals("")) {
super.replaceSelection(text);
if (this.getText().isEmpty()) {
p.getValue().setPurchaseQty(0);
p.getValue().setTotalPrice(0);
} else {
p.getValue().setPurchaseQty(Integer.parseInt(this.getText()));
p.getValue().setTotalPrice(p.getValue().purchaseQtyProperty().intValue() * p.getValue().basePriceProperty().intValue());
}
recountTotals();
}
}
};
qtyField.setText("" + p.getValue().purchaseQtyProperty().getValue());
qtyField.setAlignment(Pos.CENTER_RIGHT);
return new SimpleObjectProperty(qtyField);
}
});
I really appreciate helps from you guys.
Regards,
Chrisma Andhika
Chrisma! The problem you face is a famous issue for JavaFX about updating items in tableview. There is already some workaround about it, for example here. The solution was to trigger tableview's internal update mechanism by
tableview.getColumns().get(0).setVisible(false);
tableview.getColumns().get(0).setVisible(true);
But as far as I could understand this solution affected only changes of data, not the style of tableview's nodes.
I also used
#Override
public void updateItem(Object item, boolean empty)
with my implementation, but it was not enough, because when there were too many rows in a table and scroll bar appeared the updating of rows became an absolute mess.
What I needed is to make highlighted all visible rows satifying some criteria by using css. I achieved that in the following way:
Callback<TableView<Person>, TableRow<Person>> callBack =
new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> tableView) {
final TableRow<Person> row = new TableRow<Person>() {
#Override
public void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
if(!empty && item.getFirstName() == "John") {
getStyleClass().add("john");
}
}
};
row.visibleProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observableValue, Object t, Object t1) {
Boolean oldValue = (Boolean)t;
Boolean newValue = (Boolean)t1;
if(oldValue && !newValue) {
row.getStyleClass().remove("john");
}
if(!oldValue && newValue) {
if(row.getItem().getFirstName() == "John")
row.getStyleClass().add("john");
}
}
});
return row;
}
};
tableview.setRowFactory(callBack);
The css file should contain the following lines:
.john {
-fx-background-color: forestgreen;
-fx-text-fill: white;
-fx-border-style: solid;
}
Of course you may choose different styling of rows.
The Person class should contain
public SimpleStringProperty firstNameProperty() {
return firstName;
}
As you can see I added the listener to the VisibleProperty of a row, where I control the css behavior of each row. Maybe this idea could help to solve data update in tableview in some cases, not just rows styling.
I should aslo add that I started receiving the NullPointerException in the public void changed method in the code above, although it doesn't affect the result of my programm.
I hope it will help you, Chrisma! And others as well!
I just had that problem with checkboxes. They randomly dissapeared when I scrolled my big table rapidly. I solved it just by deleting this line in my cellFactory:
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
// setGraphic(null); (This line causes weird behaviour in scrolling, delete it)
} else {
checkBox.setSelected(item);
}
}
Well, I also had the same problem, apparently only draw the visible text field, when we use the scroll does not appear, because only drawing the scene but not the textinputcontrol , so my solution is to capture the scroll event and when you use it to resize the textfield and return it to its original size, thus forcing you to repaint the object, now appear with textinputcontrol.forcing repaint the object, now appear with textinputcontrol.
tableview.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent scrollEvent) {
System.out.println("Scrolled.");
for(ObservableList<TextField> i:data)
{
for(TextField j: i)
{
j.setPrefSize(141, 31);
j.setPrefSize(140, 30);
}
}
}
});

Resources