Difference between revisions of "Tooltips"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
(Setting the ''Tootip'' text to display)
(Added the right statement to create a tooltip on the 0.8 version and fixed the one used for 0.5-0.7 versions. Added a few comments about certain statement used on 0.8 only.)
 
(7 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 +
{{VersionBadge|0.5}} {{VersionBadge|0.6}}
 +
 
I am not an expert but I would like to share what I have learned and gleaned about the ''Tooltip'' widget.
 
I am not an expert but I would like to share what I have learned and gleaned about the ''Tooltip'' widget.
  
Line 11: Line 13:
 
== Creating the Default System ''Tootip'' widget ==
 
== Creating the Default System ''Tootip'' widget ==
  
CEGUI will manage a single default, system wide ''Tooltip'' widget for you that will take care of displaying your tooltip text. You have to initialize it by calling '''CEGUI::System::setTooltip()'' one time, somehwere in your code. I would recommend calling this right after initializing the CEGUI system. Here's a sample call:
+
CEGUI will manage a single default, system wide ''Tooltip'' widget for you that will take care of displaying your tooltip text. You have to initialize it by calling '''CEGUI::System::setDefaultTooltip()'' one time, somehwere in your code. I would recommend calling this right after initializing the CEGUI system. Here's a sample call:
  
<pre>
+
<source lang="cpp">
 
   // Create default ToolTip item
 
   // Create default ToolTip item
   CEGUI::System::getSingleton().setTooltip( (CEGUI::utf8*)"TaharezLook/Tooltip" );
+
  // On the 0.5, 0.6 and 0.7 versions only
</pre>
+
   CEGUI::System::getSingleton().setDefaultTooltip( (CEGUI::utf8*)"TaharezLook/Tooltip" );
 +
  // On the 0.8 version only
 +
  CEGUI::System::getSingleton().getDefaultGUIContext().setDefaultTooltipType( (CEGUI::utf8*)"TaharezLook/Tooltip" );
 +
</source>
  
 
Yep, that's all there is too it.
 
Yep, that's all there is too it.
 
+
In newer versions of CEGUI the method has been renamed to setDefaultTooltip for consistency.
  
 
== Setting the ''Tootip'' text to display ==
 
== Setting the ''Tootip'' text to display ==
Line 28: Line 33:
  
 
You can hardcode toolip text in your code by using the ''WindowObject::setTooltipText()'' function like so:
 
You can hardcode toolip text in your code by using the ''WindowObject::setTooltipText()'' function like so:
<pre>
+
<source lang="cpp">
 
CEGUI::PushButton *mPushButton = (CEGUI::PushButton *)mWinMgr->createWindow(
 
CEGUI::PushButton *mPushButton = (CEGUI::PushButton *)mWinMgr->createWindow(
 
(CEGUI::utf8*)"TaharezLook/PushButton", (CEGUI::utf8*)"PushButton");
 
(CEGUI::utf8*)"TaharezLook/PushButton", (CEGUI::utf8*)"PushButton");
 
// Set other properties like size, position, etc.
 
// Set other properties like size, position, etc.
 
mPushButton->setTooltipText("This is a tooltip.");
 
mPushButton->setTooltipText("This is a tooltip.");
</pre>
+
</source>
  
 
'''Setting the ''Tooltip'' text in your layout (xml) file'''
 
'''Setting the ''Tooltip'' text in your layout (xml) file'''
  
 
The prefered method is to set the tooltip text in your layout file and you can do it like so:
 
The prefered method is to set the tooltip text in your layout file and you can do it like so:
<pre>
+
<source lang="xml">
 
<Window Type="TaharezLook/Editbox" Name="Login/EditBox/Password">
 
<Window Type="TaharezLook/Editbox" Name="Login/EditBox/Password">
 
<Property Name="AbsoluteRect" Value="l:128.000000 t:0.000000 r:486.000000 b:46.000000" />
 
<Property Name="AbsoluteRect" Value="l:128.000000 t:0.000000 r:486.000000 b:46.000000" />
Line 47: Line 52:
 
</Window>
 
</Window>
  
</pre>
+
</source>
  
 
'''Ensure that the Tooltip is registered in your .Scheme file'''
 
'''Ensure that the Tooltip is registered in your .Scheme file'''
<pre>
+
<source lang="xml">
 
<WindowSet Filename="CEGUITaharezLook">
 
<WindowSet Filename="CEGUITaharezLook">
 
....
 
....
Line 56: Line 61:
 
....
 
....
 
</WindowSet>
 
</WindowSet>
</pre>
+
</source>
 +
 
 
'''Ensure that imageset contains graphics for the Tooltip'''
 
'''Ensure that imageset contains graphics for the Tooltip'''
<pre>
+
<source lang="xml">
 
<Imageset Name="TaharezLook" Imagefile="TaharezLook.png" NativeHorzRes="800" NativeVertRes="600" AutoScaled="true">
 
<Imageset Name="TaharezLook" Imagefile="TaharezLook.png" NativeHorzRes="800" NativeVertRes="600" AutoScaled="true">
 
....
 
....
Line 72: Line 78:
 
...
 
...
 
</Imageset>
 
</Imageset>
</pre>
+
</source>
  
 
== How to display the ''Tooltip'' ==
 
== How to display the ''Tooltip'' ==
Line 78: Line 84:
 
'''Inject Time Pulse'''
 
'''Inject Time Pulse'''
  
''CEGUI::System::injectTimePulse(float timeElapsed)''
+
This is a very important piece of information that is not well documented in the current CEGUI system: ''Time Pulse Injection''. The CEGUI system, to my knowledge, does not keep track of time for you by itself - it needs help. You must inject into the CEGUI System elapsed time. If you don't do this, then your ''Tooltip'' and other time sensitive window events (like fades, etc) will not work or will not work properly. What is the ''value'' you inject? The ''value'' should be the amount of time that has elapsed since the previous injection call. Both CEGUI::System and GUI Contextes must be injected. The best place to inject a time pulse into the CEGUI is in a main loop. Here are a couple of examples of how to implement this:
 
+
This is a very important piece of information that is not well documented in the current CEGUI system: ''Time Pulse Injection''. The CEGUI system, to my knowledge, does not keep track of time for you by itself - it needs help. You must inject into the CEGUI System elapsed time. If you don't do this, then your ''Tooltip'' and other time sensitive window events (like fades, etc) will not work or will not work properly. What is the ''value'' you inject? The ''value'' should be the amount of time that has elapsed since the previous injection call. Refer to the ''CEGUI::System::injectTimePulse()''. The best place to inject a time pulse into the CEGUI system is in a main loop. Here are a couple of examples of how to implement this:
+
  
 
'''Inject Time Pulse: via a MAIN Loop'''
 
'''Inject Time Pulse: via a MAIN Loop'''
  
<pre>
+
<source lang="cpp">
 
+
 
#include <time.h>
 
#include <time.h>
 
...
 
...
Line 98: Line 101:
 
   if ( CEGUI::System::getSingletonPtr() )
 
   if ( CEGUI::System::getSingletonPtr() )
 
   {
 
   {
     CEGUI::System::getSingleton().injectTimePulse( ( clock() - mLastTimeInjection ) * 0.001f );
+
    clock_t now = clock();
 +
     CEGUI::System::getSingleton().injectTimePulse( ( now - mLastTimeInjection ) * 0.001f );
 +
    // From the 0.8 version (next to the line above)
 +
    CEGUI::System::getSingleton().getDefaultGUIContext().injectTimePulse( ( now - mLastTimeInjection ) * 0.001f );
 
     mLastTimeInjection = clock();
 
     mLastTimeInjection = clock();
 
   } // CEGUI Time pulse
 
   } // CEGUI Time pulse
Line 104: Line 110:
 
} // while
 
} // while
 
...
 
...
 +
</source>
  
</pre>
+
'''Inject Time Pulse: In C++11'''
 +
<source lang="cpp">
 +
#include <chrono>
 +
...
 +
typedef std::chrono::duration<float, std::chrono::seconds::period> fpTime;
 +
std::chrono::high_resolution_clock::time_point last_time=std::chrono::high_resolution_clock::now();
 +
...
 +
// Your main loop
 +
while ( true ){
 +
 
 +
    {
 +
      CEGUI::System& system = CEGUI::System::getSingleton();
 +
      auto begin = std::chrono::high_resolution_clock::now();
 +
      float elapsed = fpTime(begin - last_time).count();
 +
 
 +
      system.injectTimePulse( elapsed );
 +
      // From the 0.8 version (next to the line above)
 +
      system.getDefaultGUIContext().injectTimePulse( elapsed );
 +
     
 +
      last_time = begin;
 +
 
 +
    }
 +
 
 +
} // while
 +
...
 +
</source>
  
 
'''Inject Time Pulse: via an Ogre::FrameListenter'''
 
'''Inject Time Pulse: via an Ogre::FrameListenter'''
<pre>
+
<source lang="cpp">
  
 
bool clsMyFrameListener::frameStarted(const Ogre::FrameEvent& evt)
 
bool clsMyFrameListener::frameStarted(const Ogre::FrameEvent& evt)
Line 116: Line 148:
 
   // and if it is, inject a time pulse
 
   // and if it is, inject a time pulse
 
   if ( CEGUI::System::getSingletonPtr() )
 
   if ( CEGUI::System::getSingletonPtr() )
     CEGUI::System::getSingleton().injectTimePulse( evt.timeSinceLastFrame );
+
     CEGUI::System::getSingleton().injectTimePulse( evt.timeSinceLastFrame)
 +
    // From the 0.8 version (next to the line above)
 +
    CEGUI::System::getSingleton().getDefaultGUIContext().injectTimePulse( evt.timeSinceLastFrame );
 
   ...
 
   ...
 
   return true;
 
   return true;
 
} //frameStarted
 
} //frameStarted
  
</pre>
+
</source>
  
  
 
The finer the resolution of the injection, the better your fades will look. Injecting time every one second or greater will make your ''fades'' just popup instead of ''fading'' in.
 
The finer the resolution of the injection, the better your fades will look. Injecting time every one second or greater will make your ''fades'' just popup instead of ''fading'' in.
 +
 +
== How to offset the ''Tooltip'' so it doesn't render under the mouse ==
 +
 +
'''Via LookNFeel animation'''
 +
 +
One way to do this is to edit your scheme's looknfeel and change the Tooltip's FadeIn animation to affect the Position property too.
 +
If the tooltip doesn't have a FadeIn animation, just add one.
 +
 +
Example:
 +
<source lang="xml">
 +
    <WidgetLook name="TaharezLook/Tooltip">
 +
        ...
 +
        <AnimationDefinition name="FadeIn" duration="0.33" replayMode="once">
 +
            ...
 +
            <Affector property="Position" interpolator="UVector2" applicationMethod="relative">
 +
                <KeyFrame position="0" value="{{0,12},{0,0}}" />
 +
            </Affector>
 +
            ...
 +
        </AnimationDefinition>
 +
        ...
 +
    </WidgetLook>
 +
</source>
 +
This will offset the tooltip's x by 12 pixels (in my case, that's how wide my system mouse cursor is -- modify to suit your needs).
  
 
== Conclusion ==
 
== Conclusion ==
Line 131: Line 188:
  
 
Some advanced notes. You can have more than one ''Tooltip'' widget. There maybe some scenerios where just one ''Tooltip'' widget won't work (like for a different look and feel from that of the default one, etc.). You can create another ''Tooltip'' widget using the standard CEGUI method for creating all the window widgets then use the ''CEGUI::Window::setToolTip()'' function to set the widget to use the desired ''Tooltip''.
 
Some advanced notes. You can have more than one ''Tooltip'' widget. There maybe some scenerios where just one ''Tooltip'' widget won't work (like for a different look and feel from that of the default one, etc.). You can create another ''Tooltip'' widget using the standard CEGUI method for creating all the window widgets then use the ''CEGUI::Window::setToolTip()'' function to set the widget to use the desired ''Tooltip''.
 +
 +
[[Category:Tutorials]]
 +
[[Category:Update requested]]

Latest revision as of 13:28, 24 November 2014

Written for CEGUI 0.5


Works with versions 0.5.x (obsolete)

Written for CEGUI 0.6


Works with versions 0.6.x (obsolete)

I am not an expert but I would like to share what I have learned and gleaned about the Tooltip widget.

API References: Tooltip System

As you would expect, the Tooltip works very similar to most tooltip systems. Basically the user hovers the mouse over a CEGUI widget and when a specified time has elapsed the tooltip is displayed for a specified amount of time. As with most of the CEGUI widgets the Tooltip widget is based upon the CEGUI Window class widget.

Please note that the following example source code was gleaned and edited from project specific code so there may be some syntax errors.

Creating the Default System Tootip widget

CEGUI will manage a single default, system wide Tooltip widget for you that will take care of displaying your tooltip text. You have to initialize it by calling 'CEGUI::System::setDefaultTooltip() one time, somehwere in your code. I would recommend calling this right after initializing the CEGUI system. Here's a sample call:

  // Create default ToolTip item
  // On the 0.5, 0.6 and 0.7 versions only
  CEGUI::System::getSingleton().setDefaultTooltip( (CEGUI::utf8*)"TaharezLook/Tooltip" );
  // On the 0.8 version only
  CEGUI::System::getSingleton().getDefaultGUIContext().setDefaultTooltipType( (CEGUI::utf8*)"TaharezLook/Tooltip" );

Yep, that's all there is too it. In newer versions of CEGUI the method has been renamed to setDefaultTooltip for consistency.

Setting the Tootip text to display

There are two methods for setting your tooltip text: In your code or in your layout file.

Setting the Tooltip text in your code

You can hardcode toolip text in your code by using the WindowObject::setTooltipText() function like so:

CEGUI::PushButton *mPushButton = (CEGUI::PushButton *)mWinMgr->createWindow(
(CEGUI::utf8*)"TaharezLook/PushButton", (CEGUI::utf8*)"PushButton");
// Set other properties like size, position, etc.
mPushButton->setTooltipText("This is a tooltip.");

Setting the Tooltip text in your layout (xml) file

The prefered method is to set the tooltip text in your layout file and you can do it like so:

<Window Type="TaharezLook/Editbox" Name="Login/EditBox/Password">
	<Property Name="AbsoluteRect" Value="l:128.000000 t:0.000000 r:486.000000 b:46.000000" />
	<Property Name="AlwaysOnTop" Value="True" />
	<Property Name="RelativeRect" Value="l:0.250000 t:0.000000 r:0.950000 b:1.000000" />
	<Property Name="Text" Value="{Enter Password Here}" />
	<Property Name="Tooltip" Value="Enter your password here." />
</Window>

Ensure that the Tooltip is registered in your .Scheme file

<WindowSet Filename="CEGUITaharezLook">
....
<WindowFactory Name="TaharezLook/Tooltip" />
....
</WindowSet>

Ensure that imageset contains graphics for the Tooltip

<Imageset Name="TaharezLook" Imagefile="TaharezLook.png" NativeHorzRes="800" NativeVertRes="600" AutoScaled="true">
....
<Image Name="TooltipLeftEdge" XPos="217" YPos="225" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipRightEdge" XPos="246" YPos="225" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipTopEdge" XPos="224" YPos="218" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipBottomEdge" XPos="224" YPos="248" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipTopLeft" XPos="217" YPos="218" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipTopRight" XPos="246" YPos="218" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipBottomLeft" XPos="217" YPos="248" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipBottomRight" XPos="246" YPos="248" Width="7" Height="7" XOffset="0" YOffset="0"/>
<Image Name="TooltipMiddle" XPos="2" YPos="2" Width="64" Height="64" XOffset="0" YOffset="0"/>
...
</Imageset>

How to display the Tooltip

Inject Time Pulse

This is a very important piece of information that is not well documented in the current CEGUI system: Time Pulse Injection. The CEGUI system, to my knowledge, does not keep track of time for you by itself - it needs help. You must inject into the CEGUI System elapsed time. If you don't do this, then your Tooltip and other time sensitive window events (like fades, etc) will not work or will not work properly. What is the value you inject? The value should be the amount of time that has elapsed since the previous injection call. Both CEGUI::System and GUI Contextes must be injected. The best place to inject a time pulse into the CEGUI is in a main loop. Here are a couple of examples of how to implement this:

Inject Time Pulse: via a MAIN Loop

#include <time.h>
...
clock_t mLastTimeInjection=clock();
...
// Your main loop
while ( true )
{
  ...
  // Make sure the CEGUI System is initialized and running
  // and if it is, inject a time pulse
  if ( CEGUI::System::getSingletonPtr() )
  {
    clock_t now = clock();
    CEGUI::System::getSingleton().injectTimePulse( ( now - mLastTimeInjection ) * 0.001f );
    // From the 0.8 version (next to the line above)
    CEGUI::System::getSingleton().getDefaultGUIContext().injectTimePulse( ( now - mLastTimeInjection ) * 0.001f );
    mLastTimeInjection = clock();
  } // CEGUI Time pulse
  ...
} // while
...

Inject Time Pulse: In C++11

#include <chrono>
...
typedef std::chrono::duration<float, std::chrono::seconds::period> fpTime;
std::chrono::high_resolution_clock::time_point last_time=std::chrono::high_resolution_clock::now();
...
// Your main loop
while ( true ){
 
    {
       CEGUI::System& system = CEGUI::System::getSingleton();
       auto begin = std::chrono::high_resolution_clock::now();
       float elapsed = fpTime(begin - last_time).count();
 
       system.injectTimePulse( elapsed );
       // From the 0.8 version (next to the line above)
       system.getDefaultGUIContext().injectTimePulse( elapsed );
 
       last_time = begin;
 
    }
 
} // while
...

Inject Time Pulse: via an Ogre::FrameListenter

bool clsMyFrameListener::frameStarted(const Ogre::FrameEvent& evt)
{ 
  ...
  // Make sure the CEGUI System is initialized and running
  // and if it is, inject a time pulse
  if ( CEGUI::System::getSingletonPtr() )
    CEGUI::System::getSingleton().injectTimePulse( evt.timeSinceLastFrame)
    // From the 0.8 version (next to the line above)
    CEGUI::System::getSingleton().getDefaultGUIContext().injectTimePulse( evt.timeSinceLastFrame );
  ...
  return true;
} //frameStarted


The finer the resolution of the injection, the better your fades will look. Injecting time every one second or greater will make your fades just popup instead of fading in.

How to offset the Tooltip so it doesn't render under the mouse

Via LookNFeel animation

One way to do this is to edit your scheme's looknfeel and change the Tooltip's FadeIn animation to affect the Position property too. If the tooltip doesn't have a FadeIn animation, just add one.

Example:

    <WidgetLook name="TaharezLook/Tooltip">
        ...
        <AnimationDefinition name="FadeIn" duration="0.33" replayMode="once">
            ...
            <Affector property="Position" interpolator="UVector2" applicationMethod="relative">
                <KeyFrame position="0" value="{{0,12},{0,0}}" />
            </Affector>
            ...
        </AnimationDefinition>
        ...
    </WidgetLook>

This will offset the tooltip's x by 12 pixels (in my case, that's how wide my system mouse cursor is -- modify to suit your needs).

Conclusion

That's really all there is to it. My biggest gotcha was the not knowing about the CEGUI::System::injectTimePulse() and how important it was. Once I stumbled upon that everything else fell into place.

Some advanced notes. You can have more than one Tooltip widget. There maybe some scenerios where just one Tooltip widget won't work (like for a different look and feel from that of the default one, etc.). You can create another Tooltip widget using the standard CEGUI method for creating all the window widgets then use the CEGUI::Window::setToolTip() function to set the widget to use the desired Tooltip.