1 /***********************************************************************
2     created:    21/2/2005
3     author:     Paul D Turner
4 *************************************************************************/
5 /***************************************************************************
6  *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
7  *
8  *   Permission is hereby granted, free of charge, to any person obtaining
9  *   a copy of this software and associated documentation files (the
10  *   "Software"), to deal in the Software without restriction, including
11  *   without limitation the rights to use, copy, modify, merge, publish,
12  *   distribute, sublicense, and/or sell copies of the Software, and to
13  *   permit persons to whom the Software is furnished to do so, subject to
14  *   the following conditions:
15  *
16  *   The above copyright notice and this permission notice shall be
17  *   included in all copies or substantial portions of the Software.
18  *
19  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  ***************************************************************************/
27 #include "CEGUI/widgets/Tooltip.h"
28 #include "CEGUI/Logger.h"
29 #include "CEGUI/Font.h"
30 #include "CEGUI/Image.h"
31 
32 // Start of CEGUI namespace section
33 namespace CEGUI
34 {
35     const String Tooltip::WidgetTypeName("CEGUI/Tooltip");
36 
37     //////////////////////////////////////////////////////////////////////////
38     // Event name constants
39     const String Tooltip::EventNamespace("Tooltip");
40     const String Tooltip::EventHoverTimeChanged("HoverTimeChanged");
41     const String Tooltip::EventDisplayTimeChanged("DisplayTimeChanged");
42     const String Tooltip::EventFadeTimeChanged("FadeTimeChanged");
43     const String Tooltip::EventTooltipActive("TooltipActive");
44     const String Tooltip::EventTooltipInactive("TooltipInactive");
45     const String Tooltip::EventTooltipTransition("TooltipTransition");
46     //////////////////////////////////////////////////////////////////////////
47 
48     /*************************************************************************
49         TooltipWindowRenderer
50     *************************************************************************/
TooltipWindowRenderer(const String & name)51     TooltipWindowRenderer::TooltipWindowRenderer(const String& name) :
52         WindowRenderer(name, Tooltip::EventNamespace)
53     {
54     }
55 
56     //////////////////////////////////////////////////////////////////////////
Tooltip(const String & type,const String & name)57     Tooltip::Tooltip(const String& type, const String& name) :
58         Window(type, name),
59 
60         d_hoverTime(0.4f),
61         d_displayTime(7.5f),
62         d_inPositionSelf(false)
63     {
64         addTooltipProperties();
65 
66         setClippedByParent(false);
67         setDestroyedByParent(false);
68         setAlwaysOnTop(true);
69         setMousePassThroughEnabled(true);
70 
71         // we need updates even when not visible
72         setUpdateMode(WUM_ALWAYS);
73 
74         switchToInactiveState();
75         hide();
76     }
77 
~Tooltip(void)78     Tooltip::~Tooltip(void)
79     {}
80 
positionSelf(void)81     void Tooltip::positionSelf(void)
82     {
83         // no recusion allowed for this function!
84         if (d_inPositionSelf)
85             return;
86 
87         d_inPositionSelf = true;
88 
89         MouseCursor& cursor = getGUIContext().getMouseCursor();
90         Rectf screen(Vector2f(0, 0), getRootContainerSize());
91         Rectf tipRect(getUnclippedOuterRect().get());
92         const Image* mouseImage = cursor.getImage();
93 
94         Vector2f mousePos(cursor.getPosition());
95         Sizef mouseSz(0,0);
96 
97         if (mouseImage)
98         {
99             mouseSz = mouseImage->getRenderedSize();
100         }
101 
102         Vector2f tmpPos(mousePos.d_x + mouseSz.d_width, mousePos.d_y + mouseSz.d_height);
103         tipRect.setPosition(tmpPos);
104 
105         // if tooltip would be off the right of the screen,
106         // reposition to the other side of the mouse cursor.
107         if (screen.right() < tipRect.right())
108         {
109             tmpPos.d_x = mousePos.d_x - tipRect.getWidth() - 5;
110         }
111 
112         // if tooltip would be off the bottom of the screen,
113         // reposition to the other side of the mouse cursor.
114         if (screen.bottom() < tipRect.bottom())
115         {
116             tmpPos.d_y = mousePos.d_y - tipRect.getHeight() - 5;
117         }
118 
119         // set final position of tooltip window.
120         setPosition(
121             UVector2(cegui_absdim(tmpPos.d_x),
122                      cegui_absdim(tmpPos.d_y)));
123 
124         d_inPositionSelf = false;
125     }
126 
sizeSelf(void)127     void Tooltip::sizeSelf(void)
128     {
129         Sizef textSize(getTextSize());
130 
131         setSize(USize(cegui_absdim(textSize.d_width),
132                       cegui_absdim(textSize.d_height)));
133     }
134 
setTargetWindow(Window * wnd)135     void Tooltip::setTargetWindow(Window* wnd)
136     {
137         if (!wnd)
138         {
139             d_target = wnd;
140         }
141         else if (wnd != this)
142         {
143             if (d_target != wnd)
144             {
145                 wnd->getGUIContext().getRootWindow()->addChild(this);
146                 d_target = wnd;
147             }
148 
149             // set text to that of the tooltip text of the target
150             setText(wnd->getTooltipTextIncludingInheritance());
151 
152             // set size and position of the tooltip window.
153             sizeSelf();
154             positionSelf();
155         }
156 
157         resetTimer();
158 
159         if (d_active)
160         {
161             WindowEventArgs args(this);
162             onTooltipTransition(args);
163         }
164     }
165 
getTargetWindow()166     const Window* Tooltip::getTargetWindow()
167     {
168         return d_target;
169     }
170 
getTextSize() const171     Sizef Tooltip::getTextSize() const
172     {
173         if (d_windowRenderer != 0)
174         {
175             TooltipWindowRenderer* wr = (TooltipWindowRenderer*)d_windowRenderer;
176             return wr->getTextSize();
177         }
178         else
179         {
180             return getTextSize_impl();
181         }
182     }
183 
getTextSize_impl() const184     Sizef Tooltip::getTextSize_impl() const
185     {
186         const RenderedString& rs(getRenderedString());
187         Sizef sz(0.0f, 0.0f);
188 
189         for (size_t i = 0; i < rs.getLineCount(); ++i)
190         {
191             const Sizef line_sz(rs.getPixelSize(this, i));
192             sz.d_height += line_sz.d_height;
193 
194             if (line_sz.d_width > sz.d_width)
195                 sz.d_width = line_sz.d_width;
196         }
197 
198         return sz;
199     }
200 
resetTimer(void)201     void Tooltip::resetTimer(void)
202     {
203         d_elapsed = 0;
204     }
205 
getHoverTime(void) const206     float Tooltip::getHoverTime(void) const
207     {
208         return d_hoverTime;
209     }
210 
setHoverTime(float seconds)211     void Tooltip::setHoverTime(float seconds)
212     {
213         if (d_hoverTime != seconds)
214         {
215             d_hoverTime = seconds;
216 
217             WindowEventArgs args(this);
218             onHoverTimeChanged(args);
219         }
220     }
221 
getDisplayTime(void) const222     float Tooltip::getDisplayTime(void) const
223     {
224         return d_displayTime;
225     }
226 
setDisplayTime(float seconds)227     void Tooltip::setDisplayTime(float seconds)
228     {
229         if (d_displayTime != seconds)
230         {
231             d_displayTime = seconds;
232 
233             WindowEventArgs args(this);
234             onDisplayTimeChanged(args);
235         }
236     }
237 
doActiveState(float elapsed)238     void Tooltip::doActiveState(float elapsed)
239     {
240         // if no target, switch immediately to inactive state.
241         if (!d_target || d_target->getTooltipTextIncludingInheritance().empty())
242         {
243             // hide immediately since the text is empty
244             hide();
245 
246             switchToInactiveState();
247         }
248         // else see if display timeout has been reached
249         else if ((d_displayTime > 0) && ((d_elapsed += elapsed) >= d_displayTime))
250         {
251             // display time is up, switch states
252             switchToInactiveState();
253         }
254     }
255 
doInactiveState(float elapsed)256     void Tooltip::doInactiveState(float elapsed)
257     {
258         if (d_target && !d_target->getTooltipTextIncludingInheritance().empty() && ((d_elapsed += elapsed) >= d_hoverTime))
259         {
260             switchToActiveState();
261         }
262     }
263 
switchToInactiveState(void)264     void Tooltip::switchToInactiveState(void)
265     {
266         d_active = false;
267         d_elapsed = 0;
268 
269         // fire event before target gets reset in case that information is required in handler.
270         WindowEventArgs args(this);
271         onTooltipInactive(args);
272 
273         d_target = 0;
274     }
275 
switchToActiveState(void)276     void Tooltip::switchToActiveState(void)
277     {
278         positionSelf();
279         show();
280 
281         d_active = true;
282         d_elapsed = 0;
283 
284         WindowEventArgs args(this);
285         onTooltipActive(args);
286     }
287 
validateWindowRenderer(const WindowRenderer * renderer) const288     bool Tooltip::validateWindowRenderer(const WindowRenderer* renderer) const
289 	{
290 		return dynamic_cast<const TooltipWindowRenderer*>(renderer) != 0;
291 	}
292 
updateSelf(float elapsed)293     void Tooltip::updateSelf(float elapsed)
294     {
295         // base class processing.
296         Window::updateSelf(elapsed);
297 
298         // do something based upon current Tooltip state.
299         if (d_active)
300         {
301             doActiveState(elapsed);
302         }
303         else
304         {
305             doInactiveState(elapsed);
306         }
307     }
308 
addTooltipProperties(void)309     void Tooltip::addTooltipProperties(void)
310     {
311         const String& propertyOrigin = WidgetTypeName;
312 
313         CEGUI_DEFINE_PROPERTY(Tooltip, float,
314             "HoverTime", "Property to get/set the hover timeout value in seconds.  Value is a float.",
315             &Tooltip::setHoverTime, &Tooltip::getHoverTime, 0.4f
316         );
317 
318         CEGUI_DEFINE_PROPERTY(Tooltip, float,
319             "DisplayTime", "Property to get/set the display timeout value in seconds.  Value is a float.",
320             &Tooltip::setDisplayTime, &Tooltip::getDisplayTime, 7.5f
321         );
322     }
323 
onHidden(WindowEventArgs & e)324     void Tooltip::onHidden(WindowEventArgs& e)
325     {
326         Window::onHidden(e);
327 
328         // The animation will take care of fade out or whatnot,
329         // at the end it will hide the tooltip to let us know the transition
330         // is done. At this point we will remove the tooltip from the
331         // previous parent.
332 
333         // NOTE: There has to be a fadeout animation! Even if it's a 0 second
334         //       immediate hide animation.
335 
336         if (getParent())
337         {
338             getParent()->removeChild(this);
339         }
340     }
341 
onMouseEnters(MouseEventArgs & e)342     void Tooltip::onMouseEnters(MouseEventArgs& e)
343     {
344         positionSelf();
345 
346         Window::onMouseEnters(e);
347     }
348 
onTextChanged(WindowEventArgs & e)349     void Tooltip::onTextChanged(WindowEventArgs& e)
350     {
351         // base class processing
352         Window::onTextChanged(e);
353 
354         // set size and position of the tooltip window to consider new text
355         sizeSelf();
356         positionSelf();
357 
358         // we do not signal we handled it, in case user wants to hear
359         // about text changes too.
360     }
361 
onHoverTimeChanged(WindowEventArgs & e)362     void Tooltip::onHoverTimeChanged(WindowEventArgs& e)
363     {
364         fireEvent(EventHoverTimeChanged, e, EventNamespace);
365     }
366 
onDisplayTimeChanged(WindowEventArgs & e)367     void Tooltip::onDisplayTimeChanged(WindowEventArgs& e)
368     {
369         fireEvent(EventDisplayTimeChanged, e, EventNamespace);
370     }
371 
onTooltipActive(WindowEventArgs & e)372     void Tooltip::onTooltipActive(WindowEventArgs& e)
373     {
374         fireEvent(EventTooltipActive, e, EventNamespace);
375     }
376 
onTooltipInactive(WindowEventArgs & e)377     void Tooltip::onTooltipInactive(WindowEventArgs& e)
378     {
379         fireEvent(EventTooltipInactive, e, EventNamespace);
380     }
381 
onTooltipTransition(WindowEventArgs & e)382     void Tooltip::onTooltipTransition(WindowEventArgs& e)
383     {
384         fireEvent(EventTooltipTransition, e, EventNamespace);
385     }
386 } // End of  CEGUI namespace section
387