diff --git a/Assets/XCharts/CHANGELOG.md b/Assets/XCharts/CHANGELOG.md index 75121dc3..05b152b5 100644 --- a/Assets/XCharts/CHANGELOG.md +++ b/Assets/XCharts/CHANGELOG.md @@ -1,6 +1,7 @@ # 更新日志 +* (2020.01.26) 增加`TextLimit`组件可以设置`AxisLabel`的文本自适应 * (2020.01.20) 优化`Tooltip`设置`itemFormatter`时显示系列颜色 * (2020.01.20) 增加`Radar`雷达图在`inspector`配置`areaStyle`的支持 * (2020.01.15) 发布`v1.2.0`版本 diff --git a/Assets/XCharts/Documentation/XCharts配置项手册.md b/Assets/XCharts/Documentation/XCharts配置项手册.md index c0b4cbf2..5e938355 100644 --- a/Assets/XCharts/Documentation/XCharts配置项手册.md +++ b/Assets/XCharts/Documentation/XCharts配置项手册.md @@ -42,6 +42,7 @@ * [SerieData 数据项](#SerieData) * [SerieLabel 图形上的文本标签](#SerieLabel) * [SerieSymbol 图形标记](#SerieSymbol) +* [TextLimit 文本自适应](#TextLimit) * [TextStyle 文本样式](#TextStyle) ## `Theme` @@ -155,6 +156,13 @@ * `min`:指示器的最小值,默认为 0 无限制。 * `textStyle`:文本样式 [TextStyle](#TextStyle)。 +## `TextLimit` + +* `enable`:是否启用文本自适应。默认为`true`。 +* `maxWidth`:设定最大宽度。默认为`0`表示自动获取,否则表示自定义。当文本的宽度大于该值进行裁剪。 +* `gap`:两边留白像素距离。默认为`10`。 +* `suffix`: 长度超出被裁剪后附加的后缀。默认为`...`。 + ## `TextStyle` * `rotate`:旋转。 @@ -632,6 +640,7 @@ * `fontStyle`:文字字体的风格。 * `formatter`:图例内容字符串模版格式器。支持用 \n 换行。模板变量为图例名称 {value},支持{value:f0},{value:f1},{value:f2}。 * `forceENotation`:是否强制使用科学计数法格式化显示数值。默认为false,当小数精度大于3时才采用科学计数法。 +* `textLimit`:文本自适应 [TextLimit](#TextLimit)。只在类目轴中有效。 ## `AxisLine` diff --git a/Assets/XCharts/Editor/PropertyDrawers/AxisLabelDrawer.cs b/Assets/XCharts/Editor/PropertyDrawers/AxisLabelDrawer.cs index b520fe8c..3514b4b4 100644 --- a/Assets/XCharts/Editor/PropertyDrawers/AxisLabelDrawer.cs +++ b/Assets/XCharts/Editor/PropertyDrawers/AxisLabelDrawer.cs @@ -30,6 +30,7 @@ namespace XCharts SerializedProperty m_FontSize = prop.FindPropertyRelative("m_FontSize"); SerializedProperty m_FontStyle = prop.FindPropertyRelative("m_FontStyle"); SerializedProperty m_ForceENotation = prop.FindPropertyRelative("m_ForceENotation"); + SerializedProperty m_TextLimit = prop.FindPropertyRelative("m_TextLimit"); ChartEditorHelper.MakeFoldout(ref drawRect, ref m_AxisLabelToggle, prop, "Axis Label", show, false); drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; @@ -54,6 +55,8 @@ namespace XCharts drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; EditorGUI.PropertyField(drawRect, m_ForceENotation); drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_TextLimit); + drawRect.y += EditorGUI.GetPropertyHeight(m_TextLimit); --EditorGUI.indentLevel; } } @@ -63,7 +66,8 @@ namespace XCharts float height = 0; if (ChartEditorHelper.IsToggle(m_AxisLabelToggle, prop)) { - height += 9 * EditorGUIUtility.singleLineHeight + 10 * EditorGUIUtility.standardVerticalSpacing; + height += 10 * EditorGUIUtility.singleLineHeight + 9 * EditorGUIUtility.standardVerticalSpacing; + height += EditorGUI.GetPropertyHeight(prop.FindPropertyRelative("m_TextLimit")); } return height; } diff --git a/Assets/XCharts/Editor/PropertyDrawers/TextLimitDrawer.cs b/Assets/XCharts/Editor/PropertyDrawers/TextLimitDrawer.cs new file mode 100644 index 00000000..bfc52500 --- /dev/null +++ b/Assets/XCharts/Editor/PropertyDrawers/TextLimitDrawer.cs @@ -0,0 +1,53 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ + +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace XCharts +{ + [CustomPropertyDrawer(typeof(TextLimit), true)] + public class TextLimitDrawer : PropertyDrawer + { + private Dictionary m_TextLimitToggle = new Dictionary(); + + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + Rect drawRect = pos; + drawRect.height = EditorGUIUtility.singleLineHeight; + SerializedProperty m_Enable = prop.FindPropertyRelative("m_Enable"); + SerializedProperty m_MaxWidth = prop.FindPropertyRelative("m_MaxWidth"); + SerializedProperty m_Gap = prop.FindPropertyRelative("m_Gap"); + SerializedProperty m_LimitSuffix = prop.FindPropertyRelative("m_Suffix"); + + ChartEditorHelper.MakeFoldout(ref drawRect, ref m_TextLimitToggle, prop, "Text Limit", m_Enable, false); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + if (ChartEditorHelper.IsToggle(m_TextLimitToggle, prop)) + { + ++EditorGUI.indentLevel; + EditorGUI.PropertyField(drawRect, m_MaxWidth); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_Gap); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + EditorGUI.PropertyField(drawRect, m_LimitSuffix); + drawRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + --EditorGUI.indentLevel; + } + } + + public override float GetPropertyHeight(SerializedProperty prop, GUIContent label) + { + float height = 0; + if (ChartEditorHelper.IsToggle(m_TextLimitToggle, prop)) + { + height += 3 * EditorGUIUtility.singleLineHeight + 2 * EditorGUIUtility.standardVerticalSpacing; + } + return height; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Editor/PropertyDrawers/TextLimitDrawer.cs.meta b/Assets/XCharts/Editor/PropertyDrawers/TextLimitDrawer.cs.meta new file mode 100644 index 00000000..708bf5d8 --- /dev/null +++ b/Assets/XCharts/Editor/PropertyDrawers/TextLimitDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 842d3986d1c1747d8b0668649e8b1a0e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Component/Main/Axis.cs b/Assets/XCharts/Runtime/Component/Main/Axis.cs index 52bfb436..d010e6b7 100644 --- a/Assets/XCharts/Runtime/Component/Main/Axis.cs +++ b/Assets/XCharts/Runtime/Component/Main/Axis.cs @@ -918,6 +918,7 @@ namespace XCharts "x1","x2","x3","x4","x5" } }; + axis.axisLabel.textLimit.enable = true; return axis; } } @@ -968,6 +969,7 @@ namespace XCharts m_BoundaryGap = false, m_Data = new List(5), }; + axis.axisLabel.textLimit.enable = false; return axis; } } diff --git a/Assets/XCharts/Runtime/Component/Sub/AxisLabel.cs b/Assets/XCharts/Runtime/Component/Sub/AxisLabel.cs index 6df65d69..e45bee3f 100644 --- a/Assets/XCharts/Runtime/Component/Sub/AxisLabel.cs +++ b/Assets/XCharts/Runtime/Component/Sub/AxisLabel.cs @@ -7,6 +7,7 @@ using System; using UnityEngine; +using UnityEngine.UI; namespace XCharts { @@ -27,7 +28,7 @@ namespace XCharts [SerializeField] private int m_FontSize; [SerializeField] private FontStyle m_FontStyle; [SerializeField] private bool m_ForceENotation = false; - + [SerializeField] private TextLimit m_TextLimit = new TextLimit(); /// /// Set this to false to prevent the axis label from appearing. @@ -78,6 +79,10 @@ namespace XCharts /// 是否强制使用科学计数法格式化显示数值。默认为false,当小数精度大于3时才采用科学计数法。 /// public bool forceENotation { get { return m_ForceENotation; } set { m_ForceENotation = value; } } + /// + /// 文本限制。 + /// + public TextLimit textLimit { get { return m_TextLimit; } } public static AxisLabel defaultAxisLabel { @@ -107,6 +112,7 @@ namespace XCharts m_FontSize = other.fontSize; m_FontStyle = other.fontStyle; m_Formatter = other.formatter; + m_TextLimit.Copy(other.textLimit); } public override bool Equals(object obj) @@ -125,7 +131,8 @@ namespace XCharts m_FontSize == other.fontSize && m_FontStyle == other.fontStyle && m_ForceENotation == other.forceENotation && - m_Formatter == other.formatter; + m_Formatter.Equals(other.formatter) && + m_TextLimit.Equals(other.textLimit); } public override int GetHashCode() @@ -133,16 +140,24 @@ namespace XCharts return base.GetHashCode(); } + public void SetRelatedText(Text txt, float labelWidth) + { + m_TextLimit.SetRelatedText(txt,labelWidth); + } + public string GetFormatterContent(string category) { + if (string.IsNullOrEmpty(category)) return category; if (string.IsNullOrEmpty(m_Formatter)) - return category; + { + return m_TextLimit.GetLimitContent(category); + } else { var content = m_Formatter.Replace("{value}", category); content = content.Replace("\\n", "\n"); content = content.Replace("
", "\n"); - return content; + return m_TextLimit.GetLimitContent(content); } } diff --git a/Assets/XCharts/Runtime/Component/Sub/TextLimit.cs b/Assets/XCharts/Runtime/Component/Sub/TextLimit.cs new file mode 100644 index 00000000..9eaa07da --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Sub/TextLimit.cs @@ -0,0 +1,136 @@ +/******************************************/ +/* */ +/* Copyright (c) 2018 monitor1394 */ +/* https://github.com/monitor1394 */ +/* */ +/******************************************/ + +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace XCharts +{ + /// + /// 文本字符限制和自适应。当文本长度超过设定的长度时进行裁剪,并将后缀附加在最后。 + /// 只在类目轴中有效。 + /// + [Serializable] + public class TextLimit : SubComponent + { + [SerializeField] private bool m_Enable = true; + [SerializeField] private float m_MaxWidth = 0; + [SerializeField] private float m_Gap = 10; + [SerializeField] private string m_Suffix = "..."; + + /// + /// 是否启用文本自适应。默认为true。 + /// + public bool enable { get { return m_Enable; } set { m_Enable = value; } } + /// + /// 设定最大宽度。默认为0表示自动获取,否则表示自定义。当文本的宽度大于该值进行裁剪。 + /// + public float maxWidth { get { return m_MaxWidth; } set { m_MaxWidth = value; } } + /// + /// 两边留白像素距离。默认为10 + /// + public float gap { get { return m_Gap; } set { m_Gap = value; } } + /// + /// 长度超出时的后缀。 + /// + public string suffix { get { return m_Suffix; } set { m_Suffix = value; } } + + private Text m_RelatedText; + private TextGenerationSettings m_RelatedTextSettings; + private float m_RelatedTextWidth = 0; + + public void Copy(TextLimit other) + { + m_Enable = other.enable; + m_Gap = other.gap; + m_Suffix = other.suffix; + m_MaxWidth = other.maxWidth; + } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + var other = (TextLimit)obj; + return m_Enable == other.enable && + m_MaxWidth == other.maxWidth && + m_Gap == other.gap && + m_Suffix.Equals(other.suffix); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public void SetRelatedText(Text txt, float labelWidth) + { + m_RelatedText = txt; + m_RelatedTextSettings = txt.GetGenerationSettings(Vector2.zero); + m_RelatedTextWidth = labelWidth; + } + + + public string GetLimitContent(string content) + { + float checkWidth = m_MaxWidth > 0 ? m_MaxWidth : m_RelatedTextWidth; + if (m_RelatedText == null || checkWidth <= 0) return content; + else + { + if (m_Enable) + { + float len = m_RelatedText.cachedTextGenerator.GetPreferredWidth(content, m_RelatedTextSettings); + float suffixLen = m_RelatedText.cachedTextGenerator.GetPreferredWidth(suffix, m_RelatedTextSettings); + if (len > checkWidth - m_Gap * 2 - suffixLen) + { + return content.Substring(0, GetAdaptLength(content, suffixLen)) + suffix; + } + else + { + return content; + } + } + else + { + return content; + } + } + } + + private int GetAdaptLength(string content, float suffixLen) + { + int start = 0; + int middle = content.Length / 2; + int end = content.Length; + float checkWidth = m_MaxWidth > 0 ? m_MaxWidth : m_RelatedTextWidth; + float limit = checkWidth - m_Gap * 2 - suffixLen; + if (limit < 0) return 0; + float len = 0; + while (len != limit && middle != start) + { + len = m_RelatedText.cachedTextGenerator.GetPreferredWidth(content.Substring(0, middle), m_RelatedTextSettings); + if (len < limit) + { + start = middle; + } + else if (len > limit) + { + end = middle; + } + else + { + break; + } + middle = (start + end) / 2; + } + return middle; + } + } +} \ No newline at end of file diff --git a/Assets/XCharts/Runtime/Component/Sub/TextLimit.cs.meta b/Assets/XCharts/Runtime/Component/Sub/TextLimit.cs.meta new file mode 100644 index 00000000..da765076 --- /dev/null +++ b/Assets/XCharts/Runtime/Component/Sub/TextLimit.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f49509a5de044535b1dd3f192f7008c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/XCharts/Runtime/Internal/CoordinateChart.cs b/Assets/XCharts/Runtime/Internal/CoordinateChart.cs index fccbc148..5a78b530 100644 --- a/Assets/XCharts/Runtime/Internal/CoordinateChart.cs +++ b/Assets/XCharts/Runtime/Internal/CoordinateChart.cs @@ -487,8 +487,9 @@ namespace XCharts Vector2.zero, new Vector2(1, 0.5f), new Vector2(m_Grid.left, 20), yAxis.axisLabel.fontSize, yAxis.axisLabel.rotate, yAxis.axisLabel.fontStyle); } - float labelWidth = yAxis.GetScaleWidth(coordinateHeight, i, m_DataZoom); + float labelWidth = yAxis.GetScaleWidth(coordinateHeight, i, m_DataZoom); + if (i == 0) yAxis.axisLabel.SetRelatedText(txt, labelWidth); txt.transform.localPosition = GetLabelYPosition(totalWidth + (yAxis.boundaryGap ? labelWidth / 2 : 0), i, yAxisIndex, yAxis); var isPercentStack = m_Series.IsPercentStack(SerieType.Bar); @@ -582,12 +583,13 @@ namespace XCharts m_ThemeInfo.font, labelColor, TextAnchor.MiddleCenter, new Vector2(0, 1), new Vector2(0, 1), new Vector2(1, 0.5f), new Vector2(labelWidth, 20), xAxis.axisLabel.fontSize, xAxis.axisLabel.rotate, xAxis.axisLabel.fontStyle); - + if (i == 0) xAxis.axisLabel.SetRelatedText(txt, labelWidth); txt.transform.localPosition = GetLabelXPosition(totalWidth + (xAxis.boundaryGap ? labelWidth : labelWidth / 2), i, xAxisIndex, xAxis); totalWidth += labelWidth; var isPercentStack = m_Series.IsPercentStack(SerieType.Bar); - txt.text = xAxis.GetLabelName(coordinateWidth, i, xAxis.runtimeMinValue, xAxis.runtimeMaxValue, m_DataZoom, isPercentStack); + txt.text = xAxis.GetLabelName(coordinateWidth, i, xAxis.runtimeMinValue, xAxis.runtimeMaxValue, m_DataZoom, + isPercentStack); txt.gameObject.SetActive(xAxis.show && (xAxis.axisLabel.interval == 0 || i % (xAxis.axisLabel.interval + 1) == 0)); xAxis.axisLabelTextList.Add(txt); diff --git a/Doc/newfeature/(2020.01.26) 增加`TextLimit`组件可以设置`AxisLabel`的文本自适应.png b/Doc/newfeature/(2020.01.26) 增加`TextLimit`组件可以设置`AxisLabel`的文本自适应.png new file mode 100644 index 00000000..08373230 Binary files /dev/null and b/Doc/newfeature/(2020.01.26) 增加`TextLimit`组件可以设置`AxisLabel`的文本自适应.png differ