PDA

View Full Version : Widget Rendered Grid with fields



Nico P
13 Feb 2012, 4:11 AM
Hi,

I have implemented a widget rendering grid like shown here: http://www.sencha.com/examples/explorer.html#widgetrenderergrid.

with one exception: Instead of buttons my renderer uses Textfields so the users can start editing immediately. The grid is rendered correctly and I can see all the textfields. But when I edit a textfield in the grid and press the space bar the input field loses the focus and the row is no longer selected.

First I thought it is the default handling of the browser that causes this behaviour. After some investigation I found out that the following piece of code in GridSelectionModel causes it:

@SuppressWarnings("unchecked")
protected void onKeyPress(GridEvent<M> e) {
...
// if space bar is pressed
if (e.getKeyCode() == 32) {
if (getLastFocused() != null) {
if (e.isShiftKey() && lastSelected != null) {
int last = listStore.indexOf(lastSelected);
int i = listStore.indexOf(getLastFocused());
select(last, i, e.isControlKey());
grid.getView().focusCell(i, 0, false);
} else {
if (isSelected(getLastFocused())) {
deselect(getLastFocused());
} else {
select(getLastFocused(), true);
grid.getView().focusCell(listStore.indexOf(getLastFocused()), 0, false);
}
}
}
}
}

Now I am asking myself if I have to use another selection model or if this is an issue that has to be fixed. From my understanding this routine should distinguish between if the event was triggered from an input field or not.

Any recommendations?

sven
13 Feb 2012, 4:15 AM
You can listen to the same event directly on the field and cancel event bubbling. This means the selectionmodel would not get the event.

Nico P
21 Mar 2012, 8:15 AM
Thanks a lot Sven! That solved my issue.


component.addListener(Events.KeyDown, new Listener<FieldEvent>() {

/** {@inheritDoc} */
@Override
public void handleEvent(FieldEvent be) {
if (be.getKeyCode() == 32) {
be.cancelBubble();
}
}
});

Nico P
21 Mar 2012, 11:43 PM
Here is the complete code of my grid cell renderer.

VGxtGrid is a wrapper for the grid that allowes to use the GXT Grid as the client component for the Vaadin server component.
TableData is a data container used by the fasade to provide fast access to cell components, IDs, data, ...
ITableCell is an interface components (our wrapper for comunication with vaadin server components) have to implement if they are used as cell components.


import com.extjs.gxt.ui.client.Style.SelectionMode;import com.extjs.gxt.ui.client.data.BaseModel;
import com.extjs.gxt.ui.client.data.BaseModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.FieldEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionProvider;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.form.Field;
import com.extjs.gxt.ui.client.widget.grid.ColumnData;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel;
import com.google.gwt.user.client.Element;


/* package private */ class ComponentCellRenderer implements GridCellRenderer<BaseModelData> {


/* ------------------------------------------------------------- *
* Constants
* ------------------------------------------------------------- */


static final String CELL_HEIGHT = Constants.TABLE_CELL_HEIGHT + "px";


private static final Listener<ComponentEvent> RENDER_LISTENER = new Listener<ComponentEvent>() {


/** {@inheritDoc} */
@Override
public void handleEvent(ComponentEvent be) {
Element element = be.getComponent().getElement();
/* enable text selection in cell component */
element.setAttribute("unselectable", "off");
/* center components in cell, added for checkboxes */
element.setAttribute("align", "center");
}
};


private static final Listener<FieldEvent> KEY_LISTENER = new Listener<FieldEvent>() {


/** {@inheritDoc} */
@Override
public void handleEvent(FieldEvent be) {
if (be.getKeyCode() == 32) {
be.cancelBubble();
}
}
};


/* ------------------------------------------------------------- *
* Members
* ------------------------------------------------------------- */


private final VGxtGrid vgxtGrid;


/* ------------------------------------------------------------- *
* Constructors
* ------------------------------------------------------------- */


ComponentCellRenderer(VGxtGrid vgxtGrid) {
assert vgxtGrid != null : "VGxtGrid must not be null";
this.vgxtGrid = vgxtGrid;
}


/* ------------------------------------------------------------- *
* Implements GridCellRenderer
* ------------------------------------------------------------- */


/** {@inheritDoc} */
@Override
public Object render(final BaseModelData model, String property, ColumnData config, final int row, final int col, final ListStore<BaseModelData> store, final Grid<BaseModelData> grid) {
Component component = null;
ITableCell<?> cell = vgxtGrid.getTableData().getCell(row, col);
if (cell != null) {
component = cell.getComponent();
component.setSize((grid.getColumnModel().getColumnWidth(col) - Constants.TABLE_CELL_OFFSET) + "px", ComponentCellRenderer.CELL_HEIGHT);
component.setToolTip(cell.getToolTipConfig());
component.addStyleName("render-cell-component");


if (component.isEnabled() == false || component instanceof Field<?> && ((Field<?>) component).isReadOnly()) {
config.css = "x-selectable render-grid-cell-inner render-grid-cell-inner-disabled";
} else {
config.css = "x-selectable render-grid-cell-inner";
}


addListeners(row, col, grid, component);
}


return component;
}


private void addListeners(final int row, final int col, final Grid<BaseModelData> grid, final Component component) {
/* add change listeners to update tooltips immediately after selection or value change */
if (component instanceof SelectionProvider) {
component.addListener(Events.SelectionChange, new SelectionChangedListener<BaseModel>() {


/** {@inheritDoc} */
@Override
public void selectionChanged(SelectionChangedEvent<BaseModel> se) {
adjustTooltip(row, col, component);
}
});
} else {
component.addListener(Events.OnChange, new Listener<BaseEvent>() {


/** {@inheritDoc} */
@Override
public void handleEvent(BaseEvent be) {
adjustTooltip(row, col, component);
}
});
}


/* Avoid table scrolling when space bar is pressed */
component.addListener(Events.KeyDown, KEY_LISTENER);


/* allow text selection in field, align components centered */
component.addListener(Events.Render, RENDER_LISTENER);


/* delegate selection change to grid */
component.addListener(Events.OnClick, new Listener<ComponentEvent>() {


/** {@inheritDoc} */
@Override
public void handleEvent(ComponentEvent be) {
setSelection(grid, row, be.isControlKey());
}
});
}


private void adjustTooltip(final int row, final int col, final Component component) {
component.setToolTip(vgxtGrid.getTableData().getCell(row, col).getToolTipConfig());
}


private void setSelection(Grid<BaseModelData> grid, int row, boolean isControlKey) {
GridSelectionModel<BaseModelData> sm = grid.getSelectionModel();
BaseModelData data = grid.getStore().getAt(row);
if (sm.isSelected(data) == false) {
boolean keep = sm.getSelectionMode() == SelectionMode.MULTI && isControlKey;
sm.select(data, keep);
}
}
}