I created a TableTree that contains object of class Component that has a boolean property "selected".
I want to hide the rows from the table where the rows component is not selected.
I tried this:
componentTree.setRowFactory(new Callback<TreeTableView<Component>, TreeTableRow<Component>>() {
#Override
public TreeTableRow<Component> call(TreeTableView<Component> param) {
TreeTableRow<Component> row = new TreeTableRow<Component>() {
#Override
protected void updateItem(Component component, boolean empty) {
if(!empty) {
if (!component.isSelected()) {
setVisible(false);
setManaged(false);
System.out.println("hide");
} else {
setVisible(true);
setManaged(true);
System.out.println("show");
}
}
}
};
return row;
}
});
On system.out I can see a lot of "show" and "hide" messages, but this doesn't affect the table, all rows are shown as before.
Any idea on this topic?
Thanks!
I used eclipse's fx.ui.controls library for the same achieve the same goal before.
<dependency>
<groupId>at.bestsolution.eclipse</groupId>
<artifactId>org.eclipse.fx.ui.controls</artifactId>
<version>2.2.0</version>
</dependency>
The library provides a class: FilterableTreeItem<T> under the tree package. This class was designed to be used in cases like yours. You can provide a Predicate to the root of the tree and the items will get hidden when the value given changes:
// Children
final FilterableTreeItem<Component> childNode1 = new FilterableTreeItem<>(component1);
final FilterableTreeItem<Component> childNode2 = new FilterableTreeItem<>(component2);
final FilterableTreeItem<Component> childNode3 = new FilterableTreeItem<>(component3);
// Root
final FilterableTreeItem<Component> root = new FilterableTreeItem<>(rootComponent);
root.getInternalChildren().setAll(childNode1, childNode2, childNode3);
root.setPredicate((parent, value) -> value.isSelected());
// TreeTableView
final TreeTableView<Component> treeTableView = new TreeTableView<>(root);
Note that you have to use getInternalChildren() to add children and the default getChildren().
FilterableTreeItem<T> also provides a predicateProperty() that you can bind to another property in case you need to update the how items are shown or hidden.
Another advatage of this class is that it shows the whole path up to the root of the items matching that predicate.
Related
I have a ComboBox called projectRequirementComboBox that is dependent from projectComboBox, from where I get the list to show in the dropdown in projectRequirementComboBox, but I want to do something like: when a user changes the project I want to empty projectRequirementComboBox, to be more clear none of the item will not be selected I am doing this right now but still my projectRequirementComboBox has the old value, I don't know what am I missing .I am using vaadin.version 8.0.7 .
private void refreshProjectRequirementCombobox()
{
List<ProjectRequirement> projectRequirements = new ArrayList<>();
if (projectComboBox.getValue() != null)
{
projectRequirements = projectRequirementService.findCurrentProjectRequirements(projectComboBox.getValue().getProjectId());
}
projectRequirementComboBox.setItems(projectRequirements);
projectRequirementComboBox.setValue(null);
}
private void loadProjectRequirement(Project project)
{
List<ProjectRequirement> projectRequirements = new ArrayList<>();
if (project != null)
{
projectRequirements = projectRequirementService.findCurrentProjectRequirements(project.getProjectId());
}
projectRequirementComboBox.setItems(projectRequirements);
}
I call refreshProjectRequirementCombobox here.
projectComboBox.addValueChangeListener(event ->
{
refreshProjectRequirementCombobox();
loadRejectReason();
});
Normally this should work. I created a minimum example with two ComboBoxes "main" and "dependent". The selection of the dependent ComboBox depends on the selection of the main ComboBox. Therefore there is a ValueChangeListener on the main ComboBox that resets the items and the selected value of the dependent ComboBox. When you start the application you see that the offered items of the dependent ComboBox change and that none of these new items is selected.
I think you have to post more of your code (where do you call refreshProjectRequirementCombobox from?) to see what you are doing different.
Here is my example minimum project code:
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
final ComboBox<String> main = new ComboBox<>();
final ComboBox<String> dependent = new ComboBox<>();
final Map<String, String[]> dependentsByMain = new HashMap<>();
dependentsByMain.put("A", new String[]{"AA", "AB", "AC"});
dependentsByMain.put("B", new String[]{"BA", "BB", "BC"});
dependentsByMain.put("C", new String[]{"CA", "CB", "CC"});
List<String> mainItems = new ArrayList<>(dependentsByMain.keySet());
main.setItems(mainItems);
dependent.setItems(Arrays.asList("Test1", "Test2", "Test3"));
dependent.setValue("Test1");
main.addValueChangeListener((HasValue.ValueChangeListener<String>) valueChangeEvent -> {
if (valueChangeEvent.getValue() != null) {
dependent.setItems(dependentsByMain.get(valueChangeEvent.getValue()));
dependent.setValue(null);
}
});
layout.addComponents(main, dependent);
setContent(layout);
}
UPDATE:
Have a look at Srinivasan Sekar's answer and its comments. This is a bug in the used version (8.0.7) which seems to be fixed in version 8.5 (according to https://github.com/vaadin/framework/issues/9047#issuecomment-437864866). I tried my example code with version 8.7.1 so it works. With version 8.0.7 it doesn't.
So the main solution is to update the used Vaadin version. As a workaround (when unable to upgrade the Vaadin version) you FIRST have to set the ComboBox's value to null and THEN set the new items. So in my example the ValueChangeListener must look like:
main.addValueChangeListener((HasValue.ValueChangeListener<String>) valueChangeEvent -> {
if (valueChangeEvent.getValue() != null) {
dependent.setValue(null);
dependent.setItems(dependentsByMain.get(valueChangeEvent.getValue()));
}
});
There is open issue in Vaadin https://github.com/vaadin/framework/issues/9566 which refers to https://github.com/vaadin/framework/issues/2813
Going through the issue I found that by creating a custom combobox you can fix the issue:
public class ClearableComboBox<T> extends ComboBox<T> {
public ClearableComboBox(String in) {
super(in);
}
protected void setSelectedFromServer(T item) {
String key = itemToKey(item);
T oldSelection = getSelectedItem().orElse(getEmptyValue());
doSetSelectedKey(key);
fireEvent(new SingleSelectionEvent<>(ClearableComboBox.this, oldSelection, false));
}
}
Additionally, make sure to call setValue before calling setItems to clear items.
cmb.setValue(null);
cmb.setItems(aEmptyCollection);
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 am building a preference page in Eclipse by extending the FieldEditorPreferencePage class. this page contains 2 fields : 1 BooleanFieldEditor (checkbox) and 1 FileFieldEditor. I would like to disable/enable the file field following the checkbox value.
I went up to something like this (some obvious code is not displayed):
public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public static final String PREF_KEY_1 = "checkBoxPref";
public static final String PREF_KEY_2 = "filePref";
private FileFieldEditor pathField;
private BooleanFieldEditor yesOrNoField;
private Composite pathFieldParent;
#Override
protected void createFieldEditors() {
this.yesOrNoField = new BooleanFieldEditor(PREF_KEY_1, "Check this box!", getFieldEditorParent());
this.pathFieldParent = getFieldEditorParent();
this.pathField = new FileFieldEditor(PREF_KEY_2, "Path:", this.pathFieldParent);
addField(this.yesOrNoField);
addField(this.pathField);
boolean isChecked = getPreferenceStore().getBoolean(PREF_KEY_1);
updatePathFieldEnablement(! isChecked);
}
/**
* Updates the fields according to entered values
*/
private void updatePathFieldEnablement(boolean enabled) {
this.pathField.setEnabled(enabled, this.pathFieldParent);
}
#SuppressWarnings("boxing")
#Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.VALUE) && event.getSource() == this.yesOrNoField) {
updatePathFieldEnablement(! (boolean) event.getNewValue());
}
super.propertyChange(event);
}
}
My question is about this second parameter in FieldEditor#setEnabled. This parameter is the parent composite of the FieldEditor's controls ("Used to create the controls if required" says the javadoc) . At first, I set the value with the return of getFieldEditorParent but then I got an exception "Different parent". So I ended storing it (cf. this.pathFieldParent) and give it back to setEnabled and it works (or it seems to work).
But I am not sure I am doing well, especially because I had to create a member in my class that means nothing to it (and I would have to create many of them if I had many fields to enable/disable).
Do you think I am doing well or is there a better way to provide this parent ? And could you explain to me why *setEnabled" needs it ?
Thanks.
You are using the default FLAT layout for the preference page. When this layout is used each call to getFieldEditorParent generates a new Composite so you have to make just one call and remember the correct parent. Using the GRID layout getFieldEditorParent always returns the same parent. This is the actual code:
protected Composite getFieldEditorParent() {
if (style == FLAT) {
// Create a new parent for each field editor
Composite parent = new Composite(fieldEditorParent, SWT.NULL);
parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return parent;
}
// Just return the parent
return fieldEditorParent;
}
setEnabled does sometimes create a new Label control so it needs to know the correct parent Composite.
While using EditingSupport for a treeColumn in a TreeViewer, Is there any way i can just reflect the Changes in the View instead of changing the model and then using getViewer().update(element,null);
Detail:
I want to achieve the following functionality:
Show a tree View with |Object| (ComboBox)property|
Upon selection and clicking on the button i want to show user the summary of changes and then upon clicking confirm i want to apply those changes to the model (Object)
I am using a TreeViewer, Within that i have a column with EditingSupport Enabled.
Whenever I select a value from the ComboBox and click somewhere else (lostFocus kind of ) the Value sets to default.
I have figured out that after SetValue() is called the TreeLabelProvider is again called(using debug points)
Is there any way i can just reflect the Changes in the View instead of changing the model and using getViewer().update(element,null);
Some FYIs :
Package Object contains multiple versions
ContentProvider does the job to fetch the object
LabelProvider gets all the Versions from the package(String[]) and shows the first one.
//Code to Create the UI
// blah
TreeViewerColumn column2 = new TreeViewerColumn(treeViewer, SWT.LEFT);
column2.getColumn().setText("Version");
column2.getColumn().setWidth(130);
treeViewer.setLabelProvider(new PackageUpdateTreeLabelProvider());
EditingSupport exampleEditingSupport = new OperationEditingSupport(
column2.getViewer());
column2.setEditingSupport(exampleEditingSupport);
OperationEditingSupport Class
private class OperationEditingSupport extends EditingSupport {
private ComboBoxCellEditor cellEditor = null;
private OperationEditingSupport(ColumnViewer viewer) {
super(viewer);
cellEditor = new ComboBoxCellEditor(
((TreeViewer) viewer).getTree(), new String[] {},
SWT.READ_ONLY);
}
#Override
protected CellEditor getCellEditor(Object element) {
// TODO Auto-generated method stub
if (element instanceof IPackageInfo) {
IPackageInfo pkg = (IPackageInfo) element;
cellEditor.setItems(PackageMgrUtil.getInstance().getVersions(
(IdmPackage) pkg, false, true));
return cellEditor;
}
return null;
}
#Override
protected boolean canEdit(Object element) {
return true;
}
#Override
protected Object getValue(Object element) {
// TODO Auto-generated method stub
return 0;
}
#Override
protected void setValue(Object element, Object value) {
/* only set new value if it differs from old one */
}
}
***************************************************
When i click on the cell of column2 i get the combo box but when i select something and move the focus somewhere else.It again shows the default Value
on debuging i found that :
it agains calls the label Provider which fetches all the Version of the package and then shows the first one hence I can not see any change.
what i want is that it should keep the selection intact without changing the underlying object.
thanks for the help.
Figured it out.
following code added to the SetValue() method solves it.
m_tree = (Tree)getViewer.getControl();
TreeItem[] ti = m_tree.getSelection();
CCombo c = ((CCombo)cellEditor.getControl());
String str = c.getItem(c.getSelectionIndex());
ti[0].setText(1, str );
I have a List, that included JPA Entity objects of a certain type. Their reference String values are displayed in a JList for the user to see.
I want my user to be able to select filters as JCheckBoxes in the UI such as 'only from Client x', or 'only of Type x' and dynamically filter the Entity List.
I had thought to just keep a copy of static List completeList; and static List filteredList; and then just run individual filter methods each time a new filter is selected in the UI to update filteredList, which would work fine until you have to un-select a single filter and leave the others selected (at which point it all falls apart).
Every situation I think through fall apart at one point or another, usually when trying to select multiple filters of from one Menu.
An example of my thought pattern that checks all the filters to determine what needs to go in the new JList;
public static void filterList(){
List filteredList = new ArrayList<Job>(StoredDataClass.completeList);
if(clientSmithsCheckBox.isSelected()){
for(Job job : filteredList){
if(!job.getClient.equals(clientSmithsCheckBox.getText())){
filteredList.remove(job);
}
}
}
....... // Check other filters here etc.
if(clientBobAndCoCheckBox.isSelected()){
for(Job job : filteredList){
if(!job.getClient.equals(clientBobAndCoCheckBox.getText())){
filteredList.remove(job);
}
}
}
Even if clientBobAndCoCheckBox is selected, no jobs with that client will show in the final list, because we already removed them all because another client was already selected. Now, we could add to the list instead but we would face similar problems of having add stuff that shouldn't be there etc.
This is obviously possible, because this type of filtering system is common practice (example, excel). Although this is more of a design question, how can I achieve this?
Here's a short (and raw!) example of how you could organize your logic. It's in the context of SwingX (which supports sorting/filtering of a JList just the same way as a JTable) because I'm lazy - but you can apply it to your own environment easily.
Think of your criteria as a collection of filters which can be on or off, and then combine them with OR (if one or more is selected) or turn off if none is selected. The sole "trick" is to evaluate all of the checkboxes' states wheneven one of them is changed:
final JXList list = new JXList(new DefaultComboBoxModel(Locale.getAvailableLocales()));
list.setAutoCreateRowSorter(true);
final List<RowFilter> filters = new ArrayList<>();
filters.add(new MyRowFilter("de"));
filters.add(new MyRowFilter("ar"));
final List<JCheckBox> boxes = new ArrayList<>();
ActionListener l = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<RowFilter<Object, Object>> orCandidates = new ArrayList<>();
for (int i = 0; i < boxes.size(); i++) {
if (boxes.get(i).isSelected())
orCandidates.add(filters.get(i));
}
RowFilter<Object, Object> or = orCandidates.isEmpty() ? null :
RowFilter.orFilter(orCandidates);
list.setRowFilter(or);
}
};
JCheckBox first = new JCheckBox("de");
first.addActionListener(l);
boxes.add(first);
JCheckBox second = new JCheckBox("ar");
second.addActionListener(l);
boxes.add(second);
JComponent content = new JPanel();
content.add(new JScrollPane(list));
for (JCheckBox box : boxes) {
content.add(box);
}
showInFrame(content, "filters");
// just for completeness, the custom RowFilter
public static class MyRowFilter extends RowFilter {
private String text;
public MyRowFilter(String text) {
this.text = text;
}
#Override
public boolean include(Entry entry) {
Locale locale = (Locale) entry.getValue(0);
return locale.getLanguage().contains(text);
}
}