eECHO BLOG

A journey of a thousand miles starts with a single step.

Auto-Completion Textbox GWT

At this moment GWT have in library autocomplete text widgets.. Libraries like GXT extend this functional widgets to search in Models and go very far with it.

Alternatively, you can create your own implementation of CompletionItems interface (see below) to get a list of items using RPC.

The autocomplete textbox is, to most people, the prototypical Web 2. Ajax tool. No Ajaxiefied site would be comp)lete without it! Heppily, this is quite simple to implement using the GWT.

For this, you’ll need a database, and as always, you’ll lean on good old test to help you out. Let’s add some spice to the search page and put some utocomplete goodness in place for the search functionality.

Source code

CompletionItems.java:

package com.gwt.components.client;

public interface CompletionItems {
       /**
         * Returns an array of all completion items matching
         * @param match The user-entered text all compleition items have to match
         * @return      Array of strings
         */
       public String[] getCompletionItems(String match);

AutoCompleteTextBox.java:

package com.gwt.components.client;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

public class AutoCompleteTextBox extends TextBox
    implements KeyboardListener, ChangeListener {
   
protected PopupPanel choicesPopup = new PopupPanel(true);
protected ListBox choices = new ListBox();
protected CompletionItems items = new SimpleAutoCompletionItems(new String[]{});
protected boolean popupAdded = false;
protected boolean visible = false;
   
/**
   * Default Constructor
   *
   */
public AutoCompleteTextBox()
{
    super();
    this.addKeyboardListener(this);
    choices.addChangeListener(this);
    this.setStyleName("AutoCompleteTextBox");
      
    choicesPopup.add(choices);
    choicesPopup.addStyleName("AutoCompleteChoices");
      
    choices.setStyleName("list");
}

/**
   * Sets an "algorithm" returning completion items
   * You can define your own way how the textbox retrieves autocompletion items
   * by implementing the CompletionItems interface and setting the according object
   * @see SimpleAutoCompletionItem
   * @param items CompletionItem implementation
   */
public void setCompletionItems(CompletionItems items)
{
    this.items = items;
}
   
/**
   * Returns the used CompletionItems object
   * @return CompletionItems implementation
   */
public CompletionItems getCompletionItems()
{
    return this.items;
}
   
/**
   * Not used at all
   */
public void onKeyDown(Widget arg0, char arg1, int arg2) {
}

/**
   * Not used at all
   */
public void onKeyPress(Widget arg0, char arg1, int arg2) {
}

/**
   * A key was released, start autocompletion
   */
public void onKeyUp(Widget arg0, char arg1, int arg2) {
    if(arg1 == KEY_DOWN)
    {
      int selectedIndex = choices.getSelectedIndex();
      selectedIndex++;
      if(selectedIndex > choices.getItemCount())
      {
       selectedIndex = 0;
      }
      choices.setSelectedIndex(selectedIndex);
         
      return;
    }
      
    if(arg1 == KEY_UP)
    {
      int selectedIndex = choices.getSelectedIndex();
      selectedIndex–;
      if(selectedIndex < 0)
      {
       selectedIndex = choices.getItemCount();
      }
      choices.setSelectedIndex(selectedIndex);
         
      return;      
    }
      
    if(arg1 == KEY_ENTER)
    {
      if(visible)
      {
       complete();
      }
         
      return;
    }
      
    if(arg1 == KEY_ESCAPE)
    {
      choices.clear();
      choicesPopup.hide();
      visible = false;
         
      return;
    }
      
    String text = this.getText();
    String[] matches = new String[]{};
    if(text.length() > 0)
    {
      matches = items.getCompletionItems(text);
    }
      
    if(matches.length > 0)
    {
      choices.clear();
         
      for(int i = 0; i < matches.length; i++)
      {
       choices.addItem((String) matches[i]);
      }
         
      // if there is only one match and it is what is in the
      // text field anyways there is no need to show autocompletion
      if(matches.length == 1 && matches[0].compareTo(text) == 0)
      {
       choicesPopup.hide();
      } else {
       choices.setSelectedIndex(0);
       choices.setVisibleItemCount(matches.length + 1);
               
       if(!popupAdded)
       {
          RootPanel.get().add(choicesPopup);
          popupAdded = true;
       }
       choicesPopup.show();
       visible = true;
       choicesPopup.setPopupPosition(this.getAbsoluteLeft(),
       this.getAbsoluteTop() + this.getOffsetHeight());
       //choicesPopup.setWidth(this.getOffsetWidth() + "px");
       choices.setWidth(this.getOffsetWidth() + "px");
      }

    } else {
      visible = false;
      choicesPopup.hide();
    }
}

/**
   * A mouseclick in the list of items
   */
public void onChange(Widget arg0) {
    complete();
}

public void onClick(Widget arg0) {
    complete();
}
   
// add selected item to textbox
protected void complete()
{
    if(choices.getItemCount() > 0)
    {
      this.setText(choices.getItemText(choices.getSelectedIndex()));
    }
      
    choices.clear();
    choicesPopup.hide();
}
}

AutoCompleteTextArea.java:

package com.gwt.components.client;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;

public class AutoCompleteTextArea extends TextArea
    implements KeyboardListener, ChangeListener {
   
protected PopupPanel choicesPopup = new PopupPanel(true);
protected ListBox choices = new ListBox();
protected CompletionItems items = new SimpleAutoCompletionItems(new String[]{});
protected boolean popupAdded = false;
protected String typedText = "";
protected boolean visible = false;
   
protected int posy = -1;
   
/**
   * Default Constructor
   *
   */
public AutoCompleteTextArea()
{
    super();
    this.addKeyboardListener(this);
    choices.addChangeListener(this);
    this.setStyleName("AutoCompleteTextArea");
      
    choicesPopup.add(choices);
    choicesPopup.addStyleName("AutoCompleteChoices");
      
    choices.setStyleName("list");
}

/**
   * Sets an "algorithm" returning completion items
   * You can define your own way how the textbox retrieves autocompletion items
   * by implementing the CompletionItems interface and setting the according object
   * @see SimpleAutoCompletionItem
   * @param items CompletionItem implementation
   */
public void setCompletionItems(CompletionItems items)
{
    this.items = items;
}
   
/**
   * Returns the used CompletionItems object
   * @return CompletionItems implementation
   */
public CompletionItems getCompletionItems()
{
    return this.items;
}
   
public void onKeyDown(Widget sender, char keyCode, int modifiers) {
}

public void onKeyPress(Widget sender, char keyCode, int modifiers) {
}

public void onKeyUp(Widget sender, char keyCode, int modifiers) {
    if(keyCode == KEY_DOWN)
    {
      int selectedIndex = choices.getSelectedIndex();
      selectedIndex++;
      if(selectedIndex > choices.getItemCount())
      {
       selectedIndex = 0;
      }
      choices.setSelectedIndex(selectedIndex);
         
      return;
    }
      
    if(keyCode == KEY_UP)
    {
      int selectedIndex = choices.getSelectedIndex();
      selectedIndex–;
      if(selectedIndex < 0)
      {
       selectedIndex = choices.getItemCount();
      }
      choices.setSelectedIndex(selectedIndex);
         
      return;      
    }
      
    if(keyCode == KEY_ENTER)
    {
      if(visible)
      {
       complete();
      }
         
      return;
    }
      
    if(keyCode == KEY_ESCAPE)
    {
      choices.clear();
      choicesPopup.hide();
      visible = false;
         
      return;
    }
      
    String text = this.getText();
    String[] matches = new String[]{};
      
    String[] words = text.split(" |\n|\r");
    text = words[words.length - 1];
    typedText = text;
      
    if(text.length() > 0)
    {
      matches = items.getCompletionItems(text);
    }
      
    if(matches.length > 0)
    {
      choices.clear();
         
      for(int i = 0; i < matches.length; i++)
      {
       choices.addItem((String) matches[i]);
      }
         
      // if there is only one match and it is what is in the
      // text field anyways there is no need to show autocompletion
      if(matches.length == 1 && matches[0].compareTo(text) == 0)
      {
       choicesPopup.hide();
      } else {
       choices.setSelectedIndex(0);
       choices.setVisibleItemCount(matches.length + 1);
               
       if(!popupAdded)
       {
          RootPanel.get().add(choicesPopup);
          popupAdded = true;
       }
       choicesPopup.show();
       visible = true;
       int nposy = this.getAbsoluteTop() + this.getOffsetHeight();
       if(posy < 0 || nposy > posy)
       {
          posy = nposy;
       }
       choicesPopup.setPopupPosition(this.getAbsoluteLeft(), posy);
       //choicesPopup.setWidth(this.getOffsetWidth() + "px");
       choices.setWidth(this.getOffsetWidth() + "px");
      }

    } else {
      choicesPopup.hide();
      visible = false;
    }
}

public void onChange(Widget sender) {
    complete();
}

// add selected item to textarea
protected void complete()
{
    if(choices.getItemCount() > 0)
    {
      String text = this.getText();
      text = text.substring(0, text.length() – typedText.length() – 1);
      text += choices.getItemText(choices.getSelectedIndex());
      this.setText(text);
      this.setFocus(true);
    }
      
    choices.clear();
    choicesPopup.hide();
}
}

SimpleAutoCompletionItems.java:

package com.gwt.components.client;

import java.util.ArrayList;

public class SimpleAutoCompletionItems implements CompletionItems {
private String[] completions;

public SimpleAutoCompletionItems(String[] items)
{
    completions = items;
}

public String[] getCompletionItems(String match) {
    ArrayList matches = new ArrayList();
    for (int i = 0; i < completions.length; i++) {
      if (completions[i].toLowerCase().startsWith(match.toLowerCase())) {
       matches.add(completions[i]);
      }
    }
    String[] returnMatches = new String[matches.size()];
    for(int i = 0; i < matches.size(); i++)
    {
      returnMatches[i] = (String)matches.get(i);
    }
    return returnMatches;
}
}

Comments are closed.