I have a Datagrid with a CheckboxCell column. There will be some rows that cannot be checked, and the way I would like to implement this would be to handle the "checked" event and cancel it if some criteria is met. Here is the code I have tried:
Column<Job, Boolean> selectColumn = new Column<Job, Boolean>(new CheckboxCell()) {
#Override
public Boolean getValue(Job job) {
// do I uncheck the cell here?
return JobDataGrid.this.jobSelection.isSelected(job);
}
#Override
public void onBrowserEvent(Cell.Context context, Element elem, Job object, NativeEvent event)
{
super.onBrowserEvent(context, elem, object, event);
String eventType = event.getType();
if ("change".equals(eventType)) {
// do I uncheck the cell here?
}
}
};
How do I handle the event and set the checkbox to be unchecked?
If you want to prevent the change in a checkbox, you can simply cancel the native event inside your onBrowserEvent method:
event.preventDefault();
Note that if you don't update your object when a checkbox is clicked, you can always refresh() your DataGrid and the checkbox will be displayed in its original state.
If you want to make a check box uncheckable, from the UX perspective it is better be disabled.
You can create a custom check box cell where you can control every aspect of rendering of the element including disabled state:
public class UncheckableCheckboxCell extends CheckboxCell {
interface Template extends SafeHtmlTemplates {
#Template("<input type=\"checkbox\" tabindex=\"-1\" checked/>")
SafeHtml INPUT_CHECKED();
#Template("<input type=\"checkbox\" tabindex=\"-1\"/>")
SafeHtml INPUT_UNCHECKED();
#Template("<input type=\"checkbox\" tabindex=\"-1\" disabled=\"disabled\"/>")
SafeHtml INPUT_UNCHECKED_DISABLED();
}
private static UncheckableCheckboxCell.Template template = GWT.create(UncheckableCheckboxCell.Template.class);
public UncheckableCheckboxCell(boolean dependsOnSelection, boolean handlesSelection) {
super(dependsOnSelection, handlesSelection);
}
#Override
public void render(Context context, Boolean value, SafeHtmlBuilder sb) {
// Get the view data.
Object key = context.getKey();
Boolean viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
if (value != null && ((viewData != null) ? viewData : value)) {
sb.append(template.INPUT_CHECKED());
} else if (value == null) {
//use null value as an indicator of unchecked and disable state
sb.append(template.INPUT_UNCHECKED_DISABLED());
} else {
sb.append(template.INPUT_UNCHECKED());
}
}
}
Then inside your getValue() method you can return null when you want the check box to be unchecked/disabled:
#Override
public Boolean getValue(Job job) {
// is my job checkable?
if (job.checkable()) {
//return null explicitly so my custom cell knows it should be rendered as disabled
return null;
} else {
return JobDataGrid.this.jobSelection.isSelected(job);
}
}
Related
I have a table with a nameColumn.
I want that when user mouseOver the title of nameColumn it will trigger a method
I tried:
Header<String> nameColumnHeader = new Header<String>(new ClickableTextCell()) {
#Override
public String getValue() {
return "Name";
}
#Override
public final void onBrowserEvent(Context context, Element elem, NativeEvent event) {
if ("mouseover".equals(event.getType())) {
//
meaningMessagesPopup.show();
}
else if("mouseout".equals(event.getType())){
meaningMessagesPopup.hide();
}
}
};
table.addColumn(nameColumn, nameColumnHeader);
But seem Gwt did not recognize "mouseover".equals(event.getType())
Do you know how to do the MOUSEOVER event in GWT Header?
i found the answer, that is to create a CustomCell that extends AbstractCell
private class HeaderCell extends AbstractCell<String> {
private String text;
public HeaderCell(String text) {
/*
* Let the parent class know that our cell responds to click events and
* keydown events.
*/
//super("click", "keydown");
super("mouseover");
this.text=text;
}
#Override
public void onBrowserEvent(Context context, Element parent, String value,
NativeEvent event, ValueUpdater<String> valueUpdater) {
// Check that the value is not null.
if (value == null) {
return;
}
// Call the super handler, which handlers the enter key.
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("mouseover".equals(event.getType())) {
SafeHtmlBuilder sb=new SafeHtmlBuilder();
sb.appendHtmlConstant("<b>");
sb.appendHtmlConstant("<font color=\"blue\">");
sb.appendEscaped(text);
sb.appendHtmlConstant("</font></b>");
meaningMessagesPopup.setWidget(new HTML(sb.toSafeHtml()));
int left = event.getClientX() -140;
int top = event.getClientY() +30;
meaningMessagesPopup.setPopupPosition(left, top);
// Show the popup
meaningMessagesPopup.show();
}
else if ("mouseout".equals(event.getType())) {
meaningMessagesPopup.hide();
}
}
#Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
/*
* Always do a null check on the value. Cell widgets can pass null to
* cells if the underlying data contains a null, or if the data arrives
* out of order.
*/
if (value == null) {
return;
}
sb.appendEscaped(value);
}
}
Then
Header<String> nameColumnHeader = new Header<String>(new HeaderCell("my Text...")) {
#Override
public String getValue() {
return "Name";
}
};
table.addColumn(nameColumn, nameColumnHeader);
I have DataGrid where one on of the columns contains images. I used this code to generate the column.
Column<Job, String> expandHideColumn = new Column<Job, String>(
imageCell) {
#Override
public String getValue(Job object) {
return null;
}
#Override
public void render(Context context, Job Object, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<img src='images/expand.jpeg' style='cursor: pointer' />");
}
}
What I want is on clicking the image it has to change. For this I added a click handler on the ImageCell like this
ImageCell imageCell = new ImageCell() {
#Override
public Set<String> getConsumedEvents() {
Set<String> events = new HashSet<String>();
events.add("click");
return events;
}
};
In the onBrowserEvent method I wrote this
#Override
public void onBrowserEvent(Context context, Element element,
Job job, NativeEvent event) {
if (element.getFirstChildElement().isOrHasChild(
Element.as(event.getEventTarget()))) {
if (element.getFirstChildElement().getPropertyString("src")
.matches("(.*)expand.jpeg")) {
element.getFirstChildElement().setPropertyString("src",
"images/collapse.jpeg");
} else {
element.getFirstChildElement().setPropertyString("src",
"images/expand.jpeg");
}
}
}
I don't think this is a good approach to change images on click event. Is there a better solution?
You can use a column value for know the state of the column :
Column<Job, Boolean> expandHideColumn = new Column<Job, Boolean>(new ImageExpandCollapseCell()) {
#Override
public Boolean getValue(Job object) {
return object.isExpand(); //The object know the expand state ?
}
}
expandHideColumn.setValueUpdater(new FieldUpdater<Job, Boolean>() {
void update(int index, Job object, Boolean value) {
object.setExpand(value);
}
});
The ImageExpandCollapseCell look like this :
public class ImageExpandCollapseCell extends AbstractCell<Boolean> {
final String EXPAND = "images/expand.jpeg";
final String COLLAPSE = "images/collapse.jpeg";
interface Template extends SafeHtmlTemplates {
#Template("<div style=\"float:right\"><img src=\"" + url + "\"></div>")
SafeHtml img(String url);
}
private static Template template;
/**
* Construct a new ImageCell.
*/
public ImageCell() {
super("click"); //Replace your getConsumedEvents()
if (template == null) {
template = GWT.create(Template.class);
}
}
#Override
public void render(Context context, Boolean value, SafeHtmlBuilder sb) {
if (value != null) {
sb.append(template.img(UriUtils.fromSafeConstant(value ? EXPAND : COLLAPSE)));
}
}
#Override
public void onBrowserEvent(Context context, Element element,
Boolean value, NativeEvent event, ValueUpdater<Boolean> valueUpdater) {
valueUpdate.update(!value);
}
}
I improve the proposed version of user905374
It's not a good idea to instantiate new value in the render method.
The column render method call the Cell render method, you musn't replace it !
With the FieldUpdater, you can change the state of the image : expand or collapse and update the cell display (it will be rendered again).
I want to make that some cells of the rows can be non-editable.
by now my solution is when i create the columns, if one is readOnly, y make a TextCell, if not, i go with the default Cell wich can be EditTextCell,DatePickerCell,etc.
The problem with this is that i can't make some rows readOnly and others not. Or they are ALL the fields readOnly or they are not.
How can i do to make this for example
TABLE:
Data1 | Data2 | Data3
--------------------------------------
readOnly | non-readOnly | readOnly
readOnly | readOnly | non-readOnly
when i mean "readOnly" it can be "enabled" or make it a "TextCell"
celda = new TextInputCell();
Column<ObjetoDato, String> columna = new Column<ObjetoDato, String>(celda) {
#Override
public String getValue(ObjetoDato object) {
if(actual.getValorDefault()!=null && object.getValor(actual.getNombreCampo()).isEmpty()){
object.setValor(actual.getNombreCampo(), actual.getValorDefault());
return actual.getValorDefault();
}
return object.getValor(actual.getNombreCampo());
}
};
tabla.agregarColumna(columna, actual.getCaption());
columna.setFieldUpdater(new FieldUpdater<ObjetoDato, String>() {
#Override
public void update(int index, ObjetoDato object, String value) {
object.setValor(actual.getNombreCampo(), value);
new Scripter(object,actual.getComportamiento(),true);
tabla.actualizar();
Sistema.get().getIG().actualizarTotales();
}
});
I tried creating my cutom cell already and replacing the TextImputCell, but the methods never trigger
celda = new FabriCel();
and
public class FabriCel extends TextInputCell {
private String campo;
public FabriCel(String campo){
this.campo=campo;
}
#Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater){
Boolean editable = false;///get it from your model
if(editable != null && !editable){
event.preventDefault();
}else{
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}
Also this
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, String value, SafeHtmlBuilder sb) {
Boolean editable = false;///get it from your model
if(editable){
Log.log();
sb.appendHtmlConstant("<div contentEditable='false'>" +value+"</div>");
}else{
Log.log("No entra");
super.render(context, value, sb);
}
}
Thanks!
You have to create one custom cell. In that, you have tell runtime like it should be readonly or no-readonly. just example.
private class CustomCell extends EditTextCell {
public void render(com.google.gwt.cell.client.Cell.Context context,
String value, SafeHtmlBuilder sb) {
Data data=context.getKey();
if(data.isReadOnly()){
sb.appendHtmlConstant("<div contentEditable='false'
unselectable='false' >" +value+"</div>");
}else{
super.render(context, value, sb);
}
}
}
In given bean, there is some condition which says readonly or no-readonly.
And create column like
Column<Data, String> nameColumn = new Column<Data, String>(new CustomCell()) {
#Override
public String getValue(Data object) {
return object.getName();
}
};
A way to do this is to override the onBrowserEvent event of your Editable Cells and consume the event if the cell is not editable.
final EditTextCell cell = new EditTextCell(renderer)
{
#Override
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater)
{
Boolean editable = false;///get it from your model
if(editable != null && !editable)
{
event.preventDefault();
}
else
{
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}
}
I had the same need; and tested out various combinations of overriding render, isEditing, resetFocus, and edit on EditTextCell (I didn't try the onBrowserEvent solution).
When I only overrode render (to show an HTML value if non-editable); I got errors resetting focus (as discussed here). This continued even if I overrode resetFocus. When I only override isEditing, the cell would flash to editing when clicked, and then flash back. What worked perfectly was overriding edit. I triggered based on adding a tag to the value passed in by Column.getValue, you can trigger however you like, but it turned out to be as simple as:
private static class LockableEditTextCell extends EditTextCell {
#Override
protected void edit(Context context, Element parent, java.lang.String value) {
if (!value.startsWith(LOCKED_CELL_VALUE)) {
super.edit(context, parent, value);
}
}
}
I am trying to create a CellTable that has a column with some text and a checkbox, which will be used as a select all checkbox (see the drawing below, "cb" is checkbox). Currently I am using an class derived from Header and overriding it's render method to output the text and a checkbox. I am overriding onBrowserEvent() however it is only giving me onChange events, which would work fine except that the checkbox doesn't function correctly. Does anyone have any ideas on this?
+-------+------------+
| col 1 | Select All |
| | cb |
+-------+------------+
| row 1 | cb |
+-------+------------+
The issues I'm having with the checkbox is that when it's not checked, you have to click it twice for the checkmark to appear (at least on Chrome), even though it's "checked" property is true the first time. One click unchecks it correctly.
Here is some code:
Setup the CellTable columns:
/** Setup the table's columns. */
private void setupTableColumns() {
// Add the first column:
TextColumn<MyObject> column1 = new TextColumn<MyObject>() {
#Override
public String getValue(final MyObject object) {
return object.getColumn1Text();
}
};
table.addColumn(macColumn, SafeHtmlUtils.fromSafeConstant("Column1"));
// the checkbox column for selecting the lease
Column<MyObject, Boolean> checkColumn = new Column<MyObject, Boolean>(
new CheckboxCell(true, false)) {
#Override
public Boolean getValue(final MyObject object) {
return selectionModel.isSelected(object);
}
};
SelectAllHeader selectAll = new SelectAllHeader();
selectAll.setSelectAllHandler(new SelectHandler());
table.addColumn(checkColumn, selectAll);
}
My Select All Header:
public static class SelectAllHeader extends Header<Boolean> {
private final String checkboxID = "selectAllCheckbox";
private ISelectAllHandler handler = null;
#Override
public void render(final Context context, final SafeHtmlBuilder sb) {
String html = "<div>Select All<div><input type=\"checkbox\" id=\"" + checkboxID + "\"/>";
sb.appendHtmlConstant(html);
}
private final Boolean allSelected;
public SelectAllHeader() {
super(new CheckboxCell());
allSelected = false;
}
#Override
public Boolean getValue() {
Element checkboxElem = DOM.getElementById(checkboxID);
return checkboxElem.getPropertyBoolean("checked");
}
#Override
public void onBrowserEvent(final Context context, final Element element, final NativeEvent event) {
Event evt = Event.as(event);
int eventType = evt.getTypeInt();
super.onBrowserEvent(context, element, event);
switch (eventType) {
case Event.ONCHANGE:
handler.onSelectAllClicked(getValue());
event.preventDefault();
break;
default:
break;
}
}
public void setSelectAllHandler(final ISelectAllHandler handler) {
this.handler = handler;
}
}
It looks like you're rendering a non-checked checkbox whenever you render the header, which could be wiping out the selection state whenever the celltable re-renders.
Try storing the checked state and rendering the checkbox with the state. It looks like you're half way there with allSelected, you're just not using it.
EDIT Here is a working implementation I've just written for Zanata (see SearchResultsView.java). The HasValue interface is implemented so that value change events can be handled in a standard way. I have not overridden the render method, if you want to do so make sure you use getValue() to determine whether you render a checked or an unchecked checkbox. The selection/de-selection logic is handled in the associated presenter class (see SearchResultsPresenter.java).
private class CheckboxHeader extends Header<Boolean> implements HasValue<Boolean> {
private boolean checked;
private HandlerManager handlerManager;
public CheckboxHeader()
{
//TODO consider custom cell with text
super(new CheckboxCell());
checked = false;
}
// This method is invoked to pass the value to the CheckboxCell's render method
#Override
public Boolean getValue()
{
return checked;
}
#Override
public void onBrowserEvent(Context context, Element elem, NativeEvent nativeEvent)
{
int eventType = Event.as(nativeEvent).getTypeInt();
if (eventType == Event.ONCHANGE)
{
nativeEvent.preventDefault();
//use value setter to easily fire change event to handlers
setValue(!checked, true);
}
}
#Override
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Boolean> handler)
{
return ensureHandlerManager().addHandler(ValueChangeEvent.getType(), handler);
}
#Override
public void fireEvent(GwtEvent<?> event)
{
ensureHandlerManager().fireEvent(event);
}
#Override
public void setValue(Boolean value)
{
checked = value;
}
#Override
public void setValue(Boolean value, boolean fireEvents)
{
checked = value;
if (fireEvents)
{
ValueChangeEvent.fire(this, value);
}
}
private HandlerManager ensureHandlerManager()
{
if (handlerManager == null)
{
handlerManager = new HandlerManager(this);
}
return handlerManager;
}
}
i want to handle double click event in a cell of a cellTree but i can't make it work.
Any idea?
Thanks.
if(((Node) value).getNodeType().equals(NodeTypes.Folder))
{
ListDataProvider<Node> dataProvider = new ListDataProvider<Node>(((Node) value).getChildNode());
Cell<Node> cell = new AbstractCell<Node>("dblclick")
{
#Override
public void render(Context context, Node value, SafeHtmlBuilder sb)
{
if (value != null) { sb.appendEscaped(value.getNodeName()); }
}
#Override
public void onBrowserEvent(Context context, Element parent, Node value,
NativeEvent event, ValueUpdater<Node> valueUpdater)
{
if (value == null) { return; }
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("dblclick".equals(event))
{
ControllerPqm.getInstance().advise(ControllerPqm.ADVISE_ERROR, "Chick");
}
}
};
return new DefaultNodeInfo<Node>(dataProvider, cell);
}
You have to extend some other Widget and sink the DoubleClick event in the constructor:
sinkEvents(Event.ONCLICK | Event.ONDBLCLICK);
and then proceed to write code to handle the sinked events. Be aware that double click has issues in Linux-version of some browsers - it tends to fire the single click event multiple times as well in linux versions, and in some older mac/win versions.
public void onBrowserEvent(Event event)
{
switch (DOM.eventGetType(event))
{
case Event.ONCLICK:
// your handler code here
break;
case Event.ONDBLCLICK:
// your handler code here
break;
}
}
Ideally in your case, this code should go in the toplevel NodeTree class itself (or something that extends a Widget), on double click it will find the node that got double-clicked and do something based on it.
You must compare the event's type property:
"dblclick".equals(event.getType())
Try this :
public class MyMenuItem {
private String name;
public MyMenuItem(String name) {
super();
this.name = name;
list = new ArrayList<MyMenuItem>();
}
//...
public boolean hasChildrens() {
return list.size()>0;
}
}
public class MyTreeModel implements TreeViewModel {
ArrayList<MyMenuItem> all;
public MyTreeModel() {
all = new ArrayList<MyMenuItem>();
//Default items
all.add(... //fill
}
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
ListDataProvider<MyMenuItem> dataProvider;
if (value == null) { // root
dataProvider = new ListDataProvider<MyMenuItem>(all);
} else {
dataProvider = new ListDataProvider<MyMenuItem>(((MyMenuItem) value).getList());
}
Cell<MyMenuItem> cell = new MyCell(); //my Cell with dblclick event
return new DefaultNodeInfo<MyMenuItem>(dataProvider, cell);
}
#Override
public boolean isLeaf(Object value) {
if (value instanceof MyMenuItem) {
MyMenuItem t = (MyMenuItem) value;
if (!t.hasChildrens())
return true;
return false;
}
return false;
}
}
Also Cell class:
private static class MyCell extends AbstractCell<MyMenuItem> {
public MyCell() {
super("keydown","dblclick"); //click?
}
#Override
public void onBrowserEvent(Context context, Element parent, MyMenuItem value,
NativeEvent event, ValueUpdater<MyMenuItem> valueUpdater) {
// Check that the value is not null.
if (value == null) {
return;
}
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("dblclick".equals(event.getType())) {
this.onEnterKeyDown(context, parent, value, event, valueUpdater);
}
}
#Override
public void render(Context context, MyMenuItem value, SafeHtmlBuilder sb) {
if (value == null) {
return;
}
sb.appendEscaped(value.getName());
}
#Override
protected void onEnterKeyDown(Context context, Element parent,
MyMenuItem value, NativeEvent event, ValueUpdater<MyMenuItem> valueUpdater) {
Window.alert("You clicked "+event.getType()+" " + value.getName());
}
}
`
In the module:
treeModel = new MyTreeModel();
CellTree tree = new CellTree(treeModel,null);
tree.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
TreeNode rootNode = tree.getRootTreeNode();
TreeNode firstPlaylist = rootNode.setChildOpen(0, true);
//...
verticalPanelWest.add(tree);
If you wand to consume a browserevent in your Cell you need to tell what are the events to be consumed.
Shows example
public class OrderCell extends AbstractCell<Order> {
public OrderCell() {
super("mousedown","mouseover","mouseup","mousemove");
}
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context,
Element parent, Order value, NativeEvent event,
ValueUpdater<Order> valueUpdater) {
System.out.print("event fired");
}