My problem is that I have just created a cellTable but it doesn't work because I see this on my broswer
The red border is of the FlowPanel that contains the table that has a black border, on side there is the GWT.log
Now I've tried everything but I don't know why, maybe it doesn't load the table for something reason. However I'm sure that dataProvider works beacause, as tou can see, the log show that data 'Carrello' are load on the column of my table 'carrello'. Here the code of table:
public class ShopTable extends CellTable {
private CellTable<Carrello> carrello;
private Column<Carrello, String> columnTitolo;
private Column<Carrello, String> columnTipoSupporto;
private AsyncDataProviderCarrello dataProvider;
private String COLUMN_NAME_TITOLO="Titolo film";
private String COLUMN_NAME_SUPPORTO="Tipo";
public ShopTable(){
carrello=new CellTable<>();
createTable();
createWithAsyncDataProvider();
GWT.log("Column example: "+carrello.getColumn(0).toString());
}
private void createTable(){
columnTitolo=buildColumnTitolo();
columnTipoSupporto=buildColumnTipoSupporto();
// NEED TO ADD HEADER (and FOOTER MAYBE)
carrello.addColumn(columnTitolo, "Titolo Film");
carrello.addColumn(columnTipoSupporto, "Tipo");
}
private Column<Carrello, String> buildColumnTitolo(){
columnTitolo=new Column<Carrello, String>(new EditTextCell()) {
#Override
public String getValue(Carrello object) {
GWT.log("aggiungo a carrelloTable: "+object);
return object.getTitolo();
}
};
columnTitolo.setDataStoreName(COLUMN_NAME_TITOLO);
return columnTitolo;
}
private Column<Carrello, String> buildColumnTipoSupporto(){
columnTipoSupporto=new Column<Carrello, String>(new EditTextCell()) {
#Override
public String getValue(Carrello object) {
GWT.log("aggiungo a carrelloTable: "+object);
return object.getTipoSupporto().toString();
}
};
columnTipoSupporto.setDataStoreName(COLUMN_NAME_TITOLO);
return columnTipoSupporto;
}
private void createWithAsyncDataProvider(){
dataProvider=new AsyncDataProviderCarrello();
dataProvider.addDataDisplay(carrello);
dataProvider.updateRowCount(10, false);
//.. SORTING METHOD NEED TO ADD
}
}
Here the code of Widget UIBinder that use the ShopTable
public class CarrelloPage extends Composite {
private static CarrelloPageUiBinder uiBinder = GWT.create(CarrelloPageUiBinder.class);
interface CarrelloPageUiBinder extends UiBinder<Widget, CarrelloPage> {
}
interface MyStyle extends CssResource{
String carrelloTable();
}
#UiField MyStyle style;
#UiField FlowPanel spazio_carrello;
/**
* necessario per dimensionare ad hoc per
* il pannello
*/
private ShopTable carrello;
private void resizeWidget(){
setWidth("100%");
setHeight(Window.getClientHeight() + "px");
}
public CarrelloPage() {
carrello=new ShopTable();
initWidget(uiBinder.createAndBindUi(this));
carrello.setStyleName(style.carrelloTable());
spazio_carrello.add(carrello);
resizeWidget();
Window.addResizeHandler(resizeHandler);
}
private ResizeHandler resizeHandler = new ResizeHandler()
{
public void onResize (ResizeEvent event)
{
setWidgetToMaxWidthAndHeight();
}
};
private void setWidgetToMaxWidthAndHeight ()
{
setWidth("100%");
setHeight(Window.getClientHeight() + "px");
}
}
Thanks for attention!
Your CellTable has a height of zero. This is why you don't see it.
You either have to set height of your CellTable in code, or you should add it to a widget that implements ProvidesResize interface, like a LayoutPanel.
Related
In my project, I have a TableViewer which needs to show around 3000 items and also filter them. Without SWT.VIRTUAL the table takes multiple seconds to display.
So, I implemented ILazyContentProvider and not using "ArrayContentProvider" but now I am facing issues in filtering the elements of TableViewer.
I am writing the below code for filtering but the select method is not running at all.
private static class DefaultFilterextends ViewerFilter {
private String searchText;
public void setSearchText(final String searchText) {
this.searchText = searchText;
}
#Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (this.searchText == null) {
return true;
}
return (element.toString().contains(searchText) || element.toString().equals(searchText));
}
}
The below ContentProvider, I am writting
private class LazyContentProvider implements ILazyContentProvider {
private TableViewer viewer;
private List<String> elements;
public LazyContentProvider(TableViewer viewer) {
this.viewer = viewer;
}
#Override
public void dispose() {
}
#Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.elements = (List<String>) newInput;
}
#Override
public void updateElement(int index) {
viewer.replace(elements.get(index), index);
}
}
Can someone please guide me how can I do the filteration of elements in TableViewer with SWT.VIRTUAL?
TableViewer does not support filtering when SWT.VIRTUAL is used. Instead you must do the filtering in the content provider.
The content provider will need to maintain a filtered list of elements. For example:
public class LazyContentProvider implements ILazyContentProvider
{
private TableViewer viewer;
private List<String> allElements;
private List<String> filteredElements;
public LazyContentProvider()
{
//
}
#Override
public void dispose()
{
//
}
#SuppressWarnings("unchecked")
#Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput)
{
this.viewer = (TableViewer)viewer;
allElements = (List<String>)newInput;
filteredElements = allElements;
}
#Override
public void updateElement(final int index)
{
viewer.replace(filteredElements.get(index), index);
}
public void filter(final String searchText)
{
filteredElements = allElements.stream().filter(element -> element.contains(searchText)).toList();
viewer.setItemCount(filteredElements.size());
viewer.refresh();
}
}
Here I have added a filteredElements list that contains just the current filtered elements, this is used in updateElement.
I added a filter method which updates the filtered elements list. It also tells the table viewer about the new item count and refreshes the table.
The .toList() method in the filter stream requires Java 16, for older versions of Java use .collect(Collectors.toList()).
My use-case:
a custom property on a control that should be configurable via css
the property must be changeable at runtime
for a given instance of the control, the programmatic change must not be reverted on re-applying the css
A custom StyleableProperty looks like a perfect match to implement the requirement. Below is an example that implements (taken without change from the class javadoc of StyleablePropertyFactory).
All is fine except for the last requirement: on applyCss, the default value from the stylesheet is reapplied. To reproduce:
run the example, note that the initial "selected" state (the checkbox' selected is bound it) of the MyButton is true
click the custom button, note that the "selected" doesn't change to false (though the actionHandler changes it)
click on the second ("toggle") button, note that the selected state of the custom button changes to false
hover the mouse over the custom button, note that the selected state falls back to true
The reason for falling back to true (the value set via style), can be traced to applyCss which happens on state changes ... which is understandable and might be the correct thingy-to-do most of the times, but not in my context.
So the questions:
am I on the right track with using StyleableProperty?
if so, how to tweak such that it's not re-apply after a manual change has happened?
if not, what else to do?
or maybe asking the wrong questions altogether: maybe properties which are settable via css are not meant to be (permanently) changed by code?
The example:
public class StyleableButtonDriver extends Application {
/**
* example code from class doc of StyleablePropertyFactory.
*/
private static class MyButton extends Button {
private static final StyleablePropertyFactory<MyButton> FACTORY
= new StyleablePropertyFactory<>(Button.getClassCssMetaData());
MyButton(String labelText) {
super(labelText);
getStyleClass().add("my-button");
setStyle("-my-selected: true");
}
// Typical JavaFX property implementation
public ObservableValue<Boolean> selectedProperty() { return (ObservableValue<Boolean>)selected; }
public final boolean isSelected() { return selected.getValue(); }
public final void setSelected(boolean isSelected) { selected.setValue(isSelected); }
// StyleableProperty implementation reduced to one line
private final StyleableProperty<Boolean> selected =
FACTORY.createStyleableBooleanProperty(
this, "selected", "-my-selected", s -> s.selected);
#Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return FACTORY.getCssMetaData();
}
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return FACTORY.getCssMetaData();
}
}
private Parent createContent() {
MyButton button = new MyButton("styleable button");
button.setOnAction(e -> {
// does not work: reset on applyCss
boolean isSelected = button.isSelected();
button.setSelected(!isSelected);
});
CheckBox box = new CheckBox("button selected");
box.selectedProperty().bind(button.selectedProperty());
Button toggle = new Button("toggle button");
toggle.setOnAction(e -> {
boolean isSelected = button.isSelected();
button.setSelected(!isSelected);
});
BorderPane content = new BorderPane(button);
content.setBottom(new HBox(10, box, toggle));
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent(), 300, 200));
//same behavior as setting the style directly
// URL uri = getClass().getResource("xstyleable.css");
// stage.getScene().getStylesheets().add(uri.toExternalForm());
// not useful: would have to override all
// Application.setUserAgentStylesheet(uri.toExternalForm());
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(StyleableButtonDriver.class.getName());
}
You are on the right track, but since you need to override the default priority of the style origins (user agent stylesheet < programmatically assigned < css stylesheet < Node.style property), you cannot use SyleablePropertyFactory for creating this property. You need to create a CssMetaData object that indicates a property as non-setable, if the property was programatically assigned.
private static class MyButton extends Button {
private static final List<CssMetaData<? extends Styleable, ?>> CLASS_CSS_METADATA;
private static final CssMetaData<MyButton, Boolean> SELECTED;
static {
SELECTED = new CssMetaData<MyButton, Boolean>("-my-selected", StyleConverter.getBooleanConverter()) {
#Override
public boolean isSettable(MyButton styleable) {
// not setable, if bound or set by user
return styleable.selected.getStyleOrigin() != StyleOrigin.USER && !styleable.selected.isBound();
}
#Override
public StyleableProperty<Boolean> getStyleableProperty(MyButton styleable) {
return styleable.selected;
}
};
// copy list of button css metadata to list and add new metadata object
List<CssMetaData<? extends Styleable, ?>> buttonData = Button.getClassCssMetaData();
List<CssMetaData<? extends Styleable, ?>> mybuttonData = new ArrayList<>(buttonData.size()+1);
mybuttonData.addAll(buttonData);
mybuttonData.add(SELECTED);
CLASS_CSS_METADATA = Collections.unmodifiableList(mybuttonData);
}
MyButton(String labelText) {
super(labelText);
getStyleClass().add("my-button");
setStyle("-my-selected: true");
}
// Typical JavaFX property implementation
public ObservableValue<Boolean> selectedProperty() { return selected; }
public final boolean isSelected() { return selected.get(); }
public final void setSelected(boolean isSelected) { selected.set(isSelected); }
// StyleableProperty implementation reduced to one line
private final SimpleStyleableBooleanProperty selected = new SimpleStyleableBooleanProperty(SELECTED, this, "selected");
#Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return CLASS_CSS_METADATA;
}
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return CLASS_CSS_METADATA;
}
}
My goal is to display every field of an instance of a Class in a tableView. The Class has a field of type enum which has a field of type String.
The enum shall be displayed in a ComboBox as it's String field name.
Of course it also has to be editable.
Now what isn't working:
The String field of enum class is only displayed if the ComboBox is clicked on, otherwise it is the name of the enum constant. Also, if another enum in the combobox is selected, it can't be commited for edit. Clicking return doesn't deselect the Combobox neither is the method commitEdit invoked. If an other column is selected for edit, the attempted edit is cancelled.
I put some effort into trying to figure this out, so I thought maybe one could help me here.
As the original task is about much bigger classes in enterprise software, I abstracted it to ask this question.
I know I could make the column holding the enum of type String and make it work with MyEnum.values() and MyEnum.valueOf() but that could not go into production due to bad performance as the original classes are too big.
Here is my code as an example, if you don't understand the problems just try to use the combobox once and you'll see.
Class which is type of TableView:
public class MyClass {
private MyEnum myEnum;
private String string;
public MyClass(MyEnum myEnum, String string) {
this.myEnum = myEnum;
this.string = string;
}
public MyEnum getMyEnum() {
return myEnum;
}
public void setMyEnum(MyEnum myEnum) {
this.myEnum = myEnum;
}
public String getString() {
return string;
}
}
It's enum field:
public enum MyEnum {
EnumOne("First Enum"),
EnumTwo("Second Enum");
private String name;
public String getName() {
return name;
}
private MyEnum(String name) {
this.name = name;
}
}
FX App:
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<MyClass> items = FXCollections.observableArrayList();
items.add(new MyClass(MyEnum.EnumOne, "String"));
TableView<MyClass> table = new TableView(items);
table.setEditable(true);
TableColumn<MyClass, MyEnum> myEnumColumn = new TableColumn();
TableColumn<MyClass, String> stringColumn = new TableColumn();
stringColumn.setCellFactory(TextFieldTableCell.forTableColumn());
stringColumn.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getString()));
myEnumColumn.setCellFactory((param) -> new MyEnumComboBoxCell());
myEnumColumn.setCellValueFactory(data -> new ReadOnlyObjectWrapper(data.getValue().getMyEnum()));
myEnumColumn.setOnEditCommit(
event -> {
event.getRowValue().setMyEnum(event.getNewValue());
System.out.println(event.getRowValue().getMyEnum());
});
table.getColumns().addAll(myEnumColumn, stringColumn);
StackPane root = new StackPane();
root.getChildren().add(table);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
class MyEnumComboBoxCell extends ComboBoxTableCell<MyClass, MyEnum> {
private ComboBox<MyEnum> box;
public MyEnumComboBoxCell() {
box = new ComboBox<>(FXCollections.observableArrayList(MyEnum.values()));
box.setCellFactory(new Callback<ListView<MyEnum>, ListCell<MyEnum>>() {
#Override
public ListCell<MyEnum> call(ListView<MyEnum> param) {
return new ListCell<MyEnum>() {
#Override
protected void updateItem(MyEnum item, boolean empty) {
super.updateItem(item, empty);
if ( item != null ) setText(item.getName());
}
};
}
});
}
#Override
public void startEdit() {
super.startEdit();
setGraphic(box);
}
#Override
public void commitEdit(MyEnum newValue) {
super.commitEdit(newValue);
if ( newValue != null ) {
setText(newValue.getName());
getTableView().getSelectionModel().getSelectedItem().setMyEnum(newValue);
box.setValue(newValue);
}
}
#Override
public void updateItem(MyEnum item, boolean empty) {
super.updateItem(item, empty);
if ( empty ) {
setGraphic(null);
} else {
setGraphic(null);
setText(item.getName());
}
}
}
Instead of setting the name in updateItem use a StringConverter like:
public class MyEnumConverter extends StringConverter<MyEnum>{
#Override public String toString(MyEnum enumConstant) {
return enumConstant.getName();
}
#Override public MyEnum fromString(String string) {
return MyEnum.valueOf(string);
}
}
Then in the cell's constructor:
this.setConverter(new MyEnumConverter());
Edit: You may not #Override all of the ComboBoxTableCell's methods, since all of them are working like you want. On the other hand you should not specify an own ComboBox for the table cell, since it has one. You just have to add a StringConverter and set the items.
You may use like this:
myEnumColumn.setCellFactory((param) -> new ComboBoxTableCell<>(new StringConverter<MyEnum>() {
#Override public String toString(MyEnum object) {
return object.getName();
}
#Override public MyEnum fromString(String string) {
return MyEnum.valueOf(string);
}
}, MyEnum.values()));
If you prefer you can create a separate class for the StringConverter like I mentioned earlier, then just simply:
myEnumColumn.setCellFactory(factory -> new ComboBoxTableCell<>(new MyEnumConverter(), MyEnum.values()));
You can also get rid of the myEnumColumn.setOnEditCommit.
Thanks a lot! Actually I've spent a day on this, partially with another person, so this is really appreciated! :)
But:
I have to implement setOnEditCommit or otherwise the myEnum field backing the tableColumn does not change! With this everything works. Without, only what is displayed is changed.
myEnumColumn.setOnEditCommit(
event ->
{
event.getRowValue().setMyEnum(event.getNewValue());
});
STARTED - 3:00PM
UPDATE 1 - 5:36PM
Apply Button in the Option() class:
private void cmdApplyActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
hud.setTime(btnTxtTime);
hud.setTemp(btnTxtTemp);
hud.setSurface(btnTxtSurface);
hud.setWeather(btnTxtWeather);
hud.setRadiation(btnTxtRadiation);
dispose();
}
This is a section of the Option() Class.
public class Options extends javax.swing.JFrame {
public String btnTxtTime;
public String btnTxtTemp;
public String btnTxtSurface;
public String btnTxtWeather;
public String btnTxtRadiation;
public static boolean ApplyClicked;
/**
* Creates new form Profile
*/
private HUD hud;
public Options(HUD hud) {
initComponents();
this.hud = hud;
}
This is a method in Option() class:
public String getTime() {
if ("Day".equals(grpTimeOfDay.getSelection())) {
btnTxtTime = "Day";
return this.btnTxtTime;
}
if ("Night".equals(grpTimeOfDay.getSelection())) {
btnTxtTime = "Night";
return this.btnTxtTime;
}
return null;
}
This is how Options() is openned from within HUD():
private void cmdOptionsActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Options o = new Options(hud);
this.getLocation(p);
o.setLocation((int) p.getX() + 100, (int) p.getY() + 100);
o.setVisible(true);
}
This is the start of my HUD() Class:
public abstract class HUD extends javax.swing.JFrame implements Runnable {
private Options o;
private HUD hud;
public HUD(Options o) {
initComponents();
this.o = o;
and this is the method from HUD() which gets the value of the JButtons from Options():
public void setTime(String strTime) {
strTime = o.getTime();
txtTime.setText(strTime);
}
However whenever I click Apply, the options set in Options() are not then set in the TextFields that display them in HUD() like they should be :/
It's difficult to navigate through your very lengthy code sample, however take a look at your cmdApplyActionPerformed() method. You are creating a new HUD() and setting values in it... and then doing absolutely nothing with it.
If you are trying to use the "Apply" button to modify an existing HUD object, your class needs to have a reference to it somewhere. If the HUD is the parent class which creates the Options, try having the Options store a reference to the parent in its constructor. Then, when you perform changes like this in the Options, you can perform them on the parent rather than on a new variable which has no effect.
private HUD parent;
/**
* Creates new form Profile
*/
public Options(HUD parent) {
initComponents();
this.parent = parent;
}
Then, in your event handler, you can have ...
parent.setTime(btnTxtTime);
parent.setTemp(btnTxtTemp);
parent.setSurface(btnTxtSurface);
parent.setWeather(btnTxtWeather);
parent.setRadiation(btnTxtRadiation);
dispose();
From what I understand, HUD is your 'main window' and the users gets to this option frame from that window.
But when you apply, you're setting the properties on a new HUD, not the one you had before.
To fix this, you need a handle to your main window in your config window, so that you can set the properties on it.
in your hud:
ConfigFrame config = new ConfigFrame();
config.setHUD(this);
config.setVisible(true);
In your config
private HUD hud;
public void setHUD(HUD hud){
this.hud = hud;
}
then just leave out the HUD hud = new hud();
CompositeCell let us customize the content of a table cell's in GWT using Java. We can put almost any other group of widget within the table's cell and layout them as we want. Problem is that if we used the html tags to define the layout of the CompositeCell as yet another table (see CompositeCell anonymous class implementation bellow) we loose the event handling for the components of the cell :(.
Running the following code, when we click in the buttons of the cell will realize the popup in response of the event handling IF WE COMMENT the CompositeCell anonymous implementation.
I've been debugging CompositeCell.onBrowserEvent(Context, Element, C, NativeEvent, ValueUpdater) because i think that the definition of the cell layout using HTML table tags breaks the event chain within GWT widgets hierarchy but haven't been successful so far.
Remark: both commented and uncommented versions of the code realize the same GUI layout. This example just intent to show that we loose event handling when customizing cell's content.
public class ActionCellTest implements EntryPoint {
private static final String SERVER_ERROR = "An error occurred while " + "attempting to contact the server. Please check your network "
+ "connection and try again.";
private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
public void onModuleLoad() {
CellTable<Person> table = new CellTable<ActionCellTest.Person>();
final List<HasCell<Person, ?>> cells = new LinkedList<HasCell<Person, ?>>();
cells.add(new HasCellImpl("first name", new ActionCell.Delegate<Person>() {
#Override
public void execute(Person object) {
Window.alert(object.getFirstName());
}
}));
cells.add(new HasCellImpl("last name", new ActionCell.Delegate<ActionCellTest.Person>() {
#Override
public void execute(Person object) {
Window.alert(object.getLastName());
}
}));
CompositeCell<Person> cell = new CompositeCell<Person>(cells) {
#Override
public void render(Context context, Person value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<table><tbody><tr>");
for (HasCell<Person, ?> hasCell : cells) {
render(context, value, sb, hasCell);
}
sb.appendHtmlConstant("</tr></tbody></table>");
}
#Override
protected <X> void render(Context context, Person value, SafeHtmlBuilder sb, HasCell<Person, X> hasCell) {
Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<td>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</td>");
}
#Override
protected Element getContainerElement(Element parent) {
return parent.getFirstChildElement().getFirstChildElement().getFirstChildElement();
}
};
table.addColumn(new TextColumn<ActionCellTest.Person>() {
#Override
public String getValue(ActionCellTest.Person object) {
return object.getFirstName() + " " + object.getLastName();
}
}, "name");
table.addColumn(new Column<Person, Person>(cell) {
#Override
public Person getValue(ActionCellTest.Person object) {
return object;
}
}, "composite");
LinkedList<Person> data = new LinkedList<ActionCellTest.Person>();
data.add(new Person("Amy", "Reed"));
data.add(new Person("Tim", "Gardner"));
table.setRowData(data);
RootPanel.get().add(table);
}
private class HasCellImpl implements HasCell<Person, Person> {
private ActionCell<Person> fCell;
public HasCellImpl(String text, Delegate<Person> delegate) {
fCell = new ActionCell<Person>(text, delegate);
}
#Override
public Cell<Person> getCell() {
return fCell;
}
#Override
public FieldUpdater<Person, Person> getFieldUpdater() {
return null;
}
#Override
public Person getValue(Person object) {
return object;
}
}
private class Person {
private String fFirstName;
private String fLastName;
public Person(String first, String last) {
fFirstName = first;
fLastName = last;
}
public String getFirstName() {
return fFirstName;
}
public String getLastName() {
return fLastName;
}
}
}
This is a known issue which will be fixed in the upcoming GWT 2.5 (a matter of weeks): http://code.google.com/p/google-web-toolkit/issues/detail?id=5714
(in the mean time, you can run off trunk or try backporting the change)