Is there any possible way to listen or to override the default TableMenuButton setonAction?
Something like this?
TreeTableView ttv = new TreeTableView();
ttv.setTableMenuButtonVisible(true);
ttv.setOnMouseClicked((MouseEvent event) -> {
....
});
I would like to know which column has been set to visible or invisible.
Any help is greatly appreciated.
I created an example about how to adapt the TableView's menu button. The TreeTableView is just similar. What you need to do is to get the ContextMenu. You can get it either by reflection or by using a lookup. Once you have it, you can do whatever you want with it.
I also filed a change request so that the context menu becomes accessible since the current implementation isn't satisfactory.
Here's the modified code of the lookup version:
public class TableUtils {
/**
* Make table menu button visible and replace the context menu with a custom context menu via reflection.
* The preferred height is modified so that an empty header row remains visible. This is needed in case you remove all columns, so that the menu button won't disappear with the row header.
* IMPORTANT: Modification is only possible AFTER the table has been made visible, otherwise you'd get a NullPointerException
* #param tableView
*/
public static void addCustomTableMenu( TreeTableView tableView) {
// enable table menu
tableView.setTableMenuButtonVisible(true);
// replace internal mouse listener with custom listener
setCustomContextMenu( tableView);
}
private static void setCustomContextMenu( TreeTableView table) {
TreeTableViewSkin<?> tableSkin = (TreeTableViewSkin<?>) table.getSkin();
// get all children of the skin
ObservableList<Node> children = tableSkin.getChildren();
// find the TableHeaderRow child
for (int i = 0; i < children.size(); i++) {
Node node = children.get(i);
if (node instanceof TableHeaderRow) {
TableHeaderRow tableHeaderRow = (TableHeaderRow) node;
// setting the preferred height for the table header row
// if the preferred height isn't set, then the table header would disappear if there are no visible columns
// and with it the table menu button
// by setting the preferred height the header will always be visible
// note: this may need adjustments in case you have different heights in columns (eg when you use grouping)
double defaultHeight = tableHeaderRow.getHeight();
tableHeaderRow.setPrefHeight(defaultHeight);
for( Node child: tableHeaderRow.getChildren()) {
// child identified as cornerRegion in TableHeaderRow.java
if( child.getStyleClass().contains( "show-hide-columns-button")) {
// get the context menu
ContextMenu columnPopupMenu = createContextMenu( table);
// replace mouse listener
child.setOnMousePressed(me -> {
// show a popupMenu which lists all columns
columnPopupMenu.show(child, Side.BOTTOM, 0, 0);
me.consume();
});
}
}
}
}
}
/**
* Create a menu with custom items. The important thing is that the menu remains open while you click on the menu items.
* #param cm
* #param table
*/
private static ContextMenu createContextMenu( TreeTableView table) {
ContextMenu cm = new ContextMenu();
// create new context menu
CustomMenuItem cmi;
// select all item
Label showAll = new Label("Show all");
showAll.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
for (Object obj : table.getColumns()) {
((TreeTableColumn<?, ?>) obj).setVisible(true);
}
}
});
cmi = new CustomMenuItem(showAll);
cmi.setHideOnClick(false);
cm.getItems().add(cmi);
// deselect all item
Label hideAll = new Label("Hide all");
hideAll.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
for (Object obj : table.getColumns()) {
((TreeTableColumn<?, ?>) obj).setVisible(false);
}
}
});
cmi = new CustomMenuItem(hideAll);
cmi.setHideOnClick(false);
cm.getItems().add(cmi);
// separator
cm.getItems().add(new SeparatorMenuItem());
// menu item for each of the available columns
for (Object obj : table.getColumns()) {
TreeTableColumn<?, ?> tableColumn = (TreeTableColumn<?, ?>) obj;
CheckBox cb = new CheckBox(tableColumn.getText());
cb.selectedProperty().bindBidirectional(tableColumn.visibleProperty());
cmi = new CustomMenuItem(cb);
cmi.setHideOnClick(false);
cm.getItems().add(cmi);
}
return cm;
}
}
Related
Iteresiting case which make my work a nightmare in GWT. I try to add to my panel tab with dynamic changed table to add some params by user. Requirements are dynamic adding new elements and that user can overview all of them and eventually correct it.
I create all that with callTable but in one browser (Chromium or Opera) I can add new elements but in the same time on the same package in mozzila I see scroll bar but canot add dynamically new elements, but if I erase some preloaded one new one appears. Can someone explain to me what exactly goes wrong as it is one package run in incognito mode with erased history and cookies after every session
Maybe screen will be helpfull- param fill added dynamically:
view with new element 1
table with scrool bar but without option of adding new element 2
And source code:
//initial layout and component
final VLayout customLayout = new VLayout();
Canvas customComponent = new Canvas();
//dataprovider modal creation with in memory list
final ListDataProvider<String> model = new ListDataProvider<>(
getProvisioningParameterList());
final CellTable<String> table = new CellTable<>();
// create column with data
Column<String, String> nameColumn = new Column<String, String>(new EditTextCell()) {
#Override
public String getValue(String object) {
return object;
}
};
//column with delete button
Column<String, String> deleteBtn = new Column<String, String>(
new ButtonCell()) {
#Override
public String getValue(String object) {
return "x";
}
};
// add column to the table
table.addColumn(nameColumn, "Custom parameters");
table.addColumn(deleteBtn, "Click to delete row");
//initialize table row size
table.setWidth("100%");
// Set the field updater, whenever user clicks on button row will be removed.
deleteBtn.setFieldUpdater(new FieldUpdater<String, String>() {
#Override
public void update(int index, String object, String value) {
model.getList().remove(object);
model.refresh();
table.redraw();
}
});
// add a data display to the table which is adapter.
model.addDataDisplay(table);
//add Button
final IButton addButton = new IButton("Add");
addButton.setIcon("icons/add.png");
addButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
model.getList().add("fill");
model.refresh();
table.redraw();
}
});
//add custom config panel with proper view
ScrollPanel sp = new ScrollPanel();
sp.setAlwaysShowScrollBars(true);
customComponent.setContents("Params");
customComponent.setAutoHeight();
customLayout.setMargin(15);
customLayout.addMember(customComponent);
customLayout.addMember(addButton, 1);
customLayout.addMember(saveButton, 2);
customLayout.addMember(table);
If someone will still wonder about it I got a solution. I forgot to add ScrollPanel to layout. Edit Additionally I put table into scrollPanel in specific dimentions and that two element structure to main VLayout. Now it works as expected :)
I have too many buttons in a table and I'd like to replace them by a button that open a dropdown list of actions. However I don't really know how to handle the events from the dropdown items. I manage to do it using a javascript function but it's not very practical because I can only pass primitive values.
I also want to make it as a custom cell in the future to use it in different pages in my project so returning some html isn't very practical.
Here's my code :
final ButtonCell buttonInfoCell = new ButtonCell();
Column<GwtStockProduct, String> buttonCell = new Column<GwtStockProduct, String>(buttonInfoCell) {
#Override
public void render(final Context context, final GwtStockProduct value, final SafeHtmlBuilder sb) {
Div div = new Div();
Div bG = new Div();
div.add(bG);
bG.addStyleName("btn-group");
Button button = new Button();
DropDownMenu dropDown = new DropDownMenu();
Span span = new Span();
span.addStyleName("caret");
span.setVisible(false);
button.add(span);
button.getElement().setAttribute("style", "background-image: none !important; background-color: #234C78 !important;");
// button.removeStyleName("");
button.addStyleName("btn-hide-icon btn-blue");
button.setDataToggle(Toggle.DROPDOWN);
button.setText("Change stock");
for (int i = 1; i < 5; ++i) {
AnchorListItem item = new AnchorListItem();
item.getElement().getFirstChildElement().removeAttribute("href");
item.getElement().getFirstChildElement().setAttribute("onclick", "triggerClick('" + i + "')");
item.setText("Item " + i);
dropDown.add(item);
}
// dropDown.getElement().setAttribute("style", "position: relative !important;");
bG.add(button);
bG.add(dropDown);
// sb.append(SafeHtmlUtils.fromTrustedString(buttonGroup));
sb.appendHtmlConstant(div.getElement().getInnerHTML());
}
#Override
public String getValue(final GwtStockProduct object) {
// TODO Auto-generated method stub
return null;
}
};
stockTable.addColumn(buttonCell, "Actions");
stockTable.setColumnWidth(buttonCell, 5, Unit.PCT);
I use SelectionCell to render a drop-down list of options. Maybe that will help you:
ArrayList<String> options = new ArrayList<String>();
options.add("choose an option..."); // the prompt text
options.add("option 1");
options.add("option 2");
// ...
final SelectionCell optionsCell = new SelectionCell(options);
Column<TableType, String> optionsColumn = new Column<TableType, String>(optionsCell) {
#Override
public String getValue(TableType object) {
return null;
}
};
optionsColumn.setFieldUpdater(new FieldUpdater<TableType, String>() {
#Override
public void update(int index, TableType object, String value) {
if(value == "option 1")
// process option 1
else if(value == "option 2")
// process option 2
// ...
// reset drop-down to show the prompt text
optionsCell.clearViewData(object);
redrawRow(index);
}
});
table.addColumn(optionsColumn, "options");
The first option is just a prompt text and after each selection change the drop-down list is reset to show the prompt.
The disadvantage is that you can not have different sets of options for different rows as the list is generated once for the whole column.
I have a combo viewer based on a CCombo:
public static ComboViewer createComboViewer(Composite parent) {
CCombo combo = new CCombo(parent, SWT.BORDER);
combo.setTextLimit(2);
combo.addVerifyListener(new UpperCaseKeyListener());
ComboViewer viewer = new ComboViewer(combo);
viewer.setContentProvider(ArrayContentProvider.getInstance());
viewer.setLabelProvider(new CustomLabelProvider());
String[] strings = {"AB","CD","EF","GH","IJ"};
viewer.getCCombo().addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent keyEvent) {
String key = viewer.getCCombo().getText();
System.out.println(key);
String[] items = viewer.getCCombo().getItems();
if (!key.equals("") && key.length()==2) {
for (int i=0;i<strings.length;i++) {
if (strings[i].contains(key)) {
final ISelection selection = new StructuredSelection(strings[i]);
viewer.setSelection(selection);
}
}
}
}
});
I have a list of strings : {"AB","CD","EF","GH","IJ"} in this combo viewer.
When I type for example "AB" my item is selected from the drop-down list , but it is not highlighted with blue.
How can I make this happen?
I want that when I type an item in the combo and it is found in the list , to be highlighted with blue when I open the drop down list.
You must call setInput on the viewer to tell it about your content:
String[] strings = {"AB","CD","EF","GH","IJ"};
viewer.setInput(string);
In general a viewer requires both the content provider and label provider to be set and then setInput to be called.
I'm having a table view and I have added a context menu to that. When I right first time it highlight the row and show the context menu. But when I right click again on some other row it show the menu but doesn't highlight the new row and clear the old selected row.
How can I clear the old selected row and highlight the new row ?
I added the context menu using setRowFactorymethod
mytblView.setRowFactory(new Callback<TableView<Rowdata>, TableRow<Rowdata>>() {
#Override
public TableRow<Rowdata> call(TableView<Rowdata> tableView) {
final TableRow<Rowdata> row = new TableRow<>();
final ContextMenu contextMenu = new ContextMenu();
final MenuItem mnuItemAnalyze = new MenuItem("Analyze");
mnuItemAnalyze.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
//logic for menu item
}
});
contextMenu.getItems().add(mnuItemAnalyze);
// Set context menu on row, but use a binding to make it only show for non-empty rows:
row.contextMenuProperty().bind(Bindings.when(row.emptyProperty()).then((ContextMenu) null).otherwise(contextMenu));
return row;
}
});
}
I have a javafx checkbox tree. I need to select the checkbox when the tree item is clicked. I have added a listener for the selection property of the tree view. But the listener gets fired only when the tree item is clicked. The above listener is not fired when the checkbox is clicked.
Required: A listener that fires when a tree item or checkbox is clicked in the treeview.
Code:
String memberArray = {"subChild1", "subChild2", "childSub1"}
Group groupRoot = new Group();
Scene scene = new Scene(groupRoot, Color.ALICEBLUE);
HBox hBox = new HBox();
hBox.setMaxWidth(fxPanel.getWidth());
final Label royalLabel = new Label("Select a item");
TreeSet<String> prefixMember = new TreeSet<String>();
String tmpName = null;
LinkedHashSet<CheckBoxTreeItem<String>> treeItems = new LinkedHashSet<CheckBoxTreeItem<String>>();
LinkedHashSet<CheckBoxTreeItem<String>> treeSubItems = new LinkedHashSet<CheckBoxTreeItem<String>>();
for (String item : memberArray) {
if (!item.isEmpty()) {
tmpName = item.substring(0, 3);
prefixMember.add(tmpName);
}
}
// Create and empty TreeView
TreeView<String> duckTree = new TreeView<String>();
// Create TreeItems for the Hierarchy of the TreeView
CheckBoxTreeItem<String> root = new CheckBoxTreeItem<String>("Parent");
CheckBoxTreeItem<String> lm1 = new CheckBoxTreeItem<String>("Child1");
CheckBoxTreeItem<String> lm2 = new CheckBoxTreeItem<String>("Child2");
for (String item : prefixMember) {
CheckBoxTreeItem<String> treeItem = new CheckBoxTreeItem<String>(item.toString());
for (String subItem : memberArray) {
if (!subItem.isEmpty() && subItem.substring(0, 3).equals(item)) {
CheckBoxTreeItem<String> treeSubItem = new CheckBoxTreeItem<String>(
subItem.toString());
treeSubItems.add(treeSubItem);
}
}
treeItems.add(treeItem);
treeItem.getChildren().addAll(treeSubItems);
treeSubItems.clear();
}
root.getChildren().addAll(treeItems);
treeItems.clear();
// Create a TreeView using the root TreeItem
TreeView<String> royalTree = new TreeView<String>(root);
royalTree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
// Set a ChangeListener to handle events that occur with a Treeitem
// is selected
royalTree.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<TreeItem<String>>() {
public void changed(
ObservableValue<? extends TreeItem<String>> observableValue,
TreeItem<String> oldItem, TreeItem<String> newItem) {
// Gets fired only on selection of tree item
// Need to get fired on selection of check box too
// Select the respective checkbox on selection of tree item
}
});
hBox.getChildren().add(royalTree);
groupRoot.getChildren().add(hBox);
fxPanel.setScene(scene);
You could just add an EventHandler to your root item in the tree:
rootItem.addEventHandler(CheckBoxTreeItem.checkBoxSelectionChangedEvent(), new EventHandler<TreeModificationEvent<Object>>() {
#Override
public void handle(TreeModificationEvent<Object> event) {
// Your code here.
}
});
i had the same problem and searched looong time. Sadly there is not offical documentation for this from oracle.
The answer is to set the CellFactory and call the getSelectedStateCallback().call(this.getTreeItem());
for your treeItem in the updateItem:
// set cellFactory
royalTree.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
#Override
public TreeCell<String> call(TreeView<String> p) {
// return new CheckBoxTreeCell, you also can make a new class with this
return new CheckBoxTreeCell<String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// call the selectedStat Callback for treeitem
ObservableValue<Boolean> selectedState = getSelectedStateCallback().call(this.getTreeItem());
if (selectedState != null) {
// do something here
}
}
}
};
}
});
i have tested this in fx 8, but it should also work in fx 2.2
happy coding,
kalasch
Yes, adding an event handler to the tree item works. Here is some example code (Java 8) with a TreeView with items as CheckBoxTreeItem:
CheckBoxTreeItem<Path> rootItem = new CheckBoxTreeItem<>(rootDirPath);
rootItem.addEventHandler(
CheckBoxTreeItem.<Path>checkBoxSelectionChangedEvent(),
(TreeModificationEvent<Path> e) -> {
CheckBoxTreeItem<Path> item = e.getTreeItem();
if (item.isSelected() || item.isIndeterminate()) {
System.out.println("Some items are checked");
}
else {
System.out.println("Some items are unchecked");
}
});
TreeView<Path> tree = new TreeView<>(rootItem);
tree.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Do you require an event for each selection immediately? If not you can create an arraylist of all your checkboxtreeitems and iterate through that to check for selected or not selected when you need it.