mirror of
https://github.com/XCharts-Team/XCharts.git
synced 2026-05-21 16:00:24 +00:00
3.0 - unitypackage
This commit is contained in:
504
Runtime/Component/Tooltip/Tooltip.cs
Normal file
504
Runtime/Component/Tooltip/Tooltip.cs
Normal file
@@ -0,0 +1,504 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XUGL;
|
||||
|
||||
namespace XCharts
|
||||
{
|
||||
/// <summary>
|
||||
/// Tooltip component.
|
||||
/// 提示框组件。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
[ComponentHandler(typeof(TooltipHandler), true)]
|
||||
public class Tooltip : MainComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicator type.
|
||||
/// 指示器类型。
|
||||
/// </summary>
|
||||
public enum Type
|
||||
{
|
||||
/// <summary>
|
||||
/// line indicator.
|
||||
/// 直线指示器
|
||||
/// </summary>
|
||||
Line,
|
||||
/// <summary>
|
||||
/// shadow crosshair indicator.
|
||||
/// 阴影指示器
|
||||
/// </summary>
|
||||
Shadow,
|
||||
/// <summary>
|
||||
/// no indicator displayed.
|
||||
/// 无指示器
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// crosshair indicator, which is actually the shortcut of enable two axisPointers of two orthometric axes.
|
||||
/// 十字准星指示器。坐标轴显示Label和交叉线。
|
||||
/// </summary>
|
||||
Corss
|
||||
}
|
||||
|
||||
public enum Trigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggered by data item, which is mainly used for charts that don't have a category axis like scatter charts or pie charts.
|
||||
/// 数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用。
|
||||
/// </summary>
|
||||
Item,
|
||||
/// <summary>
|
||||
/// Triggered by axes, which is mainly used for charts that have category axes, like bar charts or line charts.
|
||||
/// 坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用。
|
||||
/// </summary>
|
||||
Axis,
|
||||
/// <summary>
|
||||
/// Trigger nothing.
|
||||
/// 什么都不触发。
|
||||
/// </summary>
|
||||
None
|
||||
}
|
||||
|
||||
[SerializeField] private bool m_Show = true;
|
||||
[SerializeField] private Type m_Type;
|
||||
[SerializeField] private Trigger m_Trigger = Trigger.Item;
|
||||
[SerializeField] private string m_ItemFormatter;
|
||||
[SerializeField] private string m_TitleFormatter;
|
||||
[SerializeField] private string m_Marker = "●";
|
||||
[SerializeField] private float m_FixedWidth = 0;
|
||||
[SerializeField] private float m_FixedHeight = 0;
|
||||
[SerializeField] private float m_MinWidth = 0;
|
||||
[SerializeField] private float m_MinHeight = 0;
|
||||
[SerializeField] private string m_NumericFormatter = "";
|
||||
[SerializeField] private int m_PaddingLeftRight = 10;
|
||||
[SerializeField] private int m_PaddingTopBottom = 10;
|
||||
[SerializeField] private bool m_IgnoreDataShow = false;
|
||||
[SerializeField] private string m_IgnoreDataDefaultContent = "-";
|
||||
[SerializeField] private bool m_AlwayShow = false;
|
||||
[SerializeField] private Vector2 m_Offset = new Vector2(18f, -25f);
|
||||
[SerializeField] private Sprite m_BackgroundImage;
|
||||
[SerializeField] private Color m_BackgroundColor;
|
||||
[SerializeField] private float m_BorderWidth = 2f;
|
||||
[SerializeField] private bool m_FixedXEnable = false;
|
||||
[SerializeField] private float m_FixedX = 0f;
|
||||
[SerializeField] private bool m_FixedYEnable = false;
|
||||
[SerializeField] private float m_FixedY = 0f;
|
||||
[SerializeField] private float m_TitleHeight = 25f;
|
||||
[SerializeField] private float m_ItemHeight = 25f;
|
||||
[SerializeField] private Color32 m_BorderColor = new Color32(230, 230, 230, 255);
|
||||
[SerializeField] private LineStyle m_LineStyle = new LineStyle(LineStyle.Type.None);
|
||||
[SerializeField] private TextStyle m_LabelTextStyle = new TextStyle();
|
||||
[SerializeField] private TextStyle m_TitleTextStyle = new TextStyle() { alignment = TextAnchor.MiddleLeft };
|
||||
[SerializeField]
|
||||
private List<TextStyle> m_ColumnsTextStyle = new List<TextStyle>() {
|
||||
new TextStyle() { alignment = TextAnchor.MiddleLeft, extraWidth = 5 },
|
||||
new TextStyle() { alignment = TextAnchor.MiddleLeft, extraWidth = 20 },
|
||||
new TextStyle() { alignment = TextAnchor.MiddleRight, extraWidth = 5 }
|
||||
};
|
||||
|
||||
public TooltipContext context = new TooltipContext();
|
||||
public TooltipView view;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show the tooltip component.
|
||||
/// 是否显示提示框组件。
|
||||
/// </summary>
|
||||
public bool show
|
||||
{
|
||||
get { return m_Show; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_Show, value)) { SetAllDirty(); SetActive(value); } }
|
||||
}
|
||||
/// <summary>
|
||||
/// Indicator type.
|
||||
/// 提示框指示器类型。
|
||||
/// </summary>
|
||||
public Type type
|
||||
{
|
||||
get { return m_Type; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_Type, value)) SetAllDirty(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// Type of triggering.
|
||||
/// 触发类型。
|
||||
/// </summary>
|
||||
public Trigger trigger
|
||||
{
|
||||
get { return m_Trigger; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_Trigger, value)) SetAllDirty(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// The string template formatter for the tooltip title content. Support for wrapping lines with \n.
|
||||
/// The placeholder {I} can be set separately to indicate that the title is ignored and not displayed.
|
||||
/// Template variables are {.}, {a}, {b}, {c}, {d}.
|
||||
/// {.} is the dot of the corresponding color of a Serie that is currently indicated or whose index is 0.
|
||||
/// {a} is the series name of the serie that is currently indicated or whose index is 0.
|
||||
/// {b} is the name of the data item serieData that is currently indicated or whose index is 0, or a category value (such as the X-axis of a line chart).
|
||||
/// {c} is the value of a Y-dimension (dimesion is 1) from a Serie that is currently indicated or whose index is 0.
|
||||
/// {d} is the percentage value of Y-dimensions (dimesion is 1) from serie that is currently indicated or whose index is 0, with no % sign.
|
||||
/// {e} is the name of the data item serieData that is currently indicated or whose index is 0.
|
||||
/// {.1} represents a dot from serie corresponding color that specifies index as 1.
|
||||
/// 1 in {a1}, {b1}, {c1} represents a serie that specifies an index of 1.
|
||||
/// {c1:2} represents the third data from serie's current indication data item indexed to 1 (a data item has multiple data, index 2 represents the third data).
|
||||
/// {c1:2-2} represents the third data item from serie's third data item indexed to 1 (i.e., which data item must be specified to specify).
|
||||
/// {d1:2: F2} indicates that a formatted string with a value specified separately is F2 (numericFormatter is used when numericFormatter is not specified).
|
||||
/// {d:0.##} indicates that a formatted string with a value specified separately is 0.## (used for percentage, reserved 2 valid digits while avoiding the situation similar to "100.00%" when using f2 ).
|
||||
/// Example: "{a}, {c}", "{a1}, {c1: f1}", "{a1}, {c1:0: f1}", "{a1} : {c1:1-1: f1}"
|
||||
/// 提示框标题内容的字符串模版格式器。支持用 \n 换行。可以单独设置占位符{i}表示忽略不显示title。
|
||||
/// 模板变量有{.}、{a}、{b}、{c}、{d}、{e}。
|
||||
/// {.}为当前所指示或index为0的serie的对应颜色的圆点。
|
||||
/// {a}为当前所指示或index为0的serie的系列名name。
|
||||
/// {b}为当前所指示或index为0的serie的数据项serieData的name,或者类目值(如折线图的X轴)。
|
||||
/// {c}为当前所指示或index为0的serie的y维(dimesion为1)的数值。
|
||||
/// {d}为当前所指示或index为0的serie的y维(dimesion为1)百分比值,注意不带%号。
|
||||
/// {e}为当前所指示或index为0的serie的数据项serieData的name。
|
||||
/// {.1}表示指定index为1的serie对应颜色的圆点。
|
||||
/// {a1}、{b1}、{c1}中的1表示指定index为1的serie。
|
||||
/// {c1:2}表示索引为1的serie的当前指示数据项的第3个数据(一个数据项有多个数据,index为2表示第3个数据)。
|
||||
/// {c1:2-2}表示索引为1的serie的第3个数据项的第3个数据(也就是要指定第几个数据项时必须要指定第几个数据)。
|
||||
/// {d1:2:f2}表示单独指定了数值的格式化字符串为f2(不指定时用numericFormatter)。
|
||||
/// {d:0.##} 表示单独指定了数值的格式化字符串为 0.## (用于百分比,保留2位有效数同时又能避免使用 f2 而出现的类似于"100.00%"的情况 )。
|
||||
/// 示例:"{a}:{c}"、"{a1}:{c1:f1}"、"{a1}:{c1:0:f1}"、"{a1}:{c1:1-1:f1}"
|
||||
/// </summary>
|
||||
public string titleFormatter { get { return m_TitleFormatter; } set { m_TitleFormatter = value; } }
|
||||
/// <summary>
|
||||
/// a string template formatter for a single Serie or data item content. Support for wrapping lines with \n.
|
||||
/// When formatter is not null, use formatter first, otherwise use itemFormatter.
|
||||
/// 提示框单个serie或数据项内容的字符串模版格式器。支持用 \n 换行。当formatter不为空时,优先使用formatter,否则使用itemFormatter。
|
||||
/// </summary>
|
||||
public string itemFormatter { get { return m_ItemFormatter; } set { m_ItemFormatter = value; } }
|
||||
/// <summary>
|
||||
/// the marker of serie.
|
||||
/// serie的符号标志。
|
||||
/// </summary>
|
||||
public string marker { get { return m_Marker; } set { m_Marker = value; } }
|
||||
/// <summary>
|
||||
/// Fixed width. Higher priority than minWidth.
|
||||
/// 固定宽度。比 minWidth 优先。
|
||||
/// </summary>
|
||||
public float fixedWidth { get { return m_FixedWidth; } set { m_FixedWidth = value; } }
|
||||
/// <summary>
|
||||
/// Fixed height. Higher priority than minHeight.
|
||||
/// 固定高度。比 minHeight 优先。
|
||||
/// </summary>
|
||||
public float fixedHeight { get { return m_FixedHeight; } set { m_FixedHeight = value; } }
|
||||
/// <summary>
|
||||
/// Minimum width. If fixedWidth has a value, get fixedWidth first.
|
||||
/// 最小宽度。如若 fixedWidth 设有值,优先取 fixedWidth。
|
||||
/// </summary>
|
||||
public float minWidth { get { return m_MinWidth; } set { m_MinWidth = value; } }
|
||||
/// <summary>
|
||||
/// Minimum height. If fixedHeight has a value, take priority over fixedHeight.
|
||||
/// 最小高度。如若 fixedHeight 设有值,优先取 fixedHeight。
|
||||
/// </summary>
|
||||
public float minHeight { get { return m_MinHeight; } set { m_MinHeight = value; } }
|
||||
/// <summary>
|
||||
/// Standard numeric format string. Used to format numeric values to display as strings.
|
||||
/// Using 'Axx' form: 'A' is the single character of the format specifier, supporting 'C' currency,
|
||||
/// 'D' decimal, 'E' exponent, 'F' number of vertices, 'G' regular, 'N' digits, 'P' percentage,
|
||||
/// 'R' round tripping, 'X' hex etc. 'XX' is the precision specification, from '0' - '99'.
|
||||
/// 标准数字格式字符串。用于将数值格式化显示为字符串。
|
||||
/// 使用Axx的形式:A是格式说明符的单字符,支持C货币、D十进制、E指数、F定点数、G常规、N数字、P百分比、R往返、X十六进制的。xx是精度说明,从0-99。
|
||||
/// 参考:https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/standard-numeric-format-strings
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string numericFormatter
|
||||
{
|
||||
get { return m_NumericFormatter; }
|
||||
set { if (PropertyUtil.SetClass(ref m_NumericFormatter, value)) SetComponentDirty(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// the text padding of left and right. defaut:5.
|
||||
/// 左右边距。
|
||||
/// </summary>
|
||||
public int paddingLeftRight { get { return m_PaddingLeftRight; } set { m_PaddingLeftRight = value; } }
|
||||
/// <summary>
|
||||
/// the text padding of top and bottom. defaut:5.
|
||||
/// 上下边距。
|
||||
/// </summary>
|
||||
public int paddingTopBottom { get { return m_PaddingTopBottom; } set { m_PaddingTopBottom = value; } }
|
||||
/// <summary>
|
||||
/// Whether to show ignored data on tooltip.
|
||||
/// 是否显示忽略数据在tooltip上。
|
||||
/// </summary>
|
||||
public bool ignoreDataShow { get { return m_IgnoreDataShow; } set { m_IgnoreDataShow = value; } }
|
||||
/// <summary>
|
||||
/// The default display character information for ignored data.
|
||||
/// 被忽略数据的默认显示字符信息。
|
||||
/// </summary>
|
||||
public string ignoreDataDefaultContent { get { return m_IgnoreDataDefaultContent; } set { m_IgnoreDataDefaultContent = value; } }
|
||||
/// <summary>
|
||||
/// The background image of tooltip.
|
||||
/// 提示框的背景图片。
|
||||
/// </summary>
|
||||
public Sprite backgroundImage { get { return m_BackgroundImage; } set { m_BackgroundImage = value; SetComponentDirty(); } }
|
||||
/// <summary>
|
||||
/// The background color of tooltip.
|
||||
/// 提示框的背景颜色。
|
||||
/// </summary>
|
||||
public Color backgroundColor { get { return m_BackgroundColor; } set { m_BackgroundColor = value; SetComponentDirty(); } }
|
||||
/// <summary>
|
||||
/// Whether to trigger after always display.
|
||||
/// 是否触发后一直显示。
|
||||
/// </summary>
|
||||
public bool alwayShow { get { return m_AlwayShow; } set { m_AlwayShow = value; } }
|
||||
/// <summary>
|
||||
/// The position offset of tooltip relative to the mouse position.
|
||||
/// 提示框相对于鼠标位置的偏移。
|
||||
/// </summary>
|
||||
public Vector2 offset { get { return m_Offset; } set { m_Offset = value; } }
|
||||
/// <summary>
|
||||
/// the width of tooltip border.
|
||||
/// 边框线宽。
|
||||
/// </summary>
|
||||
public float borderWidth
|
||||
{
|
||||
get { return m_BorderWidth; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_BorderWidth, value)) SetVerticesDirty(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// the color of tooltip border.
|
||||
/// 边框颜色。
|
||||
/// </summary>
|
||||
public Color32 borderColor
|
||||
{
|
||||
get { return m_BorderColor; }
|
||||
set { if (PropertyUtil.SetColor(ref m_BorderColor, value)) SetVerticesDirty(); }
|
||||
}
|
||||
public bool fixedXEnable
|
||||
{
|
||||
get { return m_FixedXEnable; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_FixedXEnable, value)) SetVerticesDirty(); }
|
||||
}
|
||||
public float fixedX
|
||||
{
|
||||
get { return m_FixedX; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_FixedX, value)) SetVerticesDirty(); }
|
||||
}
|
||||
public bool fixedYEnable
|
||||
{
|
||||
get { return m_FixedYEnable; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_FixedYEnable, value)) SetVerticesDirty(); }
|
||||
}
|
||||
public float fixedY
|
||||
{
|
||||
get { return m_FixedY; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_FixedY, value)) SetVerticesDirty(); }
|
||||
}
|
||||
public float titleHeight
|
||||
{
|
||||
get { return m_TitleHeight; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_TitleHeight, value)) SetComponentDirty(); }
|
||||
}
|
||||
public float itemHeight
|
||||
{
|
||||
get { return m_ItemHeight; }
|
||||
set { if (PropertyUtil.SetStruct(ref m_ItemHeight, value)) SetComponentDirty(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// the text style of content.
|
||||
/// 提示框标签的文本样式。
|
||||
/// </summary>
|
||||
public TextStyle labelTextStyle
|
||||
{
|
||||
get { return m_LabelTextStyle; }
|
||||
set { if (value != null) { m_LabelTextStyle = value; SetComponentDirty(); } }
|
||||
}
|
||||
/// <summary>
|
||||
/// 标题的文本样式。
|
||||
/// </summary>
|
||||
public TextStyle titleTextStyle
|
||||
{
|
||||
get { return m_TitleTextStyle; }
|
||||
set { if (value != null) { m_TitleTextStyle = value; SetComponentDirty(); } }
|
||||
}
|
||||
|
||||
public List<TextStyle> columnsTextStyle
|
||||
{
|
||||
get { return m_ColumnsTextStyle; }
|
||||
set { if (value != null) { m_ColumnsTextStyle = value; SetComponentDirty(); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the line style of indicator line.
|
||||
/// 指示线样式。
|
||||
/// </summary>
|
||||
public LineStyle lineStyle
|
||||
{
|
||||
get { return m_LineStyle; }
|
||||
set { if (value != null) m_LineStyle = value; SetComponentDirty(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 组件是否需要刷新
|
||||
/// </summary>
|
||||
public override bool componentDirty
|
||||
{
|
||||
get { return m_ComponentDirty || lineStyle.componentDirty || labelTextStyle.componentDirty; }
|
||||
}
|
||||
|
||||
public override void ClearComponentDirty()
|
||||
{
|
||||
base.ClearComponentDirty();
|
||||
lineStyle.ClearComponentDirty();
|
||||
labelTextStyle.ClearComponentDirty();
|
||||
}
|
||||
/// <summary>
|
||||
/// 当前提示框所指示的Serie索引(目前只对散点图有效)。
|
||||
/// </summary>
|
||||
public Dictionary<int, List<int>> runtimeSerieIndex = new Dictionary<int, List<int>>();
|
||||
/// <summary>
|
||||
/// The data index currently indicated by Tooltip.
|
||||
/// 当前提示框所指示的数据项索引。
|
||||
/// </summary>
|
||||
public List<int> runtimeDataIndex { get { return m_RuntimeDateIndex; } internal set { m_RuntimeDateIndex = value; } }
|
||||
private List<int> m_RuntimeDateIndex = new List<int>() { -1, -1 };
|
||||
/// <summary>
|
||||
/// 当前指示的角度。
|
||||
/// </summary>
|
||||
public float runtimeAngle { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Keep Tooltiop displayed at the top.
|
||||
/// 保持Tooltiop显示在最顶上
|
||||
/// </summary>
|
||||
public void KeepTop()
|
||||
{
|
||||
gameObject.transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
public override void ClearData()
|
||||
{
|
||||
ClearValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除提示框指示数据
|
||||
/// </summary>
|
||||
internal void ClearValue()
|
||||
{
|
||||
for (int i = 0; i < runtimeDataIndex.Count; i++) runtimeDataIndex[i] = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提示框是否显示
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsActive()
|
||||
{
|
||||
return gameObject != null && gameObject.activeInHierarchy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置Tooltip组件是否显示
|
||||
/// </summary>
|
||||
/// <param name="flag"></param>
|
||||
public void SetActive(bool flag)
|
||||
{
|
||||
if (gameObject && gameObject.activeInHierarchy != flag)
|
||||
{
|
||||
gameObject.SetActive(alwayShow ? true : flag);
|
||||
}
|
||||
SetContentActive(flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新文本框位置
|
||||
/// </summary>
|
||||
/// <param name="pos"></param>
|
||||
public void UpdateContentPos(Vector2 pos)
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
if (fixedXEnable) pos.x = fixedX;
|
||||
if (fixedYEnable) pos.y = fixedY;
|
||||
view.UpdatePosition(pos);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置文本框是否显示
|
||||
/// </summary>
|
||||
/// <param name="flag"></param>
|
||||
public void SetContentActive(bool flag)
|
||||
{
|
||||
if (view == null)
|
||||
return;
|
||||
view.SetActive(alwayShow ? true : flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前提示框是否选中数据项
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsSelected()
|
||||
{
|
||||
foreach (var index in runtimeDataIndex)
|
||||
if (index >= 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定索引的数据项是否被提示框选中
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsSelected(int index)
|
||||
{
|
||||
foreach (var temp in runtimeDataIndex)
|
||||
if (temp == index) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearSerieDataIndex()
|
||||
{
|
||||
foreach (var kv in runtimeSerieIndex)
|
||||
{
|
||||
kv.Value.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddSerieDataIndex(int serieIndex, int dataIndex)
|
||||
{
|
||||
if (!runtimeSerieIndex.ContainsKey(serieIndex))
|
||||
{
|
||||
runtimeSerieIndex[serieIndex] = new List<int>();
|
||||
}
|
||||
runtimeSerieIndex[serieIndex].Add(dataIndex);
|
||||
}
|
||||
|
||||
public bool isAnySerieDataIndex()
|
||||
{
|
||||
foreach (var kv in runtimeSerieIndex)
|
||||
{
|
||||
if (kv.Value.Count > 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsTriggerItem()
|
||||
{
|
||||
return trigger == Trigger.Item;
|
||||
}
|
||||
|
||||
public bool IsTriggerAxis()
|
||||
{
|
||||
return trigger == Trigger.Axis;
|
||||
}
|
||||
|
||||
public TextStyle GetColumnTextStyle(int index)
|
||||
{
|
||||
if (m_ColumnsTextStyle.Count == 0)
|
||||
return null;
|
||||
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
else if (index > m_ColumnsTextStyle.Count - 1)
|
||||
index = m_ColumnsTextStyle.Count - 1;
|
||||
|
||||
return m_ColumnsTextStyle[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Component/Tooltip/Tooltip.cs.meta
Normal file
11
Runtime/Component/Tooltip/Tooltip.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dff3b0d6d38ee49838f054d30ab9b733
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Runtime/Component/Tooltip/TooltipContext.cs
Normal file
24
Runtime/Component/Tooltip/TooltipContext.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XUGL;
|
||||
|
||||
namespace XCharts
|
||||
{
|
||||
public class TooltipData
|
||||
{
|
||||
public string title;
|
||||
public List<SerieParams> param = new List<SerieParams>();
|
||||
}
|
||||
|
||||
public class TooltipContext
|
||||
{
|
||||
public Vector2 pointer;
|
||||
public float width;
|
||||
public float height;
|
||||
public TooltipData data = new TooltipData();
|
||||
}
|
||||
}
|
||||
11
Runtime/Component/Tooltip/TooltipContext.cs.meta
Normal file
11
Runtime/Component/Tooltip/TooltipContext.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7324ce36c9b2c475bb18abd6618b107c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
558
Runtime/Component/Tooltip/TooltipHandler.cs
Normal file
558
Runtime/Component/Tooltip/TooltipHandler.cs
Normal file
@@ -0,0 +1,558 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XUGL;
|
||||
|
||||
namespace XCharts
|
||||
{
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
internal sealed class TooltipHandler : MainComponentHandler<Tooltip>
|
||||
{
|
||||
private List<ChartLabel> m_IndicatorLabels = new List<ChartLabel>();
|
||||
private GameObject m_LabelRoot;
|
||||
private ISerieContainer m_PointerContainer;
|
||||
|
||||
public override void InitComponent()
|
||||
{
|
||||
InitTooltip(component);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
UpdateTooltip(component);
|
||||
UpdateTooltipIndicatorLabelText(component);
|
||||
if (component.view != null)
|
||||
component.view.Update();
|
||||
}
|
||||
|
||||
public override void DrawTop(VertexHelper vh)
|
||||
{
|
||||
DrawTooltipIndicator(vh, component);
|
||||
}
|
||||
|
||||
private void InitTooltip(Tooltip tooltip)
|
||||
{
|
||||
tooltip.painter = chart.m_PainterTop;
|
||||
tooltip.refreshComponent = delegate ()
|
||||
{
|
||||
var objName = ChartCached.GetComponentObjectName(tooltip);
|
||||
tooltip.gameObject = ChartHelper.AddObject(objName, chart.transform, chart.chartMinAnchor,
|
||||
chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta);
|
||||
var tooltipObject = tooltip.gameObject;
|
||||
tooltipObject.transform.localPosition = Vector3.zero;
|
||||
tooltipObject.hideFlags = chart.chartHideFlags;
|
||||
var parent = tooltipObject.transform;
|
||||
ChartHelper.HideAllObject(tooltipObject.transform);
|
||||
|
||||
tooltip.view = TooltipView.CreateView(tooltip, chart.theme, parent);
|
||||
tooltip.SetActive(false);
|
||||
|
||||
m_LabelRoot = ChartHelper.AddObject("label", tooltip.gameObject.transform, chart.chartMinAnchor,
|
||||
chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta);
|
||||
ChartHelper.HideAllObject(m_LabelRoot);
|
||||
m_IndicatorLabels.Clear();
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var labelName = "label_" + i;
|
||||
var item = ChartHelper.AddTooltipLabel(component, labelName, m_LabelRoot.transform, chart.theme, new Vector2(0.5f, 0.5f));
|
||||
item.SetActive(false);
|
||||
m_IndicatorLabels.Add(item);
|
||||
}
|
||||
};
|
||||
tooltip.refreshComponent();
|
||||
}
|
||||
|
||||
private ChartLabel GetIndicatorLabel(int index)
|
||||
{
|
||||
if (m_LabelRoot == null) return null;
|
||||
if (index < m_IndicatorLabels.Count) return m_IndicatorLabels[index];
|
||||
else
|
||||
{
|
||||
var labelName = "label_" + index;
|
||||
var item = ChartHelper.AddTooltipLabel(component, labelName, m_LabelRoot.transform, chart.theme, new Vector2(0.5f, 0.5f));
|
||||
m_IndicatorLabels.Add(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTooltip(Tooltip tooltip)
|
||||
{
|
||||
if (tooltip.trigger == Tooltip.Trigger.None) return;
|
||||
if (!chart.isPointerInChart || !tooltip.show)
|
||||
{
|
||||
if (tooltip.IsActive())
|
||||
{
|
||||
tooltip.ClearValue();
|
||||
tooltip.SetActive(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var showTooltip = false;
|
||||
for (int i = chart.series.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var serie = chart.series[i];
|
||||
if (!(serie is INeedSerieContainer))
|
||||
{
|
||||
if (SetSerieTooltip(tooltip, serie))
|
||||
{
|
||||
showTooltip = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
var containerSeries = ListPool<Serie>.Get();
|
||||
m_PointerContainer = GetPointerContainerAndSeries(tooltip, containerSeries);
|
||||
if (containerSeries.Count > 0)
|
||||
{
|
||||
if (SetSerieTooltip(tooltip, containerSeries))
|
||||
showTooltip = true;
|
||||
}
|
||||
ListPool<Serie>.Release(containerSeries);
|
||||
if (!showTooltip)
|
||||
{
|
||||
if (tooltip.type == Tooltip.Type.Corss && (m_PointerContainer == null || m_PointerContainer.IsPointerEnter()))
|
||||
{
|
||||
tooltip.SetActive(true);
|
||||
tooltip.SetContentActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltip.SetActive(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chart.RefreshTopPainter();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTooltipIndicatorLabelText(Tooltip tooltip)
|
||||
{
|
||||
if (!tooltip.show) return;
|
||||
if (tooltip.type == Tooltip.Type.None) return;
|
||||
if (m_PointerContainer != null)
|
||||
{
|
||||
if (tooltip.type == Tooltip.Type.Corss)
|
||||
{
|
||||
var labelCount = 0;
|
||||
if (m_PointerContainer is GridCoord)
|
||||
{
|
||||
var grid = m_PointerContainer as GridCoord;
|
||||
ChartHelper.HideAllObject(m_LabelRoot);
|
||||
foreach (var component in chart.components)
|
||||
{
|
||||
if (component is XAxis || component is YAxis)
|
||||
{
|
||||
var axis = component as Axis;
|
||||
if (axis.gridIndex == grid.index)
|
||||
{
|
||||
var label = GetIndicatorLabel(labelCount++);
|
||||
if (label == null) continue;
|
||||
label.SetActive(true);
|
||||
label.SetPosition(axis.context.pointerLabelPosition);
|
||||
if (axis.IsCategory())
|
||||
label.SetText(axis.GetData((int)axis.context.pointerValue));
|
||||
else
|
||||
label.SetText(axis.context.pointerValue.ToString("f2"));
|
||||
var textColor = axis.axisLabel.textStyle.GetColor(chart.theme.axis.textColor);
|
||||
label.labelBackground.color = textColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ISerieContainer GetPointerContainerAndSeries(Tooltip tooltip, List<Serie> list)
|
||||
{
|
||||
list.Clear();
|
||||
for (int i = chart.components.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var component = chart.components[i];
|
||||
if (component is ISerieContainer)
|
||||
{
|
||||
var container = component as ISerieContainer;
|
||||
if (container.IsPointerEnter())
|
||||
{
|
||||
foreach (var serie in chart.series)
|
||||
{
|
||||
if (serie is INeedSerieContainer
|
||||
&& (serie as INeedSerieContainer).containterInstanceId == component.instanceId)
|
||||
{
|
||||
if (tooltip.IsTriggerAxis())
|
||||
{
|
||||
if (container is GridCoord)
|
||||
{
|
||||
var xAxis = chart.GetChartComponent<XAxis>(serie.xAxisIndex);
|
||||
var yAxis = chart.GetChartComponent<YAxis>(serie.yAxisIndex);
|
||||
serie.context.pointerEnter = true;
|
||||
UpdateAxisPointerDataIndex(serie, xAxis, yAxis, container as GridCoord);
|
||||
}
|
||||
list.Add(serie);
|
||||
}
|
||||
else if (serie.context.pointerEnter)
|
||||
{
|
||||
list.Add(serie);
|
||||
return component as ISerieContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return component as ISerieContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void UpdateAxisPointerDataIndex(Serie serie, XAxis xAxis, YAxis yAxis, GridCoord grid)
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Clear();
|
||||
if (yAxis.IsCategory())
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Add((int)yAxis.context.pointerValue);
|
||||
yAxis.context.axisTooltipValue = yAxis.context.pointerValue;
|
||||
}
|
||||
else if (yAxis.IsTime())
|
||||
{
|
||||
GetSerieDataIndexByValue(serie, yAxis, grid);
|
||||
}
|
||||
else if (xAxis.IsCategory())
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Add((int)xAxis.context.pointerValue);
|
||||
xAxis.context.axisTooltipValue = xAxis.context.pointerValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSerieDataIndexByValue(serie, xAxis, grid);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetSerieDataIndexByValue(Serie serie, Axis axis, GridCoord grid, int dimension = 0)
|
||||
{
|
||||
var currValue = 0d;
|
||||
var lastValue = 0d;
|
||||
var nextValue = 0d;
|
||||
var axisValue = axis.context.pointerValue;
|
||||
var isTimeAxis = axis.IsTime();
|
||||
var dataCount = serie.dataCount;
|
||||
var themeSymbolSize = chart.theme.serie.scatterSymbolSize;
|
||||
serie.context.pointerAxisDataIndexs.Clear();
|
||||
var axisValueDistance = axis.GetDistance(axisValue, grid.context.width);
|
||||
for (int i = 0; i < dataCount; i++)
|
||||
{
|
||||
var serieData = serie.data[i];
|
||||
currValue = serieData.GetData(dimension);
|
||||
if (isTimeAxis)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
nextValue = serie.GetSerieData(i + 1).GetData(dimension);
|
||||
if (axisValue <= currValue + (nextValue - currValue) / 2)
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (i == dataCount - 1)
|
||||
{
|
||||
if (axisValue > lastValue + (currValue - lastValue) / 2)
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nextValue = serie.GetSerieData(i + 1).GetData(dimension);
|
||||
if (axisValue > (currValue - (currValue - lastValue) / 2) && axisValue <= currValue + (nextValue - currValue) / 2)
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lastValue = currValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var symbol = SerieHelper.GetSerieSymbol(serie, serieData);
|
||||
var symbolSize = symbol.GetSize(serieData.data, themeSymbolSize);
|
||||
var dist = axis.GetDistance(currValue, grid.context.width) - axisValueDistance;
|
||||
if (System.Math.Abs(dist) <= symbolSize)
|
||||
{
|
||||
serie.context.pointerAxisDataIndexs.Add(i);
|
||||
serieData.context.highlight = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
serieData.context.highlight = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (serie.context.pointerAxisDataIndexs.Count > 0)
|
||||
{
|
||||
var index = serie.context.pointerAxisDataIndexs[0];
|
||||
axis.context.axisTooltipValue = serie.GetSerieData(index).GetData(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
axis.context.axisTooltipValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetSerieTooltip(Tooltip tooltip, Serie serie)
|
||||
{
|
||||
if (tooltip.trigger == Tooltip.Trigger.None) return false;
|
||||
if (serie.context.pointerItemDataIndex < 0) return false;
|
||||
|
||||
tooltip.context.data.param.Clear();
|
||||
tooltip.context.data.title = serie.serieName;
|
||||
tooltip.context.pointer = chart.pointerPos;
|
||||
|
||||
serie.handler.UpdateTooltipSerieParams(serie.context.pointerItemDataIndex, false, null,
|
||||
tooltip.marker, tooltip.itemFormatter, tooltip.numericFormatter,
|
||||
ref tooltip.context.data.param,
|
||||
ref tooltip.context.data.title);
|
||||
TooltipHelper.ResetTooltipParamsByItemFormatter(tooltip, chart);
|
||||
|
||||
tooltip.SetActive(true);
|
||||
tooltip.view.Refresh();
|
||||
TooltipHelper.LimitInRect(tooltip, chart.chartRect);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SetSerieTooltip(Tooltip tooltip, List<Serie> series)
|
||||
{
|
||||
if (tooltip.trigger == Tooltip.Trigger.None)
|
||||
return false;
|
||||
|
||||
if (series.Count <= 0)
|
||||
return false;
|
||||
|
||||
string category = null;
|
||||
var showCategory = false;
|
||||
var isTriggerByAxis = false;
|
||||
var dataIndex = -1;
|
||||
tooltip.context.data.param.Clear();
|
||||
tooltip.context.pointer = chart.pointerPos;
|
||||
if (m_PointerContainer is GridCoord)
|
||||
{
|
||||
if (tooltip.trigger == Tooltip.Trigger.Axis)
|
||||
{
|
||||
isTriggerByAxis = true;
|
||||
GetAxisCategory(m_PointerContainer.index, ref dataIndex, ref category);
|
||||
if (series.Count <= 1)
|
||||
{
|
||||
showCategory = true;
|
||||
tooltip.context.data.title = series[0].serieName;
|
||||
}
|
||||
else
|
||||
tooltip.context.data.title = category;
|
||||
}
|
||||
|
||||
}
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
var serie = series[i];
|
||||
serie.context.isTriggerByAxis = isTriggerByAxis;
|
||||
if (isTriggerByAxis)
|
||||
serie.context.pointerItemDataIndex = dataIndex;
|
||||
serie.handler.UpdateTooltipSerieParams(dataIndex, showCategory, category,
|
||||
tooltip.marker, tooltip.itemFormatter, tooltip.numericFormatter,
|
||||
ref tooltip.context.data.param,
|
||||
ref tooltip.context.data.title);
|
||||
}
|
||||
TooltipHelper.ResetTooltipParamsByItemFormatter(tooltip, chart);
|
||||
if (tooltip.context.data.param.Count > 0)
|
||||
{
|
||||
tooltip.SetActive(true);
|
||||
tooltip.view.Refresh();
|
||||
TooltipHelper.LimitInRect(tooltip, chart.chartRect);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool GetAxisCategory(int gridIndex, ref int dataIndex, ref string category)
|
||||
{
|
||||
foreach (var component in chart.components)
|
||||
{
|
||||
if (component is Axis)
|
||||
{
|
||||
var axis = component as Axis;
|
||||
if (axis.gridIndex == gridIndex && axis.IsCategory())
|
||||
{
|
||||
dataIndex = (int)axis.context.pointerValue;
|
||||
category = axis.GetData(dataIndex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawTooltipIndicator(VertexHelper vh, Tooltip tooltip)
|
||||
{
|
||||
if (!tooltip.show) return;
|
||||
if (tooltip.type == Tooltip.Type.None) return;
|
||||
if (m_PointerContainer is GridCoord)
|
||||
{
|
||||
var grid = m_PointerContainer as GridCoord;
|
||||
if (IsYCategoryOfGrid(grid.index))
|
||||
DrawYAxisIndicator(vh, tooltip, grid);
|
||||
else
|
||||
DrawXAxisIndicator(vh, tooltip, grid);
|
||||
}
|
||||
else if (m_PointerContainer is PolarCoord)
|
||||
{
|
||||
DrawPolarIndicator(vh, tooltip, m_PointerContainer as PolarCoord);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsYCategoryOfGrid(int gridIndex)
|
||||
{
|
||||
var yAxes = chart.GetChartComponents<YAxis>();
|
||||
foreach (var component in yAxes)
|
||||
{
|
||||
var yAxis = component as YAxis;
|
||||
if (yAxis.gridIndex == gridIndex && yAxis.IsCategory()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawXAxisIndicator(VertexHelper vh, Tooltip tooltip, GridCoord grid)
|
||||
{
|
||||
var xAxes = chart.GetChartComponents<XAxis>();
|
||||
var lineType = tooltip.lineStyle.GetType(chart.theme.tooltip.lineType);
|
||||
var lineWidth = tooltip.lineStyle.GetWidth(chart.theme.tooltip.lineWidth);
|
||||
foreach (var component in xAxes)
|
||||
{
|
||||
var xAxis = component as XAxis;
|
||||
if (xAxis.gridIndex == grid.index)
|
||||
{
|
||||
var dataZoom = chart.GetDataZoomOfAxis(xAxis);
|
||||
int dataCount = chart.series.Count > 0 ? chart.series[0].GetDataList(dataZoom).Count : 0;
|
||||
float splitWidth = AxisHelper.GetDataWidth(xAxis, grid.context.width, dataCount, dataZoom);
|
||||
switch (tooltip.type)
|
||||
{
|
||||
case Tooltip.Type.Corss:
|
||||
case Tooltip.Type.Line:
|
||||
float pX = grid.context.x;
|
||||
pX += xAxis.IsCategory()
|
||||
? (float)(xAxis.context.pointerValue * splitWidth + (xAxis.boundaryGap ? splitWidth / 2 : 0))
|
||||
: xAxis.GetDistance(xAxis.context.axisTooltipValue, grid.context.width);
|
||||
//if (xAxis.IsValue()) pX = chart.pointerPos.x;
|
||||
Vector2 sp = new Vector2(pX, grid.context.y);
|
||||
Vector2 ep = new Vector2(pX, grid.context.y + grid.context.height);
|
||||
var lineColor = TooltipHelper.GetLineColor(tooltip, chart.theme);
|
||||
ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor);
|
||||
if (tooltip.type == Tooltip.Type.Corss)
|
||||
{
|
||||
sp = new Vector2(grid.context.x, chart.pointerPos.y);
|
||||
ep = new Vector2(grid.context.x + grid.context.width, chart.pointerPos.y);
|
||||
ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor);
|
||||
}
|
||||
break;
|
||||
case Tooltip.Type.Shadow:
|
||||
if (xAxis.IsCategory())
|
||||
{
|
||||
float tooltipSplitWid = splitWidth < 1 ? 1 : splitWidth;
|
||||
pX = (float)(grid.context.x + splitWidth * xAxis.context.pointerValue -
|
||||
(xAxis.boundaryGap ? 0 : splitWidth / 2));
|
||||
float pY = grid.context.y + grid.context.height;
|
||||
Vector3 p1 = new Vector3(pX, grid.context.y);
|
||||
Vector3 p2 = new Vector3(pX, pY);
|
||||
Vector3 p3 = new Vector3(pX + tooltipSplitWid, pY);
|
||||
Vector3 p4 = new Vector3(pX + tooltipSplitWid, grid.context.y);
|
||||
UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, chart.theme.tooltip.areaColor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void DrawYAxisIndicator(VertexHelper vh, Tooltip tooltip, GridCoord grid)
|
||||
{
|
||||
var yAxes = chart.GetChartComponents<YAxis>();
|
||||
var lineType = tooltip.lineStyle.GetType(chart.theme.tooltip.lineType);
|
||||
var lineWidth = tooltip.lineStyle.GetWidth(chart.theme.tooltip.lineWidth);
|
||||
|
||||
foreach (var component in yAxes)
|
||||
{
|
||||
var yAxis = component as YAxis;
|
||||
if (yAxis.gridIndex == grid.index)
|
||||
{
|
||||
var dataZoom = chart.GetDataZoomOfAxis(yAxis);
|
||||
int dataCount = chart.series.Count > 0 ? chart.series[0].GetDataList(dataZoom).Count : 0;
|
||||
float splitWidth = AxisHelper.GetDataWidth(yAxis, grid.context.height, dataCount, dataZoom);
|
||||
switch (tooltip.type)
|
||||
{
|
||||
case Tooltip.Type.Corss:
|
||||
case Tooltip.Type.Line:
|
||||
float pY = (float)(grid.context.y + yAxis.context.pointerValue * splitWidth
|
||||
+ (yAxis.boundaryGap ? splitWidth / 2 : 0));
|
||||
Vector2 sp = new Vector2(grid.context.x, pY);
|
||||
Vector2 ep = new Vector2(grid.context.x + grid.context.width, pY);
|
||||
var lineColor = TooltipHelper.GetLineColor(tooltip, chart.theme);
|
||||
ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor);
|
||||
if (tooltip.type == Tooltip.Type.Corss)
|
||||
{
|
||||
sp = new Vector2(chart.pointerPos.x, grid.context.y);
|
||||
ep = new Vector2(chart.pointerPos.x, grid.context.y + grid.context.height);
|
||||
ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor);
|
||||
}
|
||||
break;
|
||||
case Tooltip.Type.Shadow:
|
||||
if (yAxis.IsCategory())
|
||||
{
|
||||
float tooltipSplitWid = splitWidth < 1 ? 1 : splitWidth;
|
||||
float pX = grid.context.x + grid.context.width;
|
||||
pY = (float)(grid.context.y + splitWidth * yAxis.context.pointerValue -
|
||||
(yAxis.boundaryGap ? 0 : splitWidth / 2));
|
||||
Vector3 p1 = new Vector3(grid.context.x, pY);
|
||||
Vector3 p2 = new Vector3(grid.context.x, pY + tooltipSplitWid);
|
||||
Vector3 p3 = new Vector3(pX, pY + tooltipSplitWid);
|
||||
Vector3 p4 = new Vector3(pX, pY);
|
||||
UGL.DrawQuadrilateral(vh, p1, p2, p3, p4, chart.theme.tooltip.areaColor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPolarIndicator(VertexHelper vh, Tooltip tooltip, PolarCoord m_Polar)
|
||||
{
|
||||
if (tooltip.runtimeAngle < 0) return;
|
||||
var theme = chart.theme;
|
||||
var m_AngleAxis = ComponentHelper.GetAngleAxis(chart.components, m_Polar.index);
|
||||
var lineColor = TooltipHelper.GetLineColor(tooltip, theme);
|
||||
var lineType = tooltip.lineStyle.GetType(theme.tooltip.lineType);
|
||||
var lineWidth = tooltip.lineStyle.GetWidth(theme.tooltip.lineWidth);
|
||||
var cenPos = m_Polar.context.center;
|
||||
var radius = m_Polar.context.radius;
|
||||
var sp = m_Polar.context.center;
|
||||
var tooltipAngle = tooltip.runtimeAngle + m_AngleAxis.startAngle;
|
||||
var ep = ChartHelper.GetPos(sp, radius, tooltipAngle, true);
|
||||
|
||||
switch (tooltip.type)
|
||||
{
|
||||
case Tooltip.Type.Corss:
|
||||
ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor);
|
||||
var dist = Vector2.Distance(chart.pointerPos, cenPos);
|
||||
if (dist > radius) dist = radius;
|
||||
var outsideRaidus = dist + tooltip.lineStyle.GetWidth(theme.tooltip.lineWidth) * 2;
|
||||
UGL.DrawDoughnut(vh, cenPos, dist, outsideRaidus, lineColor, Color.clear);
|
||||
break;
|
||||
case Tooltip.Type.Line:
|
||||
ChartDrawer.DrawLineStyle(vh, lineType, lineWidth, sp, ep, lineColor);
|
||||
break;
|
||||
case Tooltip.Type.Shadow:
|
||||
UGL.DrawSector(vh, cenPos, radius, lineColor, tooltipAngle - 2, tooltipAngle + 2, chart.settings.cicleSmoothness);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Component/Tooltip/TooltipHandler.cs.meta
Normal file
11
Runtime/Component/Tooltip/TooltipHandler.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d25a5b5e3d6f45b8a06b94e33792087
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
89
Runtime/Component/Tooltip/TooltipHelper.cs
Normal file
89
Runtime/Component/Tooltip/TooltipHelper.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XCharts
|
||||
{
|
||||
public static class TooltipHelper
|
||||
{
|
||||
internal static void ResetTooltipParamsByItemFormatter(Tooltip tooltip, BaseChart chart)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(tooltip.titleFormatter))
|
||||
{
|
||||
if (tooltip.titleFormatter.Equals("{i}", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
tooltip.context.data.title = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltip.context.data.title = tooltip.titleFormatter;
|
||||
FormatterHelper.ReplaceContent(ref tooltip.context.data.title, 0,
|
||||
tooltip.numericFormatter, null, chart);
|
||||
}
|
||||
}
|
||||
foreach (var param in tooltip.context.data.param)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(param.itemFormatter))
|
||||
{
|
||||
var content = param.itemFormatter;
|
||||
FormatterHelper.ReplaceSerieLabelContent(ref content,
|
||||
param.numericFormatter,
|
||||
param.value,
|
||||
param.total,
|
||||
param.serieName,
|
||||
param.category,
|
||||
param.serieData.name,
|
||||
param.color);
|
||||
|
||||
param.columns.Clear();
|
||||
|
||||
foreach (var item in content.Split('|'))
|
||||
{
|
||||
param.columns.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void LimitInRect(Tooltip tooltip, Rect chartRect)
|
||||
{
|
||||
if (tooltip.view == null)
|
||||
return;
|
||||
|
||||
var pos = tooltip.view.GetTargetPos();
|
||||
if (pos.x + tooltip.context.width > chartRect.x + chartRect.width)
|
||||
{
|
||||
//pos.x = chartRect.x + chartRect.width - tooltip.context.width;
|
||||
pos.x = pos.x - tooltip.context.width - tooltip.offset.x;
|
||||
}
|
||||
if (pos.y - tooltip.context.height < chartRect.y)
|
||||
{
|
||||
pos.y = chartRect.y + tooltip.context.height;
|
||||
}
|
||||
tooltip.UpdateContentPos(pos);
|
||||
}
|
||||
|
||||
public static string GetItemNumericFormatter(Tooltip tooltip, Serie serie, SerieData serieData)
|
||||
{
|
||||
var itemStyle = SerieHelper.GetItemStyle(serie, serieData);
|
||||
if (!string.IsNullOrEmpty(itemStyle.numericFormatter)) return itemStyle.numericFormatter;
|
||||
else return tooltip.numericFormatter;
|
||||
}
|
||||
|
||||
public static Color32 GetLineColor(Tooltip tooltip, ThemeStyle theme)
|
||||
{
|
||||
var lineStyle = tooltip.lineStyle;
|
||||
if (!ChartHelper.IsClearColor(lineStyle.color))
|
||||
{
|
||||
return lineStyle.GetColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
var color = theme.tooltip.lineColor;
|
||||
ChartHelper.SetColorOpacity(ref color, lineStyle.opacity);
|
||||
return color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Component/Tooltip/TooltipHelper.cs.meta
Normal file
11
Runtime/Component/Tooltip/TooltipHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 184e190de6da6486b8b4d333a302477a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
270
Runtime/Component/Tooltip/TooltipView.cs
Normal file
270
Runtime/Component/Tooltip/TooltipView.cs
Normal file
@@ -0,0 +1,270 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XUGL;
|
||||
|
||||
namespace XCharts
|
||||
{
|
||||
public class TooltipViewItem
|
||||
{
|
||||
public GameObject gameObject;
|
||||
public List<ChartText> columns = new List<ChartText>();
|
||||
}
|
||||
public class TooltipView
|
||||
{
|
||||
private static Vector2 anchorMax = new Vector2(0, 1);
|
||||
private static Vector2 anchorMin = new Vector2(0, 1);
|
||||
private static Vector2 pivot = new Vector2(0, 1);
|
||||
private static Vector2 v2_0_05 = new Vector2(0, 0.5f);
|
||||
|
||||
public Tooltip tooltip;
|
||||
public ComponentTheme theme;
|
||||
public GameObject gameObject;
|
||||
public Transform transform;
|
||||
public Image background;
|
||||
public Outline border;
|
||||
public VerticalLayoutGroup layout;
|
||||
public ChartText title;
|
||||
private List<TooltipViewItem> m_Items = new List<TooltipViewItem>();
|
||||
private List<float> m_ColumnMaxWidth = new List<float>();
|
||||
private bool m_Active = false;
|
||||
private Vector3 m_TargetPos;
|
||||
private Vector3 m_CurrentVelocity;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!m_Active)
|
||||
return;
|
||||
transform.localPosition = Vector3.SmoothDamp(transform.localPosition, m_TargetPos, ref m_CurrentVelocity, 0.08f);
|
||||
}
|
||||
|
||||
public Vector3 GetCurrentPos()
|
||||
{
|
||||
return transform.localPosition;
|
||||
}
|
||||
|
||||
public Vector3 GetTargetPos()
|
||||
{
|
||||
return m_TargetPos;
|
||||
}
|
||||
|
||||
public void UpdatePosition(Vector3 pos)
|
||||
{
|
||||
m_TargetPos = pos;
|
||||
}
|
||||
|
||||
public void SetActive(bool flag)
|
||||
{
|
||||
m_Active = flag;
|
||||
ChartHelper.SetActive(gameObject, flag);
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
if (tooltip == null) return;
|
||||
var data = tooltip.context.data;
|
||||
|
||||
var titleActive = !string.IsNullOrEmpty(data.title);
|
||||
if (titleActive != title.gameObject.activeSelf)
|
||||
title.gameObject.SetActive(titleActive);
|
||||
title.SetText(data.title);
|
||||
|
||||
m_ColumnMaxWidth.Clear();
|
||||
for (int i = 0; i < data.param.Count; i++)
|
||||
{
|
||||
var item = GetItem(i);
|
||||
var param = data.param[i];
|
||||
item.gameObject.SetActive(true);
|
||||
|
||||
for (int j = 0; j < param.columns.Count; j++)
|
||||
{
|
||||
var column = GetItemColumn(item, j);
|
||||
column.SetActive(true);
|
||||
column.SetText(param.columns[j]);
|
||||
|
||||
if (j == 0)
|
||||
column.SetColor(param.color);
|
||||
|
||||
if (j >= m_ColumnMaxWidth.Count)
|
||||
m_ColumnMaxWidth.Add(0);
|
||||
|
||||
var columnWidth = column.GetPreferredWidth();
|
||||
if (m_ColumnMaxWidth[j] < columnWidth)
|
||||
m_ColumnMaxWidth[j] = columnWidth;
|
||||
}
|
||||
for (int j = param.columns.Count; j < item.columns.Count; j++)
|
||||
{
|
||||
item.columns[j].SetActive(false);
|
||||
}
|
||||
}
|
||||
for (int i = data.param.Count; i < m_Items.Count; i++)
|
||||
{
|
||||
m_Items[i].gameObject.SetActive(false);
|
||||
}
|
||||
ResetSize();
|
||||
// border.effectColor = data.param.Count == 1
|
||||
// ? data.param[0].color
|
||||
// : tooltip.borderColor;
|
||||
UpdatePosition(tooltip.context.pointer + tooltip.offset);
|
||||
tooltip.gameObject.transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ResetSize()
|
||||
{
|
||||
var maxHig = 0f;
|
||||
var maxWid = 0f;
|
||||
if (tooltip.fixedWidth > 0)
|
||||
{
|
||||
maxWid = tooltip.fixedWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxWid = TotalMaxWidth();
|
||||
var titleWid = title.GetPreferredWidth();
|
||||
if (maxWid < titleWid)
|
||||
maxWid = titleWid;
|
||||
}
|
||||
|
||||
if (tooltip.fixedHeight > 0)
|
||||
{
|
||||
maxHig = tooltip.fixedHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(title.text.text))
|
||||
maxHig += tooltip.titleHeight;
|
||||
maxHig += tooltip.itemHeight * tooltip.context.data.param.Count;
|
||||
maxHig += tooltip.paddingTopBottom * 2;
|
||||
}
|
||||
|
||||
if (tooltip.minWidth > 0 && maxWid < tooltip.minWidth)
|
||||
maxWid = tooltip.minWidth;
|
||||
|
||||
if (tooltip.minHeight > 0 && maxHig < tooltip.minHeight)
|
||||
maxHig = tooltip.minHeight;
|
||||
|
||||
for (int i = 0; i < m_Items.Count; i++)
|
||||
{
|
||||
var item = m_Items[i];
|
||||
item.gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2(maxWid, tooltip.itemHeight);
|
||||
var xPos = 0f;
|
||||
for (int j = 0; j < m_ColumnMaxWidth.Count; j++)
|
||||
{
|
||||
var deltaX = j == m_ColumnMaxWidth.Count - 1 ? maxWid - xPos : m_ColumnMaxWidth[j];
|
||||
item.columns[j].SetSizeDelta(new Vector2(deltaX, tooltip.itemHeight));
|
||||
item.columns[j].SetRectPosition(new Vector3(xPos, 0));
|
||||
xPos += m_ColumnMaxWidth[j];
|
||||
}
|
||||
}
|
||||
tooltip.context.width = maxWid + tooltip.paddingLeftRight * 2;
|
||||
tooltip.context.height = maxHig;
|
||||
background.GetComponent<RectTransform>().sizeDelta = new Vector2(tooltip.context.width, tooltip.context.height);
|
||||
}
|
||||
|
||||
private float TotalMaxWidth()
|
||||
{
|
||||
var total = 0f;
|
||||
foreach (var max in m_ColumnMaxWidth)
|
||||
total += max;
|
||||
return total;
|
||||
}
|
||||
|
||||
private TooltipViewItem GetItem(int i)
|
||||
{
|
||||
if (i < 0) i = 0;
|
||||
if (i < m_Items.Count)
|
||||
{
|
||||
return m_Items[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = CreateViewItem(i, gameObject.transform, tooltip, theme);
|
||||
m_Items.Add(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private ChartText GetItemColumn(TooltipViewItem item, int i)
|
||||
{
|
||||
if (i < 0) i = 0;
|
||||
if (i < item.columns.Count)
|
||||
{
|
||||
return item.columns[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
var column = CreateViewItemColumn(i, item.gameObject.transform, tooltip, theme);
|
||||
item.columns.Add(column);
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
public static TooltipView CreateView(Tooltip tooltip, ThemeStyle theme, Transform parent)
|
||||
{
|
||||
var view = new TooltipView();
|
||||
view.tooltip = tooltip;
|
||||
view.theme = theme.tooltip;
|
||||
|
||||
view.gameObject = ChartHelper.AddObject("view", parent, anchorMin, anchorMax, pivot, Vector3.zero);
|
||||
view.gameObject.transform.localPosition = Vector3.zero;
|
||||
view.transform = view.gameObject.transform;
|
||||
|
||||
view.background = ChartHelper.GetOrAddComponent<Image>(view.gameObject);
|
||||
view.background.color = ChartHelper.IsClearColor(tooltip.backgroundColor)
|
||||
? Color.white : tooltip.backgroundColor;
|
||||
|
||||
view.border = ChartHelper.GetOrAddComponent<Outline>(view.gameObject);
|
||||
view.border.enabled = tooltip.borderWidth > 0;
|
||||
view.border.effectColor = tooltip.borderColor;
|
||||
view.border.effectDistance = new Vector2(tooltip.borderWidth, -tooltip.borderWidth);
|
||||
|
||||
view.layout = ChartHelper.GetOrAddComponent<VerticalLayoutGroup>(view.gameObject);
|
||||
view.layout.childControlHeight = false;
|
||||
view.layout.childControlWidth = false;
|
||||
view.layout.childForceExpandHeight = false;
|
||||
view.layout.childForceExpandWidth = false;
|
||||
view.layout.padding = new RectOffset(tooltip.paddingLeftRight,
|
||||
tooltip.paddingLeftRight,
|
||||
tooltip.paddingTopBottom,
|
||||
tooltip.paddingTopBottom);
|
||||
|
||||
view.title = ChartHelper.AddTextObject("title", view.gameObject.transform, anchorMin, anchorMax, v2_0_05,
|
||||
new Vector2(10, tooltip.titleHeight), tooltip.titleTextStyle, theme.tooltip);
|
||||
view.title.SetText("");
|
||||
view.title.SetLocalPosition(new Vector2(3, -3));
|
||||
|
||||
var item = CreateViewItem(0, view.gameObject.transform, tooltip, theme.tooltip);
|
||||
view.m_Items.Add(item);
|
||||
|
||||
view.Refresh();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private static TooltipViewItem CreateViewItem(int i, Transform parent, Tooltip tooltip, ComponentTheme theme)
|
||||
{
|
||||
GameObject item1 = ChartHelper.AddObject("item" + i, parent, anchorMin, anchorMax, v2_0_05, Vector3.zero);
|
||||
|
||||
var item = new TooltipViewItem();
|
||||
item.gameObject = item1;
|
||||
item.columns.Add(CreateViewItemColumn(0, item1.transform, tooltip, theme));
|
||||
item.columns.Add(CreateViewItemColumn(1, item1.transform, tooltip, theme));
|
||||
item.columns.Add(CreateViewItemColumn(2, item1.transform, tooltip, theme));
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ChartText CreateViewItemColumn(int i, Transform parent, Tooltip tooltip, ComponentTheme theme)
|
||||
{
|
||||
var value = ChartHelper.AddTextObject("column" + i, parent,
|
||||
v2_0_05, v2_0_05, v2_0_05, new Vector2(100, tooltip.itemHeight),
|
||||
tooltip.GetColumnTextStyle(i), theme);
|
||||
value.SetRectPosition(new Vector3(0, 0));
|
||||
value.SetText("");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Component/Tooltip/TooltipView.cs.meta
Normal file
11
Runtime/Component/Tooltip/TooltipView.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20bbaf6c402824f5d8abcaf1cee57865
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user