Adding SuggestionDisplay interface to SuggestBox to allow custom display implementations.
Patch by: jlabanca
Review by: jgw
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7400 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestBox.java b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
index b18ba72..060cf8e 100644
--- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java
+++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -92,48 +92,6 @@
* <dl>
* <dt>.gwt-SuggestBox</dt>
* <dd>the suggest box itself</dd>
- * <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>
*
* @see SuggestOracle
@@ -147,6 +105,364 @@
HasValue<String>, HasSelectionHandlers<Suggestion> {
/**
+ * 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) {
+ }
+
+ /**
+ * Update the list of visible suggestions.
+ *
+ * @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.
+ *
+ * @deprecated implemented in DefaultSuggestionDisplay
+ */
+ @Deprecated
+ void setAnimationEnabledImpl(boolean enable) {
+ // Implemented in DefaultSuggestionDisplay.
+ }
+
+ /**
+ * This is here for legacy reasons. It is intentionally not visible.
+ *
+ * @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;
+
+ /**
+ * 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 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);
+ }
+
+ /**
+ * 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()) {
+ 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()) {
+ 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.
+ if (suggestions == null || suggestions.size() == 0) {
+ 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) {
+ // 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
@@ -273,12 +589,18 @@
private boolean selectsFirstItem = true;
private SuggestOracle oracle;
private String currentText;
- private final SuggestionMenu suggestionMenu;
- private final PopupPanel suggestionPopup;
+ private final SuggestionDisplay display;
private final TextBoxBase box;
private final Callback callback = new Callback() {
public void onSuggestionsReady(Request request, Response response) {
- showSuggestions(response.getSuggestions());
+ display.showSuggestions(SuggestBox.this, response.getSuggestions(),
+ oracle.isDisplayStringHTML(), isAutoSelectEnabled(),
+ suggestionCallback);
+ }
+ };
+ private final SuggestionCallback suggestionCallback = new SuggestionCallback() {
+ public void onSuggestionSelected(Suggestion suggestion) {
+ setNewSelection(suggestion);
}
};
@@ -310,14 +632,23 @@
* @param box the text widget
*/
public SuggestBox(SuggestOracle oracle, TextBoxBase box) {
- this.box = box;
- initWidget(box);
+ this(oracle, box, new DefaultSuggestionDisplay());
+ }
- // suggestionMenu must be created before suggestionPopup, because
- // suggestionMenu is suggestionPopup's widget
- suggestionMenu = new SuggestionMenu(true);
- suggestionPopup = createPopup();
- suggestionPopup.setAnimationType(AnimationType.ROLL_DOWN);
+ /**
+ * 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();
@@ -335,7 +666,8 @@
*/
@Deprecated
public void addChangeListener(final ChangeListener listener) {
- ListenerWrapper.WrappedLogicalChangeListener.add(box, listener).setSource(this);
+ ListenerWrapper.WrappedLogicalChangeListener.add(box, listener).setSource(
+ this);
}
/**
@@ -347,8 +679,8 @@
*/
@Deprecated
public void addClickListener(final ClickListener listener) {
- ListenerWrapper.WrappedClickListener legacy = ListenerWrapper.WrappedClickListener.add(box,
- listener);
+ ListenerWrapper.WrappedClickListener legacy = ListenerWrapper.WrappedClickListener.add(
+ box, listener);
legacy.setSource(this);
}
@@ -367,18 +699,19 @@
* source Widget for these events will be the SuggestBox.
*
* @param listener the listener interface to add
- * @deprecated use {@link #getTextBox}().addFocusHandler/addBlurHandler() instead
+ * @deprecated use {@link #getTextBox}().addFocusHandler/addBlurHandler()
+ * instead
*/
@Deprecated
public void addFocusListener(final FocusListener listener) {
- ListenerWrapper.WrappedFocusListener focus = ListenerWrapper.WrappedFocusListener.add(box,
- listener);
+ ListenerWrapper.WrappedFocusListener focus = ListenerWrapper.WrappedFocusListener.add(
+ box, listener);
focus.setSource(this);
}
/**
- * @deprecated Use {@link #addKeyDownHandler}, {@link
- * #addKeyUpHandler} and {@link #addKeyPressHandler} instead
+ * @deprecated Use {@link #addKeyDownHandler}, {@link #addKeyUpHandler} and
+ * {@link #addKeyPressHandler} instead
*/
@Deprecated
public void addKeyboardListener(KeyboardListener listener) {
@@ -419,6 +752,15 @@
}
/**
+ * 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}
@@ -449,14 +791,27 @@
}
/**
- * Hide current suggestions.
+ * 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() {
- this.suggestionPopup.hide();
+ 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 suggestionPopup.isAnimationEnabled();
+ return display.isAnimationEnabledImpl();
}
/**
@@ -470,15 +825,22 @@
}
/**
+ * 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 suggestionPopup.isShowing();
+ return display.isSuggestionListShowingImpl();
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by {@link #getTextBox}().addChangeHandler instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #getTextBox}().addChangeHandler
+ * instead
*/
@Deprecated
public void removeChangeListener(ChangeListener listener) {
@@ -486,8 +848,9 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by {@link #getTextBox}().addClickHandler instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #getTextBox}().addClickHandler
+ * instead
*/
@Deprecated
public void removeClickListener(ClickListener listener) {
@@ -495,8 +858,8 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method no the object returned by {@link #addSelectionHandler} instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method no the
+ * object returned by {@link #addSelectionHandler} instead
*/
@Deprecated
public void removeEventHandler(SuggestionHandler handler) {
@@ -504,8 +867,9 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by {@link #getTextBox}().addFocusListener instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #getTextBox}().addFocusListener
+ * instead
*/
@Deprecated
public void removeFocusListener(FocusListener listener) {
@@ -513,8 +877,8 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by {@link #getTextBox}().add*Handler instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #getTextBox}().add*Handler instead
*/
@Deprecated
public void removeKeyboardListener(KeyboardListener listener) {
@@ -525,8 +889,18 @@
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) {
- suggestionPopup.setAnimationEnabled(enable);
+ display.setAnimationEnabledImpl(enable);
}
/**
@@ -555,13 +929,18 @@
}
/**
- * Sets the style name of the suggestion popup.
+ * 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) {
- suggestionPopup.setStyleName(style);
+ getSuggestionDisplay().setPopupStyleNameImpl(style);
}
public void setTabIndex(int index) {
@@ -590,47 +969,10 @@
}
}
- /**
- * <b>Affected Elements:</b>
- * <ul>
- * <li>-popup = The popup that appears with suggestions.</li>
- * <li>-items-item# = The suggested item at the specified index.</li>
- * </ul>
- *
- * @see UIObject#onEnsureDebugId(String)
- */
@Override
protected void onEnsureDebugId(String baseID) {
super.onEnsureDebugId(baseID);
- suggestionPopup.ensureDebugId(baseID + "-popup");
- suggestionMenu.setMenuItemDebugIds(baseID);
- }
-
- /**
- * Gets the specified suggestion from the suggestions currently showing.
- *
- * @param index the index at which the suggestion lives
- *
- * @throws IndexOutOfBoundsException if the index is greater then the number
- * of suggestions currently showing
- *
- * @return the given suggestion
- */
- Suggestion getSuggestion(int index) {
- if (!isSuggestionListShowing()) {
- throw new IndexOutOfBoundsException(
- "No suggestions showing, so cannot show " + index);
- }
- return ((SuggestionMenuItem) suggestionMenu.getItems().get(index)).suggestion;
- }
-
- /**
- * Get the number of suggestions that are currently showing.
- *
- * @return the number of suggestions currently showing, 0 if there are none
- */
- int getSuggestionCount() {
- return isSuggestionListShowing() ? suggestionMenu.getNumItems() : 0;
+ display.onEnsureDebugId(baseID);
}
void showSuggestions(String query) {
@@ -646,25 +988,22 @@
ValueChangeHandler<String> {
public void onKeyDown(KeyDownEvent event) {
- // Make sure that the menu is actually showing. These keystrokes
- // are only relevant when choosing a suggestion.
- if (suggestionPopup.isAttached()) {
- switch (event.getNativeKeyCode()) {
- case KeyCodes.KEY_DOWN:
- suggestionMenu.selectItem(suggestionMenu.getSelectedItemIndex() + 1);
- break;
- case KeyCodes.KEY_UP:
- suggestionMenu.selectItem(suggestionMenu.getSelectedItemIndex() - 1);
- break;
- case KeyCodes.KEY_ENTER:
- case KeyCodes.KEY_TAB:
- if (suggestionMenu.getSelectedItemIndex() < 0) {
- suggestionPopup.hide();
- } else {
- suggestionMenu.doSelectedItemAction();
- }
- break;
- }
+ 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);
}
@@ -689,15 +1028,6 @@
box.addValueChangeHandler(events);
}
- private PopupPanel createPopup() {
- PopupPanel p = new DecoratedPopupPanel(true, false, "suggestPopup");
- p.setWidget(suggestionMenu);
- p.setStyleName("gwt-SuggestBoxPopup");
- p.setPreviewingAllNativeEvents(true);
- p.addAutoHidePartner(getTextBox().getElement());
- return p;
- }
-
private void fireSuggestionEvent(Suggestion selectedSuggestion) {
SelectionEvent.fire(this, selectedSuggestion);
}
@@ -713,11 +1043,16 @@
showSuggestions(text);
}
- private void setNewSelection(SuggestionMenuItem menuItem) {
- Suggestion curSuggestion = menuItem.getSuggestion();
+ /**
+ * 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);
- suggestionPopup.hide();
+ display.hideSuggestions();
fireSuggestionEvent(curSuggestion);
}
@@ -729,46 +1064,4 @@
private void setOracle(SuggestOracle oracle) {
this.oracle = oracle;
}
-
- /**
- * Show the given collection of suggestions.
- *
- * @param suggestions suggestions to show
- */
- private void showSuggestions(Collection<? extends Suggestion> suggestions) {
- if (suggestions.size() > 0) {
-
- // 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.
- boolean isAnimationEnabled = suggestionPopup.isAnimationEnabled();
- if (suggestionPopup.isAttached()) {
- suggestionPopup.hide();
- }
-
- suggestionMenu.clearItems();
-
- for (Suggestion curSuggestion : suggestions) {
- final SuggestionMenuItem menuItem = new SuggestionMenuItem(
- curSuggestion, oracle.isDisplayStringHTML());
- menuItem.setCommand(new Command() {
- public void execute() {
- SuggestBox.this.setNewSelection(menuItem);
- }
- });
-
- suggestionMenu.addItem(menuItem);
- }
-
- if (selectsFirstItem) {
- // Select the first item in the suggestion menu.
- suggestionMenu.selectItem(0);
- }
-
- suggestionPopup.showRelativeTo(getTextBox());
- suggestionPopup.setAnimationEnabled(isAnimationEnabled);
- } else {
- suggestionPopup.hide();
- }
- }
}
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index 079dab7..507a5a4 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -39,6 +39,7 @@
import com.google.gwt.user.client.ui.DecoratedTabBarTest;
import com.google.gwt.user.client.ui.DecoratedTabPanelTest;
import com.google.gwt.user.client.ui.DecoratorPanelTest;
+import com.google.gwt.user.client.ui.DefaultSuggestionDisplayTest;
import com.google.gwt.user.client.ui.DelegatingKeyboardListenerCollectionTest;
import com.google.gwt.user.client.ui.DialogBoxTest;
import com.google.gwt.user.client.ui.DisclosurePanelTest;
@@ -122,6 +123,7 @@
suite.addTestSuite(DecoratedTabBarTest.class);
suite.addTestSuite(DecoratedTabPanelTest.class);
suite.addTestSuite(DecoratorPanelTest.class);
+ suite.addTestSuite(DefaultSuggestionDisplayTest.class);
suite.addTestSuite(DelegatingKeyboardListenerCollectionTest.class);
suite.addTestSuite(DialogBoxTest.class);
suite.addTestSuite(DisclosurePanelTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/DefaultSuggestionDisplayTest.java b/user/test/com/google/gwt/user/client/ui/DefaultSuggestionDisplayTest.java
new file mode 100644
index 0000000..44b8c7d
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DefaultSuggestionDisplayTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2010 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.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
+import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
+
+import java.util.List;
+
+/**
+ * Tests for {@link DefaultSuggestionDisplay}.
+ */
+public class DefaultSuggestionDisplayTest extends SuggestionDisplayTestBase {
+
+ public void testAccessors() {
+ SuggestBox box = createSuggestBox();
+ DefaultSuggestionDisplay display = (DefaultSuggestionDisplay) box.getSuggestionDisplay();
+ PopupPanel popup = display.getPopupPanel();
+
+ // isAnimationEnabled.
+ assertFalse(display.isAnimationEnabled());
+ assertFalse(popup.isAnimationEnabled());
+ display.setAnimationEnabled(true);
+ assertTrue(display.isAnimationEnabled());
+ assertTrue(popup.isAnimationEnabled());
+
+ // isSuggestListShowing.
+ List<Suggestion> suggestions = createSuggestions("test0", "test1", "test2");
+ assertFalse(display.isSuggestionListShowing());
+ assertFalse(popup.isShowing());
+ display.showSuggestions(box, suggestions, false, false, NULL_CALLBACK);
+ assertTrue(display.isSuggestionListShowing());
+ assertTrue(popup.isShowing());
+ display.hideSuggestions();
+ assertFalse(display.isSuggestionListShowing());
+ assertFalse(popup.isShowing());
+ }
+
+ public void testGetCurrentSelectionWhenHidden() {
+ SuggestBox box = createSuggestBox();
+ DefaultSuggestionDisplay display = (DefaultSuggestionDisplay) box.getSuggestionDisplay();
+
+ // Show the suggestions and select the first item.
+ List<Suggestion> suggestions = createSuggestions("test0", "test1", "test2");
+ display.showSuggestions(box, suggestions, false, true, NULL_CALLBACK);
+ assertTrue(display.isSuggestionListShowing());
+ assertEquals(suggestions.get(0), display.getCurrentSelection());
+
+ // Hide the list and ensure that nothing is selected.
+ display.hideSuggestions();
+ assertNull(display.getCurrentSelection());
+ }
+
+ public void testShowSuggestionsEmpty() {
+ SuggestBox box = createSuggestBox();
+ DefaultSuggestionDisplay display = (DefaultSuggestionDisplay) box.getSuggestionDisplay();
+
+ // Show null suggestions.
+ display.showSuggestions(box, null, false, true, NULL_CALLBACK);
+ assertFalse(display.isSuggestionListShowing());
+
+ // Show empty suggestions.
+ List<Suggestion> suggestions = createSuggestions();
+ display.showSuggestions(box, suggestions, false, true, NULL_CALLBACK);
+ assertFalse(display.isSuggestionListShowing());
+ }
+
+ @Override
+ protected DefaultSuggestionDisplay createSuggestionDisplay() {
+ return new DefaultSuggestionDisplay();
+ }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java b/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
index feb84c1..8bf2db5 100644
--- a/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/SuggestBoxTest.java
@@ -17,14 +17,57 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
+import com.google.gwt.user.client.ui.SuggestBox.SuggestionCallback;
+import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
/**
* Tests for {@link SuggestBoxTest}.
*/
-public class SuggestBoxTest extends GWTTestCase {
+public class SuggestBoxTest extends WidgetTestBase {
+
+ /**
+ * A SuggestionDisplay used for testing.
+ */
+ private static class TestSuggestionDisplay extends DefaultSuggestionDisplay {
+
+ private List<? extends Suggestion> suggestions;
+
+ @Override
+ protected void showSuggestions(SuggestBox suggestBox,
+ Collection<? extends Suggestion> suggestions,
+ boolean isDisplayStringHTML, boolean isAutoSelectEnabled,
+ SuggestionCallback callback) {
+ super.showSuggestions(suggestBox, suggestions, isDisplayStringHTML,
+ isAutoSelectEnabled, callback);
+ this.suggestions = new ArrayList<Suggestion>(suggestions);
+ }
+
+ /**
+ * Get the suggestion at the specified index.
+ *
+ * @param index the index
+ * @return the {@link Suggestion} at the index
+ */
+ public Suggestion getSuggestion(int index) {
+ return suggestions.get(index);
+ }
+
+ /**
+ * Get the number of suggestions that are currently showing. Used for
+ * testing.
+ *
+ * @return the number of suggestions currently showing, 0 if there are none
+ */
+ public int getSuggestionCount() {
+ return suggestions.size();
+ }
+ }
@Override
public String getModuleName() {
@@ -34,6 +77,7 @@
/**
* Test the basic accessors.
*/
+ @SuppressWarnings("deprecation")
public void testAccessors() {
SuggestBox box = createSuggestBox();
@@ -53,44 +97,48 @@
assertTrue(box.isSuggestionListShowing());
}
+ @SuppressWarnings("deprecation")
public void testShowAndHide() {
SuggestBox box = createSuggestBox();
- assertFalse(box.isSuggestionListShowing());
+ TestSuggestionDisplay display = (TestSuggestionDisplay) box.getSuggestionDisplay();
+ assertFalse(display.isSuggestionListShowing());
+
// should do nothing, box is not attached.
box.showSuggestionList();
- assertFalse(box.isSuggestionListShowing());
+ assertFalse(display.isSuggestionListShowing());
// Adds the suggest box to the root panel.
RootPanel.get().add(box);
- assertFalse(box.isSuggestionListShowing());
+ assertFalse(display.isSuggestionListShowing());
// Hides the list of suggestions, should be a no-op.
box.hideSuggestionList();
// Should try to show, but still fail, as there are no default suggestions.
box.showSuggestionList();
- assertFalse(box.isSuggestionListShowing());
+ assertFalse(display.isSuggestionListShowing());
// Now, finally, should be true
box.setText("t");
box.showSuggestionList();
- assertTrue(box.isSuggestionListShowing());
+ assertTrue(display.isSuggestionListShowing());
// Hides it for real this time.
box.hideSuggestionList();
- assertFalse(box.isSuggestionListShowing());
+ assertFalse(display.isSuggestionListShowing());
}
public void testDefaults() {
MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
oracle.setDefaultSuggestionsFromText(Arrays.asList("A", "B"));
- SuggestBox box = new SuggestBox(oracle);
+ TestSuggestionDisplay display = new TestSuggestionDisplay();
+ SuggestBox box = new SuggestBox(oracle, new TextBox(), display);
RootPanel.get().add(box);
box.showSuggestionList();
- assertTrue(box.isSuggestionListShowing());
- assertEquals(2, box.getSuggestionCount());
- assertEquals("A", box.getSuggestion(0).getReplacementString());
- assertEquals("B", box.getSuggestion(1).getReplacementString());
+ assertTrue(display.isSuggestionListShowing());
+ assertEquals(2, display.getSuggestionCount());
+ assertEquals("A", display.getSuggestion(0).getReplacementString());
+ assertEquals("B", display.getSuggestion(1).getReplacementString());
}
public void testShowFirst() {
@@ -106,12 +154,6 @@
// text box and ensure that we see the correct behavior.
}
- @Override
- public void gwtTearDown() throws Exception {
- super.gwtTearDown();
- RootPanel.get().clear();
- }
-
public void testWrapUsingStaticWrapMethod() {
Element wrapper = Document.get().createTextInputElement();
RootPanel.get().getElement().appendChild(wrapper);
@@ -133,7 +175,7 @@
protected SuggestBox createSuggestBox() {
MultiWordSuggestOracle oracle = createOracle();
- return new SuggestBox(oracle);
+ return new SuggestBox(oracle, new TextBox(), new TestSuggestionDisplay());
}
private MultiWordSuggestOracle createOracle() {
diff --git a/user/test/com/google/gwt/user/client/ui/SuggestionDisplayTestBase.java b/user/test/com/google/gwt/user/client/ui/SuggestionDisplayTestBase.java
new file mode 100644
index 0000000..ad9188b
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/SuggestionDisplayTestBase.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010 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.user.client.ui.SuggestBox.SuggestionCallback;
+import com.google.gwt.user.client.ui.SuggestBox.SuggestionDisplay;
+import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base tests for {@link SuggestionDisplay}.
+ */
+public abstract class SuggestionDisplayTestBase extends WidgetTestBase {
+
+ /**
+ * A no-op callback used for testing.
+ */
+ protected static final SuggestionCallback NULL_CALLBACK = new SuggestionCallback() {
+ public void onSuggestionSelected(Suggestion suggestion) {
+ }
+ };
+
+ /**
+ * A simple {@link Suggestion} implementation that uses a single string for
+ * both the display and replacement string.
+ */
+ private static class SimpleSuggestion implements Suggestion {
+
+ public String text;
+
+ public SimpleSuggestion(String text) {
+ this.text = text;
+ }
+
+ public String getDisplayString() {
+ return text;
+ }
+
+ public String getReplacementString() {
+ return text;
+ }
+ }
+
+ public void testMoveSelectionUpAndDown() {
+ SuggestBox box = new SuggestBox();
+ SuggestionDisplay display = box.getSuggestionDisplay();
+ SuggestOracle oracle = box.getSuggestOracle();
+
+ // Show some suggestions.
+ List<Suggestion> suggestions = createSuggestions("test0", "test1", "test2",
+ "test3");
+ display.showSuggestions(box, suggestions, false, false, NULL_CALLBACK);
+ assertNull(display.getCurrentSelection());
+
+ display.moveSelectionDown();
+ assertEquals(suggestions.get(0), display.getCurrentSelection());
+ display.moveSelectionDown();
+ assertEquals(suggestions.get(1), display.getCurrentSelection());
+ display.moveSelectionDown();
+ assertEquals(suggestions.get(2), display.getCurrentSelection());
+ display.moveSelectionUp();
+ assertEquals(suggestions.get(1), display.getCurrentSelection());
+ display.moveSelectionUp();
+ assertEquals(suggestions.get(0), display.getCurrentSelection());
+ }
+
+ public void testShowSuggestionsAutoSelectDisabled() {
+ SuggestBox box = new SuggestBox();
+ SuggestionDisplay display = box.getSuggestionDisplay();
+ SuggestOracle oracle = box.getSuggestOracle();
+
+ // Show some suggestions with auto select disabled.
+ List<Suggestion> suggestions = createSuggestions("test0", "test1", "test2");
+ display.showSuggestions(box, suggestions, false, false, NULL_CALLBACK);
+
+ // Nothing should be selected.
+ assertNull(display.getCurrentSelection());
+ }
+
+ public void testShowSuggestionsAutoSelectEnabled() {
+ SuggestBox box = new SuggestBox();
+ SuggestionDisplay display = box.getSuggestionDisplay();
+ SuggestOracle oracle = box.getSuggestOracle();
+
+ // Show some suggestions with auto select enabled.
+ List<Suggestion> suggestions = createSuggestions("test0", "test1", "test2");
+ display.showSuggestions(box, suggestions, false, true, NULL_CALLBACK);
+
+ // First item should be selected.
+ assertEquals(suggestions.get(0), display.getCurrentSelection());
+ }
+
+ /**
+ * Create a list of {@link Suggestion}.
+ *
+ * @param items the items to add to the list
+ * @return the list of suggestions
+ */
+ protected List<Suggestion> createSuggestions(String... items) {
+ List<Suggestion> suggestions = new ArrayList<Suggestion>();
+ for (String item : items) {
+ suggestions.add(new SimpleSuggestion(item));
+ }
+ return suggestions;
+ }
+
+ /**
+ * Create a new {@link SuggestionDisplay} to test.
+ *
+ * @return the {@link SuggestionDisplay}
+ */
+ protected abstract SuggestionDisplay createSuggestionDisplay();
+
+ /**
+ * Create a new {@link SuggestBox}.
+ *
+ * @return the {@link SuggestBox}
+ */
+ protected SuggestBox createSuggestBox() {
+ MultiWordSuggestOracle oracle = createOracle();
+ return new SuggestBox(oracle, new TextBox(), createSuggestionDisplay());
+ }
+
+ private MultiWordSuggestOracle createOracle() {
+ MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
+ oracle.add("test");
+ oracle.add("test1");
+ oracle.add("test2");
+ oracle.add("test3");
+ oracle.add("test4");
+ oracle.add("john");
+ return oracle;
+ }
+}