diff --git a/Scripts/Utilities/ExtensionsToggle.cs b/Scripts/Utilities/ExtensionsToggle.cs
new file mode 100644
index 0000000..0097080
--- /dev/null
+++ b/Scripts/Utilities/ExtensionsToggle.cs
@@ -0,0 +1,241 @@
+using System;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.UI
+{
+ ///
+ /// Simple toggle -- something that has an 'on' and 'off' states: checkbox, toggle button, radio button, etc.
+ ///
+ [AddComponentMenu("UI/Extensions/Extensions Toggle", 31)]
+ [RequireComponent(typeof(RectTransform))]
+ public class ExtensionsToggle : Selectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
+ {
+ public enum ToggleTransition
+ {
+ None,
+ Fade
+ }
+
+ [Serializable]
+ public class ToggleEvent : UnityEvent
+ { }
+
+ ///
+ /// Transition type.
+ ///
+ public ToggleTransition toggleTransition = ToggleTransition.Fade;
+
+ ///
+ /// Graphic the toggle should be working with.
+ ///
+ public Graphic graphic;
+
+ // group that this toggle can belong to
+ [SerializeField]
+ private ExtensionsToggleGroup m_Group;
+
+ public ExtensionsToggleGroup group
+ {
+ get { return m_Group; }
+ set
+ {
+ m_Group = value;
+#if UNITY_EDITOR
+ if (Application.isPlaying)
+#endif
+ {
+ SetToggleGroup(m_Group, true);
+ PlayEffect(true);
+ }
+ }
+ }
+
+ ///
+ /// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
+ ///
+ public ToggleEvent onValueChanged = new ToggleEvent();
+
+ // Whether the toggle is on
+ [FormerlySerializedAs("m_IsActive")]
+ [Tooltip("Is the toggle currently on or off?")]
+ [SerializeField]
+ private bool m_IsOn;
+
+ protected ExtensionsToggle()
+ { }
+
+#if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+ Set(m_IsOn, false);
+ PlayEffect(toggleTransition == ToggleTransition.None);
+
+ var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
+ if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
+ CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
+ }
+
+#endif // if UNITY_EDITOR
+
+ public virtual void Rebuild(CanvasUpdate executing)
+ {
+#if UNITY_EDITOR
+ if (executing == CanvasUpdate.Prelayout)
+ onValueChanged.Invoke(m_IsOn);
+#endif
+ }
+
+ public virtual void LayoutComplete()
+ { }
+
+ public virtual void GraphicUpdateComplete()
+ { }
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ SetToggleGroup(m_Group, false);
+ PlayEffect(true);
+ }
+
+ protected override void OnDisable()
+ {
+ SetToggleGroup(null, false);
+ base.OnDisable();
+ }
+
+ protected override void OnDidApplyAnimationProperties()
+ {
+ // Check if isOn has been changed by the animation.
+ // Unfortunately there is no way to check if we don�t have a graphic.
+ if (graphic != null)
+ {
+ bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0);
+ if (m_IsOn != oldValue)
+ {
+ m_IsOn = oldValue;
+ Set(!oldValue);
+ }
+ }
+
+ base.OnDidApplyAnimationProperties();
+ }
+
+ private void SetToggleGroup(ExtensionsToggleGroup newGroup, bool setMemberValue)
+ {
+ ExtensionsToggleGroup oldGroup = m_Group;
+
+ // Sometimes IsActive returns false in OnDisable so don't check for it.
+ // Rather remove the toggle too often than too little.
+ if (m_Group != null)
+ m_Group.UnregisterToggle(this);
+
+ // At runtime the group variable should be set but not when calling this method from OnEnable or OnDisable.
+ // That's why we use the setMemberValue parameter.
+ if (setMemberValue)
+ m_Group = newGroup;
+
+ // Only register to the new group if this Toggle is active.
+ if (m_Group != null && IsActive())
+ m_Group.RegisterToggle(this);
+
+ // If we are in a new group, and this toggle is on, notify group.
+ // Note: Don't refer to m_Group here as it's not guaranteed to have been set.
+ if (newGroup != null && newGroup != oldGroup && isOn && IsActive())
+ m_Group.NotifyToggleOn(this);
+ }
+
+ ///
+ /// Whether the toggle is currently active.
+ ///
+ public bool isOn
+ {
+ get { return m_IsOn; }
+ set
+ {
+ Set(value);
+ }
+ }
+
+ void Set(bool value)
+ {
+ Set(value, true);
+ }
+
+ void Set(bool value, bool sendCallback)
+ {
+ if (m_IsOn == value)
+ return;
+
+ // if we are in a group and set to true, do group logic
+ m_IsOn = value;
+ if (m_Group != null && IsActive())
+ {
+ if (m_IsOn || (!m_Group.AnyTogglesOn() && !m_Group.allowSwitchOff))
+ {
+ m_IsOn = true;
+ m_Group.NotifyToggleOn(this);
+ }
+ }
+
+ // Always send event when toggle is clicked, even if value didn't change
+ // due to already active toggle in a toggle group being clicked.
+ // Controls like Dropdown rely on this.
+ // It's up to the user to ignore a selection being set to the same value it already was, if desired.
+ PlayEffect(toggleTransition == ToggleTransition.None);
+ if (sendCallback)
+ onValueChanged.Invoke(m_IsOn);
+ }
+
+ ///
+ /// Play the appropriate effect.
+ ///
+ private void PlayEffect(bool instant)
+ {
+ if (graphic == null)
+ return;
+
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ graphic.canvasRenderer.SetAlpha(m_IsOn ? 1f : 0f);
+ else
+#endif
+ graphic.CrossFadeAlpha(m_IsOn ? 1f : 0f, instant ? 0f : 0.1f, true);
+ }
+
+ ///
+ /// Assume the correct visual state.
+ ///
+ protected override void Start()
+ {
+ PlayEffect(true);
+ }
+
+ private void InternalToggle()
+ {
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ isOn = !isOn;
+ }
+
+ ///
+ /// React to clicks.
+ ///
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ InternalToggle();
+ }
+
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ InternalToggle();
+ }
+ }
+}
diff --git a/Scripts/Utilities/ExtensionsToggle.cs.meta b/Scripts/Utilities/ExtensionsToggle.cs.meta
new file mode 100644
index 0000000..74bc572
--- /dev/null
+++ b/Scripts/Utilities/ExtensionsToggle.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: bc733ccacdce62148ac1f46d1dd84ff6
+timeCreated: 1450456340
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Utilities/ExtensionsToggleGroup.cs b/Scripts/Utilities/ExtensionsToggleGroup.cs
new file mode 100644
index 0000000..bf1c67c
--- /dev/null
+++ b/Scripts/Utilities/ExtensionsToggleGroup.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using UnityEngine.EventSystems;
+using UnityEngine.Events;
+
+namespace UnityEngine.UI
+{
+ [AddComponentMenu("UI/Extensions/Extensions Toggle Group")]
+ [DisallowMultipleComponent]
+ public class ExtensionsToggleGroup : UIBehaviour
+ {
+ [SerializeField]
+ private bool m_AllowSwitchOff = false;
+ public bool allowSwitchOff { get { return m_AllowSwitchOff; } set { m_AllowSwitchOff = value; } }
+
+ private List m_Toggles = new List();
+
+ [Serializable]
+ public class ToggleGroupEvent : UnityEvent
+ { }
+
+ public ToggleGroupEvent onToggleGroupChanged = new ToggleGroupEvent();
+ public ToggleGroupEvent onToggleGroupToggleChanged = new ToggleGroupEvent();
+
+ public ExtensionsToggle SelectedToggle { get; private set; }
+
+
+ protected ExtensionsToggleGroup()
+ { }
+
+ private void ValidateToggleIsInGroup(ExtensionsToggle toggle)
+ {
+ if (toggle == null || !m_Toggles.Contains(toggle))
+ throw new ArgumentException(string.Format("Toggle {0} is not part of ToggleGroup {1}", new object[] { toggle, this }));
+ }
+
+ public void NotifyToggleOn(ExtensionsToggle toggle)
+ {
+ ValidateToggleIsInGroup(toggle);
+
+ // disable all toggles in the group
+ for (var i = 0; i < m_Toggles.Count; i++)
+ {
+ if (m_Toggles[i] == toggle)
+ {
+ SelectedToggle = toggle;
+ continue;
+ }
+
+ m_Toggles[i].isOn = false;
+ }
+ onToggleGroupChanged.Invoke(AnyTogglesOn());
+ }
+
+ public void UnregisterToggle(ExtensionsToggle toggle)
+ {
+ if (m_Toggles.Contains(toggle))
+ {
+ m_Toggles.Remove(toggle);
+ toggle.onValueChanged.RemoveListener(NotifyToggleChanged);
+ }
+ }
+
+ private void NotifyToggleChanged(bool isOn)
+ {
+ onToggleGroupToggleChanged.Invoke(isOn);
+ }
+
+ public void RegisterToggle(ExtensionsToggle toggle)
+ {
+ if (!m_Toggles.Contains(toggle))
+ {
+ m_Toggles.Add(toggle);
+ toggle.onValueChanged.AddListener(NotifyToggleChanged);
+ }
+ }
+
+ public bool AnyTogglesOn()
+ {
+ return m_Toggles.Find(x => x.isOn) != null;
+ }
+
+ public IEnumerable ActiveToggles()
+ {
+ return m_Toggles.Where(x => x.isOn);
+ }
+
+ public void SetAllTogglesOff()
+ {
+ bool oldAllowSwitchOff = m_AllowSwitchOff;
+ m_AllowSwitchOff = true;
+
+ for (var i = 0; i < m_Toggles.Count; i++)
+ m_Toggles[i].isOn = false;
+
+ m_AllowSwitchOff = oldAllowSwitchOff;
+ }
+
+ public void HasTheGroupToggle(bool value)
+ {
+ Debug.Log("Testing, the group has toggled [" + value + "]");
+ }
+
+ public void HasAToggleFlipped(bool value)
+ {
+ Debug.Log("Testing, a toggle has toggled [" + value + "]");
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Utilities/ExtensionsToggleGroup.cs.meta b/Scripts/Utilities/ExtensionsToggleGroup.cs.meta
new file mode 100644
index 0000000..7c471d1
--- /dev/null
+++ b/Scripts/Utilities/ExtensionsToggleGroup.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: bf31acdd4bba9fc45b8a5cc6de985aee
+timeCreated: 1450454546
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: