// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using UltimateXR.Core.Components; using UnityEngine; namespace UltimateXR.UI.UnityInputModule.Controls { /// /// Component that, added to a GameObject, will make all child components behave /// like a single group where only one selection can be active at a time. /// public partial class UxrToggleGroup : UxrComponent { #region Inspector Properties/Serialized Fields [SerializeField] private InitState _initialToggleState = InitState.DontChange; #endregion #region Public Types & Data /// /// Event called whenever the currently selected toggle control was changed. /// public event Action SelectionChanged; /// /// Gets all the current toggle components. /// public IList Toggles => _toggles; /// /// Gets the currently selected toggle component. /// public UxrToggleControlInput CurrentSelection { get; private set; } #endregion #region Public Methods /// /// Refreshes the internal list of children toggle components. This can be used to refresh the list whenever a new /// child toggle component was added dynamically, or was removed. /// public void RefreshToggleChildrenList() { _toggles.ForEach(t => t.Toggled -= Toggle_Toggled); _toggles.Clear(); _toggles = new List(); for (int i = 0; i < transform.childCount; ++i) { UxrToggleControlInput toggle = transform.GetChild(i).GetComponent(); if (toggle != null && !toggle.IsBeingDestroyed && toggle.isActiveAndEnabled) { _toggles.Add(toggle); toggle.Toggled += Toggle_Toggled; } } CurrentSelection = _toggles.FirstOrDefault(t => t.IsSelected); _toggles.ForEach(t => t.CanBeToggled = t != CurrentSelection); } /// /// Clears the current selection if there is any. /// public void ClearCurrentSelection() { BeginSync(); if (CurrentSelection != null) { CurrentSelection.IsSelected = false; CurrentSelection.CanBeToggled = true; CurrentSelection = null; } EndSyncMethod(); } #endregion #region Unity /// /// Refreshes the toggle list and subscribes to toggle events. /// protected override void OnEnable() { base.OnEnable(); if (!_toggles.Any()) { RefreshToggleChildrenList(); } } /// /// Unsubscribes from toggle events. /// protected override void OnDisable() { base.OnDisable(); _toggles.ForEach(t => t.Toggled -= Toggle_Toggled); } /// /// Initializes the current selection if required. /// protected override void Start() { base.Start(); switch (_initialToggleState) { case InitState.DontChange: break; case InitState.FirstChild: if (_toggles.Any()) { _toggles.First().IsSelected = true; } break; } } #endregion #region Event Handling Methods /// /// Called whenever a toggle component was toggled. /// /// The toggle component that was toggled private void Toggle_Toggled(UxrToggleControlInput toggle) { NotifyToggle(_toggles.IndexOf(toggle), true); } #endregion #region Private Methods /// /// Notifies that a toggle component was toggled. /// /// The index of the toggle /// Whether the call comes from handling a toggle event private void NotifyToggle(int index, bool isFromEvent) { BeginSync(); UxrToggleControlInput toggle = index >= 0 && index < _toggles.Count ? _toggles[index] : null; foreach (UxrToggleControlInput t in _toggles) { t.CanBeToggled = t != toggle; if (t != null && t != toggle) { t.SetIsSelected(false, false); } } CurrentSelection = toggle; if (CurrentSelection != null && !isFromEvent) { // Since it doesn't come from an event, we need to update the selection manually. CurrentSelection.SetIsSelected(true, false); } SelectionChanged?.Invoke(toggle); EndSyncMethod(new object[] { index, false }); } #endregion #region Private Types & Data private List _toggles = new List(); #endregion } }