Wrap text in JFXTreeTableView cells - java

How can I wrap the text in JFXTreeTableView cells?
My JFoenix TableTreeView column cell factory is created as shown below.
// Set column cell factories and width preference
for (JFXTreeTableColumn<LogEntry, String> column : columnList) {
column.setCellFactory(param ->
new GenericEditableTreeTableCell<>(new TextFieldEditorBuilder()));
column.setPrefWidth(100);
}
After hours of searching, I can't figure out how to get the cells in the tree table to wrap text. I even tried to set the wrap text value for every GenericEditableTreeTableCell to true, but I think I'm also supposed to call the setWrappingWidth() method on something. I tried the following block of code, but I ended up getting a NullPointerException.
// Set column cell factories and width preference
for (JFXTreeTableColumn<LogEntry, String> column : columnList) {
column.setCellFactory(param -> {
GenericEditableTreeTableCell<LogEntry, String> cell =
new GenericEditableTreeTableCell<>(new TextFieldEditorBuilder());
Text text = (Text) cell.getGraphic();
text.setWrappingWidth(1); //null pointer exception
cell.setWrapText(true);
return cell;
});
column.setPrefWidth(100);
}
So, I'm left with the following block of code which runs perfectly fine and displays the table, but the cells do not wrap text.
// Set column cell factories and width preference
for (JFXTreeTableColumn<LogEntry, String> column : columnList) {
column.setCellFactory(param -> {
GenericEditableTreeTableCell<LogEntry, String> cell =
new GenericEditableTreeTableCell<>(new TextFieldEditorBuilder());
// I think I should call setWrappingWidth() on some object here
// but I don't know what object
cell.setWrapText(true);
return cell;
});
column.setPrefWidth(100);
}
The documentation for setting up a JFXTreeTableView can be found here. It doesn't seem to mention anything about wrapping cell text.
Edit: I tried doing it with CSS, but didn't get any results. In fact, the cell.isWrapText() returned false after using this CSS code - meaning that it didn't event set the value to true. I know the block of CSS is working correctly because I can change every element's text fill color with it.
* {
-fx-wrap-text: true;
}
Edit 2: Some people said on other semi-related posts that a scroll pane can cause a Node to think it has a much larger width than what is shown to the user. Since a JavaFX TreeTableView uses a scroll bar when the table is too large, I figured I'd try their solutions. I tried setting the preferred width of the cell - still no results.
cell.setWrapText(true);
cell.setPrefWidth(100);
//cell.setMaxWidth(100); doing this too made no difference
//cell.setMinWidth(100); doing this too made no difference
Edit 3: I think I know the problem! It seems that the row height refuses to let the cell wrap text. If I set the rows minimum height to a large enough value, the cell wraps its text! Now I just need to know how to make the row height adjust dynamically to accommodate the cell when it wants to wrap text.
Edit 4: It appears that the row doesn't allow line breaks which may be the cause of the cell failing to wrap text. It can't wrap the text because the new lines it creates are chomped.

I have used one of your approaches, but also made custom EditorBuilder which has JFXTextArea instead of JFXTextField.
The custom TextAreaEditorBuilder is the following:
public class TextAreaEditorBuilder implements EditorNodeBuilder<String> {
private JFXTextArea textArea;
#Override
public void startEdit() {
Platform.runLater(() -> {
textArea.selectAll();
});
}
#Override
public void cancelEdit() {
}
#Override
public void updateItem(String s, boolean isEmpty) {
Platform.runLater(() -> {
textArea.selectAll();
textArea.requestFocus();
});
}
#Override
public Region createNode(
String value,
DoubleBinding minWidthBinding,
EventHandler<KeyEvent> keyEventsHandler,
ChangeListener<Boolean> focusChangeListener) {
StackPane pane = new StackPane();
pane.setStyle("-fx-padding:-10 0 -10 0");
textArea = new JFXTextArea(value);
textArea.setPrefRowCount(4);
textArea.setWrapText(true);
textArea.minWidthProperty().bind(minWidthBinding.subtract(10));
textArea.setOnKeyPressed(keyEventsHandler);
textArea.focusedProperty().addListener(focusChangeListener);
pane.getChildren().add(textArea);
return ControlUtil.styleNodeWithPadding(pane);
}
#Override
public void setValue(String s) {
textArea.setText(s);
}
#Override
public String getValue() {
return textArea.getText();
}
#Override
public void validateValue() throws Exception {
}
}
and then the configuration for your column goes something like this:
negativeCasingColumn.setCellFactory(param -> {
TextAreaEditorBuilder textAreaEditorBuilder = new TextAreaEditorBuilder();
GenericEditableTreeTableCell<DiagnosticMethodFX, String> cell
= new GenericEditableTreeTableCell<>(textAreaEditorBuilder);
cell.setWrapText(true);
return cell;
});
So basically I'm wrapping the cell, but making sure I use TextArea as editing field. For me this works just fine, but if you want more elegant solution maybe you can check the cancelEdit() method in GenericEditableTreeTableCell, where it sets the content display to text only: setContentDisplay(ContentDisplay.TEXT_ONLY) which is setting of content property in the abstract class Labeled, where the text wrap is set to false. I didn't dig too deep into it, but some workaround can be made for sure.

Related

JavaFX8: CheckboxListCell in ListView unchecks cells when scrolling [duplicate]

I am using cell factory for listview with checkboxes like:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback < Bean, ObservableValue < Boolean >> () {
#Override
public ObservableValue < Boolean > call(Bean item) {
BooleanProperty observable = new SimpleBooleanProperty();
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
if (!beanChoices.contains(item.toString())) {
beanChoices.add(item.toString());
observable.setValue(true);
//listView.scrollTo(listView.getItems().size() - 1);
}
} else if (wasSelected) {
if (beanChoices.contains(item.toString())) {
beanChoices.remove(item.toString());
observable.setValue(false);
}
}
});
/* [Code] which compares values with bean item string value and select observable to true for that for edit mode
but here the observer not called for beanItem that are under scrollpane of listview. But on scroll it gets called. */
return observable;
}
}));
It works fine but not for all cases.
Case: When I have say more than 10 entries, the scrollpane comes. Say I have beanChoices to be checked that are at 8 or 9 index(you have to scroll to view them). The listener is not called for the items not visible(that are under scrollpane). On Debug, I found that listener is called when I scroll down.
Problem: when I get checked values from beanChoices for above case, it return empty.
Detail: I have beanChoices which I need to make checked for listview items (edit mode). When I update without changing anything. (Assume that the value which is under the scrollpane of listview will be selected and added to beanChoices)
The Callback is used to retrieve the property for the checked state when the item is associated with a cell. The item may be removed from a cell and put in a new one at any time. This is how ListView (and similar controls like TableView) works. CheckBoxListCell simply gets the checked state property every time a new item is associated with the cell.
The return value is also used to set the initial state of the CheckBox. Since you do not properly initialize the property with the correct value the initial state is not preserved.
Also note that it makes little sense to update the value of the property to the new value in the change listener. It happens anyway.
Since BooleanProperty is a wrapper for primitive boolean the possible values are true and false; the ChangeListener only gets called when !Objects.equals(oldValue, newValue) you can be sure that isNowSelected = !wasSelected.
Of course you also need to return the value:
#Override
public ObservableValue < Boolean > call(Bean item) {
final String value = item.toString();
BooleanProperty observable = new SimpleBooleanProperty(beanChoices.contains(value));
observable.addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
beanChoices.add(value);
} else {
beanChoices.remove(value);
}
});
return observable;
}
I also recommend using a Collection of Beans instead of relying on the string representation of the objects. toString many not produce unique results and Beans.equals would be the better choice to compare the objects.

Work with Value from PropertyValueFactory in TableView - JavaFx

In my JavaFx project I have a scene with a few views. For the footer of the window I have a class extending from TableView:
public class FooterView extends TableView<Area> implements ViewTemplate {...}
This displays a Table with some data from a .csv-file.
When it comes to assigning the value of the specific presentationmodel property to the cells I do it like that:
TableColumn<Area,Double> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(new PropertyValueFactory<>("powerPerArea"));
this.setItems(filePM.getAreas()); //filePm is my filehandler
this.getColumns().addAll(powerColumn, other_columns);
getAreas() looks like this:
public List<Area> readCantonsFromFile() {
try (Stream<String> stream = getStreamOfLines(AREA_FILE_NAME)) {
return stream.skip(1)
.map(l -> new Area(l.split(DELIMITER, 12)))
.collect(Collectors.toList());
}
}
In the constructor of Area i set the properties. One of the properties is the mentioned powerPerArea
private final DoubleProperty powerPerArea = new SimpleDoubleProperty();
...
public void setPowerPerCanton(double powerPerCanton) {
this.powerPerCanton.set(powerPerCanton);
}
My question: Is there a way to change the value in the FooterView before the value is displayed? I tried something like this:
powerColumn.setCellValueFactory(Math.round(new PropertyValueFactory<>("powerPerArea")));
But it seems that I mixed up DoubleProperty, Double and ObservableDouble. Can I even modify the value in here?
The problem: I can not round the value in the setter because I add a double value in a loop through this function:
public void addPowerPerArea(double power){
double sum = getPowerPerCanton() + power;
setPowerPerCanton(sum);
}
And rounding the value in here would give me a wrong result. (rounding not precise enough). I need to do it in the end when all sums are added
You should use the cellValueFactory to determine which data are displayed in the cells: in this case the data returned by your PropertyValueFactory is the actual double value returned from powerPerAreaProperty().get(), which is exactly what you want.
If you want to control how the data are displayed, you should use a cellFactory. So to display the data in a particular format, including limiting the number of decimal places, you can do:
powerColumn.setCellFactory(tc -> new TableCell<Area, Double>() {
#Override
protected void updateItem(Double power, boolean empty) {
super.updateItem(power, empty);
if (empty) {
setText(null);
} else {
setText(String.format("%.0f", power.doubleValue()));
}
}
});
The point here is that you should not modify the data based on how you want to display it; the purpose of having both cellValueFactory and cellFactory is to separate the display of the data from the actual data itself.
An alternative to returning custom cells from a cellFactory would be to use a custom cellValueFactory to return the property formatted as string:
TableColumn<Area, String> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(cd -> cd.getValue().powerPerAreaProperty().asString(""%.0f""));
I see two ways to do this. You could:
use the setCellFactory method and in the updateItem method you format it. Should look something like this, haven't tested
powerColumn.setCellFactory(column -> {
return new TableCell<Area, Double>() {
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(Math.round(item), empty);
}
};
});
OR: You could make another property of your Area class that is bound to the existing powerOfArea property but returns the value rounded. I am sure that is somehow possible, you could just override some functions of a readOnlyDoubleProperty but there should be a better way. Maybe via DoubleBindings.

SmartGWT - how to check if ListGridRecord is expanded or collapsed?

I have customized ListGrid, in which records can be expanded and expansion component is displayed. I know there exists little arrow to expand/collapse record in upper left corner of the record, but I wonder if it is possible to manually check if the selected record is expanded or collapsed. I want the record to expand/collapse on single record click. My code example:
private RecordClickHandler gatherRecordClickHandler() {
return new RecordClickHandler() {
#Override
public void onRecordClick(RecordClickEvent event) {
//Here i want to check if the record is expanded/collapsed
if(/*expanded check method here*/)
collapseRecord(event.getRecord());
else
expandRecord(event.getRecord());
}
};
}
Try this:
if(myListGrid.isExpanded(event.getRecord))
collapseRecord(event.getRecord());
else
expandRecord(event.getRecord());

How do I completely disable row selection in a CellTable?

I want to completely view updates when a row is selected in a CellTable. How can this be done? In the following test case, using NoSelectionModel, the view is still updated: clicking on a row changes the background and border colors of the row until another row is clicked.
CellTable<String> table = new CellTable<String>();
TextColumn<String> column = new TextColumn<String>()
{
#Override
public String getValue(String string)
{
return string;
}
};
table.addColumn(column);
List<String> sampleData = Arrays.asList("foo", "bar", "baz");
table.setRowData(sampleData);
final NoSelectionModel<String> selectionModel = new NoSelectionModel<String>();
table.setSelectionModel(selectionModel);
RootPanel.get().add(table);
I've also attempted to subclass SingleSelectionModel with empty override methods, without success.
I can fake the behavior I want by providing empty CSS stylings for selected rows, but that method seems hack-ish.
What you're seeing is the keyboard-selection highlighting (really useful when you're not using the mouse to interact with the table).
You can disable it using setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED)

How can I get a Canvas component of a ListGridRecord?

I use smartgwt 2.4.
I'm trying to style a ListGridRecord. I want to get the Canvas component of it, but I cannot find a reference anywhere.
I know there are methods in ListGrid as createRecordComponent or getBackgroundComponent etc., but these don't return any component. They are meant as an override point (user can define his/her own components instead of default). But this is not what I want. I want to get the default component and change it (style it).
I know there's a setCellFormatter method at the ListGrid, where I can set format of a cell, but it only regards the text component of a cell, not the whole row (record).
I know there's a getBaseStyle method, where I can put a class name, but this is still not what I want. I want to change the style dynamically (e.g. I want to put any background color to the component) not only put a static class(es) (where the background color is predefined).
Can anybody help?
Thanks.
I'm afraid your options are a little limited when it comes to SmartGWT.
One, although not very simple way of achieving that is overriding the ListGrid.getCellCSSText(ListGridRecord record, int rowNum, int colNum) method on creation of ListGrid as shown here.That is how I have created customized cell styles.
final ListGrid grid= new ListGrid() {
protected String getCellCSSText(ListGridRecord record, int rowNum, int colNum) {
if (getFieldName(colNum).equals("MyColumnName")) {
ListGridRecord record = (ListGridRecord) record;
if (record.getSomeValue() > 20) {
return "font-weight:bold; color:red;";
} else if (record.getSomethingElse() < 5) {
return "font-weight:bold; color:blue;";
} else {
return super.getCellCSSText(record, rowNum, colNum);
}
} else {
return super.getCellCSSText(record, rowNum, colNum);
}
}
};

Categories

Resources