Editor stuff

This commit is contained in:
Starpelly
2022-01-05 19:11:33 -05:00
parent 775fd7e580
commit 576b0d8482
458 changed files with 37611 additions and 15 deletions

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 82ea50ac9a6ea8940a71f86ea8d13bf0
folderAsset: yes
DefaultImporter:
userData:

View File

@ -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
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4387cc9950f37044c92f9d144a2b1002
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3c9d341c4bc7de548937508e6f837144
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: b9115417252d4cd42a5e167bdc1c2d3b
folderAsset: yes
DefaultImporter:
userData:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e28e1e9e18f7daa4db5d1ac279d6ce66
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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();
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9edf9da4c14ad2843879a2331b00f738
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d00ab96853b24074cb837ee70f07dddc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a3fd655d1aa9c684d88cdfdd0da9aa34
timeCreated: 1480011174
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7ab63fe1b000c254ebed3937038bb01b
folderAsset: yes
timeCreated: 1480011199
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4210a64d2ce72e9488cf2ad174e9df5b
timeCreated: 1480011173
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b32edf53f1ae84f479c9439b4b5f5b91
timeCreated: 1480011174
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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("");
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7e448480810b55843aefa91c1ab74cd2
timeCreated: 1483197306
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 502205da447ca7a479ce5ae45e5c19d2
timeCreated: 1480011173
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
});
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 06851a815227e5044b0e3c1bf9b3a282
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -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:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 91558da930270ac4a960ba03b81c836a
timeCreated: 1480011173
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cd3cce5a27f2db94fa394c4719bddecd
timeCreated: 1480011174
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0f473590aed5e3f4ab5a0157b2a53dbd
timeCreated: 1480011173
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cbbc689694061e6439664eda55449513
folderAsset: yes
timeCreated: 1480011173
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
using System;
using UnityEngine;
using UnityEngine.Events;
[Serializable]
public class ColorChangedEvent : UnityEvent<Color>
{
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ff46fbecea7739f4690e4285c88f53c5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -0,0 +1,6 @@
using UnityEngine.Events;
public class HSVChangedEvent : UnityEvent<float, float, float>
{
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3d95ce8fba3dbbf4eb14411412169b88
timeCreated: 1442747317
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4f3189246d7fc204faba7a1e9c08e0af
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7343402602909bd4f928d58433c5c87f
timeCreated: 1480011173
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7a0647785ed421e449239dbd6512e156
timeCreated: 1480011173
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c7be5109ea5b91e4b856621023b15168
timeCreated: 1480011174
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 726a11b8d64fa0143b34f417f5453f80
folderAsset: yes
DefaultImporter:
userData:

View File

@ -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);
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ef22b091ebae52c47aa3e86ad9040c05
timeCreated: 1492278993
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cd26acd32e1be2747b9e5f3587b2b1d5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7a00cad80d8a47b438b394bebe77d0d2
timeCreated: 1492278993
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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>();
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 491c635e1f309294187ff24b5c71e8cb
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1dd512d0ed874740902b01df530b2dc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 74a62d95b3be0fe47871dd48fca58b7d
timeCreated: 1498387990
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a18c7c81729dadf40aee407a0cff0462
timeCreated: 1440843410
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9f2bebd34aaa76541b97e61752a7d262
timeCreated: 1492273203
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 803cebee00d5c504e930205383017dc1
timeCreated: 1432062988
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4735d2b59d5c699488c30a34a6a76e72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 281614f4c0e3b7a4d9056bd377134172
folderAsset: yes
timeCreated: 1446117980
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6b333d67eb08d464d823874f6a1666c2
timeCreated: 1492560112
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 252dd148b2c1dbe40b7d938a553e3caf
timeCreated: 1446062045
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 86c224aef3e999140b78d1d7135ba33f
timeCreated: 1446072313
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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>();
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 916e98f1b982a9a4082fcc45c87b66c5
timeCreated: 1492560112
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c4c2c5317e2b50f42b0302414577a62d
folderAsset: yes
timeCreated: 1440843775
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c210a6e68020b4349a4020e7da57826e
timeCreated: 1440843816
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ff46b67332ade77459ea86ba20638d24
timeCreated: 1440843795
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: db83442065d59344c9bf6ffa63a23777
timeCreated: 1440843837
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b8ea9937637d64c6da52723c68267703
timeCreated: 1503449008
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 439b3dcc2b7714c1e91776a9d161f6ee
timeCreated: 1498517608
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 1e656d866043cab4fb676dba90e3734c
folderAsset: yes
DefaultImporter:
userData:

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d0644987412a04edd8105e64bdd008b2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 72b70b31ccbda4bae8dd5cda36d2e02d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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 ();
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0f143353fa45b4df79a3722ad82431d5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b1867f0d5b751404f86b0e562a3d783b
timeCreated: 1501004355
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: dd241e0d4a8de4fc9924c1dce285c587
timeCreated: 1503449912
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}
}
}

View File

@ -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