/* * SearchWidget.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.core.client.widget; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.event.dom.client.*; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; 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.resources.client.ImageResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.*; import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay; import com.google.gwt.user.client.ui.SuggestBox.SuggestionDisplay; import org.rstudio.core.client.events.SelectionCommitEvent; import org.rstudio.core.client.events.SelectionCommitHandler; import org.rstudio.core.client.theme.res.ThemeResources; import org.rstudio.core.client.theme.res.ThemeStyles; public class SearchWidget extends Composite implements SearchDisplay { interface MyUiBinder extends UiBinder<Widget, SearchWidget> {} private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); class FocusSuggestBox extends SuggestBox implements HasAllFocusHandlers { FocusSuggestBox(SuggestOracle oracle) { super(oracle); } FocusSuggestBox(SuggestOracle oracle, TextBoxBase textBox) { super(oracle, textBox); } FocusSuggestBox(SuggestOracle oracle, TextBoxBase textBox, SuggestionDisplay suggestDisplay) { super(oracle, textBox, suggestDisplay); } public HandlerRegistration addBlurHandler(BlurHandler handler) { return addDomHandler(handler, BlurEvent.getType()); } public HandlerRegistration addFocusHandler(FocusHandler handler) { return addDomHandler(handler, FocusEvent.getType()); } } public SearchWidget(SuggestOracle oracle) { this(oracle, null); } public SearchWidget(SuggestOracle oracle, SuggestionDisplay suggestDisplay) { this(oracle, new TextBox(), suggestDisplay); } public SearchWidget(SuggestOracle oracle, TextBoxBase textBox, SuggestionDisplay suggestDisplay) { this(oracle, textBox, suggestDisplay, true); } public SearchWidget(SuggestOracle oracle, TextBoxBase textBox, SuggestionDisplay suggestDisplay, boolean continuousSearch) { textBox.getElement().setAttribute("spellcheck", "false"); if (suggestDisplay != null) suggestBox_ = new FocusSuggestBox(oracle, textBox, suggestDisplay); else suggestBox_ = new FocusSuggestBox(oracle, textBox); initWidget(uiBinder.createAndBindUi(this)); close_.setVisible(false); ThemeStyles styles = ThemeResources.INSTANCE.themeStyles(); suggestBox_.setStylePrimaryName(styles.searchBox()); suggestBox_.setAutoSelectEnabled(false) ; addKeyDownHandler(new KeyDownHandler() { public void onKeyDown(KeyDownEvent event) { switch (event.getNativeKeyCode()) { case KeyCodes.KEY_ENTER: Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { SelectionCommitEvent.fire(SearchWidget.this, suggestBox_.getText()) ; } }) ; break ; case KeyCodes.KEY_ESCAPE: event.preventDefault(); event.stopPropagation(); // defer the handling of ESC so that it doesn't end up // inside other UI (the editor) if/when the parent search // ui is dismissed Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if (getSuggestionDisplay().isSuggestionListShowing()) { getSuggestionDisplay().hideSuggestions(); setText("", true); } else { CloseEvent.fire(SearchWidget.this, SearchWidget.this); } } }); break ; } } }) ; if (continuousSearch) { // Unlike SuggestBox's ValueChangeEvent impl, we want the // event to fire as soon as the value changes suggestBox_.addKeyUpHandler(new KeyUpHandler() { public void onKeyUp(KeyUpEvent event) { String value = suggestBox_.getText(); if (!value.equals(lastValueSent_)) { updateLastValue(value); ValueChangeEvent.fire(SearchWidget.this, value); } } }); } suggestBox_.addValueChangeHandler(new ValueChangeHandler<String>() { public void onValueChange(ValueChangeEvent<String> evt) { if (!evt.getValue().equals(lastValueSent_)) { updateLastValue(evt.getValue()); delegateEvent(SearchWidget.this, evt); } } }); close_.addMouseDownHandler(new MouseDownHandler() { public void onMouseDown(MouseDownEvent event) { event.preventDefault(); suggestBox_.setText(""); ValueChangeEvent.fire(suggestBox_, ""); } }); focusTracker_ = new FocusTracker(suggestBox_); } public HandlerRegistration addFocusHandler(FocusHandler handler) { return suggestBox_.addFocusHandler(handler); } public HandlerRegistration addBlurHandler(BlurHandler handler) { return suggestBox_.addBlurHandler(handler); } public HandlerRegistration addValueChangeHandler( ValueChangeHandler<String> handler) { return addHandler(handler, ValueChangeEvent.getType()); } public HandlerRegistration addSelectionCommitHandler( SelectionCommitHandler<String> handler) { return addHandler(handler, SelectionCommitEvent.getType()) ; } public HandlerRegistration addKeyDownHandler(final KeyDownHandler handler) { return suggestBox_.addKeyDownHandler(new KeyDownHandler() { public void onKeyDown(KeyDownEvent event) { handler.onKeyDown(event); } }); } public HandlerRegistration addSelectionHandler( SelectionHandler<SuggestOracle.Suggestion> handler) { return suggestBox_.addSelectionHandler(handler); } public HandlerRegistration addCloseHandler( CloseHandler<SearchDisplay> handler) { return addHandler(handler, CloseEvent.getType()); } @Override public void setAutoSelectEnabled(boolean selectsFirstItem) { suggestBox_.setAutoSelectEnabled(selectsFirstItem); } public String getText() { return suggestBox_.getText() ; } public void setText(String text) { suggestBox_.setText(text) ; } public void setText(String text, boolean fireEvents) { suggestBox_.setValue(text, fireEvents); } @Override public String getValue() { return suggestBox_.getValue(); } @Override public void setValue(String value) { suggestBox_.setValue(value); } @Override public void setValue(String value, boolean fireEvents) { suggestBox_.setValue(value, fireEvents); } public void setIcon(ImageResource image) { icon_.setResource(image); } public void focus() { suggestBox_.setFocus(true); } public void clear() { setText("", true); close_.setVisible(false); } // NOTE: only works if you are using the default display! public DefaultSuggestionDisplay getSuggestionDisplay() { return (DefaultSuggestionDisplay) suggestBox_.getSuggestionDisplay(); } protected ValueBoxBase<String> getTextBox() { return suggestBox_.getValueBox(); } private void updateLastValue(String value) { lastValueSent_ = value; close_.setVisible(lastValueSent_.length() > 0); } public String getLastValue() { return lastValueSent_; } public void setPlaceholderText(String value) { suggestBox_.getElement().setAttribute("placeholder", value); } public boolean isFocused() { return focusTracker_.isFocused(); } @UiField(provided=true) FocusSuggestBox suggestBox_; @UiField Image close_; @UiField Image icon_; private String lastValueSent_ = null; private final FocusTracker focusTracker_; }