2015-02-03 07:07:31 +08:00
/// Credit drHogan
/// Sourced from - http://forum.unity3d.com/threads/screenspace-camera-tooltip-controller-sweat-and-tears.293991/#post-1938929
2020-07-09 03:38:28 +08:00
/// updated simonDarksideJ - refactored code to be more performant.
2018-11-26 08:56:54 +08:00
/// updated lucasvinbr - mixed with BoundTooltip, should work with Screenspace Camera (non-rotated) and Overlay
/// *Note - only works for non-rotated Screenspace Camera and Screenspace Overlay canvases at present, needs updating to include rotated Screenspace Camera and Worldspace!
2015-02-03 07:07:31 +08:00
//ToolTip is written by Emiliano Pastorelli, H&R Tallinn (Estonia), http://www.hammerandravens.com
//Copyright (c) 2015 Emiliano Pastorelli, H&R - Hammer&Ravens, Tallinn, Estonia.
//All rights reserved.
//Redistribution and use in source and binary forms are permitted
//provided that the above copyright notice and this paragraph are
//duplicated in all such forms and that any documentation,
//advertising materials, and other materials related to such
//distribution and use acknowledge that the software was developed
//by H&R, Hammer&Ravens. The name of the
//H&R, Hammer&Ravens may not be used to endorse or promote products derived
//from this software without specific prior written permission.
//THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
//IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(RectTransform))]
2018-11-26 08:56:54 +08:00
[AddComponentMenu("UI/Extensions/Tooltip/Tooltip")]
2015-02-03 07:07:31 +08:00
public class ToolTip : MonoBehaviour
{
//text of the tooltip
private Text _text ;
2018-11-26 08:56:54 +08:00
private RectTransform _rectTransform , canvasRectTransform ;
2018-11-26 09:04:28 +08:00
[Tooltip("The canvas used by the tooltip as positioning and scaling reference. Should usually be the root Canvas of the hierarchy this component is in")]
public Canvas canvas ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
[ Tooltip ( "Sets if tooltip triggers will run ForceUpdateCanvases and refresh the tooltip's layout group " +
2020-07-09 03:38:28 +08:00
"(if any) when hovered, in order to prevent momentousness misplacement sometimes caused by ContentSizeFitters" ) ]
2018-11-26 09:04:28 +08:00
public bool tooltipTriggersCanForceCanvasUpdate = false ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
/// <summary>
/// the tooltip's Layout Group, if any
/// </summary>
private LayoutGroup _layoutGroup ;
2015-02-03 07:07:31 +08:00
//if the tooltip is inside a UI element
private bool _inside ;
2016-05-18 05:37:01 +08:00
private float width , height ; //, canvasWidth, canvasHeight;
2015-02-03 07:07:31 +08:00
2018-11-26 08:56:54 +08:00
public float YShift , xShift ;
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
[HideInInspector]
2018-11-26 08:56:54 +08:00
public RenderMode guiMode ;
2015-02-03 07:07:31 +08:00
private Camera _guiCamera ;
2018-11-26 09:04:28 +08:00
public Camera GuiCamera
2015-02-03 07:07:31 +08:00
{
2018-11-26 09:04:28 +08:00
get
{
if ( ! _guiCamera ) {
_guiCamera = Camera . main ;
}
return _guiCamera ;
}
}
private Vector3 screenLowerLeft , screenUpperRight , shiftingVector ;
/// <summary>
/// a screen-space point where the tooltip would be placed before applying X and Y shifts and border checks
/// </summary>
private Vector3 baseTooltipPos ;
private Vector3 newTTPos ;
private Vector3 adjustedNewTTPos ;
private Vector3 adjustedTTLocalPos ;
private Vector3 shifterForBorders ;
private float borderTest ;
// Standard Singleton Access
private static ToolTip instance ;
public static ToolTip Instance
{
get
{
if ( instance = = null )
instance = FindObjectOfType < ToolTip > ( ) ;
return instance ;
}
}
void Reset ( ) {
canvas = GetComponentInParent < Canvas > ( ) ;
canvas = canvas . rootCanvas ;
}
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
// Use this for initialization
public void Awake ( )
{
instance = this ;
if ( ! canvas ) {
canvas = GetComponentInParent < Canvas > ( ) ;
canvas = canvas . rootCanvas ;
}
_guiCamera = canvas . worldCamera ;
2018-11-26 08:56:54 +08:00
guiMode = canvas . renderMode ;
2015-02-03 07:07:31 +08:00
_rectTransform = GetComponent < RectTransform > ( ) ;
2018-11-26 09:04:28 +08:00
canvasRectTransform = canvas . GetComponent < RectTransform > ( ) ;
_layoutGroup = GetComponentInChildren < LayoutGroup > ( ) ;
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
_text = GetComponentInChildren < Text > ( ) ;
2015-02-03 07:07:31 +08:00
_inside = false ;
this . gameObject . SetActive ( false ) ;
}
//Call this function externally to set the text of the template and activate the tooltip
2018-11-26 08:56:54 +08:00
public void SetTooltip ( string ttext , Vector3 basePos , bool refreshCanvasesBeforeGetSize = false )
2015-02-03 07:07:31 +08:00
{
2018-11-26 09:04:28 +08:00
baseTooltipPos = basePos ;
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
//set the text
if ( _text ) {
_text . text = ttext ;
}
else {
Debug . LogWarning ( "[ToolTip] Couldn't set tooltip text, tooltip has no child Text component" ) ;
}
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
ContextualTooltipUpdate ( refreshCanvasesBeforeGetSize ) ;
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
}
2015-02-03 07:07:31 +08:00
//call this function on mouse exit to deactivate the template
public void HideTooltip ( )
{
2018-11-26 08:56:54 +08:00
gameObject . SetActive ( false ) ;
_inside = false ;
2015-02-03 07:07:31 +08:00
}
// Update is called once per frame
2018-11-26 08:56:54 +08:00
void Update ( )
2015-02-03 07:07:31 +08:00
{
if ( _inside )
{
2018-11-26 09:04:28 +08:00
ContextualTooltipUpdate ( ) ;
}
}
/// <summary>
/// forces rebuilding of Canvases in order to update the tooltip's content size fitting.
/// Can prevent the tooltip from being visibly misplaced for one frame when being resized.
/// Only runs if tooltipTriggersCanForceCanvasUpdate is true
/// </summary>
public void RefreshTooltipSize ( ) {
if ( tooltipTriggersCanForceCanvasUpdate ) {
Canvas . ForceUpdateCanvases ( ) ;
if ( _layoutGroup ) {
_layoutGroup . enabled = false ;
_layoutGroup . enabled = true ;
}
2015-02-03 07:07:31 +08:00
}
2018-11-26 09:04:28 +08:00
2015-02-03 07:07:31 +08:00
}
2018-11-26 09:04:28 +08:00
/// <summary>
/// Runs the appropriate tooltip placement method, according to the parent canvas's render mode
/// </summary>
/// <param name="refreshCanvasesBeforeGettingSize"></param>
public void ContextualTooltipUpdate ( bool refreshCanvasesBeforeGettingSize = false ) {
switch ( guiMode ) {
case RenderMode . ScreenSpaceCamera :
OnScreenSpaceCamera ( refreshCanvasesBeforeGettingSize ) ;
break ;
case RenderMode . ScreenSpaceOverlay :
OnScreenSpaceOverlay ( refreshCanvasesBeforeGettingSize ) ;
break ;
}
}
2018-11-26 08:56:54 +08:00
//main tooltip edge of screen guard and movement - camera
public void OnScreenSpaceCamera ( bool refreshCanvasesBeforeGettingSize = false )
2015-02-03 07:07:31 +08:00
{
2018-11-26 09:04:28 +08:00
shiftingVector . x = xShift ;
shiftingVector . y = YShift ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
baseTooltipPos . z = canvas . planeDistance ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
newTTPos = GuiCamera . ScreenToViewportPoint ( baseTooltipPos - shiftingVector ) ;
2018-11-26 08:56:54 +08:00
adjustedNewTTPos = GuiCamera . ViewportToWorldPoint ( newTTPos ) ;
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
gameObject . SetActive ( true ) ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
if ( refreshCanvasesBeforeGettingSize ) RefreshTooltipSize ( ) ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
//consider scaled dimensions when comparing against the edges
width = transform . lossyScale . x * _rectTransform . sizeDelta [ 0 ] ;
2018-11-26 08:56:54 +08:00
height = transform . lossyScale . y * _rectTransform . sizeDelta [ 1 ] ;
2015-02-03 07:07:31 +08:00
// check and solve problems for the tooltip that goes out of the screen on the horizontal axis
2018-11-26 08:56:54 +08:00
RectTransformUtility . ScreenPointToWorldPointInRectangle ( canvasRectTransform , Vector2 . zero , GuiCamera , out screenLowerLeft ) ;
2018-11-26 09:04:28 +08:00
RectTransformUtility . ScreenPointToWorldPointInRectangle ( canvasRectTransform , new Vector2 ( Screen . width , Screen . height ) , GuiCamera , out screenUpperRight ) ;
2018-11-26 08:56:54 +08:00
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
//check for right edge of screen
borderTest = ( adjustedNewTTPos . x + width / 2 ) ;
2018-11-26 08:56:54 +08:00
if ( borderTest > screenUpperRight . x )
2015-02-03 07:07:31 +08:00
{
2018-11-26 09:04:28 +08:00
shifterForBorders . x = borderTest - screenUpperRight . x ;
adjustedNewTTPos . x - = shifterForBorders . x ;
2015-02-03 07:07:31 +08:00
}
//check for left edge of screen
2018-11-26 08:56:54 +08:00
borderTest = ( adjustedNewTTPos . x - width / 2 ) ;
if ( borderTest < screenLowerLeft . x )
2015-02-03 07:07:31 +08:00
{
2018-11-26 09:04:28 +08:00
shifterForBorders . x = screenLowerLeft . x - borderTest ;
adjustedNewTTPos . x + = shifterForBorders . x ;
2015-02-03 07:07:31 +08:00
}
2018-11-26 09:04:28 +08:00
// check and solve problems for the tooltip that goes out of the screen on the vertical axis
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
//check for lower edge of the screen
borderTest = ( adjustedNewTTPos . y - height / 2 ) ;
if ( borderTest < screenLowerLeft . y ) {
shifterForBorders . y = screenLowerLeft . y - borderTest ;
adjustedNewTTPos . y + = shifterForBorders . y ;
}
2015-02-03 07:07:31 +08:00
2018-11-26 09:04:28 +08:00
//check for upper edge of the screen
borderTest = ( adjustedNewTTPos . y + height / 2 ) ;
2018-11-26 08:56:54 +08:00
if ( borderTest > screenUpperRight . y )
2015-02-03 07:07:31 +08:00
{
2018-11-26 09:04:28 +08:00
shifterForBorders . y = borderTest - screenUpperRight . y ;
adjustedNewTTPos . y - = shifterForBorders . y ;
2015-02-03 07:07:31 +08:00
}
2018-11-26 09:04:28 +08:00
//failed attempt to circumvent issues caused when rotating the camera
adjustedNewTTPos = transform . rotation * adjustedNewTTPos ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
transform . position = adjustedNewTTPos ;
adjustedTTLocalPos = transform . localPosition ;
adjustedTTLocalPos . z = 0 ;
transform . localPosition = adjustedTTLocalPos ;
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
_inside = true ;
2015-02-03 07:07:31 +08:00
}
2018-11-26 08:56:54 +08:00
2018-11-26 09:04:28 +08:00
//main tooltip edge of screen guard and movement - overlay
public void OnScreenSpaceOverlay ( bool refreshCanvasesBeforeGettingSize = false ) {
shiftingVector . x = xShift ;
shiftingVector . y = YShift ;
newTTPos = ( baseTooltipPos - shiftingVector ) / canvas . scaleFactor ;
adjustedNewTTPos = newTTPos ;
gameObject . SetActive ( true ) ;
if ( refreshCanvasesBeforeGettingSize ) RefreshTooltipSize ( ) ;
width = _rectTransform . sizeDelta [ 0 ] ;
height = _rectTransform . sizeDelta [ 1 ] ;
// check and solve problems for the tooltip that goes out of the screen on the horizontal axis
//screen's 0 = overlay canvas's 0 (always?)
screenLowerLeft = Vector3 . zero ;
screenUpperRight = canvasRectTransform . sizeDelta ;
//check for right edge of screen
borderTest = ( newTTPos . x + width / 2 ) ;
if ( borderTest > screenUpperRight . x ) {
shifterForBorders . x = borderTest - screenUpperRight . x ;
adjustedNewTTPos . x - = shifterForBorders . x ;
}
//check for left edge of screen
borderTest = ( adjustedNewTTPos . x - width / 2 ) ;
if ( borderTest < screenLowerLeft . x ) {
shifterForBorders . x = screenLowerLeft . x - borderTest ;
adjustedNewTTPos . x + = shifterForBorders . x ;
}
// check and solve problems for the tooltip that goes out of the screen on the vertical axis
//check for lower edge of the screen
borderTest = ( adjustedNewTTPos . y - height / 2 ) ;
if ( borderTest < screenLowerLeft . y ) {
shifterForBorders . y = screenLowerLeft . y - borderTest ;
adjustedNewTTPos . y + = shifterForBorders . y ;
}
//check for upper edge of the screen
borderTest = ( adjustedNewTTPos . y + height / 2 ) ;
if ( borderTest > screenUpperRight . y ) {
shifterForBorders . y = borderTest - screenUpperRight . y ;
adjustedNewTTPos . y - = shifterForBorders . y ;
}
//remove scale factor for the actual positioning of the TT
adjustedNewTTPos * = canvas . scaleFactor ;
transform . position = adjustedNewTTPos ;
_inside = true ;
}
}
2015-02-03 03:25:41 +08:00
}