using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute;
namespace Lean.Common
{
/// This is the base class for all object selectors.
[HelpURL(LeanHelper.HelpUrlPrefix + "LeanSelect")]
public class LeanSelect : MonoBehaviour
{
[System.Serializable] public class LeanSelectableEvent : UnityEvent {}
public enum ReselectType
{
KeepSelected,
Deselect,
DeselectAndSelect,
SelectAgain
}
public enum LimitType
{
Unlimited,
StopAtMax,
DeselectFirst
}
public static LinkedList Instances = new LinkedList(); [System.NonSerialized] private LinkedListNode instancesNode;
/// If you attempt to select a point that has no objects underneath, should all currently selected objects be deselected?
public bool DeselectWithNothing { set { deselectWithNothing = value; } get { return deselectWithNothing; } } [FSA("AutoDeselect")] [SerializeField] private bool deselectWithNothing;
/// If you have selected the maximum number of objects, what should happen?
/// Unlimited = Always allow selection.
/// StopAtMax = Allow selection up to the MaxSelectables count, then do nothing.
/// DeselectFirst = Always allow selection, but deselect the first object when the MaxSelectables count is reached.
public LimitType Limit { set { limit = value; } get { return limit; } } [SerializeField] private LimitType limit;
/// The maximum number of selectables that can be selected at the same time.
/// 0 = Unlimited.
public int MaxSelectables { set { maxSelectables = value; } get { return maxSelectables; } } [FSA("MaxSelectables")] [SerializeField] private int maxSelectables = 5;
/// If you select an already selected selectable, what should happen?
public ReselectType Reselect { set { reselect = value; } get { return reselect; } } [FSA("Reselect")] [SerializeField] private ReselectType reselect = ReselectType.SelectAgain;
/// This stores all objects selected by this component.
public List Selectables { get { if (selectables == null) selectables = new List(); return selectables; } } [SerializeField] protected List selectables;
/// This is invoked when an object is selected.
public LeanSelectableEvent OnSelected { get { if (onSelected == null) onSelected = new LeanSelectableEvent(); return onSelected; } } [SerializeField] private LeanSelectableEvent onSelected;
/// This is invoked when an object is deselected.
public LeanSelectableEvent OnDeselected { get { if (onDeselected == null) onDeselected = new LeanSelectableEvent(); return onDeselected; } } [SerializeField] private LeanSelectableEvent onDeselected;
/// This is invoked when you try to select, but nothing is found.
public UnityEvent OnNothing { get { if (onNothing == null) onNothing = new UnityEvent(); return onNothing; } } [SerializeField] private UnityEvent onNothing;
public static event System.Action OnAnySelected;
public static event System.Action OnAnyDeselected;
public bool IsSelected(LeanSelectable selectable)
{
return selectables != null && selectables.Contains(selectable);
}
/// This will select the specified object and add it to this component's Selectables list, if it isn't already there.
public void Select(LeanSelectable selectable)
{
TrySelect(selectable);
}
/// This remove the specified object from this component's Selectables list if present, and deselect it.
public void Deselect(LeanSelectable selectable)
{
if (selectable != null && selectables != null)
{
TryDeselect(selectable);
}
}
protected bool TrySelect(LeanSelectable selectable)
{
if (selectable != null && selectable.isActiveAndEnabled == true)
{
if (TryReselect(selectable) == true)
{
if (Selectables.Contains(selectable) == false) // NOTE: Property
{
switch (limit)
{
case LimitType.Unlimited:
{
}
break;
case LimitType.StopAtMax:
{
if (selectables.Count >= maxSelectables)
{
return false;
}
}
break;
case LimitType.DeselectFirst:
{
if (selectables.Count > 0 && selectables.Count >= maxSelectables)
{
TryDeselect(selectables[0]);
}
}
break;
}
}
selectables.Add(selectable);
if (onSelected != null) onSelected.Invoke(selectable);
if (OnAnySelected != null) OnAnySelected.Invoke(this, selectable);
selectable.InvokeOnSelected(this);
return true;
}
}
// Nothing was selected?
else
{
if (onNothing != null) onNothing.Invoke();
if (deselectWithNothing == true)
{
DeselectAll();
}
}
return false;
}
private bool TryReselect(LeanSelectable selectable)
{
switch (reselect)
{
case ReselectType.KeepSelected:
{
if (Selectables.Contains(selectable) == false) // NOTE: Property
{
return true;
}
}
break;
case ReselectType.Deselect:
{
if (Selectables.Contains(selectable) == false) // NOTE: Property
{
return true;
}
else
{
Deselect(selectable);
}
}
break;
case ReselectType.DeselectAndSelect:
{
if (Selectables.Contains(selectable) == true) // NOTE: Property
{
Deselect(selectable);
}
}
return true;
case ReselectType.SelectAgain:
{
}
return true;
}
return false;
}
protected bool TryDeselect(LeanSelectable selectable)
{
if (selectables != null)
{
var index = selectables.IndexOf(selectable);
if (index >= 0)
{
return TryDeselect(index);
}
}
return false;
}
protected bool TryDeselect(int index)
{
var success = false;
if (selectables != null && index >= 0 && index < selectables.Count)
{
var selectable = selectables[index];
selectables.RemoveAt(index);
if (selectable != null)
{
selectable.InvokeOnDeslected(this);
if (onDeselected != null)
{
onDeselected.Invoke(selectable);
}
if (OnAnyDeselected != null)
{
OnAnyDeselected.Invoke(this, selectable);
}
}
success = true;
}
return success;
}
/// This will deselect all objects that were selected by this component.
[ContextMenu("Deselect All")]
public void DeselectAll()
{
if (selectables != null)
{
while (selectables.Count > 0)
{
var index = selectables.Count - 1;
var selectable = selectables[index];
selectables.RemoveAt(index);
selectable.InvokeOnDeslected(this);
}
}
}
/// This will deselect objects in chronological order until the selected object count reaches the specified amount.
public void Cull(int maxCount)
{
if (selectables != null)
{
while (selectables.Count > 0 && selectables.Count > maxCount)
{
var selectable = selectables[0];
selectables.RemoveAt(0);
if (selectable != null)
{
if (selectable != null)
{
Deselect(selectable);
}
}
}
}
}
protected virtual void OnEnable()
{
instancesNode = Instances.AddLast(this);
}
protected virtual void OnDisable()
{
Instances.Remove(instancesNode); instancesNode = null;
}
protected virtual void OnDestroy()
{
DeselectAll();
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using TARGET = LeanSelect;
[UnityEditor.CanEditMultipleObjects]
[UnityEditor.CustomEditor(typeof(TARGET))]
public class LeanSelect_Editor : LeanEditor
{
[System.NonSerialized] TARGET tgt; [System.NonSerialized] TARGET[] tgts;
protected override void OnInspector()
{
GetTargets(out tgt, out tgts);
Draw("deselectWithNothing", "If you attempt to select a point that has no objects underneath, should all currently selected objects be deselected?");
Draw("limit", "If you have selected the maximum number of objects, what should happen?\n\nUnlimited = Always allow selection.\n\nStopAtMax = Allow selection up to the MaxSelectables count, then do nothing.\n\nDeselectFirst = Always allow selection, but deselect the first object when the MaxSelectables count is reached.");
if (Any(tgts, t => t.Limit != LeanSelect.LimitType.Unlimited))
{
BeginIndent();
Draw("maxSelectables", "The maximum number of selectables that can be selected at the same time.\n\n0 = Unlimited.");
EndIndent();
}
Draw("reselect", "If you select an already selected selectable, what should happen?");
Separator();
var select = (LeanSelectable)UnityEditor.EditorGUILayout.ObjectField(new GUIContent("Select", "Drop a selectable object here to select it."), null, typeof(LeanSelectable), true);
var deselect = (LeanSelectable)UnityEditor.EditorGUILayout.ObjectField(new GUIContent("Deselect", "Drop a selectable object here to deselect it."), null, typeof(LeanSelectable), true);
BeginDisabled();
Draw("selectables", "This stores all objects selected by this component.");
EndDisabled();
Separator();
var showUnusedEvents = DrawFoldout("Show Unused Events", "Show all events?");
DrawEvents(showUnusedEvents);
if (select != null)
{
Each(tgts, t => t.Select(select), true);
}
if (deselect != null)
{
Each(tgts, t => t.Deselect(deselect), true);
}
}
protected virtual void DrawEvents(bool showUnusedEvents)
{
if (Any(tgts, t => t.OnSelected.GetPersistentEventCount() > 0) == true || showUnusedEvents == true)
{
Draw("onSelected");
}
if (Any(tgts, t => t.OnDeselected.GetPersistentEventCount() > 0) == true || showUnusedEvents == true)
{
Draw("onDeselected");
}
if (Any(tgts, t => t.OnNothing.GetPersistentEventCount() > 0) == true || showUnusedEvents == true)
{
Draw("onNothing");
}
}
}
}
#endif