diff --git a/Documentation~/zh/changelog.md b/Documentation~/zh/changelog.md index e01c7ea0..3810a760 100644 --- a/Documentation~/zh/changelog.md +++ b/Documentation~/zh/changelog.md @@ -81,6 +81,7 @@ slug: /changelog ## master +* (2026.05.22) 增加`LabelStyle`的`showCondition`,`showFilter`,`showThreshold`可控制`label`显示和隐藏 * (2026.05.22) 增加`LabelStyle`的`minGap`可避免`label`过于密集 * (2026.05.17) 修复`DataZoom`点击时指示区域不准的问题 * (2026.05.17) 增加`Legend`的`Width`和`Height`可设置固定宽高 diff --git a/Editor/ChildComponents/BasePropertyDrawer.cs b/Editor/ChildComponents/BasePropertyDrawer.cs index e5574085..4edc9c78 100644 --- a/Editor/ChildComponents/BasePropertyDrawer.cs +++ b/Editor/ChildComponents/BasePropertyDrawer.cs @@ -113,6 +113,15 @@ namespace XCharts.Editor } } + protected void PropertyFlagsField(SerializedProperty prop, string relativePropName, System.Type enumType) + { + if (IngorePropertys.Contains(relativePropName)) return; + if (!ChartEditorHelper.PropertyFlagsField(ref m_DrawRect, m_Heights, m_KeyName, prop, relativePropName, enumType)) + { + Debug.LogError("PropertyFlagsField ERROR:" + prop.displayName + ", " + relativePropName); + } + } + protected void PropertyFieldLimitMin(SerializedProperty prop, string relativePropName, float minValue) { if (IngorePropertys.Contains(relativePropName)) return; diff --git a/Editor/ChildComponents/LabelStyleDrawer.cs b/Editor/ChildComponents/LabelStyleDrawer.cs index 1a6db5e6..97689e68 100644 --- a/Editor/ChildComponents/LabelStyleDrawer.cs +++ b/Editor/ChildComponents/LabelStyleDrawer.cs @@ -26,7 +26,10 @@ namespace XCharts.Editor PropertyField(prop, "m_Height"); PropertyField(prop, "m_FixedX"); PropertyField(prop, "m_FixedY"); - PropertyField(prop, "m_MinGap"); + PropertyField(prop, "m_ShowCondition"); + PropertyField(prop, "m_ShowFilter"); + PropertyField(prop, "m_ShowThreshold"); + PropertyField(prop, "m_ShowMinGap"); PropertyField(prop, "m_Icon"); PropertyField(prop, "m_Background"); PropertyField(prop, "m_TextStyle"); diff --git a/Editor/Utilities/ChartEditorHelper.cs b/Editor/Utilities/ChartEditorHelper.cs index 27ffdafd..28607acf 100644 --- a/Editor/Utilities/ChartEditorHelper.cs +++ b/Editor/Utilities/ChartEditorHelper.cs @@ -507,6 +507,28 @@ namespace XCharts.Editor { return PropertyField(ref drawRect, heights, key, parentProp.FindPropertyRelative(relativeName)); } + + public static bool PropertyFlagsField(ref Rect drawRect, Dictionary heights, string key, + SerializedProperty parentProp, string relativeName, System.Type enumType) + { + return PropertyFlagsField(ref drawRect, heights, key, parentProp.FindPropertyRelative(relativeName), enumType); + } + + public static bool PropertyFlagsField(ref Rect drawRect, Dictionary heights, string key, + SerializedProperty prop, System.Type enumType) + { + if (prop == null) return false; + var label = GetContent(prop.displayName); + var enumValue = (System.Enum)System.Enum.ToObject(enumType, prop.intValue); + EditorGUI.BeginChangeCheck(); + var newValue = EditorGUI.EnumFlagsField(drawRect, label, enumValue); + if (EditorGUI.EndChangeCheck()) + prop.intValue = (int)(object)newValue; + var hig = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + drawRect.y += hig; + heights[key] += hig; + return true; + } public static bool PropertyFieldWithMinValue(ref Rect drawRect, Dictionary heights, string key, SerializedProperty parentProp, string relativeName, float minValue) { diff --git a/Runtime/Component/Label/LabelStyle.cs b/Runtime/Component/Label/LabelStyle.cs index 243aafa3..febb30f3 100644 --- a/Runtime/Component/Label/LabelStyle.cs +++ b/Runtime/Component/Label/LabelStyle.cs @@ -68,6 +68,50 @@ namespace XCharts.Runtime /// End } + /// + /// The value-based condition for showing label. Controls visibility based on threshold comparison. + /// ||标签基于值的显示条件,通过与阈值比较来控制标签的显示。 + /// + public enum ShowCondition + { + /// + /// Always show label. + /// ||总是显示标签。 + /// + Always, + /// + /// Show label when value is greater than showThreshold. + /// ||大于showThreshold才显示标签。 + /// + GreaterThan, + /// + /// Show label when value is less than showThreshold. + /// ||小于showThreshold才显示标签。 + /// + LessThan, + } + /// + /// The data-pattern-based filter for showing label. Controls visibility based on data topology. + /// ||标签基于数据形态的显示筛选,通过数据的拓扑特征(波峰/波谷)来控制标签的显示。 + /// + public enum ShowFilter + { + /// + /// All data points show label. + /// ||所有数据点都显示标签。 + /// + All, + /// + /// Show label when value is at a peak. + /// ||波峰才显示标签。 + /// + Peak, + /// + /// Show label when value is at a valley. + /// ||波谷才显示标签。 + /// + Valley + } [SerializeField] protected bool m_Show = true; [SerializeField] Position m_Position = Position.Default; @@ -82,7 +126,10 @@ namespace XCharts.Runtime [SerializeField] protected float m_Height = 0; [SerializeField][Since("v3.15.0")] protected float m_FixedX = 0; [SerializeField][Since("v3.15.0")] protected float m_FixedY = 0; - [SerializeField][Since("v3.16.0")] protected float m_MinGap = 0; + [SerializeField][Since("v3.16.0")] protected ShowCondition m_ShowCondition = ShowCondition.Always; + [SerializeField][Since("v3.16.0")] protected ShowFilter m_ShowFilter = ShowFilter.All; + [SerializeField][Since("v3.16.0")] protected double m_ShowThreshold = 0; + [SerializeField][Since("v3.16.0")] protected float m_ShowMinGap = 0; [SerializeField] protected IconStyle m_Icon = new IconStyle(); [SerializeField] protected ImageStyle m_Background = new ImageStyle(); @@ -101,6 +148,9 @@ namespace XCharts.Runtime m_Height = 0; m_NumericFormatter = ""; m_AutoOffset = false; + m_ShowCondition = ShowCondition.Always; + m_ShowFilter = ShowFilter.All; + m_ShowThreshold = 0; } /// @@ -309,14 +359,45 @@ namespace XCharts.Runtime set { if (PropertyUtil.SetStruct(ref m_FixedY, value)) SetComponentDirty(); } } /// + /// The value-based show condition of label. Default is ShowCondition.Always. + /// When set to GreaterThan or LessThan, the label is shown only when the value satisfies the threshold. + /// ||标签基于值的显示条件。默认为ShowCondition.Always,总是显示。 + /// 设为GreaterThan或LessThan时,只有满足showThreshold阈值条件的数据点才显示标签。 + /// + public ShowCondition showCondition + { + get { return m_ShowCondition; } + set { if (PropertyUtil.SetStruct(ref m_ShowCondition, value)) SetComponentDirty(); } + } + /// + /// The data-pattern-based show filter of label. Default is ShowFilter.All. + /// When set to Peak or Valley, the label is shown only at local maximum or minimum data points. + /// ||标签基于数据形态的显示筛选。默认为ShowFilter.All,所有数据点都显示。 + /// 设为Peak或Valley时,只在局部波峰或波谷的数据点显示标签。 + /// + public ShowFilter showFilter + { + get { return m_ShowFilter; } + set { if (PropertyUtil.SetStruct(ref m_ShowFilter, value)) SetComponentDirty(); } + } + /// + /// The threshold for showCondition. When showCondition is GreaterThan or LessThan, only values that satisfy the comparison will show label. Default is 0. + /// ||showCondition的阈值。当showCondition为GreaterThan或LessThan时生效,只有满足比较条件的值才显示标签。默认值为0。 + /// + public double showThreshold + { + get { return m_ShowThreshold; } + set { if (PropertyUtil.SetStruct(ref m_ShowThreshold, value)) SetComponentDirty(); } + } + /// /// the gap between label and the previous label. When the distance to the previous label is less than this value, /// the label with smaller y value will be hidden. Default is 0, which means this function is turned off. /// 和上一个标签的最小间距。当和上一个标签的距离小于该值时,隐藏对应y值较小的标签。默认为0不开启该功能。 /// - public float minGap + public float showMinGap { - get { return m_MinGap; } - set { if (PropertyUtil.SetStruct(ref m_MinGap, value)) SetComponentDirty(); } + get { return m_ShowMinGap; } + set { if (PropertyUtil.SetStruct(ref m_ShowMinGap, value)) SetComponentDirty(); } } /// /// the sytle of background. diff --git a/Runtime/Serie/SerieHandler.cs b/Runtime/Serie/SerieHandler.cs index 5209e311..308b4e2b 100644 --- a/Runtime/Serie/SerieHandler.cs +++ b/Runtime/Serie/SerieHandler.cs @@ -546,6 +546,7 @@ namespace XCharts.Runtime currLabel, color, chart); var offset = GetSerieDataLabelOffset(serieData, currLabel); var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value); + if (active) active = CheckLabelVisible(currLabel, serieData.index, value, i); if (active) { anyLabelActive = true; @@ -577,10 +578,11 @@ namespace XCharts.Runtime currLabel, color, chart); var labelPos = UpdateLabelPosition(serieData, currLabel); var active = currLabel.show && !isIgnore && !serie.IsMinShowLabelValue(value); - if (active && currLabel.minGap > 0 && lastActiveLabelSerieData != null) + if (active) active = CheckLabelVisible(currLabel, serieData.index, value, defaultDimension); + if (active && currLabel.showMinGap > 0 && lastActiveLabelSerieData != null) { var dist = Mathf.Abs(labelPos.x - lastActiveLabelPos.x); - if (dist < currLabel.minGap) + if (dist < currLabel.showMinGap) { var currValue = serieData.GetData(1); if (Math.Abs(currValue) >= Math.Abs(lastActiveLabelValue)) @@ -641,6 +643,66 @@ namespace XCharts.Runtime } } + private bool CheckLabelVisible(LabelStyle label, int dataIndex, double value, int dimension) + { + // showCondition: 基于阈值的条件检查(AND showFilter) + bool conditionResult; + switch (label.showCondition) + { + case LabelStyle.ShowCondition.GreaterThan: + conditionResult = value > label.showThreshold; + break; + case LabelStyle.ShowCondition.LessThan: + conditionResult = value < label.showThreshold; + break; + default: // Always + conditionResult = true; + break; + } + + if (!conditionResult) + return false; + + // showFilter: 基于数据形态的过滤检查 + switch (label.showFilter) + { + case LabelStyle.ShowFilter.Peak: + { + bool isPeak = true; + bool hasNeighbor = false; + if (dataIndex > 0) + { + hasNeighbor = true; + isPeak &= value > serie.data[dataIndex - 1].GetData(dimension); + } + if (dataIndex < serie.dataCount - 1) + { + hasNeighbor = true; + isPeak &= value > serie.data[dataIndex + 1].GetData(dimension); + } + return isPeak && hasNeighbor; + } + case LabelStyle.ShowFilter.Valley: + { + bool isValley = true; + bool hasNeighbor = false; + if (dataIndex > 0) + { + hasNeighbor = true; + isValley &= value < serie.data[dataIndex - 1].GetData(dimension); + } + if (dataIndex < serie.dataCount - 1) + { + hasNeighbor = true; + isValley &= value < serie.data[dataIndex + 1].GetData(dimension); + } + return isValley && hasNeighbor; + } + default: // All + return true; + } + } + public virtual void RefreshEndLabelInternal() { if (m_EndLabel == null)