Skip to content

Commit f3395b0

Browse files
authored
Merge pull request #6858 from artsouflMS/PinchSliderOrientation
Pinch slider orientation
2 parents 5a85f49 + 082f43b commit f3395b0

File tree

6 files changed

+297
-7
lines changed

6 files changed

+297
-7
lines changed

Assets/MixedRealityToolkit.SDK/Features/UX/Prefabs/Sliders/PinchSlider.prefab

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,7 @@ MonoBehaviour:
10611061
columns: 13
10621062
cellWidth: 0.021
10631063
cellHeight: 0.01
1064+
assetVersion: 1
10641065
--- !u!1 &7307063430618434862
10651066
GameObject:
10661067
m_ObjectHideFlags: 0
@@ -1110,6 +1111,9 @@ MonoBehaviour:
11101111
m_EditorClassIdentifier:
11111112
thumbRoot: {fileID: 7307063430758334042}
11121113
sliderValue: 0.5
1114+
trackVisuals: {fileID: 7307063431327029218}
1115+
tickMarks: {fileID: 7307063430561165487}
1116+
thumbVisual: {fileID: 7307063430134822100}
11131117
sliderAxis: 0
11141118
sliderStartDistance: -0.125
11151119
sliderEndDistance: 0.125

Assets/MixedRealityToolkit.SDK/Features/UX/Scripts/Sliders/PinchSlider.cs

Lines changed: 152 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,86 @@ public float SliderValue
4848
}
4949
}
5050

51+
[Header("Slider Axis Visuals")]
52+
53+
[Tooltip("The gameObject that contains the trackVisuals. This will get rotated to match the slider axis")]
54+
[SerializeField]
55+
private GameObject trackVisuals = null;
56+
/// <summary>
57+
/// Property accessor of trackVisuals, it contains the desired track Visuals. This will get rotated to match the slider axis.
58+
/// </summary>
59+
public GameObject TrackVisuals
60+
{
61+
get
62+
{
63+
return trackVisuals;
64+
}
65+
set
66+
{
67+
trackVisuals = value;
68+
UpdateTrackVisuals();
69+
}
70+
}
71+
72+
[Tooltip("The gameObject that contains the tickMarks. This will get rotated to match the slider axis")]
73+
[SerializeField]
74+
private GameObject tickMarks = null;
75+
/// <summary>
76+
/// Property accessor of tickMarks, it contains the desired tick Marks. This will get rotated to match the slider axis.
77+
/// </summary>
78+
public GameObject TickMarks
79+
{
80+
get
81+
{
82+
return tickMarks;
83+
}
84+
set
85+
{
86+
tickMarks = value;
87+
UpdateTickMarks();
88+
}
89+
}
90+
91+
5192
[Header("Slider Track")]
5293

5394
[Tooltip("The axis the slider moves along")]
5495
[SerializeField]
5596
private SliderAxis sliderAxis = SliderAxis.XAxis;
56-
[Serializable]
57-
private enum SliderAxis
97+
/// <summary>
98+
/// Property accessor of sliderAxis. The axis the slider moves along.
99+
/// </summary>
100+
public SliderAxis CurrentSliderAxis
58101
{
59-
XAxis = 0,
60-
YAxis,
61-
ZAxis
102+
get { return sliderAxis; }
103+
set
104+
{
105+
sliderAxis = value;
106+
UpdateVisualsOrientation();
107+
}
108+
}
109+
110+
/// <summary>
111+
/// Previous value of slider axis, is used in order to detect change in current slider axis value
112+
/// </summary>
113+
private SliderAxis? previousSliderAxis = null;
114+
/// <summary>
115+
/// Property accessor for previousSliderAxis that is used also to initiallize the property with the current value in case of null value.
116+
/// </summary>
117+
private SliderAxis PreviousSliderAxis
118+
{
119+
get
120+
{
121+
if (previousSliderAxis == null)
122+
{
123+
previousSliderAxis = CurrentSliderAxis;
124+
}
125+
return previousSliderAxis.Value;
126+
}
127+
set
128+
{
129+
previousSliderAxis = value;
130+
}
62131
}
63132

64133
[SerializeField]
@@ -144,15 +213,19 @@ public void Start()
144213
OnValueUpdated.Invoke(new SliderEventData(sliderValue, sliderValue, null, this));
145214
}
146215

147-
148-
149216
private void OnDisable()
150217
{
151218
if (activePointer != null)
152219
{
153220
EndInteraction();
154221
}
155222
}
223+
224+
private void OnValidate()
225+
{
226+
CurrentSliderAxis = sliderAxis;
227+
}
228+
156229
#endregion
157230

158231
#region Private Methods
@@ -165,6 +238,78 @@ private void InitializeSliderThumb()
165238
UpdateUI();
166239
}
167240

241+
/// <summary>
242+
/// Update orientation of track visuals based on slider axis orientation
243+
/// </summary>
244+
private void UpdateTrackVisuals()
245+
{
246+
if (TrackVisuals)
247+
{
248+
TrackVisuals.transform.localPosition = Vector3.zero;
249+
250+
switch (sliderAxis)
251+
{
252+
case SliderAxis.XAxis:
253+
TrackVisuals.transform.localRotation = Quaternion.identity;
254+
break;
255+
case SliderAxis.YAxis:
256+
TrackVisuals.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, 90.0f);
257+
break;
258+
case SliderAxis.ZAxis:
259+
TrackVisuals.transform.localRotation = Quaternion.Euler(0.0f, 90.0f, 0.0f);
260+
break;
261+
}
262+
}
263+
}
264+
265+
/// <summary>
266+
/// Update orientation of tick marks based on slider axis orientation
267+
/// </summary>
268+
private void UpdateTickMarks()
269+
{
270+
if (TickMarks)
271+
{
272+
TickMarks.transform.localPosition = Vector3.zero;
273+
TickMarks.transform.localRotation = Quaternion.identity;
274+
275+
var grid = TickMarks.GetComponent<Utilities.GridObjectCollection>();
276+
if (grid)
277+
{
278+
// Update cellwidth or cellheight depending on what was the previous axis set to
279+
var previousAxis = grid.Layout;
280+
if (previousAxis == Utilities.LayoutOrder.Vertical)
281+
{
282+
grid.CellWidth = grid.CellHeight;
283+
}
284+
else
285+
{
286+
grid.CellHeight = grid.CellWidth;
287+
}
288+
289+
grid.Layout = (sliderAxis == SliderAxis.YAxis) ? Utilities.LayoutOrder.Vertical : Utilities.LayoutOrder.Horizontal;
290+
grid.UpdateCollection();
291+
}
292+
293+
if (sliderAxis == SliderAxis.ZAxis)
294+
{
295+
TickMarks.transform.localRotation = Quaternion.Euler(0.0f, 90.0f, 0.0f);
296+
}
297+
}
298+
}
299+
300+
/// <summary>
301+
/// Update orientation of the visual components of pinch slider
302+
/// </summary>
303+
private void UpdateVisualsOrientation()
304+
{
305+
if (PreviousSliderAxis != sliderAxis)
306+
{
307+
UpdateTrackVisuals();
308+
UpdateTickMarks();
309+
PreviousSliderAxis = sliderAxis;
310+
}
311+
}
312+
168313
private Vector3 GetSliderAxis()
169314
{
170315
switch (sliderAxis)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.MixedReality.Toolkit.UI
7+
{
8+
/// <summary>
9+
/// Describes in which axis to orient the slider
10+
/// </summary>
11+
[Serializable]
12+
public enum SliderAxis
13+
{
14+
XAxis = 0,
15+
YAxis,
16+
ZAxis
17+
}
18+
}

Assets/MixedRealityToolkit.SDK/Features/UX/Scripts/Sliders/SliderAxis.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/MixedRealityToolkit.Tests/PlayModeTests/PinchSliderTests.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,97 @@ public IEnumerator TestAssembeInteractableAndEventsRaised()
173173
GameObject.Destroy(pinchSliderObject);
174174
}
175175

176+
/// <summary>
177+
/// Tests that pinch slider visuals can be null
178+
/// </summary>
179+
[UnityTest]
180+
public IEnumerator TestNullVisuals()
181+
{
182+
GameObject pinchSliderObject;
183+
PinchSlider slider;
184+
185+
// This should not throw exception
186+
InstantiateDefaultSliderPrefab(Vector3.forward, Vector3.zero, out pinchSliderObject, out slider);
187+
188+
// Remove references to visuals
189+
slider.TrackVisuals = null;
190+
slider.TickMarks = null;
191+
192+
// Test that the slider still works
193+
Debug.Assert(slider.SliderValue == 0.5, "Slider should have value 0.5 at start");
194+
yield return DirectPinchAndMoveSlider(slider, 1.0f);
195+
Debug.Assert(slider.SliderValue == 1.0, "Slider should have value 1.0 after being manipulated at start");
196+
197+
// clean up
198+
GameObject.Destroy(pinchSliderObject);
199+
yield return null;
200+
}
201+
202+
/// <summary>
203+
/// Tests that pinch slider visuals have the correct orientation after slider axis change
204+
/// </summary>
205+
[UnityTest]
206+
public IEnumerator TestVisualsOrientation()
207+
{
208+
GameObject pinchSliderObject;
209+
PinchSlider slider;
210+
211+
// This should not throw exception
212+
InstantiateDefaultSliderPrefab(Vector3.forward, Vector3.zero, out pinchSliderObject, out slider);
213+
214+
var tickMarks = slider.TickMarks;
215+
var trackVisuals = slider.TrackVisuals;
216+
217+
slider.CurrentSliderAxis = SliderAxis.XAxis;
218+
219+
yield return null;
220+
221+
if (trackVisuals)
222+
{
223+
Debug.Assert(trackVisuals.transform.localRotation == Quaternion.identity, "TrackVisuals should have local rotation equal to Quaternion.identity");
224+
}
225+
226+
if (tickMarks)
227+
{
228+
Debug.Assert(tickMarks.transform.localRotation == Quaternion.identity, "TickMarks should have local rotation equal to Quaternion.identity");
229+
Debug.Assert(tickMarks.GetComponent<GridObjectCollection>().Layout == LayoutOrder.Horizontal, "TickMarks GridObjectCollection Layout should be Horizontal");
230+
}
231+
232+
slider.CurrentSliderAxis = SliderAxis.YAxis;
233+
234+
yield return null;
235+
236+
if (trackVisuals)
237+
{
238+
Debug.Assert(trackVisuals.transform.localRotation == Quaternion.Euler(0.0f, 0.0f, 90.0f), "TrackVisuals should have local rotation equal to Quaternion.Euler(0.0f, 0.0f, 90.0f)");
239+
}
240+
241+
if (tickMarks)
242+
{
243+
Debug.Assert(tickMarks.transform.localRotation == Quaternion.identity, "TickMarks should have local rotation equal to Quaternion.identity");
244+
Debug.Assert(tickMarks.GetComponent<GridObjectCollection>().Layout == LayoutOrder.Vertical, "TickMarks GridObjectCollection Layout should be Vertical");
245+
}
246+
247+
slider.CurrentSliderAxis = SliderAxis.ZAxis;
248+
249+
yield return null;
250+
251+
if (trackVisuals)
252+
{
253+
Debug.Assert(trackVisuals.transform.localRotation == Quaternion.Euler(0.0f, 90.0f, 0.0f), "TrackVisuals should have local rotation equal to Quaternion.Euler(0.0f, 90.0f, 0.0f)");
254+
}
255+
256+
if (tickMarks)
257+
{
258+
Debug.Assert(tickMarks.transform.localRotation == Quaternion.Euler(0.0f, 90.0f, 0.0f), "TickMarks should have local rotation equal to Quaternion.Euler(0.0f, 90.0f, 0.0f)");
259+
Debug.Assert(tickMarks.GetComponent<GridObjectCollection>().Layout == LayoutOrder.Horizontal, "TickMarks GridObjectCollection Layout should be Horizontal");
260+
}
261+
262+
// clean up
263+
GameObject.Destroy(pinchSliderObject);
264+
yield return null;
265+
}
266+
176267
#endregion Tests
177268

178269
#region Private methods

Documentation/README_Sliders.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,24 @@ You can directly move the starting and end points of the slider by moving the ha
4242
You can also specify the axis (in local space) of the slider via the _Slider Axis_ field
4343

4444
If you cannot use the handles, you can instead specify the start and end points of the slider via the _Slider Start Distance_ and _Slider End Distance_ fields. These specify start / end position of slider as a distance from the slider's center, in local coordinates. This means that once you set the slider start and end distances as you want them, you can scale the slider to be smaller or larger without needing to update the start and end distances.
45+
46+
## Inspector properties
47+
48+
**Thumb Root** The gameobject that contains the slider thumb.
49+
50+
**Slider Value** The value of the slider.
51+
52+
**Track Visuals** The gameobject that contains the desired track visuals that goes along the slider.
53+
54+
**Tick Marks** The gameobject that contains the desired tick marks that goes along the slider.
55+
56+
**Slider Axis** The axis the slider moves along.
57+
58+
**Slider Start Distance** Where the slider track starts, as distance from center along slider axis, in local space units.
59+
60+
**Slider End Distance** Where the slider track ends, as distance from center along slider axis, in local space units.
61+
62+
When user updates the slider axis value in editor then if Track Visuals or Tick Visuals are specified then their transform is updated.
63+
Specifically, their local position is reset and their local rotation is set to match the Slider Axis orientation.
64+
Their scale isn't modified.
65+
If Tick Marks have a Grid Object Collection component then the Layout and CellWidth or CellHeight is updated accordingly to match the Slider Axis.

0 commit comments

Comments
 (0)