001package gwt.material.design.client.ui;
002
003/*
004 * #%L
005 * GwtMaterial
006 * %%
007 * Copyright (C) 2015 GwtMaterialDesign
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
023import java.util.Iterator;
024import java.util.NoSuchElementException;
025
026import com.google.gwt.dom.client.Element;
027import com.google.gwt.event.logical.shared.AttachEvent;
028import com.google.gwt.event.shared.HandlerRegistration;
029import com.google.gwt.user.client.ui.HasOneWidget;
030import com.google.gwt.user.client.ui.HasText;
031import com.google.gwt.user.client.ui.HasWidgets;
032import com.google.gwt.user.client.ui.IsWidget;
033import com.google.gwt.user.client.ui.Widget;
034import gwt.material.design.client.constants.Position;
035import gwt.material.design.client.base.HasId;
036import gwt.material.design.client.base.HasPosition;
037
038/**
039 * Basic implementation for the Material Design tooltip.
040 * <h3>UiBinder Example</h3>
041 * <pre>
042 *{@code<m:MaterialTooltip text="...">
043 *    ...
044 * </b:MaterialTooltip>
045 *}
046 * </pre>
047 * @author kevzlou7979
048 * @author Ben Dol
049 */
050public class MaterialTooltip implements IsWidget, HasWidgets, HasOneWidget, HasId, HasText, HasPosition {
051
052    private String text;
053    private Position position = Position.TOP;
054    private int delayMs = 0;
055
056    private Widget widget;
057    private String id;
058
059    private HandlerRegistration attachHandler;
060
061    /**
062     * Creates the empty Tooltip
063     */
064    public MaterialTooltip() {
065    }
066
067    /**
068     * Creates the tooltip around this widget
069     *
070     * @param w widget for the tooltip
071     */
072    public MaterialTooltip(final Widget w) {
073        setWidget(w);
074    }
075
076    /**
077     * Creates the tooltip around this widget with given title
078     *
079     * @param w widget for the tooltip
080     * @param text text for the tooltip
081     */
082    public MaterialTooltip(final Widget w, final String text) {
083        setWidget(w);
084        setText(text);
085    }
086
087    /**
088     * {@inheritDoc}
089     */
090    @Override
091    public void setWidget(final Widget w) {
092        // Validate
093        if (w == widget) {
094            return;
095        }
096
097        if(attachHandler != null) {
098            attachHandler.removeHandler();
099            attachHandler = null;
100        }
101
102        // Detach new child
103        if (w != null) {
104            w.removeFromParent();
105        }
106
107        // Remove old child
108        if (widget != null) {
109            remove(widget);
110        }
111
112        // Logical attach, but don't physical attach; done by jquery.
113        widget = w;
114        if (widget == null) {
115            return;
116        }
117
118        if(!widget.isAttached()) {
119            // When we attach it, configure the tooltip
120            attachHandler = widget.addAttachHandler(new AttachEvent.Handler() {
121                @Override
122                public void onAttachOrDetach(final AttachEvent event) {
123                    reconfigure();
124                }
125            });
126        } else {
127            reconfigure();
128        }
129    }
130
131    /**
132     * {@inheritDoc}
133     */
134    @Override
135    public void add(final Widget child) {
136        if (getWidget() != null) {
137            throw new IllegalStateException("Can only contain one child widget");
138        }
139        setWidget(child);
140    }
141
142    /**
143     * {@inheritDoc}
144     */
145    @Override
146    public void setWidget(final IsWidget w) {
147        setWidget(w.asWidget());
148    }
149
150    /**
151     * {@inheritDoc}
152     */
153    @Override
154    public Widget getWidget() {
155        return widget;
156    }
157
158    /**
159     * {@inheritDoc}
160     */
161    @Override
162    public void setId(final String id) {
163        this.id = id;
164        if (widget != null) {
165            widget.getElement().setId(id);
166        }
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public String getId() {
174        return (widget == null) ? id : widget.getElement().getId();
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    @Override
181    public void setPosition(final Position position) {
182        this.position = position;
183
184        widget.getElement().setAttribute("data-position", position.getCssName());
185    }
186
187    /**
188     * {@inheritDoc}
189     */
190    @Override
191    public Position getPosition() {
192        return position;
193    }
194
195    public void setDelayMs(final int delayMs) {
196        this.delayMs = delayMs;
197
198        widget.getElement().setAttribute("data-delay", String.valueOf(delayMs));
199    }
200
201    public int getDelayMs() {
202        return delayMs;
203    }
204
205    /**
206     * Gets the tooltip's display string
207     *
208     * @return String tooltip display string
209     */
210    @Override
211    public String getText() {
212        return text;
213    }
214
215    /**
216     * Sets the tooltip's display string
217     *
218     * @param text String display string
219     */
220    @Override
221    public void setText(final String text) {
222        this.text = text;
223
224        widget.getElement().setAttribute("data-tooltip", text);
225    }
226
227    /**
228     * Reconfigures the tooltip, must be called when altering
229     * any tooltip after it has already been shown.
230     */
231    public void reconfigure() {
232        remove();
233        configure();
234    }
235
236    protected void configure() {
237        configure(widget.getElement(), text, position.getCssName(), delayMs);
238    }
239
240    /**
241     * Force the Tooltip to be destroyed
242     */
243    public void remove() {
244        if(widget != null) {
245            command(widget.getElement(), "remove");
246        }
247    }
248
249    /**
250     * {@inheritDoc}
251     */
252    @Override
253    public void clear() {
254        widget = null;
255    }
256
257    /**
258     * {@inheritDoc}
259     */
260    @Override
261    public Iterator<Widget> iterator() {
262        // Simple iterator for the widget
263        return new Iterator<Widget>() {
264            boolean hasElement = widget != null;
265            Widget returned = null;
266
267            @Override
268            public boolean hasNext() {
269                return hasElement;
270            }
271
272            @Override
273            public Widget next() {
274                if (!hasElement || (widget == null)) {
275                    throw new NoSuchElementException();
276                }
277                hasElement = false;
278                return (returned = widget);
279            }
280
281            @Override
282            public void remove() {
283                if (returned != null) {
284                    MaterialTooltip.this.remove(returned);
285                }
286            }
287        };
288    }
289
290    /**
291     * {@inheritDoc}
292     */
293    @Override
294    public boolean remove(final Widget w) {
295        // Validate.
296        if (widget != w) {
297            return false;
298        }
299
300        // Logical detach.
301        clear();
302        return true;
303    }
304
305    /**
306     * {@inheritDoc}
307     */
308    @Override
309    public Widget asWidget() {
310        return widget;
311    }
312
313    /**
314     * {@inheritDoc}
315     */
316    @Override
317    public String toString() {
318        return asWidget().toString();
319    }
320
321    protected native void configure(Element e, String tooltip, String position, int delay) /*-{
322        $wnd.jQuery(e).tooltip({
323            tooltip: tooltip,
324            position: position,
325            delay: delay
326        });
327    }-*/;
328
329    protected native void command(final Element e, final String command) /*-{
330        $wnd.jQuery(e).tooltip(command);
331    }-*/;
332}