001 package org.gwtbootstrap3.client.ui;
002
003 /*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 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.Iterator;
024 import java.util.NoSuchElementException;
025
026 import org.gwtbootstrap3.client.shared.event.HiddenEvent;
027 import org.gwtbootstrap3.client.shared.event.HiddenHandler;
028 import org.gwtbootstrap3.client.shared.event.HideEvent;
029 import org.gwtbootstrap3.client.shared.event.HideHandler;
030 import org.gwtbootstrap3.client.shared.event.ShowEvent;
031 import org.gwtbootstrap3.client.shared.event.ShowHandler;
032 import org.gwtbootstrap3.client.shared.event.ShownEvent;
033 import org.gwtbootstrap3.client.shared.event.ShownHandler;
034 import org.gwtbootstrap3.client.ui.base.HasHover;
035 import org.gwtbootstrap3.client.ui.base.HasId;
036 import org.gwtbootstrap3.client.ui.constants.Placement;
037 import org.gwtbootstrap3.client.ui.constants.Trigger;
038
039 import com.google.gwt.dom.client.Element;
040 import com.google.gwt.event.logical.shared.AttachEvent;
041 import com.google.gwt.user.client.Event;
042 import com.google.gwt.user.client.ui.HasOneWidget;
043 import com.google.gwt.user.client.ui.HasWidgets;
044 import com.google.gwt.user.client.ui.IsWidget;
045 import com.google.gwt.user.client.ui.Widget;
046 import com.google.web.bindery.event.shared.HandlerRegistration;
047
048 /**
049 * Basic implementation for the Bootstrap tooltip
050 * <p/>
051 * <a href="http://getbootstrap.com/javascript/#tooltips">Bootstrap Documentation</a>
052 * <p/>
053 * <p/>
054 * <h3>UiBinder example</h3>
055 * <p/>
056 * <pre>
057 * {@code
058 * <b:Tooltip text="...">
059 * ...
060 * </b:Tooltip>
061 * }
062 * </pre>
063 * <p/>
064 * ** Must call reconfigure() after altering any/all Tooltips!
065 *
066 * @author Joshua Godi
067 * @author Pontus Enmark
068 */
069 public class Tooltip implements IsWidget, HasWidgets, HasOneWidget, HasId, HasHover {
070 private static final String TOGGLE = "toggle";
071 private static final String SHOW = "show";
072 private static final String HIDE = "hide";
073 private static final String DESTROY = "destroy";
074
075 // Defaults from http://getbootstrap.com/javascript/#tooltips
076 private boolean isAnimated = true;
077 private boolean isHTML = false;
078 private Placement placement = Placement.TOP;
079 private Trigger trigger = Trigger.HOVER;
080 private String title = "";
081 private int hideDelayMs = 0;
082 private int showDelayMs = 0;
083 private String container = null;
084 private final String selector = null;
085
086 private Widget widget;
087 private String id;
088
089 /**
090 * Creates the empty Tooltip
091 */
092 public Tooltip() {
093 }
094
095 /**
096 * Creates the tooltip around this widget
097 *
098 * @param w widget for the tooltip
099 */
100 public Tooltip(final Widget w) {
101 setWidget(w);
102 }
103
104 /**
105 * {@inheritDoc}
106 */
107 @Override
108 public void setWidget(final Widget w) {
109 // Validate
110 if (w == widget) {
111 return;
112 }
113
114 // Detach new child
115 if (w != null) {
116 w.removeFromParent();
117 }
118
119 // Remove old child
120 if (widget != null) {
121 remove(widget);
122 }
123
124 // Logical attach, but don't physical attach; done by jquery.
125 widget = w;
126 if (widget == null) {
127 return;
128 }
129
130 // Bind jquery events
131 bindJavaScriptEvents(widget.getElement());
132
133 // When we attach it, configure the tooltip
134 widget.addAttachHandler(new AttachEvent.Handler() {
135 @Override
136 public void onAttachOrDetach(final AttachEvent event) {
137 reconfigure();
138 }
139 });
140 }
141
142 /**
143 * {@inheritDoc}
144 */
145 @Override
146 public void add(final Widget child) {
147 if (getWidget() != null) {
148 throw new IllegalStateException("Can only contain one child widget");
149 }
150 setWidget(child);
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 @Override
157 public void setWidget(final IsWidget w) {
158 widget = (w == null) ? null : w.asWidget();
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 @Override
165 public Widget getWidget() {
166 return widget;
167 }
168
169 /**
170 * {@inheritDoc}
171 */
172 @Override
173 public void setId(final String id) {
174 this.id = id;
175 if (widget != null) {
176 widget.getElement().setId(id);
177 }
178 }
179
180 /**
181 * {@inheritDoc}
182 */
183 @Override
184 public String getId() {
185 return (widget == null) ? id : widget.getElement().getId();
186 }
187
188 @Override
189 public void setIsAnimated(final boolean isAnimated) {
190 this.isAnimated = isAnimated;
191 }
192
193 /**
194 * {@inheritDoc}
195 */
196 @Override
197 public boolean isAnimated() {
198 return isAnimated;
199 }
200
201 /**
202 * {@inheritDoc}
203 */
204 @Override
205 public void setIsHtml(final boolean isHTML) {
206 this.isHTML = isHTML;
207 }
208
209 /**
210 * {@inheritDoc}
211 */
212 @Override
213 public boolean isHtml() {
214 return isHTML;
215 }
216
217 /**
218 * {@inheritDoc}
219 */
220 @Override
221 public void setPlacement(final Placement placement) {
222 this.placement = placement;
223 }
224
225 /**
226 * {@inheritDoc}
227 */
228 @Override
229 public Placement getPlacement() {
230 return placement;
231 }
232
233 /**
234 * {@inheritDoc}
235 */
236 @Override
237 public void setTrigger(final Trigger trigger) {
238 this.trigger = trigger;
239 }
240
241 /**
242 * {@inheritDoc}
243 */
244 @Override
245 public Trigger getTrigger() {
246 return trigger;
247 }
248
249 @Override
250 public void setShowDelayMs(final int showDelayMs) {
251 this.showDelayMs = showDelayMs;
252 }
253
254 /**
255 * {@inheritDoc}
256 */
257 @Override
258 public int getShowDelayMs() {
259 return showDelayMs;
260 }
261
262 /**
263 * {@inheritDoc}
264 */
265 @Override
266 public void setHideDelayMs(final int hideDelayMs) {
267 this.hideDelayMs = hideDelayMs;
268 }
269
270 /**
271 * {@inheritDoc}
272 */
273 @Override
274 public int getHideDelayMs() {
275 return hideDelayMs;
276 }
277
278 /**
279 * {@inheritDoc}
280 */
281 @Override
282 public void setContainer(final String container) {
283 this.container = container;
284 }
285
286 /**
287 * {@inheritDoc}
288 */
289 @Override
290 public String getContainer() {
291 return container;
292 }
293
294 /**
295 * Gets the tooltip's display string
296 *
297 * @return String tooltip display string
298 */
299 public String getTitle() {
300 return title;
301 }
302
303 /**
304 * Sets the tooltip's display string
305 *
306 * @param title String display string
307 */
308 public void setTitle(final String title) {
309 this.title = title;
310 }
311
312 /**
313 * Reconfigures the tooltip, must be called when altering any tooltip after it has already been shown
314 */
315 public void reconfigure() {
316 // First destroy the old tooltip
317 destroy();
318
319 // Setup the new tooltip
320 if (container != null && selector != null) {
321 tooltip(widget.getElement(), isAnimated, isHTML, placement.getCssName(), selector, title,
322 trigger.getCssName(), showDelayMs, hideDelayMs, container);
323 } else if (container != null) {
324 tooltip(widget.getElement(), isAnimated, isHTML, placement.getCssName(), title,
325 trigger.getCssName(), showDelayMs, hideDelayMs, container);
326 } else if (selector != null) {
327 tooltip(widget.getElement(), isAnimated, isHTML, placement.getCssName(), selector, title,
328 trigger.getCssName(), showDelayMs, hideDelayMs);
329 } else {
330 tooltip(widget.getElement(), isAnimated, isHTML, placement.getCssName(), title,
331 trigger.getCssName(), showDelayMs, hideDelayMs);
332 }
333 }
334
335 /**
336 * Toggle the Tooltip to either show/hide
337 */
338 public void toggle() {
339 call(widget.getElement(), TOGGLE);
340 }
341
342 /**
343 * Force show the Tooltip
344 */
345 public void show() {
346 call(widget.getElement(), SHOW);
347 }
348
349 /**
350 * Force hide the Tooltip
351 */
352 public void hide() {
353 call(widget.getElement(), HIDE);
354 }
355
356 /**
357 * Force the Tooltip to be destroyed
358 */
359 public void destroy() {
360 call(widget.getElement(), DESTROY);
361 }
362
363 /**
364 * Can be override by subclasses to handle Tooltip's "show" event however
365 * it's recommended to add an event handler to the tooltip.
366 *
367 * @param evt Event
368 * @see org.gwtbootstrap3.client.shared.event.ShowEvent
369 */
370 protected void onShow(final Event evt) {
371 widget.fireEvent(new ShowEvent(evt));
372 }
373
374 /**
375 * Can be override by subclasses to handle Tooltip's "shown" event however
376 * it's recommended to add an event handler to the tooltip.
377 *
378 * @param evt Event
379 * @see ShownEvent
380 */
381 protected void onShown(final Event evt) {
382 widget.fireEvent(new ShownEvent(evt));
383 }
384
385 /**
386 * Can be override by subclasses to handle Tooltip's "hide" event however
387 * it's recommended to add an event handler to the tooltip.
388 *
389 * @param evt Event
390 * @see org.gwtbootstrap3.client.shared.event.HideEvent
391 */
392 protected void onHide(final Event evt) {
393 widget.fireEvent(new HideEvent(evt));
394 }
395
396 /**
397 * Can be override by subclasses to handle Tooltip's "hidden" event however
398 * it's recommended to add an event handler to the tooltip.
399 *
400 * @param evt Event
401 * @see org.gwtbootstrap3.client.shared.event.HiddenEvent
402 */
403 protected void onHidden(final Event evt) {
404 widget.fireEvent(new HiddenEvent(evt));
405 }
406
407 /**
408 * Adds a show handler to the Tooltip that will be fired when the Tooltip's show event is fired
409 *
410 * @param showHandler ShowHandler to handle the show event
411 * @return HandlerRegistration of the handler
412 */
413 public HandlerRegistration addShowHandler(final ShowHandler showHandler) {
414 return widget.addHandler(showHandler, ShowEvent.getType());
415 }
416
417 /**
418 * Adds a shown handler to the Tooltip that will be fired when the Tooltip's shown event is fired
419 *
420 * @param shownHandler ShownHandler to handle the shown event
421 * @return HandlerRegistration of the handler
422 */
423 public HandlerRegistration addShownHandler(final ShownHandler shownHandler) {
424 return widget.addHandler(shownHandler, ShownEvent.getType());
425 }
426
427 /**
428 * Adds a hide handler to the Tooltip that will be fired when the Tooltip's hide event is fired
429 *
430 * @param hideHandler HideHandler to handle the hide event
431 * @return HandlerRegistration of the handler
432 */
433 public HandlerRegistration addHideHandler(final HideHandler hideHandler) {
434 return widget.addHandler(hideHandler, HideEvent.getType());
435 }
436
437 /**
438 * Adds a hidden handler to the Tooltip that will be fired when the Tooltip's hidden event is fired
439 *
440 * @param hiddenHandler HiddenHandler to handle the hidden event
441 * @return HandlerRegistration of the handler
442 */
443 public HandlerRegistration addHiddenHandler(final HiddenHandler hiddenHandler) {
444 return widget.addHandler(hiddenHandler, HiddenEvent.getType());
445 }
446
447 /**
448 * {@inheritDoc}
449 */
450 @Override
451 public void clear() {
452 widget = null;
453 }
454
455 /**
456 * {@inheritDoc}
457 */
458 @Override
459 public Iterator<Widget> iterator() {
460 // Simple iterator for the widget
461 return new Iterator<Widget>() {
462 boolean hasElement = widget != null;
463 Widget returned = null;
464
465 @Override
466 public boolean hasNext() {
467 return hasElement;
468 }
469
470 @Override
471 public Widget next() {
472 if (!hasElement || (widget == null)) {
473 throw new NoSuchElementException();
474 }
475 hasElement = false;
476 return (returned = widget);
477 }
478
479 @Override
480 public void remove() {
481 if (returned != null) {
482 Tooltip.this.remove(returned);
483 }
484 }
485 };
486 }
487
488 /**
489 * {@inheritDoc}
490 */
491 @Override
492 public boolean remove(final Widget w) {
493 // Validate.
494 if (widget != w) {
495 return false;
496 }
497
498 // Logical detach.
499 clear();
500 return true;
501 }
502
503 /**
504 * {@inheritDoc}
505 */
506 @Override
507 public Widget asWidget() {
508 return widget;
509 }
510
511 /**
512 * {@inheritDoc}
513 */
514 @Override
515 public String toString() {
516 return asWidget().toString();
517 }
518
519 // @formatter:off
520 private native void bindJavaScriptEvents(final Element e) /*-{
521 var target = this;
522 var $tooltip = $wnd.jQuery(e);
523
524 $tooltip.on('show.bs.tooltip', function (evt) {
525 target.@org.gwtbootstrap3.client.ui.Tooltip::onShow(Lcom/google/gwt/user/client/Event;)(evt);
526 });
527
528 $tooltip.on('shown.bs.tooltip', function (evt) {
529 target.@org.gwtbootstrap3.client.ui.Tooltip::onShown(Lcom/google/gwt/user/client/Event;)(evt);
530 });
531
532 $tooltip.on('hide.bs.tooltip', function (evt) {
533 target.@org.gwtbootstrap3.client.ui.Tooltip::onHide(Lcom/google/gwt/user/client/Event;)(evt);
534 });
535
536 $tooltip.on('hidden.bs.tooltip', function (evt) {
537 target.@org.gwtbootstrap3.client.ui.Tooltip::onHidden(Lcom/google/gwt/user/client/Event;)(evt);
538 });
539 }-*/;
540
541 private native void call(final Element e, final String arg) /*-{
542 $wnd.jQuery(e).tooltip(arg);
543 }-*/;
544
545 private native void tooltip(Element e, boolean animation, boolean html, String placement, String selector,
546 String title, String trigger, int showDelay, int hideDelay, String container) /*-{
547 $wnd.jQuery(e).tooltip({
548 animation: animation,
549 html: html,
550 placement: placement,
551 selector: selector,
552 title: title,
553 trigger: trigger,
554 delay: {
555 show: showDelay,
556 hide: hideDelay
557 },
558 container: container
559 });
560 }-*/;
561
562 private native void tooltip(Element e, boolean animation, boolean html, String placement,
563 String title, String trigger, int showDelay, int hideDelay, String container) /*-{
564 $wnd.jQuery(e).tooltip({
565 animation: animation,
566 html: html,
567 placement: placement,
568 title: title,
569 trigger: trigger,
570 delay: {
571 show: showDelay,
572 hide: hideDelay
573 },
574 container: container
575 });
576 }-*/;
577
578 private native void tooltip(Element e, boolean animation, boolean html, String placement, String selector,
579 String title, String trigger, int showDelay, int hideDelay) /*-{
580 $wnd.jQuery(e).tooltip({
581 animation: animation,
582 html: html,
583 placement: placement,
584 selector: selector,
585 title: title,
586 trigger: trigger,
587 delay: {
588 show: showDelay,
589 hide: hideDelay
590 }
591 });
592 }-*/;
593
594 private native void tooltip(Element e, boolean animation, boolean html, String placement,
595 String title, String trigger, int showDelay, int hideDelay) /*-{
596 $wnd.jQuery(e).tooltip({
597 animation: animation,
598 html: html,
599 placement: placement,
600 title: title,
601 trigger: trigger,
602 delay: {
603 show: showDelay,
604 hide: hideDelay
605 }
606 });
607 }-*/;
608 }