Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GUITooltip.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2009 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "GUITooltip.h"
21 #include "lib/timer.h"
22 #include "IGUIObject.h"
23 #include "CGUI.h"
24 #include "GUIutil.h"
25 
26 #include "ps/CLogger.h"
27 
28 /*
29  Tooltips:
30  When holding the mouse stationary over an object for some amount of time,
31  the tooltip is displayed. If the mouse moves off that object, the tooltip
32  disappears. If the mouse re-enters an object within a short time, the new
33  tooltip is displayed immediately. (This lets you run the mouse across a
34  series of buttons, without waiting ages for the text to pop up every time.)
35 
36  See Visual Studio's toolbar buttons for an example.
37 
38 
39  Implemented as a state machine:
40 
41  (where "*" lines are checked constantly, and "<" lines are handled
42  on entry to that state)
43 
44  IN MOTION
45  * If the mouse stops, check whether it should have a tooltip and move to
46  'STATIONARY, NO TOOLTIP' or 'STATIONARY, TOOLIP'
47  * If the mouse enters an object with a tooltip delay of 0, switch to 'SHOWING'
48 
49  STATIONARY, NO TOOLTIP
50  * If the mouse moves, switch to 'IN MOTION'
51 
52  STATIONARY, TOOLTIP
53  < Set target time = now + tooltip time
54  * If the mouse moves, switch to 'IN MOTION'
55  * If now > target time, switch to 'SHOWING'
56 
57  SHOWING
58  < Start displaying the tooltip
59  * If the mouse leaves the object, check whether the new object has a tooltip
60  and switch to 'SHOWING' or 'COOLING'
61 
62  COOLING (since I can't think of a better name)
63  < Stop displaying the tooltip
64  < Set target time = now + cooldown time
65  * If the mouse has moved and is over a tooltipped object, switch to 'SHOWING'
66  * If now > target time, switch to 'STATIONARY, NO TOOLTIP'
67 */
68 
69 enum
70 {
76 };
77 
79 : m_State(ST_IN_MOTION), m_PreviousObject(NULL), m_PreviousTooltipName()
80 {
81 }
82 
83 const double CooldownTime = 0.25; // TODO: Don't hard-code this value
84 
85 bool GUITooltip::GetTooltip(IGUIObject* obj, CStr& style)
86 {
87  if (obj && obj->SettingExists("_icon_tooltip_style") && obj->MouseOverIcon())
88  {
89  // Special code to handle icon tooltips in text objects
90  if (GUI<CStr>::GetSetting(obj, "_icon_tooltip_style", style) == PSRETURN_OK)
91  {
92  // Check if icon tooltip text exists
93  CStrW text;
94  if (GUI<CStrW>::GetSetting(obj, "_icon_tooltip", text) == PSRETURN_OK)
95  {
96  if (text.empty())
97  {
98  // No tooltip
99  return false;
100  }
101 
102  if (style.empty())
103  {
104  // Text, but no style - use default
105  style = "default";
106  }
107 
108  m_IsIconTooltip = true;
109  return true;
110  }
111  }
112  }
113  else if (obj && obj->SettingExists("tooltip_style")
114  && GUI<CStr>::GetSetting(obj, "tooltip_style", style) == PSRETURN_OK)
115  {
116  CStrW text;
117  if (GUI<CStrW>::GetSetting(obj, "tooltip", text) == PSRETURN_OK)
118  {
119  if (text.empty())
120  {
121  // No tooltip
122  return false;
123  }
124 
125  if (style.empty())
126  {
127  // Text, but no style - use default
128  style = "default";
129  }
130 
131  m_IsIconTooltip = false;
132  return true;
133  }
134  }
135 
136  // Failed while retrieving tooltip_style or tooltip
137  return false;
138 }
139 
140 void GUITooltip::ShowTooltip(IGUIObject* obj, CPos pos, const CStr& style, CGUI* gui)
141 {
142  ENSURE(obj);
143 
144  // Ignore attempts to use tooltip ""
145  if (style.empty())
146  return;
147 
148  // Get the object referenced by 'tooltip_style'
149  IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style);
150  if (! tooltipobj)
151  {
152  LOGERROR(L"Cannot find tooltip named '%hs'", style.c_str());
153  return;
154  }
155 
156  IGUIObject* usedobj = tooltipobj; // object actually used to display the tooltip in
157 
158  CStr usedObjectName;
159  if (GUI<CStr>::GetSetting(tooltipobj, "use_object", usedObjectName) == PSRETURN_OK
160  && !usedObjectName.empty())
161  {
162  usedobj = gui->FindObjectByName(usedObjectName);
163  if (! usedobj)
164  {
165  LOGERROR(L"Cannot find object named '%hs' used by tooltip '%hs'", usedObjectName.c_str(), style.c_str());
166  return;
167  }
168 
169  // Unhide the object. (If it had use_object and hide_object="true",
170  // still unhide it, because the used object might be hidden by default)
171  GUI<bool>::SetSetting(usedobj, "hidden", false);
172  }
173  else
174  {
175  // Unhide the object
176  GUI<bool>::SetSetting(usedobj, "hidden", false);
177 
178  // Store mouse position inside the CTooltip
179  if (GUI<CPos>::SetSetting(usedobj, "_mousepos", pos) != PSRETURN_OK)
180  debug_warn(L"Failed to set tooltip mouse position");
181  }
182 
183  // Retrieve object's 'tooltip' setting
184  CStrW text;
185  if (m_IsIconTooltip)
186  {
187  // Use icon tooltip property
188  if (GUI<CStrW>::GetSetting(obj, "_icon_tooltip", text) != PSRETURN_OK)
189  debug_warn(L"Failed to retrieve icon tooltip text"); // shouldn't fail
190  }
191  else
192  {
193  // Use normal tooltip property
194  if (GUI<CStrW>::GetSetting(obj, "tooltip", text) != PSRETURN_OK)
195  debug_warn(L"Failed to retrieve tooltip text"); // shouldn't fail
196  }
197 
198  // Do some minimal processing ("\n" -> newline, etc)
199  text = text.UnescapeBackslashes();
200 
201  // Set tooltip's caption
202  if (usedobj->SetSetting("caption", text) != PSRETURN_OK)
203  debug_warn(L"Failed to set tooltip caption"); // shouldn't fail
204 
205  // Make the tooltip object regenerate its text
206  SGUIMessage msg(GUIM_SETTINGS_UPDATED, "caption");
207  usedobj->HandleMessage(msg);
208 }
209 
210 void GUITooltip::HideTooltip(const CStr& style, CGUI* gui)
211 {
212  // Ignore attempts to use tooltip ""
213  if (style.empty())
214  return;
215 
216  IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style);
217  if (! tooltipobj)
218  {
219  LOGERROR(L"Cannot find tooltip named '%hs'", style.c_str());
220  return;
221  }
222 
223  CStr usedObjectName;
224  if (GUI<CStr>::GetSetting(tooltipobj, "use_object", usedObjectName) == PSRETURN_OK
225  && !usedObjectName.empty())
226  {
227  IGUIObject* usedobj = gui->FindObjectByName(usedObjectName);
228  if (! usedobj)
229  {
230  LOGERROR(L"Cannot find object named '%hs' used by tooltip '%hs'", usedObjectName.c_str(), style.c_str());
231  return;
232  }
233 
234  // Clear the caption
235  usedobj->SetSetting("caption", L"");
236  SGUIMessage msg(GUIM_SETTINGS_UPDATED, "caption");
237  usedobj->HandleMessage(msg);
238 
239  bool hideobject = true;
240  GUI<bool>::GetSetting(tooltipobj, "hide_object", hideobject);
241 
242  // If hide_object was enabled, hide it
243  if (hideobject)
244  GUI<bool>::SetSetting(usedobj, "hidden", true);
245  }
246  else
247  {
248  GUI<bool>::SetSetting(tooltipobj, "hidden", true);
249  }
250 
251 }
252 
253 static int GetTooltipDelay(CStr& style, CGUI* gui)
254 {
255  int delay = 500; // default value (in msec)
256 
257  IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style);
258  if (! tooltipobj)
259  {
260  LOGERROR(L"Cannot find tooltip object named '%hs'", style.c_str());
261  return delay;
262  }
263  GUI<int>::GetSetting(tooltipobj, "delay", delay);
264  return delay;
265 }
266 
267 void GUITooltip::Update(IGUIObject* Nearest, CPos MousePos, CGUI* GUI)
268 {
269  // Called once per frame, so efficiency isn't vital
270 
271 
272  double now = timer_Time();
273 
274  CStr style;
275 
276  int nextstate = -1;
277 
278  switch (m_State)
279  {
280  case ST_IN_MOTION:
281  if (MousePos == m_PreviousMousePos)
282  {
283  if (GetTooltip(Nearest, style))
284  nextstate = ST_STATIONARY_TOOLTIP;
285  else
286  nextstate = ST_STATIONARY_NO_TOOLTIP;
287  }
288  else
289  {
290  // Check for movement onto a zero-delayed tooltip
291  if (GetTooltip(Nearest, style) && GetTooltipDelay(style, GUI)==0)
292  {
293  // Reset any previous tooltips completely
294  //m_Time = now + (double)GetTooltipDelay(style, GUI) / 1000.;
296 
297  nextstate = ST_SHOWING;
298  }
299  }
300  break;
301 
303  if (MousePos != m_PreviousMousePos)
304  nextstate = ST_IN_MOTION;
305  break;
306 
308  if (MousePos != m_PreviousMousePos)
309  nextstate = ST_IN_MOTION;
310  else if (now >= m_Time)
311  {
312  // Make sure the tooltip still exists
313  if (GetTooltip(Nearest, style))
314  nextstate = ST_SHOWING;
315  else
316  {
317  // Failed to retrieve style - the object has probably been
318  // altered, so just restart the process
319  nextstate = ST_IN_MOTION;
320  }
321  }
322  break;
323 
324  case ST_SHOWING:
325  // Handle special case of icon tooltips
326  if (Nearest == m_PreviousObject && (!m_IsIconTooltip || Nearest->MouseOverIcon()))
327  {
328  // Still showing the same object's tooltip, but the text might have changed
329  if (GetTooltip(Nearest, style))
330  ShowTooltip(Nearest, MousePos, style, GUI);
331  }
332  else
333  {
334  // Mouse moved onto a new object
335 
336  if (GetTooltip(Nearest, style))
337  {
338  CStr style_old;
339 
340  // If we're displaying a tooltip with no delay, then we want to
341  // reset so that other object that should have delay can't
342  // "ride this tail", it have to wait.
343  // Notice that this doesn't apply to when you go from one delay=0
344  // to another delay=0
345  if (GetTooltip(m_PreviousObject, style_old) && GetTooltipDelay(style_old, GUI)==0 &&
346  GetTooltipDelay(style, GUI)!=0)
347  {
349  nextstate = ST_IN_MOTION;
350  }
351  else
352  {
353  // Hide old scrollbar
355  nextstate = ST_SHOWING;
356  }
357  }
358  else
359  {
360  nextstate = ST_COOLING;
361  }
362  }
363  break;
364 
365  case ST_COOLING:
366  if (GetTooltip(Nearest, style))
367  nextstate = ST_SHOWING;
368  else if (now >= m_Time)
369  nextstate = ST_IN_MOTION;
370  break;
371  }
372 
373  // Handle state-entry code:
374 
375  if (nextstate != -1)
376  {
377  switch (nextstate)
378  {
380  m_Time = now + (double)GetTooltipDelay(style, GUI) / 1000.;
381  break;
382 
383  case ST_SHOWING:
384  ShowTooltip(Nearest, MousePos, style, GUI);
385  m_PreviousTooltipName = style;
386  break;
387 
388  case ST_COOLING:
390  m_Time = now + CooldownTime;
391  break;
392  }
393 
394  m_State = nextstate;
395  }
396 
397  m_PreviousMousePos = MousePos;
398  m_PreviousObject = Nearest;
399 }
bool m_IsIconTooltip
Definition: GUITooltip.h:45
void HideTooltip(const CStr &style, CGUI *gui)
Definition: GUITooltip.cpp:210
#define LOGERROR
Definition: CLogger.h:35
int m_State
Definition: GUITooltip.h:39
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
CStr m_PreviousTooltipName
Definition: GUITooltip.h:42
static PSRETURN GetSetting(const IGUIObject *pObject, const CStr &Setting, T &Value)
Retrieves a setting by name from object pointer.
Definition: GUIutil.cpp:344
Base settings, all objects possess these settings in their m_BaseSettings Instructions can be found i...
Definition: IGUIObject.h:140
The main object that represents a whole GUI page.
Definition: CGUI.h:97
Includes static functions that needs one template argument.
Definition: GUIutil.h:103
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
static int GetTooltipDelay(CStr &style, CGUI *gui)
Definition: GUITooltip.cpp:253
void Update(IGUIObject *Nearest, CPos MousePos, CGUI *GUI)
Definition: GUITooltip.cpp:267
void ShowTooltip(IGUIObject *obj, CPos pos, const CStr &style, CGUI *gui)
Definition: GUITooltip.cpp:140
bool GetTooltip(IGUIObject *obj, CStr &style)
Definition: GUITooltip.cpp:85
double timer_Time()
Definition: timer.cpp:98
Made to represent screen positions and delta values.
Definition: Overlay.h:167
const double CooldownTime
Definition: GUITooltip.cpp:83
CPos m_PreviousMousePos
Definition: GUITooltip.h:43
bool SettingExists(const CStr &Setting) const
Checks if settings exists, only available for derived classes that has this set up, that&#39;s why the base class just returns false.
Definition: IGUIObject.cpp:239
PSRETURN SetSetting(const CStr &Setting, const CStrW &Value, const bool &SkipMessage=false)
Set a setting by string, regardless of what type it is.
Definition: IGUIObject.cpp:260
static PSRETURN SetSetting(IGUIObject *pObject, const CStr &Setting, const T &Value, const bool &SkipMessage=false)
Sets a value by name using a real datatype as input.
Definition: GUIutil.cpp:366
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
Message send to IGUIObject::HandleMessage() in order to give life to Objects manually with a derived ...
Definition: GUIbase.h:106
IGUIObject * FindObjectByName(const CStr &Name) const
Returns the GUI object with the desired name, or NULL if no match is found,.
Definition: CGUI.cpp:568
virtual void HandleMessage(SGUIMessage &Message)
This function is called with different messages for instance when the mouse enters the object...
Definition: IGUIObject.h:322
double m_Time
Definition: GUITooltip.h:44
virtual bool MouseOverIcon()
Test if mouse position is over an icon.
Definition: IGUIObject.cpp:202
IGUIObject * m_PreviousObject
Definition: GUITooltip.h:41