I have some dependencies hierarchy on my form, so I implemented hierarchy check on server side of the scout. If one field is changed, it triggered check if other need to be changed as well. This is done with export/import form data.
MyFormData input = new MyFormData();
FormDataUtility.exportFormData(this, input);
input = BEANS.get(IMYService.class).validate(input, field);
FormDataUtility.importFormFieldData(this, input, false, null, null);
validate function change all other fields that need to be changed.
My problem is with editing cells in editable tables.
If I change value in cell, and this chain validation is triggered, after importing form data I lose focus in cell. So instead, tab will move me to another cell, tab trigger import and focus in cells are lost. And this is a really bad user experience.
How to fix this?
How to stay in focus (of next cell) after import has been called?
Marko
I am not sure, if this applies for you, but you can try the following:
I assume, that you do your export/validate/import logic in the execCompleteEdit(ITableRow row, IFormField editingField) of your column class. I suggest, that you calculate your next focusable cell by yourself and request its focus after importing the form data.
As an example, you can do this like that:
#Override
protected void execCompleteEdit(ITableRow row, IFormField editingField) {
super.execCompleteEdit(row, editingField);
// create form data object
// export form data
// call service and validate
// import form data
// request focus for next cell
focusNextAvailableCell(this, row);
}
with focusNextAvailableCell(this, row) as following:
private void focusNextAvailableCell(IColumn<?> col, ITableRow row) {
if (col == null || row == null) {
return;
}
IColumn<?> nextColumn = getColumnSet().getColumn(col.getColumnIndex()+1);
ITableRow nextRow = getTable().getRow(row.getRowIndex());
if (nextColumn == null) {
// no next column (last column lost focus)
// check if next row is available
nextRow = getTable().getRow(row.getRowIndex()+1);
// maybe select first cell again?
if (nextRow == null) {
nextColumn = getColumnSet().getColumn(0);
nextRow = getTable().getRow(0);
}
}
if (nextColumn != null && nextRow != null) {
getTable().requestFocusInCell(nextColumn, nextRow);
}
}
You should be aware, that you have to call this after every form data import in your execCompleteEdit method of your column. Also this is triggered not only when switching cells through pressing the tab key, but also when clicking with the mouse button.
Best regards!
Related
We have a NatTable with no header and I treated the 1st row as a Header,
- Register CELL_PAINTER to change the visualization to look this row similar to header.
Also registered the CustomCommandHandler which implements ILayerCommandHandler to prevent cell/row selection for the 1st row.
selectionLayer.registerCommandHandler(new CustomCommandHandler());
Cell selection is working fine for other cells.
public boolean doCommand(final ILayer layer, final ILayerCommand command)
{
if (command instanceof ViewportSelectRowCommand)
{
return ((ViewportSelectRowCommand) command).getRowPosition() <= 1;
}
else if (command instanceof SelectCellCommand)
{
return ((SelectCellCommand) command).getRowPosition() <= 1
}
return false;
}
Now how can I select the entire column on selecting cells on 1st row. So that it should not affect the cell selection for other row cells.
Clicking any cells on 1st row should select entire column.
Clicking any cells on other rows should select the same cell. (currently this is happening)
Although I am not quite sure what sense it makes to only have a body that is configured in a complicated way to look and behave like it has headers without really having headers (IMHO this does not make any sense), you need to register a custom handler that checks for the column position and transform the SelectCellCommand into a SelectColumnCommand.
this.selectionLayer.registerCommandHandler(new SelectCellCommandHandler(this.selectionLayer) {
#Override
public boolean doCommand(ILayer targetLayer, SelectCellCommand command) {
if (command.convertToTargetLayer(targetLayer)
&& command.getColumnPosition() == 0) {
return targetLayer.doCommand(
new SelectColumnCommand(
targetLayer,
command.getColumnPosition(),
command.getRowPosition(),
command.isShiftMask(),
command.isControlMask()));
}
return super.doCommand(targetLayer, command);
}
});
But I expect there will be more issues on the way as the immitated headers do not behave like real headers also in other scenarios. You could also try to override getRegionLabelsByXY(int, int) but I am not sure if this would work or cause more problems.
I created a new tool in ContentTools that adds a static table (I don't want you to edit).
But being a static element doesn't maintain focus and I can not remove it when I click remove button.
I can do so that the table is not editable but can be removed if you click on it?
That's how I created the element:
new ContentEdit.Static('div', {'data-ce-moveable': true}, '<table><thead><tr><th>Foo head</th></tr></thead><tbody><tr><td>Foo body</td></tr></tbody></table>')
Thank you!
Static elements can't be interacted with for the most part (other elements can be dragged around them but that's about it). ContentEdit/Tools does allow you to restrict the some behaviour for elements but not being able to modify the content of a text element isn't one right now (though I think this might be a worthy addition).
However whilst there's no set way to do this at the moment here's an approach you can use that should provide the behaviour you describe (do let me know how you get on):
ContentEdit.Root.get().bind('mount', function(element) {
// We only care about `TableCell` elements
if (element.type() != 'TableCell') {
return;
}
// We only want to make the element read-only if the parent table has
// the `data-read-only` attribute.
var table = element.closest(function(node) {
return node.type() == 'Table';
});
if (table.attr('data-read-only') !== undefined) {
// Disable text editing for the table cell
element.tableCellText()._onKeyDown = function(ev) {
ev.preventDefault();
}
}
// Disable dragging of the table rows
var tableRow = element.closest(function(node) {
return node.type() == 'TableRow';
});
tableRow.can('drag', false);
tableRow.can('drop', false);
});
I'm probably doing something else wrong but I've followed examples given here:
How to remove a row from the Cell Table
and
GWT get CellTable contents for printing or export
to accomplish my goal and the result is close but not quite right.
I have a page with two widgets. The first wiget contains a CellTable that uses an aSync ListDataProvider to pull results and populate a table. The table has a selection change event handler associated with it that loads further details about the selected item into the second widget below it.
public OrderAdminTable() {
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
#Override
public void onSelectionChange(SelectionChangeEvent event) {
OrderAdminListProxy selected = selectionModel.getSelectedObject();
if (selected != null && orderSnapShot != null) {
orderSnapShot.loadSnapShot(selected);
}
}
});
initTable();
this.addStyleName("order-list fixed_headers BOM");
this.setSelectionModel(selectionModel);
}
Once the second widget has loaded the details about the selected item, the user can remove the item from the table/list by clicking a button in the RootPanel that is the parent of both widgets.
searchView.getCmdReview().addClickHandler(new ClickHandler() {
#Override public void onClick(ClickEvent event) {
searchView.getOrderAdminSnapshot().reviewOrder();//this line calls a web service that deletes the item from the server data
dataProvider.getList().remove(searchView.getOrderAdminSnapshot().getSelectedOrder());
for(int i=0;i<table.getRowCount();i++){
TableRowElement row = table.getRowElement(i);
for(int j=0;j<row.getCells().getLength();j++){
if(row.getCells().getItem(j).getInnerText().contains(searchView.getOrderAdminSnapshot().getSelectedOrder().getSalesOrderNumber())){
row.setAttribute("removed", "true");
row.addClassName("hidden");
}
}
}
}
});
This all works fine until you select another item in the table. When that happens, the selection change event seems to redraw the table and remove my custom attribute and class from the previously selected item. This makes it appear in the list again.
The ultimate goal here is to avoid a round trip to the server to pull new results when you remove an item from the list. The line "searchView.getOrderAdminSnapshot().reviewOrder();" makes a web service call that removes the item from the data on the server side so it does not appear in subsequent reloads.
Is there some way to force the selection change event to maintain the state of the table row that was previously selected? Is there a better way to remove the selected item from the list? Any advice would be appreciated.
Once you remove the object from the list dataProvider.getList().remove, it should disappear from the table. There is no need to hide the row - this row should be gone. So your loop should never find it.
Im using a GWT Cell Table with sorting enabled on the columns. I am also providing an option of filtering the column. This is roughly how the header looks.
----ColumnName (Image) -------
When the user clicks on the image, i start off a filtering process. I achieved this using the Cell Table's addCellPreviewHandler. The issue I am facing is , when the image is clicked , the column sorts itself too. Can anyone tell me how to prevent this from happening ? To be clearer, I want the sort to be called when the user clicks anywhere in the header but the image.
Thanks.
breakDownCellTable.addColumn(indexedColumn, columnHeader);
final int currentColumnIndex = columnIndex;
// Add a ColumnSortEvent.ListHandler to connect sorting to the
// java.util.List.
ListHandler<List<GwtContributorCellData>> columnSortHandler = new ListHandler<List<GwtContributorCellData>>(contributorList);
columnSortHandler.setComparator(indexedColumn, new Comparator<List<CustomClass>>() {
public int compare(List<CustomClass> o1, List<CustomClass> o2) {
if (o1 == o2) {
return 0;
}
// Compare the columns.
if (o1 != null) {
return (o2 != null) ? o1.get(currentColumnIndex).compareTo(o2.get(currentColumnIndex)) : 1;
}
return -1;
}
});
breakDownCellTable.addColumnSortHandler(columnSortHandler);
and in the onBrowserEvent method:
if("click".equals(event.getType())){
EventTarget eventTarget = event.getEventTarget();
if(eventTarget.toString().contains("img") && !eventTarget.toString().contains("<th")){
//event.stopPropagation();
// Window.alert("here");
//breakDownCellTable.fireEvent()
ColumnSortEvent.fire(breakDownCellTable, breakDownCellTable.getColumnSortList());
Your can implement a subclass of the cell you're using and override the onBrowserEvent() method.
This post explains how to find out which DOM element is the exact source of the event.
Once you know that the image has been clicked or not, fire the filtering / sorting event accordingly.
I am using a TableViewer with a content provider, label provider, a ICellModifier and TextCellEditors for each column.
How can I add arrow key navigation and cell editing when the user selects the cell? I would like this to be as natural a behavior as possible.
After looking at some of the online examples, there seems to be an old way (with a TableCursor) and a new way (TableCursor does not mix with CellEditors??).
Currently, my TableViewer without a cursor will scroll in the first column only. The underlying SWT table is showing cursor as null.
Is there a good example of TableViewer using CellEditors and cell navigation via keyboard?
Thanks!
I don't know if there is a good example. I use a cluster of custom code to get what I would consider to be basic table behaviors for my application working on top of TableViewer. (Note that we are still targetting 3.2.2 at this point, so maybe things have gotten better or have otherwise changed.) Some highlights:
I do setCellEditors() on my TableViewer.
On each CellEditor's control, I establish what I consider to be an appropriate TraverseListener. For example, for text cells:
cellEditor = new TextCellEditor(table, SWT.SINGLE | getAlignment());
cellEditor.getControl().addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent e) {
switch (e.detail) {
case SWT.TRAVERSE_TAB_NEXT:
// edit next column
e.doit = true;
e.detail = SWT.TRAVERSE_NONE;
break;
case SWT.TRAVERSE_TAB_PREVIOUS:
// edit previous column
e.doit = true;
e.detail = SWT.TRAVERSE_NONE;
break;
case SWT.TRAVERSE_ARROW_NEXT:
// Differentiate arrow right from down (they both produce the same traversal #*$&#%^)
if (e.keyCode == SWT.ARROW_DOWN) {
// edit same column next row
e.doit = true;
e.detail = SWT.TRAVERSE_NONE;
}
break;
case SWT.TRAVERSE_ARROW_PREVIOUS:
// Differentiate arrow left from up (they both produce the same traversal #*$&#%^)
if (e.keyCode == SWT.ARROW_UP) {
// edit same column previous row
e.doit = true;
e.detail = SWT.TRAVERSE_NONE;
}
break;
}
}
});
(For drop-down table cells, I catch left and right arrow instead of up and down.)
I also add a TraverseListener to the TableViewer's control whose job it is to begin cell editing if someone hits "return" while an entire row is selected.
// This really just gets the traverse events for the TABLE itself. If there is an active cell editor, this doesn't see anything.
tableViewer.getControl().addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN) {
// edit first column of selected row
}
}
});
Now, how exactly I control the editing is another story. In my case, my whole TableViewer (and a representation of each column therein) is loosely wrapped up in a custom object with methods to do what the comments above say. The implementations of those methods ultimately end up calling tableViewer.editElement() and then checking tableViewer.isCellEditorActive() to see if the cell was actually editable (so we can skip to the next editable one if not).
I also found it useful to be able to programmatically "relinquish editing" (e.g. when tabbing out of the last cell in a row). Unfortunately the only way I could come up with to do that is a terrible hack determined to work with my particular version by spelunking through the source for things that would produce the desired "side effects":
private void relinquishEditing() {
// OMG this is the only way I could find to relinquish editing without aborting.
tableViewer.refresh("some element you don't have", false);
}
Sorry I can't give a more complete chunk of code, but really, I'd have to release a whole mini-project of stuff, and I'm not prepared to do that now. Hopefully this is enough of a "jumpstart" to get you going.
Here is what has worked for me:
TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer,new FocusCellOwnerDrawHighlighter(tableViewer));
ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(tableViewer) {
protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
|| event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
|| (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED && event.keyCode == SWT.CR)
|| event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
}
};
I can navigate in all directions with tab while editing, and arrow around when not in edit mode.
I got it working based on this JFace Snippet, but I had to copy a couple of related classes also:
org.eclipse.jface.snippets.viewers.TableCursor
org.eclipse.jface.snippets.viewers.CursorCellHighlighter
org.eclipse.jface.snippets.viewers.AbstractCellCursor
and I don't remember exactly where I found them. The is also a org.eclipse.swt.custom.TableCursor, but I couldn't get that to work.
Have a look at
Example of enabling Editor Activation on a Double Click.
The stuff between lines [ 110 - 128 ] add a ColumnViewerEditorActivationStrategy and TableViewerEditor. In my case the I wanted a single click to begin editing so i changed line 115 from:
ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION
to ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION. After adding this to my TableViewer, the tab key would go from field to field with the editor enabled.