Search Unity

any decent tooltip implementations?

Discussion in 'Immediate Mode GUI (IMGUI)' started by JoeStrout, Sep 30, 2014.

  1. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I just went to add some mouseover help to elements in my GUI and wow, Unity left a sock on the floor there.

    If I've understood everything correctly, the Unity framework will set GUI.tooltip to the tooltip string of the content the mouse is over. It will not, however, display it, nor give you the information you need to position it properly yourself.

    The docs show just showing the tooltip at a fixed position on the screen, while most solutions on the net show drawing a label at some fixed offset relative to the mouse. Both these solutions are... let's say, suboptimal. The way a tooltip should work is that it's displayed at a fixed position relative to the control (typically, just below it), and only after a the mouse has sat still for a brief delay.

    Unity leaves it up to us to implement the delay, which is annoying but doable — but since we don't get any information about which control the tooltip is set to, or where that is on screen, I don't see any easy way to position it properly.

    All this leads me to conclude that GUI.tooltip is pretty much useless, and I'll need to use something that starts from scratch, based on finding the control under the mouse. But as this poor guy from 2007 found when facing the same problem, there's apparently no way to get the control under the mouse, either, at least when using GUI scripting. (And yeah, seven years later, it appears we're still in the same boat.)

    I guess what I'll need to do is make my own wrapper around GUI.Button (and any other controls I want to add tooltips for), that checks the mouse position as it draws, and stores the information (including the control bounds) needed to display a tooltip later. Then have another function that takes that information, and implements proper drawing of the tooltip after a delay.

    Before I do all that, does anybody know of an existing implementation (that works with GUI scripting)?

    Thanks,
    - Joe
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Well foo, I seem to be answering my own questions a lot lately.

    It turns out this isn't all that terrible... I added some stuff to own "GUIUtils" static class like so:
    Code (CSharp):
    1.     public static Rect mouseOverRect;
    2.     public static string mouseOverCaption;
    3.     public static string mouseOverTooltip;
    4.  
    5.     static public bool Button(Rect r, string caption, string tooltip, Vector2 tooltipOffset=new Vector2()) {
    6.         bool result = GUI.Button(r, caption);
    7.         if (r.Contains(Event.current.mousePosition)) {
    8.             Vector2 topLeft = GUIUtility.GUIToScreenPoint(new Vector2(r.xMin, r.yMin));
    9.             Vector2 botRight = GUIUtility.GUIToScreenPoint(new Vector2(r.xMax, r.yMax));
    10.             mouseOverRect.Set(topLeft.x + tooltipOffset.x,
    11.                               topLeft.y + tooltipOffset.y,
    12.                               botRight.x - topLeft.x,
    13.                               botRight.y - topLeft.y);
    14.             mouseOverCaption = caption;
    15.             mouseOverTooltip = tooltip;
    16.         }
    17.         return result;
    18.     }
    19.  
    20.     static public void ClearMouseOver() {
    21.         mouseOverRect.Set(0, 0, 0, 0);
    22.         mouseOverCaption = "";
    23.         mouseOverTooltip = "";
    24.     }
    ...and replaced GUI.Button with GUIUtils.Button wherever I wanted a button to have a tooltip. Then, I created a new MonoBehaviour for actually drawing the tip, and stuck on some object in the scene:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TooltipDisplay : MonoBehaviour {
    4.     public float stillTime = 1f;    // how long the mouse must be still to show a tip
    5.     public float maxTipWidth = 150f;
    6.     Vector2 lastMousePos;
    7.     float timeOfLastMove;
    8.  
    9.     void OnGUI() {
    10.         Vector2 mousePos = Event.current.mousePosition;
    11.         if ((mousePos - lastMousePos).sqrMagnitude > 4) {
    12.             lastMousePos = mousePos;
    13.             timeOfLastMove = Time.time;
    14.         }
    15.         if (Time.time - timeOfLastMove < stillTime) return;
    16.  
    17.         Rect r = GUIUtils.mouseOverRect;
    18.         if (r.width < 1) return;
    19.  
    20.         GUI.skin = Skins.instance.tooltips;
    21.         GUIStyle style = GUI.skin.GetStyle("Label");
    22.         GUIContent tip = new GUIContent(GUIUtils.mouseOverTooltip);
    23.  
    24.         float minWidth, maxWidth;
    25.         style.CalcMinMaxWidth(tip, out minWidth, out maxWidth);
    26.         r.yMin = r.yMax + 5;
    27.         r.width = Mathf.Min(maxWidth, maxTipWidth);
    28.         r.height = style.CalcHeight(tip, r.width);
    29.         GUI.Label(r, tip);
    30.     }
    31.  
    32.     void LateUpdate() {
    33.         GUIUtils.ClearMouseOver();
    34.     }
    35. }
    I hope this proves useful to somebody!