blob: a291aec9580bfc96230f3858e50fed8eae352bbc [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.client.ui;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.editor.client.adapters.TakesValueEditor;
import com.google.gwt.event.dom.client.HandlesAllKeyEvents;
import com.google.gwt.event.dom.client.HasAllKeyHandlers;
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.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
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.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.PopupPanel.AnimationType;
import com.google.gwt.user.client.ui.SuggestOracle.Callback;
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 java.util.Collection;
import java.util.List;
/**
* A {@link SuggestBox} is a text box or text area which displays a
* pre-configured set of selections that match the user's input.
*
* Each {@link SuggestBox} is associated with a single {@link SuggestOracle}.
* The {@link SuggestOracle} is used to provide a set of selections given a
* specific query string.
*
* <p>
* By default, the {@link SuggestBox} uses a {@link MultiWordSuggestOracle} as
* its oracle. Below we show how a {@link MultiWordSuggestOracle} can be
* configured:
* </p>
*
* <pre>
* MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
* oracle.add("Cat");
* oracle.add("Dog");
* oracle.add("Horse");
* oracle.add("Canary");
*
* SuggestBox box = new SuggestBox(oracle);
* </pre>
*
* Using the example above, if the user types "C" into the text widget, the
* oracle will configure the suggestions with the "Cat" and "Canary"
* suggestions. Specifically, whenever the user types a key into the text
* widget, the value is submitted to the <code>MultiWordSuggestOracle</code>.
*
* <p>
* Note that there is no method to retrieve the "currently selected suggestion"
* in a SuggestBox, because there are points in time where the currently
* selected suggestion is not defined. For example, if the user types in some
* text that does not match any of the SuggestBox's suggestions, then the
* SuggestBox will not have a currently selected suggestion. It is more useful
* to know when a suggestion has been chosen from the SuggestBox's list of
* suggestions. A SuggestBox fires {@link SuggestionEvent SuggestionEvents}
* whenever a suggestion is chosen, and handlers for these events can be added
* using the {@link #addValueChangeHandler(ValueChangeHandler)} method.
* </p>
*
* <p>
* <img class='gallery' src='doc-files/SuggestBox.png'/>
* </p>
*
* <h3>CSS Style Rules</h3>
* <dl>
* <dt>.gwt-SuggestBox</dt>
* <dd>the suggest box itself</dd>
* </dl>
*
* TODO(pdr): Add SafeHtml support to this and implementing classes.
*
* @see SuggestOracle
* @see MultiWordSuggestOracle
* @see TextBoxBase
*/
@SuppressWarnings("deprecation")
public class SuggestBox extends Composite implements HasText, HasFocus,
HasAnimation, SourcesClickEvents, SourcesFocusEvents, SourcesChangeEvents,
SourcesKeyboardEvents, FiresSuggestionEvents, HasAllKeyHandlers,
HasValue<String>, HasSelectionHandlers<Suggestion>,
IsEditor<LeafValueEditor<String>> {
/**
* The callback used when a user selects a {@link Suggestion}.
*/
public static interface SuggestionCallback {
void onSuggestionSelected(Suggestion suggestion);
}
/**
* Used to display suggestions to the user.
*/
public abstract static class SuggestionDisplay {
/**
* Get the currently selected {@link Suggestion} in the display.
*
* @return the current suggestion, or null if none selected
*/
protected abstract Suggestion getCurrentSelection();
/**
* Hide the list of suggestions from view.
*/
protected abstract void hideSuggestions();
/**
* Highlight the suggestion directly below the current selection in the
* list.
*/
protected abstract void moveSelectionDown();
/**
* Highlight the suggestion directly above the current selection in the
* list.
*/
protected abstract void moveSelectionUp();
/**
* Set the debug id of widgets used in the SuggestionDisplay.
*
* @param suggestBoxBaseID the baseID of the {@link SuggestBox}
* @see UIObject#onEnsureDebugId(String)
*/
protected void onEnsureDebugId(String suggestBoxBaseID) {
}
/**
* Accepts information about whether there were more suggestions matching
* than were provided to {@link #showSuggestions}.
*
* @param hasMoreSuggestions true if more matches were available
* @param numMoreSuggestions number of more matches available. If the
* specific number is unknown, 0 will be passed.
*/
protected void setMoreSuggestions(boolean hasMoreSuggestions,
int numMoreSuggestions) {
// Subclasses may optionally implement.
}
/**
* Update the list of visible suggestions.
*
* Use care when using isDisplayStringHtml; it is an easy way to expose
* script-based security problems.
*
* @param suggestBox the suggest box where the suggestions originated
* @param suggestions the suggestions to show
* @param isDisplayStringHTML should the suggestions be displayed as HTML
* @param isAutoSelectEnabled if true, the first item should be selected
* automatically
* @param callback the callback used when the user makes a suggestion
*/
protected abstract void showSuggestions(SuggestBox suggestBox,
Collection<? extends Suggestion> suggestions,
boolean isDisplayStringHTML, boolean isAutoSelectEnabled,
SuggestionCallback callback);
/**
* This is here for legacy reasons. It is intentionally not visible.
*
* @deprecated implemented in DefaultSuggestionDisplay
*/
@Deprecated
boolean isAnimationEnabledImpl() {
// Implemented in DefaultSuggestionDisplay.
return false;
}
/**
* This is here for legacy reasons. It is intentionally not visible.
*
* @deprecated implemented in DefaultSuggestionDisplay
*/
@Deprecated
boolean isSuggestionListShowingImpl() {
// Implemented in DefaultSuggestionDisplay.
return false;
}
/**
* This is here for legacy reasons. It is intentionally not visible.
*
* @param enable true to enable animation
*
* @deprecated implemented in DefaultSuggestionDisplay
*/
@Deprecated
void setAnimationEnabledImpl(boolean enable) {
// Implemented in DefaultSuggestionDisplay.
}
/**
* This is here for legacy reasons. It is intentionally not visible.
*
* @param style the style name
*
* @deprecated implemented in DefaultSuggestionDisplay
*/
@Deprecated
void setPopupStyleNameImpl(String style) {
// Implemented in DefaultSuggestionDisplay.
}
}
/**
* <p>
* The default implementation of {@link SuggestionDisplay} displays
* suggestions in a {@link PopupPanel} beneath the {@link SuggestBox}.
* </p>
*
* <h3>CSS Style Rules</h3>
* <dl>
* <dt>.gwt-SuggestBoxPopup</dt>
* <dd>the suggestion popup</dd>
* <dt>.gwt-SuggestBoxPopup .item</dt>
* <dd>an unselected suggestion</dd>
* <dt>.gwt-SuggestBoxPopup .item-selected</dt>
* <dd>a selected suggestion</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupTopLeft</dt>
* <dd>the top left cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupTopLeftInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupTopCenter</dt>
* <dd>the top center cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupTopCenterInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupTopRight</dt>
* <dd>the top right cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupTopRightInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupMiddleLeft</dt>
* <dd>the middle left cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupMiddleLeftInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupMiddleCenter</dt>
* <dd>the middle center cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupMiddleCenterInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupMiddleRight</dt>
* <dd>the middle right cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupMiddleRightInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupBottomLeft</dt>
* <dd>the bottom left cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupBottomLeftInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupBottomCenter</dt>
* <dd>the bottom center cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupBottomCenterInner</dt>
* <dd>the inner element of the cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupBottomRight</dt>
* <dd>the bottom right cell</dd>
* <dt>.gwt-SuggestBoxPopup .suggestPopupBottomRightInner</dt>
* <dd>the inner element of the cell</dd>
* </dl>
*/
public static class DefaultSuggestionDisplay extends SuggestionDisplay
implements HasAnimation {
private final SuggestionMenu suggestionMenu;
private final PopupPanel suggestionPopup;
/**
* We need to keep track of the last {@link SuggestBox} because it acts as
* an autoHide partner for the {@link PopupPanel}. If we use the same
* display for multiple {@link SuggestBox}, we need to switch the autoHide
* partner.
*/
private SuggestBox lastSuggestBox = null;
/**
* Sub-classes making use of {@link decorateSuggestionList} to add
* elements to the suggestion popup _may_ want those elements to show even
* when there are 0 suggestions. An example would be showing a "No
* matches" message.
*/
private boolean hideWhenEmpty = true;
/**
* Construct a new {@link DefaultSuggestionDisplay}.
*/
public DefaultSuggestionDisplay() {
suggestionMenu = new SuggestionMenu(true);
suggestionPopup = createPopup();
suggestionPopup.setWidget(decorateSuggestionList(suggestionMenu));
}
@Override
public void hideSuggestions() {
suggestionPopup.hide();
}
public boolean isAnimationEnabled() {
return suggestionPopup.isAnimationEnabled();
}
/**
* Check whether or not the suggestion list is hidden when there are no
* suggestions to display.
*
* @return true if hidden when empty, false if not
*/
public boolean isSuggestionListHiddenWhenEmpty() {
return hideWhenEmpty;
}
/**
* Check whether or not the list of suggestions is being shown.
*
* @return true if the suggestions are visible, false if not
*/
public boolean isSuggestionListShowing() {
return suggestionPopup.isShowing();
}
public void setAnimationEnabled(boolean enable) {
suggestionPopup.setAnimationEnabled(enable);
}
/**
* Sets the style name of the suggestion popup.
*
* @param style the new primary style name
* @see UIObject#setStyleName(String)
*/
public void setPopupStyleName(String style) {
suggestionPopup.setStyleName(style);
}
/**
* Set whether or not the suggestion list should be hidden when there are
* no suggestions to display. Defaults to true.
*
* @param hideWhenEmpty true to hide when empty, false not to
*/
public void setSuggestionListHiddenWhenEmpty(boolean hideWhenEmpty) {
this.hideWhenEmpty = hideWhenEmpty;
}
/**
* Create the PopupPanel that will hold the list of suggestions.
*
* @return the popup panel
*/
protected PopupPanel createPopup() {
PopupPanel p = new DecoratedPopupPanel(true, false, "suggestPopup");
p.setStyleName("gwt-SuggestBoxPopup");
p.setPreviewingAllNativeEvents(true);
p.setAnimationType(AnimationType.ROLL_DOWN);
return p;
}
/**
* Wrap the list of suggestions before adding it to the popup. You can
* override this method if you want to wrap the suggestion list in a
* decorator.
*
* @param suggestionList the widget that contains the list of suggestions
* @return the suggestList, optionally inside of a wrapper
*/
protected Widget decorateSuggestionList(Widget suggestionList) {
return suggestionList;
}
@Override
protected Suggestion getCurrentSelection() {
if (!isSuggestionListShowing()) {
return null;
}
MenuItem item = suggestionMenu.getSelectedItem();
return item == null ? null : ((SuggestionMenuItem) item).getSuggestion();
}
/**
* Get the {@link PopupPanel} used to display suggestions.
*
* @return the popup panel
*/
protected PopupPanel getPopupPanel() {
return suggestionPopup;
}
@Override
protected void moveSelectionDown() {
// Make sure that the menu is actually showing. These keystrokes
// are only relevant when choosing a suggestion.
if (isSuggestionListShowing()) {
// If nothing is selected, getSelectedItemIndex will return -1 and we
// will select index 0 (the first item) by default.
suggestionMenu.selectItem(suggestionMenu.getSelectedItemIndex() + 1);
}
}
@Override
protected void moveSelectionUp() {
// Make sure that the menu is actually showing. These keystrokes
// are only relevant when choosing a suggestion.
if (isSuggestionListShowing()) {
// if nothing is selected, then we should select the last suggestion by
// default. This is because, in some cases, the suggestions menu will
// appear above the text box rather than below it (for example, if the
// text box is at the bottom of the window and the suggestions will not
// fit below the text box). In this case, users would expect to be able
// to use the up arrow to navigate to the suggestions.
if (suggestionMenu.getSelectedItemIndex() == -1) {
suggestionMenu.selectItem(suggestionMenu.getNumItems() - 1);
} else {
suggestionMenu.selectItem(suggestionMenu.getSelectedItemIndex() - 1);
}
}
}
/**
* <b>Affected Elements:</b>
* <ul>
* <li>-popup = The popup that appears with suggestions.</li>
* <li>-item# = The suggested item at the specified index.</li>
* </ul>
*
* @see UIObject#onEnsureDebugId(String)
*/
@Override
protected void onEnsureDebugId(String baseID) {
suggestionPopup.ensureDebugId(baseID + "-popup");
suggestionMenu.setMenuItemDebugIds(baseID);
}
@Override
protected void showSuggestions(final SuggestBox suggestBox,
Collection<? extends Suggestion> suggestions,
boolean isDisplayStringHTML, boolean isAutoSelectEnabled,
final SuggestionCallback callback) {
// Hide the popup if there are no suggestions to display.
boolean anySuggestions = (suggestions != null && suggestions.size() > 0);
if (!anySuggestions && hideWhenEmpty) {
hideSuggestions();
return;
}
// Hide the popup before we manipulate the menu within it. If we do not
// do this, some browsers will redraw the popup as items are removed
// and added to the menu.
if (suggestionPopup.isAttached()) {
suggestionPopup.hide();
}
suggestionMenu.clearItems();
for (final Suggestion curSuggestion : suggestions) {
final SuggestionMenuItem menuItem = new SuggestionMenuItem(
curSuggestion, isDisplayStringHTML);
menuItem.setCommand(new Command() {
public void execute() {
callback.onSuggestionSelected(curSuggestion);
}
});
suggestionMenu.addItem(menuItem);
}
if (isAutoSelectEnabled && anySuggestions) {
// Select the first item in the suggestion menu.
suggestionMenu.selectItem(0);
}
// Link the popup autoHide to the TextBox.
if (lastSuggestBox != suggestBox) {
// If the suggest box has changed, free the old one first.
if (lastSuggestBox != null) {
suggestionPopup.removeAutoHidePartner(lastSuggestBox.getElement());
}
lastSuggestBox = suggestBox;
suggestionPopup.addAutoHidePartner(suggestBox.getElement());
}
// Show the popup under the TextBox.
suggestionPopup.showRelativeTo(suggestBox);
}
@Override
boolean isAnimationEnabledImpl() {
return isAnimationEnabled();
}
@Override
boolean isSuggestionListShowingImpl() {
return isSuggestionListShowing();
}
@Override
void setAnimationEnabledImpl(boolean enable) {
setAnimationEnabled(enable);
}
@Override
void setPopupStyleNameImpl(String style) {
setPopupStyleName(style);
}
}
/**
* The SuggestionMenu class is used for the display and selection of
* suggestions in the SuggestBox widget. SuggestionMenu differs from MenuBar
* in that it always has a vertical orientation, and it has no submenus. It
* also allows for programmatic selection of items in the menu, and
* programmatically performing the action associated with the selected item.
* In the MenuBar class, items cannot be selected programatically - they can
* only be selected when the user places the mouse over a particlar item.
* Additional methods in SuggestionMenu provide information about the number
* of items in the menu, and the index of the currently selected item.
*/
private static class SuggestionMenu extends MenuBar {
public SuggestionMenu(boolean vertical) {
super(vertical);
// Make sure that CSS styles specified for the default Menu classes
// do not affect this menu
setStyleName("");
setFocusOnHoverEnabled(false);
}
public void doSelectedItemAction() {
// In order to perform the action of the item that is currently
// selected, the menu must be showing.
MenuItem selectedItem = getSelectedItem();
if (selectedItem != null) {
doItemAction(selectedItem, true, false);
}
}
public int getNumItems() {
return getItems().size();
}
/**
* Returns the index of the menu item that is currently selected.
*
* @return returns the selected item
*/
public int getSelectedItemIndex() {
// The index of the currently selected item can only be
// obtained if the menu is showing.
MenuItem selectedItem = getSelectedItem();
if (selectedItem != null) {
return getItems().indexOf(selectedItem);
}
return -1;
}
/**
* Selects the item at the specified index in the menu. Selecting the item
* does not perform the item's associated action; it only changes the style
* of the item and updates the value of SuggestionMenu.selectedItem.
*
* @param index index
*/
public void selectItem(int index) {
List<MenuItem> items = getItems();
if (index > -1 && index < items.size()) {
itemOver(items.get(index), false);
}
}
}
/**
* Class for menu items in a SuggestionMenu. A SuggestionMenuItem differs from
* a MenuItem in that each item is backed by a Suggestion object. The text of
* each menu item is derived from the display string of a Suggestion object,
* and each item stores a reference to its Suggestion object.
*/
private static class SuggestionMenuItem extends MenuItem {
@SuppressWarnings("hiding")
private static final String STYLENAME_DEFAULT = "item";
private Suggestion suggestion;
public SuggestionMenuItem(Suggestion suggestion, boolean asHTML) {
super(suggestion.getDisplayString(), asHTML);
// Each suggestion should be placed in a single row in the suggestion
// menu. If the window is resized and the suggestion cannot fit on a
// single row, it should be clipped (instead of wrapping around and
// taking up a second row).
DOM.setStyleAttribute(getElement(), "whiteSpace", "nowrap");
setStyleName(STYLENAME_DEFAULT);
setSuggestion(suggestion);
}
public Suggestion getSuggestion() {
return suggestion;
}
public void setSuggestion(Suggestion suggestion) {
this.suggestion = suggestion;
}
}
private static final String STYLENAME_DEFAULT = "gwt-SuggestBox";
/**
* Creates a {@link SuggestBox} widget that wraps an existing &lt;input
* type='text'&gt; element.
*
* This element must already be attached to the document. If the element is
* removed from the document, you must call
* {@link RootPanel#detachNow(Widget)}.
*
* @param oracle the suggest box oracle to use
* @param element the element to be wrapped
*/
public static SuggestBox wrap(SuggestOracle oracle, Element element) {
// Assert that the element is attached.
assert Document.get().getBody().isOrHasChild(element);
TextBox textBox = new TextBox(element);
SuggestBox suggestBox = new SuggestBox(oracle, textBox);
// Mark it attached and remember it for cleanup.
suggestBox.onAttach();
RootPanel.detachOnWindowClose(suggestBox);
return suggestBox;
}
private int limit = 20;
private boolean selectsFirstItem = true;
private SuggestOracle oracle;
private String currentText;
private final SuggestionDisplay display;
private final TextBoxBase box;
private final Callback callback = new Callback() {
public void onSuggestionsReady(Request request, Response response) {
display.showSuggestions(SuggestBox.this, response.getSuggestions(),
oracle.isDisplayStringHTML(), isAutoSelectEnabled(),
suggestionCallback);
display.setMoreSuggestions(response.hasMoreSuggestions(),
response.getMoreSuggestionsCount());
}
};
private final SuggestionCallback suggestionCallback = new SuggestionCallback() {
public void onSuggestionSelected(Suggestion suggestion) {
setNewSelection(suggestion);
}
};
/**
* Constructor for {@link SuggestBox}. Creates a
* {@link MultiWordSuggestOracle} and {@link TextBox} to use with this
* {@link SuggestBox}.
*/
public SuggestBox() {
this(new MultiWordSuggestOracle());
}
/**
* Constructor for {@link SuggestBox}. Creates a {@link TextBox} to use with
* this {@link SuggestBox}.
*
* @param oracle the oracle for this <code>SuggestBox</code>
*/
public SuggestBox(SuggestOracle oracle) {
this(oracle, new TextBox());
}
/**
* Constructor for {@link SuggestBox}. The text box will be removed from it's
* current location and wrapped by the {@link SuggestBox}.
*
* @param oracle supplies suggestions based upon the current contents of the
* text widget
* @param box the text widget
*/
public SuggestBox(SuggestOracle oracle, TextBoxBase box) {
this(oracle, box, new DefaultSuggestionDisplay());
}
/**
* Constructor for {@link SuggestBox}. The text box will be removed from it's
* current location and wrapped by the {@link SuggestBox}.
*
* @param oracle supplies suggestions based upon the current contents of the
* text widget
* @param box the text widget
* @param suggestDisplay the class used to display suggestions
*/
public SuggestBox(SuggestOracle oracle, TextBoxBase box,
SuggestionDisplay suggestDisplay) {
this.box = box;
this.display = suggestDisplay;
initWidget(box);
addEventsToTextBox();
setOracle(oracle);
setStyleName(STYLENAME_DEFAULT);
}
/**
*
* Adds a listener to receive change events on the SuggestBox's text box. The
* source Widget for these events will be the SuggestBox.
*
* @param listener the listener interface to add
* @deprecated use {@link #getTextBox}().addChangeHandler instead
*/
@Deprecated
public void addChangeListener(final ChangeListener listener) {
ListenerWrapper.WrappedLogicalChangeListener.add(box, listener).setSource(
this);
}
/**
* Adds a listener to receive click events on the SuggestBox's text box. The
* source Widget for these events will be the SuggestBox.
*
* @param listener the listener interface to add
* @deprecated use {@link #getTextBox}().addClickHandler instead
*/
@Deprecated
public void addClickListener(final ClickListener listener) {
ListenerWrapper.WrappedClickListener legacy = ListenerWrapper.WrappedClickListener.add(
box, listener);
legacy.setSource(this);
}
/**
* Adds an event to this handler.
*
* @deprecated use {@link #addSelectionHandler} instead.
*/
@Deprecated
public void addEventHandler(final SuggestionHandler handler) {
ListenerWrapper.WrappedOldSuggestionHandler.add(this, handler);
}
/**
* Adds a listener to receive focus events on the SuggestBox's text box. The
* source Widget for these events will be the SuggestBox.
*
* @param listener the listener interface to add
* @deprecated use {@link #getTextBox}().addFocusHandler/addBlurHandler()
* instead
*/
@Deprecated
public void addFocusListener(final FocusListener listener) {
ListenerWrapper.WrappedFocusListener focus = ListenerWrapper.WrappedFocusListener.add(
box, listener);
focus.setSource(this);
}
/**
* @deprecated Use {@link #addKeyDownHandler}, {@link #addKeyUpHandler} and
* {@link #addKeyPressHandler} instead
*/
@Deprecated
public void addKeyboardListener(KeyboardListener listener) {
ListenerWrapper.WrappedKeyboardListener.add(this, listener);
}
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
return addDomHandler(handler, KeyDownEvent.getType());
}
public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
return addDomHandler(handler, KeyPressEvent.getType());
}
public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
return addDomHandler(handler, KeyUpEvent.getType());
}
public HandlerRegistration addSelectionHandler(
SelectionHandler<Suggestion> handler) {
return addHandler(handler, SelectionEvent.getType());
}
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<String> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
/**
* Returns a {@link TakesValueEditor} backed by the DateBox.
*/
public LeafValueEditor<String> asEditor() {
return TakesValueEditor.of(this);
}
/**
* Gets the limit for the number of suggestions that should be displayed for
* this box. It is up to the current {@link SuggestOracle} to enforce this
* limit.
*
* @return the limit for the number of suggestions
*/
public int getLimit() {
return limit;
}
/**
* Get the {@link SuggestionDisplay} used to display suggestions.
*
* @return the {@link SuggestionDisplay}
*/
public SuggestionDisplay getSuggestionDisplay() {
return display;
}
/**
* Gets the suggest box's {@link com.google.gwt.user.client.ui.SuggestOracle}.
*
* @return the {@link SuggestOracle}
*/
public SuggestOracle getSuggestOracle() {
return oracle;
}
public int getTabIndex() {
return box.getTabIndex();
}
public String getText() {
return box.getText();
}
/**
* Get the text box associated with this suggest box.
*
* @return this suggest box's text box
*/
public TextBoxBase getTextBox() {
return box;
}
public String getValue() {
return box.getValue();
}
/**
* Hide current suggestions in the {@link DefaultSuggestionDisplay}. Note that
* this method is a no-op unless the {@link DefaultSuggestionDisplay} is used.
*
* @deprecated use {@link DefaultSuggestionDisplay#hideSuggestions()} instead
*/
@Deprecated
public void hideSuggestionList() {
display.hideSuggestions();
}
/**
* Check whether or not the {@link DefaultSuggestionDisplay} has animations
* enabled. Note that this method only has a meaningful return value when the
* {@link DefaultSuggestionDisplay} is used.
*
* @deprecated use {@link DefaultSuggestionDisplay#isAnimationEnabled()}
* instead
*/
@Deprecated
public boolean isAnimationEnabled() {
return display.isAnimationEnabledImpl();
}
/**
* Returns whether or not the first suggestion will be automatically selected.
* This behavior is on by default.
*
* @return true if the first suggestion will be automatically selected
*/
public boolean isAutoSelectEnabled() {
return selectsFirstItem;
}
/**
* Check if the {@link DefaultSuggestionDisplay} is showing. Note that this
* method only has a meaningful return value when the
* {@link DefaultSuggestionDisplay} is used.
*
* @return true if the list of suggestions is currently showing, false if not
* @deprecated use {@link DefaultSuggestionDisplay#isSuggestionListShowing()}
*/
@Deprecated
public boolean isSuggestionListShowing() {
return display.isSuggestionListShowingImpl();
}
/**
* @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
* object returned by {@link #getTextBox}().addChangeHandler
* instead
*/
@Deprecated
public void removeChangeListener(ChangeListener listener) {
ListenerWrapper.WrappedChangeListener.remove(box, listener);
}
/**
* @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
* object returned by {@link #getTextBox}().addClickHandler
* instead
*/
@Deprecated
public void removeClickListener(ClickListener listener) {
ListenerWrapper.WrappedClickListener.remove(box, listener);
}
/**
* @deprecated Use the {@link HandlerRegistration#removeHandler} method no the
* object returned by {@link #addSelectionHandler} instead
*/
@Deprecated
public void removeEventHandler(SuggestionHandler handler) {
ListenerWrapper.WrappedOldSuggestionHandler.remove(this, handler);
}
/**
* @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
* object returned by {@link #getTextBox}().addFocusListener
* instead
*/
@Deprecated
public void removeFocusListener(FocusListener listener) {
ListenerWrapper.WrappedFocusListener.remove(this, listener);
}
/**
* @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
* object returned by {@link #getTextBox}().add*Handler instead
*/
@Deprecated
public void removeKeyboardListener(KeyboardListener listener) {
ListenerWrapper.WrappedKeyboardListener.remove(this, listener);
}
public void setAccessKey(char key) {
box.setAccessKey(key);
}
/**
* Enable or disable animations in the {@link DefaultSuggestionDisplay}. Note
* that this method is a no-op unless the {@link DefaultSuggestionDisplay} is
* used.
*
* @deprecated use
* {@link DefaultSuggestionDisplay#setAnimationEnabled(boolean)}
* instead
*/
@Deprecated
public void setAnimationEnabled(boolean enable) {
display.setAnimationEnabledImpl(enable);
}
/**
* Turns on or off the behavior that automatically selects the first suggested
* item. This behavior is on by default.
*
* @param selectsFirstItem Whether or not to automatically select the first
* suggestion
*/
public void setAutoSelectEnabled(boolean selectsFirstItem) {
this.selectsFirstItem = selectsFirstItem;
}
public void setFocus(boolean focused) {
box.setFocus(focused);
}
/**
* Sets the limit to the number of suggestions the oracle should provide. It
* is up to the oracle to enforce this limit.
*
* @param limit the limit to the number of suggestions provided
*/
public void setLimit(int limit) {
this.limit = limit;
}
/**
* Sets the style name of the suggestion popup in the
* {@link DefaultSuggestionDisplay}. Note that this method is a no-op unless
* the {@link DefaultSuggestionDisplay} is used.
*
* @param style the new primary style name
* @see UIObject#setStyleName(String)
* @deprecated use {@link DefaultSuggestionDisplay#setPopupStyleName(String)}
* instead
*/
@Deprecated
public void setPopupStyleName(String style) {
getSuggestionDisplay().setPopupStyleNameImpl(style);
}
public void setTabIndex(int index) {
box.setTabIndex(index);
}
public void setText(String text) {
box.setText(text);
}
public void setValue(String newValue) {
box.setValue(newValue);
}
public void setValue(String value, boolean fireEvents) {
box.setValue(value, fireEvents);
}
/**
* Show the current list of suggestions.
*/
public void showSuggestionList() {
if (isAttached()) {
currentText = null;
refreshSuggestions();
}
}
@Override
protected void onEnsureDebugId(String baseID) {
super.onEnsureDebugId(baseID);
display.onEnsureDebugId(baseID);
}
void showSuggestions(String query) {
if (query.length() == 0) {
oracle.requestDefaultSuggestions(new Request(null, limit), callback);
} else {
oracle.requestSuggestions(new Request(query, limit), callback);
}
}
private void addEventsToTextBox() {
class TextBoxEvents extends HandlesAllKeyEvents implements
ValueChangeHandler<String> {
public void onKeyDown(KeyDownEvent event) {
switch (event.getNativeKeyCode()) {
case KeyCodes.KEY_DOWN:
display.moveSelectionDown();
break;
case KeyCodes.KEY_UP:
display.moveSelectionUp();
break;
case KeyCodes.KEY_ENTER:
case KeyCodes.KEY_TAB:
Suggestion suggestion = display.getCurrentSelection();
if (suggestion == null) {
display.hideSuggestions();
} else {
setNewSelection(suggestion);
}
break;
}
delegateEvent(SuggestBox.this, event);
}
public void onKeyPress(KeyPressEvent event) {
delegateEvent(SuggestBox.this, event);
}
public void onKeyUp(KeyUpEvent event) {
// After every user key input, refresh the popup's suggestions.
refreshSuggestions();
delegateEvent(SuggestBox.this, event);
}
public void onValueChange(ValueChangeEvent<String> event) {
delegateEvent(SuggestBox.this, event);
}
}
TextBoxEvents events = new TextBoxEvents();
events.addKeyHandlersTo(box);
box.addValueChangeHandler(events);
}
private void fireSuggestionEvent(Suggestion selectedSuggestion) {
SelectionEvent.fire(this, selectedSuggestion);
}
private void refreshSuggestions() {
// Get the raw text.
String text = getText();
if (text.equals(currentText)) {
return;
} else {
currentText = text;
}
showSuggestions(text);
}
/**
* Set the new suggestion in the text box.
*
* @param curSuggestion the new suggestion
*/
private void setNewSelection(Suggestion curSuggestion) {
assert curSuggestion != null : "suggestion cannot be null";
currentText = curSuggestion.getReplacementString();
setText(currentText);
display.hideSuggestions();
fireSuggestionEvent(curSuggestion);
}
/**
* Sets the suggestion oracle used to create suggestions.
*
* @param oracle the oracle
*/
private void setOracle(SuggestOracle oracle) {
this.oracle = oracle;
}
}