mirror of
https://github.com/RHeavenStudio/HeavenStudio.git
synced 2025-06-12 12:07:41 +02:00
Editor stuff
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82ea50ac9a6ea8940a71f86ea8d13bf0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,61 @@
|
||||
///Credit ChoMPHi
|
||||
///Sourced from - http://forum.unity3d.com/threads/accordion-type-layout.271818/
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(HorizontalOrVerticalLayoutGroup), typeof(ContentSizeFitter), typeof(ToggleGroup))]
|
||||
[AddComponentMenu("UI/Extensions/Accordion/Accordion Group")]
|
||||
public class Accordion : MonoBehaviour
|
||||
{
|
||||
private bool m_expandVertical = true;
|
||||
[HideInInspector]
|
||||
public bool ExpandVerticval => m_expandVertical;
|
||||
|
||||
public enum Transition
|
||||
{
|
||||
Instant,
|
||||
Tween
|
||||
}
|
||||
|
||||
[SerializeField] private Transition m_Transition = Transition.Instant;
|
||||
[SerializeField] private float m_TransitionDuration = 0.3f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transition.
|
||||
/// </summary>
|
||||
/// <value>The transition.</value>
|
||||
public Transition transition
|
||||
{
|
||||
get { return this.m_Transition; }
|
||||
set { this.m_Transition = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration of the transition.
|
||||
/// </summary>
|
||||
/// <value>The duration of the transition.</value>
|
||||
public float transitionDuration
|
||||
{
|
||||
get { return this.m_TransitionDuration; }
|
||||
set { this.m_TransitionDuration = value; }
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_expandVertical = GetComponent<HorizontalLayoutGroup>() ? false : true;
|
||||
var group = GetComponent<ToggleGroup>();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (!GetComponent<HorizontalLayoutGroup>() && !GetComponent<VerticalLayoutGroup>())
|
||||
{
|
||||
Debug.LogError("Accordion requires either a Horizontal or Vertical Layout group to place children");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4387cc9950f37044c92f9d144a2b1002
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,218 @@
|
||||
///Credit ChoMPHi
|
||||
///Sourced from - http://forum.unity3d.com/threads/accordion-type-layout.271818/
|
||||
|
||||
using System;
|
||||
using UnityEngine.UI.Extensions.Tweens;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(RectTransform), typeof(LayoutElement))]
|
||||
[AddComponentMenu("UI/Extensions/Accordion/Accordion Element")]
|
||||
public class AccordionElement : Toggle
|
||||
{
|
||||
|
||||
[SerializeField] private float m_MinHeight = 18f;
|
||||
|
||||
public float MinHeight => m_MinHeight;
|
||||
|
||||
[SerializeField] private float m_MinWidth = 40f;
|
||||
|
||||
public float MinWidth => m_MinWidth;
|
||||
|
||||
private Accordion m_Accordion;
|
||||
private RectTransform m_RectTransform;
|
||||
private LayoutElement m_LayoutElement;
|
||||
|
||||
[NonSerialized]
|
||||
private readonly TweenRunner<FloatTween> m_FloatTweenRunner;
|
||||
|
||||
protected AccordionElement()
|
||||
{
|
||||
if (this.m_FloatTweenRunner == null)
|
||||
this.m_FloatTweenRunner = new TweenRunner<FloatTween>();
|
||||
|
||||
this.m_FloatTweenRunner.Init(this);
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
base.transition = Transition.None;
|
||||
base.toggleTransition = ToggleTransition.None;
|
||||
this.m_Accordion = this.gameObject.GetComponentInParent<Accordion>();
|
||||
this.m_RectTransform = this.transform as RectTransform;
|
||||
this.m_LayoutElement = this.gameObject.GetComponent<LayoutElement>();
|
||||
this.onValueChanged.AddListener(OnValueChanged);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
this.m_Accordion = this.gameObject.GetComponentInParent<Accordion>();
|
||||
|
||||
if (this.group == null)
|
||||
{
|
||||
ToggleGroup tg = this.GetComponentInParent<ToggleGroup>();
|
||||
|
||||
if (tg != null)
|
||||
{
|
||||
this.group = tg;
|
||||
}
|
||||
}
|
||||
|
||||
LayoutElement le = this.gameObject.GetComponent<LayoutElement>();
|
||||
|
||||
if (le != null && m_Accordion != null)
|
||||
{
|
||||
if (this.isOn)
|
||||
{
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
le.preferredHeight = -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
le.preferredWidth = -1f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
le.preferredHeight = this.m_MinHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
le.preferredWidth = this.m_MinWidth;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void OnValueChanged(bool state)
|
||||
{
|
||||
if (this.m_LayoutElement == null)
|
||||
return;
|
||||
|
||||
Accordion.Transition transition = (this.m_Accordion != null) ? this.m_Accordion.transition : Accordion.Transition.Instant;
|
||||
|
||||
if (transition == Accordion.Transition.Instant && m_Accordion != null)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
this.m_LayoutElement.preferredHeight = -1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_LayoutElement.preferredWidth = -1f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
this.m_LayoutElement.preferredHeight = this.m_MinHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_LayoutElement.preferredWidth = this.m_MinWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (transition == Accordion.Transition.Tween)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
this.StartTween(this.m_MinHeight, this.GetExpandedHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.StartTween(this.m_MinWidth, this.GetExpandedWidth());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
this.StartTween(this.m_RectTransform.rect.height, this.m_MinHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.StartTween(this.m_RectTransform.rect.width, this.m_MinWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float GetExpandedHeight()
|
||||
{
|
||||
if (this.m_LayoutElement == null)
|
||||
return this.m_MinHeight;
|
||||
|
||||
float originalPrefH = this.m_LayoutElement.preferredHeight;
|
||||
this.m_LayoutElement.preferredHeight = -1f;
|
||||
float h = LayoutUtility.GetPreferredHeight(this.m_RectTransform);
|
||||
this.m_LayoutElement.preferredHeight = originalPrefH;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
protected float GetExpandedWidth()
|
||||
{
|
||||
if (this.m_LayoutElement == null)
|
||||
return this.m_MinWidth;
|
||||
|
||||
float originalPrefW = this.m_LayoutElement.preferredWidth;
|
||||
this.m_LayoutElement.preferredWidth = -1f;
|
||||
float w = LayoutUtility.GetPreferredWidth(this.m_RectTransform);
|
||||
this.m_LayoutElement.preferredWidth = originalPrefW;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
protected void StartTween(float startFloat, float targetFloat)
|
||||
{
|
||||
float duration = (this.m_Accordion != null) ? this.m_Accordion.transitionDuration : 0.3f;
|
||||
|
||||
FloatTween info = new FloatTween
|
||||
{
|
||||
duration = duration,
|
||||
startFloat = startFloat,
|
||||
targetFloat = targetFloat
|
||||
};
|
||||
if (m_Accordion.ExpandVerticval)
|
||||
{
|
||||
info.AddOnChangedCallback(SetHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.AddOnChangedCallback(SetWidth);
|
||||
}
|
||||
info.ignoreTimeScale = true;
|
||||
this.m_FloatTweenRunner.StartTween(info);
|
||||
}
|
||||
|
||||
protected void SetHeight(float height)
|
||||
{
|
||||
if (this.m_LayoutElement == null)
|
||||
return;
|
||||
|
||||
this.m_LayoutElement.preferredHeight = height;
|
||||
}
|
||||
|
||||
protected void SetWidth(float width)
|
||||
{
|
||||
if (this.m_LayoutElement == null)
|
||||
return;
|
||||
|
||||
this.m_LayoutElement.preferredWidth = width;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c9d341c4bc7de548937508e6f837144
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9115417252d4cd42a5e167bdc1c2d3b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,120 @@
|
||||
///Credit ChoMPHi
|
||||
///Sourced from - http://forum.unity3d.com/threads/accordion-type-layout.271818/
|
||||
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.Tweens
|
||||
{
|
||||
public struct FloatTween : ITweenValue
|
||||
{
|
||||
public class FloatTweenCallback : UnityEvent<float> {}
|
||||
public class FloatFinishCallback : UnityEvent {}
|
||||
|
||||
private float m_StartFloat;
|
||||
private float m_TargetFloat;
|
||||
private float m_Duration;
|
||||
private bool m_IgnoreTimeScale;
|
||||
private FloatTweenCallback m_Target;
|
||||
private FloatFinishCallback m_Finish;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the starting float.
|
||||
/// </summary>
|
||||
/// <value>The start float.</value>
|
||||
public float startFloat
|
||||
{
|
||||
get { return m_StartFloat; }
|
||||
set { m_StartFloat = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the target float.
|
||||
/// </summary>
|
||||
/// <value>The target float.</value>
|
||||
public float targetFloat
|
||||
{
|
||||
get { return m_TargetFloat; }
|
||||
set { m_TargetFloat = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration of the tween.
|
||||
/// </summary>
|
||||
/// <value>The duration.</value>
|
||||
public float duration
|
||||
{
|
||||
get { return m_Duration; }
|
||||
set { m_Duration = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="UnityEngine.UI.Tweens.ColorTween"/> should ignore time scale.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if ignore time scale; otherwise, <c>false</c>.</value>
|
||||
public bool ignoreTimeScale
|
||||
{
|
||||
get { return m_IgnoreTimeScale; }
|
||||
set { m_IgnoreTimeScale = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tweens the float based on percentage.
|
||||
/// </summary>
|
||||
/// <param name="floatPercentage">Float percentage.</param>
|
||||
public void TweenValue(float floatPercentage)
|
||||
{
|
||||
if (!ValidTarget())
|
||||
return;
|
||||
|
||||
m_Target.Invoke( Mathf.Lerp (m_StartFloat, m_TargetFloat, floatPercentage) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a on changed callback.
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback.</param>
|
||||
public void AddOnChangedCallback(UnityAction<float> callback)
|
||||
{
|
||||
if (m_Target == null)
|
||||
m_Target = new FloatTweenCallback();
|
||||
|
||||
m_Target.AddListener(callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a on finish callback.
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback.</param>
|
||||
public void AddOnFinishCallback(UnityAction callback)
|
||||
{
|
||||
if (m_Finish == null)
|
||||
m_Finish = new FloatFinishCallback();
|
||||
|
||||
m_Finish.AddListener(callback);
|
||||
}
|
||||
|
||||
public bool GetIgnoreTimescale()
|
||||
{
|
||||
return m_IgnoreTimeScale;
|
||||
}
|
||||
|
||||
public float GetDuration()
|
||||
{
|
||||
return m_Duration;
|
||||
}
|
||||
|
||||
public bool ValidTarget()
|
||||
{
|
||||
return m_Target != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the on finish callback.
|
||||
/// </summary>
|
||||
public void Finished()
|
||||
{
|
||||
if (m_Finish != null)
|
||||
m_Finish.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e28e1e9e18f7daa4db5d1ac279d6ce66
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,15 @@
|
||||
///Credit ChoMPHi
|
||||
///Sourced from - http://forum.unity3d.com/threads/accordion-type-layout.271818/
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions.Tweens
|
||||
{
|
||||
internal interface ITweenValue
|
||||
{
|
||||
void TweenValue(float floatPercentage);
|
||||
bool ignoreTimeScale { get; }
|
||||
float duration { get; }
|
||||
bool ValidTarget();
|
||||
void Finished();
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9edf9da4c14ad2843879a2331b00f738
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,63 @@
|
||||
///Credit ChoMPHi
|
||||
///Sourced from - http://forum.unity3d.com/threads/accordion-type-layout.271818/
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.Tweens
|
||||
{
|
||||
// Tween runner, executes the given tween.
|
||||
// The coroutine will live within the given
|
||||
// behaviour container.
|
||||
internal class TweenRunner<T> where T : struct, ITweenValue
|
||||
{
|
||||
protected MonoBehaviour m_CoroutineContainer;
|
||||
protected IEnumerator m_Tween;
|
||||
|
||||
// utility function for starting the tween
|
||||
private static IEnumerator Start(T tweenInfo)
|
||||
{
|
||||
if (!tweenInfo.ValidTarget())
|
||||
yield break;
|
||||
|
||||
float elapsedTime = 0.0f;
|
||||
while (elapsedTime < tweenInfo.duration)
|
||||
{
|
||||
elapsedTime += tweenInfo.ignoreTimeScale ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
var percentage = Mathf.Clamp01 (elapsedTime / tweenInfo.duration);
|
||||
tweenInfo.TweenValue (percentage);
|
||||
yield return null;
|
||||
}
|
||||
tweenInfo.TweenValue (1.0f);
|
||||
tweenInfo.Finished();
|
||||
}
|
||||
|
||||
public void Init(MonoBehaviour coroutineContainer)
|
||||
{
|
||||
m_CoroutineContainer = coroutineContainer;
|
||||
}
|
||||
|
||||
public void StartTween(T info)
|
||||
{
|
||||
if (m_CoroutineContainer == null)
|
||||
{
|
||||
Debug.LogWarning ("Coroutine container not configured... did you forget to call Init?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Tween != null)
|
||||
{
|
||||
m_CoroutineContainer.StopCoroutine (m_Tween);
|
||||
m_Tween = null;
|
||||
}
|
||||
|
||||
if (!m_CoroutineContainer.gameObject.activeInHierarchy)
|
||||
{
|
||||
info.TweenValue(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Tween = Start (info);
|
||||
m_CoroutineContainer.StartCoroutine (m_Tween);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d00ab96853b24074cb837ee70f07dddc
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,371 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[AddComponentMenu("UI/Extensions/BoxSlider")]
|
||||
public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
|
||||
{
|
||||
public enum Direction
|
||||
{
|
||||
LeftToRight,
|
||||
RightToLeft,
|
||||
BottomToTop,
|
||||
TopToBottom,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class BoxSliderEvent : UnityEvent<float, float> { }
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_HandleRect;
|
||||
public RectTransform HandleRect { get { return m_HandleRect; } set { if (SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
|
||||
|
||||
[Space(6)]
|
||||
|
||||
[SerializeField]
|
||||
private float m_MinValue = 0;
|
||||
public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private float m_MaxValue = 1;
|
||||
public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private bool m_WholeNumbers = false;
|
||||
public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private float m_ValueX = 1f;
|
||||
public float ValueX
|
||||
{
|
||||
get
|
||||
{
|
||||
if (WholeNumbers)
|
||||
return Mathf.Round(m_ValueX);
|
||||
return m_ValueX;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetX(value);
|
||||
}
|
||||
}
|
||||
|
||||
public float NormalizedValueX
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Approximately(MinValue, MaxValue))
|
||||
return 0;
|
||||
return Mathf.InverseLerp(MinValue, MaxValue, ValueX);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.ValueX = Mathf.Lerp(MinValue, MaxValue, value);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private float m_ValueY = 1f;
|
||||
public float ValueY
|
||||
{
|
||||
get
|
||||
{
|
||||
if (WholeNumbers)
|
||||
return Mathf.Round(m_ValueY);
|
||||
return m_ValueY;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetY(value);
|
||||
}
|
||||
}
|
||||
|
||||
public float NormalizedValueY
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Approximately(MinValue, MaxValue))
|
||||
return 0;
|
||||
return Mathf.InverseLerp(MinValue, MaxValue, ValueY);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.ValueY = Mathf.Lerp(MinValue, MaxValue, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Space(6)]
|
||||
|
||||
// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
|
||||
[SerializeField]
|
||||
private BoxSliderEvent m_OnValueChanged = new BoxSliderEvent();
|
||||
public BoxSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
|
||||
|
||||
// Private fields
|
||||
|
||||
private Transform m_HandleTransform;
|
||||
private RectTransform m_HandleContainerRect;
|
||||
|
||||
// The offset from handle position to mouse down position
|
||||
private Vector2 m_Offset = Vector2.zero;
|
||||
|
||||
private DrivenRectTransformTracker m_Tracker;
|
||||
|
||||
// Size of each step.
|
||||
float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } }
|
||||
|
||||
protected BoxSlider()
|
||||
{ }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
if (WholeNumbers)
|
||||
{
|
||||
m_MinValue = Mathf.Round(m_MinValue);
|
||||
m_MaxValue = Mathf.Round(m_MaxValue);
|
||||
}
|
||||
UpdateCachedReferences();
|
||||
SetX(m_ValueX, false);
|
||||
SetY(m_ValueY, false);
|
||||
// Update rects since other things might affect them even if value didn't change.
|
||||
if(!Application.isPlaying) UpdateVisuals();
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
if (!Application.isPlaying)
|
||||
#else
|
||||
var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
|
||||
if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // if UNITY_EDITOR
|
||||
|
||||
public virtual void Rebuild(CanvasUpdate executing)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (executing == CanvasUpdate.Prelayout)
|
||||
OnValueChanged.Invoke(ValueX, ValueY);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void LayoutComplete()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void GraphicUpdateComplete()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
|
||||
{
|
||||
if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
|
||||
return false;
|
||||
|
||||
currentValue = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
|
||||
{
|
||||
if (currentValue.Equals(newValue))
|
||||
return false;
|
||||
|
||||
currentValue = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
UpdateCachedReferences();
|
||||
SetX(m_ValueX, false);
|
||||
SetY(m_ValueY, false);
|
||||
// Update rects since they need to be initialized correctly.
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
m_Tracker.Clear();
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
void UpdateCachedReferences()
|
||||
{
|
||||
|
||||
if (m_HandleRect)
|
||||
{
|
||||
m_HandleTransform = m_HandleRect.transform;
|
||||
if (m_HandleTransform.parent != null)
|
||||
m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HandleContainerRect = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the valueUpdate the visible Image.
|
||||
void SetX(float input)
|
||||
{
|
||||
SetX(input, true);
|
||||
}
|
||||
|
||||
void SetX(float input, bool sendCallback)
|
||||
{
|
||||
// Clamp the input
|
||||
float newValue = Mathf.Clamp(input, MinValue, MaxValue);
|
||||
if (WholeNumbers)
|
||||
newValue = Mathf.Round(newValue);
|
||||
|
||||
// If the stepped value doesn't match the last one, it's time to update
|
||||
if (m_ValueX == newValue)
|
||||
return;
|
||||
|
||||
m_ValueX = newValue;
|
||||
UpdateVisuals();
|
||||
if (sendCallback)
|
||||
m_OnValueChanged.Invoke(newValue, ValueY);
|
||||
}
|
||||
|
||||
void SetY(float input)
|
||||
{
|
||||
SetY(input, true);
|
||||
}
|
||||
|
||||
void SetY(float input, bool sendCallback)
|
||||
{
|
||||
// Clamp the input
|
||||
float newValue = Mathf.Clamp(input, MinValue, MaxValue);
|
||||
if (WholeNumbers)
|
||||
newValue = Mathf.Round(newValue);
|
||||
|
||||
// If the stepped value doesn't match the last one, it's time to update
|
||||
if (m_ValueY == newValue)
|
||||
return;
|
||||
|
||||
m_ValueY = newValue;
|
||||
UpdateVisuals();
|
||||
if (sendCallback)
|
||||
m_OnValueChanged.Invoke(ValueX, newValue);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnRectTransformDimensionsChange()
|
||||
{
|
||||
base.OnRectTransformDimensionsChange();
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
enum Axis
|
||||
{
|
||||
Horizontal = 0,
|
||||
Vertical = 1
|
||||
}
|
||||
|
||||
|
||||
// Force-update the slider. Useful if you've changed the properties and want it to update visually.
|
||||
private void UpdateVisuals()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
UpdateCachedReferences();
|
||||
#endif
|
||||
|
||||
m_Tracker.Clear();
|
||||
|
||||
|
||||
//to business!
|
||||
if (m_HandleContainerRect != null)
|
||||
{
|
||||
m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
|
||||
Vector2 anchorMin = Vector2.zero;
|
||||
Vector2 anchorMax = Vector2.one;
|
||||
anchorMin[0] = anchorMax[0] = (NormalizedValueX);
|
||||
anchorMin[1] = anchorMax[1] = (NormalizedValueY);
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
m_HandleRect.anchorMin = anchorMin;
|
||||
m_HandleRect.anchorMax = anchorMax;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Update the slider's position based on the mouse.
|
||||
void UpdateDrag(PointerEventData eventData, Camera cam)
|
||||
{
|
||||
RectTransform clickRect = m_HandleContainerRect;
|
||||
if (clickRect != null && clickRect.rect.size[0] > 0)
|
||||
{
|
||||
Vector2 localCursor;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
|
||||
return;
|
||||
localCursor -= clickRect.rect.position;
|
||||
|
||||
float val = Mathf.Clamp01((localCursor - m_Offset)[0] / clickRect.rect.size[0]);
|
||||
NormalizedValueX = (val);
|
||||
|
||||
float valY = Mathf.Clamp01((localCursor - m_Offset)[1] / clickRect.rect.size[1]);
|
||||
NormalizedValueY = (valY);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanDrag(PointerEventData eventData)
|
||||
{
|
||||
return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if (!CanDrag(eventData))
|
||||
return;
|
||||
|
||||
base.OnPointerDown(eventData);
|
||||
|
||||
m_Offset = Vector2.zero;
|
||||
if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
|
||||
{
|
||||
Vector2 localMousePos;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
|
||||
m_Offset = localMousePos;
|
||||
m_Offset.y = -m_Offset.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outside the slider handle - jump to this point instead
|
||||
UpdateDrag(eventData, eventData.pressEventCamera);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!CanDrag(eventData))
|
||||
return;
|
||||
|
||||
UpdateDrag(eventData, eventData.pressEventCamera);
|
||||
}
|
||||
|
||||
public virtual void OnInitializePotentialDrag(PointerEventData eventData)
|
||||
{
|
||||
eventData.useDragThreshold = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3fd655d1aa9c684d88cdfdd0da9aa34
|
||||
timeCreated: 1480011174
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ab63fe1b000c254ebed3937038bb01b
|
||||
folderAsset: yes
|
||||
timeCreated: 1480011199
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,30 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
[RequireComponent(typeof(Image))]
|
||||
public class ColorImage : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl picker;
|
||||
|
||||
private Image image;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
image = GetComponent<Image>();
|
||||
picker.onValueChanged.AddListener(ColorChanged);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
picker.onValueChanged.RemoveListener(ColorChanged);
|
||||
}
|
||||
|
||||
private void ColorChanged(Color newColor)
|
||||
{
|
||||
image.color = newColor;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4210a64d2ce72e9488cf2ad174e9df5b
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,87 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(Text))]
|
||||
public class ColorLabel : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl picker;
|
||||
|
||||
public ColorValues type;
|
||||
|
||||
public string prefix = "R: ";
|
||||
public float minValue = 0;
|
||||
public float maxValue = 255;
|
||||
|
||||
public int precision = 0;
|
||||
|
||||
private Text label;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
label = GetComponent<Text>();
|
||||
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (Application.isPlaying && picker != null)
|
||||
{
|
||||
picker.onValueChanged.AddListener(ColorChanged);
|
||||
picker.onHSVChanged.AddListener(HSVChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (picker != null)
|
||||
{
|
||||
picker.onValueChanged.RemoveListener(ColorChanged);
|
||||
picker.onHSVChanged.RemoveListener(HSVChanged);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
label = GetComponent<Text>();
|
||||
UpdateValue();
|
||||
}
|
||||
#endif
|
||||
|
||||
private void ColorChanged(Color color)
|
||||
{
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
private void HSVChanged(float hue, float sateration, float value)
|
||||
{
|
||||
UpdateValue();
|
||||
}
|
||||
|
||||
private void UpdateValue()
|
||||
{
|
||||
if (picker == null)
|
||||
{
|
||||
label.text = prefix + "-";
|
||||
}
|
||||
else
|
||||
{
|
||||
float value = minValue + (picker.GetValue(type) * (maxValue - minValue));
|
||||
|
||||
label.text = prefix + ConvertToDisplayString(value);
|
||||
}
|
||||
}
|
||||
|
||||
private string ConvertToDisplayString(float value)
|
||||
{
|
||||
if (precision > 0)
|
||||
return value.ToString("f " + precision);
|
||||
else
|
||||
return Mathf.FloorToInt(value).ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b32edf53f1ae84f479c9439b4b5f5b91
|
||||
timeCreated: 1480011174
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,303 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
public class ColorPickerControl : MonoBehaviour
|
||||
{
|
||||
private float _hue = 0;
|
||||
private float _saturation = 0;
|
||||
private float _brightness = 0;
|
||||
|
||||
private float _red = 0;
|
||||
private float _green = 0;
|
||||
private float _blue = 0;
|
||||
|
||||
private float _alpha = 1;
|
||||
|
||||
public ColorChangedEvent onValueChanged = new ColorChangedEvent();
|
||||
public HSVChangedEvent onHSVChanged = new HSVChangedEvent();
|
||||
|
||||
[SerializeField]
|
||||
bool hsvSlidersOn = true;
|
||||
|
||||
[SerializeField]
|
||||
List<GameObject> hsvSliders = new List<GameObject>();
|
||||
|
||||
[SerializeField]
|
||||
bool rgbSlidersOn = true;
|
||||
|
||||
[SerializeField]
|
||||
List<GameObject> rgbSliders = new List<GameObject>();
|
||||
|
||||
[SerializeField]
|
||||
GameObject alphaSlider = null;
|
||||
|
||||
public void SetHSVSlidersOn(bool value)
|
||||
{
|
||||
hsvSlidersOn = value;
|
||||
|
||||
foreach (var item in hsvSliders)
|
||||
item.SetActive(value);
|
||||
|
||||
if (alphaSlider)
|
||||
alphaSlider.SetActive(hsvSlidersOn || rgbSlidersOn);
|
||||
}
|
||||
|
||||
public void SetRGBSlidersOn(bool value)
|
||||
{
|
||||
rgbSlidersOn = value;
|
||||
foreach (var item in rgbSliders)
|
||||
item.SetActive(value);
|
||||
|
||||
if (alphaSlider)
|
||||
alphaSlider.SetActive(hsvSlidersOn || rgbSlidersOn);
|
||||
}
|
||||
|
||||
|
||||
void Update()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SetHSVSlidersOn(hsvSlidersOn);
|
||||
SetRGBSlidersOn(rgbSlidersOn);
|
||||
#endif
|
||||
}
|
||||
|
||||
public Color CurrentColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Color(_red, _green, _blue, _alpha);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (CurrentColor == value)
|
||||
return;
|
||||
|
||||
_red = value.r;
|
||||
_green = value.g;
|
||||
_blue = value.b;
|
||||
_alpha = value.a;
|
||||
|
||||
RGBChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
SendChangedEvent();
|
||||
}
|
||||
|
||||
public float H
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hue;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_hue == value)
|
||||
return;
|
||||
|
||||
_hue = value;
|
||||
|
||||
HSVChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public float S
|
||||
{
|
||||
get
|
||||
{
|
||||
return _saturation;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_saturation == value)
|
||||
return;
|
||||
|
||||
_saturation = value;
|
||||
|
||||
HSVChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public float V
|
||||
{
|
||||
get
|
||||
{
|
||||
return _brightness;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_brightness == value)
|
||||
return;
|
||||
|
||||
_brightness = value;
|
||||
|
||||
HSVChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public float R
|
||||
{
|
||||
get
|
||||
{
|
||||
return _red;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_red == value)
|
||||
return;
|
||||
|
||||
_red = value;
|
||||
|
||||
RGBChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public float G
|
||||
{
|
||||
get
|
||||
{
|
||||
return _green;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_green == value)
|
||||
return;
|
||||
|
||||
_green = value;
|
||||
|
||||
RGBChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public float B
|
||||
{
|
||||
get
|
||||
{
|
||||
return _blue;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_blue == value)
|
||||
return;
|
||||
|
||||
_blue = value;
|
||||
|
||||
RGBChanged();
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private float A
|
||||
{
|
||||
get
|
||||
{
|
||||
return _alpha;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_alpha == value)
|
||||
return;
|
||||
|
||||
_alpha = value;
|
||||
|
||||
SendChangedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private void RGBChanged()
|
||||
{
|
||||
HsvColor color = HSVUtil.ConvertRgbToHsv(CurrentColor);
|
||||
|
||||
_hue = color.NormalizedH;
|
||||
_saturation = color.NormalizedS;
|
||||
_brightness = color.NormalizedV;
|
||||
}
|
||||
|
||||
private void HSVChanged()
|
||||
{
|
||||
Color color = HSVUtil.ConvertHsvToRgb(_hue * 360, _saturation, _brightness, _alpha);
|
||||
|
||||
_red = color.r;
|
||||
_green = color.g;
|
||||
_blue = color.b;
|
||||
}
|
||||
|
||||
private void SendChangedEvent()
|
||||
{
|
||||
onValueChanged.Invoke(CurrentColor);
|
||||
onHSVChanged.Invoke(_hue, _saturation, _brightness);
|
||||
}
|
||||
|
||||
public void AssignColor(ColorValues type, float value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
R = value;
|
||||
break;
|
||||
case ColorValues.G:
|
||||
G = value;
|
||||
break;
|
||||
case ColorValues.B:
|
||||
B = value;
|
||||
break;
|
||||
case ColorValues.A:
|
||||
A = value;
|
||||
break;
|
||||
case ColorValues.Hue:
|
||||
H = value;
|
||||
break;
|
||||
case ColorValues.Saturation:
|
||||
S = value;
|
||||
break;
|
||||
case ColorValues.Value:
|
||||
V = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetValue(ColorValues type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
return R;
|
||||
case ColorValues.G:
|
||||
return G;
|
||||
case ColorValues.B:
|
||||
return B;
|
||||
case ColorValues.A:
|
||||
return A;
|
||||
case ColorValues.Hue:
|
||||
return H;
|
||||
case ColorValues.Saturation:
|
||||
return S;
|
||||
case ColorValues.Value:
|
||||
return V;
|
||||
default:
|
||||
throw new System.NotImplementedException("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e448480810b55843aefa91c1ab74cd2
|
||||
timeCreated: 1483197306
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,194 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
public class ColorPickerPresets : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl picker;
|
||||
|
||||
[SerializeField]
|
||||
protected GameObject presetPrefab;
|
||||
|
||||
[SerializeField]
|
||||
protected int maxPresets = 16;
|
||||
|
||||
[SerializeField]
|
||||
protected Color[] predefinedPresets;
|
||||
|
||||
protected List<Color> presets = new List<Color>();
|
||||
public Image createPresetImage;
|
||||
public Transform createButton;
|
||||
|
||||
public enum SaveType { None, PlayerPrefs, JsonFile }
|
||||
[SerializeField]
|
||||
public SaveType saveMode = SaveType.None;
|
||||
|
||||
[SerializeField]
|
||||
protected string playerPrefsKey;
|
||||
|
||||
public virtual string JsonFilePath
|
||||
{
|
||||
get { return Application.persistentDataPath + "/" + playerPrefsKey + ".json"; }
|
||||
}
|
||||
|
||||
protected virtual void Reset()
|
||||
{
|
||||
playerPrefsKey = "colorpicker_" + GetInstanceID().ToString();
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
picker.onHSVChanged.AddListener(HSVChanged);
|
||||
picker.onValueChanged.AddListener(ColorChanged);
|
||||
picker.CurrentColor = Color.white;
|
||||
presetPrefab.SetActive(false);
|
||||
|
||||
presets.AddRange(predefinedPresets);
|
||||
LoadPresets(saveMode);
|
||||
}
|
||||
|
||||
public virtual void CreatePresetButton()
|
||||
{
|
||||
CreatePreset(picker.CurrentColor);
|
||||
}
|
||||
|
||||
public virtual void LoadPresets(SaveType saveType)
|
||||
{
|
||||
string jsonData = "";
|
||||
switch (saveType)
|
||||
{
|
||||
case SaveType.None:
|
||||
break;
|
||||
case SaveType.PlayerPrefs:
|
||||
if (PlayerPrefs.HasKey(playerPrefsKey))
|
||||
{
|
||||
jsonData = PlayerPrefs.GetString(playerPrefsKey);
|
||||
}
|
||||
break;
|
||||
case SaveType.JsonFile:
|
||||
if (System.IO.File.Exists(JsonFilePath))
|
||||
{
|
||||
jsonData = System.IO.File.ReadAllText(JsonFilePath);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new System.NotImplementedException(saveType.ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(jsonData))
|
||||
{
|
||||
try
|
||||
{
|
||||
var jsonColors = JsonUtility.FromJson<JsonColor>(jsonData);
|
||||
presets.AddRange(jsonColors.GetColors());
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in presets)
|
||||
{
|
||||
CreatePreset(item, true);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SavePresets(SaveType saveType)
|
||||
{
|
||||
if (presets == null || presets.Count <= 0)
|
||||
{
|
||||
Debug.LogError(
|
||||
"presets cannot be null or empty: " + (presets == null ? "NULL" : "EMPTY"));
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonColor = new JsonColor();
|
||||
jsonColor.SetColors(presets.ToArray());
|
||||
|
||||
|
||||
string jsonData = JsonUtility.ToJson(jsonColor);
|
||||
|
||||
switch (saveType)
|
||||
{
|
||||
case SaveType.None:
|
||||
Debug.LogWarning("Called SavePresets with SaveType = None...");
|
||||
break;
|
||||
case SaveType.PlayerPrefs:
|
||||
PlayerPrefs.SetString(playerPrefsKey, jsonData);
|
||||
break;
|
||||
case SaveType.JsonFile:
|
||||
System.IO.File.WriteAllText(JsonFilePath, jsonData);
|
||||
//Application.OpenURL(JsonFilePath);
|
||||
break;
|
||||
default:
|
||||
throw new System.NotImplementedException(saveType.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
protected class JsonColor
|
||||
{
|
||||
public Color32[] colors;
|
||||
public void SetColors(Color[] colorsIn)
|
||||
{
|
||||
this.colors = new Color32[colorsIn.Length];
|
||||
for (int i = 0; i < colorsIn.Length; i++)
|
||||
{
|
||||
this.colors[i] = colorsIn[i];
|
||||
}
|
||||
}
|
||||
|
||||
public Color[] GetColors()
|
||||
{
|
||||
Color[] colorsOut = new Color[colors.Length];
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
colorsOut[i] = colors[i];
|
||||
}
|
||||
return colorsOut;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void CreatePreset(Color color, bool loading)
|
||||
{
|
||||
createButton.gameObject.SetActive(presets.Count < maxPresets);
|
||||
|
||||
var newPresetButton = Instantiate(presetPrefab, presetPrefab.transform.parent);
|
||||
newPresetButton.transform.SetAsLastSibling();
|
||||
newPresetButton.SetActive(true);
|
||||
newPresetButton.GetComponent<Image>().color = color;
|
||||
|
||||
createPresetImage.color = Color.white;
|
||||
|
||||
if (!loading)
|
||||
{
|
||||
presets.Add(color);
|
||||
SavePresets(saveMode);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void CreatePreset(Color color)
|
||||
{
|
||||
CreatePreset(color, false);
|
||||
}
|
||||
|
||||
public virtual void PresetSelect(Image sender)
|
||||
{
|
||||
picker.CurrentColor = sender.color;
|
||||
}
|
||||
|
||||
protected virtual void HSVChanged(float h, float s, float v)
|
||||
{
|
||||
createPresetImage.color = HSVUtil.ConvertHsvToRgb(h * 360, s, v, 1);
|
||||
//Debug.Log("hsv util color: " + createPresetImage.color);
|
||||
}
|
||||
|
||||
protected virtual void ColorChanged(Color color)
|
||||
{
|
||||
createPresetImage.color = color;
|
||||
//Debug.Log("color changed: " + color);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 502205da447ca7a479ce5ae45e5c19d2
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,24 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
public class ColorPickerTester : MonoBehaviour
|
||||
{
|
||||
public Renderer pickerRenderer;
|
||||
public ColorPickerControl picker;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
pickerRenderer = GetComponent<Renderer>();
|
||||
}
|
||||
// Use this for initialization
|
||||
void Start()
|
||||
{
|
||||
picker.onValueChanged.AddListener(color =>
|
||||
{
|
||||
pickerRenderer.material.color = color;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06851a815227e5044b0e3c1bf9b3a282
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,95 @@
|
||||
/// Credit judah4
|
||||
/// Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
/// Updated by SimonDarksideJ - Updated to use touch position rather than mouse for multi-touch
|
||||
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
/// <summary>
|
||||
/// Samples colors from a screen capture.
|
||||
/// Warning! In the editor if you're not in Free aspect mode then
|
||||
/// the captured area includes the grey areas to the left and right of the game view window.
|
||||
/// In a build this will not be an issue.
|
||||
///
|
||||
/// This does not work well with a world space UI as positioning is working with screen space.
|
||||
/// </summary>
|
||||
public class ColorSampler : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
|
||||
{
|
||||
private Vector2 m_screenPos;
|
||||
|
||||
[SerializeField]
|
||||
protected Button sampler;
|
||||
private RectTransform sampleRectTransform;
|
||||
|
||||
[SerializeField]
|
||||
protected Outline samplerOutline;
|
||||
|
||||
protected Texture2D screenCapture;
|
||||
|
||||
public ColorChangedEvent oncolorSelected = new ColorChangedEvent();
|
||||
|
||||
protected Color color;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
screenCapture = ScreenCapture.CaptureScreenshotAsTexture();
|
||||
sampleRectTransform = sampler.GetComponent<RectTransform>();
|
||||
sampler.gameObject.SetActive(true);
|
||||
sampler.onClick.AddListener(SelectColor);
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
Destroy(screenCapture);
|
||||
sampler.gameObject.SetActive(false);
|
||||
sampler.onClick.RemoveListener(SelectColor);
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (screenCapture == null)
|
||||
return;
|
||||
|
||||
sampleRectTransform.position = m_screenPos;
|
||||
color = screenCapture.GetPixel((int)m_screenPos.x, (int)m_screenPos.y);
|
||||
|
||||
HandleSamplerColoring();
|
||||
}
|
||||
|
||||
protected virtual void HandleSamplerColoring()
|
||||
{
|
||||
sampler.image.color = color;
|
||||
|
||||
if (samplerOutline)
|
||||
{
|
||||
var c = Color.Lerp(Color.white, Color.black, color.grayscale > 0.5f ? 1 : 0);
|
||||
c.a = samplerOutline.effectColor.a;
|
||||
samplerOutline.effectColor = c;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SelectColor()
|
||||
{
|
||||
if (oncolorSelected != null)
|
||||
oncolorSelected.Invoke(color);
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = eventData.position;
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = Vector2.zero;
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = eventData.position;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01c80b26fb6519c4ea3f410dc08f5814
|
||||
timeCreated: 1519007668
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,93 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays one of the color values of aColorPicker
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Slider))]
|
||||
public class ColorSlider : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl ColorPicker;
|
||||
|
||||
/// <summary>
|
||||
/// Which value this slider can edit.
|
||||
/// </summary>
|
||||
public ColorValues type;
|
||||
|
||||
private Slider slider;
|
||||
|
||||
private bool listen = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
slider = GetComponent<Slider>();
|
||||
|
||||
ColorPicker.onValueChanged.AddListener(ColorChanged);
|
||||
ColorPicker.onHSVChanged.AddListener(HSVChanged);
|
||||
slider.onValueChanged.AddListener(SliderChanged);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ColorPicker.onValueChanged.RemoveListener(ColorChanged);
|
||||
ColorPicker.onHSVChanged.RemoveListener(HSVChanged);
|
||||
slider.onValueChanged.RemoveListener(SliderChanged);
|
||||
}
|
||||
|
||||
private void ColorChanged(Color newColor)
|
||||
{
|
||||
listen = false;
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
slider.normalizedValue = newColor.r;
|
||||
break;
|
||||
case ColorValues.G:
|
||||
slider.normalizedValue = newColor.g;
|
||||
break;
|
||||
case ColorValues.B:
|
||||
slider.normalizedValue = newColor.b;
|
||||
break;
|
||||
case ColorValues.A:
|
||||
slider.normalizedValue = newColor.a;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HSVChanged(float hue, float saturation, float value)
|
||||
{
|
||||
listen = false;
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.Hue:
|
||||
slider.normalizedValue = hue; //1 - hue;
|
||||
break;
|
||||
case ColorValues.Saturation:
|
||||
slider.normalizedValue = saturation;
|
||||
break;
|
||||
case ColorValues.Value:
|
||||
slider.normalizedValue = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SliderChanged(float newValue)
|
||||
{
|
||||
if (listen)
|
||||
{
|
||||
newValue = slider.normalizedValue;
|
||||
//if (type == ColorValues.Hue)
|
||||
// newValue = 1 - newValue;
|
||||
|
||||
ColorPicker.AssignColor(type, newValue);
|
||||
}
|
||||
listen = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91558da930270ac4a960ba03b81c836a
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,231 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
[RequireComponent(typeof(RawImage)), ExecuteInEditMode()]
|
||||
public class ColorSliderImage : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl picker;
|
||||
|
||||
/// <summary>
|
||||
/// Which value this slider can edit.
|
||||
/// </summary>
|
||||
public ColorValues type;
|
||||
|
||||
public Slider.Direction direction;
|
||||
|
||||
private RawImage image;
|
||||
|
||||
private RectTransform RectTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
return transform as RectTransform;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
image = GetComponent<RawImage>();
|
||||
if (image)
|
||||
{
|
||||
RegenerateTexture();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Missing RawImage on object [" + name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (picker != null && Application.isPlaying)
|
||||
{
|
||||
picker.onValueChanged.AddListener(ColorChanged);
|
||||
picker.onHSVChanged.AddListener(ColorChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (picker != null)
|
||||
{
|
||||
picker.onValueChanged.RemoveListener(ColorChanged);
|
||||
picker.onHSVChanged.RemoveListener(ColorChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (image.texture != null)
|
||||
DestroyImmediate(image.texture);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
image = GetComponent<RawImage>();
|
||||
if (image)
|
||||
{
|
||||
RegenerateTexture();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Missing RawImage on object [" + name + "]");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void ColorChanged(Color newColor)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
case ColorValues.G:
|
||||
case ColorValues.B:
|
||||
case ColorValues.Saturation:
|
||||
case ColorValues.Value:
|
||||
RegenerateTexture();
|
||||
break;
|
||||
case ColorValues.A:
|
||||
case ColorValues.Hue:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ColorChanged(float hue, float saturation, float value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
case ColorValues.G:
|
||||
case ColorValues.B:
|
||||
case ColorValues.Saturation:
|
||||
case ColorValues.Value:
|
||||
RegenerateTexture();
|
||||
break;
|
||||
case ColorValues.A:
|
||||
case ColorValues.Hue:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegenerateTexture()
|
||||
{
|
||||
if (!picker)
|
||||
{
|
||||
Debug.LogWarning("Missing Picker on object [" + name + "]");
|
||||
}
|
||||
Color32 baseColor = picker != null ? picker.CurrentColor : Color.black;
|
||||
|
||||
float h = picker != null ? picker.H : 0;
|
||||
float s = picker != null ? picker.S : 0;
|
||||
float v = picker != null ? picker.V : 0;
|
||||
|
||||
Texture2D texture;
|
||||
Color32[] colors;
|
||||
|
||||
bool vertical = direction == Slider.Direction.BottomToTop || direction == Slider.Direction.TopToBottom;
|
||||
bool inverted = direction == Slider.Direction.TopToBottom || direction == Slider.Direction.RightToLeft;
|
||||
|
||||
int size;
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
case ColorValues.G:
|
||||
case ColorValues.B:
|
||||
case ColorValues.A:
|
||||
size = 255;
|
||||
break;
|
||||
case ColorValues.Hue:
|
||||
size = 360;
|
||||
break;
|
||||
case ColorValues.Saturation:
|
||||
case ColorValues.Value:
|
||||
size = 100;
|
||||
break;
|
||||
default:
|
||||
throw new System.NotImplementedException("");
|
||||
}
|
||||
if (vertical)
|
||||
texture = new Texture2D(1, size);
|
||||
else
|
||||
texture = new Texture2D(size, 1);
|
||||
|
||||
texture.hideFlags = HideFlags.DontSave;
|
||||
colors = new Color32[size];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ColorValues.R:
|
||||
for (byte i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = new Color32(i, baseColor.g, baseColor.b, 255);
|
||||
}
|
||||
break;
|
||||
case ColorValues.G:
|
||||
for (byte i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = new Color32(baseColor.r, i, baseColor.b, 255);
|
||||
}
|
||||
break;
|
||||
case ColorValues.B:
|
||||
for (byte i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = new Color32(baseColor.r, baseColor.g, i, 255);
|
||||
}
|
||||
break;
|
||||
case ColorValues.A:
|
||||
for (byte i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = new Color32(i, i, i, 255);
|
||||
}
|
||||
break;
|
||||
case ColorValues.Hue:
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = HSVUtil.ConvertHsvToRgb(i, 1, 1, 1);
|
||||
}
|
||||
break;
|
||||
case ColorValues.Saturation:
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = HSVUtil.ConvertHsvToRgb(h * 360, (float)i / size, v, 1);
|
||||
}
|
||||
break;
|
||||
case ColorValues.Value:
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
colors[inverted ? size - 1 - i : i] = HSVUtil.ConvertHsvToRgb(h * 360, s, (float)i / size, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new System.NotImplementedException("");
|
||||
}
|
||||
texture.SetPixels32(colors);
|
||||
texture.Apply();
|
||||
|
||||
if (image.texture != null)
|
||||
DestroyImmediate(image.texture);
|
||||
image.texture = texture;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case Slider.Direction.BottomToTop:
|
||||
case Slider.Direction.TopToBottom:
|
||||
image.uvRect = new Rect(0, 0, 2, 1);
|
||||
break;
|
||||
case Slider.Direction.LeftToRight:
|
||||
case Slider.Direction.RightToLeft:
|
||||
image.uvRect = new Rect(0, 0, 1, 2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd3cce5a27f2db94fa394c4719bddecd
|
||||
timeCreated: 1480011174
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,17 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
public enum ColorValues
|
||||
{
|
||||
R,
|
||||
G,
|
||||
B,
|
||||
A,
|
||||
|
||||
Hue,
|
||||
Saturation,
|
||||
Value
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f473590aed5e3f4ab5a0157b2a53dbd
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbbc689694061e6439664eda55449513
|
||||
folderAsset: yes
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[Serializable]
|
||||
public class ColorChangedEvent : UnityEvent<Color>
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff46fbecea7739f4690e4285c88f53c5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,6 @@
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class HSVChangedEvent : UnityEvent<float, float, float>
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d95ce8fba3dbbf4eb14411412169b88
|
||||
timeCreated: 1442747317
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,206 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
#region ColorUtilities
|
||||
|
||||
public static class HSVUtil
|
||||
{
|
||||
public static HsvColor ConvertRgbToHsv(Color color)
|
||||
{
|
||||
return ConvertRgbToHsv((int)(color.r * 255), (int)(color.g * 255), (int)(color.b * 255));
|
||||
}
|
||||
|
||||
//Converts an RGB color to an HSV color.
|
||||
public static HsvColor ConvertRgbToHsv(double r, double b, double g)
|
||||
{
|
||||
double delta, min;
|
||||
double h = 0, s, v;
|
||||
|
||||
min = Math.Min(Math.Min(r, g), b);
|
||||
v = Math.Max(Math.Max(r, g), b);
|
||||
delta = v - min;
|
||||
|
||||
if (v == 0.0)
|
||||
s = 0;
|
||||
else
|
||||
s = delta / v;
|
||||
|
||||
if (s == 0)
|
||||
h = 360;
|
||||
else
|
||||
{
|
||||
if (r == v)
|
||||
h = (g - b) / delta;
|
||||
else if (g == v)
|
||||
h = 2 + (b - r) / delta;
|
||||
else if (b == v)
|
||||
h = 4 + (r - g) / delta;
|
||||
|
||||
h *= 60;
|
||||
if (h <= 0.0)
|
||||
h += 360;
|
||||
}
|
||||
|
||||
HsvColor hsvColor = new HsvColor()
|
||||
{
|
||||
H = 360 - h,
|
||||
S = s,
|
||||
V = v / 255
|
||||
};
|
||||
return hsvColor;
|
||||
|
||||
}
|
||||
|
||||
// Converts an HSV color to an RGB color.
|
||||
public static Color ConvertHsvToRgb(double h, double s, double v, float alpha)
|
||||
{
|
||||
|
||||
double r = 0, g = 0, b = 0;
|
||||
|
||||
if (s == 0)
|
||||
{
|
||||
r = v;
|
||||
g = v;
|
||||
b = v;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
int i;
|
||||
double f, p, q, t;
|
||||
|
||||
|
||||
if (h == 360)
|
||||
h = 0;
|
||||
else
|
||||
h = h / 60;
|
||||
|
||||
i = (int)(h);
|
||||
f = h - i;
|
||||
|
||||
p = v * (1.0 - s);
|
||||
q = v * (1.0 - (s * f));
|
||||
t = v * (1.0 - (s * (1.0f - f)));
|
||||
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
r = v;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
r = q;
|
||||
g = v;
|
||||
b = p;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
r = p;
|
||||
g = v;
|
||||
b = t;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = v;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = v;
|
||||
break;
|
||||
|
||||
default:
|
||||
r = v;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Color((float)r, (float)g, (float)b, alpha);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion ColorUtilities
|
||||
|
||||
// Describes a color in terms of
|
||||
// Hue, Saturation, and Value (brightness)
|
||||
#region HsvColor
|
||||
public struct HsvColor
|
||||
{
|
||||
/// <summary>
|
||||
/// The Hue, ranges between 0 and 360
|
||||
/// </summary>
|
||||
public double H;
|
||||
|
||||
/// <summary>
|
||||
/// The saturation, ranges between 0 and 1
|
||||
/// </summary>
|
||||
public double S;
|
||||
|
||||
// The value (brightness), ranges between 0 and 1
|
||||
public double V;
|
||||
|
||||
public float NormalizedH
|
||||
{
|
||||
get
|
||||
{
|
||||
return (float)H / 360f;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
H = (double)value * 360;
|
||||
}
|
||||
}
|
||||
|
||||
public float NormalizedS
|
||||
{
|
||||
get
|
||||
{
|
||||
return (float)S;
|
||||
}
|
||||
set
|
||||
{
|
||||
S = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float NormalizedV
|
||||
{
|
||||
get
|
||||
{
|
||||
return (float)V;
|
||||
}
|
||||
set
|
||||
{
|
||||
V = (double)value;
|
||||
}
|
||||
}
|
||||
|
||||
public HsvColor(double h, double s, double v)
|
||||
{
|
||||
this.H = h;
|
||||
this.S = s;
|
||||
this.V = v;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" + H.ToString("f2") + "," + S.ToString("f2") + "," + V.ToString("f2") + "}";
|
||||
}
|
||||
}
|
||||
#endregion HsvColor
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f3189246d7fc204faba7a1e9c08e0af
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,101 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(InputField))]
|
||||
public class HexColorField : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl ColorPicker;
|
||||
|
||||
public bool displayAlpha;
|
||||
|
||||
private InputField hexInputField;
|
||||
|
||||
private const string hexRegex = "^#?(?:[0-9a-fA-F]{3,4}){1,2}$";
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
hexInputField = GetComponent<InputField>();
|
||||
|
||||
// Add listeners to keep text (and color) up to date
|
||||
hexInputField.onEndEdit.AddListener(UpdateColor);
|
||||
ColorPicker.onValueChanged.AddListener(UpdateHex);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
hexInputField.onValueChanged.RemoveListener(UpdateColor);
|
||||
ColorPicker.onValueChanged.RemoveListener(UpdateHex);
|
||||
}
|
||||
|
||||
private void UpdateHex(Color newColor)
|
||||
{
|
||||
hexInputField.text = ColorToHex(newColor);
|
||||
}
|
||||
|
||||
private void UpdateColor(string newHex)
|
||||
{
|
||||
Color32 color;
|
||||
if (HexToColor(newHex, out color))
|
||||
ColorPicker.CurrentColor = color;
|
||||
else
|
||||
Debug.Log("hex value is in the wrong format, valid formats are: #RGB, #RGBA, #RRGGBB and #RRGGBBAA (# is optional)");
|
||||
}
|
||||
|
||||
private string ColorToHex(Color32 color)
|
||||
{
|
||||
if (displayAlpha)
|
||||
return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.r, color.g, color.b, color.a);
|
||||
else
|
||||
return string.Format("#{0:X2}{1:X2}{2:X2}", color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
public static bool HexToColor(string hex, out Color32 color)
|
||||
{
|
||||
// Check if this is a valid hex string (# is optional)
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(hex, hexRegex))
|
||||
{
|
||||
int startIndex = hex.StartsWith("#") ? 1 : 0;
|
||||
|
||||
if (hex.Length == startIndex + 8) //#RRGGBBAA
|
||||
{
|
||||
color = new Color32(byte.Parse(hex.Substring(startIndex, 2), NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse(hex.Substring(startIndex + 2, 2), NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse(hex.Substring(startIndex + 4, 2), NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse(hex.Substring(startIndex + 6, 2), NumberStyles.AllowHexSpecifier));
|
||||
}
|
||||
else if (hex.Length == startIndex + 6) //#RRGGBB
|
||||
{
|
||||
color = new Color32(byte.Parse(hex.Substring(startIndex, 2), NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse(hex.Substring(startIndex + 2, 2), NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse(hex.Substring(startIndex + 4, 2), NumberStyles.AllowHexSpecifier),
|
||||
255);
|
||||
}
|
||||
else if (hex.Length == startIndex + 4) //#RGBA
|
||||
{
|
||||
color = new Color32(byte.Parse("" + hex[startIndex] + hex[startIndex], NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse("" + hex[startIndex + 1] + hex[startIndex + 1], NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse("" + hex[startIndex + 2] + hex[startIndex + 2], NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse("" + hex[startIndex + 3] + hex[startIndex + 3], NumberStyles.AllowHexSpecifier));
|
||||
}
|
||||
else //#RGB
|
||||
{
|
||||
color = new Color32(byte.Parse("" + hex[startIndex] + hex[startIndex], NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse("" + hex[startIndex + 1] + hex[startIndex + 1], NumberStyles.AllowHexSpecifier),
|
||||
byte.Parse("" + hex[startIndex + 2] + hex[startIndex + 2], NumberStyles.AllowHexSpecifier),
|
||||
255);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = new Color32();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7343402602909bd4f928d58433c5c87f
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,122 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions.ColorPicker
|
||||
{
|
||||
[RequireComponent(typeof(BoxSlider), typeof(RawImage)), ExecuteInEditMode()]
|
||||
public class SVBoxSlider : MonoBehaviour
|
||||
{
|
||||
public ColorPickerControl picker;
|
||||
|
||||
private BoxSlider slider;
|
||||
private RawImage image;
|
||||
|
||||
private float lastH = -1;
|
||||
private bool listen = true;
|
||||
|
||||
public RectTransform RectTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
return transform as RectTransform;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
slider = GetComponent<BoxSlider>();
|
||||
image = GetComponent<RawImage>();
|
||||
|
||||
RegenerateSVTexture();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (Application.isPlaying && picker != null)
|
||||
{
|
||||
slider.OnValueChanged.AddListener(SliderChanged);
|
||||
picker.onHSVChanged.AddListener(HSVChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (picker != null)
|
||||
{
|
||||
slider.OnValueChanged.RemoveListener(SliderChanged);
|
||||
picker.onHSVChanged.RemoveListener(HSVChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (image.texture != null)
|
||||
DestroyImmediate(image.texture);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
image = GetComponent<RawImage>();
|
||||
RegenerateSVTexture();
|
||||
}
|
||||
#endif
|
||||
|
||||
private void SliderChanged(float saturation, float value)
|
||||
{
|
||||
if (listen)
|
||||
{
|
||||
picker.AssignColor(ColorValues.Saturation, saturation);
|
||||
picker.AssignColor(ColorValues.Value, value);
|
||||
}
|
||||
listen = true;
|
||||
}
|
||||
|
||||
private void HSVChanged(float h, float s, float v)
|
||||
{
|
||||
if (lastH != h)
|
||||
{
|
||||
lastH = h;
|
||||
RegenerateSVTexture();
|
||||
}
|
||||
|
||||
if (s != slider.NormalizedValueX)
|
||||
{
|
||||
listen = false;
|
||||
slider.NormalizedValueX = s;
|
||||
}
|
||||
|
||||
if (v != slider.NormalizedValueY)
|
||||
{
|
||||
listen = false;
|
||||
slider.NormalizedValueY = v;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegenerateSVTexture()
|
||||
{
|
||||
double h = picker != null ? picker.H * 360 : 0;
|
||||
|
||||
if (image.texture != null)
|
||||
DestroyImmediate(image.texture);
|
||||
|
||||
Texture2D texture = new Texture2D(100, 100)
|
||||
{
|
||||
hideFlags = HideFlags.DontSave
|
||||
};
|
||||
for (int s = 0; s < 100; s++)
|
||||
{
|
||||
Color32[] colors = new Color32[100];
|
||||
for (int v = 0; v < 100; v++)
|
||||
{
|
||||
colors[v] = HSVUtil.ConvertHsvToRgb(h, (float)s / 100, (float)v / 100, 1);
|
||||
}
|
||||
texture.SetPixels32(s, 0, 1, 100, colors);
|
||||
}
|
||||
texture.Apply();
|
||||
|
||||
image.texture = texture;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a0647785ed421e449239dbd6512e156
|
||||
timeCreated: 1480011173
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,43 @@
|
||||
///Credit judah4
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
public class TiltWindow : MonoBehaviour, IDragHandler
|
||||
{
|
||||
public Vector2 range = new Vector2(5f, 3f);
|
||||
|
||||
private Transform mTrans;
|
||||
private Quaternion mStart;
|
||||
private Vector2 mRot = Vector2.zero;
|
||||
private Vector2 m_screenPos;
|
||||
|
||||
|
||||
void Start()
|
||||
{
|
||||
mTrans = transform;
|
||||
mStart = mTrans.localRotation;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Vector3 pos = m_screenPos;
|
||||
|
||||
float halfWidth = Screen.width * 0.5f;
|
||||
float halfHeight = Screen.height * 0.5f;
|
||||
float x = Mathf.Clamp((pos.x - halfWidth) / halfWidth, -1f, 1f);
|
||||
float y = Mathf.Clamp((pos.y - halfHeight) / halfHeight, -1f, 1f);
|
||||
mRot = Vector2.Lerp(mRot, new Vector2(x, y), Time.deltaTime * 5f);
|
||||
|
||||
mTrans.localRotation = mStart * Quaternion.Euler(-mRot.y * range.y, mRot.x * range.x, 0f);
|
||||
}
|
||||
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = eventData.position;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7be5109ea5b91e4b856621023b15168
|
||||
timeCreated: 1480011174
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 726a11b8d64fa0143b34f417f5453f80
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,531 @@
|
||||
///Credit perchik
|
||||
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
public enum AutoCompleteSearchType
|
||||
{
|
||||
ArraySort,
|
||||
Linq
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[AddComponentMenu("UI/Extensions/AutoComplete ComboBox")]
|
||||
public class AutoCompleteComboBox : MonoBehaviour
|
||||
{
|
||||
public Color disabledTextColor;
|
||||
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
|
||||
|
||||
/// <summary>
|
||||
/// Contains the included items. To add and remove items to/from this list, use the <see cref="AddItem(string)"/>,
|
||||
/// <see cref="RemoveItem(string)"/> and <see cref="SetAvailableOptions(List{string})"/> methods as these also execute
|
||||
/// the required methods to update to the current collection.
|
||||
/// </summary>
|
||||
public List<string> AvailableOptions;
|
||||
|
||||
//private bool isInitialized = false;
|
||||
private bool _isPanelActive = false;
|
||||
private bool _hasDrawnOnce = false;
|
||||
|
||||
private InputField _mainInput;
|
||||
private RectTransform _inputRT;
|
||||
|
||||
//private Button _arrow_Button;
|
||||
|
||||
private RectTransform _rectTransform;
|
||||
|
||||
private RectTransform _overlayRT;
|
||||
private RectTransform _scrollPanelRT;
|
||||
private RectTransform _scrollBarRT;
|
||||
private RectTransform _slidingAreaRT;
|
||||
private RectTransform _scrollHandleRT;
|
||||
private RectTransform _itemsPanelRT;
|
||||
private Canvas _canvas;
|
||||
private RectTransform _canvasRT;
|
||||
|
||||
private ScrollRect _scrollRect;
|
||||
|
||||
private List<string> _panelItems; //items that will get shown in the drop-down
|
||||
private List<string> _prunedPanelItems; //items that used to show in the drop-down
|
||||
|
||||
private Dictionary<string, GameObject> panelObjects;
|
||||
|
||||
private GameObject itemTemplate;
|
||||
|
||||
public string Text { get; private set; }
|
||||
|
||||
[SerializeField]
|
||||
private float _scrollBarWidth = 20.0f;
|
||||
public float ScrollBarWidth
|
||||
{
|
||||
get { return _scrollBarWidth; }
|
||||
set
|
||||
{
|
||||
_scrollBarWidth = value;
|
||||
RedrawPanel();
|
||||
}
|
||||
}
|
||||
|
||||
// private int scrollOffset; //offset of the selected item
|
||||
// private int _selectedIndex = 0;
|
||||
|
||||
[SerializeField]
|
||||
private int _itemsToDisplay;
|
||||
public int ItemsToDisplay
|
||||
{
|
||||
get { return _itemsToDisplay; }
|
||||
set
|
||||
{
|
||||
_itemsToDisplay = value;
|
||||
RedrawPanel();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectFirstItemOnStart = false;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Change input text color based on matching items")]
|
||||
private bool _ChangeInputTextColorBasedOnMatchingItems = false;
|
||||
public bool InputColorMatching{
|
||||
get { return _ChangeInputTextColorBasedOnMatchingItems; }
|
||||
set
|
||||
{
|
||||
_ChangeInputTextColorBasedOnMatchingItems = value;
|
||||
if (_ChangeInputTextColorBasedOnMatchingItems) {
|
||||
SetInputTextColor ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float DropdownOffset = 10f;
|
||||
|
||||
//TODO design as foldout for Inspector
|
||||
public Color ValidSelectionTextColor = Color.green;
|
||||
public Color MatchingItemsRemainingTextColor = Color.black;
|
||||
public Color NoItemsRemainingTextColor = Color.red;
|
||||
|
||||
public AutoCompleteSearchType autocompleteSearchType = AutoCompleteSearchType.Linq;
|
||||
|
||||
[SerializeField]
|
||||
private bool _displayPanelAbove = false;
|
||||
|
||||
private bool _selectionIsValid = false;
|
||||
|
||||
[System.Serializable]
|
||||
public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<string, bool> {
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SelectionTextChangedEvent : UnityEngine.Events.UnityEvent<string> {
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SelectionValidityChangedEvent : UnityEngine.Events.UnityEvent<bool> {
|
||||
}
|
||||
|
||||
// fires when input text is changed;
|
||||
public SelectionTextChangedEvent OnSelectionTextChanged;
|
||||
// fires when an Item gets selected / deselected (including when items are added/removed once this is possible)
|
||||
public SelectionValidityChangedEvent OnSelectionValidityChanged;
|
||||
// fires in both cases
|
||||
public SelectionChangedEvent OnSelectionChanged;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (SelectFirstItemOnStart && AvailableOptions.Count > 0) {
|
||||
ToggleDropdownPanel (false);
|
||||
OnItemClicked (AvailableOptions [0]);
|
||||
}
|
||||
RedrawPanel();
|
||||
}
|
||||
|
||||
private bool Initialize()
|
||||
{
|
||||
bool success = true;
|
||||
try
|
||||
{
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
_inputRT = _rectTransform.Find("InputField").GetComponent<RectTransform>();
|
||||
_mainInput = _inputRT.GetComponent<InputField>();
|
||||
|
||||
//_arrow_Button = _rectTransform.FindChild ("ArrowBtn").GetComponent<Button> ();
|
||||
|
||||
_overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
|
||||
_overlayRT.gameObject.SetActive(false);
|
||||
|
||||
|
||||
_scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>();
|
||||
_scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>();
|
||||
_slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
|
||||
_scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>();
|
||||
_itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>();
|
||||
//itemPanelLayout = itemsPanelRT.gameObject.GetComponent<LayoutGroup>();
|
||||
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
_canvasRT = _canvas.GetComponent<RectTransform>();
|
||||
|
||||
_scrollRect = _scrollPanelRT.GetComponent<ScrollRect>();
|
||||
_scrollRect.scrollSensitivity = _rectTransform.sizeDelta.y / 2;
|
||||
_scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
_scrollRect.content = _itemsPanelRT;
|
||||
|
||||
itemTemplate = _rectTransform.Find("ItemTemplate").gameObject;
|
||||
itemTemplate.SetActive(false);
|
||||
}
|
||||
catch (System.NullReferenceException ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception");
|
||||
success = false;
|
||||
}
|
||||
panelObjects = new Dictionary<string, GameObject>();
|
||||
|
||||
_prunedPanelItems = new List<string>();
|
||||
_panelItems = new List<string>();
|
||||
|
||||
RebuildPanel();
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to <see cref="this.AvailableOptions"/> if it is not a duplicate and rebuilds the panel.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add.</param>
|
||||
public void AddItem(string item)
|
||||
{
|
||||
if (!this.AvailableOptions.Contains(item))
|
||||
{
|
||||
this.AvailableOptions.Add(item);
|
||||
this.RebuildPanel();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"{nameof(AutoCompleteComboBox)}.{nameof(AddItem)}: items may only exists once. '{item}' can not be added.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the item from <see cref="this.AvailableOptions"/> and rebuilds the panel.
|
||||
/// </summary>
|
||||
/// <param name="item">Item to remove.</param>
|
||||
public void RemoveItem(string item)
|
||||
{
|
||||
if (this.AvailableOptions.Contains(item))
|
||||
{
|
||||
this.AvailableOptions.Remove(item);
|
||||
this.RebuildPanel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given items as new content for the comboBox. Previous entries will be cleared.
|
||||
/// </summary>
|
||||
/// <param name="newOptions">New entries.</param>
|
||||
public void SetAvailableOptions(List<string> newOptions)
|
||||
{
|
||||
var uniqueOptions = newOptions.Distinct().ToList();
|
||||
if (newOptions.Count != uniqueOptions.Count)
|
||||
{
|
||||
Debug.LogWarning($"{nameof(AutoCompleteComboBox)}.{nameof(SetAvailableOptions)}: items may only exists once. {newOptions.Count - uniqueOptions.Count} duplicates.");
|
||||
}
|
||||
|
||||
this.AvailableOptions.Clear();
|
||||
this.AvailableOptions = uniqueOptions;
|
||||
this.RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the given items as new content for the comboBox. Previous entries will be cleared.
|
||||
/// </summary>
|
||||
/// <param name="newOptions">New entries.</param>
|
||||
public void SetAvailableOptions(string[] newOptions)
|
||||
{
|
||||
var uniqueOptions = newOptions.Distinct().ToList();
|
||||
if (newOptions.Length != uniqueOptions.Count)
|
||||
{
|
||||
Debug.LogWarning($"{nameof(AutoCompleteComboBox)}.{nameof(SetAvailableOptions)}: items may only exists once. {newOptions.Length - uniqueOptions.Count} duplicates.");
|
||||
}
|
||||
|
||||
this.AvailableOptions.Clear();
|
||||
for (int i = 0; i < newOptions.Length; i++)
|
||||
{
|
||||
this.AvailableOptions.Add(newOptions[i]);
|
||||
}
|
||||
|
||||
this.RebuildPanel();
|
||||
}
|
||||
|
||||
public void ResetItems()
|
||||
{
|
||||
AvailableOptions.Clear();
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the contents of the panel in response to items being added.
|
||||
/// </summary>
|
||||
private void RebuildPanel()
|
||||
{
|
||||
if (_isPanelActive) ToggleDropdownPanel();
|
||||
|
||||
//panel starts with all options
|
||||
_panelItems.Clear();
|
||||
_prunedPanelItems.Clear();
|
||||
panelObjects.Clear();
|
||||
|
||||
//clear Autocomplete children in scene
|
||||
foreach (Transform child in _itemsPanelRT.transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
foreach (string option in AvailableOptions)
|
||||
{
|
||||
_panelItems.Add(option.ToLower());
|
||||
}
|
||||
|
||||
List<GameObject> itemObjs = new List<GameObject>(panelObjects.Values);
|
||||
|
||||
int indx = 0;
|
||||
while (itemObjs.Count < AvailableOptions.Count)
|
||||
{
|
||||
GameObject newItem = Instantiate(itemTemplate) as GameObject;
|
||||
newItem.name = "Item " + indx;
|
||||
newItem.transform.SetParent(_itemsPanelRT, false);
|
||||
itemObjs.Add(newItem);
|
||||
indx++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemObjs.Count; i++)
|
||||
{
|
||||
itemObjs[i].SetActive(i <= AvailableOptions.Count);
|
||||
if (i < AvailableOptions.Count)
|
||||
{
|
||||
itemObjs[i].name = "Item " + i + " " + _panelItems[i];
|
||||
itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value
|
||||
|
||||
Button itemBtn = itemObjs[i].GetComponent<Button>();
|
||||
itemBtn.onClick.RemoveAllListeners();
|
||||
string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away
|
||||
itemBtn.onClick.AddListener(() =>
|
||||
{
|
||||
OnItemClicked(textOfItem);
|
||||
});
|
||||
panelObjects[_panelItems[i]] = itemObjs[i];
|
||||
}
|
||||
}
|
||||
SetInputTextColor ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// what happens when an item in the list is selected
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
private void OnItemClicked(string item)
|
||||
{
|
||||
//Debug.Log("item " + item + " clicked");
|
||||
Text = item;
|
||||
_mainInput.text = Text;
|
||||
ToggleDropdownPanel(true);
|
||||
}
|
||||
|
||||
//private void UpdateSelected()
|
||||
//{
|
||||
// SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
|
||||
// if (SelectedItem == null) return;
|
||||
|
||||
// bool hasImage = SelectedItem.Image != null;
|
||||
// if (hasImage)
|
||||
// {
|
||||
// mainButton.img.sprite = SelectedItem.Image;
|
||||
// mainButton.img.color = Color.white;
|
||||
|
||||
// //if (Interactable) mainButton.img.color = Color.white;
|
||||
// //else mainButton.img.color = new Color(1, 1, 1, .5f);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// mainButton.img.sprite = null;
|
||||
// }
|
||||
|
||||
// mainButton.txt.text = SelectedItem.Caption;
|
||||
|
||||
// //update selected index color
|
||||
// for (int i = 0; i < itemsPanelRT.childCount; i++)
|
||||
// {
|
||||
// panelItems[i].btnImg.color = (_selectedIndex == i) ? mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
private void RedrawPanel()
|
||||
{
|
||||
float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
|
||||
_scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay);
|
||||
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta)
|
||||
{
|
||||
_hasDrawnOnce = true;
|
||||
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
|
||||
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
|
||||
|
||||
_scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay
|
||||
_scrollPanelRT.anchoredPosition = _displayPanelAbove ?
|
||||
new Vector2(0, DropdownOffset + _rectTransform.sizeDelta.y * _panelItems.Count - 1) :
|
||||
new Vector2(0, -_rectTransform.sizeDelta.y);
|
||||
|
||||
//make the overlay fill the screen
|
||||
_overlayRT.SetParent(_canvas.transform, false); //attach it to top level object
|
||||
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
|
||||
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
|
||||
|
||||
_overlayRT.SetParent(transform, true);//reattach to this object
|
||||
_scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay
|
||||
}
|
||||
|
||||
if (_panelItems.Count < 1) return;
|
||||
|
||||
float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count) + DropdownOffset;
|
||||
|
||||
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
|
||||
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
|
||||
|
||||
_itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5);
|
||||
_itemsPanelRT.anchoredPosition = new Vector2(5, 0);
|
||||
|
||||
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth);
|
||||
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
|
||||
if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true);
|
||||
|
||||
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0);
|
||||
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x);
|
||||
}
|
||||
|
||||
public void OnValueChanged(string currText)
|
||||
{
|
||||
Text = currText;
|
||||
PruneItems(currText);
|
||||
RedrawPanel();
|
||||
//Debug.Log("value changed to: " + currText);
|
||||
|
||||
if (_panelItems.Count == 0)
|
||||
{
|
||||
_isPanelActive = true;//this makes it get turned off
|
||||
ToggleDropdownPanel(false);
|
||||
}
|
||||
else if (!_isPanelActive)
|
||||
{
|
||||
ToggleDropdownPanel(false);
|
||||
}
|
||||
|
||||
bool validity_changed = (_panelItems.Contains (Text) != _selectionIsValid);
|
||||
_selectionIsValid = _panelItems.Contains (Text);
|
||||
OnSelectionChanged.Invoke (Text, _selectionIsValid);
|
||||
OnSelectionTextChanged.Invoke (Text);
|
||||
if(validity_changed){
|
||||
OnSelectionValidityChanged.Invoke (_selectionIsValid);
|
||||
}
|
||||
|
||||
SetInputTextColor ();
|
||||
}
|
||||
|
||||
private void SetInputTextColor(){
|
||||
if (InputColorMatching) {
|
||||
if (_selectionIsValid) {
|
||||
_mainInput.textComponent.color = ValidSelectionTextColor;
|
||||
} else if (_panelItems.Count > 0) {
|
||||
_mainInput.textComponent.color = MatchingItemsRemainingTextColor;
|
||||
} else {
|
||||
_mainInput.textComponent.color = NoItemsRemainingTextColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Toggle the drop down list
|
||||
/// </summary>
|
||||
/// <param name="directClick"> whether an item was directly clicked on</param>
|
||||
public void ToggleDropdownPanel(bool directClick = false)
|
||||
{
|
||||
_isPanelActive = !_isPanelActive;
|
||||
|
||||
_overlayRT.gameObject.SetActive(_isPanelActive);
|
||||
if (_isPanelActive)
|
||||
{
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
else if (directClick)
|
||||
{
|
||||
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
|
||||
}
|
||||
}
|
||||
|
||||
private void PruneItems(string currText)
|
||||
{
|
||||
if (autocompleteSearchType == AutoCompleteSearchType.Linq)
|
||||
{
|
||||
PruneItemsLinq(currText);
|
||||
}
|
||||
else
|
||||
{
|
||||
PruneItemsArray(currText);
|
||||
}
|
||||
}
|
||||
|
||||
private void PruneItemsLinq(string currText)
|
||||
{
|
||||
currText = currText.ToLower();
|
||||
var toPrune = _panelItems.Where(x => !x.Contains(currText)).ToArray();
|
||||
foreach (string key in toPrune)
|
||||
{
|
||||
panelObjects[key].SetActive(false);
|
||||
_panelItems.Remove(key);
|
||||
_prunedPanelItems.Add(key);
|
||||
}
|
||||
|
||||
var toAddBack = _prunedPanelItems.Where(x => x.Contains(currText)).ToArray();
|
||||
foreach (string key in toAddBack)
|
||||
{
|
||||
panelObjects[key].SetActive(true);
|
||||
_panelItems.Add(key);
|
||||
_prunedPanelItems.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
//Updated to not use Linq
|
||||
private void PruneItemsArray(string currText)
|
||||
{
|
||||
string _currText = currText.ToLower();
|
||||
|
||||
for (int i = _panelItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
string _item = _panelItems[i];
|
||||
if (!_item.Contains(_currText))
|
||||
{
|
||||
panelObjects[_panelItems[i]].SetActive(false);
|
||||
_panelItems.RemoveAt(i);
|
||||
_prunedPanelItems.Add(_item);
|
||||
}
|
||||
}
|
||||
for (int i = _prunedPanelItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
string _item = _prunedPanelItems[i];
|
||||
if (_item.Contains(_currText))
|
||||
{
|
||||
panelObjects[_prunedPanelItems[i]].SetActive(true);
|
||||
_prunedPanelItems.RemoveAt(i);
|
||||
_panelItems.Add(_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef22b091ebae52c47aa3e86ad9040c05
|
||||
timeCreated: 1492278993
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,348 @@
|
||||
///Credit perchik
|
||||
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[AddComponentMenu("UI/Extensions/ComboBox")]
|
||||
public class ComboBox : MonoBehaviour
|
||||
{
|
||||
public Color disabledTextColor;
|
||||
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
|
||||
|
||||
public List<string> AvailableOptions;
|
||||
|
||||
[SerializeField]
|
||||
private float _scrollBarWidth = 20.0f;
|
||||
|
||||
[SerializeField]
|
||||
private int _itemsToDisplay;
|
||||
|
||||
//Sorting disabled as it causes issues.
|
||||
//[SerializeField]
|
||||
//private bool _sortItems = true;
|
||||
|
||||
[SerializeField]
|
||||
private bool _displayPanelAbove = false;
|
||||
|
||||
[System.Serializable]
|
||||
public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<string>
|
||||
{
|
||||
}
|
||||
// fires when item is changed;
|
||||
public SelectionChangedEvent OnSelectionChanged;
|
||||
|
||||
//private bool isInitialized = false;
|
||||
private bool _isPanelActive = false;
|
||||
private bool _hasDrawnOnce = false;
|
||||
|
||||
private InputField _mainInput;
|
||||
private RectTransform _inputRT;
|
||||
|
||||
|
||||
private RectTransform _rectTransform;
|
||||
|
||||
private RectTransform _overlayRT;
|
||||
private RectTransform _scrollPanelRT;
|
||||
private RectTransform _scrollBarRT;
|
||||
private RectTransform _slidingAreaRT;
|
||||
private RectTransform _scrollHandleRT;
|
||||
private RectTransform _itemsPanelRT;
|
||||
private Canvas _canvas;
|
||||
private RectTransform _canvasRT;
|
||||
|
||||
private ScrollRect _scrollRect;
|
||||
|
||||
private List<string> _panelItems; //items that will get shown in the drop-down
|
||||
|
||||
private Dictionary<string, GameObject> panelObjects;
|
||||
|
||||
private GameObject itemTemplate;
|
||||
|
||||
public string Text { get; private set; }
|
||||
|
||||
public float ScrollBarWidth
|
||||
{
|
||||
get { return _scrollBarWidth; }
|
||||
set
|
||||
{
|
||||
_scrollBarWidth = value;
|
||||
RedrawPanel();
|
||||
}
|
||||
}
|
||||
|
||||
// private int scrollOffset; //offset of the selected item
|
||||
// private int _selectedIndex = 0;
|
||||
|
||||
public int ItemsToDisplay
|
||||
{
|
||||
get { return _itemsToDisplay; }
|
||||
set
|
||||
{
|
||||
_itemsToDisplay = value;
|
||||
RedrawPanel();
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
RedrawPanel();
|
||||
}
|
||||
|
||||
private bool Initialize()
|
||||
{
|
||||
bool success = true;
|
||||
try
|
||||
{
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
_inputRT = _rectTransform.Find("InputField").GetComponent<RectTransform>();
|
||||
_mainInput = _inputRT.GetComponent<InputField>();
|
||||
|
||||
_overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
|
||||
_overlayRT.gameObject.SetActive(false);
|
||||
|
||||
|
||||
_scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>();
|
||||
_scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>();
|
||||
_slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
|
||||
_scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>();
|
||||
_itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>();
|
||||
//itemPanelLayout = itemsPanelRT.gameObject.GetComponent<LayoutGroup>();
|
||||
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
_canvasRT = _canvas.GetComponent<RectTransform>();
|
||||
|
||||
_scrollRect = _scrollPanelRT.GetComponent<ScrollRect>();
|
||||
_scrollRect.scrollSensitivity = _rectTransform.sizeDelta.y / 2;
|
||||
_scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
_scrollRect.content = _itemsPanelRT;
|
||||
|
||||
itemTemplate = _rectTransform.Find("ItemTemplate").gameObject;
|
||||
itemTemplate.SetActive(false);
|
||||
}
|
||||
catch (System.NullReferenceException ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception");
|
||||
success = false;
|
||||
}
|
||||
panelObjects = new Dictionary<string, GameObject>();
|
||||
|
||||
_panelItems = AvailableOptions.ToList();
|
||||
|
||||
RebuildPanel();
|
||||
//RedrawPanel(); - causes an initialisation failure in U5
|
||||
return success;
|
||||
}
|
||||
|
||||
public void AddItem(string item)
|
||||
{
|
||||
AvailableOptions.Add(item);
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
public void RemoveItem(string item)
|
||||
{
|
||||
AvailableOptions.Remove(item);
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
public void SetAvailableOptions(List<string> newOptions)
|
||||
{
|
||||
AvailableOptions.Clear();
|
||||
AvailableOptions = newOptions;
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
public void SetAvailableOptions(string[] newOptions)
|
||||
{
|
||||
AvailableOptions.Clear();
|
||||
|
||||
for (int i = 0; i < newOptions.Length; i++)
|
||||
{
|
||||
AvailableOptions.Add(newOptions[i]);
|
||||
}
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
public void ResetItems()
|
||||
{
|
||||
AvailableOptions.Clear();
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the contents of the panel in response to items being added.
|
||||
/// </summary>
|
||||
private void RebuildPanel()
|
||||
{
|
||||
//panel starts with all options
|
||||
_panelItems.Clear();
|
||||
foreach (string option in AvailableOptions)
|
||||
{
|
||||
_panelItems.Add(option.ToLower());
|
||||
}
|
||||
//if(_sortItems) _panelItems.Sort();
|
||||
|
||||
List<GameObject> itemObjs = new List<GameObject>(panelObjects.Values);
|
||||
panelObjects.Clear();
|
||||
|
||||
int indx = 0;
|
||||
while (itemObjs.Count < AvailableOptions.Count)
|
||||
{
|
||||
GameObject newItem = Instantiate(itemTemplate) as GameObject;
|
||||
newItem.name = "Item " + indx;
|
||||
newItem.transform.SetParent(_itemsPanelRT, false);
|
||||
itemObjs.Add(newItem);
|
||||
indx++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemObjs.Count; i++)
|
||||
{
|
||||
itemObjs[i].SetActive(i <= AvailableOptions.Count);
|
||||
if (i < AvailableOptions.Count)
|
||||
{
|
||||
itemObjs[i].name = "Item " + i + " " + _panelItems[i];
|
||||
itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value
|
||||
|
||||
Button itemBtn = itemObjs[i].GetComponent<Button>();
|
||||
itemBtn.onClick.RemoveAllListeners();
|
||||
string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away
|
||||
itemBtn.onClick.AddListener(() =>
|
||||
{
|
||||
OnItemClicked(textOfItem);
|
||||
});
|
||||
panelObjects[_panelItems[i]] = itemObjs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// what happens when an item in the list is selected
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
private void OnItemClicked(string item)
|
||||
{
|
||||
//Debug.Log("item " + item + " clicked");
|
||||
Text = item;
|
||||
_mainInput.text = Text;
|
||||
ToggleDropdownPanel(true);
|
||||
}
|
||||
|
||||
//private void UpdateSelected()
|
||||
//{
|
||||
// SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
|
||||
// if (SelectedItem == null) return;
|
||||
|
||||
// bool hasImage = SelectedItem.Image != null;
|
||||
// if (hasImage)
|
||||
// {
|
||||
// mainButton.img.sprite = SelectedItem.Image;
|
||||
// mainButton.img.color = Color.white;
|
||||
|
||||
// //if (Interactable) mainButton.img.color = Color.white;
|
||||
// //else mainButton.img.color = new Color(1, 1, 1, .5f);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// mainButton.img.sprite = null;
|
||||
// }
|
||||
|
||||
// mainButton.txt.text = SelectedItem.Caption;
|
||||
|
||||
// //update selected index color
|
||||
// for (int i = 0; i < itemsPanelRT.childCount; i++)
|
||||
// {
|
||||
// panelItems[i].btnImg.color = (_selectedIndex == i) ? mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
private void RedrawPanel()
|
||||
{
|
||||
float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
|
||||
_scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay);
|
||||
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta)
|
||||
{
|
||||
_hasDrawnOnce = true;
|
||||
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
|
||||
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
|
||||
|
||||
_scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay
|
||||
_scrollPanelRT.anchoredPosition = _displayPanelAbove ?
|
||||
new Vector2(0, _rectTransform.sizeDelta.y * ItemsToDisplay - 1) :
|
||||
new Vector2(0, -_rectTransform.sizeDelta.y);
|
||||
|
||||
//make the overlay fill the screen
|
||||
_overlayRT.SetParent(_canvas.transform, false); //attach it to top level object
|
||||
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
|
||||
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
|
||||
|
||||
_overlayRT.SetParent(transform, true);//reattach to this object
|
||||
_scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay
|
||||
}
|
||||
|
||||
if (_panelItems.Count < 1) return;
|
||||
|
||||
float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count);
|
||||
|
||||
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
|
||||
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
|
||||
|
||||
_itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5);
|
||||
_itemsPanelRT.anchoredPosition = new Vector2(5, 0);
|
||||
|
||||
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth);
|
||||
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
|
||||
if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true);
|
||||
|
||||
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0);
|
||||
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x);
|
||||
}
|
||||
|
||||
public void OnValueChanged(string currText)
|
||||
{
|
||||
Text = currText;
|
||||
RedrawPanel();
|
||||
//Debug.Log("value changed to: " + currText);
|
||||
|
||||
if (_panelItems.Count == 0)
|
||||
{
|
||||
_isPanelActive = true;//this makes it get turned off
|
||||
ToggleDropdownPanel(false);
|
||||
}
|
||||
else if (!_isPanelActive)
|
||||
{
|
||||
ToggleDropdownPanel(false);
|
||||
}
|
||||
OnSelectionChanged.Invoke(Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle the drop down list
|
||||
/// </summary>
|
||||
/// <param name="directClick"> whether an item was directly clicked on</param>
|
||||
public void ToggleDropdownPanel(bool directClick)
|
||||
{
|
||||
_isPanelActive = !_isPanelActive;
|
||||
|
||||
_overlayRT.gameObject.SetActive(_isPanelActive);
|
||||
if (_isPanelActive)
|
||||
{
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
else if (directClick)
|
||||
{
|
||||
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd26acd32e1be2747b9e5f3587b2b1d5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,389 @@
|
||||
///Credit perchik
|
||||
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
|
||||
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to the UI class which creates a dropdown list
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[AddComponentMenu("UI/Extensions/Dropdown List")]
|
||||
public class DropDownList : MonoBehaviour
|
||||
{
|
||||
public Color disabledTextColor;
|
||||
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
|
||||
|
||||
public List<DropDownListItem> Items;
|
||||
public bool OverrideHighlighted = true;
|
||||
|
||||
//private bool isInitialized = false;
|
||||
private bool _isPanelActive = false;
|
||||
private bool _hasDrawnOnce = false;
|
||||
|
||||
private DropDownListButton _mainButton;
|
||||
|
||||
private RectTransform _rectTransform;
|
||||
|
||||
private RectTransform _overlayRT;
|
||||
private RectTransform _scrollPanelRT;
|
||||
private RectTransform _scrollBarRT;
|
||||
private RectTransform _slidingAreaRT;
|
||||
private RectTransform _scrollHandleRT;
|
||||
private RectTransform _itemsPanelRT;
|
||||
private Canvas _canvas;
|
||||
private RectTransform _canvasRT;
|
||||
|
||||
private ScrollRect _scrollRect;
|
||||
|
||||
private List<DropDownListButton> _panelItems;
|
||||
|
||||
private GameObject _itemTemplate;
|
||||
|
||||
[SerializeField]
|
||||
private float _scrollBarWidth = 20.0f;
|
||||
public float ScrollBarWidth
|
||||
{
|
||||
get { return _scrollBarWidth; }
|
||||
set
|
||||
{
|
||||
_scrollBarWidth = value;
|
||||
RedrawPanel();
|
||||
}
|
||||
}
|
||||
|
||||
// private int scrollOffset; //offset of the selected item
|
||||
private int _selectedIndex = -1;
|
||||
|
||||
[SerializeField]
|
||||
private int _itemsToDisplay;
|
||||
public int ItemsToDisplay
|
||||
{
|
||||
get { return _itemsToDisplay; }
|
||||
set
|
||||
{
|
||||
_itemsToDisplay = value;
|
||||
RedrawPanel();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectFirstItemOnStart = false;
|
||||
|
||||
[SerializeField]
|
||||
private bool _displayPanelAbove = false;
|
||||
|
||||
[System.Serializable]
|
||||
public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<int> {
|
||||
}
|
||||
// fires when item is changed;
|
||||
public SelectionChangedEvent OnSelectionChanged;
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Initialize();
|
||||
if (SelectFirstItemOnStart && Items.Count > 0) {
|
||||
ToggleDropdownPanel (false);
|
||||
OnItemClicked (0);
|
||||
}
|
||||
RedrawPanel();
|
||||
}
|
||||
|
||||
private bool Initialize()
|
||||
{
|
||||
bool success = true;
|
||||
try
|
||||
{
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
_mainButton = new DropDownListButton(_rectTransform.Find("MainButton").gameObject);
|
||||
|
||||
_overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
|
||||
_overlayRT.gameObject.SetActive(false);
|
||||
|
||||
|
||||
_scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>();
|
||||
_scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>();
|
||||
_slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
|
||||
_scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>();
|
||||
_itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>();
|
||||
//itemPanelLayout = itemsPanelRT.gameObject.GetComponent<LayoutGroup>();
|
||||
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
_canvasRT = _canvas.GetComponent<RectTransform>();
|
||||
|
||||
_scrollRect = _scrollPanelRT.GetComponent<ScrollRect>();
|
||||
_scrollRect.scrollSensitivity = _rectTransform.sizeDelta.y / 2;
|
||||
_scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
_scrollRect.content = _itemsPanelRT;
|
||||
|
||||
|
||||
_itemTemplate = _rectTransform.Find("ItemTemplate").gameObject;
|
||||
_itemTemplate.SetActive(false);
|
||||
}
|
||||
catch (System.NullReferenceException ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception");
|
||||
success = false;
|
||||
}
|
||||
|
||||
_panelItems = new List<DropDownListButton>();
|
||||
|
||||
RebuildPanel();
|
||||
RedrawPanel();
|
||||
return success;
|
||||
}
|
||||
|
||||
// currently just using items in the list instead of being able to add to it.
|
||||
/// <summary>
|
||||
/// Rebuilds the list from a new collection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// NOTE, this will clear all existing items
|
||||
/// </remarks>
|
||||
/// <param name="list"></param>
|
||||
public void RefreshItems(params object[] list)
|
||||
{
|
||||
Items.Clear();
|
||||
List<DropDownListItem> ddItems = new List<DropDownListItem>();
|
||||
foreach (var obj in list)
|
||||
{
|
||||
if (obj is DropDownListItem)
|
||||
{
|
||||
ddItems.Add((DropDownListItem)obj);
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
ddItems.Add(new DropDownListItem(caption: (string)obj));
|
||||
}
|
||||
else if (obj is Sprite)
|
||||
{
|
||||
ddItems.Add(new DropDownListItem(image: (Sprite)obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception("Only ComboBoxItems, Strings, and Sprite types are allowed");
|
||||
}
|
||||
}
|
||||
Items.AddRange(ddItems);
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an additional item to the drop down list (recommended)
|
||||
/// </summary>
|
||||
/// <param name="item">Item of type DropDownListItem</param>
|
||||
public void AddItem(DropDownListItem item)
|
||||
{
|
||||
Items.Add(item);
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an additional drop down list item using a string name
|
||||
/// </summary>
|
||||
/// <param name="item">Item of type String</param>
|
||||
public void AddItem(string item)
|
||||
{
|
||||
Items.Add(new DropDownListItem(caption: (string)item));
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an additional drop down list item using a sprite image
|
||||
/// </summary>
|
||||
/// <param name="item">Item of type UI Sprite</param>
|
||||
public void AddItem(Sprite item)
|
||||
{
|
||||
Items.Add(new DropDownListItem(image: (Sprite)item));
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the drop down list (recommended)
|
||||
/// </summary>
|
||||
/// <param name="item">Item of type DropDownListItem</param>
|
||||
public void RemoveItem(DropDownListItem item)
|
||||
{
|
||||
Items.Remove(item);
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the drop down list item using a string name
|
||||
/// </summary>
|
||||
/// <param name="item">Item of type String</param>
|
||||
public void RemoveItem(string item)
|
||||
{
|
||||
Items.Remove(new DropDownListItem(caption: (string)item));
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the drop down list item using a sprite image
|
||||
/// </summary>
|
||||
/// <param name="item">Item of type UI Sprite</param>
|
||||
public void RemoveItem(Sprite item)
|
||||
{
|
||||
Items.Remove(new DropDownListItem(image: (Sprite)item));
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
public void ResetItems()
|
||||
{
|
||||
Items.Clear();
|
||||
RebuildPanel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the contents of the panel in response to items being added.
|
||||
/// </summary>
|
||||
private void RebuildPanel()
|
||||
{
|
||||
if (Items.Count == 0) return;
|
||||
|
||||
int indx = _panelItems.Count;
|
||||
while (_panelItems.Count < Items.Count)
|
||||
{
|
||||
GameObject newItem = Instantiate(_itemTemplate) as GameObject;
|
||||
newItem.name = "Item " + indx;
|
||||
newItem.transform.SetParent(_itemsPanelRT, false);
|
||||
|
||||
_panelItems.Add(new DropDownListButton(newItem));
|
||||
indx++;
|
||||
}
|
||||
for (int i = 0; i < _panelItems.Count; i++)
|
||||
{
|
||||
if (i < Items.Count)
|
||||
{
|
||||
DropDownListItem item = Items[i];
|
||||
|
||||
_panelItems[i].txt.text = item.Caption;
|
||||
if (item.IsDisabled) _panelItems[i].txt.color = disabledTextColor;
|
||||
|
||||
if (_panelItems[i].btnImg != null) _panelItems[i].btnImg.sprite = null;//hide the button image
|
||||
_panelItems[i].img.sprite = item.Image;
|
||||
_panelItems[i].img.color = (item.Image == null) ? new Color(1, 1, 1, 0)
|
||||
: item.IsDisabled ? new Color(1, 1, 1, .5f)
|
||||
: Color.white;
|
||||
int ii = i; //have to copy the variable for use in anonymous function
|
||||
_panelItems[i].btn.onClick.RemoveAllListeners();
|
||||
_panelItems[i].btn.onClick.AddListener(() =>
|
||||
{
|
||||
OnItemClicked(ii);
|
||||
if (item.OnSelect != null) item.OnSelect();
|
||||
});
|
||||
}
|
||||
_panelItems[i].gameobject.SetActive(i < Items.Count);// if we have more thanks in the panel than Items in the list hide them
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemClicked(int indx)
|
||||
{
|
||||
//Debug.Log("item " + indx + " clicked");
|
||||
if (indx != _selectedIndex && OnSelectionChanged != null) OnSelectionChanged.Invoke(indx);
|
||||
|
||||
_selectedIndex = indx;
|
||||
ToggleDropdownPanel(true);
|
||||
UpdateSelected();
|
||||
}
|
||||
|
||||
private void UpdateSelected()
|
||||
{
|
||||
SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
|
||||
if (SelectedItem == null) return;
|
||||
|
||||
bool hasImage = SelectedItem.Image != null;
|
||||
if (hasImage)
|
||||
{
|
||||
_mainButton.img.sprite = SelectedItem.Image;
|
||||
_mainButton.img.color = Color.white;
|
||||
|
||||
//if (Interactable) mainButton.img.color = Color.white;
|
||||
//else mainButton.img.color = new Color(1, 1, 1, .5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mainButton.img.sprite = null;
|
||||
}
|
||||
|
||||
_mainButton.txt.text = SelectedItem.Caption;
|
||||
|
||||
//update selected index color
|
||||
if (OverrideHighlighted)
|
||||
{
|
||||
|
||||
for (int i = 0; i < _itemsPanelRT.childCount; i++)
|
||||
{
|
||||
_panelItems[i].btnImg.color = (_selectedIndex == i) ? _mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void RedrawPanel()
|
||||
{
|
||||
float scrollbarWidth = Items.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
|
||||
|
||||
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _mainButton.rectTransform.sizeDelta)
|
||||
{
|
||||
_hasDrawnOnce = true;
|
||||
_mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
|
||||
_mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
|
||||
_mainButton.txt.rectTransform.offsetMax = new Vector2(4, 0);
|
||||
|
||||
_scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay
|
||||
_scrollPanelRT.anchoredPosition = _displayPanelAbove ?
|
||||
new Vector2(0, _rectTransform.sizeDelta.y * ItemsToDisplay - 1) :
|
||||
new Vector2(0, -_rectTransform.sizeDelta.y);
|
||||
|
||||
//make the overlay fill the screen
|
||||
_overlayRT.SetParent(_canvas.transform, false); //attach it to top level object
|
||||
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
|
||||
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
|
||||
|
||||
_overlayRT.SetParent(transform, true);//reattach to this object
|
||||
_scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay
|
||||
}
|
||||
|
||||
if (Items.Count < 1) return;
|
||||
|
||||
float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, Items.Count);
|
||||
|
||||
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
|
||||
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
|
||||
|
||||
_itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5);
|
||||
_itemsPanelRT.anchoredPosition = new Vector2(5, 0);
|
||||
|
||||
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth);
|
||||
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
|
||||
if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true);
|
||||
|
||||
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0);
|
||||
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle the drop down list
|
||||
/// </summary>
|
||||
/// <param name="directClick"> whether an item was directly clicked on</param>
|
||||
public void ToggleDropdownPanel(bool directClick)
|
||||
{
|
||||
_overlayRT.transform.localScale = new Vector3(1, 1, 1);
|
||||
_scrollBarRT.transform.localScale = new Vector3(1, 1, 1);
|
||||
_isPanelActive = !_isPanelActive;
|
||||
_overlayRT.gameObject.SetActive(_isPanelActive);
|
||||
if (_isPanelActive)
|
||||
{
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
else if (directClick)
|
||||
{
|
||||
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a00cad80d8a47b438b394bebe77d0d2
|
||||
timeCreated: 1492278993
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,26 @@
|
||||
///Credit perchik
|
||||
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(RectTransform), typeof(Button))]
|
||||
public class DropDownListButton
|
||||
{
|
||||
public RectTransform rectTransform;
|
||||
public Button btn;
|
||||
public Text txt;
|
||||
public Image btnImg;
|
||||
public Image img;
|
||||
public GameObject gameobject;
|
||||
|
||||
public DropDownListButton(GameObject btnObj)
|
||||
{
|
||||
gameobject = btnObj;
|
||||
rectTransform = btnObj.GetComponent<RectTransform>();
|
||||
btnImg = btnObj.GetComponent<Image>();
|
||||
btn = btnObj.GetComponent<Button>();
|
||||
txt = rectTransform.Find("Text").GetComponent<Text>();
|
||||
img = rectTransform.Find("Image").GetComponent<Image>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 491c635e1f309294187ff24b5c71e8cb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,100 @@
|
||||
///Credit perchik
|
||||
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
|
||||
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[Serializable]
|
||||
public class DropDownListItem
|
||||
{
|
||||
[SerializeField]
|
||||
private string _caption;
|
||||
/// <summary>
|
||||
/// Caption of the Item
|
||||
/// </summary>
|
||||
public string Caption
|
||||
{
|
||||
get
|
||||
{
|
||||
return _caption;
|
||||
}
|
||||
set
|
||||
{
|
||||
_caption = value;
|
||||
if (OnUpdate != null)
|
||||
OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Sprite _image;
|
||||
/// <summary>
|
||||
/// Image component of the Item
|
||||
/// </summary>
|
||||
public Sprite Image
|
||||
{
|
||||
get
|
||||
{
|
||||
return _image;
|
||||
}
|
||||
set
|
||||
{
|
||||
_image = value;
|
||||
if (OnUpdate != null)
|
||||
OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool _isDisabled;
|
||||
/// <summary>
|
||||
/// Is the Item currently enabled?
|
||||
/// </summary>
|
||||
public bool IsDisabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isDisabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isDisabled = value;
|
||||
if (OnUpdate != null)
|
||||
OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private string _id;
|
||||
///<summary>
|
||||
///ID exists so that an item can have a caption and a value like in traditional windows forms. IE. an item may be a student's name, and the ID can be the student's ID number
|
||||
///</summary>
|
||||
public string ID
|
||||
{
|
||||
get { return _id; }
|
||||
set { _id = value; }
|
||||
}
|
||||
|
||||
public Action OnSelect = null; //action to be called when this item is selected
|
||||
|
||||
internal Action OnUpdate = null; //action to be called when something changes.
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Drop Down List panelItems
|
||||
/// </summary>
|
||||
/// <param name="caption">Caption for the item </param>
|
||||
/// <param name="val">ID of the item </param>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="disabled">Should the item start disabled</param>
|
||||
/// <param name="onSelect">Action to be called when this item is selected</param>
|
||||
public DropDownListItem(string caption = "", string inId = "", Sprite image = null, bool disabled = false, Action onSelect = null)
|
||||
{
|
||||
_caption = caption;
|
||||
_image = image;
|
||||
_id = inId;
|
||||
_isDisabled = disabled;
|
||||
OnSelect = onSelect;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1dd512d0ed874740902b01df530b2dc
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,197 @@
|
||||
/// Credit SimonDarksideJ
|
||||
/// Sourced from my head
|
||||
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/Cooldown Button")]
|
||||
public class CooldownButton : MonoBehaviour, IPointerDownHandler
|
||||
{
|
||||
#region Sub-Classes
|
||||
[System.Serializable]
|
||||
public class CooldownButtonEvent : UnityEvent<PointerEventData.InputButton> { }
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
[SerializeField]
|
||||
private float cooldownTimeout;
|
||||
[SerializeField]
|
||||
private float cooldownSpeed = 1;
|
||||
[SerializeField][ReadOnly]
|
||||
private bool cooldownActive;
|
||||
[SerializeField][ReadOnly]
|
||||
private bool cooldownInEffect;
|
||||
[SerializeField][ReadOnly]
|
||||
private float cooldownTimeElapsed;
|
||||
[SerializeField][ReadOnly]
|
||||
private float cooldownTimeRemaining;
|
||||
[SerializeField][ReadOnly]
|
||||
private int cooldownPercentRemaining;
|
||||
[SerializeField][ReadOnly]
|
||||
private int cooldownPercentComplete;
|
||||
|
||||
PointerEventData buttonSource;
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public float CooldownTimeout
|
||||
{
|
||||
get { return cooldownTimeout; }
|
||||
set { cooldownTimeout = value; }
|
||||
}
|
||||
|
||||
public float CooldownSpeed
|
||||
{
|
||||
get { return cooldownSpeed; }
|
||||
set { cooldownSpeed = value; }
|
||||
}
|
||||
|
||||
public bool CooldownInEffect
|
||||
{
|
||||
get { return cooldownInEffect; }
|
||||
}
|
||||
|
||||
public bool CooldownActive
|
||||
{
|
||||
get { return cooldownActive; }
|
||||
set { cooldownActive = value; }
|
||||
}
|
||||
|
||||
public float CooldownTimeElapsed
|
||||
{
|
||||
get { return cooldownTimeElapsed; }
|
||||
set { cooldownTimeElapsed = value; }
|
||||
}
|
||||
|
||||
public float CooldownTimeRemaining
|
||||
{
|
||||
get { return cooldownTimeRemaining; }
|
||||
}
|
||||
|
||||
public int CooldownPercentRemaining
|
||||
{
|
||||
get { return cooldownPercentRemaining; }
|
||||
}
|
||||
|
||||
public int CooldownPercentComplete
|
||||
{
|
||||
get { return cooldownPercentComplete; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
[Tooltip("Event that fires when a button is initially pressed down")]
|
||||
public CooldownButtonEvent OnCooldownStart;
|
||||
[Tooltip("Event that fires when a button is released")]
|
||||
public CooldownButtonEvent OnButtonClickDuringCooldown;
|
||||
[Tooltip("Event that continually fires while a button is held down")]
|
||||
public CooldownButtonEvent OnCoolDownFinish;
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
if (CooldownActive)
|
||||
{
|
||||
cooldownTimeRemaining -= Time.deltaTime * cooldownSpeed;
|
||||
cooldownTimeElapsed = CooldownTimeout - CooldownTimeRemaining;
|
||||
if (cooldownTimeRemaining < 0)
|
||||
{
|
||||
StopCooldown();
|
||||
}
|
||||
else
|
||||
{
|
||||
cooldownPercentRemaining = (int)(100 * cooldownTimeRemaining * CooldownTimeout / 100);
|
||||
cooldownPercentComplete = (int)((CooldownTimeout - cooldownTimeRemaining) / CooldownTimeout * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Pause Cooldown without resetting values, allows Restarting of cooldown
|
||||
/// </summary>
|
||||
public void PauseCooldown()
|
||||
{
|
||||
if (CooldownInEffect)
|
||||
{
|
||||
CooldownActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart a paused cooldown
|
||||
/// </summary>
|
||||
public void RestartCooldown()
|
||||
{
|
||||
if (CooldownInEffect)
|
||||
{
|
||||
CooldownActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a cooldown from outside
|
||||
/// </summary>
|
||||
public void StartCooldown()
|
||||
{
|
||||
PointerEventData emptySource = new PointerEventData(EventSystem.current);
|
||||
buttonSource = emptySource;
|
||||
OnCooldownStart.Invoke(emptySource.button);
|
||||
cooldownTimeRemaining = cooldownTimeout;
|
||||
CooldownActive = cooldownInEffect = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop a running Cooldown and reset all values
|
||||
/// </summary>
|
||||
public void StopCooldown()
|
||||
{
|
||||
cooldownTimeElapsed = CooldownTimeout;
|
||||
cooldownTimeRemaining = 0;
|
||||
cooldownPercentRemaining = 0;
|
||||
cooldownPercentComplete = 100;
|
||||
cooldownActive = cooldownInEffect = false;
|
||||
if (OnCoolDownFinish != null) OnCoolDownFinish.Invoke(buttonSource.button);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop a running Cooldown and retain current values
|
||||
/// </summary>
|
||||
public void CancelCooldown()
|
||||
{
|
||||
cooldownActive = cooldownInEffect = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPointerDownHandler
|
||||
|
||||
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
buttonSource = eventData;
|
||||
if (CooldownInEffect)
|
||||
{
|
||||
if (OnButtonClickDuringCooldown != null) OnButtonClickDuringCooldown.Invoke(eventData.button);
|
||||
}
|
||||
if (!CooldownInEffect)
|
||||
{
|
||||
if(OnCooldownStart != null) OnCooldownStart.Invoke(eventData.button);
|
||||
cooldownTimeRemaining = cooldownTimeout;
|
||||
cooldownActive = cooldownInEffect = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74a62d95b3be0fe47871dd48fca58b7d
|
||||
timeCreated: 1498387990
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,83 @@
|
||||
/// Credit Zelek
|
||||
/// Sourced from - http://forum.unity3d.com/threads/inputfield-focus-and-unfocus.306634/
|
||||
/// Usage, assign component to Input field, set OnEndEdit function to the one in this script and the Click for the submit button to the buttonPressed function.
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(InputField))]
|
||||
[AddComponentMenu("UI/Extensions/InputFocus")]
|
||||
public class InputFocus : MonoBehaviour
|
||||
{
|
||||
#region Private Variables
|
||||
|
||||
// The input field we use for chat
|
||||
protected InputField _inputField;
|
||||
|
||||
// When set to true, we will ignore the next time the "Enter" key is released
|
||||
public bool _ignoreNextActivation = false;
|
||||
|
||||
#endregion
|
||||
|
||||
void Start()
|
||||
{
|
||||
_inputField = GetComponent<InputField>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Check if the "Enter" key was just released with the chat input not focused
|
||||
if (UIExtensionsInputManager.GetKeyUp(KeyCode.Return) && !_inputField.isFocused)
|
||||
{
|
||||
// If we need to ignore the keypress, do nothing - otherwise activate the input field
|
||||
if (_ignoreNextActivation)
|
||||
{
|
||||
_ignoreNextActivation = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputField.Select();
|
||||
_inputField.ActivateInputField();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void buttonPressed()
|
||||
{
|
||||
// Do whatever you want with the input field text here
|
||||
|
||||
// Make note of whether the input string was empty, and then clear it out
|
||||
bool wasEmpty = _inputField.text == "";
|
||||
_inputField.text = "";
|
||||
|
||||
// If the string was not empty, we should reactivate the input field
|
||||
if (!wasEmpty)
|
||||
{
|
||||
_inputField.Select();
|
||||
_inputField.ActivateInputField();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEndEdit(string textString)
|
||||
{
|
||||
// If the edit ended because we clicked away, don't do anything extra
|
||||
if (!UIExtensionsInputManager.GetKeyDown(KeyCode.Return))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Do whatever you want with the input field text here
|
||||
|
||||
// Make note of whether the input string was empty, and then clear it out
|
||||
bool wasEmpty = _inputField.text == "";
|
||||
_inputField.text = "";
|
||||
|
||||
// if the input string was empty, then allow the field to deactivate
|
||||
if (wasEmpty)
|
||||
{
|
||||
_ignoreNextActivation = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a18c7c81729dadf40aee407a0cff0462
|
||||
timeCreated: 1440843410
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,39 @@
|
||||
/// Credit Erdener Gonenc - @PixelEnvision
|
||||
/*USAGE: Simply use that instead of the regular ScrollRect */
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu ("UI/Extensions/MultiTouchScrollRect")]
|
||||
public class MultiTouchScrollRect : ScrollRect
|
||||
{
|
||||
private int pid = -100;
|
||||
|
||||
/// <summary>
|
||||
/// Begin drag event
|
||||
/// </summary>
|
||||
public override void OnBeginDrag (UnityEngine.EventSystems.PointerEventData eventData)
|
||||
{
|
||||
pid = eventData.pointerId;
|
||||
base.OnBeginDrag (eventData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drag event
|
||||
/// </summary>
|
||||
public override void OnDrag (UnityEngine.EventSystems.PointerEventData eventData)
|
||||
{
|
||||
if (pid == eventData.pointerId)
|
||||
base.OnDrag (eventData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End drag event
|
||||
/// </summary>
|
||||
public override void OnEndDrag (UnityEngine.EventSystems.PointerEventData eventData)
|
||||
{
|
||||
pid = -100;
|
||||
base.OnEndDrag (eventData);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f2bebd34aaa76541b97e61752a7d262
|
||||
timeCreated: 1492273203
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,258 @@
|
||||
/// Credit mgear, SimonDarksideJ
|
||||
/// Sourced from - https://forum.unity3d.com/threads/radial-slider-circle-slider.326392/#post-3143582
|
||||
/// Updated to include lerping features and programmatic access to angle/value
|
||||
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/Radial Slider")]
|
||||
[RequireComponent(typeof(Image))]
|
||||
public class RadialSlider : MonoBehaviour, IPointerEnterHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler
|
||||
{
|
||||
private bool isPointerDown, isPointerReleased, lerpInProgress;
|
||||
private Vector2 m_localPos, m_screenPos;
|
||||
private float m_targetAngle, m_lerpTargetAngle, m_startAngle, m_currentLerpTime, m_lerpTime;
|
||||
private Camera m_eventCamera;
|
||||
private Image m_image;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Radial Gradient Start Color")]
|
||||
private Color m_startColor = Color.green;
|
||||
[SerializeField]
|
||||
[Tooltip("Radial Gradient End Color")]
|
||||
private Color m_endColor = Color.red;
|
||||
[Tooltip("Move slider absolute or use Lerping?\nDragging only supported with absolute")]
|
||||
[SerializeField]
|
||||
private bool m_lerpToTarget;
|
||||
[Tooltip("Curve to apply to the Lerp\nMust be set to enable Lerp")]
|
||||
[SerializeField]
|
||||
private AnimationCurve m_lerpCurve;
|
||||
[Tooltip("Event fired when value of control changes, outputs an INT angle value")]
|
||||
[SerializeField]
|
||||
private RadialSliderValueChangedEvent _onValueChanged = new RadialSliderValueChangedEvent();
|
||||
[Tooltip("Event fired when value of control changes, outputs a TEXT angle value")]
|
||||
[SerializeField]
|
||||
private RadialSliderTextValueChangedEvent _onTextValueChanged = new RadialSliderTextValueChangedEvent();
|
||||
|
||||
public float Angle
|
||||
{
|
||||
get { return RadialImage.fillAmount * 360f; }
|
||||
set
|
||||
{
|
||||
if (LerpToTarget)
|
||||
{
|
||||
StartLerp(value / 360f);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateRadialImage(value / 360f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Value
|
||||
{
|
||||
get { return RadialImage.fillAmount; }
|
||||
set
|
||||
{
|
||||
if (LerpToTarget)
|
||||
{
|
||||
StartLerp(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateRadialImage(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color EndColor
|
||||
{
|
||||
get { return m_endColor; }
|
||||
set { m_endColor = value; }
|
||||
}
|
||||
|
||||
public Color StartColor
|
||||
{
|
||||
get { return m_startColor; }
|
||||
set { m_startColor = value; }
|
||||
}
|
||||
|
||||
public bool LerpToTarget
|
||||
{
|
||||
get { return m_lerpToTarget; }
|
||||
set { m_lerpToTarget = value; }
|
||||
}
|
||||
|
||||
public AnimationCurve LerpCurve
|
||||
{
|
||||
get { return m_lerpCurve; }
|
||||
set { m_lerpCurve = value; m_lerpTime = LerpCurve[LerpCurve.length - 1].time; }
|
||||
}
|
||||
|
||||
public bool LerpInProgress
|
||||
{
|
||||
get { return lerpInProgress; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RadialSliderValueChangedEvent : UnityEvent<int> { }
|
||||
[Serializable]
|
||||
public class RadialSliderTextValueChangedEvent : UnityEvent<string> { }
|
||||
|
||||
public Image RadialImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_image == null)
|
||||
{
|
||||
m_image = GetComponent<Image>();
|
||||
m_image.type = Image.Type.Filled;
|
||||
m_image.fillMethod = Image.FillMethod.Radial360;
|
||||
m_image.fillAmount = 0;
|
||||
}
|
||||
return m_image;
|
||||
}
|
||||
}
|
||||
|
||||
public RadialSliderValueChangedEvent onValueChanged
|
||||
{
|
||||
get { return _onValueChanged; }
|
||||
set { _onValueChanged = value; }
|
||||
}
|
||||
public RadialSliderTextValueChangedEvent onTextValueChanged
|
||||
{
|
||||
get { return _onTextValueChanged; }
|
||||
set { _onTextValueChanged = value; }
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (LerpCurve != null && LerpCurve.length > 0)
|
||||
{
|
||||
m_lerpTime = LerpCurve[LerpCurve.length - 1].time;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lerpTime = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (isPointerDown)
|
||||
{
|
||||
m_targetAngle = GetAngleFromMousePoint();
|
||||
if (!lerpInProgress)
|
||||
{
|
||||
if (!LerpToTarget)
|
||||
{
|
||||
UpdateRadialImage(m_targetAngle);
|
||||
|
||||
NotifyValueChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isPointerReleased) StartLerp(m_targetAngle);
|
||||
isPointerReleased = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lerpInProgress && Value != m_lerpTargetAngle)
|
||||
{
|
||||
m_currentLerpTime += Time.deltaTime;
|
||||
float perc = m_currentLerpTime / m_lerpTime;
|
||||
if (LerpCurve != null && LerpCurve.length > 0)
|
||||
{
|
||||
UpdateRadialImage(Mathf.Lerp(m_startAngle, m_lerpTargetAngle, LerpCurve.Evaluate(perc)));
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateRadialImage(Mathf.Lerp(m_startAngle, m_lerpTargetAngle, perc));
|
||||
}
|
||||
}
|
||||
if (m_currentLerpTime >= m_lerpTime || Value == m_lerpTargetAngle)
|
||||
{
|
||||
lerpInProgress = false;
|
||||
UpdateRadialImage(m_lerpTargetAngle);
|
||||
NotifyValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartLerp(float targetAngle)
|
||||
{
|
||||
if (!lerpInProgress)
|
||||
{
|
||||
m_startAngle = Value;
|
||||
m_lerpTargetAngle = targetAngle;
|
||||
m_currentLerpTime = 0f;
|
||||
lerpInProgress = true;
|
||||
}
|
||||
}
|
||||
|
||||
private float GetAngleFromMousePoint()
|
||||
{
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(transform as RectTransform, m_screenPos, m_eventCamera, out m_localPos);
|
||||
|
||||
// radial pos of the mouse position.
|
||||
return (Mathf.Atan2(-m_localPos.y, m_localPos.x) * 180f / Mathf.PI + 180f) / 360f;
|
||||
}
|
||||
|
||||
private void UpdateRadialImage(float targetAngle)
|
||||
{
|
||||
RadialImage.fillAmount = targetAngle;
|
||||
|
||||
RadialImage.color = Color.Lerp(m_startColor, m_endColor, targetAngle);
|
||||
}
|
||||
|
||||
private void NotifyValueChanged()
|
||||
{
|
||||
_onValueChanged.Invoke((int)(m_targetAngle * 360f));
|
||||
_onTextValueChanged.Invoke(((int)(m_targetAngle * 360f)).ToString());
|
||||
}
|
||||
|
||||
//#if UNITY_EDITOR
|
||||
|
||||
// private void OnValidate()
|
||||
// {
|
||||
// if (LerpToTarget && LerpCurve.length < 2)
|
||||
// {
|
||||
// LerpToTarget = false;
|
||||
// Debug.LogError("You need to define a Lerp Curve to enable 'Lerp To Target'");
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
#region Interfaces
|
||||
// Called when the pointer enters our GUI component.
|
||||
// Start tracking the mouse
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = eventData.position;
|
||||
m_eventCamera = eventData.enterEventCamera;
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = eventData.position;
|
||||
m_eventCamera = eventData.enterEventCamera;
|
||||
isPointerDown = true;
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = Vector2.zero;
|
||||
isPointerDown = false;
|
||||
isPointerReleased = true;
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
m_screenPos = eventData.position;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 803cebee00d5c504e930205383017dc1
|
||||
timeCreated: 1432062988
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,604 @@
|
||||
/// Credit Ben MacKinnon @Dover8
|
||||
/// Sourced from - https://github.com/Dover8/Unity-UI-Extensions/tree/range-slider
|
||||
/// Usage: Extension of the standard slider. Two handles determine a low and high value between a Min and Max.
|
||||
/// Raises a UnityEvent passing the low and high values
|
||||
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/Range Slider", 34)]
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class RangeSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
public class RangeSliderEvent : UnityEvent<float, float> { }
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_FillRect;
|
||||
|
||||
public RectTransform FillRect { get { return m_FillRect; } set { if (SetClass(ref m_FillRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_LowHandleRect;
|
||||
|
||||
public RectTransform LowHandleRect { get { return m_LowHandleRect; } set { if (SetClass(ref m_LowHandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_HighHandleRect;
|
||||
|
||||
public RectTransform HighHandleRect { get { return m_HighHandleRect; } set { if (SetClass(ref m_HighHandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
|
||||
|
||||
[Space]
|
||||
|
||||
[SerializeField]
|
||||
private float m_MinValue = 0;
|
||||
|
||||
public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { SetLow(m_LowValue); SetHigh(m_HighValue); UpdateVisuals(); } } }
|
||||
|
||||
|
||||
[SerializeField]
|
||||
private float m_MaxValue = 1;
|
||||
|
||||
public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { SetLow(m_LowValue); SetHigh(m_HighValue); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private bool m_WholeNumbers = false;
|
||||
|
||||
public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { SetLow(m_LowValue); SetHigh(m_HighValue); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private float m_LowValue;
|
||||
public virtual float LowValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (WholeNumbers)
|
||||
{
|
||||
return Mathf.Round(m_LowValue);
|
||||
}
|
||||
|
||||
return m_LowValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetLow(value);
|
||||
}
|
||||
}
|
||||
|
||||
public float NormalizedLowValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Approximately(MinValue, MaxValue))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Mathf.InverseLerp(MinValue, MaxValue, LowValue);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.LowValue = Mathf.Lerp(MinValue, MaxValue, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[SerializeField]
|
||||
private float m_HighValue;
|
||||
public virtual float HighValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (WholeNumbers)
|
||||
{
|
||||
return Mathf.Round(m_HighValue);
|
||||
}
|
||||
|
||||
return m_HighValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetHigh(value);
|
||||
}
|
||||
}
|
||||
|
||||
public float NormalizedHighValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Approximately(MinValue, MaxValue))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return Mathf.InverseLerp(MinValue, MaxValue, HighValue);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.HighValue = Mathf.Lerp(MinValue, MaxValue, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the slider without invoking onValueChanged callback.
|
||||
/// </summary>
|
||||
/// <param name="input">The new value for the slider.</param>
|
||||
public virtual void SetValueWithoutNotify(float low, float high)
|
||||
{
|
||||
SetLow(low, false);
|
||||
SetHigh(high, false);
|
||||
}
|
||||
|
||||
[Space]
|
||||
|
||||
[SerializeField]
|
||||
private RangeSliderEvent m_OnValueChanged = new RangeSliderEvent();
|
||||
|
||||
public RangeSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
|
||||
|
||||
// Private fields
|
||||
|
||||
/// <summary>
|
||||
/// An Enum that says in what state we and interacting with the slider
|
||||
/// </summary>
|
||||
private enum InteractionState
|
||||
{
|
||||
Low,
|
||||
High,
|
||||
Bar,
|
||||
None
|
||||
}
|
||||
|
||||
private InteractionState interactionState = InteractionState.None;
|
||||
|
||||
private Image m_FillImage;
|
||||
private Transform m_FillTransform;
|
||||
private RectTransform m_FillContainerRect;
|
||||
private Transform m_HighHandleTransform;
|
||||
private RectTransform m_HighHandleContainerRect;
|
||||
private Transform m_LowHandleTransform;
|
||||
private RectTransform m_LowHandleContainerRect;
|
||||
|
||||
// The offset from handle position to mouse down position
|
||||
private Vector2 m_LowOffset = Vector2.zero;
|
||||
// The offset from handle position to mouse down position
|
||||
private Vector2 m_HighOffset = Vector2.zero;
|
||||
|
||||
private DrivenRectTransformTracker m_Tracker;
|
||||
|
||||
// This "delayed" mechanism is required for case 1037681.
|
||||
private bool m_DelayedUpdateVisuals = false;
|
||||
|
||||
// Size of each step.
|
||||
float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } }
|
||||
|
||||
protected RangeSlider()
|
||||
{ }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
if (WholeNumbers)
|
||||
{
|
||||
m_MinValue = Mathf.Round(m_MinValue);
|
||||
m_MaxValue = Mathf.Round(m_MaxValue);
|
||||
}
|
||||
|
||||
if (IsActive())
|
||||
{
|
||||
UpdateCachedReferences();
|
||||
SetLow(m_LowValue, false);
|
||||
SetHigh(m_HighValue, false);
|
||||
//Update rects since other things might affect them even if value didn't change
|
||||
m_DelayedUpdateVisuals = true;
|
||||
}
|
||||
|
||||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying)
|
||||
{
|
||||
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual void Rebuild(CanvasUpdate executing)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (executing == CanvasUpdate.Prelayout)
|
||||
{
|
||||
OnValueChanged.Invoke(LowValue, HighValue);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See ICanvasElement.LayoutComplete
|
||||
/// </summary>
|
||||
public virtual void LayoutComplete()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// See ICanvasElement.GraphicUpdateComplete
|
||||
/// </summary>
|
||||
public virtual void GraphicUpdateComplete()
|
||||
{ }
|
||||
|
||||
public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
|
||||
{
|
||||
if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
|
||||
return false;
|
||||
|
||||
currentValue = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
|
||||
{
|
||||
if (currentValue.Equals(newValue))
|
||||
return false;
|
||||
|
||||
currentValue = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
UpdateCachedReferences();
|
||||
SetLow(LowValue, false);
|
||||
SetHigh(HighValue, false);
|
||||
// Update rects since they need to be initialized correctly.
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
m_Tracker.Clear();
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the rect based on the delayed update visuals.
|
||||
/// Got around issue of calling sendMessage from onValidate.
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (m_DelayedUpdateVisuals)
|
||||
{
|
||||
m_DelayedUpdateVisuals = false;
|
||||
UpdateVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDidApplyAnimationProperties()
|
||||
{
|
||||
base.OnDidApplyAnimationProperties();
|
||||
}
|
||||
|
||||
void UpdateCachedReferences()
|
||||
{
|
||||
if (m_FillRect && m_FillRect != (RectTransform)transform)
|
||||
{
|
||||
m_FillTransform = m_FillRect.transform;
|
||||
m_FillImage = m_FillRect.GetComponent<Image>();
|
||||
if (m_FillTransform.parent != null)
|
||||
m_FillContainerRect = m_FillTransform.parent.GetComponent<RectTransform>();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FillRect = null;
|
||||
m_FillContainerRect = null;
|
||||
m_FillImage = null;
|
||||
}
|
||||
|
||||
if (m_HighHandleRect && m_HighHandleRect != (RectTransform)transform)
|
||||
{
|
||||
m_HighHandleTransform = m_HighHandleRect.transform;
|
||||
if (m_HighHandleTransform.parent != null)
|
||||
m_HighHandleContainerRect = m_HighHandleTransform.parent.GetComponent<RectTransform>();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HighHandleRect = null;
|
||||
m_HighHandleContainerRect = null;
|
||||
}
|
||||
|
||||
if (m_LowHandleRect && m_LowHandleRect != (RectTransform)transform)
|
||||
{
|
||||
m_LowHandleTransform = m_LowHandleRect.transform;
|
||||
if (m_LowHandleTransform.parent != null)
|
||||
{
|
||||
m_LowHandleContainerRect = m_LowHandleTransform.parent.GetComponent<RectTransform>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LowHandleRect = null;
|
||||
m_LowHandleContainerRect = null;
|
||||
}
|
||||
}
|
||||
|
||||
void SetLow(float input)
|
||||
{
|
||||
SetLow(input, true);
|
||||
}
|
||||
|
||||
protected virtual void SetLow(float input, bool sendCallback)
|
||||
{
|
||||
// Clamp the input
|
||||
float newValue = Mathf.Clamp(input, MinValue, HighValue); //clamp between min and High
|
||||
if (WholeNumbers)
|
||||
{
|
||||
newValue = Mathf.Round(newValue);
|
||||
}
|
||||
|
||||
// If the stepped value doesn't match the last one, it's time to update
|
||||
if (m_LowValue == newValue)
|
||||
return;
|
||||
|
||||
m_LowValue = newValue;
|
||||
UpdateVisuals();
|
||||
if (sendCallback)
|
||||
{
|
||||
UISystemProfilerApi.AddMarker("RangeSlider.lowValue", this);
|
||||
m_OnValueChanged.Invoke(newValue, HighValue);
|
||||
}
|
||||
}
|
||||
|
||||
void SetHigh(float input)
|
||||
{
|
||||
SetHigh(input, true);
|
||||
}
|
||||
|
||||
protected virtual void SetHigh(float input, bool sendCallback)
|
||||
{
|
||||
// Clamp the input
|
||||
float newValue = Mathf.Clamp(input, LowValue, MaxValue); //clamp between min and High
|
||||
if (WholeNumbers)
|
||||
{
|
||||
newValue = Mathf.Round(newValue);
|
||||
}
|
||||
|
||||
// If the stepped value doesn't match the last one, it's time to update
|
||||
if (m_HighValue == newValue)
|
||||
return;
|
||||
|
||||
m_HighValue = newValue;
|
||||
UpdateVisuals();
|
||||
if (sendCallback)
|
||||
{
|
||||
UISystemProfilerApi.AddMarker("RangeSlider.highValue", this);
|
||||
m_OnValueChanged.Invoke(LowValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void OnRectTransformDimensionsChange()
|
||||
{
|
||||
base.OnRectTransformDimensionsChange();
|
||||
|
||||
//This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
|
||||
if (!IsActive())
|
||||
return;
|
||||
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
|
||||
// Force-update the slider. Useful if you've changed the properties and want it to update visually.
|
||||
private void UpdateVisuals()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
UpdateCachedReferences();
|
||||
#endif
|
||||
|
||||
m_Tracker.Clear();
|
||||
|
||||
if (m_FillContainerRect != null)
|
||||
{
|
||||
m_Tracker.Add(this, m_FillRect, DrivenTransformProperties.Anchors);
|
||||
Vector2 anchorMin = Vector2.zero;
|
||||
Vector2 anchorMax = Vector2.one;
|
||||
|
||||
//this is where some new magic must happen. Slider just uses a filled image
|
||||
//and changes the % of fill. We must move the image anchors to be between the two handles.
|
||||
anchorMin[0] = NormalizedLowValue;
|
||||
anchorMax[0] = NormalizedHighValue;
|
||||
|
||||
m_FillRect.anchorMin = anchorMin;
|
||||
m_FillRect.anchorMax = anchorMax;
|
||||
}
|
||||
|
||||
if (m_LowHandleContainerRect != null)
|
||||
{
|
||||
m_Tracker.Add(this, m_LowHandleRect, DrivenTransformProperties.Anchors);
|
||||
Vector2 anchorMin = Vector2.zero;
|
||||
Vector2 anchorMax = Vector2.one;
|
||||
anchorMin[0] = anchorMax[0] = NormalizedLowValue;
|
||||
m_LowHandleRect.anchorMin = anchorMin;
|
||||
m_LowHandleRect.anchorMax = anchorMax;
|
||||
}
|
||||
|
||||
if (m_HighHandleContainerRect != null)
|
||||
{
|
||||
m_Tracker.Add(this, m_HighHandleRect, DrivenTransformProperties.Anchors);
|
||||
Vector2 anchorMin = Vector2.zero;
|
||||
Vector2 anchorMax = Vector2.one;
|
||||
anchorMin[0] = anchorMax[0] = NormalizedHighValue;
|
||||
m_HighHandleRect.anchorMin = anchorMin;
|
||||
m_HighHandleRect.anchorMax = anchorMax;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the slider's position based on the mouse.
|
||||
void UpdateDrag(PointerEventData eventData, Camera cam)
|
||||
{
|
||||
//this needs to differ from slider in that we have two handles, and need to move the right one.
|
||||
//and if it was neither handle, we will have a separate case where both handles move uniformly
|
||||
//moving the entire range
|
||||
|
||||
//this is where we use our interationState
|
||||
switch (interactionState)
|
||||
{
|
||||
case InteractionState.Low:
|
||||
NormalizedLowValue = CalculateDrag(eventData, cam, m_LowHandleContainerRect, m_LowOffset);
|
||||
break;
|
||||
case InteractionState.High:
|
||||
NormalizedHighValue = CalculateDrag(eventData, cam, m_HighHandleContainerRect, m_HighOffset);
|
||||
break;
|
||||
case InteractionState.Bar:
|
||||
//special case
|
||||
CalculateBarDrag(eventData, cam);
|
||||
break;
|
||||
case InteractionState.None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private float CalculateDrag(PointerEventData eventData, Camera cam, RectTransform containerRect, Vector2 offset)
|
||||
{
|
||||
RectTransform clickRect = containerRect ?? m_FillContainerRect;
|
||||
if (clickRect != null && clickRect.rect.size[0] > 0)
|
||||
{
|
||||
Vector2 localCursor;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
localCursor -= clickRect.rect.position;
|
||||
|
||||
float val = Mathf.Clamp01((localCursor - offset)[0] / clickRect.rect.size[0]);
|
||||
|
||||
return val;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void CalculateBarDrag(PointerEventData eventData, Camera cam)
|
||||
{
|
||||
RectTransform clickRect = m_FillContainerRect;
|
||||
if (clickRect != null && clickRect.rect.size[0] > 0)
|
||||
{
|
||||
Vector2 localCursor;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
localCursor -= clickRect.rect.position;
|
||||
|
||||
//now we need to get the delta drag on the bar
|
||||
//and move both the normalized low and high values by this amount
|
||||
//but also check that neither is going beyond the bounds
|
||||
if (NormalizedLowValue >= 0 && NormalizedHighValue <= 1)
|
||||
{
|
||||
//find the mid point on the current bar
|
||||
float mid = (NormalizedHighValue + NormalizedLowValue)/2;
|
||||
//find where the new mid point should be
|
||||
float val = Mathf.Clamp01((localCursor)[0] / clickRect.rect.size[0]);
|
||||
//calculate the delta
|
||||
float delta = val - mid;
|
||||
//check the clamp range
|
||||
if (NormalizedLowValue + delta < 0)
|
||||
{
|
||||
delta = -NormalizedLowValue;
|
||||
}
|
||||
else if (NormalizedHighValue + delta > 1)
|
||||
{
|
||||
delta = 1 - NormalizedHighValue;
|
||||
}
|
||||
|
||||
//adjust both ends
|
||||
NormalizedLowValue += delta;
|
||||
NormalizedHighValue += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool MayDrag(PointerEventData eventData)
|
||||
{
|
||||
return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if (!MayDrag(eventData))
|
||||
return;
|
||||
|
||||
|
||||
//HANDLE DRAG EVENTS
|
||||
m_LowOffset = m_HighOffset = Vector2.zero;
|
||||
Vector2 localMousePos;
|
||||
if (m_HighHandleRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HighHandleRect, eventData.position, eventData.enterEventCamera))
|
||||
{
|
||||
//dragging the high value handle
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HighHandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
|
||||
{
|
||||
m_HighOffset = localMousePos;
|
||||
}
|
||||
interactionState = InteractionState.High;
|
||||
if (transition == Transition.ColorTint)
|
||||
{
|
||||
targetGraphic = m_HighHandleRect.GetComponent<Graphic>();
|
||||
}
|
||||
}
|
||||
else if (m_LowHandleRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_LowHandleRect, eventData.position, eventData.enterEventCamera))
|
||||
{
|
||||
//dragging the low value handle
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_LowHandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
|
||||
{
|
||||
m_LowOffset = localMousePos;
|
||||
}
|
||||
interactionState = InteractionState.Low;
|
||||
if (transition == Transition.ColorTint)
|
||||
{
|
||||
targetGraphic = m_LowHandleRect.GetComponent<Graphic>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//outside the handles, move the entire slider along
|
||||
UpdateDrag(eventData, eventData.pressEventCamera);
|
||||
if (eventData.pointerCurrentRaycast.gameObject == m_FillRect.gameObject)
|
||||
{
|
||||
interactionState = InteractionState.Bar;
|
||||
}
|
||||
if (transition == Transition.ColorTint)
|
||||
{
|
||||
targetGraphic = m_FillImage;
|
||||
}
|
||||
}
|
||||
base.OnPointerDown(eventData);
|
||||
}
|
||||
|
||||
public virtual void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!MayDrag(eventData))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateDrag(eventData, eventData.pressEventCamera);
|
||||
}
|
||||
|
||||
public override void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
base.OnPointerUp(eventData);
|
||||
interactionState = InteractionState.None;
|
||||
}
|
||||
|
||||
public override void OnMove(AxisEventData eventData)
|
||||
{
|
||||
//this requires further investigation
|
||||
}
|
||||
|
||||
public virtual void OnInitializePotentialDrag(PointerEventData eventData)
|
||||
{
|
||||
eventData.useDragThreshold = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4735d2b59d5c699488c30a34a6a76e72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 281614f4c0e3b7a4d9056bd377134172
|
||||
folderAsset: yes
|
||||
timeCreated: 1446117980
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,155 @@
|
||||
/// Credit Ziboo
|
||||
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
|
||||
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(RectTransform)), DisallowMultipleComponent]
|
||||
[AddComponentMenu("UI/Extensions/Re-orderable list")]
|
||||
public class ReorderableList : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Child container with re-orderable items in a layout group")]
|
||||
public LayoutGroup ContentLayout;
|
||||
[Tooltip("Parent area to draw the dragged element on top of containers. Defaults to the root Canvas")]
|
||||
public RectTransform DraggableArea;
|
||||
|
||||
[Tooltip("Can items be dragged from the container?")]
|
||||
public bool IsDraggable = true;
|
||||
|
||||
[Tooltip("Should the draggable components be removed or cloned?")]
|
||||
public bool CloneDraggedObject = false;
|
||||
|
||||
[Tooltip("Can new draggable items be dropped in to the container?")]
|
||||
public bool IsDropable = true;
|
||||
|
||||
[Tooltip("Should dropped items displace a current item if the list is full?\n " +
|
||||
"Depending on the dropped items origin list, the displaced item may be added, dropped in space or deleted.")]
|
||||
public bool IsDisplacable = false;
|
||||
|
||||
// This sets every item size (when being dragged over this list) to the current size of the first element of this list
|
||||
[Tooltip("Should items being dragged over this list have their sizes equalized?")]
|
||||
public bool EqualizeSizesOnDrag = false;
|
||||
|
||||
public int maxItems = int.MaxValue;
|
||||
|
||||
|
||||
[Header("UI Re-orderable Events")]
|
||||
public ReorderableListHandler OnElementDropped = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementGrabbed = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementRemoved = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementAdded = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementDisplacedFrom = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementDisplacedTo = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementDisplacedFromReturned = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementDisplacedToReturned = new ReorderableListHandler();
|
||||
public ReorderableListHandler OnElementDroppedWithMaxItems = new ReorderableListHandler();
|
||||
|
||||
private RectTransform _content;
|
||||
private ReorderableListContent _listContent;
|
||||
|
||||
public RectTransform Content
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_content == null)
|
||||
{
|
||||
_content = ContentLayout.GetComponent<RectTransform>();
|
||||
}
|
||||
return _content;
|
||||
}
|
||||
}
|
||||
|
||||
Canvas GetCanvas()
|
||||
{
|
||||
Transform t = transform;
|
||||
Canvas canvas = null;
|
||||
|
||||
|
||||
int lvlLimit = 100;
|
||||
int lvl = 0;
|
||||
|
||||
while (canvas == null && lvl < lvlLimit)
|
||||
{
|
||||
canvas = t.gameObject.GetComponent<Canvas>();
|
||||
if (canvas == null)
|
||||
{
|
||||
t = t.parent;
|
||||
}
|
||||
|
||||
lvl++;
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh related list content
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
_listContent = ContentLayout.gameObject.GetOrAddComponent<ReorderableListContent>();
|
||||
_listContent.Init(this);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
|
||||
if (ContentLayout == null)
|
||||
{
|
||||
Debug.LogError("You need to have a child LayoutGroup content set for the list: " + name, gameObject);
|
||||
return;
|
||||
}
|
||||
if (DraggableArea == null)
|
||||
{
|
||||
DraggableArea = transform.root.GetComponentInChildren<Canvas>().GetComponent<RectTransform>();
|
||||
}
|
||||
if (IsDropable && !GetComponent<Graphic>())
|
||||
{
|
||||
Debug.LogError("You need to have a Graphic control (such as an Image) for the list [" + name + "] to be droppable", gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
#region Nested type: ReorderableListEventStruct
|
||||
|
||||
[Serializable]
|
||||
public struct ReorderableListEventStruct
|
||||
{
|
||||
public GameObject DroppedObject;
|
||||
public int FromIndex;
|
||||
public ReorderableList FromList;
|
||||
public bool IsAClone;
|
||||
public GameObject SourceObject;
|
||||
public int ToIndex;
|
||||
public ReorderableList ToList;
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
SourceObject.GetComponent<ReorderableListElement>().isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Nested type: ReorderableListHandler
|
||||
|
||||
[Serializable]
|
||||
public class ReorderableListHandler : UnityEvent<ReorderableListEventStruct>
|
||||
{
|
||||
}
|
||||
|
||||
public void TestReOrderableListTarget(ReorderableListEventStruct item)
|
||||
{
|
||||
Debug.Log("Event Received");
|
||||
Debug.Log("Hello World, is my item a clone? [" + item.IsAClone + "]");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b333d67eb08d464d823874f6a1666c2
|
||||
timeCreated: 1492560112
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,74 @@
|
||||
/// Credit Ziboo
|
||||
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public class ReorderableListContent : MonoBehaviour
|
||||
{
|
||||
private List<Transform> _cachedChildren;
|
||||
private List<ReorderableListElement> _cachedListElement;
|
||||
private ReorderableListElement _ele;
|
||||
private ReorderableList _extList;
|
||||
private RectTransform _rect;
|
||||
private bool _started = false;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if(_rect)StartCoroutine(RefreshChildren());
|
||||
}
|
||||
|
||||
|
||||
public void OnTransformChildrenChanged()
|
||||
{
|
||||
if(this.isActiveAndEnabled)StartCoroutine(RefreshChildren());
|
||||
}
|
||||
|
||||
public void Init(ReorderableList extList)
|
||||
{
|
||||
if (_started) { StopCoroutine(RefreshChildren()); }
|
||||
|
||||
_extList = extList;
|
||||
_rect = GetComponent<RectTransform>();
|
||||
_cachedChildren = new List<Transform>();
|
||||
_cachedListElement = new List<ReorderableListElement>();
|
||||
|
||||
StartCoroutine(RefreshChildren());
|
||||
_started = true;
|
||||
}
|
||||
|
||||
private IEnumerator RefreshChildren()
|
||||
{
|
||||
//Handle new children
|
||||
for (int i = 0; i < _rect.childCount; i++)
|
||||
{
|
||||
if (_cachedChildren.Contains(_rect.GetChild(i)))
|
||||
continue;
|
||||
|
||||
//Get or Create ReorderableListElement
|
||||
_ele = _rect.GetChild(i).gameObject.GetComponent<ReorderableListElement>() ??
|
||||
_rect.GetChild(i).gameObject.AddComponent<ReorderableListElement>();
|
||||
_ele.Init(_extList);
|
||||
|
||||
_cachedChildren.Add(_rect.GetChild(i));
|
||||
_cachedListElement.Add(_ele);
|
||||
}
|
||||
|
||||
//HACK a little hack, if I don't wait one frame I don't have the right deleted children
|
||||
yield return 0;
|
||||
|
||||
//Remove deleted child
|
||||
for (int i = _cachedChildren.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_cachedChildren[i] == null)
|
||||
{
|
||||
_cachedChildren.RemoveAt(i);
|
||||
_cachedListElement.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 252dd148b2c1dbe40b7d938a553e3caf
|
||||
timeCreated: 1446062045
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,29 @@
|
||||
/// Credit Ziboo
|
||||
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
public class ReorderableListDebug : MonoBehaviour
|
||||
{
|
||||
public Text DebugLabel;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
foreach (var list in FindObjectsOfType<ReorderableList>())
|
||||
{
|
||||
list.OnElementDropped.AddListener(ElementDropped);
|
||||
}
|
||||
}
|
||||
|
||||
private void ElementDropped(ReorderableList.ReorderableListEventStruct droppedStruct)
|
||||
{
|
||||
DebugLabel.text = "";
|
||||
DebugLabel.text += "Dropped Object: " + droppedStruct.DroppedObject.name + "\n";
|
||||
DebugLabel.text += "Is Clone ?: " + droppedStruct.IsAClone + "\n";
|
||||
if (droppedStruct.IsAClone)
|
||||
DebugLabel.text += "Source Object: " + droppedStruct.SourceObject.name + "\n";
|
||||
DebugLabel.text += string.Format("From {0} at Index {1} \n", droppedStruct.FromList.name, droppedStruct.FromIndex);
|
||||
DebugLabel.text += string.Format("To {0} at Index {1} \n", droppedStruct.ToList == null ? "Empty space" : droppedStruct.ToList.name, droppedStruct.ToIndex);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86c224aef3e999140b78d1d7135ba33f
|
||||
timeCreated: 1446072313
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,556 @@
|
||||
/// Credit Ziboo, Andrew Quesenberry
|
||||
/// Sourced from - http://forum.unity3d.com/threads/free-reorderable-list.364600/
|
||||
/// Last Child Fix - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/issues/70/all-re-orderable-lists-cause-a-transform
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(RectTransform), typeof(LayoutElement))]
|
||||
public class ReorderableListElement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
|
||||
{
|
||||
[Tooltip("Can this element be dragged?")]
|
||||
[SerializeField]
|
||||
private bool IsGrabbable = true;
|
||||
|
||||
[Tooltip("Can this element be transfered to another list")]
|
||||
[SerializeField]
|
||||
private bool _isTransferable = true;
|
||||
|
||||
[Tooltip("Can this element be dropped in space?")]
|
||||
[SerializeField]
|
||||
private bool isDroppableInSpace = false;
|
||||
|
||||
|
||||
public bool IsTransferable
|
||||
{
|
||||
get { return _isTransferable; }
|
||||
set
|
||||
{
|
||||
_canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
|
||||
_canvasGroup.blocksRaycasts = value;
|
||||
_isTransferable = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<RaycastResult> _raycastResults = new List<RaycastResult>();
|
||||
private ReorderableList _currentReorderableListRaycasted;
|
||||
|
||||
private int _fromIndex;
|
||||
private RectTransform _draggingObject;
|
||||
private LayoutElement _draggingObjectLE;
|
||||
private Vector2 _draggingObjectOriginalSize;
|
||||
|
||||
private RectTransform _fakeElement;
|
||||
private LayoutElement _fakeElementLE;
|
||||
|
||||
private int _displacedFromIndex;
|
||||
private RectTransform _displacedObject;
|
||||
private LayoutElement _displacedObjectLE;
|
||||
private Vector2 _displacedObjectOriginalSize;
|
||||
private ReorderableList _displacedObjectOriginList;
|
||||
|
||||
private bool _isDragging;
|
||||
private RectTransform _rect;
|
||||
private ReorderableList _reorderableList;
|
||||
private CanvasGroup _canvasGroup;
|
||||
internal bool isValid;
|
||||
|
||||
|
||||
#region IBeginDragHandler Members
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!_canvasGroup) { _canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>(); }
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
isValid = true;
|
||||
if (_reorderableList == null)
|
||||
return;
|
||||
|
||||
//Can't drag, return...
|
||||
if (!_reorderableList.IsDraggable || !this.IsGrabbable)
|
||||
{
|
||||
_draggingObject = null;
|
||||
return;
|
||||
}
|
||||
|
||||
//If not CloneDraggedObject just set draggingObject to this gameobject
|
||||
if (_reorderableList.CloneDraggedObject == false)
|
||||
{
|
||||
_draggingObject = _rect;
|
||||
_fromIndex = _rect.GetSiblingIndex();
|
||||
_displacedFromIndex = -1;
|
||||
//Send OnElementRemoved Event
|
||||
if (_reorderableList.OnElementRemoved != null)
|
||||
{
|
||||
_reorderableList.OnElementRemoved.Invoke(new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _draggingObject.gameObject,
|
||||
IsAClone = _reorderableList.CloneDraggedObject,
|
||||
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
|
||||
FromList = _reorderableList,
|
||||
FromIndex = _fromIndex,
|
||||
});
|
||||
}
|
||||
if (isValid == false)
|
||||
{
|
||||
_draggingObject = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Else Duplicate
|
||||
GameObject clone = (GameObject)Instantiate(gameObject);
|
||||
_draggingObject = clone.GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
//Put _dragging object into the dragging area
|
||||
_draggingObjectOriginalSize = gameObject.GetComponent<RectTransform>().rect.size;
|
||||
_draggingObjectLE = _draggingObject.GetComponent<LayoutElement>();
|
||||
_draggingObject.SetParent(_reorderableList.DraggableArea, true);
|
||||
_draggingObject.SetAsLastSibling();
|
||||
_reorderableList.Refresh();
|
||||
|
||||
//Create a fake element for previewing placement
|
||||
_fakeElement = new GameObject("Fake").AddComponent<RectTransform>();
|
||||
_fakeElementLE = _fakeElement.gameObject.AddComponent<LayoutElement>();
|
||||
|
||||
RefreshSizes();
|
||||
|
||||
//Send OnElementGrabbed Event
|
||||
if (_reorderableList.OnElementGrabbed != null)
|
||||
{
|
||||
_reorderableList.OnElementGrabbed.Invoke(new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _draggingObject.gameObject,
|
||||
IsAClone = _reorderableList.CloneDraggedObject,
|
||||
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
|
||||
FromList = _reorderableList,
|
||||
FromIndex = _fromIndex,
|
||||
});
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
CancelDrag();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_isDragging = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region IDragHandler Members
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!_isDragging)
|
||||
return;
|
||||
if (!isValid)
|
||||
{
|
||||
CancelDrag();
|
||||
return;
|
||||
}
|
||||
//Set dragging object on cursor
|
||||
var canvas = _draggingObject.GetComponentInParent<Canvas>();
|
||||
Vector3 worldPoint;
|
||||
RectTransformUtility.ScreenPointToWorldPointInRectangle(canvas.GetComponent<RectTransform>(), eventData.position,
|
||||
canvas.renderMode != RenderMode.ScreenSpaceOverlay ? canvas.worldCamera : null, out worldPoint);
|
||||
_draggingObject.position = worldPoint;
|
||||
|
||||
ReorderableList _oldReorderableListRaycasted = _currentReorderableListRaycasted;
|
||||
|
||||
//Check everything under the cursor to find a ReorderableList
|
||||
EventSystem.current.RaycastAll(eventData, _raycastResults);
|
||||
for (int i = 0; i < _raycastResults.Count; i++)
|
||||
{
|
||||
_currentReorderableListRaycasted = _raycastResults[i].gameObject.GetComponent<ReorderableList>();
|
||||
if (_currentReorderableListRaycasted != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//If nothing found or the list is not dropable, put the fake element outside
|
||||
if (_currentReorderableListRaycasted == null || _currentReorderableListRaycasted.IsDropable == false
|
||||
// || (_oldReorderableListRaycasted != _reorderableList && !IsTransferable)
|
||||
|| ((_fakeElement.parent == _currentReorderableListRaycasted.Content
|
||||
? _currentReorderableListRaycasted.Content.childCount - 1
|
||||
: _currentReorderableListRaycasted.Content.childCount) >= _currentReorderableListRaycasted.maxItems && !_currentReorderableListRaycasted.IsDisplacable)
|
||||
|| _currentReorderableListRaycasted.maxItems <= 0)
|
||||
{
|
||||
RefreshSizes();
|
||||
_fakeElement.transform.SetParent(_reorderableList.DraggableArea, false);
|
||||
// revert the displaced element when not hovering over its list
|
||||
if (_displacedObject != null)
|
||||
{
|
||||
revertDisplacedElement();
|
||||
}
|
||||
}
|
||||
//Else find the best position on the list and put fake element on the right index
|
||||
else
|
||||
{
|
||||
if (_currentReorderableListRaycasted.Content.childCount < _currentReorderableListRaycasted.maxItems && _fakeElement.parent != _currentReorderableListRaycasted.Content)
|
||||
{
|
||||
_fakeElement.SetParent(_currentReorderableListRaycasted.Content, false);
|
||||
}
|
||||
|
||||
float minDistance = float.PositiveInfinity;
|
||||
int targetIndex = 0;
|
||||
float dist = 0;
|
||||
for (int j = 0; j < _currentReorderableListRaycasted.Content.childCount; j++)
|
||||
{
|
||||
var c = _currentReorderableListRaycasted.Content.GetChild(j).GetComponent<RectTransform>();
|
||||
|
||||
if (_currentReorderableListRaycasted.ContentLayout is VerticalLayoutGroup)
|
||||
dist = Mathf.Abs(c.position.y - worldPoint.y);
|
||||
else if (_currentReorderableListRaycasted.ContentLayout is HorizontalLayoutGroup)
|
||||
dist = Mathf.Abs(c.position.x - worldPoint.x);
|
||||
else if (_currentReorderableListRaycasted.ContentLayout is GridLayoutGroup)
|
||||
dist = (Mathf.Abs(c.position.x - worldPoint.x) + Mathf.Abs(c.position.y - worldPoint.y));
|
||||
|
||||
if (dist < minDistance)
|
||||
{
|
||||
minDistance = dist;
|
||||
targetIndex = j;
|
||||
}
|
||||
}
|
||||
if ((_currentReorderableListRaycasted != _oldReorderableListRaycasted || targetIndex != _displacedFromIndex)
|
||||
&& _currentReorderableListRaycasted.Content.childCount == _currentReorderableListRaycasted.maxItems)
|
||||
{
|
||||
Transform toDisplace = _currentReorderableListRaycasted.Content.GetChild(targetIndex);
|
||||
if (_displacedObject != null)
|
||||
{
|
||||
revertDisplacedElement();
|
||||
if (_currentReorderableListRaycasted.Content.childCount > _currentReorderableListRaycasted.maxItems)
|
||||
{
|
||||
displaceElement(targetIndex, toDisplace);
|
||||
}
|
||||
}
|
||||
else if (_fakeElement.parent != _currentReorderableListRaycasted.Content)
|
||||
{
|
||||
_fakeElement.SetParent(_currentReorderableListRaycasted.Content, false);
|
||||
displaceElement(targetIndex, toDisplace);
|
||||
}
|
||||
}
|
||||
RefreshSizes();
|
||||
_fakeElement.SetSiblingIndex(targetIndex);
|
||||
_fakeElement.gameObject.SetActive(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Displacement
|
||||
|
||||
private void displaceElement(int targetIndex, Transform displaced)
|
||||
{
|
||||
_displacedFromIndex = targetIndex;
|
||||
_displacedObjectOriginList = _currentReorderableListRaycasted;
|
||||
_displacedObject = displaced.GetComponent<RectTransform>();
|
||||
_displacedObjectLE = _displacedObject.GetComponent<LayoutElement>();
|
||||
_displacedObjectOriginalSize = _displacedObject.rect.size;
|
||||
|
||||
var args = new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _displacedObject.gameObject,
|
||||
FromList = _currentReorderableListRaycasted,
|
||||
FromIndex = targetIndex,
|
||||
};
|
||||
|
||||
|
||||
int c = _fakeElement.parent == _reorderableList.Content
|
||||
? _reorderableList.Content.childCount - 1
|
||||
: _reorderableList.Content.childCount;
|
||||
|
||||
if (_reorderableList.IsDropable && c < _reorderableList.maxItems && _displacedObject.GetComponent<ReorderableListElement>().IsTransferable)
|
||||
{
|
||||
_displacedObjectLE.preferredWidth = _draggingObjectOriginalSize.x;
|
||||
_displacedObjectLE.preferredHeight = _draggingObjectOriginalSize.y;
|
||||
_displacedObject.SetParent(_reorderableList.Content, false);
|
||||
_displacedObject.rotation = _reorderableList.transform.rotation;
|
||||
_displacedObject.SetSiblingIndex(_fromIndex);
|
||||
// Force refreshing both lists because otherwise we get inappropriate FromList in ReorderableListEventStruct
|
||||
_reorderableList.Refresh();
|
||||
_currentReorderableListRaycasted.Refresh();
|
||||
|
||||
args.ToList = _reorderableList;
|
||||
args.ToIndex = _fromIndex;
|
||||
_reorderableList.OnElementDisplacedTo.Invoke(args);
|
||||
_reorderableList.OnElementAdded.Invoke(args);
|
||||
}
|
||||
else if (_displacedObject.GetComponent<ReorderableListElement>().isDroppableInSpace)
|
||||
{
|
||||
_displacedObject.SetParent(_currentReorderableListRaycasted.DraggableArea, true);
|
||||
_currentReorderableListRaycasted.Refresh();
|
||||
_displacedObject.position += new Vector3(_draggingObjectOriginalSize.x / 2, _draggingObjectOriginalSize.y / 2, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_displacedObject.SetParent(null, true);
|
||||
_displacedObjectOriginList.Refresh();
|
||||
_displacedObject.gameObject.SetActive(false);
|
||||
}
|
||||
_displacedObjectOriginList.OnElementDisplacedFrom.Invoke(args);
|
||||
_reorderableList.OnElementRemoved.Invoke(args);
|
||||
}
|
||||
|
||||
private void revertDisplacedElement()
|
||||
{
|
||||
var args = new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _displacedObject.gameObject,
|
||||
FromList = _displacedObjectOriginList,
|
||||
FromIndex = _displacedFromIndex,
|
||||
};
|
||||
if (_displacedObject.parent != null)
|
||||
{
|
||||
args.ToList = _reorderableList;
|
||||
args.ToIndex = _fromIndex;
|
||||
}
|
||||
|
||||
_displacedObjectLE.preferredWidth = _displacedObjectOriginalSize.x;
|
||||
_displacedObjectLE.preferredHeight = _displacedObjectOriginalSize.y;
|
||||
_displacedObject.SetParent(_displacedObjectOriginList.Content, false);
|
||||
_displacedObject.rotation = _displacedObjectOriginList.transform.rotation;
|
||||
_displacedObject.SetSiblingIndex(_displacedFromIndex);
|
||||
_displacedObject.gameObject.SetActive(true);
|
||||
|
||||
// Force refreshing both lists because otherwise we get inappropriate FromList in ReorderableListEventStruct
|
||||
_reorderableList.Refresh();
|
||||
_displacedObjectOriginList.Refresh();
|
||||
|
||||
if (args.ToList != null)
|
||||
{
|
||||
_reorderableList.OnElementDisplacedToReturned.Invoke(args);
|
||||
_reorderableList.OnElementRemoved.Invoke(args);
|
||||
}
|
||||
_displacedObjectOriginList.OnElementDisplacedFromReturned.Invoke(args);
|
||||
_displacedObjectOriginList.OnElementAdded.Invoke(args);
|
||||
|
||||
_displacedFromIndex = -1;
|
||||
_displacedObjectOriginList = null;
|
||||
_displacedObject = null;
|
||||
_displacedObjectLE = null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void finishDisplacingElement()
|
||||
{
|
||||
if (_displacedObject.parent == null)
|
||||
{
|
||||
Destroy(_displacedObject.gameObject);
|
||||
}
|
||||
_displacedFromIndex = -1;
|
||||
_displacedObjectOriginList = null;
|
||||
_displacedObject = null;
|
||||
_displacedObjectLE = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region IEndDragHandler Members
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
_isDragging = false;
|
||||
|
||||
if (_draggingObject != null)
|
||||
{
|
||||
//If we have a ReorderableList that is dropable
|
||||
//Put the dragged object into the content and at the right index
|
||||
if (_currentReorderableListRaycasted != null && _fakeElement.parent == _currentReorderableListRaycasted.Content)
|
||||
{
|
||||
var args = new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _draggingObject.gameObject,
|
||||
IsAClone = _reorderableList.CloneDraggedObject,
|
||||
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
|
||||
FromList = _reorderableList,
|
||||
FromIndex = _fromIndex,
|
||||
ToList = _currentReorderableListRaycasted,
|
||||
ToIndex = _fakeElement.GetSiblingIndex()
|
||||
};
|
||||
//Send OnelementDropped Event
|
||||
if (_reorderableList && _reorderableList.OnElementDropped != null)
|
||||
{
|
||||
_reorderableList.OnElementDropped.Invoke(args);
|
||||
}
|
||||
if (!isValid)
|
||||
{
|
||||
CancelDrag();
|
||||
return;
|
||||
}
|
||||
RefreshSizes();
|
||||
_draggingObject.SetParent(_currentReorderableListRaycasted.Content, false);
|
||||
_draggingObject.rotation = _currentReorderableListRaycasted.transform.rotation;
|
||||
_draggingObject.SetSiblingIndex(_fakeElement.GetSiblingIndex());
|
||||
|
||||
//If the item is transferable, it can be dragged out again
|
||||
if (IsTransferable)
|
||||
{
|
||||
var cg = _draggingObject.GetComponent<CanvasGroup>();
|
||||
cg.blocksRaycasts = true;
|
||||
}
|
||||
// Force refreshing both lists because otherwise we get inappropriate FromList in ReorderableListEventStruct
|
||||
_reorderableList.Refresh();
|
||||
_currentReorderableListRaycasted.Refresh();
|
||||
|
||||
_reorderableList.OnElementAdded.Invoke(args);
|
||||
|
||||
if (_displacedObject != null)
|
||||
{
|
||||
finishDisplacingElement();
|
||||
}
|
||||
|
||||
if (!isValid)
|
||||
throw new Exception("It's too late to cancel the Transfer! Do so in OnElementDropped!");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//We don't have an ReorderableList
|
||||
if (this.isDroppableInSpace)
|
||||
{
|
||||
_reorderableList.OnElementDropped.Invoke(new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _draggingObject.gameObject,
|
||||
IsAClone = _reorderableList.CloneDraggedObject,
|
||||
SourceObject =
|
||||
_reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
|
||||
FromList = _reorderableList,
|
||||
FromIndex = _fromIndex
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
CancelDrag();
|
||||
}
|
||||
|
||||
//If there is no more room for the element in the target list, notify it (OnElementDroppedWithMaxItems event)
|
||||
if (_currentReorderableListRaycasted != null)
|
||||
{
|
||||
if ((_currentReorderableListRaycasted.Content.childCount >=
|
||||
_currentReorderableListRaycasted.maxItems &&
|
||||
!_currentReorderableListRaycasted.IsDisplacable)
|
||||
|| _currentReorderableListRaycasted.maxItems <= 0)
|
||||
{
|
||||
GameObject o = _draggingObject.gameObject;
|
||||
_reorderableList.OnElementDroppedWithMaxItems.Invoke(
|
||||
new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = o,
|
||||
IsAClone = _reorderableList.CloneDraggedObject,
|
||||
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : o,
|
||||
FromList = _reorderableList,
|
||||
ToList = _currentReorderableListRaycasted,
|
||||
FromIndex = _fromIndex
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Delete fake element
|
||||
if (_fakeElement != null)
|
||||
{
|
||||
Destroy(_fakeElement.gameObject);
|
||||
_fakeElement = null;
|
||||
}
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
void CancelDrag()
|
||||
{
|
||||
_isDragging = false;
|
||||
//If it's a clone, delete it
|
||||
if (_reorderableList.CloneDraggedObject)
|
||||
{
|
||||
Destroy(_draggingObject.gameObject);
|
||||
}
|
||||
//Else replace the draggedObject to his first place
|
||||
else
|
||||
{
|
||||
RefreshSizes();
|
||||
_draggingObject.SetParent(_reorderableList.Content, false);
|
||||
_draggingObject.rotation = _reorderableList.Content.transform.rotation;
|
||||
_draggingObject.SetSiblingIndex(_fromIndex);
|
||||
|
||||
|
||||
var args = new ReorderableList.ReorderableListEventStruct
|
||||
{
|
||||
DroppedObject = _draggingObject.gameObject,
|
||||
IsAClone = _reorderableList.CloneDraggedObject,
|
||||
SourceObject = _reorderableList.CloneDraggedObject ? gameObject : _draggingObject.gameObject,
|
||||
FromList = _reorderableList,
|
||||
FromIndex = _fromIndex,
|
||||
ToList = _reorderableList,
|
||||
ToIndex = _fromIndex
|
||||
};
|
||||
|
||||
_reorderableList.Refresh();
|
||||
|
||||
_reorderableList.OnElementAdded.Invoke(args);
|
||||
|
||||
if (!isValid)
|
||||
throw new Exception("Transfer is already Canceled.");
|
||||
|
||||
}
|
||||
|
||||
//Delete fake element
|
||||
if (_fakeElement != null)
|
||||
{
|
||||
Destroy(_fakeElement.gameObject);
|
||||
_fakeElement = null;
|
||||
}
|
||||
if (_displacedObject != null)
|
||||
{
|
||||
revertDisplacedElement();
|
||||
}
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
}
|
||||
|
||||
private void RefreshSizes()
|
||||
{
|
||||
Vector2 size = _draggingObjectOriginalSize;
|
||||
|
||||
if (_currentReorderableListRaycasted != null
|
||||
&& _currentReorderableListRaycasted.IsDropable
|
||||
&& _currentReorderableListRaycasted.Content.childCount > 0
|
||||
&& _currentReorderableListRaycasted.EqualizeSizesOnDrag)
|
||||
{
|
||||
var firstChild = _currentReorderableListRaycasted.Content.GetChild(0);
|
||||
if (firstChild != null)
|
||||
{
|
||||
size = firstChild.GetComponent<RectTransform>().rect.size;
|
||||
}
|
||||
}
|
||||
|
||||
_draggingObject.sizeDelta = size;
|
||||
_fakeElementLE.preferredHeight = _draggingObjectLE.preferredHeight = size.y;
|
||||
_fakeElementLE.preferredWidth = _draggingObjectLE.preferredWidth = size.x;
|
||||
_fakeElement.GetComponent<RectTransform>().sizeDelta = size;
|
||||
}
|
||||
|
||||
public void Init(ReorderableList reorderableList)
|
||||
{
|
||||
_reorderableList = reorderableList;
|
||||
_rect = GetComponent<RectTransform>();
|
||||
_canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 916e98f1b982a9a4082fcc45c87b66c5
|
||||
timeCreated: 1492560112
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4c2c5317e2b50f42b0302414577a62d
|
||||
folderAsset: yes
|
||||
timeCreated: 1440843775
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,64 @@
|
||||
/// Credit .entity
|
||||
/// Sourced from - http://forum.unity3d.com/threads/rescale-panel.309226/
|
||||
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/RescalePanels/RescaleDragPanel")]
|
||||
public class RescaleDragPanel : MonoBehaviour, IPointerDownHandler, IDragHandler
|
||||
{
|
||||
private Vector2 pointerOffset;
|
||||
private RectTransform canvasRectTransform;
|
||||
private RectTransform panelRectTransform;
|
||||
|
||||
private Transform goTransform;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
canvasRectTransform = canvas.transform as RectTransform;
|
||||
panelRectTransform = transform.parent as RectTransform;
|
||||
goTransform = transform.parent;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData data)
|
||||
{
|
||||
panelRectTransform.SetAsLastSibling();
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRectTransform, data.position, data.pressEventCamera, out pointerOffset);
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData data)
|
||||
{
|
||||
if (panelRectTransform == null)
|
||||
return;
|
||||
|
||||
Vector2 pointerPosition = ClampToWindow(data);
|
||||
|
||||
Vector2 localPointerPosition;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
canvasRectTransform, pointerPosition, data.pressEventCamera, out localPointerPosition
|
||||
))
|
||||
{
|
||||
panelRectTransform.localPosition = localPointerPosition - new Vector2(pointerOffset.x * goTransform.localScale.x, pointerOffset.y * goTransform.localScale.y);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 ClampToWindow(PointerEventData data)
|
||||
{
|
||||
Vector2 rawPointerPosition = data.position;
|
||||
|
||||
Vector3[] canvasCorners = new Vector3[4];
|
||||
canvasRectTransform.GetWorldCorners(canvasCorners);
|
||||
|
||||
float clampedX = Mathf.Clamp(rawPointerPosition.x, canvasCorners[0].x, canvasCorners[2].x);
|
||||
float clampedY = Mathf.Clamp(rawPointerPosition.y, canvasCorners[0].y, canvasCorners[2].y);
|
||||
|
||||
Vector2 newPointerPosition = new Vector2(clampedX, clampedY);
|
||||
return newPointerPosition;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c210a6e68020b4349a4020e7da57826e
|
||||
timeCreated: 1440843816
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,62 @@
|
||||
/// Credit .entity
|
||||
/// Sourced from - http://forum.unity3d.com/threads/rescale-panel.309226/
|
||||
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/RescalePanels/RescalePanel")]
|
||||
public class RescalePanel : MonoBehaviour, IPointerDownHandler, IDragHandler
|
||||
{
|
||||
public Vector2 minSize;
|
||||
public Vector2 maxSize;
|
||||
|
||||
private RectTransform rectTransform;
|
||||
private Transform goTransform;
|
||||
private Vector2 currentPointerPosition;
|
||||
private Vector2 previousPointerPosition;
|
||||
|
||||
private RectTransform thisRectTransform;
|
||||
Vector2 sizeDelta;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
rectTransform = transform.parent.GetComponent<RectTransform>();
|
||||
goTransform = transform.parent;
|
||||
|
||||
thisRectTransform = GetComponent<RectTransform>();
|
||||
sizeDelta = thisRectTransform.sizeDelta;
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData data)
|
||||
{
|
||||
rectTransform.SetAsLastSibling();
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, data.position, data.pressEventCamera, out previousPointerPosition);
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData data)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
return;
|
||||
|
||||
Vector3 scaleDelta = goTransform.localScale;
|
||||
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, data.position, data.pressEventCamera, out currentPointerPosition);
|
||||
Vector2 resizeValue = currentPointerPosition - previousPointerPosition;
|
||||
|
||||
scaleDelta += new Vector3(-resizeValue.y * 0.001f, -resizeValue.y * 0.001f, 0f);
|
||||
scaleDelta = new Vector3(
|
||||
Mathf.Clamp(scaleDelta.x, minSize.x, maxSize.x),
|
||||
Mathf.Clamp(scaleDelta.y, minSize.y, maxSize.y),
|
||||
1
|
||||
);
|
||||
|
||||
goTransform.localScale = scaleDelta;
|
||||
|
||||
previousPointerPosition = currentPointerPosition;
|
||||
float resizeDeltaValue = sizeDelta.x / goTransform.localScale.x;
|
||||
Vector2 newSizeDelta = new Vector2(resizeDeltaValue, resizeDeltaValue);
|
||||
thisRectTransform.sizeDelta = newSizeDelta;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff46b67332ade77459ea86ba20638d24
|
||||
timeCreated: 1440843795
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,60 @@
|
||||
/// Credit .entity
|
||||
/// Sourced from - http://forum.unity3d.com/threads/rescale-panel.309226/
|
||||
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/RescalePanels/ResizePanel")]
|
||||
public class ResizePanel : MonoBehaviour, IPointerDownHandler, IDragHandler
|
||||
{
|
||||
public Vector2 minSize;
|
||||
public Vector2 maxSize;
|
||||
|
||||
private RectTransform rectTransform;
|
||||
private Vector2 currentPointerPosition;
|
||||
private Vector2 previousPointerPosition;
|
||||
|
||||
private float ratio;
|
||||
|
||||
|
||||
void Awake()
|
||||
{
|
||||
rectTransform = transform.parent.GetComponent<RectTransform>();
|
||||
float originalWidth;
|
||||
float originalHeight;
|
||||
originalWidth = rectTransform.rect.width;
|
||||
originalHeight = rectTransform.rect.height;
|
||||
ratio = originalHeight / originalWidth;
|
||||
minSize = new Vector2(0.1f * originalWidth, 0.1f * originalHeight);
|
||||
maxSize = new Vector2(10f * originalWidth, 10f * originalHeight);
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData data)
|
||||
{
|
||||
rectTransform.SetAsLastSibling();
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, data.position, data.pressEventCamera, out previousPointerPosition);
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData data)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
return;
|
||||
|
||||
Vector2 sizeDelta = rectTransform.sizeDelta;
|
||||
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, data.position, data.pressEventCamera, out currentPointerPosition);
|
||||
Vector2 resizeValue = currentPointerPosition - previousPointerPosition;
|
||||
|
||||
sizeDelta += new Vector2(resizeValue.x, ratio * resizeValue.x);
|
||||
sizeDelta = new Vector2(
|
||||
Mathf.Clamp(sizeDelta.x, minSize.x, maxSize.x),
|
||||
Mathf.Clamp(sizeDelta.y, minSize.y, maxSize.y)
|
||||
);
|
||||
|
||||
rectTransform.sizeDelta = sizeDelta;
|
||||
|
||||
previousPointerPosition = currentPointerPosition;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db83442065d59344c9bf6ffa63a23777
|
||||
timeCreated: 1440843837
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,236 @@
|
||||
/// Credit David Gileadi
|
||||
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/12
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[AddComponentMenu("UI/Extensions/Segmented Control/Segment")]
|
||||
[RequireComponent(typeof(Selectable))]
|
||||
public class Segment :
|
||||
UIBehaviour,
|
||||
IPointerClickHandler,
|
||||
ISubmitHandler,
|
||||
IPointerEnterHandler, IPointerExitHandler,
|
||||
IPointerDownHandler, IPointerUpHandler,
|
||||
ISelectHandler, IDeselectHandler
|
||||
{
|
||||
internal int index;
|
||||
internal SegmentedControl segmentedControl;
|
||||
|
||||
internal bool leftmost
|
||||
{
|
||||
get { return index == 0; }
|
||||
}
|
||||
internal bool rightmost
|
||||
{
|
||||
get { return index == segmentedControl.segments.Length - 1; }
|
||||
}
|
||||
|
||||
public bool selected
|
||||
{
|
||||
get { return segmentedControl.selectedSegment == this.button; }
|
||||
set { SetSelected(value); }
|
||||
}
|
||||
|
||||
internal Selectable button
|
||||
{
|
||||
get { return GetComponent<Selectable>(); }
|
||||
}
|
||||
|
||||
internal Sprite cutSprite;
|
||||
|
||||
protected Segment()
|
||||
{ }
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
StartCoroutine(DelayedInit());
|
||||
}
|
||||
|
||||
IEnumerator DelayedInit()
|
||||
{
|
||||
yield return null;
|
||||
yield return null;
|
||||
|
||||
button.image.overrideSprite = cutSprite;
|
||||
if (selected)
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
selected = true;
|
||||
}
|
||||
|
||||
public virtual void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnSelect(BaseEventData eventData)
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnDeselect(BaseEventData eventData)
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
if (segmentedControl)
|
||||
MaintainSelection();
|
||||
}
|
||||
|
||||
public virtual void OnSubmit(BaseEventData eventData)
|
||||
{
|
||||
selected = true;
|
||||
}
|
||||
|
||||
private void SetSelected(bool value)
|
||||
{
|
||||
if (value && button.IsActive() && button.IsInteractable())
|
||||
{
|
||||
if (segmentedControl.selectedSegment == this.button)
|
||||
{
|
||||
if (segmentedControl.allowSwitchingOff)
|
||||
{
|
||||
Deselect();
|
||||
}
|
||||
else
|
||||
{
|
||||
MaintainSelection();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (segmentedControl.selectedSegment)
|
||||
{
|
||||
var segment = segmentedControl.selectedSegment.GetComponent<Segment>();
|
||||
segmentedControl.selectedSegment = null;
|
||||
if (segment)
|
||||
{
|
||||
segment.TransitionButton();
|
||||
}
|
||||
}
|
||||
|
||||
segmentedControl.selectedSegment = this.button;
|
||||
TransitionButton();
|
||||
segmentedControl.onValueChanged.Invoke(index);
|
||||
}
|
||||
}
|
||||
else if (segmentedControl.selectedSegment == this.button)
|
||||
{
|
||||
Deselect();
|
||||
}
|
||||
}
|
||||
|
||||
private void Deselect()
|
||||
{
|
||||
segmentedControl.selectedSegment = null;
|
||||
TransitionButton();
|
||||
segmentedControl.onValueChanged.Invoke(-1);
|
||||
}
|
||||
|
||||
void MaintainSelection()
|
||||
{
|
||||
if (button != segmentedControl.selectedSegment)
|
||||
return;
|
||||
|
||||
TransitionButton(true);
|
||||
}
|
||||
|
||||
internal void TransitionButton()
|
||||
{
|
||||
TransitionButton(false);
|
||||
}
|
||||
|
||||
internal void TransitionButton(bool instant)
|
||||
{
|
||||
Color tintColor = selected ? button.colors.pressedColor : button.colors.normalColor;
|
||||
Color textColor = selected ? button.colors.normalColor : button.colors.pressedColor;
|
||||
Sprite transitionSprite = selected ? button.spriteState.pressedSprite : cutSprite;
|
||||
string triggerName = selected ? button.animationTriggers.pressedTrigger : button.animationTriggers.normalTrigger;
|
||||
|
||||
switch (button.transition)
|
||||
{
|
||||
case Selectable.Transition.ColorTint:
|
||||
button.image.overrideSprite = cutSprite;
|
||||
StartColorTween(tintColor * button.colors.colorMultiplier, instant);
|
||||
ChangeTextColor(textColor * button.colors.colorMultiplier);
|
||||
break;
|
||||
case Selectable.Transition.SpriteSwap:
|
||||
if (transitionSprite != cutSprite)
|
||||
transitionSprite = SegmentedControl.CutSprite(transitionSprite, leftmost, rightmost);
|
||||
DoSpriteSwap(transitionSprite);
|
||||
break;
|
||||
case Selectable.Transition.Animation:
|
||||
button.image.overrideSprite = cutSprite;
|
||||
TriggerAnimation(triggerName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StartColorTween(Color targetColor, bool instant)
|
||||
{
|
||||
if (button.targetGraphic == null)
|
||||
return;
|
||||
|
||||
button.targetGraphic.CrossFadeColor(targetColor, instant ? 0f : button.colors.fadeDuration, true, true);
|
||||
}
|
||||
|
||||
void ChangeTextColor(Color targetColor)
|
||||
{
|
||||
var text = GetComponentInChildren<Text>();
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
text.color = targetColor;
|
||||
}
|
||||
|
||||
void DoSpriteSwap(Sprite newSprite)
|
||||
{
|
||||
if (button.image == null)
|
||||
return;
|
||||
|
||||
button.image.overrideSprite = newSprite;
|
||||
}
|
||||
|
||||
void TriggerAnimation(string triggername)
|
||||
{
|
||||
if (button.animator == null || !button.animator.isActiveAndEnabled || !button.animator.hasBoundPlayables || string.IsNullOrEmpty(triggername))
|
||||
return;
|
||||
|
||||
button.animator.ResetTrigger(button.animationTriggers.normalTrigger);
|
||||
button.animator.ResetTrigger(button.animationTriggers.pressedTrigger);
|
||||
button.animator.ResetTrigger(button.animationTriggers.highlightedTrigger);
|
||||
button.animator.ResetTrigger(button.animationTriggers.disabledTrigger);
|
||||
|
||||
button.animator.SetTrigger(triggername);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8ea9937637d64c6da52723c68267703
|
||||
timeCreated: 1503449008
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,245 @@
|
||||
/// Credit David Gileadi
|
||||
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/12
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
// Segmented control, like a group of buttons
|
||||
[AddComponentMenu("UI/Extensions/Segmented Control/Segmented Control")]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class SegmentedControl : UIBehaviour
|
||||
{
|
||||
private Selectable[] m_segments;
|
||||
[SerializeField]
|
||||
[Tooltip("A GameObject with an Image to use as a separator between segments. Size of the RectTransform will determine the size of the separator used.\nNote, make sure to disable the separator GO so that it does not affect the scene")]
|
||||
private Graphic m_separator;
|
||||
private float m_separatorWidth = 0;
|
||||
[SerializeField]
|
||||
[Tooltip("When True, it allows each button to be toggled on/off")]
|
||||
private bool m_allowSwitchingOff = false;
|
||||
[SerializeField]
|
||||
[Tooltip("The selected default for the control (zero indexed array)")]
|
||||
private int m_selectedSegmentIndex = -1;
|
||||
// Event delegates triggered on click.
|
||||
[SerializeField]
|
||||
[Tooltip("Event to fire once the selection has been changed")]
|
||||
private SegmentSelectedEvent m_onValueChanged = new SegmentSelectedEvent();
|
||||
|
||||
internal Selectable selectedSegment;
|
||||
|
||||
protected float SeparatorWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_separatorWidth == 0 && separator)
|
||||
{
|
||||
m_separatorWidth = separator.rectTransform.rect.width;
|
||||
var image = separator.GetComponent<Image>();
|
||||
if (image)
|
||||
m_separatorWidth /= image.pixelsPerUnit;
|
||||
}
|
||||
return m_separatorWidth;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SegmentSelectedEvent : UnityEvent<int> { }
|
||||
|
||||
public Selectable[] segments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_segments == null || m_segments.Length == 0)
|
||||
{
|
||||
m_segments = GetChildSegments();
|
||||
}
|
||||
return m_segments;
|
||||
}
|
||||
}
|
||||
|
||||
public Graphic separator { get { return m_separator; } set { m_separator = value; m_separatorWidth = 0; LayoutSegments(); } }
|
||||
|
||||
public bool allowSwitchingOff { get { return m_allowSwitchingOff; } set { m_allowSwitchingOff = value; } }
|
||||
|
||||
public int selectedSegmentIndex
|
||||
{
|
||||
get { return Array.IndexOf(segments, selectedSegment); }
|
||||
set
|
||||
{
|
||||
value = Math.Max(value, -1);
|
||||
value = Math.Min(value, segments.Length - 1);
|
||||
|
||||
if (m_selectedSegmentIndex == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_selectedSegmentIndex = value;
|
||||
|
||||
if (selectedSegment)
|
||||
{
|
||||
var segment = selectedSegment.GetComponent<Segment>();
|
||||
if (segment)
|
||||
{
|
||||
segment.selected = false;
|
||||
}
|
||||
selectedSegment = null;
|
||||
}
|
||||
|
||||
if (value != -1)
|
||||
{
|
||||
selectedSegment = segments[value];
|
||||
var segment = selectedSegment.GetComponent<Segment>();
|
||||
if (segment)
|
||||
{
|
||||
segment.selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SegmentSelectedEvent onValueChanged
|
||||
{
|
||||
get { return m_onValueChanged; }
|
||||
set { m_onValueChanged = value; }
|
||||
}
|
||||
|
||||
protected SegmentedControl()
|
||||
{ }
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
if (isActiveAndEnabled)
|
||||
StartCoroutine(DelayedInit());
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
StartCoroutine(DelayedInit());
|
||||
}
|
||||
|
||||
IEnumerator DelayedInit()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
LayoutSegments();
|
||||
|
||||
if (m_selectedSegmentIndex != -1)
|
||||
selectedSegmentIndex = m_selectedSegmentIndex;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
if (isActiveAndEnabled)
|
||||
StartCoroutine(DelayedInit());
|
||||
|
||||
if (m_selectedSegmentIndex > transform.childCount)
|
||||
{
|
||||
selectedSegmentIndex = transform.childCount - 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private Selectable[] GetChildSegments()
|
||||
{
|
||||
var buttons = GetComponentsInChildren<Selectable>();
|
||||
if (buttons.Length < 2)
|
||||
{
|
||||
throw new InvalidOperationException("A segmented control must have at least two Button children");
|
||||
}
|
||||
|
||||
for (int i = 0; i < buttons.Length; i++)
|
||||
{
|
||||
var segment = buttons[i].GetComponent<Segment>();
|
||||
if (segment != null)
|
||||
{
|
||||
segment.index = i;
|
||||
segment.segmentedControl = this;
|
||||
}
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private void RecreateSprites()
|
||||
{
|
||||
for (int i = 0; i < segments.Length; i++)
|
||||
{
|
||||
if (segments[i].image == null)
|
||||
continue;
|
||||
|
||||
var sprite = CutSprite(segments[i].image.sprite, i == 0, i == segments.Length - 1);
|
||||
var segment = segments[i].GetComponent<Segment>();
|
||||
if (segment)
|
||||
{
|
||||
segment.cutSprite = sprite;
|
||||
}
|
||||
segments[i].image.overrideSprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
static internal Sprite CutSprite(Sprite sprite, bool leftmost, bool rightmost)
|
||||
{
|
||||
if (sprite.border.x == 0 || sprite.border.z == 0)
|
||||
return sprite;
|
||||
|
||||
var rect = sprite.rect;
|
||||
var border = sprite.border;
|
||||
|
||||
if (!leftmost)
|
||||
{
|
||||
rect.xMin = border.x;
|
||||
border.x = 0;
|
||||
}
|
||||
if (!rightmost)
|
||||
{
|
||||
rect.xMax = border.z;
|
||||
border.z = 0;
|
||||
}
|
||||
|
||||
return Sprite.Create(sprite.texture, rect, sprite.pivot, sprite.pixelsPerUnit, 0, SpriteMeshType.FullRect, border);
|
||||
}
|
||||
|
||||
public void LayoutSegments()
|
||||
{
|
||||
RecreateSprites();
|
||||
|
||||
RectTransform transform = this.transform as RectTransform;
|
||||
float width = (transform.rect.width / segments.Length) - (SeparatorWidth * (segments.Length - 1));
|
||||
|
||||
for (int i = 0; i < segments.Length; i++)
|
||||
{
|
||||
float insetX = ((width + SeparatorWidth) * i);
|
||||
|
||||
var rectTransform = segments[i].GetComponent<RectTransform>();
|
||||
rectTransform.anchorMin = Vector2.zero;
|
||||
rectTransform.anchorMax = Vector2.zero;
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, insetX, width);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, transform.rect.height);
|
||||
|
||||
if (separator && i > 0)
|
||||
{
|
||||
var sepTransform = gameObject.transform.Find("Separator " + i);
|
||||
Graphic sep = (sepTransform != null) ? sepTransform.GetComponent<Graphic>() : (GameObject.Instantiate(separator.gameObject) as GameObject).GetComponent<Graphic>();
|
||||
sep.gameObject.name = "Separator " + i;
|
||||
sep.gameObject.SetActive(true);
|
||||
sep.rectTransform.SetParent(this.transform, false);
|
||||
sep.rectTransform.anchorMin = Vector2.zero;
|
||||
sep.rectTransform.anchorMax = Vector2.zero;
|
||||
sep.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, insetX - SeparatorWidth, SeparatorWidth);
|
||||
sep.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, transform.rect.height);
|
||||
}
|
||||
// TODO: maybe adjust text position
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 439b3dcc2b7714c1e91776a9d161f6ee
|
||||
timeCreated: 1498517608
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e656d866043cab4fb676dba90e3734c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@ -0,0 +1,93 @@
|
||||
/// Original Credit Korindian
|
||||
/// Sourced from - http://forum.unity3d.com/threads/rts-style-drag-selection-box.265739/
|
||||
/// Updated Credit BenZed
|
||||
/// Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
public class ExampleSelectable : MonoBehaviour, IBoxSelectable
|
||||
{
|
||||
#region Implemented members of IBoxSelectable
|
||||
bool _selected = false;
|
||||
public bool selected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _selected;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_selected = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool _preSelected = false;
|
||||
public bool preSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _preSelected;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_preSelected = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
//We want the test object to be either a UI element, a 2D element or a 3D element, so we'll get the appropriate components
|
||||
SpriteRenderer spriteRenderer;
|
||||
Image image;
|
||||
Text text;
|
||||
|
||||
void Start()
|
||||
{
|
||||
spriteRenderer = transform.GetComponent<SpriteRenderer>();
|
||||
image = transform.GetComponent<Image>();
|
||||
text = transform.GetComponent<Text>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
|
||||
//What the game object does with the knowledge that it is selected is entirely up to it.
|
||||
//In this case we're just going to change the color.
|
||||
|
||||
//White if deselected.
|
||||
Color color = Color.white;
|
||||
|
||||
if (preSelected)
|
||||
{
|
||||
//Yellow if preselected
|
||||
color = Color.yellow;
|
||||
}
|
||||
if (selected)
|
||||
{
|
||||
//And green if selected.
|
||||
color = Color.green;
|
||||
}
|
||||
|
||||
//Set the color depending on what the game object has.
|
||||
if (spriteRenderer)
|
||||
{
|
||||
spriteRenderer.color = color;
|
||||
}
|
||||
else if (text)
|
||||
{
|
||||
text.color = color;
|
||||
}
|
||||
else if (image)
|
||||
{
|
||||
image.color = color;
|
||||
}
|
||||
else if (GetComponent<UnityEngine.Renderer>())
|
||||
{
|
||||
GetComponent<UnityEngine.Renderer>().material.color = color;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0644987412a04edd8105e64bdd008b2
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,31 @@
|
||||
///Original Credit Korindian
|
||||
///Sourced from - http://forum.unity3d.com/threads/rts-style-drag-selection-box.265739/
|
||||
///Updated Credit BenZed
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
|
||||
/*
|
||||
* Implement this interface on any MonoBehaviour that you'd like to be considered selectable.
|
||||
*/
|
||||
public interface IBoxSelectable {
|
||||
bool selected {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
bool preSelected {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//This property doesn't actually need to be implemented, as this interface should already be placed on a MonoBehaviour, which will
|
||||
//already have it. Defining it here only allows us access to the transform property by casting through the selectable interface.
|
||||
Transform transform {
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72b70b31ccbda4bae8dd5cda36d2e02d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,440 @@
|
||||
///Original Credit Korindian
|
||||
///Sourced from - http://forum.unity3d.com/threads/rts-style-drag-selection-box.265739/
|
||||
///Updated Credit BenZed
|
||||
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
|
||||
|
||||
/*
|
||||
* What the SelectionBox component does is allow the game player to select objects using an RTS style click and drag interface:
|
||||
*
|
||||
* We want to be able to select Game Objects of any type,
|
||||
* We want to be able to drag select a group of game objects to select them,
|
||||
* We want to be able to hold the shift key and drag select a group of game objects to add them to the current selection,
|
||||
* We want to be able to single click a game object to select it,
|
||||
* We want to be able to hold the shift key and single click a game object to add it to the current selection,
|
||||
* We want to be able to hold the shift key and single click an already selected game object to remove it from the current selection.
|
||||
*
|
||||
* Most importantly, we want this behaviour to work with UI, 2D or 3D gameObjects, so it has to be smart about considering their respective screen spaces.
|
||||
*
|
||||
* Add this component to a Gameobject with a Canvas with RenderMode.ScreenSpaceOverlay
|
||||
* And implement the IBoxSelectable interface on any MonoBehaviour to make it selectable.
|
||||
*
|
||||
* Improvements that could be made:
|
||||
*
|
||||
* Control clicking a game object to select all objects of that type or tag.
|
||||
* Compatibility with Canvas Scaling
|
||||
* Filtering single click selections of objects occupying the same space. (So that, for example, you're only click selecting the game object found closest to the camera)
|
||||
*
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
[RequireComponent(typeof(Canvas))]
|
||||
[AddComponentMenu("UI/Extensions/Selection Box")]
|
||||
public class SelectionBox : MonoBehaviour
|
||||
{
|
||||
|
||||
// The color of the selection box.
|
||||
public Color color;
|
||||
|
||||
// An optional parameter, but you can add a sprite to the selection box to give it a border or a stylized look.
|
||||
// It's suggested you use a monochrome sprite so that the selection
|
||||
// Box color is still relevant.
|
||||
public Sprite art;
|
||||
|
||||
// Will store the location of wherever we first click before dragging.
|
||||
private Vector2 origin;
|
||||
|
||||
// A rectTransform set by the User that can limit which part of the screen is eligible for drag selection
|
||||
public RectTransform selectionMask;
|
||||
|
||||
//Stores the rectTransform connected to the generated gameObject being used for the selection box visuals
|
||||
private RectTransform boxRect;
|
||||
|
||||
// Stores all of the selectable game objects
|
||||
private IBoxSelectable[] selectables;
|
||||
|
||||
// A secondary storage of objects that the user can manually set.
|
||||
private MonoBehaviour[] selectableGroup;
|
||||
|
||||
//Stores the selectable that was touched when the mouse button was pressed down
|
||||
private IBoxSelectable clickedBeforeDrag;
|
||||
|
||||
//Stores the selectable that was touched when the mouse button was released
|
||||
private IBoxSelectable clickedAfterDrag;
|
||||
|
||||
//Custom UnityEvent so we can add Listeners to this instance when Selections are changed.
|
||||
public class SelectionEvent : UnityEvent<IBoxSelectable[]> {}
|
||||
public SelectionEvent onSelectionChange = new SelectionEvent();
|
||||
|
||||
//Ensures that the canvas that this component is attached to is set to the correct render mode. If not, it will not render the selection box properly.
|
||||
void ValidateCanvas(){
|
||||
var canvas = gameObject.GetComponent<Canvas>();
|
||||
|
||||
if (canvas.renderMode != RenderMode.ScreenSpaceOverlay) {
|
||||
throw new System.Exception("SelectionBox component must be placed on a canvas in Screen Space Overlay mode.");
|
||||
}
|
||||
|
||||
var canvasScaler = gameObject.GetComponent<CanvasScaler>();
|
||||
|
||||
if (canvasScaler && canvasScaler.enabled && (!Mathf.Approximately(canvasScaler.scaleFactor, 1f) || canvasScaler.uiScaleMode != CanvasScaler.ScaleMode.ConstantPixelSize)) {
|
||||
Destroy(canvasScaler);
|
||||
Debug.LogWarning("SelectionBox component is on a gameObject with a Canvas Scaler component. As of now, Canvas Scalers without the default settings throw off the coordinates of the selection box. Canvas Scaler has been removed.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The user can manually set a group of objects with monoBehaviours to be the pool of objects considered to be selectable. The benefits of this are two fold:
|
||||
*
|
||||
* 1) The default behaviour is to check every game object in the scene, which is much slower.
|
||||
* 2) The user can filter which objects should be selectable, for example units versus menu selections
|
||||
*
|
||||
*/
|
||||
void SetSelectableGroup(IEnumerable<MonoBehaviour> behaviourCollection) {
|
||||
|
||||
// If null, the selectionbox reverts to it's default behaviour
|
||||
if (behaviourCollection == null) {
|
||||
selectableGroup = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//Runs a double check to ensure each of the objects in the collection can be selectable, and doesn't include them if not.
|
||||
var behaviourList = new List<MonoBehaviour>();
|
||||
|
||||
foreach(var behaviour in behaviourCollection) {
|
||||
if (behaviour as IBoxSelectable != null) {
|
||||
behaviourList.Add (behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
selectableGroup = behaviourList.ToArray();
|
||||
}
|
||||
|
||||
void CreateBoxRect(){
|
||||
var selectionBoxGO = new GameObject();
|
||||
|
||||
selectionBoxGO.name = "Selection Box";
|
||||
selectionBoxGO.transform.parent = transform;
|
||||
selectionBoxGO.AddComponent<Image>();
|
||||
|
||||
boxRect = selectionBoxGO.transform as RectTransform;
|
||||
|
||||
}
|
||||
|
||||
//Set all of the relevant rectTransform properties to zero,
|
||||
//finally deactivates the boxRect gameobject since it doesn't
|
||||
//need to be enabled when not in a selection action.
|
||||
void ResetBoxRect(){
|
||||
|
||||
//Update the art and color on the off chance they've changed
|
||||
Image image = boxRect.GetComponent<Image>();
|
||||
image.color = color;
|
||||
image.sprite = art;
|
||||
|
||||
origin = Vector2.zero;
|
||||
|
||||
boxRect.anchoredPosition = Vector2.zero;
|
||||
boxRect.sizeDelta = Vector2.zero;
|
||||
boxRect.anchorMax = Vector2.zero;
|
||||
boxRect.anchorMin = Vector2.zero;
|
||||
boxRect.pivot = Vector2.zero;
|
||||
boxRect.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
|
||||
void BeginSelection(){
|
||||
// Click somewhere in the Game View.
|
||||
if (!UIExtensionsInputManager.GetMouseButtonDown(0))
|
||||
return;
|
||||
|
||||
//The boxRect will be inactive up until the point we start selecting
|
||||
boxRect.gameObject.SetActive(true);
|
||||
|
||||
// Get the initial click position of the mouse.
|
||||
origin = new Vector2(UIExtensionsInputManager.MousePosition.x, UIExtensionsInputManager.MousePosition.y);
|
||||
|
||||
//If the initial click point is not inside the selection mask, we abort the selection
|
||||
if (!PointIsValidAgainstSelectionMask(origin)) {
|
||||
ResetBoxRect();
|
||||
return;
|
||||
}
|
||||
|
||||
// The anchor is set to the same place.
|
||||
boxRect.anchoredPosition = origin;
|
||||
|
||||
MonoBehaviour[] behavioursToGetSelectionsFrom;
|
||||
|
||||
// If we do not have a group of selectables already set, we'll just loop through every object that's a monobehaviour, and look for selectable interfaces in them
|
||||
if (selectableGroup == null) {
|
||||
behavioursToGetSelectionsFrom = GameObject.FindObjectsOfType<MonoBehaviour>();
|
||||
} else {
|
||||
behavioursToGetSelectionsFrom = selectableGroup;
|
||||
}
|
||||
|
||||
//Temporary list to store the found selectables before converting to the main selectables array
|
||||
List<IBoxSelectable> selectableList = new List<IBoxSelectable>();
|
||||
|
||||
foreach (MonoBehaviour behaviour in behavioursToGetSelectionsFrom) {
|
||||
|
||||
//If the behaviour implements the selectable interface, we add it to the selectable list
|
||||
IBoxSelectable selectable = behaviour as IBoxSelectable;
|
||||
if (selectable != null) {
|
||||
selectableList.Add (selectable);
|
||||
|
||||
//We're using left shift to act as the "Add To Selection" command. So if left shift isn't pressed, we want everything to begin deselected
|
||||
if (!UIExtensionsInputManager.GetKey (KeyCode.LeftShift)) {
|
||||
selectable.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
selectables = selectableList.ToArray();
|
||||
|
||||
//For single-click actions, we need to get the selectable that was clicked when selection began (if any)
|
||||
clickedBeforeDrag = GetSelectableAtMousePosition();
|
||||
|
||||
}
|
||||
|
||||
bool PointIsValidAgainstSelectionMask(Vector2 screenPoint){
|
||||
//If there is no selection mask, any point is valid
|
||||
if (!selectionMask) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Camera screenPointCamera = GetScreenPointCamera(selectionMask);
|
||||
|
||||
return RectTransformUtility.RectangleContainsScreenPoint(selectionMask, screenPoint, screenPointCamera);
|
||||
}
|
||||
|
||||
IBoxSelectable GetSelectableAtMousePosition() {
|
||||
//Firstly, we cannot click on something that is not inside the selection mask (if we have one)
|
||||
if (!PointIsValidAgainstSelectionMask(UIExtensionsInputManager.MousePosition)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//This gets a bit tricky, because we have to make considerations depending on the hierarchy of the selectable's gameObject
|
||||
foreach (var selectable in selectables) {
|
||||
|
||||
//First we check to see if the selectable has a rectTransform
|
||||
var rectTransform = (selectable.transform as RectTransform);
|
||||
|
||||
if (rectTransform) {
|
||||
//Because if it does, the camera we use to calculate it's screen point will vary
|
||||
var screenCamera = GetScreenPointCamera(rectTransform);
|
||||
|
||||
//Once we've found the rendering camera, we check if the selectables rectTransform contains the click. That way we
|
||||
//Can click anywhere on a rectTransform to select it.
|
||||
if (RectTransformUtility.RectangleContainsScreenPoint(rectTransform, UIExtensionsInputManager.MousePosition, screenCamera)) {
|
||||
|
||||
//And if it does, we select it and send it back
|
||||
return selectable;
|
||||
}
|
||||
} else {
|
||||
//If it doesn't have a rectTransform, we need to get the radius so we can use it as an area around the center to detect a click.
|
||||
//This works because a 2D or 3D renderer will both return a radius
|
||||
var radius = selectable.transform.GetComponent<UnityEngine.Renderer>().bounds.extents.magnitude;
|
||||
|
||||
var selectableScreenPoint = GetScreenPointOfSelectable(selectable);
|
||||
|
||||
//Check that the click fits within the screen-radius of the selectable
|
||||
if (Vector2.Distance(selectableScreenPoint, UIExtensionsInputManager.MousePosition) <= radius) {
|
||||
|
||||
//And if it does, we select it and send it back
|
||||
return selectable;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
void DragSelection(){
|
||||
//Return if we're not dragging or if the selection has been aborted (BoxRect disabled)
|
||||
if (!UIExtensionsInputManager.GetMouseButton(0) || !boxRect.gameObject.activeSelf)
|
||||
return;
|
||||
|
||||
// Store the current mouse position in screen space.
|
||||
Vector2 currentMousePosition = new Vector2(UIExtensionsInputManager.MousePosition.x, UIExtensionsInputManager.MousePosition.y);
|
||||
|
||||
// How far have we moved the mouse?
|
||||
Vector2 difference = currentMousePosition - origin;
|
||||
|
||||
// Copy the initial click position to a new variable. Using the original variable will cause
|
||||
// the anchor to move around to wherever the current mouse position is,
|
||||
// which isn't desirable.
|
||||
Vector2 startPoint = origin;
|
||||
|
||||
// The following code accounts for dragging in various directions.
|
||||
if (difference.x < 0)
|
||||
{
|
||||
startPoint.x = currentMousePosition.x;
|
||||
difference.x = -difference.x;
|
||||
}
|
||||
if (difference.y < 0)
|
||||
{
|
||||
startPoint.y = currentMousePosition.y;
|
||||
difference.y = -difference.y;
|
||||
}
|
||||
|
||||
// Set the anchor, width and height every frame.
|
||||
boxRect.anchoredPosition = startPoint;
|
||||
boxRect.sizeDelta = difference;
|
||||
|
||||
//Then we check our list of Selectables to see if they're being preselected or not.
|
||||
foreach(var selectable in selectables) {
|
||||
|
||||
Vector3 screenPoint = GetScreenPointOfSelectable(selectable);
|
||||
|
||||
//If the box Rect contains the selectables screen point and that point is inside a valid selection mask, it's being preselected, otherwise it is not.
|
||||
selectable.preSelected = RectTransformUtility.RectangleContainsScreenPoint(boxRect, screenPoint, null) && PointIsValidAgainstSelectionMask(screenPoint);
|
||||
|
||||
}
|
||||
|
||||
//Finally, since it's possible for our first clicked object to not be within the bounds of the selection box
|
||||
//If it exists, we always ensure that it is preselected.
|
||||
if (clickedBeforeDrag != null) {
|
||||
clickedBeforeDrag.preSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplySingleClickDeselection(){
|
||||
|
||||
//If we didn't touch anything with the original mouse press, we don't need to continue checking
|
||||
if (clickedBeforeDrag == null)
|
||||
return;
|
||||
|
||||
//If we clicked a selectable without dragging, and that selectable was previously selected, we must be trying to deselect it.
|
||||
if (clickedAfterDrag != null && clickedBeforeDrag.selected && clickedBeforeDrag.transform == clickedAfterDrag.transform ) {
|
||||
clickedBeforeDrag.selected = false;
|
||||
clickedBeforeDrag.preSelected = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ApplyPreSelections(){
|
||||
|
||||
foreach(var selectable in selectables) {
|
||||
|
||||
//If the selectable was preSelected, we finalize it as selected.
|
||||
if (selectable.preSelected) {
|
||||
selectable.selected = true;
|
||||
selectable.preSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vector2 GetScreenPointOfSelectable(IBoxSelectable selectable) {
|
||||
//Getting the screen point requires it's own function, because we have to take into consideration the selectables hierarchy.
|
||||
|
||||
//Cast the transform as a rectTransform
|
||||
var rectTransform = selectable.transform as RectTransform;
|
||||
|
||||
//If it has a rectTransform component, it must be in the hierarchy of a canvas, somewhere.
|
||||
if (rectTransform) {
|
||||
|
||||
//And the camera used to calculate it's screen point will vary.
|
||||
Camera renderingCamera = GetScreenPointCamera(rectTransform);
|
||||
|
||||
return RectTransformUtility.WorldToScreenPoint(renderingCamera, selectable.transform.position);
|
||||
}
|
||||
|
||||
//If it's no in the hierarchy of a canvas, the regular Camera.main.WorldToScreenPoint will do.
|
||||
return Camera.main.WorldToScreenPoint(selectable.transform.position);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Finding the camera used to calculate the screenPoint of an object causes a couple of problems:
|
||||
*
|
||||
* If it has a rectTransform, the root Canvas that the rectTransform is a descendant of will give unusable
|
||||
* screen points depending on the Canvas.RenderMode, if we don't do any further calculation.
|
||||
*
|
||||
* This function solves that problem.
|
||||
*/
|
||||
Camera GetScreenPointCamera(RectTransform rectTransform) {
|
||||
|
||||
Canvas rootCanvas = null;
|
||||
RectTransform rectCheck = rectTransform;
|
||||
|
||||
//We're going to check all the canvases in the hierarchy of this rectTransform until we find the root.
|
||||
do {
|
||||
rootCanvas = rectCheck.GetComponent<Canvas>();
|
||||
|
||||
//If we found a canvas on this Object, and it's not the rootCanvas, then we don't want to keep it
|
||||
if (rootCanvas && !rootCanvas.isRootCanvas) {
|
||||
rootCanvas = null;
|
||||
}
|
||||
|
||||
//Then we promote the rect we're checking to it's parent.
|
||||
rectCheck = (RectTransform)rectCheck.parent;
|
||||
|
||||
} while (rootCanvas == null);
|
||||
|
||||
//Once we've found the root Canvas, we return a camera depending on it's render mode.
|
||||
switch (rootCanvas.renderMode) {
|
||||
case RenderMode.ScreenSpaceOverlay:
|
||||
//If we send back a camera when set to screen space overlay, the coordinates will not be accurate. If we return null, they will be.
|
||||
return null;
|
||||
|
||||
case RenderMode.ScreenSpaceCamera:
|
||||
//If it's set to screen space we use the world Camera that the Canvas is using.
|
||||
//If it doesn't have one set, however, we have to send back the current camera. otherwise the coordinates will not be accurate.
|
||||
return (rootCanvas.worldCamera) ? rootCanvas.worldCamera : Camera.main;
|
||||
|
||||
default:
|
||||
case RenderMode.WorldSpace:
|
||||
//World space always uses the current camera.
|
||||
return Camera.main;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBoxSelectable[] GetAllSelected(){
|
||||
if (selectables == null) {
|
||||
return new IBoxSelectable[0];
|
||||
}
|
||||
|
||||
var selectedList = new List<IBoxSelectable>();
|
||||
|
||||
foreach(var selectable in selectables) {
|
||||
if (selectable.selected) {
|
||||
selectedList.Add (selectable);
|
||||
}
|
||||
}
|
||||
|
||||
return selectedList.ToArray();
|
||||
}
|
||||
|
||||
void EndSelection(){
|
||||
//Get out if we haven't finished selecting, or if the selection has been aborted (boxRect disabled)
|
||||
if (!UIExtensionsInputManager.GetMouseButtonUp(0) || !boxRect.gameObject.activeSelf)
|
||||
return;
|
||||
|
||||
clickedAfterDrag = GetSelectableAtMousePosition();
|
||||
|
||||
ApplySingleClickDeselection();
|
||||
ApplyPreSelections();
|
||||
ResetBoxRect();
|
||||
onSelectionChange.Invoke(GetAllSelected());
|
||||
}
|
||||
|
||||
void Start(){
|
||||
ValidateCanvas();
|
||||
CreateBoxRect();
|
||||
ResetBoxRect();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
BeginSelection ();
|
||||
DragSelection ();
|
||||
EndSelection ();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f143353fa45b4df79a3722ad82431d5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,254 @@
|
||||
/// Credit David Gileadi
|
||||
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/11
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
// Stepper control
|
||||
[AddComponentMenu("UI/Extensions/Stepper")]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class Stepper : UIBehaviour
|
||||
{
|
||||
private Selectable[] _sides;
|
||||
[SerializeField]
|
||||
[Tooltip("The current step value of the control")]
|
||||
private int _value = 0;
|
||||
[SerializeField]
|
||||
[Tooltip("The minimum step value allowed by the control. When reached it will disable the '-' button")]
|
||||
private int _minimum = 0;
|
||||
[SerializeField]
|
||||
[Tooltip("The maximum step value allowed by the control. When reached it will disable the '+' button")]
|
||||
private int _maximum = 100;
|
||||
[SerializeField]
|
||||
[Tooltip("The step increment used to increment / decrement the step value")]
|
||||
private int _step = 1;
|
||||
[SerializeField]
|
||||
[Tooltip("Does the step value loop around from end to end")]
|
||||
private bool _wrap = false;
|
||||
[SerializeField]
|
||||
[Tooltip("A GameObject with an Image to use as a separator between segments. Size of the RectTransform will determine the size of the separator used.\nNote, make sure to disable the separator GO so that it does not affect the scene")]
|
||||
private Graphic _separator;
|
||||
private float _separatorWidth = 0;
|
||||
|
||||
private float separatorWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_separatorWidth == 0 && separator)
|
||||
{
|
||||
_separatorWidth = separator.rectTransform.rect.width;
|
||||
var image = separator.GetComponent<Image>();
|
||||
if (image)
|
||||
_separatorWidth /= image.pixelsPerUnit;
|
||||
}
|
||||
return _separatorWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Event delegates triggered on click.
|
||||
[SerializeField]
|
||||
private StepperValueChangedEvent _onValueChanged = new StepperValueChangedEvent();
|
||||
|
||||
[Serializable]
|
||||
public class StepperValueChangedEvent : UnityEvent<int> { }
|
||||
|
||||
public Selectable[] sides
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_sides == null || _sides.Length == 0)
|
||||
{
|
||||
_sides = GetSides();
|
||||
}
|
||||
return _sides;
|
||||
}
|
||||
}
|
||||
|
||||
public int value { get { return _value; } set { _value = value; } }
|
||||
|
||||
public int minimum { get { return _minimum; } set { _minimum = value; } }
|
||||
|
||||
public int maximum { get { return _maximum; } set { _maximum = value; } }
|
||||
|
||||
public int step { get { return _step; } set { _step = value; } }
|
||||
|
||||
public bool wrap { get { return _wrap; } set { _wrap = value; } }
|
||||
|
||||
public Graphic separator { get { return _separator; } set { _separator = value; _separatorWidth = 0; LayoutSides(sides); } }
|
||||
|
||||
public StepperValueChangedEvent onValueChanged
|
||||
{
|
||||
get { return _onValueChanged; }
|
||||
set { _onValueChanged = value; }
|
||||
}
|
||||
|
||||
protected Stepper()
|
||||
{ }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
|
||||
RecreateSprites(sides);
|
||||
if (separator)
|
||||
LayoutSides();
|
||||
|
||||
if (!wrap)
|
||||
{
|
||||
DisableAtExtremes(sides);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
if (isActiveAndEnabled)
|
||||
StartCoroutine(DelayedInit());
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
StartCoroutine(DelayedInit());
|
||||
}
|
||||
|
||||
IEnumerator DelayedInit()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
RecreateSprites(sides);
|
||||
}
|
||||
|
||||
private Selectable[] GetSides()
|
||||
{
|
||||
var buttons = GetComponentsInChildren<Selectable>();
|
||||
if (buttons.Length != 2)
|
||||
{
|
||||
throw new InvalidOperationException("A stepper must have two Button children");
|
||||
}
|
||||
|
||||
if (!wrap)
|
||||
{
|
||||
DisableAtExtremes(buttons);
|
||||
}
|
||||
LayoutSides(buttons);
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public void StepUp()
|
||||
{
|
||||
Step(step);
|
||||
}
|
||||
|
||||
public void StepDown()
|
||||
{
|
||||
Step(-step);
|
||||
}
|
||||
|
||||
private void Step(int amount)
|
||||
{
|
||||
value += amount;
|
||||
|
||||
if (wrap)
|
||||
{
|
||||
if (value > maximum) value = minimum;
|
||||
if (value < minimum) value = maximum;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Math.Max(minimum, value);
|
||||
value = Math.Min(maximum, value);
|
||||
|
||||
DisableAtExtremes(sides);
|
||||
}
|
||||
|
||||
_onValueChanged.Invoke(value);
|
||||
}
|
||||
|
||||
private void DisableAtExtremes(Selectable[] sides)
|
||||
{
|
||||
sides[0].interactable = wrap || value > minimum;
|
||||
sides[1].interactable = wrap || value < maximum;
|
||||
}
|
||||
|
||||
private void RecreateSprites(Selectable[] sides)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (sides[i].image == null)
|
||||
continue;
|
||||
|
||||
var sprite = CutSprite(sides[i].image.sprite, i == 0);
|
||||
var side = sides[i].GetComponent<StepperSide>();
|
||||
if (side)
|
||||
{
|
||||
side.cutSprite = sprite;
|
||||
}
|
||||
sides[i].image.overrideSprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
static internal Sprite CutSprite(Sprite sprite, bool leftmost)
|
||||
{
|
||||
if (sprite.border.x == 0 || sprite.border.z == 0)
|
||||
return sprite;
|
||||
|
||||
var rect = sprite.rect;
|
||||
var border = sprite.border;
|
||||
|
||||
if (leftmost)
|
||||
{
|
||||
rect.xMax = border.z;
|
||||
border.z = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.xMin = border.x;
|
||||
border.x = 0;
|
||||
}
|
||||
|
||||
return Sprite.Create(sprite.texture, rect, sprite.pivot, sprite.pixelsPerUnit, 0, SpriteMeshType.FullRect, border);
|
||||
}
|
||||
|
||||
public void LayoutSides(Selectable[] sides = null)
|
||||
{
|
||||
sides = sides ?? this.sides;
|
||||
|
||||
RecreateSprites(sides);
|
||||
|
||||
RectTransform transform = this.transform as RectTransform;
|
||||
float width = (transform.rect.width / 2) - separatorWidth;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
float insetX = i == 0 ? 0 : width + separatorWidth;
|
||||
|
||||
var rectTransform = sides[i].GetComponent<RectTransform>();
|
||||
rectTransform.anchorMin = Vector2.zero;
|
||||
rectTransform.anchorMax = Vector2.zero;
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, insetX, width);
|
||||
rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, transform.rect.height);
|
||||
|
||||
// TODO: maybe adjust text position
|
||||
}
|
||||
|
||||
if (separator)
|
||||
{
|
||||
var sepTransform = gameObject.transform.Find("Separator");
|
||||
Graphic sep = (sepTransform != null) ? sepTransform.GetComponent<Graphic>() : (GameObject.Instantiate(separator.gameObject) as GameObject).GetComponent<Graphic>();
|
||||
sep.gameObject.name = "Separator";
|
||||
sep.gameObject.SetActive(true);
|
||||
sep.rectTransform.SetParent(this.transform, false);
|
||||
sep.rectTransform.anchorMin = Vector2.zero;
|
||||
sep.rectTransform.anchorMax = Vector2.zero;
|
||||
sep.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, width, separatorWidth);
|
||||
sep.rectTransform.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, transform.rect.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1867f0d5b751404f86b0e562a3d783b
|
||||
timeCreated: 1501004355
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,103 @@
|
||||
/// Credit David Gileadi
|
||||
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/11
|
||||
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(Selectable))]
|
||||
public class StepperSide :
|
||||
UIBehaviour,
|
||||
IPointerClickHandler,
|
||||
ISubmitHandler,
|
||||
IPointerEnterHandler, IPointerExitHandler,
|
||||
IPointerDownHandler, IPointerUpHandler,
|
||||
ISelectHandler, IDeselectHandler
|
||||
{
|
||||
Selectable button { get { return GetComponent<Selectable>(); } }
|
||||
|
||||
Stepper stepper { get { return GetComponentInParent<Stepper>(); } }
|
||||
|
||||
bool leftmost { get { return button == stepper.sides[0]; } }
|
||||
|
||||
internal Sprite cutSprite;
|
||||
|
||||
protected StepperSide()
|
||||
{ }
|
||||
|
||||
public virtual void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
Press();
|
||||
AdjustSprite(false);
|
||||
}
|
||||
|
||||
public virtual void OnSubmit(BaseEventData eventData)
|
||||
{
|
||||
Press();
|
||||
AdjustSprite(true);
|
||||
}
|
||||
|
||||
public virtual void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
AdjustSprite(false);
|
||||
}
|
||||
|
||||
public virtual void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
AdjustSprite(true);
|
||||
}
|
||||
|
||||
public virtual void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
AdjustSprite(false);
|
||||
}
|
||||
|
||||
public virtual void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
AdjustSprite(false);
|
||||
}
|
||||
|
||||
public virtual void OnSelect(BaseEventData eventData)
|
||||
{
|
||||
AdjustSprite(false);
|
||||
}
|
||||
|
||||
public virtual void OnDeselect(BaseEventData eventData)
|
||||
{
|
||||
AdjustSprite(true);
|
||||
}
|
||||
|
||||
private void Press()
|
||||
{
|
||||
if (!button.IsActive() || !button.IsInteractable())
|
||||
return;
|
||||
|
||||
if (leftmost)
|
||||
{
|
||||
stepper.StepDown();
|
||||
}
|
||||
else
|
||||
{
|
||||
stepper.StepUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void AdjustSprite(bool restore)
|
||||
{
|
||||
var image = button.image;
|
||||
if (!image || image.overrideSprite == cutSprite)
|
||||
return;
|
||||
|
||||
if (restore)
|
||||
image.overrideSprite = cutSprite;
|
||||
else
|
||||
image.overrideSprite = Stepper.CutSprite(image.overrideSprite, leftmost);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd241e0d4a8de4fc9924c1dce285c587
|
||||
timeCreated: 1503449912
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,735 @@
|
||||
/// Credit drobina, w34edrtfg, playemgames
|
||||
/// Sourced from - http://forum.unity3d.com/threads/sprite-icons-with-text-e-g-emoticons.265927/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI.Extensions {
|
||||
// Image according to the label inside the name attribute to load, read from the Resources directory. The size of the image is controlled by the size property.
|
||||
|
||||
// Use: Add Icon name and sprite to the icons list
|
||||
|
||||
[AddComponentMenu("UI/Extensions/TextPic")]
|
||||
|
||||
[ExecuteInEditMode] // Needed for culling images that are not used //
|
||||
public class TextPic : Text, IPointerClickHandler, IPointerExitHandler, IPointerEnterHandler, ISelectHandler {
|
||||
// Icon entry to replace text with
|
||||
[Serializable]
|
||||
public struct IconName {
|
||||
public string name;
|
||||
public Sprite sprite;
|
||||
public Vector2 offset;
|
||||
public Vector2 scale;
|
||||
}
|
||||
|
||||
// Icons and text to replace
|
||||
public IconName[] inspectorIconList;
|
||||
|
||||
[Tooltip("Global scaling factor for all images")]
|
||||
public float ImageScalingFactor = 1;
|
||||
|
||||
// Write the name or hex value of the hyperlink color
|
||||
public string hyperlinkColor = "blue";
|
||||
|
||||
// Offset image by x, y
|
||||
public Vector2 imageOffset = Vector2.zero;
|
||||
public bool isCreating_m_HrefInfos = true;
|
||||
|
||||
[Serializable]
|
||||
public class HrefClickEvent : UnityEvent<string> { }
|
||||
|
||||
[SerializeField]
|
||||
private HrefClickEvent m_OnHrefClick = new HrefClickEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Hyperlink Click Event
|
||||
/// </summary>
|
||||
public HrefClickEvent onHrefClick {
|
||||
get { return m_OnHrefClick; }
|
||||
set { m_OnHrefClick = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Pool
|
||||
/// </summary>
|
||||
private readonly List<Image> m_ImagesPool = new List<Image>();
|
||||
private readonly List<GameObject> culled_ImagesPool = new List<GameObject>();
|
||||
|
||||
// Used for check for culling images
|
||||
private bool clearImages = false;
|
||||
|
||||
// Lock to ensure images get culled properly
|
||||
private Object thisLock = new Object();
|
||||
|
||||
/// <summary>
|
||||
/// Vertex Index
|
||||
/// </summary>
|
||||
private readonly List<int> m_ImagesVertexIndex = new List<int>();
|
||||
|
||||
/// <summary>
|
||||
/// Regular expression to replace
|
||||
/// </summary>
|
||||
private static readonly Regex s_Regex =
|
||||
new Regex(@"<quad name=(.+?) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />", RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// Hyperlink Regular Expression
|
||||
/// </summary>
|
||||
private static readonly Regex s_HrefRegex =
|
||||
new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
|
||||
|
||||
// String to create quads
|
||||
private string fixedString;
|
||||
|
||||
// Update the quad images when true
|
||||
private bool updateQuad = false;
|
||||
|
||||
/// <summary>
|
||||
/// After parsing the final text
|
||||
/// </summary>
|
||||
private string m_OutputText;
|
||||
|
||||
private Button button;
|
||||
|
||||
// Used for custom selection as a variable for other scripts
|
||||
private bool selected = false;
|
||||
|
||||
public bool Selected
|
||||
{
|
||||
get { return selected; }
|
||||
set { selected = value; }
|
||||
}
|
||||
|
||||
// Positions of images for icon placement
|
||||
private List<Vector2> positions = new List<Vector2>();
|
||||
|
||||
// Little hack to support multiple hrefs with same name
|
||||
private string previousText = "";
|
||||
|
||||
/// <summary>
|
||||
/// Hyperlinks Info
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class HrefInfo {
|
||||
public int startIndex;
|
||||
|
||||
public int endIndex;
|
||||
|
||||
public string name;
|
||||
|
||||
public readonly List<Rect> boxes = new List<Rect>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hyperlink List
|
||||
/// </summary>
|
||||
private readonly List<HrefInfo> m_HrefInfos = new List<HrefInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Text Builder
|
||||
/// </summary>
|
||||
private static readonly StringBuilder s_TextBuilder = new StringBuilder();
|
||||
|
||||
// Matches for quad tags
|
||||
private MatchCollection matches;
|
||||
|
||||
// Matches for quad tags
|
||||
private MatchCollection href_matches;
|
||||
|
||||
// Matches for removing characters
|
||||
private MatchCollection removeCharacters;
|
||||
|
||||
// Index of current pic
|
||||
private int picIndex;
|
||||
|
||||
// Index of current pic vertex
|
||||
private int vertIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Unity 2019.1.5 Fixes to text placement resulting from the removal of verts for spaces and non rendered characters
|
||||
/// </summary>
|
||||
|
||||
// There is no directive for incremented versions so will have to hack together
|
||||
private bool usesNewRendering = false;
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
/// <summary>
|
||||
/// Regular expression to remove non rendered characters
|
||||
/// </summary>
|
||||
private static readonly Regex remove_Regex =
|
||||
new Regex(@"<b>|</b>|<i>|</i>|<size=.*?>|</size>|<color=.*?>|</color>|<material=.*?>|</material>|<quad name=(.+?) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />|<a href=([^>\n\s]+)>|</a>|\s", RegexOptions.Singleline);
|
||||
|
||||
// List of indexes that are compared against matches to remove quad tags
|
||||
List<int> indexes = new List<int>();
|
||||
|
||||
// Characters to remove from string for finding the correct index for vertices for images
|
||||
private int charactersRemoved = 0;
|
||||
|
||||
// Characters to remove from string for finding the correct start index for vertices for href bounds
|
||||
private int startCharactersRemoved = 0;
|
||||
|
||||
// Characters to remove from string for finding the correct end index for vertices for href bounds
|
||||
private int endCharactersRemoved = 0;
|
||||
#endif
|
||||
|
||||
// Count of current href
|
||||
private int count = 0;
|
||||
|
||||
// Index of current href
|
||||
private int indexText = 0;
|
||||
|
||||
// Original text temporary variable holder
|
||||
private string originalText;
|
||||
|
||||
// Vertex we are modifying
|
||||
private UIVertex vert;
|
||||
|
||||
// Local Point for Href
|
||||
private Vector2 lp;
|
||||
|
||||
|
||||
/// METHODS ///
|
||||
|
||||
public void ResetIconList() {
|
||||
Reset_m_HrefInfos ();
|
||||
base.Start();
|
||||
}
|
||||
|
||||
protected void UpdateQuadImage() {
|
||||
#if UNITY_EDITOR && !UNITY_2018_3_OR_NEWER
|
||||
if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
m_OutputText = GetOutputText();
|
||||
|
||||
matches = s_Regex.Matches(m_OutputText);
|
||||
|
||||
if (matches != null && matches.Count > 0) {
|
||||
for (int i = 0; i < matches.Count; i++) {
|
||||
m_ImagesPool.RemoveAll(image => image == null);
|
||||
|
||||
if (m_ImagesPool.Count == 0) {
|
||||
GetComponentsInChildren<Image>(true, m_ImagesPool);
|
||||
}
|
||||
|
||||
if (matches.Count > m_ImagesPool.Count) {
|
||||
DefaultControls.Resources resources = new DefaultControls.Resources();
|
||||
GameObject go = DefaultControls.CreateImage(resources);
|
||||
|
||||
go.layer = gameObject.layer;
|
||||
|
||||
RectTransform rt = go.transform as RectTransform;
|
||||
|
||||
if (rt) {
|
||||
rt.SetParent(rectTransform);
|
||||
rt.anchoredPosition3D = Vector3.zero;
|
||||
rt.localRotation = Quaternion.identity;
|
||||
rt.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
m_ImagesPool.Add(go.GetComponent<Image>());
|
||||
}
|
||||
|
||||
string spriteName = matches[i].Groups[1].Value;
|
||||
|
||||
Image img = m_ImagesPool[i];
|
||||
|
||||
Vector2 imgoffset = Vector2.zero;
|
||||
|
||||
if (img.sprite == null || img.sprite.name != spriteName) {
|
||||
if (inspectorIconList != null && inspectorIconList.Length > 0) {
|
||||
for (int s = 0; s < inspectorIconList.Length; s++) {
|
||||
if (inspectorIconList[s].name == spriteName) {
|
||||
img.sprite = inspectorIconList[s].sprite;
|
||||
img.preserveAspect = true;
|
||||
img.rectTransform.sizeDelta = new Vector2(fontSize * ImageScalingFactor * inspectorIconList[s].scale.x,
|
||||
fontSize * ImageScalingFactor * inspectorIconList[s].scale.y);
|
||||
imgoffset = inspectorIconList[s].offset;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img.enabled = true;
|
||||
|
||||
if (positions.Count > 0 && i < positions.Count) {
|
||||
img.rectTransform.anchoredPosition = positions[i] += imgoffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If there are no matches, remove the images from the pool
|
||||
for (int i = m_ImagesPool.Count - 1; i > 0; i--) {
|
||||
if (m_ImagesPool[i]) {
|
||||
if (!culled_ImagesPool.Contains(m_ImagesPool[i].gameObject)) {
|
||||
culled_ImagesPool.Add(m_ImagesPool[i].gameObject);
|
||||
m_ImagesPool.Remove(m_ImagesPool[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any images that are not being used
|
||||
for (int i = m_ImagesPool.Count - 1; i >= matches.Count; i--) {
|
||||
if (i >= 0 && m_ImagesPool.Count > 0) {
|
||||
if (m_ImagesPool[i]) {
|
||||
if (!culled_ImagesPool.Contains(m_ImagesPool[i].gameObject)) {
|
||||
culled_ImagesPool.Add(m_ImagesPool[i].gameObject);
|
||||
m_ImagesPool.Remove(m_ImagesPool[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the images when it is safe to do so
|
||||
if (culled_ImagesPool.Count > 0) {
|
||||
clearImages = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Reseting m_HrefInfos array if there is any change in text
|
||||
void Reset_m_HrefInfos () {
|
||||
previousText = text;
|
||||
|
||||
m_HrefInfos.Clear();
|
||||
|
||||
isCreating_m_HrefInfos = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finally, the output text hyperlinks get parsed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string GetOutputText() {
|
||||
s_TextBuilder.Length = 0;
|
||||
|
||||
indexText = 0;
|
||||
|
||||
fixedString = this.text;
|
||||
|
||||
if (inspectorIconList != null && inspectorIconList.Length > 0) {
|
||||
for (int i = 0; i < inspectorIconList.Length; i++) {
|
||||
if (!string.IsNullOrEmpty(inspectorIconList[i].name)) {
|
||||
fixedString = fixedString.Replace(inspectorIconList[i].name,
|
||||
"<quad name=" + inspectorIconList[i].name + " size=" + fontSize + " width=1 />");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
href_matches = s_HrefRegex.Matches(fixedString);
|
||||
|
||||
if (href_matches != null && href_matches.Count > 0) {
|
||||
for (int i = 0; i < href_matches.Count; i++ ) {
|
||||
s_TextBuilder.Append(fixedString.Substring(indexText, href_matches[i].Index - indexText));
|
||||
s_TextBuilder.Append("<color=" + hyperlinkColor + ">"); // Hyperlink color
|
||||
|
||||
var group = href_matches[i].Groups[1];
|
||||
|
||||
if (isCreating_m_HrefInfos) {
|
||||
HrefInfo hrefInfo = new HrefInfo {
|
||||
// Hyperlinks in text starting index
|
||||
startIndex = (usesNewRendering ? s_TextBuilder.Length : s_TextBuilder.Length * 4),
|
||||
endIndex = (usesNewRendering ? (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) : (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) * 4 + 3),
|
||||
name = group.Value
|
||||
};
|
||||
|
||||
m_HrefInfos.Add(hrefInfo);
|
||||
}
|
||||
else {
|
||||
if (count <= m_HrefInfos.Count - 1) {
|
||||
// Hyperlinks in text starting index
|
||||
m_HrefInfos[count].startIndex = (usesNewRendering ? s_TextBuilder.Length : s_TextBuilder.Length * 4);
|
||||
m_HrefInfos[count].endIndex = (usesNewRendering ? (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) : (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) * 4 + 3);
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
s_TextBuilder.Append(href_matches[i].Groups[2].Value);
|
||||
s_TextBuilder.Append("</color>");
|
||||
|
||||
indexText = href_matches[i].Index + href_matches[i].Length;
|
||||
}
|
||||
}
|
||||
|
||||
// we should create array only once or if there is any change in the text
|
||||
if (isCreating_m_HrefInfos)
|
||||
isCreating_m_HrefInfos = false;
|
||||
|
||||
s_TextBuilder.Append(fixedString.Substring(indexText, fixedString.Length - indexText));
|
||||
|
||||
m_OutputText = s_TextBuilder.ToString();
|
||||
|
||||
m_ImagesVertexIndex.Clear();
|
||||
|
||||
matches = s_Regex.Matches(m_OutputText);
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
href_matches = s_HrefRegex.Matches(m_OutputText);
|
||||
|
||||
indexes.Clear();
|
||||
|
||||
for (int r = 0; r < matches.Count; r++) {
|
||||
indexes.Add(matches[r].Index);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (matches != null && matches.Count > 0) {
|
||||
for (int i = 0; i < matches.Count; i++) {
|
||||
picIndex = matches[i].Index;
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (usesNewRendering) {
|
||||
charactersRemoved = 0;
|
||||
|
||||
removeCharacters = remove_Regex.Matches(m_OutputText);
|
||||
|
||||
for (int r = 0; r < removeCharacters.Count; r++) {
|
||||
if (removeCharacters[r].Index < picIndex && !indexes.Contains(removeCharacters[r].Index)) {
|
||||
charactersRemoved += removeCharacters[r].Length;
|
||||
}
|
||||
}
|
||||
|
||||
for (int r = 0; r < i; r++) {
|
||||
charactersRemoved += (matches[r].Length - 1);
|
||||
}
|
||||
|
||||
picIndex -= charactersRemoved;
|
||||
}
|
||||
#endif
|
||||
|
||||
vertIndex = picIndex * 4 + 3;
|
||||
|
||||
m_ImagesVertexIndex.Add(vertIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
if (usesNewRendering) {
|
||||
if (m_HrefInfos != null && m_HrefInfos.Count > 0) {
|
||||
for (int i = 0; i < m_HrefInfos.Count; i++) {
|
||||
startCharactersRemoved = 0;
|
||||
endCharactersRemoved = 0;
|
||||
|
||||
removeCharacters = remove_Regex.Matches(m_OutputText);
|
||||
|
||||
for (int r = 0; r < removeCharacters.Count; r++) {
|
||||
if (removeCharacters[r].Index < m_HrefInfos[i].startIndex && !indexes.Contains(removeCharacters[r].Index)) {
|
||||
startCharactersRemoved += removeCharacters[r].Length;
|
||||
}
|
||||
else if (removeCharacters[r].Index < m_HrefInfos[i].startIndex && indexes.Contains(removeCharacters[r].Index)) {
|
||||
startCharactersRemoved += removeCharacters[r].Length - 1;
|
||||
}
|
||||
|
||||
if (removeCharacters[r].Index < m_HrefInfos[i].endIndex && !indexes.Contains(removeCharacters[r].Index)) {
|
||||
endCharactersRemoved += removeCharacters[r].Length;
|
||||
}
|
||||
else if (removeCharacters[r].Index < m_HrefInfos[i].endIndex && indexes.Contains(removeCharacters[r].Index)) {
|
||||
endCharactersRemoved += removeCharacters[r].Length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_HrefInfos[i].startIndex -= startCharactersRemoved;
|
||||
|
||||
m_HrefInfos[i].startIndex = m_HrefInfos[i].startIndex * 4;
|
||||
|
||||
m_HrefInfos[i].endIndex -= endCharactersRemoved;
|
||||
|
||||
m_HrefInfos[i].endIndex = m_HrefInfos[i].endIndex * 4 + 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_OutputText;
|
||||
}
|
||||
|
||||
// Process href links to open them as a url, can override this function for custom functionality
|
||||
public virtual void OnHrefClick(string hrefName) {
|
||||
Application.OpenURL(hrefName);
|
||||
|
||||
// Debug.Log(hrefName);
|
||||
}
|
||||
|
||||
|
||||
/// UNITY METHODS ///
|
||||
|
||||
protected override void OnPopulateMesh(VertexHelper toFill) {
|
||||
originalText = m_Text;
|
||||
m_Text = GetOutputText();
|
||||
|
||||
base.OnPopulateMesh(toFill);
|
||||
|
||||
m_DisableFontTextureRebuiltCallback = true;
|
||||
|
||||
m_Text = originalText;
|
||||
|
||||
positions.Clear();
|
||||
|
||||
vert = new UIVertex();
|
||||
|
||||
for (int i = 0; i < m_ImagesVertexIndex.Count; i++) {
|
||||
int endIndex = m_ImagesVertexIndex[i];
|
||||
|
||||
if (endIndex < toFill.currentVertCount) {
|
||||
toFill.PopulateUIVertex(ref vert, endIndex);
|
||||
|
||||
positions.Add(new Vector2((vert.position.x + fontSize / 2), (vert.position.y + fontSize / 2)) + imageOffset);
|
||||
|
||||
// Erase the lower left corner of the black specks
|
||||
toFill.PopulateUIVertex(ref vert, endIndex - 3);
|
||||
|
||||
Vector3 pos = vert.position;
|
||||
|
||||
for (int j = endIndex, m = endIndex - 3; j > m; j--) {
|
||||
toFill.PopulateUIVertex(ref vert, endIndex);
|
||||
vert.position = pos;
|
||||
toFill.SetUIVertex(vert, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hyperlinks surround processing box
|
||||
for (int h = 0; h < m_HrefInfos.Count; h++) {
|
||||
m_HrefInfos[h].boxes.Clear();
|
||||
|
||||
if (m_HrefInfos[h].startIndex >= toFill.currentVertCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Hyperlink inside the text is added to surround the vertex index coordinate frame
|
||||
toFill.PopulateUIVertex(ref vert, m_HrefInfos[h].startIndex);
|
||||
|
||||
Vector3 pos = vert.position;
|
||||
|
||||
Bounds bounds = new Bounds(pos, Vector3.zero);
|
||||
|
||||
for (int i = m_HrefInfos[h].startIndex, m = m_HrefInfos[h].endIndex; i < m; i++) {
|
||||
if (i >= toFill.currentVertCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
toFill.PopulateUIVertex(ref vert, i);
|
||||
|
||||
pos = vert.position;
|
||||
|
||||
// Wrap re-add surround frame
|
||||
if (pos.x < bounds.min.x) {
|
||||
m_HrefInfos[h].boxes.Add(new Rect(bounds.min, bounds.size));
|
||||
bounds = new Bounds(pos, Vector3.zero);
|
||||
}
|
||||
else {
|
||||
bounds.Encapsulate(pos); // Extended enclosed box
|
||||
}
|
||||
}
|
||||
|
||||
m_HrefInfos[h].boxes.Add(new Rect(bounds.min, bounds.size));
|
||||
}
|
||||
|
||||
// Update the quad images
|
||||
updateQuad = true;
|
||||
|
||||
m_DisableFontTextureRebuiltCallback = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Click event is detected whether to click a hyperlink text
|
||||
/// </summary>
|
||||
/// <param name="eventData"></param>
|
||||
public void OnPointerClick(PointerEventData eventData) {
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
rectTransform, eventData.position, eventData.pressEventCamera, out lp);
|
||||
|
||||
for (int h = 0; h < m_HrefInfos.Count; h++) {
|
||||
for (int i = 0; i < m_HrefInfos[h].boxes.Count; ++i) {
|
||||
if (m_HrefInfos[h].boxes[i].Contains(lp)) {
|
||||
m_OnHrefClick.Invoke(m_HrefInfos[h].name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData) {
|
||||
//do your stuff when highlighted
|
||||
selected = true;
|
||||
|
||||
if (m_ImagesPool.Count >= 1) {
|
||||
for (int i = 0; i < m_ImagesPool.Count; i++) {
|
||||
if (button != null && button.isActiveAndEnabled) {
|
||||
m_ImagesPool[i].color = button.colors.highlightedColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData) {
|
||||
//do your stuff when highlighted
|
||||
selected = false;
|
||||
|
||||
if (m_ImagesPool.Count >= 1) {
|
||||
for (int i = 0; i < m_ImagesPool.Count; i++) {
|
||||
if (button != null && button.isActiveAndEnabled) {
|
||||
m_ImagesPool[i].color = button.colors.normalColor;
|
||||
}
|
||||
else {
|
||||
m_ImagesPool[i].color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSelect(BaseEventData eventData) {
|
||||
//do your stuff when selected
|
||||
selected = true;
|
||||
|
||||
if (m_ImagesPool.Count >= 1) {
|
||||
for (int i = 0; i < m_ImagesPool.Count; i++) {
|
||||
if (button != null && button.isActiveAndEnabled) {
|
||||
m_ImagesPool[i].color = button.colors.highlightedColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDeselect(BaseEventData eventData) {
|
||||
//do your stuff when selected
|
||||
selected = false;
|
||||
|
||||
if (m_ImagesPool.Count >= 1) {
|
||||
for (int i = 0; i < m_ImagesPool.Count; i++) {
|
||||
if (button != null && button.isActiveAndEnabled) {
|
||||
m_ImagesPool[i].color = button.colors.normalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void SetVerticesDirty() {
|
||||
base.SetVerticesDirty();
|
||||
|
||||
// Update the quad images
|
||||
updateQuad = true;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate() {
|
||||
base.OnValidate();
|
||||
|
||||
// Update the quad images
|
||||
updateQuad = true;
|
||||
|
||||
if (inspectorIconList != null) {
|
||||
for (int i = 0; i < inspectorIconList.Length; i++) {
|
||||
if (inspectorIconList[i].scale == Vector2.zero) {
|
||||
inspectorIconList[i].scale = Vector2.one;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override void OnEnable() {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
// Here is the hack to see if Unity is using the new rendering system for text
|
||||
usesNewRendering = false;
|
||||
|
||||
if (Application.unityVersion.StartsWith("2019.1.")) {
|
||||
if (!Char.IsDigit(Application.unityVersion[8])) {
|
||||
int number = Convert.ToInt32(Application.unityVersion[7].ToString());
|
||||
|
||||
if (number > 4) {
|
||||
usesNewRendering = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
usesNewRendering = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
usesNewRendering = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
base.OnEnable();
|
||||
|
||||
supportRichText = true;
|
||||
alignByGeometry = true;
|
||||
|
||||
// Enable images on TextPic disable
|
||||
if (m_ImagesPool.Count >= 1) {
|
||||
for (int i = 0; i < m_ImagesPool.Count; i++) {
|
||||
if(m_ImagesPool[i] != null) {
|
||||
m_ImagesPool[i].enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the quads on re-enable
|
||||
updateQuad = true;
|
||||
|
||||
this.onHrefClick.AddListener(OnHrefClick);
|
||||
}
|
||||
|
||||
protected override void OnDisable() {
|
||||
base.OnDisable();
|
||||
|
||||
// Disable images on TextPic disable
|
||||
if (m_ImagesPool.Count >= 1) {
|
||||
for (int i = 0; i < m_ImagesPool.Count; i++) {
|
||||
if (m_ImagesPool[i] != null) {
|
||||
m_ImagesPool[i].enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.onHrefClick.RemoveListener(OnHrefClick);
|
||||
}
|
||||
|
||||
new void Start() {
|
||||
button = GetComponent<Button>();
|
||||
ResetIconList();
|
||||
}
|
||||
|
||||
void LateUpdate() {
|
||||
// Reset the hrefs if text is changed
|
||||
if (previousText != text) {
|
||||
Reset_m_HrefInfos();
|
||||
|
||||
// Update the quad on text change
|
||||
updateQuad = true;
|
||||
}
|
||||
|
||||
// Need to lock to remove images properly
|
||||
lock (thisLock) {
|
||||
// Can only update the images when it is not in a rebuild, this prevents the error
|
||||
if (updateQuad) {
|
||||
UpdateQuadImage();
|
||||
updateQuad = false;
|
||||
}
|
||||
|
||||
// Destroy any images that are not in use
|
||||
if (clearImages) {
|
||||
for (int i = 0; i < culled_ImagesPool.Count; i++) {
|
||||
DestroyImmediate(culled_ImagesPool[i]);
|
||||
}
|
||||
|
||||
culled_ImagesPool.Clear();
|
||||
|
||||
clearImages = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b41c7c4761612e4419bd8a5d44c35a6c
|
||||
timeCreated: 1468515487
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user