I'd like to know, if it is possible to render simple HTML tags in JavaFX TableView (b, i, subscript, supscript). In my code snippet I used default cellValueFactory, but maybe someone could tell me if exists any cell factory which allow me to display html.
From code:
class Data{
private String row = "<b> Sample data</b>"
public String getRow(){
return row;
}
TableView<Data> tableView = new TableView();
TableColumn<Data,String> column = new TableColumn("Sample Column");
column.setCellValueFactory(new PropertyValueFactory<Data, String>("row"));
tableView.getColumns().addAll(column);
I wish I could see Sample Data in my table in bold. Thanks in advance!
--UPDATE
Code that allows me to see my HTML, but resizes table cell, WebView size is ignored and not wrapped tight
private class HTMLCell extends TableCell<Component, Component> {
#Override
protected void updateItem(Component item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
WebView webView = new WebView();
webView.setMaxWidth(200);
webView.setMaxHeight(50);
WebEngine engine = webView.getEngine();
// setGraphic(new Label("Test"));
setGraphic(webView);
String formula = item.getFormula();
engine.loadContent(formula);
}
}
}
TableColumn<Component, Component> formulaColumn = new TableColumn<>("Formula");
formulaColumn.setMinWidth(300);
formulaColumn.setCellFactory(new Callback<TableColumn<Component, Component>, TableCell<Component, Component>>() {
#Override
public TableCell<Component, Component> call(TableColumn<Component, Component> param) {
return new HTMLCell();
}
});
formulaColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Component, Component>, ObservableValue<Component>>() {
#Override
public ObservableValue<Component> call(CellDataFeatures<Component, Component> param) {
return new SimpleObjectProperty<Component>(param.getValue());
}
});
HTML in a WebView in a TableCell
You have to make your own cell factory which returns a WebView node into which you load your HTML content.
On Correctly Sizing the WebView
In terms of establishing the preferred size of the WebView node, that is a little tricky. It would be simpler if RT-25005 Automatic preferred sizing of WebView were implemented.
I think the sample code from your question will work if you just replace the maxSize setting for the WebView with a webView.setPrefSize(prefX, prefY) call. You will just have to guess what the prefX and prefY values should be as I don't know a good way of determining programmatically.
I think your code should work by setting the max size for the WebView, but the WebView doesn't seem to respect the max size setting and just uses the pref size setting, which I think may be a bug in Java 8b129 (you could file that in the JavaFX Issue Tracker with a minimal, executable test case which reproduces it and a description of your test environment).
TextFlow Alternative
You might also consider using TextFlow component for representing your styled text. It is not HTML, but if all you want to do is some simple styling like making some text in the cell bold or italic, it might be a good option.
Use HTML tables in WebEngine/WebView rather than TableView.
Table examples
Related
I am working on a TreeView which represents a robot controlling program, each TreeCell represents a statement, and a TreeCell can be nested in an other one. Like in programming, statements can be nested in if or for statements.
Here I have created a simple demo, filled with some random blocks.
Demo Screenshot
To customize the rendering of TreeCell, I have create a class extending TreeCell:
public class TreeDataCell extends TreeCell<TreeData> {
public void updateItem(TreeData item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if (item == null || empty) {
setGraphic(null);
} else {
setGraphic(getCellGraphic(item));
}
}
private Group getCellGraphic(TreeData data) {
Group grp = new Group();
VBox vbox = new VBox();
vbox.setMinWidth(100);
vbox.setMaxWidth(200);
vbox.setBorder(new Border(new BorderStroke(
Color.LIGHTGRAY.darker(),
BorderStrokeStyle.SOLID,
new CornerRadii(10.0),
new BorderWidths(2.0))));
vbox.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, new CornerRadii(10.0), null)));
vbox.setEffect(new DropShadow(2.0, 3.0, 3.0, Color.DIMGRAY));
Region header = new Region();
header.setPrefHeight(5.0);
Region footer = new Region();
footer.setPrefHeight(5.0);
Label labTitle = new Label();
labTitle.setFont(new Font("San Serif", 20));
labTitle.setText(data.getTitle());
Label labDesc = null;
if (data.getDescription() != null) {
labDesc = new Label();
labDesc.setWrapText(true);
labDesc.setText(data.getDescription());
}
vbox.getChildren().addAll(header, labTitle);
if (labDesc != null) {
vbox.getChildren().add(labDesc);
}
vbox.getChildren().add(footer);
grp.getChildren().add(vbox);
return grp;
}
}
The TreeData is a simple class containing 2 Strings:
public class TreeData {
private String title;
private String desc;
/* getters + setters */
}
As you can see, the indentation between two levels are too small, and we can barely see statement nesting.
I am hard coding all the styles in Java, since I haven't learnt FXML+CSS yet.
I'd like to know if it is possible to set the size of indentation in Java? I cannot find any API for this purpose. In addition, is it possible to draw lines between parent node and its children like JTree in Swing ?
Thank you.
Regarding having lines like in JTree, there is no built in way to do that as of JavaFX 11. There is a feature request (JDK-8090579) but there doesn't seem to be any plans to implement it. You may be able to implement it yourself but I'm not sure how.
As to modifying the indent of the TreeCells, the easiest way is by using CSS.
As documented in the JavaFX CSS Reference Guide, TreeCell has a CSS property named -fx-indent whose value is a <size>. You can set this property by using a stylesheet or inline it via the style property. An example using inline styles:
public class TreeDataCell extends TreeCell<TreeData> {
public TreeDataCell() {
setStyle("-fx-indent: <size>;");
}
}
However, since you are currently not using CSS or FXML, there is another option that is purely code: Modifying the indent property of TreeCellSkin. This class became public API in JavaFX 9. There may be equivalent internal API in JavaFX 8 but I'm not sure.
By default, the Skin of a TreeCell will be an instance of TreeCellSkin. This means you can get this skin and set the indent value as needed. You have to be careful, though, as the skin is lazily created; it won't necessarily be available until the TreeView is actually part of a showing window.
If you only want to set the property once, one way is to intercept the skin inside createDefaultSkin():
public class TreeDataCell extends TreeCell<TreeData> {
#Override
protected Skin<?> createDefaultSkin() {
TreeCellSkin<?> skin = (TreeCellSkin<?>) super.createDefaultSkin();
skin.setIndent(/* your value */);
return skin;
}
}
You could also extend TreeCellSkin and customize it. Just remember to override createDefaultSkin() and return you custom skin implementation.
I want to use GWT-Bootstrap Tooltip for cells of a column in the CellTable. Each Tooltip will show description of a cell which is a field in the "MyCustomObject" class used for generating the table.
One of the quick solutions I found in one of the answers on this question is the following.
CellTable<MyCustomObject> table = new CellTable<MyCustomObject>();
table.addCellPreviewHandler(new Handler<MyCustomObject>() {
#Override
public void onCellPreview(CellPreviewEvent<MyCustomObject> event) {
if (event.getNativeEvent().getType().equals("mouseover")){
table.getRowElement(event.getIndex()).getCells()
.getItem(event.getColumn()).setTitle(event.getValue().getDescription());
}
}
}
);
It makes a little sense but I don't see anything when I hover the cursor over the table cells and nothing is attached to the DOM. Can anyone explain how this even works? I want to attach the Tooltip to table cell similar to the following.
Tooltip tt = new Tooltip("Here goes the description");
tt.setAnimation(true);
tt.setWidget(tableColumn);
tt.reconfigure();
The problem is that a CellTable cell is not a widget so it can't be attached in that way.
So is there any workaround for this?
I've faced this issue once and I created my own cell that displays a tooltip, here's how I did it: Note: My cell was displaying an image with a tooltip so I quickly changed the code to display String text... So I did not test it with text.
private class TooltipCell extends AbstractCell<String> {
private String tooltipText = "";
#Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
if (value != null) {
sb.appendHtmlConstant("<div title=\"" + tooltipText + "\">");
sb.append(SafeHtmlUtils.fromString(value));
}
}
public void setTooltip(String tootltipTextToSet){
tooltipText = tootltipTextToSet;
}
}
And in your table use method setTooltip("tooltip text") in method getValue just before returning the cell text.
Hope this helps
Vaadin Grid allows to be defined as editable with
grid.setEditorEnabled(true);
This makes all visible columns editable. However I don't want the user to edit an specific column, but seems like the editable is an all or nothing.
The next best solution I have found is to define an editor field with a disabled editor, which almost does the trick but the user is still able to select the text and move the cursor (but the field is not editable anymore).
Grid.Column nameColumn = grid.getColumn("fullName");
nameColumn.setHeaderCaption("Full Name");
nameColumn.setEditorField(getNoEditableTextField());
...
private Field<?> getNoEditableTextField() {
TextField noEditableTextFiled = new TextField();
noEditableTextFiled.setEnabled(false);
return noEditableTextFiled;
}
I believe Label cannot be used because it's not a Field.
Is there a better option to achieve this?
edit: as aakath said, there is a way of achieving this not enabling the column to be edited, but in doing so the cell value disappears when you edit the row, which is not desirable.
Did you try calling setEditable(false) method on the column? I believe it should make the field non-editable when the item editor is active.
grid.getColumn("fullName").setEditable(false);
my solution is below. i have just finished. it was not tested too much. but it may give you some ideas.
ati
getColumn(columnName).setEditable(true).setEditorField(getNoEditableField(columnName));
...
private Field<?> getNoEditableField(final String columnName) {
CustomField<Label> result = new CustomField() {
#Override
protected Component getContent() {
Label result = (Label) super.getContent();
Object editedItemId = getEditedItemId();
String value = DEFAULT_VALUE;
if (editedItemId != null) {
value = CustomizableGrid.this.toString(getContainerDataSource().getItem(editedItemId).getItemProperty(columnName).getValue());
}
result.setValue(value);
return result;
}
#Override
protected Component initContent() {
Label result = new Label(DEFAULT_VALUE, ContentMode.HTML);
result.setDescription(getColumnDescription(columnName));
result.setStyleName("immutablegridcellstyle");
return result;
}
#Override
public Class getType() {
return Label.class;
}
};
result.setConverter(new Converter<Label, Object>() {
//converter for your data
});
return result;
}
I had the same problem and didn't want that clicking on id column opens editor. I solved it with adding an ItemClickListener as below. It works fine for me.
grid.addItemClickListener((ItemClickListener<GridBean>) event -> grid.getEditor().setEnabled(!event.getColumn().getCaption().equals("Id")));
Also byc clicking on specific columns Grid is not editable any more.
There is one tricky way to do it! I've just found out it.
So, first of all you need to use grid with container, instead of direct rows adding:
BeanItemContainer<MyBean> container = new BeanItemContainer<>(MyBean.class);
setContainerDataSource(container);
Then remove fields setters from MyBean, except setters for fields what you have to edit.
I think the same can be achieved by making the grid an editable one by grid.setEditorEnabled(true); and disabling editing option for other columns like grid.getColumn(columnName).setEditable(false);. But I am not sure of any demerits of this method. Any suggestion is always appreciated.
Its simple just go to Vaadin Documentation what did from it is below:
you can see here I gave a specified column Name
grid = new Grid<>();
lst = new ArrayList<>();
provider = new ListDataProvider<>(lst);
lst.add(new Company(1, "Java"));
grid.setDataProvider(provider);
grid.addColumn(Company::getSerialNo).setCaption("Sr.no");
TextField tf = new TextField();
grid.getEditor().setEnabled(true);
HorizontalLayout hlyt = new HorizontalLayout();
grid.addColumn(Company::getName).setEditorComponent(tf, Company::setName).setCaption("Name").setExpandRatio(2);
hlyt.addComponent(grid);
I use the following approach to get a read-only field, the trick is override the setEnabled method to get a disabled textfield. If you trace the source code in Vaadin Grid, no matter what field you pass to a Grid, it will always call the field.setEnabled(true).
myGrid.getColumn(propertyId).setEditorField(new ReadOnlyField());
And
public class ReadOnlyField extends TextField
{
public ReadOnlyField()
{
super();
this.setReadOnly(true);
}
#Override
public void setEnabled(boolean enabled)
{
// always set to disabled state
super.setEnabled(false);
}
}
I currently have a ListView that is resizable in Width and I use a custom CellFactory that returns my custom ListCell objects.
I already read this:
Customize ListView in JavaFX with FXML and I use a similar construct like the one mentioned:
public class ListViewCell extends ListCell<String>{
#Override
public void updateItem(String string, boolean empty){
super.updateItem(string,empty);
if(string != null) {
...
setGraphic(myComponent);
}
}
I want myComponent to take the full size that is available inside the ListCell, however it seems that the setGraphic method limits the size of the given Node.
I can provide all my code if it is necessary, however I am not sure which parts are relevant and I do not want to post a big wall of code.
Try setting prefWidth property of your control to Infinity either via CSS or via API so as to provide its maximal expansion whithin container:
myComponent.setStyle("-fx-pref-width: Infinity");
I solved my Problem now, here is how:
class MyListCell extends ListCell<MyObject>
{
private final AnchorPane _myComponent;
public MyListCell()
{
...
this.setPrefWidth(0);
myComponent.prefWidthProperty().bind(this.widthProperty());
}
...
#Override
protected void updateItem(MyObject item, boolean empty)
{
...
setGraphic(_myComponent);
...
}
}
If this.setPrefWdith(0) is ommited, myComponent will grow inside the ListCell, but the Cell won't shrink if I shrink my ListView.
I'm using Ext-GWT and I think ListView is the right layout for what I need. My problem is that I have to use a HTML template for all of my items, but I want to build GWT/Ext-GWT widgets instead, so I'm using div placeholders that I will replace with the proper widgets.
How can I replace my div with a widget? My first attempt was to use RootPanel.get('div-id'), but apparently you can't create a RootPanel that is in a widget (I used debug mode to step through the code till I found that silent exception).
public class TicketContainer extends LayoutContainer {
private ArrayList<BasicTicket> tickets;
public TicketContainer(ArrayList<BasicTicket> tickets) {
this.tickets = tickets;
}
#Override
protected void onRender(Element parent, int index) {
super.onRender(parent, index);
setLayout(new FlowLayout(1));
ListStore<BasicTicket> store = new ListStore<BasicTicket>();
store.add(this.tickets);
ListView<BasicTicket> view = new ListView<BasicTicket>(store);
view.addListener(Events.Refresh, new Listener<BaseEvent>() {
#Override
public void handleEvent(BaseEvent be) {
for (BasicTicket ticket : tickets) {
// At this point I need to find the div with the id
// "ticket_"+ticket.getId() and replace it with a GWT
// widget that I can add events to and enable drag and drop
}
}
});
add(view);
}
private native String getTemplate() /*-{
return ['<tpl for=".">',
'<div id="ticket_{id}"></div>',
'</tpl>'].join("");
}-*/;
}
The full source is at https://code.launchpad.net/~asa-ayers/+junk/Kanban if you need additional context in the code.
In "pure" GWT, the answer would be to use HTMLPanel:
String id = DOM.createUniqueId();
HTMLPanel panel = new HTMLPanel("<div class=\"content\" id=\"" + id + "\"></div>");
panel.add(new Label("Something cool"), id);
As you can see, the com.google.gwt.user.client.ui.HTMLPanel.add(Widget, String) takes the id of an element withing the HTMLPanel and places the Widget inside that element.
I haven't used Ext-GWT, but you can either use HTMLPanel or search for an exquivalent in Ext-GWT.
You can also wrap an existing div in an HTML Panel.
HTMLPanel newPanel = HTMLPanel.wrap(Document.get().getElementById("yourDivId"));
newPanel.add(your_widget);