I am creating application in javaFX, Where I have used Treeview
with CheckBoxTreeItem of String as its nodes.
I want to get all checked item in the treeview , How do I achieve that?
Heres code :
private TreeView<String> treeView_businessAreas;
Set<String> businessAreas = config.getBusinessAreas();
CheckBoxTreeItem<String> item = null;
for (String businessArea : businessAreas) {
item = new CheckBoxTreeItem<>(businessArea);
root.getChildren().add(item);
}
treeView_businessAreas.setRoot(root);
This is the code that you need:
treeView_businessAreas.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<TreeItem>() {
#Override
public void onChanged(Change<? extends TreeItem> change) {
ObservableList<TreeItem<String>> allSelectedItems = (ObservableList<TreeItem<String>>) treeView_businessAreas.getSelectionModel().getSelectedItems();
//DO SOMETHING HERE WITH THE SELECTED ITEMS
}
});
Related
I need to call an API, get a response, add items to ComboBox and update the view in the plugin.
Attached is the image of the combobox
I need to update the thread Ids as they load from an API. My custom Combobox for this is as shown below. I am not sure how to update the custom component from outside the class. Any help?
public class MyComboBox extends AnAction implements CustomComponentAction {
#Override
public void actionPerformed(#NotNull AnActionEvent e) {
}
#Override
public #NotNull JComponent createCustomComponent(#NotNull Presentation presentation, #NotNull String place) {
ComboBox<String> jComboBox = new ComboBox<>();
jComboBox.setMinLength(100);
jComboBox.addItem("Thread Id: " + UUID.randomUUID().toString());
jComboBox.addItem("Thread Id: " + UUID.randomUUID().toString());
jComboBox.setEnabled(true);
return jComboBox;
}
}
I needed a reference to the already instantiated combobox.
I got it with
MyComboBox myComboBox = (MyComboBox) ActionManager.getInstance().getAction("searchThread")
I added another method in MyComboBox:
public void updateUI(List<String> ids) {
this.ids = ids;
String[] array = this.ids.toArray(new String[0]);
jComboBox.setModel(new DefaultComboBoxModel<>(array));
jComboBox.setSelectedIndex(0);
jComboBox.updateUI();
}
and used:
myComboBox.updateUI(newList);
That solved my problem.
I'm attempting to bind between a ListView where more than one item can be selected and a ObjectProperty. When an item is selected, I want to filter a TableView column accordingly:
With the two lower filters (Components, Details), I do the binding like this:
ObjectProperty<Predicate<Log>> detailsSearchFilter = new SimpleObjectProperty<>();
TextField detailsSearchField = new TextField();
detailsSearchField.setPromptText("e.g. finished initializing");
detailsSearchFilter.bind(Bindings.createObjectBinding(() ->
log -> log.getDetails().toLowerCase().contains(detailsSearchField.getText().toLowerCase()),
detailsSearchField.textProperty()
));
Then later adding the logical operator method and() in order to be able to combine all filters:
logFilteredList.predicateProperty().bind(Bindings.createObjectBinding(() ->
detailsSearchFilter.get().and(componentSearchFilter.get()).and(sourceFilter.get()),
detailsSearchFilter, componentSearchFilter, sourceFilter
));
For the other two ListView filters, I was thinking to do something like this:
private final static String[] sources = new String[]{"ECS","IISNode","PrismWebServer"};
ListView<String> sourceList = new ListView<>();
ObservableList sourceItems = FXCollections.observableArrayList(sources);
sourceList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
sourceList.getItems().addAll(sources);
ListView<String> selected = new ListView<>();
sourceList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
selected.setItems(sourceList.getSelectionModel().getSelectedItems());
System.out.println(Arrays.toString(selected.getItems().toArray()));
});
So the selected list now includes all values that are currently selected.
How do I bind the sourceFilter with all values of the selected list?
I was thinking of iterating through each value in the list and binding this way:
sourceFilter.bind(Bindings.createObjectBinding(() ->
log -> log.getSource().toLowerCase().contains(selected.getItems().get(i).toLowerCase()),
sourceList.selectionModelProperty()
));
But it seems not very elegant and I'm not sure I'm using the selectionModelProperty in the right way.
The selectionModelProperty only triggers changes when the selectionModel is replaced which usually doesn't happen. The dependency should be to sourceList.getSelectionModel().getSelectedItems().
Furthermore the way I understand it you should query, if the Log's source is among the selected items, not if the source string contains some part of the string at some position in the selected items list.
Also note that using a ListView just for storing the data should be avoided and for large lists it's much more efficient to do the contains checks on a Set instead of a List.
You could use code like this:
ObjectBinding<Predicate<Log>> binding = new ObjectBinding<Predicate<String>>() {
private final Set<String> strings = new HashSet<>();
{
sourceList.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<String>() {
#Override
public void onChanged(ListChangeListener.Change<? extends String> c) {
boolean changed = false;
// modify set on selection change
while (c.next()) {
if (c.wasRemoved()) {
changed = true;
c.getRemoved().stream().map(String::toLowerCase).forEach(strings::remove);
}
if (c.wasAdded()) {
changed = true;
c.getAddedSubList().stream().map(String::toLowerCase).forEach(strings::add);
}
}
if (changed) {
invalidate();
}
}
});
}
#Override
protected Predicate<Log> computeValue() {
return log -> strings.contains(log.getSource().toLowerCase());
}
};
sourceFilter.bind(binding);
I have two ComboBoxes in my editable Grid where the second ComboBox based on the first one. So for example you would have Car Make and then Car Model. When you change the make combobox the model combobox then changes accordingly.
With that in mind I have:
ComboBox<String> makeComboBox = new ComboBox<String>();
ComboBox<String> modelComboBox = new ComboBox<String>();
Specifically:
grid.addColumn(CarRental::getPerson)
.setEditorBinding(binder.forField(personComboxBox).bind(CarRental::getPerson, CarRental::setPerson));
grid.addColumn(CarRental::getMake)
.setEditorBinding(binder.forField(makeComboxBox).bind(CarRental::getMake, CarRental::setMake));
grid.addColumn(CarRental::getModel)
.setEditorBinding(binder.forField(modelComboxBox).bind(CarRental::getModel, CarRental::setModel));
The key here is that I want the modelComboBox to change if the makeComboBox is changed. In other words if you select Honda then I want the model ComboBox to change to Fit, Civic, Accord, and so on. To do this I add a SelectionListener (but it could also be a ValueChangeListener, it doesn't matter, the effect is still the same).
Specifcally I have:
makeComboBox.addSelectionListener(event ->
{
modelComboBox.clear();
modelComboBox.setItems(getModelsBasedOnMake(makeComboBox.getValue()));
// Assuming someone has just edited the make value,
// say changed from Toyota to Honda, then I want the model selected to be empty
});
Because the ComboBox can be different I've added some logic to update the components on theOpenListenerfor the Grid Editor. Specifically I do:
grid.getEditor().addOpenListener(open ->
{
...
CarRental selectedCarRental = (CarRental)event.getBean();
makeComboBox.setItems(makeList);
modelComboBox.setItems(getModelsBasedOnMake(carRental.getMake()));
});
The problem here is that the modelComBoxbox tends to be unselected because if you look at it there's no guarantee which value it will be because there is a conflict.
I looked at temporarily disabling the selectionListener but this all the remove listeners have been deprecated with Vaadin 8. Therefore how can I setup the grid to be able to edit both the car make and model in the grid?
I tried it by a simple example. Looks ok for me. What is the exact problem? (I don't really get your sentence "because if you look at it there's no guarantee which value it will be because there is a conflict.")
#SpringUI
public class VaadinUI extends UI {
#Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
ComboBox<String> cmb1 = new ComboBox<>();
ComboBox<String> cmb2 = new ComboBox<>();
cmb1.setItems("1", "2", "3");
cmb1.addSelectionListener(event -> {
cmb2.clear();
cmb2.setItems(getCmb2Content(event.getValue()));
});
Grid<MyBean> grid = new Grid<>();
grid.setWidth("800px");
grid.setHeightByRows(10);
grid.addColumn(System::identityHashCode).setCaption("ID");
grid.addColumn(MyBean::getProp1).setCaption("Prop 1")
.setEditorBinding(grid.getEditor().getBinder().forField(cmb1).bind(MyBean::getProp1, MyBean::setProp1));
grid.addColumn(MyBean::getProp2).setCaption("Prop 2")
.setEditorBinding(grid.getEditor().getBinder().forField(cmb2).bind(MyBean::getProp2, MyBean::setProp2));
grid.setItems(new MyBean(), new MyBean(), new MyBean());
grid.getEditor().setEnabled(true);
layout.addComponent(grid);
setContent(layout);
}
private List<String> getCmb2Content(String cmb1Content) {
return Arrays.asList(cmb1Content + "1", cmb1Content + "2", cmb1Content + "3");
}
}
public static class MyBean {
private String prop1;
private String prop2;
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
public String getProp2() {
return prop2;
}
public void setProp2(String prop2) {
this.prop2 = prop2;
}
}
I want to filter GXT ComboBox Store. for example if I type 'st' in combobox I want combobox to show only values that contain 'st'
Here is my implementation
combo = new ComboBox<MerchantDTO>(store, label);
StoreFilter<MerchantDTO> filter = new StoreFilter<MerchantDTO>() {
#Override
public boolean select(Store<MerchantDTO> store, MerchantDTO parent, MerchantDTO item) {
boolean canView = (item.getName() != null && item.getName().toLowerCase().contains(combo.getText().toLowerCase()));
return canView;
}
};
store.setEnableFilters(true);
store.addFilter(filter);
This filter works and shows correct values, But combobox's dropdown list does not open automatically. I have to click on combobox manually to open dropdown list and see filtered results. I am using GXT 3.1.0 and GWT 2.7.0
I tried using combo.expand(); function but it didnt open dropdown list.
Any help would be appreciated.
I found solution. Here is sample how to add custom filter to GXT (version 3.1.0) ComboBox
1) Create class which extends ListStore and add String variable for user input text
public abstract class XListStore<M> extends ListStore<M> {
private String userText;
public XListStore(ModelKeyProvider<? super M> keyProvider) {
super(keyProvider);
}
#Override
protected boolean isFilteredOut(M item) {
return filter(item);
}
public abstract boolean filter(M item);
public String getUserText() {
return userText;
}
public void setUserText(String userText) {
this.userText = userText;
}
}
2) Initialize custom list store and implement filter method
XListStore<SampleDTO> store = new XListStore<SampleDTO>(new ModelKeyProvider<SampleDTO>() {
#Override
public String getKey(SampleDTO item) {
return item.getId();
}
}) {
public boolean filter(SampleDTO item) {
boolean result = false;
//Write you filter logic here
return result;
}
};
store.setEnableFilters(true);
3) Initialize ComboBox and add Key up handler
ComboBox<SampleDTO> comboBox = new ComboBox<SampleDTO>(store, label);
comboBox.addKeyUpHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent event) {
store.setUserText(comboBox.getText());
}
});
Done. Now ComboBox will filter store according to user input and will open dropdown window automatically
Basically, I wanted to know if I could create a tree and custom it on javaFX...
I tried to do it, but couldn't do anything so far with this code...
public class Main{
......
public Main() throws Exception{
......
// TreeView created
TreeView tv = (TreeView) fxmlLoader.getNamespace().get("treeview");
TreeItem<String> rootItem = new TreeItem<String>("liss");
rootItem.setExpanded(true);
tv.setRoot(rootItem);
/*for (int i = 1; i < 6; i++) {
TreeItem<String> item = new TreeItem<String> ("Message" + i);
rootItem.getChildren().add(item);
}
TreeItem<String> item = new TreeItem<String> ("MessageWoot");
rootItem.getChildren().add(item);
*/
//tv.setEditable(true);
tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
#Override
public TreeCell<String> call(TreeView<String> arg0) {
// custom tree cell that defines a context menu for the root tree item
return new MyTreeCell();
}
});
stage.show();
}
//
private static class MyTreeCell extends TextFieldTreeCell<String> {
private ContextMenu addMenu = new ContextMenu();
public boolean clickedFirstTime = false;
public MyTreeCell() {
// instantiate the root context menu
MenuItem addMenuItem = new MenuItem("Expand");
addMenu.getItems().add(addMenuItem);
addMenuItem.setOnAction(new EventHandler() {
public void handle(Event t) {
TreeItem n0 =
new TreeItem<String>("'program'");
TreeItem n1 =
new TreeItem<String>("<identifier>");
TreeItem n2 =
new TreeItem<String>("body");
getTreeItem().getChildren().add(n0);
getTreeItem().getChildren().add(n1);
getTreeItem().getChildren().add(n2);
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
// if the item is not empty and is a root...
//if (!empty && getTreeItem().getParent() == null && this.clickedFirstTime) {
System.out.println("UPDATEITEM -> clickedFirstTime : "+this.clickedFirstTime);
if (!this.clickedFirstTime) {
System.out.println("WOOT");
setContextMenu(addMenu);
this.clickedFirstTime = true;
}
}
}
And I'm questioning myself if this is the right "technology" which will solve what I'm trying to do...
What's my objective in this?
Firstly, I'm looking to add or delete a treeItem. I must say that a certain treeItem may be added only once or any N times, like a restriction (for example: treeItem < 6 for a certain level scope and a certain path of the root of tree view).
Secondly, make some treeItem editable and others not editable! When it is Editable, you may pop up something for the user in order to insert some input for example!
Is it possible ?
I saw the tutorial from https://docs.oracle.com/javafx/2/ui_controls/tree-view.htm#BABJGGGF but I'm really confused with this tutorial ... I don't really understand the cell factory mechanism... The fact that he does apply to TreeView when i want only a certain TreeItem... Or how could I control that effect/behaviour ?
I mean, I'm really really lost with TreeView. Probably, TreeView isn't what I'm looking for ...
P.S.: I know that I cannot apply any visual effect or add menus to a tree items and that i use a cell factory mechanism to overcome this obstacle. Just I don't understand the idea and how could I do it !
Sure this is the right "technology", if you want to use JavaFX. You should probably use a more complex type parameter for TreeItem however. You can use your a custom TreeCell to allow the desired user interaction.
This example allows adding children and removing nodes via context menu (unless the content is "nocontext") as well as editing the content (as long as the content is not "noedit"); on the root node the delete option is disabled:
tv.setEditable(true);
tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
private final MyContextMenu contextMenu = new MyContextMenu();
private final StringConverter converter = new DefaultStringConverter();
#Override
public TreeCell<String> call(TreeView<String> param) {
return new CustomTreeCell(contextMenu, converter);
}
});
public class CustomTreeCell extends TextFieldTreeCell<String> {
private final MyContextMenu contextMenu;
public CustomTreeCell(MyContextMenu contextMenu, StringConverter<String> converter) {
super(converter);
if (contextMenu == null) {
throw new NullPointerException();
}
this.contextMenu = contextMenu;
this.setOnContextMenuRequested(evt -> {
prepareContextMenu(getTreeItem());
evt.consume();
});
}
private void prepareContextMenu(TreeItem<String> item) {
MenuItem delete = contextMenu.getDelete();
boolean root = item.getParent() == null;
if (!root) {
delete.setOnAction(evt -> {
item.getParent().getChildren().remove(item);
contextMenu.freeActionListeners();
});
}
delete.setDisable(root);
contextMenu.getAdd().setOnAction(evt -> {
item.getChildren().add(new TreeItem<>("new item"));
contextMenu.freeActionListeners();
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContextMenu("nocontext".equals(item) ? null : contextMenu.getContextMenu());
setEditable(!"noedit".equals(item));
}
}
}
public class MyContextMenu {
private final ContextMenu contextMenu;
private final MenuItem add;
private final MenuItem delete;
public MyContextMenu() {
this.add = new MenuItem("add child");
this.delete = new MenuItem("delete");
this.contextMenu = new ContextMenu(add, delete);
}
public ContextMenu getContextMenu() {
return contextMenu;
}
public MenuItem getAdd() {
return add;
}
public MenuItem getDelete() {
return delete;
}
/**
* This method prevents memory leak by setting all actionListeners to null.
*/
public void freeActionListeners() {
this.add.setOnAction(null);
this.delete.setOnAction(null);
}
}
Of course more complex checks can be done in the updateItem and prepareContextMenu and different user interactions can be supported (TextFieldTreeCell may not be the appropriate superclass for you; You could use a "normal" TreeCell and show a different stage/dialog to edit the item when the user selects a MenuItem in the context menu).
Some clarification about cell factories
Cell factories are used to create the cells in a class that displays data (e.g. TableColumn, TreeView, ListView). When such a class needs to display content, it uses it's cell factory to create the Cells that it uses to display the data. The content displayed in such a cell may be changed (see updateItem method).
Example
(I'm not 100% sure this is exactly the way it's done, but it should be sufficiently close)
A TreeView is created to display a expanded root node with 2 non expanded children.
The TreeView determines that it needs to display 3 items for the root node and it's 2 children. The TreeView therefore uses it's cell factory to creates 3 cells and adds them to it's layout and assigns the displayed items.
Now the user expands the first child, which has 2 children of it's own. The TreeView determines that it needs 2 more cells to display the items. The new cells are added at the end of the layout for efficiency and the items of the cells are updated:
The cell that previously contained the last child is updated and now contains the first child of the first item.
The 2 newly added cells are updated to contain the second child of the first child and the second child of the root respecitvely.
So I decided to eliminate TreeView (because the documentation is so trash...) and, instead, I decided to implement a Webview !
Why ?
Like that, I could create an HTML document and use jstree (jquery plugin - https://www.jstree.com ) in there. It is a plugin which will create the treeview basically.
And the documentation is ten time better than treeview oracle documentation unfortunately.
Also, the possibility in creating/editing the tree with jstree is better.
Which concludes me that it was the best solution that I could figure out for me.
Also, whoever who will read me, I did a bridge with the webview and my javafx application ! It is a connection between my HTML document and the java application (Read more here - https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx).
Hope It will help more people.