So I've been on a webapp of mine that does RPC, Google Maps, MVP, uses a ton of stock and homebrewn widgets and plenty of other things, touching most of the GWT API and, like in every other GWT App I've done so far it also uses SuggestBox. And like in every other GWT App I've written, I come to battle the usual cross browser compatibility issues, double events firing, display issues etc [1] that haunt SuggestBox.
A discouraging look at the SOYC report reveals that a whooping 17% of the code volume is courtesy of SuggestBox & friends, so I decide to write a (simpler) version (something like they [2] are doing) which results in a 15% reduction of the gzipped code. On a side note: this made me really appreciate the ingenuity of the original SuggestBox author - there is a myriad of details one needs to take care of, for instance keeping track of focus/blur events between the listbox and textbox. [1] https://code.google.com/p/google-web-toolkit/issues/list?can=2&q=suggestbox&colspec=ID+Type+Status+Owner+Milestone+Summary+Stars&cells=tiles [2] http://www.airbnb.com/ [3] import java.util.ArrayList; import java.util.List; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyEvent; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HasText; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.SuggestOracle; import com.google.gwt.user.client.ui.SuggestOracle.Request; import com.google.gwt.user.client.ui.SuggestOracle.Response; import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; import com.google.gwt.user.client.ui.TextBox; public class SuggestBox extends Composite implements BlurHandler, KeyDownHandler, ChangeHandler, ClickHandler, SuggestOracle.Callback, FocusHandler, HasText, HasValueChangeHandlers<String> { private TextBox textbox = new TextBox(); private ListBox listOfSuggestions = new ListBox(); private SuggestOracle oracle; private FlowPanel panel = new FlowPanel(); private Timer timer; private boolean hasFocus; private String lastAnnouncedValue = ""; public SuggestBox(SuggestOracle oracle) { initWidget(panel); this.oracle = oracle; panel.add(textbox); panel.add(listOfSuggestions); setStyleName("SuggestBox"); listOfSuggestions.setVisibleItemCount(10); //textbox.addKeyPressHandler(this); textbox.addKeyDownHandler(this); textbox.addFocusHandler(this); listOfSuggestions.addFocusHandler(this); listOfSuggestions.addChangeHandler(this); listOfSuggestions.addClickHandler(this); listOfSuggestions.addBlurHandler(this); textbox.addBlurHandler(this); hideListOfSuggestions(); } private void hideListOfSuggestions() { listOfSuggestions.removeStyleName("hidden"); listOfSuggestions.addStyleName("hidden"); } private void showListOfSuggestions() { listOfSuggestions.getElement().getStyle().setLeft(textbox.getAbsoluteLeft(), Unit.PX); listOfSuggestions.getElement().getStyle().setTop(textbox.getAbsoluteTop() + textbox.getOffsetHeight(), Unit.PX); listOfSuggestions.removeStyleName("hidden"); } private boolean isListOfSuggestionsShowing() { return !listOfSuggestions.getStyleName().contains("hidden"); } private void showSuggestions(String text) { oracle.requestSuggestions(new SuggestOracle.Request(text), this); } private void showSuggestionsForCurrentText() { if (timer != null) timer.cancel(); timer = new Timer() { @Override public void run() { showSuggestions(textbox.getText()); } }; timer.schedule(1); } private void putCurrentSelectionFromSuggestionsIntoTextBox() { String value = listOfSuggestions.getItemText(listOfSuggestions.getSelectedIndex()); textbox.setValue(value); } private boolean handleControls(KeyEvent event) { switch (event.getNativeEvent().getKeyCode()) { case KeyCodes.KEY_DOWN: if (!isListOfSuggestionsShowing()) return false; int index = listOfSuggestions.getSelectedIndex(); index = (index + 1) % listOfSuggestions.getItemCount(); listOfSuggestions.setSelectedIndex(index); return true; case KeyCodes.KEY_UP: if (!isListOfSuggestionsShowing()) return false; index = listOfSuggestions.getSelectedIndex(); index = index - 1; if (index < 0) index = listOfSuggestions.getItemCount() + index; listOfSuggestions.setSelectedIndex(index); return true; case KeyCodes.KEY_ENTER: if (isListOfSuggestionsShowing()) { putCurrentSelectionFromSuggestionsIntoTextBox(); hideListOfSuggestions(); } announceChangedValue(); return true; } return false; } private void pickCurrentSuggestionFromList() { textbox.setText(listOfSuggestions.getItemText(listOfSuggestions.getSelectedIndex())); hideListOfSuggestions(); textbox.setFocus(true); } @Override public void onChange(ChangeEvent event) { if (event.getSource() == textbox) { return; } if (event.getSource() == listOfSuggestions) { pickCurrentSuggestionFromList(); announceChangedValue(); return; } } @Override public void onSuggestionsReady(Request request, Response response) { listOfSuggestions.clear(); for (Suggestion suggestion : response.getSuggestions()) listOfSuggestions.addItem(suggestion.getReplacementString()); if (listOfSuggestions.getItemCount() > 0) showListOfSuggestions(); else hideListOfSuggestions(); if (!response.getSuggestions().isEmpty()) listOfSuggestions.setSelectedIndex(0); } @Override public void onFocus(FocusEvent event) { if (event.getSource() == textbox) textbox.selectAll(); hasFocus = true; } @Override public void onClick(ClickEvent event) { GWT.log(event.toString()); if (event.getSource() == listOfSuggestions) { pickCurrentSuggestionFromList(); announceChangedValue(); } } @Override public void onBlur(BlurEvent event) { defocusWidgetIfneccessary(); hasFocus = false; } private void defocusWidgetIfneccessary() { new Timer() { @Override public void run() { if (!hasFocus) hideListOfSuggestions(); announceChangedValue(); } }.schedule(100); } @Override public String getText() { return textbox.getText(); } @Override public void setText(String text) { textbox.setText(text); } private void announceChangedValue(){ String currentValue = getText(); if (lastAnnouncedValue.equals(currentValue)) return; lastAnnouncedValue = currentValue; ValueChangeEvent.fire(this, currentValue); textbox.selectAll(); } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { return addHandler(handler, ValueChangeEvent.getType()); } @Override public void onKeyDown(KeyDownEvent event) { boolean wasHandled = handleControls(event); if (!wasHandled) showSuggestionsForCurrentText(); } } -- You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group. To post to this group, send email to google-web-tool...@googlegroups.com. To unsubscribe from this group, send email to google-web-toolkit+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.