001 package org.gwtbootstrap3.client.ui;
002
003 /*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2015 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023 import java.util.Collection;
024 import java.util.List;
025
026 import org.gwtbootstrap3.client.ui.base.HasAutoComplete;
027 import org.gwtbootstrap3.client.ui.base.HasId;
028 import org.gwtbootstrap3.client.ui.base.HasPlaceholder;
029 import org.gwtbootstrap3.client.ui.base.HasResponsiveness;
030 import org.gwtbootstrap3.client.ui.base.HasSize;
031 import org.gwtbootstrap3.client.ui.base.ValueBoxBase;
032 import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
033 import org.gwtbootstrap3.client.ui.base.mixin.BlankValidatorMixin;
034 import org.gwtbootstrap3.client.ui.base.mixin.EnabledMixin;
035 import org.gwtbootstrap3.client.ui.base.mixin.ErrorHandlerMixin;
036 import org.gwtbootstrap3.client.ui.base.mixin.IdMixin;
037 import org.gwtbootstrap3.client.ui.constants.DeviceSize;
038 import org.gwtbootstrap3.client.ui.constants.InputSize;
039 import org.gwtbootstrap3.client.ui.constants.Styles;
040 import org.gwtbootstrap3.client.ui.form.error.ErrorHandler;
041 import org.gwtbootstrap3.client.ui.form.error.ErrorHandlerType;
042 import org.gwtbootstrap3.client.ui.form.error.HasErrorHandler;
043 import org.gwtbootstrap3.client.ui.form.validator.HasBlankValidator;
044 import org.gwtbootstrap3.client.ui.form.validator.HasValidators;
045 import org.gwtbootstrap3.client.ui.form.validator.Validator;
046
047 import com.google.gwt.core.client.Scheduler;
048 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
049 import com.google.gwt.dom.client.Element;
050 import com.google.gwt.dom.client.Style.Display;
051 import com.google.gwt.dom.client.Style.Unit;
052 import com.google.gwt.editor.client.EditorError;
053 import com.google.gwt.editor.client.HasEditorErrors;
054 import com.google.gwt.event.logical.shared.ResizeEvent;
055 import com.google.gwt.event.logical.shared.ResizeHandler;
056 import com.google.gwt.user.client.Timer;
057 import com.google.gwt.user.client.Window;
058 import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
059 import com.google.gwt.user.client.ui.PopupPanel;
060 import com.google.gwt.user.client.ui.SuggestOracle;
061 import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
062
063 /**
064 * Wrapper for a {@link com.google.gwt.user.client.ui.SuggestBox}.<br/>
065 * <br/>
066 * The default style is inherited from the {@link Styles#DROPDOWN_MENU}. Styling of the suggestions items need
067 * a bit of css in order to be pleasing to the eye.
068 *
069 * <pre>
070 * .dropdown-menu .item {
071 * padding: 5px;
072 * }
073 *
074 * .dropdown-menu .item-selected {
075 * background-color: #eee;
076 * }
077 * </pre>
078 *
079 * @author Steven Jardine
080 */
081 public class SuggestBox extends com.google.gwt.user.client.ui.SuggestBox implements HasId, HasResponsiveness, HasPlaceholder,
082 HasAutoComplete, HasSize<InputSize>, HasEditorErrors<String>, HasErrorHandler, HasValidators<String>,
083 HasBlankValidator<String> {
084
085 static class CustomSuggestionDisplay extends DefaultSuggestionDisplay {
086
087 private ResizeHandler popupResizeHandler = null;
088
089 public CustomSuggestionDisplay() {
090 super();
091 final PopupPanel popup = getPopupPanel();
092 popup.setStyleName(Styles.DROPDOWN_MENU);
093 popup.getElement().getStyle().setDisplay(Display.BLOCK);
094 }
095
096 /**
097 * Resize the popup panel to the size of the suggestBox and place it below the SuggestBox. This is not
098 * ideal but works better in a mobile environment.
099 *
100 * @param box the box the SuggestBox.
101 */
102 private void resizePopup(final com.google.gwt.user.client.ui.SuggestBox box) {
103 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
104 @Override
105 public void execute() {
106 Element e = box.getElement();
107 PopupPanel panel = getPopupPanel();
108 panel.setWidth((e.getAbsoluteRight() - e.getAbsoluteLeft() - 2) + Unit.PX.getType());
109 panel.setPopupPosition(e.getAbsoluteLeft(), e.getAbsoluteBottom());
110 }
111 });
112 }
113
114 /** {@inheritDoc} */
115 @Override
116 protected void showSuggestions(final com.google.gwt.user.client.ui.SuggestBox suggestBox,
117 final Collection<? extends Suggestion> suggestions, final boolean isDisplayStringHTML,
118 final boolean isAutoSelectEnabled, final SuggestionCallback callback) {
119 super.showSuggestions(suggestBox, suggestions, isDisplayStringHTML, isAutoSelectEnabled, callback);
120 resizePopup(suggestBox);
121 if (popupResizeHandler == null) {
122 popupResizeHandler = new ResizeHandler() {
123 private Timer timer = new Timer() {
124 public void run() {
125 resizePopup(suggestBox);
126 }
127 };
128
129 @Override
130 public void onResize(ResizeEvent event) {
131 timer.schedule(250);
132 }
133 };
134 Window.addResizeHandler(popupResizeHandler);
135 }
136 }
137
138 }
139
140 private final EnabledMixin<SuggestBox> enabledMixin = new EnabledMixin<SuggestBox>(this);
141
142 private final ErrorHandlerMixin<String> errorHandlerMixin = new ErrorHandlerMixin<String>(this);
143
144 private final IdMixin<SuggestBox> idMixin = new IdMixin<SuggestBox>(this);
145
146 private final BlankValidatorMixin<SuggestBox, String> validatorMixin = new BlankValidatorMixin<SuggestBox, String>(this,
147 errorHandlerMixin.getErrorHandler());
148
149 /**
150 * Constructor for {@link SuggestBox}. Creates a {@link MultiWordSuggestOracle} and {@link TextBox} to use
151 * with this {@link SuggestBox}.
152 */
153 public SuggestBox() {
154 this(new MultiWordSuggestOracle());
155 }
156
157 /**
158 * Constructor for {@link SuggestBox}. Creates a {@link TextBox} to use with this {@link SuggestBox}.
159 *
160 * @param oracle the oracle for this <code>SuggestBox</code>
161 */
162 public SuggestBox(SuggestOracle oracle) {
163 this(oracle, new TextBox());
164 }
165
166 /**
167 * Constructor for {@link SuggestBox}. The text box will be removed from it's current location and wrapped
168 * by the {@link SuggestBox}.
169 *
170 * @param oracle supplies suggestions based upon the current contents of the text widget
171 * @param box the text widget
172 */
173 public SuggestBox(SuggestOracle oracle, ValueBoxBase<String> box) {
174 this(oracle, box, new CustomSuggestionDisplay());
175 }
176
177 /**
178 * Constructor for {@link SuggestBox}. The text box will be removed from it's current location and wrapped
179 * by the {@link SuggestBox}.
180 *
181 * @param oracle supplies suggestions based upon the current contents of the text widget
182 * @param box the text widget
183 * @param suggestDisplay the class used to display suggestions
184 */
185 public SuggestBox(SuggestOracle oracle, ValueBoxBase<String> box, SuggestionDisplay suggestDisplay) {
186 super(oracle, box, suggestDisplay);
187 setStyleName(Styles.FORM_CONTROL);
188 }
189
190 @Override
191 public void addValidator(Validator<String> validator) {
192 validatorMixin.addValidator(validator);
193 }
194
195 @Override
196 public boolean getAllowBlank() {
197 return validatorMixin.getAllowBlank();
198 }
199
200 /** {@inheritDoc} */
201 @Override
202 public String getAutoComplete() {
203 return getElement().getAttribute(AUTO_COMPLETE);
204 }
205
206 /** {@inheritDoc} */
207 @Override
208 public ErrorHandler getErrorHandler() {
209 return errorHandlerMixin.getErrorHandler();
210 }
211
212 /** {@inheritDoc} */
213 @Override
214 public ErrorHandlerType getErrorHandlerType() {
215 return errorHandlerMixin.getErrorHandlerType();
216 }
217
218 /**
219 * {@inheritDoc}
220 */
221 @Override
222 public String getId() {
223 return idMixin.getId();
224 }
225
226 /** {@inheritDoc} */
227 @Override
228 public String getPlaceholder() {
229 return getElement().getAttribute(PLACEHOLDER);
230 }
231
232 /** {@inheritDoc} */
233 @Override
234 public InputSize getSize() {
235 return InputSize.fromStyleName(getStyleName());
236 }
237
238 @Override
239 public boolean getValidateOnBlur() {
240 return validatorMixin.getValidateOnBlur();
241 }
242
243 /** {@inheritDoc} */
244 @Override
245 public boolean isEnabled() {
246 return enabledMixin.isEnabled();
247 }
248
249 @Override
250 public void reset() {
251 validatorMixin.reset();
252 }
253
254 @Override
255 public void setAllowBlank(boolean allowBlank) {
256 validatorMixin.setAllowBlank(allowBlank);
257 }
258
259 /** {@inheritDoc} */
260 @Override
261 public void setAutoComplete(final boolean autoComplete) {
262 getElement().setAttribute(AUTO_COMPLETE, autoComplete ? ON : OFF);
263 }
264
265 /** {@inheritDoc} */
266 @Override
267 public void setEnabled(final boolean enabled) {
268 enabledMixin.setEnabled(enabled);
269 }
270
271 /** {@inheritDoc} */
272 @Override
273 public void setErrorHandler(ErrorHandler handler) {
274 errorHandlerMixin.setErrorHandler(handler);
275 }
276
277 /** {@inheritDoc} */
278 @Override
279 public void setErrorHandlerType(ErrorHandlerType type) {
280 errorHandlerMixin.setErrorHandlerType(type);
281 }
282
283 /** {@inheritDoc} */
284 @Override
285 public void setHiddenOn(final DeviceSize deviceSize) {
286 StyleHelper.setHiddenOn(this, deviceSize);
287 }
288
289 /** {@inheritDoc} */
290 @Override
291 public void setId(final String id) {
292 idMixin.setId(id);
293 }
294
295 /** {@inheritDoc} */
296 @Override
297 public void setPlaceholder(final String placeHolder) {
298 getElement().setAttribute(PLACEHOLDER, placeHolder != null ? placeHolder : "");
299 }
300
301 /** {@inheritDoc} */
302 @Override
303 public void setSize(final InputSize size) {
304 StyleHelper.addUniqueEnumStyleName(this, InputSize.class, size);
305 }
306
307 @Override
308 public void setValidateOnBlur(boolean validateOnBlur) {
309 validatorMixin.setValidateOnBlur(validateOnBlur);
310 }
311
312 @Override
313 public void setValidators(Validator<String>... validators) {
314 validatorMixin.setValidators(validators);
315 }
316
317 /** {@inheritDoc} */
318 @Override
319 public void setVisibleOn(final DeviceSize deviceSize) {
320 StyleHelper.setVisibleOn(this, deviceSize);
321 }
322
323 /** {@inheritDoc} */
324 @Override
325 public void showErrors(List<EditorError> errors) {
326 errorHandlerMixin.showErrors(errors);
327 }
328
329 @Override
330 public boolean validate() {
331 return validatorMixin.validate();
332 }
333
334 @Override
335 public boolean validate(boolean show) {
336 return validatorMixin.validate(show);
337 }
338
339 }