Hi all,

I'm currently evaluating the possibility to use a SuggestBox as a table 
cell. As a proof of concept I copied the code from EditTextCell and added 
the creation of the SuggestBox to the edit method (please ignore the  map 
holding all SuggestBox instances, this is just for debugging purposes):

---
protected void edit(Context context, Element parent, String value)
{
setValue(context, parent, value);
InputElement input = getInputElement(parent);

input.focus();
input.select();

if (!suggestBoxes.containsKey(getKeyFromKontext(context)))
{
TextBox textBox = new MyTextBox(input);

parent.replaceChild(input, textBox.getElement()).getOwnerDocument();

SuggestBox suggestBox = new SuggestBox(getSuggestOracle(), textBox);
suggestBoxes.put(getKeyFromKontext(context), suggestBox);
}
 }
---

All relevant browser events are rerouted to the TextBox contained in the 
SuggestBox and it works like a charm, except for one little detail, that 
drives me crazy. The first activated SuggestBox (no matter which row) is 
always drawn on the upper left corner of the page, the second and all 
others ones a drawn correctly below the textbox. The problem seems to 
reside inside the PopupPanel around line 1191:

---
int textBoxOffsetWidth = relativeObject.getOffsetWidth();
---

for the first Box, relativeObject.getOffsetWidth() always returns zero. 

I attached a hopefully working example, any hints on this?

Regards,

   Pelle

-- 
You received this message because you are subscribed to the Google Groups 
"Google Web Toolkit" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/google-web-toolkit/-/t-ZHJmlcerAJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-web-toolkit?hl=en.


import java.util.ArrayList;

import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;

public class CellTableTest implements EntryPoint
{
	public class SomeDTO
	{
		private String a;

		public SomeDTO(String a)
		{
			this.a = a;
		}

		public String getA()
		{
			return a;
		}

		public void setA(String a)
		{
			this.a = a;
		}

	}

	private void addColumns(CellTable<SomeDTO> cellTable)
	{
		Column<SomeDTO, String> colA = new Column<SomeDTO, String>(new SuggestCell())
		{
			@Override
			public String getValue(SomeDTO object)
			{
				return object.getA();
			}
		};
		colA.setFieldUpdater(new FieldUpdater<SomeDTO, String>()
		{
			@Override
			public void update(int index, SomeDTO object, String value)
			{
			}
		});
		
		cellTable.addColumn(colA, "Column A");
	}


	private ArrayList<SomeDTO> getData()
	{
		ArrayList<SomeDTO> tableData = new ArrayList<SomeDTO>();
		tableData.add(new SomeDTO("AAA"));
		tableData.add(new SomeDTO("BBB"));
		tableData.add(new SomeDTO("CCC"));
		tableData.add(new SomeDTO("DDD"));
		tableData.add(new SomeDTO("EEE"));
		return tableData;
	}

	@Override
	public void onModuleLoad()
	{
		CellTable<SomeDTO> cellTable = new CellTable<SomeDTO>();
		cellTable.setSelectionModel(new SingleSelectionModel<SomeDTO>());

		addColumns(cellTable);

		ListDataProvider<SomeDTO> listDataProvider = new ListDataProvider<SomeDTO>();
		listDataProvider.setList(getData());
		listDataProvider.addDataDisplay(cellTable);

		RootPanel.get().add(cellTable);

	}

}

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.cell.client.AbstractEditableCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.client.SafeHtmlTemplates.Template;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.text.shared.SimpleSafeHtmlRenderer;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.TextBox;

public class SuggestCell extends AbstractEditableCell<String, SuggestCell.ViewData>
{

	private class MyTextBox extends TextBox
	{
		public MyTextBox(Element element)
		{
			super(element);
		}
	}

	interface Template extends SafeHtmlTemplates
	{
		@Template("<input type=\"text\" value=\"{0}\" tabindex=\"-1\"></input>")
		SafeHtml input(String value);
	}

	static class ViewData
	{

		private boolean isEditing;

		private boolean isEditingAgain;

		private String original;

		private String text;

		public ViewData(String text)
		{
			this.original = text;
			this.text = text;
			this.isEditing = true;
			this.isEditingAgain = false;
		}

		@Override
		public boolean equals(Object o)
		{
			if (o == null)
			{
				return false;
			}
			ViewData vd = (ViewData) o;
			return equalsOrBothNull(original, vd.original) && equalsOrBothNull(text, vd.text) && isEditing == vd.isEditing
					&& isEditingAgain == vd.isEditingAgain;
		}

		private boolean equalsOrBothNull(Object o1, Object o2)
		{
			return (o1 == null) ? o2 == null : o1.equals(o2);
		}

		public String getOriginal()
		{
			return original;
		}

		public String getText()
		{
			return text;
		}

		@Override
		public int hashCode()
		{
			return original.hashCode() + text.hashCode() + Boolean.valueOf(isEditing).hashCode() * 29 + Boolean.valueOf(isEditingAgain).hashCode();
		}

		public boolean isEditing()
		{
			return isEditing;
		}

		public boolean isEditingAgain()
		{
			return isEditingAgain;
		}

		public void setEditing(boolean isEditing)
		{
			boolean wasEditing = this.isEditing;
			this.isEditing = isEditing;

			// This is a subsequent edit, so start from where we left off.
			if (!wasEditing && isEditing)
			{
				isEditingAgain = true;
				original = text;
			}
		}

		public void setText(String text)
		{
			this.text = text;
		}
	}

	private static Template template;

	private final SafeHtmlRenderer<String> renderer;

	private Map<String, SuggestBox> suggestBoxes = new HashMap<String, SuggestBox>();

	public SuggestCell()
	{
		this(SimpleSafeHtmlRenderer.getInstance());
	}

	public SuggestCell(SafeHtmlRenderer<String> renderer)
	{
		super("click", "keyup", "keydown", "blur");
		if (template == null)
		{
			template = GWT.create(Template.class);
		}
		if (renderer == null)
		{
			throw new IllegalArgumentException("renderer == null");
		}
		this.renderer = renderer;
	}

	private void cancel(Context context, Element parent, String value)
	{
		clearInput(getInputElement(parent));
		setValue(context, parent, value);
	}

	private native void clearInput(Element input) /*-{
													if (input.selectionEnd)
													input.selectionEnd = input.selectionStart;
													else if ($doc.selection)
													$doc.selection.clear();
													}-*/;

	private void commit(Context context, Element parent, ViewData viewData, ValueUpdater<String> valueUpdater, NativeEvent event)
	{

		String value = updateViewData(context, parent, viewData, false, event);
		clearInput(getInputElement(parent));
		setValue(context, parent, viewData.getOriginal());
		
		if (valueUpdater != null)
		{
			valueUpdater.update(value);
		}

	}

	private String getKeyFromKontext(Context context)
	{
		return context.getColumn() + "" + context.getIndex();
	}
	
	protected void edit(Context context, Element parent, String value)
	{
		setValue(context, parent, value);
		InputElement input = getInputElement(parent);

		input.focus();
		input.select();

		if (!suggestBoxes.containsKey(getKeyFromKontext(context)))
		{
			TextBox textBox = new MyTextBox(input);

			parent.replaceChild(input, textBox.getElement()).getOwnerDocument();

			SuggestBox suggestBox = new SuggestBox(getSuggestOracle(), textBox);
			suggestBoxes.put(getKeyFromKontext(context), suggestBox);
		}
		
	}

	private void editEvent(Context context, Element parent, String value, ViewData viewData, NativeEvent event, ValueUpdater<String> valueUpdater)
	{
		String type = event.getType();
		boolean keyUp = "keyup".equals(type);
		boolean keyDown = "keydown".equals(type);

		if (keyUp || keyDown)
		{
			int keyCode = event.getKeyCode();
			if (keyUp && keyCode == KeyCodes.KEY_ENTER)
			{
				// Commit the change.
				commit(context, parent, viewData, valueUpdater, event);
			}
			else if (keyUp && keyCode == KeyCodes.KEY_ESCAPE)
			{
				// Cancel edit mode.
				String originalText = viewData.getOriginal();
				if (viewData.isEditingAgain())
				{
					viewData.setText(originalText);
					viewData.setEditing(false);
				}
				else
				{
					setViewData(context.getKey(), null);
				}
				cancel(context, parent, value);
			}
			else
			{
				// Update the text in the view data on each key.
				updateViewData(context, parent, viewData, true, event);
			}
		}
		else if ("blur".equals(type))
		{
			// Commit the change. Ensure that we are blurring the input element
			// and not the parent element itself.
//			EventTarget eventTarget = event.getEventTarget();
//			if (Element.is(eventTarget))
//			{
//				Element target = Element.as(eventTarget);
//				if ("input".equals(target.getTagName().toLowerCase()))
//				{
//					commit(context, parent, viewData, valueUpdater, event);
//				}
//			}
		}
	}

	/**
	 * Get the input element in edit mode.
	 */
	private InputElement getInputElement(Element parent)
	{
		return parent.getFirstChild().<InputElement> cast();
	}

	private SuggestOracle getSuggestOracle()
	{
		final SuggestOracle so = new SuggestOracle()
		{

			@Override
			public void requestSuggestions(final Request request, Callback callback)
			{
				List<Suggestion> suggestions = new ArrayList<SuggestOracle.Suggestion>();
				
				for (int i = 0; i < 10; i++)
				{
					final int o = i;
					
					suggestions.add(new Suggestion()
					{

						@Override
						public String getDisplayString()
						{
							return request.getQuery() + Integer.toString(o);
						}

						@Override
						public String getReplacementString()
						{
							return request.getQuery() + Integer.toString(o);
						}
					});
				}
				

				Response response = new Response();
				response.setSuggestions(suggestions);

				callback.onSuggestionsReady(request, response);

			}
		};

		return so;
	}

	@Override
	public boolean isEditing(Context context, Element parent, String value)
	{
		ViewData viewData = getViewData(context.getKey());
		return viewData == null ? false : viewData.isEditing();
	}

	@Override
	public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater)
	{
		String type = event.getType();
		int keyCode = event.getKeyCode();

		Object key = context.getKey();
		ViewData viewData = getViewData(key);
		if (viewData != null && viewData.isEditing())
		{
			// Handle the edit event.
			editEvent(context, parent, value, viewData, event, valueUpdater);
		}
		else
		{
			boolean enterPressed = "keyup".equals(type) && keyCode == KeyCodes.KEY_ENTER;
			if ("click".equals(type) || enterPressed)
			{
				// Go into edit mode.
				if (viewData == null)
				{
					viewData = new ViewData(value);
					setViewData(key, viewData);
				}
				else
				{
					viewData.setEditing(true);
				}
				edit(context, parent, value);
			}
		}
	}

	@Override
	public void render(Context context, String value, SafeHtmlBuilder sb)
	{
		Object key = context.getKey();
		ViewData viewData = getViewData(key);

		if (viewData != null && !viewData.isEditing() && value != null && value.equals(viewData.getText()))
		{
			clearViewData(key);
			viewData = null;
		}

		if (viewData != null)
		{
			String text = viewData.getText();
			if (viewData.isEditing())
			{
				sb.append(template.input(text));
			}
			else
			{
				sb.append(renderer.render(text));
			}
		}
		else if (value != null)
		{
			sb.append(renderer.render(value));
		}

	}

	@Override
	public boolean resetFocus(Context context, Element parent, String value)
	{
		if (isEditing(context, parent, value))
		{
			getInputElement(parent).focus();
			return true;
		}
		return false;
	}

	private String updateViewData(Context context, Element parent, ViewData viewData, boolean isEditing, NativeEvent event)
	{
		InputElement input = (InputElement) parent.getFirstChild();
		String value = input.getValue();
		viewData.setText(value);
		viewData.setEditing(isEditing);


		SuggestBox suggestBox = suggestBoxes.get(getKeyFromKontext(context));
		
		if (suggestBox != null)
		{
			suggestBox.setText(value);
			DomEvent.fireNativeEvent(event, suggestBox.getTextBox());
		}
		
		return value;
	}
}

Reply via email to