PDA

View Full Version : [OPEN] Strange behavior: Adding Textfields to Grid Header



acerberus
16 Dec 2015, 4:53 AM
Hi,

I am experiencing some strange behavior when trying to add a TextField to a grid header. The text field is added, but one cannot use the mouse to either select text or set the cursor to a specific position. This behavior is present in GXT 3.1.4. and older versions. I haven't yet tried it with GXT 4.0.

Here is a minimal code example showing the phenomenon:



public void onModuleLoad() {
ColumnConfig<String, String> nameCol = new ColumnConfig<String, String>(new IdentityValueProvider<String>(), 150, "Test");

List<ColumnConfig<String, ?>> columns = new ArrayList<ColumnConfig<String, ?>>();
columns.add(nameCol);

ColumnModel<String> cm = new ColumnModel<String>(columns);

HeaderGroupConfig hgc = new HeaderGroupConfig(new TextField(), 1, 1);
cm.addHeaderGroup(0, 0, hgc);

ListStore<String> store = new ListStore<String>(new ModelKeyProvider<String>() {
@Override
public String getKey(String item) {
return item;
}
});
store.add("foo");
store.add("bar");

final Grid<String> grid = new Grid<String>(store, cm);

RootPanel.get().add(grid);
}


Is that expected behavior, and if so why? Is there any known workaround to have the text field working properly?

Best regards,
Arno

branflake2267
16 Dec 2015, 8:24 AM
The reason you can use the mouse is there are some preventDefaults in the ColumnHeader which prevents some of the default behavior. This is because the clicks used to sort and work with the context menu. So this ends up being a bug. To workaround this you'll need to override ColumnHeader and onbrowser event because of a private method onClick. Would you need some help figuring out an option to override onBrowserEvent?

The override is going to take some jsni violation, but here is a start. Let me know if you need more help.


import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.editor.client.Editor.Path;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.RootPanel;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.util.DateWrapper;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.PropertyAccess;
import com.sencha.gxt.widget.core.client.container.Viewport;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnHeader;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.grid.HeaderGroupConfig;


public class GridWithTextFieldInColumnHeader implements EntryPoint {
private static int COUNTER = 0;
private static final StockProperties props = GWT.create(StockProperties.class);


public Grid<Stock> createGrid() {
ColumnConfig<Stock, String> nameCol = new ColumnConfig<Stock, String>(props.name(), 100, "Company");
ColumnConfig<Stock, String> symbolCol = new ColumnConfig<Stock, String>(props.symbol(), 100, "Symbol");
ColumnConfig<Stock, Double> lastCol = new ColumnConfig<Stock, Double>(props.last(), 100, "Last");
ColumnConfig<Stock, Double> changeCol = new ColumnConfig<Stock, Double>(props.change(), 100, "Change");
ColumnConfig<Stock, Date> lastTransCol = new ColumnConfig<Stock, Date>(props.lastTrans(), 100, "Last Updated");


List<ColumnConfig<Stock, ?>> columns = new ArrayList<ColumnConfig<Stock, ?>>();
columns.add(nameCol);
columns.add(symbolCol);
columns.add(lastCol);
columns.add(changeCol);
columns.add(lastTransCol);


HeaderGroupConfig hgc = new HeaderGroupConfig(new TextField(), 1, 1);


ColumnModel<Stock> cm = new ColumnModel<Stock>(columns);
cm.addHeaderGroup(0, 0, hgc);


ListStore<Stock> store = new ListStore<Stock>(props.key());
store.addAll(getStocks());


GridView<Stock> gridView = new GridView<Stock>();
Grid<Stock> grid = new Grid<Stock>(store, cm, gridView);


ColumnHeader<Stock> columnHeader = new ColumnHeader<Stock>(grid, cm) {
@Override
public void onBrowserEvent(Event event) {
switch (event.getTypeInt()) {
case Event.ONCLICK:
onClick(event);
return;
default:
}
super.onBrowserEvent(event);
}


protected void onClick(Event ce) {
// ce.preventDefault();
if (ce.getEventTarget().<Element> cast() == (Element) btn.cast()) {
onDropDownClick(ce, column);
} else {
onHeaderClick(ce, column);
}
}
};
gridView.setColumnHeader(columnHeader);


return grid;
}


public class Stock implements Serializable {
private Integer id;
private Double change;
private Date date = new Date();
private String industry = getType();
private Double last;
private String name;
private Double open;
private String symbol;
private boolean split = Boolean.valueOf(Math.random() > .5);


public Stock() {
this.id = Integer.valueOf(COUNTER++);
}


public Stock(String name, String symbol, double open, double last, Date date) {
this();
this.name = name;
this.symbol = symbol;
this.change = last - open;
this.open = open;
this.last = last;
this.date = date;
}


public Double getChange() {
return change;
}


public Integer getId() {
return id;
}


public String getIndustry() {
return industry;
}


public Double getLast() {
return last;
}


public Date getLastTrans() {
return date;
}


public String getName() {
return name;
}


public Double getOpen() {
return open;
}


/**
* Read-only property, based on other values
*
* @return the percent change
*/
public double getPercentChange() {
return getChange() / getOpen();
}


public String getSymbol() {
return symbol;
}


public boolean isSplit() {
return split;
}


public void setChange(Double change) {
this.change = change;
}


public void setId(Integer id) {
this.id = id;
}


public void setIndustry(String industry) {
this.industry = industry;
}


public void setLast(Double last) {
this.last = last;
}


public void setLastTrans(Date date) {
this.date = date;
}


public void setName(String name) {
this.name = name;
}


public void setOpen(Double open) {
this.open = open;
}


public void setSplit(boolean split) {
this.split = split;
}


public void setSymbol(String symbol) {
this.symbol = symbol;
}


public String toString() {
return getName();
}


private String getType() {
double r = Math.random();
if (r <= .25) {
return "Auto";
} else if (r > .25 && r <= .50) {
return "Media";
} else if (r > .5 && r <= .75) {
return "Medical";
} else {
return "Tech";
}
}
}


public List<Stock> getStocks() {
List<Stock> stocks = new ArrayList<Stock>();
stocks.add(new Stock("Apple Inc.", "AAPL", 125.64, 123.43, randomDate()));
stocks.add(new Stock("cisco Systems, Inc.", "CSCO", 25.84, 26.3, randomDate()));
stocks.add(new Stock("Cisco Inc.", "GOOG", 516.2, 512.6, randomDate()));
stocks.add(new Stock("Intel Corporation", "INTC", 21.36, 21.53, randomDate()));
stocks.add(new Stock("Level 3 Communications, Inc.", "LVLT", 5.55, 5.54, randomDate()));
stocks.add(new Stock("Microsoft Corporation", "MSFT", 29.56, 29.72, randomDate()));
stocks.add(new Stock("Nokia Corporation (ADR)", "NOK", 27.83, 27.93, randomDate()));
stocks.add(new Stock("Oracle Corporation", "ORCL", 18.73, 18.98, randomDate()));
return stocks;
}


private Date randomDate() {
DateWrapper w = new DateWrapper();
int r = (int) (Math.random() * 10) * 10;
w = w.addDays(-r);
return w.asDate();
}


public interface StockProperties extends PropertyAccess<Stock> {
@Path("symbol")
ModelKeyProvider<Stock> key();


ValueProvider<Stock, String> name();


ValueProvider<Stock, String> symbol();


ValueProvider<Stock, Double> last();


ValueProvider<Stock, Double> change();


ValueProvider<Stock, Date> lastTrans();


ValueProvider<Stock, String> industry();
}


@Override
public void onModuleLoad() {
Viewport viewport = new Viewport();
RootPanel.get().add(viewport);
viewport.add(createGrid());
}
}

acerberus
16 Dec 2015, 9:01 AM
Hi,

thanks for the quick reply.

I am not quite sure this works, or I am missing something. The mismanaged event in the onClick method does not occur in the ColumnHeader class but in the inner class Head. So here is what I've tried



public void onModuleLoad() {
ColumnConfig<String, String> nameCol = new ColumnConfig<String, String>(new IdentityValueProvider<String>(), 150, "Test");

List<ColumnConfig<String, ?>> columns = new ArrayList<ColumnConfig<String, ?>>();
columns.add(nameCol);

ColumnModel<String> cm = new ColumnModel<String>(columns);

HeaderGroupConfig hgc = new HeaderGroupConfig(new TextField(), 1, 1);
cm.addHeaderGroup(0, 0, hgc);

ListStore<String> store = new ListStore<String>(new ModelKeyProvider<String>() {
@Override
public String getKey(String item) {
return item;
}
});
store.add("foo");
store.add("bar");

final Grid<String> grid = new Grid<String>(store, cm);

grid.getView().setColumnHeader(new ColumnHeader<String>(grid, cm){
protected Head createNewHead(ColumnConfig config) {
return new Head(config){
@Override
public void onBrowserEvent(Event event) {
switch (event.getTypeInt()) {
case Event.ONCLICK:
onClick(event);
return;
default:
}
super.onBrowserEvent(event);
}


protected void onClick(Event ce) {
onHeaderClick(ce, column);
}
};
}
});

RootPanel.get().add(grid);
}


(for simplicity I did not put the violater pattern in here and reduced the onClick method to only handle basic clicks and no dropdown clicks.)

This should have prevented any event canceling in case of a click event, but the outcome is still the same. The text field is still not acting normal. Any idea where further event canceling could take place? Also it seems that the onBrowserEvent method is not called when I click on the text field, but only if I click on the column header below the text field.

Regards,
Arno

branflake2267
18 Dec 2015, 11:36 AM
Sorry for the delay. I'm still trying to find the event obstruction. It's not obvious so I'm still digging.

branflake2267
18 Dec 2015, 11:54 AM
I was going to hunt for it in super dev mode, and noticed it seemed to work fine. Would you like to try super dev mode? If that works in SDM it probably works fine in web mode or production. That might be a workaround for the moment as I look for more.

acerberus
19 Dec 2015, 2:56 AM
You are right in super dev mode and in production mode the bug does not occur even without fiddling to the event handling within the column header.

Cheers
-Arno