I would like to add a button on some rows of my TreeViewer. To do that, I’ve used a method that I saw on a forum and it was working on a TableViewer.
I‘ve implemented my own Label provider on the column where I want the button to be. So I’ve overriden the class update(ViewerCell cell) which calls my method addButton(cell):
(I have simplified the code for a better comprehension)
public class SelectVariableButtonLabelProvider extends ColumnLabelProvider {
#Override
public void update(ViewerCell cell) {
if(...){
addButton(cell);
}
}
private void addButton(ViewerCell cell) {
TreeItem item = (TreeItem) cell.getItem();
Button btn = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE);
btn.setText(" select variable ");
//action when the button is clicked
btn.addListener(SWT.Selection, new SelectVariableButtonListener(tree,
DataTypeTreeUtils.getTreeNodeDataTypeInstance(cell.getElement()), viewer));
TreeEditor editor = new TreeEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
// editor.horizontalAlignment = SWT.RIGHT;
editor.minimumWidth = btn.getSize().x + 110;
editor.setEditor(btn, item, cell.getColumnIndex());
editor.layout();
}
}
It’s almost working. Except that the buttons of the column of buttons is duplicated when I want to extend the column.
screenshot of the bug
The left “column of buttons'' : is completely working. The buttons are functional and they adapt themself to the extension of the nodes in the tree Viewer.
The right “column of buttons” : is fixed on the viewer and the buttons are not completely functional. And when I want to extend or not the nodes in the tree, the buttons are not corresponding to their rows anymore. (These are also the buttons in the foreground).
So I would like to not have the right columns which probably appeared because of a bug. I think this could be due to the composite to which the button is initialized :
Button btn = new Button((Composite) cell.getViewerRow().getControl(), SWT.NONE);
Or just because buttons are simply bugging when they are on Tree Viewers ? The same method is working on Table Viewers.
Just in case and if it helps, this is the declaration of the viewer:
viewer = new TreeViewer(treeContainer, SWT.FULL_SELECTION);
viewer.setContentProvider(new TreeNodeTreeContentProvider());
viewer.setLabelProvider(new CustomColumnLabelProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
and this is the declaration of the column where I want to add the buttons:
//column with the buttons "select variable"
TreeViewerColumn viewerSetValueColumn = new TreeViewerColumn(viewer, SWT.NONE);
viewerSetValueColumn.getColumn().setWidth(110);
viewerSetValueColumn.setLabelProvider(new SelectVariableButtonLabelProvider(viewer, getAllVariables()));
EDIT:
I would like to have buttons in some rows of a column of my treeViewer. But I want them to be always visible, so I would like to avoid using editing support.
I've used the LabelProvider to do it but it caused a bug (screenshot of the bug).
Does anyone know how to add buttons to a treeViewer using the labelProvider?
Related
I have a TextField and two buttons in a row.
One button (the 'add button') adds another row of a TextField and another pair of add and delete button, the other button deletes the row.
The delete button is disabled while the current row is the only row, so there can't be no rows.
The add button is enabled only if the textfield of the current row is not empty and if it is the last textfield. So every row has a disabled 'add button' except for the last one.
My question now is how can I bind the 'add button' disableProperty to all textfields that exist and check if they are empty. As a matter of fact, I think I only have to check the last textfield and if it is empty I disable the last 'add button' if something is written the last 'add button' remains disabled but the current one of the row gets enabled.
I have found a workaround, in which I bind the button to the textfield, then if I add another row I unbind the button, disable it, and if I delete a row I only enable the last button and bind it againt to the textfield.
This solution seems very clunky and I was wondering if there is a more elgant solution with property binding.
My code (with the workaround, so you can see what I want to do):
public class Controller {
#FXML
private VBox VBox;
public ObservableList<TextField> oList = FXCollections.observableArrayList();
public ObservableList<Button> bList = FXCollections.observableArrayList();
public void initialize(){
createRow();
}
private void createRow(){
HBox box = new HBox(10);
TextField textField = new TextField();
Button addButton = new Button("Add row");
Button deleteButton = new Button("Delete");
box.getChildren().addAll(textField, addButton, deleteButton);
VBox.getChildren().add(box);
oList.add(textField);
bList.add(addButton);
addButton.disableProperty().bind(Bindings.isEmpty(textField.textProperty()));
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
addButton.disableProperty().unbind();
createRow();
textField.setDisable(true);
addButton.setDisable(true);
}
});
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
int idx = oList.indexOf(textField);
oList.remove(idx);
bList.remove(idx);
VBox.getChildren().remove(idx);
for(TextField tf : oList){
int i = oList.indexOf(tf);
if(oList.size()-1 == i){
tf.setDisable(false);
bList.get(i).disableProperty().bind(Bindings.isEmpty(tf.textProperty()));
}
}
}
});
}
}
And two Screenshots:
4 rows added, all 'add buttons' disabled, also the last one bc nothing is written in textfield
Here I deleted row test1 and the empty row and all 'add buttons' are still disabled except for the last one because there is text in the text field
Thanks for your help!
PS: I know in my Code ObservableLists aren't necessary, but I was trying things out and let them in because I forgot...
You can create a BooleanExpression that returns true if all TextFields in the list are empty. Note that you need to recreate that expression as the contents of the ObservableList<TextField>' change:
oList.addListener((ListChangeListener<? super TextField>) c -> {
addButton.disableProperty().unbind();
BooleanExpression allEmpty = oList.stream()
.map(tf -> BooleanExpression.booleanExpression(tf.textProperty().isEmpty()))
.reduce(new SimpleBooleanProperty(true), BooleanExpression::and);
addButton.disableProperty().bind(allEmpty);
});
Everytime a new TextField is added or removed, each TextField in the list will be mapped to a BooleanExpression of the empty property of that TextField. Then, all expressions will be anded together.
Note: The same way can also be done using a loop instead of a stream.
Note: You need to add this listener before adding any element to the list.
Background Information: I am currently working in a Dialog class I have extended for my game. Inside of this dialog's content table I have both an Image and a Table (lets call it ioTable). Inside of ioTable I have a combination of both Labels and TextFields. The idea is that the dialog becomes a sort of form for the use to fill out.
Next, inside of the Dialog's button table, I want to include a "Clear" TextButton (clearButton). The idea that clearButton will clear any values written to the TextFields of ioTable.
My Question: Is is possible to add a listener to each of the TextFields of ioTable that will trigger when clearButton is pressed. As always, any other creative solution is more than welcome.
You could just give the EventListener a reference to the table you want to clear:
// Assuming getSkin() and ioTable are defined elsewhere and ioTable is final
TextButton clearButton = new TextButton("Clear", getSkin());
clearButton.addListener(new EventListener() {
#Override
public boolean handle(Event event) {
for(Actor potentialField : table.getChildren()) {
if(potentialField instanceof TextField) {
((TextField)potentialField).setText("");
}
}
return true;
}
});
// Add clearButton to your dialog
If you see yourself creating multiple clearButtons, you could easily wrap this in a helper method or extend TextButton.
when I click on the ComboBox the first time and the popup menu list that is shown has its width very short. The second time I click on the ComboBox and the list is shown again, the width is now correct as the width of the list now aligns itself with the Combbox.
i tried to alter the width of the drop down on mouse click on the combo box. But it did not work,
final ComboBox<String> combo = new ComboBox<String>();
combo.getStyleClass().add("combo-border");
combo.setMinWidth(100.0);
combo.setEditable(true);
combo.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
combo.setMinWidth(100.0); // Did not work
//csectCombo.setPrefWidth(100.0); // Did not work
}
});
I am using Javafx 2.2. Is there any workaround for this?
From the below post, it says that is the known bug in JavaFx 2 and has been fixed in JavaFx 8.
http://tech.chitgoks.com/2013/09/20/width-of-combobox-popup-list-is-too-small-in-java-fx-2/
Try this
final ComboBox<String> combo = new ComboBox<String>();
combo.getStyleClass().add("combo-border");
combo.setMinWidth(100.0);
combo.setEditable(true);
combo.setPrefWidth(combo.getMinWidth());
I wanted to add the button to the cell but now I'm ready just to set an image for the background of the cell and than handle the click. how to add the image to the cell?
If you want to render a button in a column, see setRenderer method of ColumnConfig.
The following will set a button on each row of the grid:
ColumnConfig cfg = new ColumnConfig();
cfg.setRenderer(new GridCellRenderer() {
#Override
public Object render(M model, String property,
ColumnData config, int rowIndex, int colIndex,
ListStore<M> store, Grid<M> grid) {
Button button = new Button();
// set up button based on params to this render function
// for example, the `model` argument is the item backing each row.
// this render method is called for each row in the grid
// see http://dev.sencha.com/deploy/gxt-2.2.5/docs/api/com/extjs/gxt/ui/client/widget/grid/GridCellRenderer.html
return button;
}
}):
No need to add manually There is already a ButtonCell.
Just make use of it.
And you can Add icon to it even by setIcon() method.
What I am hoping is, when typing in editable JCombobox , the Popup Menu of the JCombobox to appear autumaticaly , i did this and it worked . But, when i changed the Icon of the Arrow Button in the JCombobox it didnt worked any more as shown in the picture
before changing Arrow Button Icon
After changing Arrow Button Icon (the Popup never appears, when one writes in the JCombobox)
this is what i did :
JTextComponent editor;
/** Creates new form combo */
public combo() {
initComponents();
editor = (JTextComponent) jComboBox1.getEditor().getEditorComponent();
jComboBox1.setEditable(true);
editor.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
char keyChar = e.getKeyChar();
if (jComboBox1.isDisplayable())
{
jComboBox1.setPopupVisible(true);
}
editor.setCaretPosition(editor.getText().length());
// System.out.println("wwwweeeee"+keyChar);
}
});
jComboBox1.setUI(new SynthComboBoxUI() {
protected JButton createArrowButton() {
JButton btn = new JButton();
btn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Image/error3.png")));
return btn;
}
});
}
Pleeeese help because i'm really tired from searching for a solution
The technical problem here is that the editor is created/maintained by the ui. When setting a custom ui it is replaced by a new editor, so you are listening to a component that is no longer part of the container hierarchy.
After digging a bit ... I still don't have a solution :-( On face value, you'd call setUI before installing the listener on the editor - BUT calling setUI is always wrong ... so simply don't.
Seeing that the ui is synth-based, the correct way to update its visual fore/background properties is to supply custom painters, per-application or per-instance. Nimbus specifically allows to install per-instance custom UIDefaults via the "Nimbus.Overrides" client property. For changing the icon on the arrow button, the appropriate override would be
Painter core = // a custom painter which paints the icon
comboDefaults.put("ComboBox:\"ComboBox.arrowButton\"[Enabled].foregroundPainter", core);
combo.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
combo.putClientProperty("Nimbus.Overrides", comboDefaults);
All fine, except not working - looks like the overrides are not properly installed on the children.
Edit 2
... hours later ...
from all available resources, the above should work, see f.i. Jasper's initial explanation of how-to define custom properties:
ComponentA:ChildComponentB.foreground which lets you specify a ChildComponentB contained within ComponentA.
So I suspect it's really a bug. A not really satisfying hack-around is to install the override on the button itself:
JButton org = null;
for (int i = 0; i < combo.getComponentCount(); i++) {
if (combo.getComponent(i) instanceof JButton) {
org = (JButton) combo.getComponent(i);
UIDefaults buttonDefaults = new UIDefaults();
buttonDefaults.put("ComboBox:\"ComboBox.arrowButton\"[Enabled].foregroundPainter", painter);
org.putClientProperty("Nimbus.Overrides.InheritDefaults", false);
org.putClientProperty("Nimbus.Overrides", buttonDefaults);
break;
}
}
That's not satisfying at all, as the button creation is controlled by the ui delegate, so this config will not survive a switch of LAF. Or the other way round: you'll need a install a PropertyChangeListener with the UIManager and on detecting a switch to Nimbus, manually copy the overrides from the combo to its children.