diff --git a/Editor/ChildComponents/MarqueeStyleDrawer.cs b/Editor/ChildComponents/MarqueeStyleDrawer.cs new file mode 100644 index 00000000..28bf280e --- /dev/null +++ b/Editor/ChildComponents/MarqueeStyleDrawer.cs @@ -0,0 +1,25 @@ +using UnityEditor; +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Editor +{ + [CustomPropertyDrawer(typeof(MarqueeStyle), true)] + public class MarqueeStyleDrawer : BasePropertyDrawer + { + public override string ClassName { get { return "MarqueeStyle"; } } + public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) + { + base.OnGUI(pos, prop, label); + if (MakeComponentFoldout(prop, "m_Show", true)) + { + ++EditorGUI.indentLevel; + PropertyField(prop, "m_Apply"); + PropertyField(prop, "m_RealRect"); + PropertyField(prop, "m_LineStyle"); + PropertyField(prop, "m_AreaStyle"); + --EditorGUI.indentLevel; + } + } + } +} \ No newline at end of file diff --git a/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta b/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta new file mode 100644 index 00000000..e0659300 --- /dev/null +++ b/Editor/ChildComponents/MarqueeStyleDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1a225478c2e14da3854aea28fb59882 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MainComponents/DataZoomEditor.cs b/Editor/MainComponents/DataZoomEditor.cs index f54c0012..7f3ec005 100644 --- a/Editor/MainComponents/DataZoomEditor.cs +++ b/Editor/MainComponents/DataZoomEditor.cs @@ -10,6 +10,7 @@ namespace XCharts.Editor { var m_SupportInside = baseProperty.FindPropertyRelative("m_SupportInside"); var m_SupportSlider = baseProperty.FindPropertyRelative("m_SupportSlider"); + var m_SupportSelect = baseProperty.FindPropertyRelative("m_SupportSelect"); var m_Start = baseProperty.FindPropertyRelative("m_Start"); var m_End = baseProperty.FindPropertyRelative("m_End"); var m_MinShowNum = baseProperty.FindPropertyRelative("m_MinShowNum"); @@ -22,6 +23,7 @@ namespace XCharts.Editor PropertyField("m_SupportInsideDrag"); } PropertyField(m_SupportSlider); + PropertyField(m_SupportSelect); PropertyField("m_ZoomLock"); PropertyField("m_ScrollSensitivity"); PropertyField("m_RangeMode"); @@ -54,6 +56,7 @@ namespace XCharts.Editor PropertyListField("m_XAxisIndexs", true); PropertyListField("m_YAxisIndexs", true); } + PropertyField("m_MarqueeStyle"); --EditorGUI.indentLevel; } } diff --git a/Examples/Example04_DataZoom.cs b/Examples/Example04_DataZoom.cs new file mode 100644 index 00000000..8f18c272 --- /dev/null +++ b/Examples/Example04_DataZoom.cs @@ -0,0 +1,50 @@ +using UnityEngine; +using XCharts.Runtime; + +namespace XCharts.Example +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + public class Example04_DataZoom : MonoBehaviour + { + BaseChart chart; + + void Awake() + { + chart = gameObject.GetComponent(); + if (chart == null) return; + var dataZoom = chart.GetChartComponent(); + if (dataZoom == null) return; + dataZoom.marqueeStyle.onStart = OnMarqueeStart; + dataZoom.marqueeStyle.onEnd = OnMarqueeEnd; + dataZoom.marqueeStyle.onGoing = OnMarquee; + } + + void OnMarqueeStart(DataZoom dataZoom) + { + //Debug.Log("OnMarqueeStart:" + dataZoom); + } + + void OnMarquee(DataZoom dataZoom) + { + //Debug.Log("OnMarquee:" + dataZoom); + } + + void OnMarqueeEnd(DataZoom dataZoom) + { + //Debug.Log("OnMarqueeEnd:" + dataZoom); + var serie = chart.GetSerie(0); + foreach (var serieData in serie.data) + { + if (dataZoom.IsInMarqueeArea(serieData)) + { + serieData.GetOrAddComponent().color = Color.red; + } + else + { + serieData.GetOrAddComponent().color = Color.clear; + } + } + } + } +} \ No newline at end of file diff --git a/Examples/Example04_DataZoom.cs.meta b/Examples/Example04_DataZoom.cs.meta new file mode 100644 index 00000000..e0a5838a --- /dev/null +++ b/Examples/Example04_DataZoom.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2cc0ca220d904377984528de6214b97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Component/Child/MarqueeStyle.cs b/Runtime/Component/Child/MarqueeStyle.cs new file mode 100644 index 00000000..e28ee74d --- /dev/null +++ b/Runtime/Component/Child/MarqueeStyle.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace XCharts.Runtime +{ + /// + /// Marquee style. It can be used for the DataZoom component. + /// 选取框样式。可用于DataZoom组件。 + /// + [Since("v3.5.0")] + [System.Serializable] + public class MarqueeStyle : ChildComponent + { + [SerializeField][Since("v3.5.0")] private bool m_Apply = false; + [SerializeField][Since("v3.5.0")] private bool m_RealRect = false; + [SerializeField][Since("v3.5.0")] private AreaStyle m_AreaStyle = new AreaStyle(); + [SerializeField][Since("v3.5.0")] private LineStyle m_LineStyle = new LineStyle(); + + protected Action m_OnStart; + protected Action m_OnGoing; + protected Action m_OnEnd; + + /// + /// The area style of marquee. + /// |选取框区域填充样式。 + /// + public AreaStyle areaStyle { get { return m_AreaStyle; } set { m_AreaStyle = value; } } + /// + /// The line style of marquee border. + /// |选取框区域边框样式。 + /// + public LineStyle lineStyle { get { return m_LineStyle; } set { m_LineStyle = value; } } + /// + /// Check whether the scope is applied to the DataZoom. + /// If this parameter is set to true, the range after the selection is complete is the DataZoom selection range. + /// |选取框范围是否应用到DataZoom上。当为true时,框选结束后的范围即为DataZoom的选择范围。 + /// + public bool apply { get { return m_Apply; } set { m_Apply = value; } } + /// + /// Whether to select the actual box selection area. When true, + /// the actual range between the mouse's actual point and the end point is used as the box selection area. + /// |是否选取实际框选区域。当为true时,以鼠标的其实点和结束点间的实际范围作为框选区域。 + /// + public bool realRect { get { return m_RealRect; } set { m_RealRect = value; } } + /// + /// Customize the callback to the start of the selection of the checkbox. + /// |自定义选取框开始选取时的回调。 + /// + public Action onStart { set { m_OnStart = value; } get { return m_OnStart; } } + /// + /// Custom checkboxes select ongoing callbacks. + /// |自定义选取框选取进行时的回调。 + /// + public Action onGoing { set { m_OnStart = value; } get { return m_OnStart; } } + /// + /// Customize the callback at the end of the selection. + /// |自定义选取框结束选取时的回调。 + /// + public Action onEnd { set { m_OnEnd = value; } get { return m_OnEnd; } } + } +} \ No newline at end of file diff --git a/Runtime/Component/Child/MarqueeStyle.cs.meta b/Runtime/Component/Child/MarqueeStyle.cs.meta new file mode 100644 index 00000000..5c804f34 --- /dev/null +++ b/Runtime/Component/Child/MarqueeStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: effa7d6629485469d91d41f896b9de8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Component/DataZoom/DataZoom.cs b/Runtime/Component/DataZoom/DataZoom.cs index 14b72be9..340cdf9f 100644 --- a/Runtime/Component/DataZoom/DataZoom.cs +++ b/Runtime/Component/DataZoom/DataZoom.cs @@ -90,6 +90,7 @@ namespace XCharts.Runtime [SerializeField] private LabelStyle m_LabelStyle = new LabelStyle(); [SerializeField] private LineStyle m_LineStyle = new LineStyle(LineStyle.Type.Solid); [SerializeField] private AreaStyle m_AreaStyle = new AreaStyle(); + [SerializeField][Since("v3.5.0")] private MarqueeStyle m_MarqueeStyle = new MarqueeStyle(); public DataZoomContext context = new DataZoomContext(); @@ -141,7 +142,8 @@ namespace XCharts.Runtime set { if (PropertyUtil.SetStruct(ref m_SupportInside, value)) SetVerticesDirty(); } } /// - /// 是否支持坐标系内滚动 + /// Whether inside scrolling is supported. + /// |是否支持坐标系内滚动 /// public bool supportInsideScroll { @@ -149,7 +151,8 @@ namespace XCharts.Runtime set { if (PropertyUtil.SetStruct(ref m_SupportInsideScroll, value)) SetVerticesDirty(); } } /// - /// 是否支持坐标系内拖拽 + /// Whether insde drag is supported. + /// |是否支持坐标系内拖拽 /// public bool supportInsideDrag { @@ -363,6 +366,14 @@ namespace XCharts.Runtime get { return m_AreaStyle; } set { if (PropertyUtil.SetClass(ref m_AreaStyle, value)) SetComponentDirty(); } } + /// + /// 选取框样式。 + /// + public MarqueeStyle marqueeStyle + { + get { return m_MarqueeStyle; } + set { if (PropertyUtil.SetClass(ref m_MarqueeStyle, value)) SetAllDirty(); } + } class AxisIndexValueInfo { @@ -414,6 +425,7 @@ namespace XCharts.Runtime show = true, opacity = 0.3f }; + m_MarqueeStyle = new MarqueeStyle(); } /// @@ -515,6 +527,17 @@ namespace XCharts.Runtime } } + public bool IsInMarqueeArea(SerieData serieData) + { + return IsInMarqueeArea(serieData.context.position); + } + + public bool IsInMarqueeArea(Vector2 pos) + { + if (!supportSelect) return false; + return context.marqueeRect.Contains(pos); + } + public bool IsContainsAxis(Axis axis) { if (axis == null) diff --git a/Runtime/Component/DataZoom/DataZoomContext.cs b/Runtime/Component/DataZoom/DataZoomContext.cs index 49cf852c..d856eadc 100644 --- a/Runtime/Component/DataZoom/DataZoomContext.cs +++ b/Runtime/Component/DataZoom/DataZoomContext.cs @@ -22,5 +22,10 @@ namespace XCharts.Runtime /// public double endValue { get; set; } public bool invert { get; set; } + + public bool isMarqueeDrag { get; set; } + public Vector3 marqueeStartPos { get; set; } + public Vector3 marqueeEndPos { get; set; } + public Rect marqueeRect { get; set; } } } \ No newline at end of file diff --git a/Runtime/Component/DataZoom/DataZoomHandler.cs b/Runtime/Component/DataZoom/DataZoomHandler.cs index 8f57bc9b..9d9aa4bd 100644 --- a/Runtime/Component/DataZoom/DataZoomHandler.cs +++ b/Runtime/Component/DataZoom/DataZoomHandler.cs @@ -72,9 +72,11 @@ namespace XCharts.Runtime { case Orient.Horizonal: DrawHorizonalDataZoomSlider(vh, dataZoom); + DrawMarquee(vh, dataZoom); break; case Orient.Vertical: DrawVerticalDataZoomSlider(vh, dataZoom); + DrawMarquee(vh, dataZoom); break; } } @@ -87,14 +89,14 @@ namespace XCharts.Runtime if (Input.touchCount > 1) return; - Vector2 pos; - if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) - return; - var dataZoom = component; if (!dataZoom.enable) return; + Vector2 pos; + if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) + return; + var grid = chart.GetGridOfDataZoom(dataZoom); if (dataZoom.supportInside && dataZoom.supportInsideDrag) { @@ -103,6 +105,23 @@ namespace XCharts.Runtime dataZoom.context.isCoordinateDrag = true; } } + if (dataZoom.supportSelect) + { + dataZoom.context.isMarqueeDrag = true; + dataZoom.context.marqueeStartPos = pos; + dataZoom.context.marqueeEndPos = pos; + + if (dataZoom.marqueeStyle.realRect) + dataZoom.context.marqueeRect = new Rect(pos.x, pos.y, 0, 0); + else + dataZoom.context.marqueeRect = new Rect(pos.x, grid.context.y, 0, grid.context.height); + + if (dataZoom.marqueeStyle.onStart != null) + { + dataZoom.marqueeStyle.onStart(dataZoom); + } + return; + } if (dataZoom.supportSlider) { if (!dataZoom.zoomLock) @@ -136,18 +155,40 @@ namespace XCharts.Runtime var dataZoom = component; var grid = chart.GetGridOfDataZoom(dataZoom); - switch (dataZoom.orient) + if (dataZoom.supportSelect) { - case Orient.Horizonal: - var deltaPercent = eventData.delta.x / grid.context.width * 100; - OnDragInside(dataZoom, deltaPercent); - OnDragSlider(dataZoom, deltaPercent); - break; - case Orient.Vertical: - deltaPercent = eventData.delta.y / grid.context.height * 100; - OnDragInside(dataZoom, deltaPercent); - OnDragSlider(dataZoom, deltaPercent); - break; + Vector2 pos; + if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) + return; + + dataZoom.context.marqueeEndPos = pos; + var oldRect = dataZoom.context.marqueeRect; + var rectWidth = pos.x - dataZoom.context.marqueeStartPos.x; + if (dataZoom.marqueeStyle.realRect) + dataZoom.context.marqueeRect = Rect.MinMaxRect(dataZoom.context.marqueeStartPos.x, pos.y, pos.x, dataZoom.context.marqueeStartPos.y); + else + dataZoom.context.marqueeRect = new Rect(oldRect.x, oldRect.y, rectWidth, oldRect.height); + + dataZoom.SetVerticesDirty(); + if (dataZoom.marqueeStyle.onGoing != null) + dataZoom.marqueeStyle.onGoing(dataZoom); + return; + } + else + { + switch (dataZoom.orient) + { + case Orient.Horizonal: + var deltaPercent = eventData.delta.x / grid.context.width * 100; + OnDragInside(dataZoom, deltaPercent); + OnDragSlider(dataZoom, deltaPercent); + break; + case Orient.Vertical: + deltaPercent = eventData.delta.y / grid.context.height * 100; + OnDragInside(dataZoom, deltaPercent); + OnDragSlider(dataZoom, deltaPercent); + break; + } } } @@ -157,6 +198,23 @@ namespace XCharts.Runtime return; var dataZoom = component; + + if (dataZoom.supportSelect) + { + dataZoom.context.isMarqueeDrag = false; + if (dataZoom.marqueeStyle.apply) + { + var grid = chart.GetGridOfDataZoom(dataZoom); + var start = (dataZoom.context.marqueeRect.x - grid.context.x) / grid.context.width * 100; + var end = (dataZoom.context.marqueeRect.x - grid.context.x + dataZoom.context.marqueeRect.width) / grid.context.width * 100; + UpdateDataZoomRange(dataZoom, start, end); + } + if (dataZoom.marqueeStyle.onEnd != null) + { + dataZoom.marqueeStyle.onEnd(dataZoom); + } + return; + } if (dataZoom.context.isDrag || dataZoom.context.isStartDrag || dataZoom.context.isEndDrag || dataZoom.context.isCoordinateDrag) { @@ -208,6 +266,7 @@ namespace XCharts.Runtime UpdateDataZoomRange(dataZoom, start, end); } } + public override void OnScroll(PointerEventData eventData) { if (chart == null) @@ -215,14 +274,14 @@ namespace XCharts.Runtime if (Input.touchCount > 1) return; - Vector2 pos; - if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) - return; - var dataZoom = component; if (!dataZoom.enable || dataZoom.zoomLock) return; + Vector2 pos; + if (!chart.ScreenPointToChartPoint(eventData.position, out pos)) + return; + var grid = chart.GetGridOfDataZoom(dataZoom); if ((dataZoom.supportInside && dataZoom.supportInsideScroll && grid.Contains(pos)) || dataZoom.IsInZoom(pos)) @@ -614,5 +673,13 @@ namespace XCharts.Runtime break; } } + + private void DrawMarquee(VertexHelper vh, DataZoom dataZoom) + { + if (!dataZoom.enable || !dataZoom.supportSelect) + return; + var areaColor = dataZoom.marqueeStyle.areaStyle.GetColor(chart.theme.dataZoom.dataAreaColor); + UGL.DrawRectangle(vh, dataZoom.context.marqueeRect, areaColor); + } } } \ No newline at end of file