using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace XCharts.Runtime { public abstract class SerieHandler { public BaseChart chart { get; internal set; } public SerieHandlerAttribute attribute { get; internal set; } public virtual int defaultDimension { get; internal set; } public virtual void InitComponent() { } public virtual void RemoveComponent() { } public virtual void CheckComponent(StringBuilder sb) { } public virtual void Update() { } public virtual void DrawBase(VertexHelper vh) { } public virtual void DrawSerie(VertexHelper vh) { } public virtual void DrawUpper(VertexHelper vh) { } public virtual void DrawTop(VertexHelper vh) { } public virtual void OnPointerClick(PointerEventData eventData) { } public virtual void OnPointerDown(PointerEventData eventData) { } public virtual void OnPointerUp(PointerEventData eventData) { } public virtual void OnPointerEnter(PointerEventData eventData) { } public virtual void OnPointerExit(PointerEventData eventData) { } public virtual void OnDrag(PointerEventData eventData) { } public virtual void OnBeginDrag(PointerEventData eventData) { } public virtual void OnEndDrag(PointerEventData eventData) { } public virtual void OnScroll(PointerEventData eventData) { } public virtual void RefreshLabelNextFrame() { } public virtual void RefreshLabelInternal() { } public virtual void UpdateTooltipSerieParams(int dataIndex, bool showCategory, string category, string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, ref List paramList, ref string title) { } public virtual void OnLegendButtonClick(int index, string legendName, bool show) { } public virtual void OnLegendButtonEnter(int index, string legendName) { } public virtual void OnLegendButtonExit(int index, string legendName) { } internal abstract void SetSerie(Serie serie); } public abstract class SerieHandler : SerieHandler where T : Serie { private static readonly string s_SerieLabelObjectName = "label"; private static readonly string s_SerieTitleObjectName = "title"; private static readonly string s_SerieRootObjectName = "serie"; private static readonly string s_SerieEndLabelObjectName = "end_label"; protected GameObject m_SerieRoot; protected GameObject m_SerieLabelRoot; protected bool m_InitedLabel; protected bool m_NeedInitComponent; protected bool m_RefreshLabel; protected bool m_LastCheckContextFlag = false; protected bool m_LegendEnter = false; protected int m_LegendEnterIndex; protected ChartLabel m_EndLabel; public T serie { get; internal set; } public GameObject labelObject { get { return m_SerieLabelRoot; } } internal override void SetSerie(Serie serie) { this.serie = (T) serie; this.serie.context.param.serieType = typeof(T); m_NeedInitComponent = true; AnimationStyleHelper.UpdateSerieAnimation(serie); } public override void Update() { if (m_NeedInitComponent) { m_NeedInitComponent = false; InitComponent(); } if (m_RefreshLabel) { m_RefreshLabel = false; RefreshLabelInternal(); RefreshEndLabelInternal(); } if (serie.dataDirty) { SeriesHelper.UpdateSerieNameList(chart, ref chart.m_LegendRealShowName); serie.OnDataUpdate(); serie.dataDirty = false; } if (serie.label != null && (serie.labelDirty || serie.label.componentDirty)) { serie.labelDirty = false; serie.label.ClearComponentDirty(); InitSerieLabel(); InitSerieEndLabel(); } if (serie.endLabel != null && serie.endLabel.componentDirty) { serie.endLabel.ClearComponentDirty(); InitSerieEndLabel(); } if (serie.titleStyle != null && (serie.titleDirty || serie.titleStyle.componentDirty)) { serie.titleDirty = false; serie.titleStyle.ClearComponentDirty(); InitSerieTitle(); } if (serie.nameDirty) { foreach (var component in chart.components) { if (component is Legend) component.SetAllDirty(); } chart.RefreshChart(); serie.ClearSerieNameDirty(); } if (serie.vertsDirty) { chart.RefreshPainter(serie); serie.ResetInteract(); serie.ClearVerticesDirty(); } } public override void RefreshLabelNextFrame() { m_RefreshLabel = true; } public override void InitComponent() { m_InitedLabel = false; InitRoot(); InitSerieLabel(); InitSerieTitle(); InitSerieEndLabel(); } public override void RemoveComponent() { ChartHelper.SetActive(m_SerieRoot, false); } public override void OnLegendButtonClick(int index, string legendName, bool show) { if (serie.colorByData && serie.IsSerieDataLegendName(legendName)) { LegendHelper.CheckDataShow(serie, legendName, show); chart.UpdateLegendColor(legendName, show); chart.RefreshPainter(serie); } else if (serie.IsLegendName(legendName)) { chart.SetSerieActive(serie, show); chart.RefreshPainter(serie); } } public override void OnLegendButtonEnter(int index, string legendName) { if (serie.colorByData && serie.IsSerieDataLegendName(legendName)) { LegendHelper.CheckDataHighlighted(serie, legendName, true); chart.RefreshPainter(serie); } else if (serie.IsLegendName(legendName)) { m_LegendEnter = true; chart.RefreshPainter(serie); } } public override void OnLegendButtonExit(int index, string legendName) { if (serie.colorByData && serie.IsSerieDataLegendName(legendName)) { LegendHelper.CheckDataHighlighted(serie, legendName, false); chart.RefreshPainter(serie); } else if (serie.IsLegendName(legendName)) { m_LegendEnter = false; chart.RefreshPainter(serie); } } private void InitRoot() { if (m_SerieRoot != null) { var rect = ChartHelper.GetOrAddComponent(m_SerieRoot); rect.localPosition = Vector3.zero; rect.sizeDelta = chart.chartSizeDelta; rect.anchorMin = chart.chartMinAnchor; rect.anchorMax = chart.chartMaxAnchor; rect.pivot = chart.chartPivot; return; } var objName = s_SerieRootObjectName + "_" + serie.index; m_SerieRoot = ChartHelper.AddObject(objName, chart.transform, chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); m_SerieRoot.hideFlags = chart.chartHideFlags; ChartHelper.SetActive(m_SerieRoot, true); ChartHelper.HideAllObject(m_SerieRoot); } private void InitSerieLabel() { InitRoot(); m_SerieLabelRoot = ChartHelper.AddObject(s_SerieLabelObjectName, m_SerieRoot.transform, chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); m_SerieLabelRoot.hideFlags = chart.chartHideFlags; SerieLabelPool.ReleaseAll(m_SerieLabelRoot.transform); int count = 0; SerieHelper.UpdateCenter(serie, chart.chartPosition, chart.chartWidth, chart.chartHeight); for (int j = 0; j < serie.data.Count; j++) { var serieData = serie.data[j]; serieData.index = j; serieData.labelObject = null; if (AddSerieLabel(m_SerieLabelRoot, serieData, ref count)) { m_InitedLabel = true; count++; } } RefreshLabelInternal(); } protected bool AddSerieLabel(GameObject serieLabelRoot, SerieData serieData, ref int count) { if (serieData == null) return false; if (serieLabelRoot == null) return false; if (serie.IsPerformanceMode()) return false; if (count == -1) count = serie.dataCount; var serieLabel = SerieHelper.GetSerieLabel(serie, serieData); if (serieLabel == null) { return false; } var dataAutoColor = GetSerieDataAutoColor(serieData); serieData.context.dataLabels.Clear(); if (serie.multiDimensionLabel) { for (int i = 0; i < serieData.data.Count; i++) { var textName = string.Format("{0}_{1}_{2}_{3}", s_SerieLabelObjectName, serie.index, serieData.index, i); var label = ChartHelper.AddChartLabel(textName, serieLabelRoot.transform, serieLabel, chart.theme.common, "", dataAutoColor, TextAnchor.MiddleCenter); label.SetActive(serieLabel.show); serieData.context.dataLabels.Add(label); } } else { var textName = ChartCached.GetSerieLabelName(s_SerieLabelObjectName, serie.index, serieData.index); var label = ChartHelper.AddChartLabel(textName, serieLabelRoot.transform, serieLabel, chart.theme.common, "", dataAutoColor, TextAnchor.MiddleCenter); label.SetActive(serieLabel.show); serieData.labelObject = label; } if (serieData.context.children.Count > 0) { foreach (var childSerieData in serieData.context.children) { AddSerieLabel(serieLabelRoot, childSerieData, ref count); count++; } } return true; } private void InitSerieEndLabel() { if (serie.endLabel == null) { if (m_EndLabel != null) { m_EndLabel.SetActive(false); m_EndLabel = null; } return; } InitRoot(); var dataAutoColor = (Color) chart.GetLegendRealShowNameColor(serie.legendName); m_EndLabel = ChartHelper.AddChartLabel(s_SerieEndLabelObjectName, m_SerieRoot.transform, serie.endLabel, chart.theme.common, "", dataAutoColor, TextAnchor.MiddleLeft); m_EndLabel.SetActive(serie.endLabel.show); RefreshEndLabelInternal(); } private void InitSerieTitle() { InitRoot(); var serieTitleRoot = ChartHelper.AddObject(s_SerieTitleObjectName, m_SerieRoot.transform, chart.chartMinAnchor, chart.chartMaxAnchor, chart.chartPivot, chart.chartSizeDelta); serieTitleRoot.hideFlags = chart.chartHideFlags; SerieLabelPool.ReleaseAll(serieTitleRoot.transform); ChartHelper.RemoveComponent(serieTitleRoot); SerieHelper.UpdateCenter(serie, chart.chartPosition, chart.chartWidth, chart.chartHeight); if (serie.titleJustForSerie) { var titleStyle = SerieHelper.GetTitleStyle(serie, null); if (titleStyle != null) { var color = chart.GetItemColor(serie, null); var content = string.Empty; if (string.IsNullOrEmpty(titleStyle.formatter)) { content = serie.serieName; } else { content = titleStyle.formatter; FormatterHelper.ReplaceContent(ref content, 0, titleStyle.numericFormatter, serie, chart); } var label = ChartHelper.AddChartLabel("title_" + 0, serieTitleRoot.transform, titleStyle, chart.theme.common, content, color, TextAnchor.MiddleCenter); serie.context.titleObject = label; label.SetActive(titleStyle.show); var labelPosition = GetSerieDataTitlePosition(null, titleStyle); var offset = titleStyle.GetOffset(serie.context.insideRadius); label.SetPosition(labelPosition + offset); } } else { for (int i = 0; i < serie.dataCount; i++) { var serieData = serie.data[i]; var titleStyle = SerieHelper.GetTitleStyle(serie, serieData); if (titleStyle == null) continue; var color = chart.GetItemColor(serie, serieData); var content = string.Empty; if (string.IsNullOrEmpty(titleStyle.formatter)) { content = serieData.name; } else { content = titleStyle.formatter; FormatterHelper.ReplaceContent(ref content, 0, titleStyle.numericFormatter, serie, chart); } FormatterHelper.ReplaceContent(ref content, i, titleStyle.numericFormatter, serie, chart); var label = ChartHelper.AddChartLabel("title_" + i, serieTitleRoot.transform, titleStyle, chart.theme.common, content, color, TextAnchor.MiddleCenter); serieData.titleObject = label; label.SetActive(titleStyle.show); var labelPosition = GetSerieDataTitlePosition(serieData, titleStyle); var offset = titleStyle.GetOffset(serie.context.insideRadius); label.SetPosition(labelPosition + offset); } } } public override void RefreshLabelInternal() { if (!m_InitedLabel) return; var dataChangeDuration = serie.animation.GetUpdateAnimationDuration(); var unscaledTime = serie.animation.unscaledTime; var needCheck = serie.context.dataIndexs.Count > 0; foreach (var serieData in serie.data) { if (serieData.labelObject == null && serieData.context.dataLabels.Count <= 0) { continue; } if (needCheck && !serie.context.dataIndexs.Contains(serieData.index)) { serieData.SetLabelActive(false); continue; }; var currLabel = SerieHelper.GetSerieLabel(serie, serieData); var isIgnore = serie.IsIgnoreIndex(serieData.index, defaultDimension); if (serie.show && currLabel != null && serieData.context.canShowLabel && !isIgnore) { if (serie.multiDimensionLabel) { var total = serieData.GetTotalData(); var color = chart.GetItemColor(serie, serieData); for (int i = 0; i < serieData.context.dataLabels.Count; i++) { if (i >= serieData.context.dataPoints.Count) continue; var labelObject = serieData.context.dataLabels[i]; var value = serieData.GetCurrData(i, dataChangeDuration, unscaledTime); var content = string.IsNullOrEmpty(currLabel.formatter) ? ChartCached.NumberToStr(value, currLabel.numericFormatter) : SerieLabelHelper.GetFormatterContent(serie, serieData, value, total, currLabel, color); var offset = GetSerieDataLabelOffset(serieData, currLabel); labelObject.SetActive(currLabel.show && !isIgnore); labelObject.SetText(content); labelObject.SetPosition(serieData.context.dataPoints[i] + offset); labelObject.UpdateIcon(currLabel.icon); if (currLabel.textStyle.autoColor) { var dataAutoColor = GetSerieDataAutoColor(serieData); if (!ChartHelper.IsClearColor(dataAutoColor)) labelObject.SetTextColor(dataAutoColor); } } } else { var value = serieData.GetCurrData(defaultDimension, dataChangeDuration, unscaledTime); var total = serie.GetDataTotal(defaultDimension, serieData); var color = chart.GetItemColor(serie, serieData); var content = string.IsNullOrEmpty(currLabel.formatter) ? ChartCached.NumberToStr(value, currLabel.numericFormatter) : SerieLabelHelper.GetFormatterContent(serie, serieData, value, total, currLabel, color); serieData.SetLabelActive(currLabel.show && !isIgnore); serieData.labelObject.UpdateIcon(currLabel.icon); serieData.labelObject.SetText(content); UpdateLabelPosition(serieData, currLabel); if (currLabel.textStyle.autoColor) { var dataAutoColor = GetSerieDataAutoColor(serieData); if (!ChartHelper.IsClearColor(dataAutoColor)) serieData.labelObject.SetTextColor(dataAutoColor); } } } else { serieData.SetLabelActive(false); } } } public virtual void RefreshEndLabelInternal() { if (m_EndLabel == null) return; var endLabelStyle = serie.endLabel; if (endLabelStyle == null) return; var dataCount = serie.context.dataPoints.Count; var active = endLabelStyle.show && dataCount > 0; m_EndLabel.SetActive(active); if (active) { var value = serie.context.lineEndValue; var content = SerieLabelHelper.GetFormatterContent(serie, null, value, 0, endLabelStyle, Color.clear); m_EndLabel.SetText(content); m_EndLabel.SetPosition(serie.context.lineEndPostion + endLabelStyle.offset); } m_EndLabel.isAnimationEnd = serie.animation.IsFinish(); } private void UpdateLabelPosition(SerieData serieData, LabelStyle currLabel) { var labelPosition = GetSerieDataLabelPosition(serieData, currLabel); var offset = GetSerieDataLabelOffset(serieData, currLabel); serieData.labelObject.SetPosition(labelPosition + offset); } public virtual Vector3 GetSerieDataLabelPosition(SerieData serieData, LabelStyle label) { return ChartHelper.IsZeroVector(serieData.context.labelPosition) ? serieData.context.position : serieData.context.labelPosition; } public virtual Vector3 GetSerieDataLabelOffset(SerieData serieData, LabelStyle label) { return label.GetOffset(serie.context.insideRadius); } public virtual Vector3 GetSerieDataTitlePosition(SerieData serieData, TitleStyle titleStyle) { return serieData.context.position; } public virtual Color GetSerieDataAutoColor(SerieData serieData) { var colorIndex = serie.colorByData ? serieData.index : serie.index; Color32 color, toColor; SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, SerieState.Normal, false); return (Color) color; } protected void UpdateCoordSerieParams(ref List paramList, ref string title, int dataIndex, bool showCategory, string category, string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent) { var dimension = 1; if (dataIndex < 0) dataIndex = serie.context.pointerItemDataIndex; if (dataIndex < 0) return; var serieData = serie.GetSerieData(dataIndex); if (serieData == null) return; var ignore = serie.IsIgnoreValue(serieData, dimension); if (ignore && string.IsNullOrEmpty(ignoreDataDefaultContent)) return; itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); if (serie.placeHolder || TooltipHelper.IsIgnoreFormatter(itemFormatter)) return; var param = serie.context.param; param.serieName = serie.serieName; param.serieIndex = serie.index; param.category = category; param.dimension = dimension; param.serieData = serieData; param.dataCount = serie.dataCount; param.value = serieData.GetData(dimension); param.ignore = ignore; param.total = serie.yTotal; param.color = chart.GetMarkColor(serie, serieData); param.marker = SerieHelper.GetItemMarker(serie, serieData, marker); param.itemFormatter = itemFormatter; param.numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); param.columns.Clear(); param.columns.Add(param.marker); param.columns.Add(showCategory ? category : serie.serieName); param.columns.Add(ignore?ignoreDataDefaultContent : ChartCached.NumberToStr(param.value, param.numericFormatter)); paramList.Add(param); } protected void UpdateItemSerieParams(ref List paramList, ref string title, int dataIndex, string category, string marker, string itemFormatter, string numericFormatter, string ignoreDataDefaultContent, int dimension = 1, int colorIndex = -1) { if (dataIndex < 0) dataIndex = serie.context.pointerItemDataIndex; if (dataIndex < 0) return; var serieData = serie.GetSerieData(dataIndex); if (serieData == null) return; var ignore = serie.IsIgnoreValue(serieData, dimension); if (ignore && string.IsNullOrEmpty(ignoreDataDefaultContent)) return; itemFormatter = SerieHelper.GetItemFormatter(serie, serieData, itemFormatter); if (serie.placeHolder || TooltipHelper.IsIgnoreFormatter(itemFormatter)) return; if (colorIndex < 0) colorIndex = serie.colorByData?dataIndex : chart.GetLegendRealShowNameIndex(serieData.name); Color32 color, toColor; SerieHelper.GetItemColor(out color, out toColor, serie, serieData, chart.theme, colorIndex, SerieState.Normal); var param = serie.context.param; param.serieName = serie.serieName; param.serieIndex = serie.index; param.category = category; param.dimension = dimension; param.serieData = serieData; param.dataCount = serie.dataCount; param.value = serieData.GetData(param.dimension); param.ignore = ignore; param.total = serie.multiDimensionLabel? serieData.GetTotalData() : serie.GetDataTotal(defaultDimension); param.color = color; param.marker = SerieHelper.GetItemMarker(serie, serieData, marker); param.itemFormatter = itemFormatter; param.numericFormatter = SerieHelper.GetNumericFormatter(serie, serieData, numericFormatter); param.columns.Clear(); param.columns.Add(param.marker); param.columns.Add(serieData.name); param.columns.Add(ignore?ignoreDataDefaultContent : ChartCached.NumberToStr(param.value, param.numericFormatter)); paramList.Add(param); } public void DrawLabelLineSymbol(VertexHelper vh, LabelLine labelLine, Vector3 startPos, Vector3 endPos, Color32 defaultColor) { if (labelLine.startSymbol != null && labelLine.startSymbol.show) { DrawSymbol(vh, labelLine.startSymbol, startPos, defaultColor); } if (labelLine.endSymbol != null && labelLine.endSymbol.show) { DrawSymbol(vh, labelLine.endSymbol, endPos, defaultColor); } } private void DrawSymbol(VertexHelper vh, SymbolStyle symbol, Vector3 pos, Color32 defaultColor) { var color = symbol.GetColor(defaultColor); chart.DrawSymbol(vh, symbol.type, symbol.size, 1, pos, color, color, ColorUtil.clearColor32, color, symbol.gap, null); } } }