Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions org.mixedrealitytoolkit.uxcore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Added

* Added `AlphaBlend` tint mode to `TintEffect`. [PR #1131](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1131)
* Allowed `ToggleCollection` to start with all toggles in an off state (`CurrentIndex = -1`) when `AllowSwitchOff` is true, and updated `CurrentIndex` to `-1` when an active toggle is switched off at runtime. [PR #1146](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1146)

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,124 @@ public IEnumerator TestAllowSwitchOff()
testInteractor.EndManualInteraction();

Assert.IsFalse(toggleCollection.Toggles[5].IsToggled, "Interactable didn't get detoggled! ToggleCollection should have allowed it.");
Assert.AreEqual(-1, toggleCollection.CurrentIndex, "ToggleCollection should update CurrentIndex to -1 when active toggle is detoggled!");
}

/// <summary>
/// Ensures ToggleCollection can start with all toggles switched off when AllowSwitchOff is true.
/// </summary>
[UnityTest]
public IEnumerator TestStartSwitchedOff()
{
var parent = new GameObject();

for (int i = 0; i < 5; i++)
{
var toggleObject = new GameObject();
toggleObject.transform.parent = parent.transform;
var interactable = toggleObject.AddComponent<StatefulInteractable>();
interactable.ToggleMode = StatefulInteractable.ToggleType.Toggle;
}

var toggleCollection = parent.AddComponent<ToggleCollection>();
toggleCollection.AllowSwitchOff = true;
toggleCollection.CurrentIndex = -1;
yield return null;

Assert.AreEqual(5, toggleCollection.Toggles.Count, "ToggleCollection didn't detect all of the toggles!");
Assert.AreEqual(-1, toggleCollection.CurrentIndex, "ToggleCollection should remain at CurrentIndex = -1 when starting switched off!");
foreach (var toggle in toggleCollection.Toggles)
{
Assert.IsFalse(toggle.IsToggled, "No toggle should be toggled when starting switched off!");
}
}

/// <summary>
/// Ensures setting CurrentIndex to -1 deselects all toggles when AllowSwitchOff is true,
/// but is rejected when AllowSwitchOff is false.
/// </summary>
[UnityTest]
public IEnumerator TestSetSelectionMinusOne()
{
var parent = new GameObject();

for (int i = 0; i < 5; i++)
{
var toggleObject = new GameObject();
toggleObject.transform.parent = parent.transform;
var interactable = toggleObject.AddComponent<StatefulInteractable>();
interactable.ToggleMode = StatefulInteractable.ToggleType.Toggle;
}

var toggleCollection = parent.AddComponent<ToggleCollection>();
toggleCollection.AllowSwitchOff = true;
yield return null;

// Select toggle 2
toggleCollection.CurrentIndex = 2;
Assert.AreEqual(2, toggleCollection.CurrentIndex);
Assert.IsTrue(toggleCollection.Toggles[2].IsToggled);

// Set selection to -1 to deselect all
toggleCollection.CurrentIndex = -1;
Assert.AreEqual(-1, toggleCollection.CurrentIndex, "CurrentIndex should be -1 after setting selection to -1 with AllowSwitchOff = true!");
foreach (var toggle in toggleCollection.Toggles)
{
Assert.IsFalse(toggle.IsToggled, "All toggles should be untoggled after setting CurrentIndex to -1!");
}

// When AllowSwitchOff is false, setting CurrentIndex to -1 should be rejected
toggleCollection.AllowSwitchOff = false;
toggleCollection.CurrentIndex = 3;
Assert.AreEqual(3, toggleCollection.CurrentIndex);

LogAssert.Expect(LogType.Warning, "Index out of range of ToggleCollection: -1");
toggleCollection.CurrentIndex = -1;
Assert.AreEqual(3, toggleCollection.CurrentIndex, "CurrentIndex should remain unchanged when AllowSwitchOff is false!");
}

/// <summary>
/// Ensures OnToggleSelected fires with -1 when an active toggle is detoggled.
/// </summary>
[UnityTest]
public IEnumerator TestSwitchOffEvent()
{
var parent = new GameObject();

for (int i = 0; i < 5; i++)
{
var toggleObject = new GameObject();
toggleObject.transform.parent = parent.transform;
var interactable = toggleObject.AddComponent<StatefulInteractable>();
interactable.ToggleMode = StatefulInteractable.ToggleType.Toggle;
}

var toggleCollection = parent.AddComponent<ToggleCollection>();
toggleCollection.AllowSwitchOff = true;
yield return null;

int lastSelected = -999;
toggleCollection.OnToggleSelected.AddListener((index) => lastSelected = index);

var testInteractor = parent.AddComponent<TestInteractor>();

// Toggle on toggle 2
testInteractor.StartManualInteraction(toggleCollection.Toggles[2] as IXRSelectInteractable);
yield return null;
yield return null;
testInteractor.EndManualInteraction();

Assert.AreEqual(2, lastSelected, "OnToggleSelected should have fired with index 2!");
Assert.AreEqual(2, toggleCollection.CurrentIndex);

// Toggle off toggle 2
testInteractor.StartManualInteraction(toggleCollection.Toggles[2] as IXRSelectInteractable);
yield return null;
yield return null;
testInteractor.EndManualInteraction();

Assert.AreEqual(-1, lastSelected, "OnToggleSelected should have fired with index -1 when detoggled!");
Assert.AreEqual(-1, toggleCollection.CurrentIndex);
}
}
}
Expand Down
26 changes: 21 additions & 5 deletions org.mixedrealitytoolkit.uxcore/Toggle/ToggleCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public List<StatefulInteractable> Toggles
// Add listeners to new list
AddSelectionListeners();

int index = Mathf.Clamp(CurrentIndex, 0, toggles.Count - 1);
int index = Mathf.Clamp(CurrentIndex, allowSwitchOff ? -1 : 0, toggles.Count - 1);
SetSelection(index);
}
}
Expand Down Expand Up @@ -71,7 +71,7 @@ public bool AllowSwitchOff
}

[SerializeField]
[Tooltip("Currently selected index in the ToggleCollection, default is 0")]
[Tooltip("Currently selected index in the ToggleCollection, default is 0. Set to -1 to start with no toggle selected (requires AllowSwitchOff).")]
private int currentIndex;

/// <summary>
Expand All @@ -83,12 +83,12 @@ public int CurrentIndex
set => SetSelection(value);
}

[Tooltip("This event is triggered when any of the toggles in the ToggleCollection are selected. The event data is the index of the toggle button with in the ToggleCollection.")]
[Tooltip("This event is triggered when any of the toggles in the ToggleCollection are selected, or when all toggles are deselected (-1). The event data is the index of the toggle button within the ToggleCollection.")]
[SerializeField]
private ToggleSelectedEvent onToggleSelected = new ToggleSelectedEvent();

/// <summary>
/// This event is triggered when any of the toggles in the <see cref="ToggleCollection"/> are selected. The event data is the index of the toggle button within the <see cref="ToggleCollection"/>.
/// This event is triggered when any of the toggles in the <see cref="ToggleCollection"/> are selected, or when all toggles are deselected (-1). The event data is the index of the toggle button within the <see cref="ToggleCollection"/>.
/// </summary>
public ToggleSelectedEvent OnToggleSelected
{
Expand All @@ -97,6 +97,7 @@ public ToggleSelectedEvent OnToggleSelected

// List of the actions for the toggles in ToggleCollection.
private List<UnityAction<float>> toggleActions = new List<UnityAction<float>>();
private List<UnityAction<float>> toggleDeselectionActions = new List<UnityAction<float>>();

/// <summary>
/// A Unity Editor only event function that is called when the script is loaded or a value changes in the Unity Inspector.
Expand Down Expand Up @@ -143,6 +144,10 @@ private void Start()
SetSelection(CurrentIndex, true);
Toggles[CurrentIndex].ForceSetToggled(true);
}
else if (CurrentIndex == -1 && allowSwitchOff)
{
SetSelection(-1, true);
}
}

// Initialize the interactables with the proper allow-toggle-off setting.
Expand All @@ -156,7 +161,7 @@ private void Start()
/// </summary>
public void SetSelection(int index, bool force = false)
{
if (index < 0 || Toggles.Count <= index || Toggles == null || !isActiveAndEnabled)
if (index < (allowSwitchOff ? -1 : 0) || Toggles.Count <= index || Toggles == null || !isActiveAndEnabled)
{
Debug.LogWarning("Index out of range of ToggleCollection: " + index, this);
return;
Expand Down Expand Up @@ -202,10 +207,19 @@ private void AddSelectionListeners()

int itemIndex = i;
UnityAction<float> setSelectionAction = (_) => SetSelection(itemIndex);
UnityAction<float> setDeselectionAction = (_) =>
{
if (allowSwitchOff && CurrentIndex == itemIndex)
{
SetSelection(-1);
}
};

toggleActions.Add(setSelectionAction);
toggleDeselectionActions.Add(setDeselectionAction);

Toggles[i].IsToggled.OnEntered.AddListener(setSelectionAction);
Toggles[i].IsToggled.OnExited.AddListener(setDeselectionAction);
Toggles[i].ToggleMode = allowSwitchOff ? StatefulInteractable.ToggleType.Toggle : StatefulInteractable.ToggleType.OneWayToggle;
}
}
Expand All @@ -217,9 +231,11 @@ private void RemoveSelectionListeners()
if (Toggles[i] == null) { continue; }

Toggles[i].IsToggled.OnEntered.RemoveListener(toggleActions[i]);
Toggles[i].IsToggled.OnExited.RemoveListener(toggleDeselectionActions[i]);
}

toggleActions.Clear();
toggleDeselectionActions.Clear();
}

/// <summary>
Expand Down
Loading