2017-07-26 02:54:07 +08:00
/// Credit David Gileadi
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/12
2017-07-24 12:48:38 +08:00
using System ;
2017-09-01 00:29:16 +08:00
using System.Collections ;
2017-07-24 12:48:38 +08:00
using UnityEngine.Events ;
using UnityEngine.EventSystems ;
namespace UnityEngine.UI.Extensions
{
// Segmented control, like a group of buttons
2017-09-01 00:29:16 +08:00
[AddComponentMenu("UI/Extensions/Segmented Control/Segmented Control")]
2017-07-24 12:48:38 +08:00
[RequireComponent(typeof(RectTransform))]
public class SegmentedControl : UIBehaviour
{
2017-07-27 05:32:48 +08:00
private Selectable [ ] m_segments ;
2017-07-26 02:54:07 +08:00
[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 ( ) ;
2017-09-01 00:29:16 +08:00
internal Selectable selectedSegment ;
2017-07-26 02:54:07 +08:00
protected float SeparatorWidth
2017-07-24 12:48:38 +08:00
{
get
{
2017-07-26 02:54:07 +08:00
if ( m_separatorWidth = = 0 & & separator )
2017-07-24 12:48:38 +08:00
{
2017-07-26 02:54:07 +08:00
m_separatorWidth = separator . rectTransform . rect . width ;
var image = separator . GetComponent < Image > ( ) ;
if ( image )
m_separatorWidth / = image . pixelsPerUnit ;
2017-07-24 12:48:38 +08:00
}
2017-07-26 02:54:07 +08:00
return m_separatorWidth ;
2017-07-24 12:48:38 +08:00
}
}
2017-07-26 02:54:07 +08:00
[Serializable]
public class SegmentSelectedEvent : UnityEvent < int > { }
2017-07-24 12:48:38 +08:00
2017-07-27 05:32:48 +08:00
public Selectable [ ] segments
2017-07-24 12:48:38 +08:00
{
get
{
2017-07-26 02:54:07 +08:00
if ( m_segments = = null | | m_segments . Length = = 0 )
2017-07-24 12:48:38 +08:00
{
2017-07-26 02:54:07 +08:00
m_segments = GetChildSegments ( ) ;
2017-07-24 12:48:38 +08:00
}
2017-07-26 02:54:07 +08:00
return m_segments ;
2017-07-24 12:48:38 +08:00
}
}
2017-07-26 02:54:07 +08:00
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 ; } }
2017-07-24 12:48:38 +08:00
public int selectedSegmentIndex
{
get { return Array . IndexOf ( segments , selectedSegment ) ; }
set
{
value = Math . Max ( value , - 1 ) ;
value = Math . Min ( value , segments . Length - 1 ) ;
2017-08-20 12:57:27 +08:00
2017-07-26 02:54:07 +08:00
m_selectedSegmentIndex = value ;
2017-08-20 12:57:27 +08:00
if ( selectedSegment )
2017-07-24 12:48:38 +08:00
{
2017-08-20 12:57:27 +08:00
var segment = selectedSegment . GetComponent < Segment > ( ) ;
if ( segment )
2017-07-24 12:48:38 +08:00
{
2017-08-20 12:57:27 +08:00
segment . selected = false ;
2017-07-24 12:48:38 +08:00
}
2017-08-20 12:57:27 +08:00
selectedSegment = null ;
2017-07-24 12:48:38 +08:00
}
2017-08-20 12:57:27 +08:00
if ( value ! = - 1 )
2017-07-24 12:48:38 +08:00
{
2017-08-20 12:57:27 +08:00
selectedSegment = segments [ value ] ;
2017-08-13 07:56:22 +08:00
var segment = selectedSegment . GetComponent < Segment > ( ) ;
if ( segment )
{
segment . selected = true ;
}
2017-07-24 12:48:38 +08:00
}
}
}
public SegmentSelectedEvent onValueChanged
{
2017-07-26 02:54:07 +08:00
get { return m_onValueChanged ; }
set { m_onValueChanged = value ; }
2017-07-24 12:48:38 +08:00
}
protected SegmentedControl ( )
{ }
protected override void Start ( )
{
base . Start ( ) ;
2017-09-01 00:29:16 +08:00
if ( isActiveAndEnabled )
StartCoroutine ( DelayedInit ( ) ) ;
}
protected override void OnEnable ( )
{
StartCoroutine ( DelayedInit ( ) ) ;
}
IEnumerator DelayedInit ( )
{
yield return null ;
2017-07-24 12:48:38 +08:00
LayoutSegments ( ) ;
2017-07-26 02:54:07 +08:00
if ( m_selectedSegmentIndex ! = - 1 )
selectedSegmentIndex = m_selectedSegmentIndex ;
2017-07-24 12:48:38 +08:00
}
#if UNITY_EDITOR
protected override void OnValidate ( )
{
base . OnValidate ( ) ;
2017-09-01 00:29:16 +08:00
if ( isActiveAndEnabled )
StartCoroutine ( DelayedInit ( ) ) ;
2017-07-26 02:54:07 +08:00
if ( m_selectedSegmentIndex > transform . childCount )
{
selectedSegmentIndex = transform . childCount - 1 ;
}
2017-07-24 12:48:38 +08:00
}
# endif
2017-07-27 05:32:48 +08:00
private Selectable [ ] GetChildSegments ( )
2017-07-24 12:48:38 +08:00
{
2017-07-27 05:32:48 +08:00
var buttons = GetComponentsInChildren < Selectable > ( ) ;
2017-07-24 12:48:38 +08:00
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 > ( ) ;
2017-09-01 00:29:16 +08:00
if ( segment ! = null )
2017-07-24 12:48:38 +08:00
{
2017-09-01 00:29:16 +08:00
segment . index = i ;
segment . segmentedControl = this ;
2017-07-24 12:48:38 +08:00
}
}
return buttons ;
}
2017-07-24 13:42:17 +08:00
private void RecreateSprites ( )
{
for ( int i = 0 ; i < segments . Length ; i + + )
{
if ( segments [ i ] . image = = null )
continue ;
2017-08-13 07:19:19 +08:00
var sprite = CutSprite ( segments [ i ] . image . sprite , i = = 0 , i = = segments . Length - 1 ) ;
2017-08-13 07:56:22 +08:00
var segment = segments [ i ] . GetComponent < Segment > ( ) ;
if ( segment )
{
segment . cutSprite = sprite ;
}
2017-08-13 07:19:19 +08:00
segments [ i ] . image . overrideSprite = sprite ;
}
}
2017-07-24 13:42:17 +08:00
2017-08-13 07:19:19 +08:00
static internal Sprite CutSprite ( Sprite sprite , bool leftmost , bool rightmost )
{
if ( sprite . border . x = = 0 | | sprite . border . z = = 0 )
return sprite ;
2017-07-24 13:42:17 +08:00
2017-08-13 07:19:19 +08:00
var rect = sprite . rect ;
var border = sprite . border ;
2017-07-24 13:42:17 +08:00
2017-08-13 07:19:19 +08:00
if ( ! leftmost )
{
rect . xMin = border . x ;
border . x = 0 ;
}
if ( ! rightmost )
{
rect . xMax = border . z ;
border . z = 0 ;
2017-07-24 13:42:17 +08:00
}
2017-08-13 07:19:19 +08:00
return Sprite . Create ( sprite . texture , rect , sprite . pivot , sprite . pixelsPerUnit , 0 , SpriteMeshType . FullRect , border ) ;
2017-07-24 13:42:17 +08:00
}
2017-07-24 12:48:38 +08:00
public void LayoutSegments ( )
{
2017-07-24 13:42:17 +08:00
RecreateSprites ( ) ;
2017-07-24 12:48:38 +08:00
RectTransform transform = this . transform as RectTransform ;
2017-07-26 02:54:07 +08:00
float width = ( transform . rect . width / segments . Length ) - ( SeparatorWidth * ( segments . Length - 1 ) ) ;
2017-07-24 12:48:38 +08:00
for ( int i = 0 ; i < segments . Length ; i + + )
{
2017-07-26 02:54:07 +08:00
float insetX = ( ( width + SeparatorWidth ) * i ) ;
2017-07-24 12:48:38 +08:00
var rectTransform = segments [ i ] . GetComponent < RectTransform > ( ) ;
rectTransform . anchorMin = Vector2 . zero ;
rectTransform . anchorMax = Vector2 . zero ;
2017-07-24 13:42:17 +08:00
rectTransform . SetInsetAndSizeFromParentEdge ( RectTransform . Edge . Left , insetX , width ) ;
2017-07-24 12:48:38 +08:00
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 ;
2017-07-26 02:54:07 +08:00
sep . rectTransform . SetInsetAndSizeFromParentEdge ( RectTransform . Edge . Left , insetX - SeparatorWidth , SeparatorWidth ) ;
2017-07-24 12:48:38 +08:00
sep . rectTransform . SetInsetAndSizeFromParentEdge ( RectTransform . Edge . Top , 0 , transform . rect . height ) ;
}
// TODO: maybe adjust text position
}
}
}
}