I'm trying to clear the user selected value on a IPickTreeItem.
This is the only solution I've found to restrict the user from selecting some of the Tree root values (not all).
To be more clear, it seems that calling event.cancel() do not stop the event from bubbling.
Am I doing something wrong ?
TreeNode treenode = new TreeNode("root", new TreeNode("Operation A"),
new TreeNode("Operation B"));
final DynamicForm dynamicForm = new DynamicForm();
Tree tree = new Tree();
tree.setRoot(treenode);
final IPickTreeItem pickTreeItem = new IPickTreeItem();
pickTreeItem.setValueTree(tree);
pickTreeItem.addChangeHandler(new ChangeHandler()
{
#Override
public void onChange(ChangeEvent event)
{
pickTreeItem.clearValue() // Not clearing the value
pickTreeItem.setValue((String)null) // Not working neither
event.cancel() // Not seeming to work...
}
});
dynamicForm.setItems(pickTreeItem);
dynamicForm.draw();
This is not working either :
pickTreeItem.setInputTransformer(new FormItemInputTransformer()
{
#Override
public Object transformInput(DynamicForm form, FormItem item,
Object value, Object oldValue)
{
return "Desired New Value (not working)...";
}
});
This is weird because it works using an external Button to clear the value (outside the picktreeitem handler)
Button bt = new Button("click");
bt.addClickHandler(new ClickHandler()
{
#Override
public void onClick(ClickEvent event)
{
pickTreeItem.setValue((Object) null);
}
});
Expected behavior
My Tree :
-aaaa
----bbbb
----cccc
-dddd
----eeee
----ffff
If the user selects "aaaa" the PickTreeItem value should be reverted to the defaultValue ("Choose a value"), optionally inform the user that he cannot pick "aaaa".
The PickTreeItem should accept "dddd" as a valid choosen value.
As with all FormItems, event.cancel() is the correct way to disallow the change. There was a framework level bug that was preventing this from behaving correctly that has now been corrected.
See this thread on the Isomorphic forums
I understand it is not exactly the same with what you are trying to achieve, but you could consider to define a CustomValidator, that reads the selected values and returns false and an appropriate message, when one of the parent values that shouldn't be, is selected. For this to work, you must set pickTreeItem.setCanSelectParentItems(Boolean.TRUE), to allow for parent items to be selected, and pickTreeItem.setValidateOnChange(Boolean.TRUE), to validate the selected values upon selection.
Related
I create an application in JavaFX where there is a lot of choicebox'es (around 100). Clicking each of them changes the status of one Boolean variable (selected - true, unselected - false). I have ActionEvent for each of choicebox, but I would like to make action event which suport all of them.
One of ActionEvent looks like:
public void onActionClick(ActionEvent actionEvent) {
if(firstCheckbox.isSelected()){
firstBooleanValue=true;
} else {
firstBooleanValue=false;
}
}
Second looks similar:
public void onActionClick(ActionEvent actionEvent) {
if(secondCheckbox.isSelected()){
secondBooleanValue=true;
} else {
secondBooleanValue=false;
}
}
I heard from my friend that I should create class with EventHandler and pass parameters (Checkbox and Boolean variable) but I don't know how. Any solutions?
I heard from my friend that I should create class with EventHandler and pass parameters (Checkbox and Boolean variable)
Unless you want to use a container class for the boolean variable (e.g. BooleanProperty), it's not really possible to pass a variable in a way that allows you to write it. You could of course pass a Consumer<Boolean>.
(Theoretically it would be possible to access fields via reflection to write a value, but I strongly recommend not doing this.)
In the event handler you could use the source property to get the object that triggered the change (the CheckBox). This fact would allow you to create a Map<CheckBox, Consumer<Boolean>> to handle the event with the same event handler without testing for reference equality with 100 CheckBoxes.
private boolean a;
private boolean b;
private boolean c;
private Map<CheckBox, Consumer<Boolean>> eventMap = new HashMap<>();
private void comboAction(ActionEvent event) {
CheckBox cb = (CheckBox) event.getSource();
eventMap.get(cb).accept(cb.isSelected());
}
#Override
public void start(Stage primaryStage) {
CheckBox cb1 = new CheckBox("a");
CheckBox cb2 = new CheckBox("b");
CheckBox cb3 = new CheckBox("c");
// tell event handler what to do with the booleans
eventMap.put(cb1, v -> a = v);
eventMap.put(cb2, v -> b = v);
eventMap.put(cb3, v -> c = v);
// register event handlers
EventHandler<ActionEvent> onAction = this::comboAction;
for (CheckBox cb : eventMap.keySet()) {
cb.setOnAction(onAction);
}
Button button = new Button("print");
button.setOnAction(evt -> {
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println("-------");
});
Scene scene = new Scene(new VBox(cb1, cb2, cb3, button));
primaryStage.setScene(scene);
primaryStage.show();
}
However the fact that there are 100 boolean fields in a single class indicates a design issue. Consider storing the data in a different data structure, like List, Map or similar data structures. You could also store the CheckBoxes in such a data structure which would make the use of an onAction event handler unnecessary; you could simply retrieve the CheckBox responsible for the property and use isSelected when you need the value...
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.
Using the CheckBoxTableCell in a TableView I wan't to perform some code when the user clicks the checkbox.
This code needs to be performed before the CheckBox get's checked and before the associated property is changed.
Since CheckBoxTableCell does not trigger startEdit(), commitEdit() and so on I need another point where I can put my code.
updateItem() would be too late, since the value has already been changed there.
Does anybody know any other place where I can insert my code? Or am I better of just writing my own CheckBoxTableCell?
For those who want to think further: When the user checks the checkBox I want to run a code that checks if his decision may cause him problems later on. When the property changes I have a changeListener that uploads the new value to a database. If there may be any problems (my code returns true) then there should be the obligatory error message: "Do you really want to do this...blabla" and only if he confirms the check box should be actually checked, the value changed and uploaded to the database.
This is how I set up my CheckBoxTableCell:
//init cell factories
Callback<TableColumn<PublicMovie, Boolean>, TableCell<PublicMovie, Boolean>> checkBoxCellFactory
= (TableColumn<PublicMovie, Boolean> p) -> new CheckBoxTableCell<>();
//Assign Column
allMoviesCheckBoxColumn = (TableColumn<PublicMovie, Boolean>) allMoviesTableView.getColumns().get(0);
//Set CellValueFactory
allMoviesCheckBoxColumn.setCellValueFactory(cellData -> cellData.getValue().getSomeBooleanProperty();
someBooleanProperty has a listener. When changed to true, the statusProperty changes to 1.
This code snippet is in the constructor of the TableViews underlying class. Whenever the user clicks the CheckBox, it is executed.
this.status.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
DataHandler.fireOnChangedEvent(PublicMovieChangeListener.STATUS_CHANGED, getMovie(), newValue, oldValue);
});
DataHandler holds a list of Listeners (so far only one). The following code is executed when the onChange event is called by the DataHandler:
#Override
public void onChange(int changeID, PublicMovie changedPublicMovie, Object newValue, Object oldValue) {
switch (changeID) {
case STATUS_CHANGED:
boolean mayUpdate = check();
//check is a placeholder for the code that checks for problems.
//It will return false if there might be a problem.
if(!mayUpdate){
mayUpdate = ErrorDialogs.getConfirmationDialog("Überfüllung!", header, content);
}
if (mayUpdate) {
updateMovie(changedPublicMovie);
} else {
changedPublicMovie.getStatusProperty().set((int) oldValue);
refreshAllMoviesTable();
}
break;
case THREE_DIM_CHANGED:
updateMovie(changedPublicMovie);
break;
case CHILDREN_CHANGED:
updateMovie(changedPublicMovie);
break;
}
What you can see here is my attempt to reverse the changed Property... but the checkbox remains checked. Thus I'm looking for a way to perform the checking prior to changing the Property.
Based on the documentation of CheckBoxTableCell:
If you want to be notified of changes, it is recommended to directly
observe the boolean properties that are manipulated by the CheckBox.
I don't say that it is impossible with CheckBoxTableCell, but most probably it is not so straightforward.
This kind of "pre-select" events can be managed in most of the cases with EventFilters.
Therefore you could try to use a normal TableColumn with the appropriate cellFactory that utilizes these EventFilters.
My example
The used cellFactory:
tableCol.setCellFactory(p -> {
CheckBox checkBox = new CheckBox();
TableCell<Person, Boolean> tableCell = new TableCell<Person, Boolean>() {
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null)
setGraphic(null);
else {
setGraphic(checkBox);
checkBox.setSelected(item);
}
}
};
checkBox.addEventFilter(MouseEvent.MOUSE_PRESSED, event ->
validate(checkBox, (Person) cell.getTableRow().getItem(), event));
checkBox.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if(event.getCode() == KeyCode.SPACE)
validate(checkBox, (Person) cell.getTableRow().getItem(), event);
});
tableCell.setAlignment(Pos.CENTER);
tableCell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
return tableCell;
});
and the used validate method:
private void validate(CheckBox checkBox, Person item, Event event){
// Validate here
event.consume();
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");
// Set the checkbox if the user want to continue
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK)
checkBox.setSelected(!checkBox.isSelected());
}
What is does:
It renders a simple CheckBox and adds two EventFilters to this CheckBox. The first one is executed on mouse press, the second one on keypress (here the KeyCode.SPACE is used). Both of them call the validate method. The validate method consumes the event (ensures that the selection state of the CheckBox is not modified), "validates" the input (no validation on my side) then shows a confirmation dialog. If the user agrees, the selectedProperty of the CheckBox is set.
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 textbox and one suggestbox. I attach a value change and key up handler to the text box such that whatever the user types (or pastes) into the text box is echo-ed inside the suggestbox. I can get the suggestbox to display the suggestion list by calling showSuggestionList on each value change and key up event.
Now, how do I get the suggestbox to automatically choose the first item in the suggestion list?
One of the methods I tried is to programatically simulate key presses, i.e
suggestBox.setFocus(true);
NativeEvent enterEvent = Document.get().createKeyPressEvent(false, false, false, false, KeyCodes.KEY_ENTER);
DomEvent.fireNativeEvent(enterEvent, suggestBox);
textBox.setFocus(true);
This doesn't work at all. The enter key isn't simulated. Another possible solution is to extend SuggestionBox.SuggestionDisplay, but I'm not too sure how to that. Any pointers appreciated.
Update: I'm still working on this and trying various methods.
Here, I tried to implement my own SuggestionDisplay by subclassing DefaultSuggestionDisplay and overriding getCurrentSelection() to make accessible from my class. This doesn't work either. Null is returned.
private class CustomSuggestionDisplay extends DefaultSuggestionDisplay {
#Override
protected Suggestion getCurrentSelection() {
return super.getCurrentSelection();
}
}
suggestBox.setAutoSelectEnabled(true);
textBox.addKeyUpHandler(new KeyUpHandler() {
public void onKeyUp(KeyUpEvent event) {
suggestBox.setValue(textBox.getText(), true);
suggestBox.showSuggestionList();
if (suggestBox.isSuggestionListShowing()) {
String s = ((CustomSuggestionDisplay) suggestBox.getSuggestionDisplay()).getCurrentSelection().getDisplayString();
Window.alert(s);
}
}
});
Here, I tried to attach a value change handler to the SuggestBox, and casting the event type to SuggestOracle.Suggestion. Again, null is returned.
suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
String s = ((SuggestOracle.Suggestion) event).getDisplayString();
Window.alert(s);
}
});
Use suggesBox.setAutoSelectEnabled(true)
Here more info about the SuggestBox of GWT:
You could try using addSelectionHandler in conjunction with setAutoSelectEnabled to receive an event whenever a suggestion is selected. You could also have your Oracle send a message when it suggests something, or your Display send a message when it displays a list:
public class AutomaticallySelectingSuggestionDisplay extends SuggestBox.DefaultSuggestionDisplay {
#Override
protected void showSuggestions(SuggestBox box, Collection<? extends SuggestOracle.Suggestion> suggestions, boolean isDisplayHtml, boolean isAutoSelectEnabled, SuggestBox.SuggestionCallback callback) {
super.showSuggestions(box, suggestions, isDisplayHtml, isAutoSelectEnabled, callback);
fireValueChangeEventWithFirstSuggestion(suggestions);
}
}
This idea feels a little muddled to me, so I hope you can find a solution just using event handlers.