Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ namespace MixedReality.Toolkit.UX.Runtime.Tests
public class SliderTests : BaseRuntimeInputTests
{
// UXComponents/Sliders/Prefabs/NonCanvasSliderBase.prefab
private const string defaultSliderPrefabGuid = "5cf5d5d5cc7fe184a93c388dbab66bb9";
private static readonly string defaultSliderPrefabPath = AssetDatabase.GUIDToAssetPath(defaultSliderPrefabGuid);
private const float sliderValueDelta = 0.02f;
private const string DefaultSliderPrefabGuid = "5cf5d5d5cc7fe184a93c388dbab66bb9";
private static readonly string DefaultSliderPrefabPath = AssetDatabase.GUIDToAssetPath(DefaultSliderPrefabGuid);
private const float SliderValueDelta = 0.02f;

public override IEnumerator Setup()
{
Expand Down Expand Up @@ -57,10 +57,10 @@ public IEnumerator TestAssembleInteractableAndNearManip()
// This should not throw exception
AssembleSlider(InputTestUtilities.InFrontOfUser(Vector3.forward), Vector3.zero, out GameObject sliderObject, out Slider slider, out _);

Assert.AreEqual(0.5f, slider.Value, sliderValueDelta, "Slider should have value 0.5 at start");
Assert.AreEqual(0.5f, slider.Value, SliderValueDelta, "Slider should have value 0.5 at start");
yield return DirectPinchAndMoveSlider(slider, 1.0f);
// Allow some leeway due to grab positions shifting on open
Assert.AreEqual(1.0f, slider.Value, sliderValueDelta, "Slider should have value 1.0 after being manipulated at start");
Assert.AreEqual(1.0f, slider.Value, SliderValueDelta, "Slider should have value 1.0 after being manipulated at start");

//// clean up
Object.Destroy(sliderObject);
Expand All @@ -75,10 +75,10 @@ public IEnumerator TestLoadPrefabAndNearManipNoSnap()
slider.SnapToPosition = false;
slider.IsTouchable = false;

Assert.AreEqual(0.5f, slider.Value, sliderValueDelta, "Slider should have value 0.5 at start");
Assert.AreEqual(0.5f, slider.Value, SliderValueDelta, "Slider should have value 0.5 at start");
yield return DirectPinchAndMoveSlider(slider, 1.0f);
// Allow some leeway due to grab positions shifting on open
Assert.AreEqual(1.0f, slider.Value, sliderValueDelta, "Slider should have value 1.0 after being manipulated at start");
Assert.AreEqual(1.0f, slider.Value, SliderValueDelta, "Slider should have value 1.0 after being manipulated at start");

// clean up
Object.Destroy(sliderObject);
Expand Down Expand Up @@ -435,7 +435,7 @@ public IEnumerator TestNullVisualsNoSnap()

// Test that the slider still works and no errors are thrown
yield return RuntimeTestUtilities.WaitForUpdates();
Assert.AreEqual(0.5f, slider.Value, sliderValueDelta, "Slider should have value 0.5 at start");
Assert.AreEqual(0.5f, slider.Value, SliderValueDelta, "Slider should have value 0.5 at start");

// clean up
Object.Destroy(sliderObject);
Expand Down Expand Up @@ -669,7 +669,7 @@ private void AssembleSlider(Vector3 position, Vector3 rotation, out GameObject s
private void InstantiateDefaultSliderPrefab(Vector3 position, Vector3 rotation, out GameObject sliderObject, out Slider slider, out SliderVisuals sliderVisuals)
{
// Load interactable prefab
Object sliderPrefab = AssetDatabase.LoadAssetAtPath(defaultSliderPrefabPath, typeof(Object));
Object sliderPrefab = AssetDatabase.LoadAssetAtPath(DefaultSliderPrefabPath, typeof(Object));
sliderObject = Object.Instantiate(sliderPrefab) as GameObject;
slider = sliderObject.GetComponent<Slider>();
sliderVisuals = sliderObject.GetComponent<SliderVisuals>();
Expand Down
194 changes: 194 additions & 0 deletions org.mixedrealitytoolkit.uxcomponents/Tests/Runtime/SliderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Copyright (c) Mixed Reality Toolkit Contributors
// Licensed under the BSD 3-Clause

// Disable "missing XML comment" warning for tests. While nice to have, this documentation is not required.
#pragma warning disable CS1591

using MixedReality.Toolkit.Core.Tests;
using MixedReality.Toolkit.Input;
using MixedReality.Toolkit.Input.Tests;
using NUnit.Framework;
using System.Collections;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UI;
using Object = UnityEngine.Object;

namespace MixedReality.Toolkit.UX.Runtime.Tests
{
public class SliderTests : BaseRuntimeInputTests
{
// Slider/CanvasSlider.prefab
private const string DefaultSliderPrefabGuid = "f64620d502cdf0f429efa27703913cb7";
private static readonly string DefaultSliderPrefabPath = AssetDatabase.GUIDToAssetPath(DefaultSliderPrefabGuid);

private const int HandMovementFrames = 10;

private TestHand hand;

[SetUp]
public void SetUp()
{
hand = new TestHand(Handedness.Right);
}

[UnityTest]
public IEnumerator TouchSlider_MoveRight_ValueIncreasesCorrectly([ValueSource(nameof(MoveRightTestCases))] TestCase testCase)
{
InputTestUtilities.InitializeCameraToOriginAndForward();

var testPrefab = InstantiateSlider(DefaultSliderPrefabPath);
testPrefab.transform.position = InputTestUtilities.InFrontOfUser(new Vector3(0, 0, 1));

var slider = testPrefab.GetComponentInChildren<Slider>();
slider.MinValue = testCase.MinValue;
slider.MaxValue = testCase.MaxValue;
slider.Value = ((testCase.MaxValue - testCase.MinValue) / 2) + testCase.MinValue;

yield return ShowHand();
yield return hand.MoveTo(testPrefab.transform.position - new Vector3(0, 0, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.MoveTo(testPrefab.transform.position - new Vector3(-0.04f, 0f, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
Assert.That(slider.Value, Is.EqualTo(testCase.Expected).Within(0.00001f));

Object.Destroy(testPrefab);
}

[UnityTest]
public IEnumerator GrabSlider_MoveRight_ValueIncreasesCorrectly([ValueSource(nameof(MoveRightTestCases))] TestCase testCase)
{
InputTestUtilities.InitializeCameraToOriginAndForward();

var testPrefab = InstantiateSlider(DefaultSliderPrefabPath);
testPrefab.transform.position = InputTestUtilities.InFrontOfUser(new Vector3(0, 0, 1));

var slider = testPrefab.GetComponentInChildren<Slider>();
slider.IsTouchable = false;
slider.MinValue = testCase.MinValue;
slider.MaxValue = testCase.MaxValue;
slider.Value = ((testCase.MaxValue - testCase.MinValue) / 2) + testCase.MinValue;

yield return ShowHand();
yield return hand.MoveTo(testPrefab.transform.position - new Vector3(0, 0, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.MoveTo(testPrefab.transform.position - new Vector3(-0.04f, 0f, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.SetHandshape(HandshapeTypes.HandshapeId.Open);
yield return RuntimeTestUtilities.WaitForUpdates();

Assert.That(slider.Value, Is.EqualTo(testCase.Expected).Within(0.00001f));

Object.Destroy(testPrefab);
}
[UnityTest]
public IEnumerator TouchSlider_MoveLeft_ValueIncreasesCorrectly([ValueSource(nameof(MoveLeftTestCases))] TestCase testCase)
{
InputTestUtilities.InitializeCameraToOriginAndForward();

var testPrefab = InstantiateSlider(DefaultSliderPrefabPath);
testPrefab.transform.position = InputTestUtilities.InFrontOfUser(new Vector3(0, 0, 1));

var slider = testPrefab.GetComponentInChildren<Slider>();
slider.MinValue = testCase.MinValue;
slider.MaxValue = testCase.MaxValue;
slider.Value = ((testCase.MaxValue - testCase.MinValue) / 2) + testCase.MinValue;

yield return ShowHand();
yield return hand.MoveTo(testPrefab.transform.position - new Vector3(0, 0, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.MoveTo(testPrefab.transform.position + new Vector3(-0.04f, 0f, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
Assert.That(slider.Value, Is.EqualTo(testCase.Expected).Within(0.00001f));

Object.Destroy(testPrefab);
}

[UnityTest]
public IEnumerator GrabSlider_MoveLeft_ValueIncreasesCorrectly([ValueSource(nameof(MoveLeftTestCases))] TestCase testCase)
{
InputTestUtilities.InitializeCameraToOriginAndForward();

var testPrefab = InstantiateSlider(DefaultSliderPrefabPath);
testPrefab.transform.position = InputTestUtilities.InFrontOfUser(new Vector3(0, 0, 1));

var slider = testPrefab.GetComponentInChildren<Slider>();
slider.IsTouchable = false;
slider.MinValue = testCase.MinValue;
slider.MaxValue = testCase.MaxValue;
slider.Value = ((testCase.MaxValue - testCase.MinValue) / 2) + testCase.MinValue;

yield return ShowHand();
yield return hand.MoveTo(testPrefab.transform.position - new Vector3(0, 0, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.MoveTo(testPrefab.transform.position + new Vector3(-0.04f, 0f, 0.00f), HandMovementFrames);
yield return RuntimeTestUtilities.WaitForUpdates();
yield return hand.SetHandshape(HandshapeTypes.HandshapeId.Open);
yield return RuntimeTestUtilities.WaitForUpdates();

Assert.That(slider.Value, Is.EqualTo(testCase.Expected).Within(0.00001f));

Object.Destroy(testPrefab);
}

public readonly struct TestCase
{
public float MinValue { get; }
public float MaxValue { get; }
public float Expected { get; }

public TestCase(float minValue, float maxValue, float expected)
{
MinValue = minValue;
MaxValue = maxValue;
Expected = expected;
}
}

private static IEnumerable MoveRightTestCases()
{
yield return new TestCase(minValue: 0, maxValue: 1, expected: 0.7f);
yield return new TestCase(minValue: 0, maxValue: 10, expected: 7);
yield return new TestCase(minValue: 0, maxValue: 0.1f, expected: 0.07f);
yield return new TestCase(minValue: -1, maxValue: 1, expected: 0.4f);
yield return new TestCase(minValue: -1, maxValue: 0, expected: -0.3f);
}

private static IEnumerable MoveLeftTestCases()
{
yield return new TestCase(minValue: 0, maxValue: 1, expected: 0.3f);
yield return new TestCase(minValue: 0, maxValue: 10, expected: 3);
yield return new TestCase(minValue: 0, maxValue: 0.1f, expected: 0.03f);
yield return new TestCase(minValue: -1, maxValue: 1, expected: -0.4f);
yield return new TestCase(minValue: -1, maxValue: 0, expected: -0.7f);
}

private IEnumerator ShowHand()
{
Vector3 initialHandPosition = InputTestUtilities.InFrontOfUser(new Vector3(0.05f, -0.05f, 0.3f));
yield return hand.Show(initialHandPosition);
yield return RuntimeTestUtilities.WaitForUpdates();
}

private GameObject InstantiateSlider(string prefabPath)
{
GameObject canvas = new GameObject("SliderParent", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler));
canvas.transform.localScale = Vector3.one * 0.001f;
(canvas.transform as RectTransform).sizeDelta = Vector2.one * 200;
GameObject slider = Object.Instantiate(AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath));
slider.transform.SetParent(canvas.transform, worldPositionStays: false);
slider.transform.position = Vector3.zero;
if (slider.transform is RectTransform rectTransform)
{
rectTransform.sizeDelta = new Vector2(200f, rectTransform.sizeDelta.y);
}
return canvas;
}
}
}
#pragma warning restore CS1591

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions org.mixedrealitytoolkit.uxcore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
* Prevent simultaneous editing of multiple input fields when using Non-Native Keyboard. [PR #942](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/942)
* Don't try to start a coroutine in VirtualizedScrollRectList when the GameObject is inactive. [PR #972](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/972)
* Fix SliderSounds playing sound even when disabled. [PR #1007](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1007)
* Fix for sliders with min-max values outside range \[0-1\] when slider is configured for grab interaction (IsTouchable = false) [Issue 944](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/issues/944)

## [3.2.2] - 2024-09-18

Expand Down
7 changes: 4 additions & 3 deletions org.mixedrealitytoolkit.uxcore/Slider/Slider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ private void UpdateSliderValue()

var handDelta = Vector3.Dot(SliderTrackDirection.normalized, interactorDelta);

float normalizedValue = Mathf.Clamp(StartSliderValue + handDelta / SliderTrackDirection.magnitude, 0f, 1.0f);
var normalizedStartValue = (StartSliderValue - MinValue) / (MaxValue - MinValue);
float normalizedValue = Mathf.Clamp(normalizedStartValue + handDelta / SliderTrackDirection.magnitude, 0f, 1.0f);

var unsnappedValue = normalizedValue * (MaxValue - MinValue) + MinValue;
Value = useSliderStepDivisions ? SnapSliderToStepPositions(unsnappedValue) : unsnappedValue;
Expand All @@ -386,12 +387,12 @@ protected override void OnSelectEntered(SelectEnterEventArgs args)
base.OnSelectEntered(args);

// Snap to position by setting the startPosition
// to the slider start, and start value to zero.
// to the slider start, and start value to MinValue.
// However, don't snap when using grabs.
if (snapToPosition && !(args.interactorObject is IGrabInteractor))
{
StartInteractionPoint = SliderStart.position;
StartSliderValue = 0.0f;
StartSliderValue = MinValue;
}
else
{
Expand Down