namespace UIWidgets { using System; using System.Collections.Generic; using System.ComponentModel; using UIWidgets.Attributes; using UIWidgets.Styles; using UnityEngine; using UnityEngine.UI; /// /// Scale widget. /// [RequireComponent(typeof(RectTransform))] public class Scale : MonoBehaviourConditional, IObservable, INotifyPropertyChanged, IStylable { /// /// Mark data. /// public struct MarkData : IEquatable { /// /// Position. /// public readonly Vector2 Position; /// /// Rotation. /// public readonly float Rotation; /// /// Anchors min. /// public readonly Vector2 AnchorMin; /// /// Anchors max. /// public readonly Vector2 AnchorMax; /// /// Pivot. /// public readonly Vector2 Pivot; /// /// Value. /// public readonly float Value; /// /// Convert value to label. /// public readonly Func Value2Label; /// /// Label. /// public string Label { get { return Value2Label != null ? Value2Label(Value) : Value.ToString(); } } /// /// Initializes a new instance of the struct. /// /// Value. /// Position. /// Rotation. /// Anchors min. /// Anchors max. /// Pivot. /// Convert value to label. public MarkData( float value, Vector2 position = default(Vector2), float rotation = 0f, Vector2 anchorMin = default(Vector2), Vector2 anchorMax = default(Vector2), Vector2? pivot = null, Func value2label = null) { Value = value; Position = position; Rotation = rotation; AnchorMin = anchorMin; AnchorMax = anchorMax; Pivot = pivot.HasValue ? pivot.Value : new Vector2(0.5f, 0.5f); Value2Label = value2label; } /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) { if (obj is MarkData) { return Equals((MarkData)obj); } return false; } /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. public bool Equals(MarkData other) { return (Position == other.Position) && (Rotation == other.Rotation) && (AnchorMin == other.AnchorMin) && (AnchorMax == other.AnchorMax) && (Pivot == other.Pivot) && (Value == other.Value) && (Value2Label == other.Value2Label); } /// /// Hash function. /// /// A hash code for the current object. public override int GetHashCode() { return Position.GetHashCode() ^ Rotation.GetHashCode() ^ AnchorMin.GetHashCode() ^ AnchorMax.GetHashCode() ^ Pivot.GetHashCode() ^ Value.GetHashCode() ^ Value2Label.GetHashCode(); } /// /// Compare specified instances. /// /// First instance. /// Second instance. /// true if the instances are equal; otherwise, false. public static bool operator ==(MarkData a, MarkData b) { return a.Equals(b); } /// /// Compare specified instances. /// /// First instance. /// Second instance. /// true if the instances not equal; otherwise, false. public static bool operator !=(MarkData a, MarkData b) { return !a.Equals(b); } } [SerializeField] RectTransform container; /// /// Container. /// public RectTransform Container { get { return container; } set { if (container != value) { container = value; NotifyPropertyChanged("Container"); } } } [SerializeField] Image mainLine; /// /// Main line. /// public Image MainLine { get { return mainLine; } set { if (mainLine != value) { mainLine = value; NotifyPropertyChanged("MainLine"); } } } [SerializeField] bool showCurrentValue = true; /// /// Show current values. /// public bool ShowCurrentValue { get { return showCurrentValue; } set { if (showCurrentValue != value) { showCurrentValue = value; NotifyPropertyChanged("ShowCurrentValue"); } } } [SerializeField] [EditorConditionBool("showCurrentValue")] ScaleMarkTemplate currentMarkTemplate; /// /// Current mark template. /// public ScaleMarkTemplate CurrentMarkTemplate { get { return currentMarkTemplate; } set { if (currentMarkTemplate != value) { currentMarkTemplate = value; NotifyPropertyChanged("CurrentTemplate"); } } } [SerializeField] bool showMinValue = true; /// /// Show minimal value. /// public bool ShowMinValue { get { return showMinValue; } set { if (showMinValue != value) { showMinValue = value; NotifyPropertyChanged("ShowMinValue"); } } } [SerializeField] [EditorConditionBool("showMinValue")] ScaleMarkTemplate minMark; /// /// Min mark. /// public ScaleMarkTemplate MinMark { get { return minMark; } set { if (minMark != value) { minMark = value; NotifyPropertyChanged("MinTemplate"); } } } [SerializeField] bool showMaxValue = true; /// /// Show max value. /// public bool ShowMaxValue { get { return showMaxValue; } set { if (showMaxValue != value) { showMaxValue = value; NotifyPropertyChanged("ShowMaxValue"); } } } [SerializeField] [EditorConditionBool("showMaxValue")] ScaleMarkTemplate maxMark; /// /// Max mark. /// public ScaleMarkTemplate MaxMark { get { return maxMark; } set { if (maxMark != value) { maxMark = value; NotifyPropertyChanged("MaxTemplate"); } } } [SerializeField] List scaleMarks = new List(); ObservableList marks; /// /// Marks. /// public ObservableList Marks { get { if (marks == null) { marks = new ObservableList(scaleMarks); marks.Comparison = MarkComparison; } return marks; } set { if (marks != null) { marks.OnChange -= MarksChanged; } marks = value; if (marks != null) { marks.Comparison = MarkComparison; marks.OnChange += MarksChanged; } MarksChanged(); } } RectTransform rectTransform; /// /// RectTransform. /// public RectTransform RectTransform { get { if (rectTransform == null) { rectTransform = transform as RectTransform; } return rectTransform; } } bool isInited; /// /// Items comparison. /// /// First item. /// Second item. /// Result of the comparison. public static int MarkComparison(ScaleMark x, ScaleMark y) { return -x.Step.CompareTo(y.Step); } /// /// Process the start event. /// protected virtual void Start() { Init(); } /// /// Process the destroy event. /// protected virtual void OnDestroy() { Clear(); if (marks != null) { marks.OnChange -= MarksChanged; marks = null; } } /// /// Init this instance. /// public virtual void Init() { if (isInited) { return; } isInited = true; if (container == null) { container = transform as RectTransform; } if (currentMarkTemplate != null) { currentMarkTemplate.gameObject.SetActive(false); } if (minMark != null) { minMark.gameObject.SetActive(false); } if (maxMark != null) { maxMark.gameObject.SetActive(false); } if (mainLine != null) { mainLine.gameObject.SetActive(false); } for (int i = 0; i < Marks.Count; i++) { var mark = Marks[i]; if (mark.Step <= 0) { Debug.LogWarning("ScaleMark.Step cannot be negative or zero.", this); continue; } mark.Template.gameObject.SetActive(false); } foreach (var instance in MarksInstances) { if (instance != null) { instance.Return(); } } MarksInstances.Clear(); } /// /// Occurs when a property value changes. /// public event OnChange OnChange; /// /// Occurs when a property value changes. /// public event PropertyChangedEventHandler PropertyChanged; /// /// Mark values, temporary list. /// [HideInInspector] protected List MarkValues = new List(); /// /// Mark values generator. /// public Action> MarkValuesGenerator = DefaultGenerator; /// /// Used values. /// protected HashSet UsedValues = new HashSet(); /// /// Marks instances. /// [SerializeField] [HideInInspector] protected List MarksInstances = new List(); /// /// Properties tracker. /// protected DrivenRectTransformTracker PropertiesTracker; /// /// Driven properties. /// protected DrivenTransformProperties DrivenProperties = DrivenTransformProperties.AnchoredPosition | DrivenTransformProperties.Anchors | DrivenTransformProperties.Pivot | DrivenTransformProperties.Rotation; /// /// Raise PropertyChanged event. /// /// Property name. protected virtual void NotifyPropertyChanged(string propertyName) { var c_handlers = OnChange; if (c_handlers != null) { c_handlers(); } var handlers = PropertyChanged; if (handlers != null) { handlers(this, new PropertyChangedEventArgs(propertyName)); } } /// /// Process marks changed events. /// protected virtual void MarksChanged() { Clear(); NotifyPropertyChanged("Marks"); } /// /// Clear this instance. /// public virtual void Clear() { PropertiesTracker.Clear(); UsedValues.Clear(); if (CurrentMarkTemplate != null) { CurrentMarkTemplate.gameObject.SetActive(false); } if (MinMark != null) { MinMark.gameObject.SetActive(false); } if (MaxMark != null) { MaxMark.gameObject.SetActive(false); } foreach (var instance in MarksInstances) { if (instance != null) { instance.Return(); } } MarksInstances.Clear(); } /// /// Set min mark. /// /// Mark data. protected virtual void SetMin(MarkData mark) { if (MinMark == null) { return; } if (ShowMinValue) { MinMark.gameObject.SetActive(true); Set(MinMark, mark); } else { MinMark.gameObject.SetActive(false); } } /// /// Set max mark. /// /// Mark data. protected virtual void SetMax(MarkData mark) { if (MaxMark == null) { return; } if (ShowMaxValue) { MaxMark.gameObject.SetActive(true); Set(MaxMark, mark); } else { MaxMark.gameObject.SetActive(false); } } /// /// Set mark. /// /// Instance. /// Mark data. protected virtual void Set(ScaleMarkTemplate instance, MarkData mark) { var rt = instance.RectTransform; PropertiesTracker.Add(this, rt, DrivenProperties); rt.anchorMin = mark.AnchorMin; rt.anchorMax = mark.AnchorMax; rt.pivot = mark.Pivot; rt.anchoredPosition = mark.Position; rt.localRotation = Quaternion.Euler(0f, 0f, mark.Rotation); if (instance.Label != null) { instance.Label.text = mark.Label; var label_rt = instance.Label.transform as RectTransform; label_rt.localRotation = Quaternion.Euler(0f, 0f, -mark.Rotation); } } /// /// Default marks values generator. /// /// Min value. /// Max value. /// Step. /// Result. public static void DefaultGenerator(float min, float max, float step, List output) { for (var value = min; value < max; value += step) { output.Add(value); } } /// /// Set scale data. /// /// Convert value to mark data. /// Min value. /// Max value. /// Current values. public virtual void Set(Func value2mark, float min, float max, params float[] current) { Set(value2mark, min, max); if (ShowCurrentValue) { SetCurrent(value2mark, current); } } /// /// Set base scale data. /// /// Convert value to mark data. /// Min value. /// Max value. public virtual void Set(Func value2mark, float min, float max) { Init(); Clear(); if (MarkValuesGenerator == null) { MarkValuesGenerator = DefaultGenerator; } if (MainLine != null) { MainLine.gameObject.SetActive(true); } SetMin(value2mark(min)); if (ShowMinValue) { UsedValues.Add(min); } SetMax(value2mark(max)); if (ShowMinValue) { UsedValues.Add(max); } for (int i = 0; i < Marks.Count; i++) { var mark = Marks[i]; if (mark.Step <= 0) { continue; } MarkValues.Clear(); MarkValuesGenerator(min, max, mark.Step, MarkValues); foreach (var value in MarkValues) { if (UsedValues.Contains(value)) { continue; } var instance = mark.Template.GetInstance(Container); Set(instance, value2mark(value)); MarksInstances.Add(instance); UsedValues.Add(value); } MarkValues.Clear(); } } /// /// Set scale data. /// /// Convert value to mark data. /// Min value. /// Max value. /// Current value 0. public virtual void Set(Func value2mark, float min, float max, float current) { Set(value2mark, min, max); if (ShowCurrentValue) { SetCurrent(value2mark, current); } } /// /// Set scale data. /// /// Convert value to mark data. /// Min value. /// Max value. /// Current value 0. /// Current value 1. public virtual void Set(Func value2mark, float min, float max, float current0, float current1) { Set(value2mark, min, max); if (ShowCurrentValue) { SetCurrent(value2mark, current0); SetCurrent(value2mark, current1); } } /// /// Set scale data. /// /// Convert value to mark data. /// Min value. /// Max value. /// Current value 0. /// Current value 1. /// Current value 2. public virtual void Set(Func value2mark, float min, float max, float current0, float current1, float current2) { Set(value2mark, min, max); if (ShowCurrentValue) { SetCurrent(value2mark, current0); SetCurrent(value2mark, current1); SetCurrent(value2mark, current2); } } /// /// Set scale data. /// /// Convert value to mark data. /// Min value. /// Max value. /// Current value 0. /// Current value 1. /// Current value 2. /// Current value 3. public virtual void Set(Func value2mark, float min, float max, float current0, float current1, float current2, float current3) { Set(value2mark, min, max); if (ShowCurrentValue) { SetCurrent(value2mark, current0); SetCurrent(value2mark, current1); SetCurrent(value2mark, current2); SetCurrent(value2mark, current3); } } /// /// Set scale data. /// /// Convert value to mark data. /// Min value. /// Max value. /// Current value 0. /// Current value 1. /// Current value 2. /// Current value 3. /// Current value 4. public virtual void Set(Func value2mark, float min, float max, float current0, float current1, float current2, float current3, float current4) { Set(value2mark, min, max); if (ShowCurrentValue) { SetCurrent(value2mark, current0); SetCurrent(value2mark, current1); SetCurrent(value2mark, current2); SetCurrent(value2mark, current3); SetCurrent(value2mark, current4); } } /// /// Set current marks. /// /// Convert value to mark data. /// Current values. protected virtual void SetCurrent(Func value2mark, params float[] current) { foreach (var value in current) { SetCurrent(value2mark, value); } } /// /// Set current mark. /// /// Convert value to mark data. /// Current value. protected virtual void SetCurrent(Func value2mark, float current) { var instance = CurrentMarkTemplate.GetInstance(Container); Set(instance, value2mark(current)); MarksInstances.Add(instance); } #region IStylable implementation /// public virtual bool SetStyle(Style style) { style.Scale.MainLine.ApplyTo(MainLine); foreach (var instance in MarksInstances) { if (instance != null) { instance.SetStyle(style); } } if (currentMarkTemplate != null) { currentMarkTemplate.SetStyle(style); } if (minMark != null) { minMark.SetStyle(style); } if (maxMark != null) { maxMark.SetStyle(style); } if (isInited) { for (int i = 0; i < Marks.Count; i++) { Marks[i].Template.SetStyle(style); } } else { for (int i = 0; i < scaleMarks.Count; i++) { scaleMarks[i].Template.SetStyle(style); } } return true; } /// public virtual bool GetStyle(Style style) { style.Scale.MainLine.GetFrom(MainLine); if (currentMarkTemplate != null) { currentMarkTemplate.GetStyle(style); } else if (minMark != null) { minMark.GetStyle(style); } else if (maxMark != null) { maxMark.GetStyle(style); } else { for (int i = 0; i < scaleMarks.Count; i++) { scaleMarks[i].Template.GetStyle(style); } } return true; } #endregion } }