Skip to content

Commit 235652c

Browse files
authored
Deprecate HasUIHover and HasUISelection and query the underlying TrackedDeviceModel directly instead (#1090)
* Deprecate HasUIHover and HasUISelection and query the underlying TrackedDeviceModel directly instead * Update InteractionDetector.cs * Update tests * Update CHANGELOG.md * Remove System.Linq * GetComponent -> TryGetComponent
1 parent f92574a commit 235652c

5 files changed

Lines changed: 94 additions & 79 deletions

File tree

org.mixedrealitytoolkit.input/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1414
* Added input action focus handling to disable controller/hand tracked state when the XrSession goes out of focus. [PR #1057](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1057)
1515
* Added support for XR_MSFT_hand_tracking_mesh and XR_ANDROID_hand_mesh on compatible runtimes. [PR #993](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/993)
1616

17+
### Changed
18+
19+
* Updated `InteractionDetector` to work across all `XRRayInteractor` and `NearFarInteractor` implementations, instead of just MRTK-specific `MRTKRayInteractor` implementations. [PR #1090](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1090)
20+
21+
### Deprecated
22+
23+
* Deprecated `HasUIHover` and `HasUISelection` from `MRTKRayInteractor` in favor of querying the underlying `TrackedDeviceModel` directly instead. [PR #1090](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1090)
24+
1725
## [4.0.0-pre.2] - 2025-12-05
1826

1927
### Changed

org.mixedrealitytoolkit.input/InteractionModes/InteractionDetector.cs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using UnityEngine;
77
using UnityEngine.Serialization;
88
using UnityEngine.XR.Interaction.Toolkit.Interactors;
9+
using UnityEngine.XR.Interaction.Toolkit.UI;
910

1011
namespace MixedReality.Toolkit.Input
1112
{
@@ -85,24 +86,7 @@ public InteractionMode ModeOnSelect
8586
}
8687

8788
/// <inheritdoc />
88-
public InteractionMode ModeOnDetection => GetDetectedMode();
89-
90-
/// <summary>
91-
/// Determines which mode should be set.
92-
/// </summary>
93-
/// <returns>The detected mode.</returns>
94-
private InteractionMode GetDetectedMode()
95-
{
96-
if (interactor.hasSelection)
97-
{
98-
return modeOnSelect;
99-
}
100-
else
101-
{
102-
return modeOnHover;
103-
}
104-
105-
}
89+
public InteractionMode ModeOnDetection => interactor.hasSelection ? modeOnSelect : modeOnHover;
10690

10791
[SerializeField]
10892
[FormerlySerializedAs("Controllers")]
@@ -122,10 +106,13 @@ public bool IsModeDetected()
122106
{
123107
bool isDetected = (interactor.hasHover && detectHover) || (interactor.hasSelection && detectSelect);
124108

125-
// Remove if/when XRI sets hasHover/Selection when their ray interactor is hovering/selecting legacy UI.
126-
if (interactor is MRTKRayInteractor rayInteractor)
109+
if (interactor is XRRayInteractor rayInteractor)
110+
{
111+
isDetected |= rayInteractor.TryGetUIModel(out TrackedDeviceModel model) && model.currentRaycast.isValid && (detectHover || (model.select && detectSelect));
112+
}
113+
else if (interactor is NearFarInteractor nearFarInteractor)
127114
{
128-
isDetected |= (rayInteractor.HasUIHover && detectHover) || (rayInteractor.HasUISelection && detectSelect);
115+
isDetected |= nearFarInteractor.TryGetUIModel(out TrackedDeviceModel model) && model.currentRaycast.isValid && (detectHover || (model.select && detectSelect));
129116
}
130117

131118
return isDetected;

org.mixedrealitytoolkit.input/Interactors/Ray/MRTKRayInteractor.cs

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -63,33 +63,14 @@ public GameObject ModeManagedRoot
6363
/// <summary>
6464
/// Is this ray currently hovering a UnityUI/Canvas element?
6565
/// </summary>
66+
[Obsolete("This property has been deprecated in version 4.0.0. Call " + nameof(TryGetUIModel) + " and use " + nameof(TrackedDeviceModel.currentRaycast.isValid) + " instead.")]
6667
public bool HasUIHover => TryGetUIModel(out TrackedDeviceModel model) && model.currentRaycast.isValid;
6768

6869
/// <summary>
6970
/// Is this ray currently selecting a UnityUI/Canvas element?
7071
/// </summary>
71-
public bool HasUISelection
72-
{
73-
get
74-
{
75-
bool hasUISelection = HasUIHover;
76-
#pragma warning disable CS0618 // isUISelectActive is obsolete
77-
if (forceDeprecatedInput)
78-
{
79-
hasUISelection &= isUISelectActive;
80-
}
81-
#pragma warning restore CS0618 // isUISelectActiver is obsolete
82-
else if (uiPressInput != null)
83-
{
84-
hasUISelection &= uiPressInput.ReadIsPerformed();
85-
}
86-
else
87-
{
88-
hasUISelection = false;
89-
}
90-
return hasUISelection;
91-
}
92-
}
72+
[Obsolete("This property has been deprecated in version 4.0.0. Call " + nameof(TryGetUIModel) + " and use " + nameof(TrackedDeviceModel.select) + " instead.")]
73+
public bool HasUISelection => TryGetUIModel(out TrackedDeviceModel model) && model.select;
9374

9475
/// <summary>
9576
/// Used to check if the parent controller is tracked or not

org.mixedrealitytoolkit.input/Tests/Runtime/InteractionModeManagerTests.cs

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System;
1111
using System.Collections;
1212
using System.Collections.Generic;
13-
using System.Linq;
1413
using UnityEngine;
1514
using UnityEngine.TestTools;
1615
using UnityEngine.XR.Interaction.Toolkit;
@@ -68,7 +67,7 @@ public IEnumerator ProximityDetectorTest()
6867
}
6968

7069
/// <summary>
71-
/// Tests the basic Interaction detector. The controller should enter one mode during hover, another during select, and fall back to the default mode during neither
70+
/// Tests the basic Interaction detector. The controller should enter one mode during hover, another during select, and fall back to the default mode during neither.
7271
/// </summary>
7372
[UnityTest]
7473
public IEnumerator InteractionDetectorTest()
@@ -89,28 +88,43 @@ public IEnumerator InteractionDetectorTest()
8988
yield return rightHand.AimAt(cube.transform.position);
9089
yield return RuntimeTestUtilities.WaitForUpdates();
9190

92-
InteractionMode currentMode = rightHandController.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>().ModeOnHover;
93-
Assert.AreEqual(currentMode, rightHandController.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>().ModeOnDetection);
94-
ValidateInteractionModeActive(rightHandController, currentMode);
91+
if (!rightHandController.GetComponentInChildren<MRTKRayInteractor>().TryGetComponent(out InteractionDetector interactionDetector))
92+
{
93+
Assert.Fail("No interaction detector found on right hand ray interactor. Is the component missing?");
94+
}
9595

96+
InteractionMode expectedMode = interactionDetector.ModeOnHover;
97+
Assert.AreEqual(expectedMode, interactionDetector.ModeOnDetection);
98+
ValidateInteractionModeActive(rightHandController, expectedMode);
99+
100+
// Select the cube and check that we're in the correct mode
96101
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
97102
yield return RuntimeTestUtilities.WaitForUpdates();
98-
currentMode = rightHandController.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>().ModeOnSelect;
99-
Assert.AreEqual(currentMode, rightHandController.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>().ModeOnDetection);
100-
ValidateInteractionModeActive(rightHandController, currentMode);
103+
expectedMode = interactionDetector.ModeOnSelect;
104+
Assert.AreEqual(expectedMode, interactionDetector.ModeOnDetection);
105+
ValidateInteractionModeActive(rightHandController, expectedMode);
101106

102-
// move the hand far away and validate that we are in the default mode
107+
// Release the selection and move the hand far away and validate that we are in the default mode
103108
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Open);
104109
yield return RuntimeTestUtilities.WaitForUpdates();
105-
yield return rightHand.MoveTo(cube.transform.position + new Vector3(3.0f,0,0));
110+
yield return rightHand.MoveTo(cube.transform.position + new Vector3(3.0f, 0, 0));
106111
yield return RuntimeTestUtilities.WaitForUpdates();
112+
expectedMode = InteractionModeManager.Instance.DefaultMode;
113+
ValidateInteractionModeActive(rightHandController, expectedMode);
107114

108-
currentMode = InteractionModeManager.Instance.DefaultMode;
109-
ValidateInteractionModeActive(rightHandController, currentMode);
115+
// Put the hand into a grab state and validate that we are in the default mode, since we're not selecting an object
116+
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
117+
yield return RuntimeTestUtilities.WaitForUpdates();
118+
ValidateInteractionModeActive(rightHandController, expectedMode);
119+
120+
// Release the grab state and validate that we are in the default mode
121+
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Open);
122+
yield return RuntimeTestUtilities.WaitForUpdates();
123+
ValidateInteractionModeActive(rightHandController, expectedMode);
110124
}
111125

112126
/// <summary>
113-
/// Tests that mode mediation works properly.
127+
/// Tests that mode mediation works properly.
114128
/// </summary>
115129
/// <remarks>
116130
/// The interaction mode with the higher priority should be the valid one which affects the controller.
@@ -136,12 +150,17 @@ public IEnumerator ModeMediationTest()
136150
InputTestUtilities.SetHandAnchorPoint(Handedness.Right, ControllerAnchorPoint.Grab);
137151
yield return RuntimeTestUtilities.WaitForUpdates();
138152

139-
// Moving the hand to a position where it's far ray is hovering over the cube
153+
if (!rightHandController.GetComponentInChildren<MRTKRayInteractor>().TryGetComponent(out InteractionDetector rayInteractionDetector))
154+
{
155+
Assert.Fail("No interaction detector found on right hand ray interactor. Is the component missing?");
156+
}
157+
158+
// Moving the hand to a position where its far ray is hovering over the cube
140159
yield return rightHand.AimAt(cube.transform.position);
141160
yield return RuntimeTestUtilities.WaitForUpdates();
142-
InteractionMode farRayMode = rightHandController.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>().ModeOnHover;
161+
InteractionMode farRayMode = rayInteractionDetector.ModeOnHover;
143162
yield return RuntimeTestUtilities.WaitForUpdates();
144-
Assert.AreEqual(farRayMode, rightHandController.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>().ModeOnDetection);
163+
Assert.AreEqual(farRayMode, rayInteractionDetector.ModeOnDetection);
145164
ValidateInteractionModeActive(rightHandController, farRayMode);
146165

147166
// Now move the hand in range for the proximity detector
@@ -159,8 +178,13 @@ public IEnumerator ModeMediationTest()
159178
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
160179
yield return RuntimeTestUtilities.WaitForUpdates();
161180

162-
InteractionMode grabMode = rightHandController.GetComponentInChildren<GrabInteractor>().GetComponent<InteractionDetector>().ModeOnSelect;
163-
Assert.AreEqual(grabMode, rightHandController.GetComponentInChildren<GrabInteractor>().GetComponent<InteractionDetector>().ModeOnDetection);
181+
if (!rightHandController.GetComponentInChildren<GrabInteractor>().TryGetComponent(out InteractionDetector grabInteractionDetector))
182+
{
183+
Assert.Fail("No interaction detector found on right hand grab interactor. Is the component missing?");
184+
}
185+
186+
InteractionMode grabMode = grabInteractionDetector.ModeOnSelect;
187+
Assert.AreEqual(grabMode, grabInteractionDetector.ModeOnDetection);
164188
yield return RuntimeTestUtilities.WaitForUpdates();
165189
ValidateInteractionModeActive(rightHandController, grabMode);
166190
Assert.IsTrue(grabMode.Priority > nearMode.Priority);
@@ -176,7 +200,7 @@ public IEnumerator ModeMediationTest()
176200

177201
// Moving the hand to a position where it's far ray is hovering over the cube
178202
yield return rightHand.MoveTo(cube.transform.position + new Vector3(0.02f, -0.1f, -0.8f));
179-
yield return RuntimeTestUtilities.WaitForUpdates(frameCount:120);
203+
yield return RuntimeTestUtilities.WaitForUpdates(frameCount: 120);
180204

181205
ValidateInteractionModeActive(rightHandController, farRayMode);
182206
}
@@ -189,15 +213,18 @@ public IEnumerator ModeMediationTest()
189213
private void ValidateInteractionModeActive(XRBaseController controller, InteractionMode currentMode)
190214
{
191215
// We construct the list of managed interactor types manually because we don't want to expose the internal controller mapping implementation to even internal use, since
192-
// we don't want any other class to be able to modify those collections without going through the Mode Manager or it's in-editor inspector.
193-
HashSet<System.Type> managedInteractorTypes = new HashSet<System.Type>(InteractionModeManager.Instance.PrioritizedInteractionModes.SelectMany(x => x.AssociatedTypes));
194-
HashSet<System.Type> activeInteractorTypes = InteractionModeManager.Instance.PrioritizedInteractionModes.Find(x => x.ModeName == currentMode.Name).AssociatedTypes;
216+
// we don't want any other class to be able to modify those collections without going through the Mode Manager or its in-editor inspector.
217+
HashSet<Type> managedInteractorTypes = new HashSet<Type>();
218+
foreach (InteractionModeDefinition interactionMode in InteractionModeManager.Instance.PrioritizedInteractionModes)
219+
{
220+
managedInteractorTypes.UnionWith(interactionMode.AssociatedTypes);
221+
}
222+
HashSet<Type> activeInteractorTypes = InteractionModeManager.Instance.PrioritizedInteractionModes.Find(x => x.ModeName == currentMode.Name).AssociatedTypes;
195223

196224
// Ensure the prox detector has actually had the desired effect of enabling/disabling interactors.
197-
foreach (System.Type interactorType in managedInteractorTypes)
225+
foreach (Type interactorType in managedInteractorTypes)
198226
{
199-
XRBaseInteractor interactor = controller.GetComponentInChildren(interactorType) as XRBaseInputInteractor;
200-
if (interactor != null)
227+
if (controller.GetComponentInChildren(interactorType) is XRBaseInputInteractor interactor && interactor != null)
201228
{
202229
Assert.AreEqual(activeInteractorTypes.Contains(interactorType), interactor.enabled);
203230
}

org.mixedrealitytoolkit.input/Tests/Runtime/InteractionModeManagerTestsForControllerlessRig.cs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,30 +91,42 @@ public IEnumerator InteractionDetectorTest()
9191
yield return RuntimeTestUtilities.WaitForUpdates();
9292

9393
TrackedPoseDriver rightHandTrackedPoseDriver = CachedTrackedPoseDriverLookup.RightHandTrackedPoseDriver;
94-
InteractionDetector rightHandInteractionDetector = rightHandTrackedPoseDriver.transform.parent.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>();
94+
Assert.IsTrue(rightHandTrackedPoseDriver != null, "No tracked pose driver found for right hand.");
9595

9696
// Moving the hand to a position where it's far ray is hovering over the cube
9797
yield return rightHand.AimAt(cube.transform.position);
9898
yield return RuntimeTestUtilities.WaitForUpdates();
9999

100-
InteractionMode currentMode = rightHandInteractionDetector.ModeOnHover;
101-
Assert.AreEqual(currentMode, rightHandInteractionDetector.ModeOnDetection);
102-
ValidateInteractionModeActive(rightHandTrackedPoseDriver, currentMode);
100+
InteractionDetector interactionDetector = rightHandTrackedPoseDriver.transform.parent.GetComponentInChildren<MRTKRayInteractor>().GetComponent<InteractionDetector>();
101+
102+
InteractionMode expectedMode = interactionDetector.ModeOnHover;
103+
Assert.AreEqual(expectedMode, interactionDetector.ModeOnDetection);
104+
ValidateInteractionModeActive(rightHandTrackedPoseDriver, expectedMode);
103105

106+
// Select the cube and check that we're in the correct mode
104107
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
105108
yield return RuntimeTestUtilities.WaitForUpdates();
106-
currentMode = rightHandInteractionDetector.ModeOnSelect;
107-
Assert.AreEqual(currentMode, rightHandInteractionDetector.ModeOnDetection);
108-
ValidateInteractionModeActive(rightHandTrackedPoseDriver, currentMode);
109+
expectedMode = interactionDetector.ModeOnSelect;
110+
Assert.AreEqual(expectedMode, interactionDetector.ModeOnDetection);
111+
ValidateInteractionModeActive(rightHandTrackedPoseDriver, expectedMode);
109112

110-
// move the hand far away and validate that we are in the default mode
113+
// Release the selection and move the hand far away and validate that we are in the default mode
111114
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Open);
112115
yield return RuntimeTestUtilities.WaitForUpdates();
113-
yield return rightHand.MoveTo(cube.transform.position + new Vector3(3.0f,0,0));
116+
yield return rightHand.MoveTo(cube.transform.position + new Vector3(3.0f, 0, 0));
114117
yield return RuntimeTestUtilities.WaitForUpdates();
118+
expectedMode = InteractionModeManager.Instance.DefaultMode;
119+
ValidateInteractionModeActive(rightHandTrackedPoseDriver, expectedMode);
115120

116-
currentMode = InteractionModeManager.Instance.DefaultMode;
117-
ValidateInteractionModeActive(rightHandTrackedPoseDriver, currentMode);
121+
// Put the hand into a grab state and validate that we are in the default mode, since we're not selecting an object
122+
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Grab);
123+
yield return RuntimeTestUtilities.WaitForUpdates();
124+
ValidateInteractionModeActive(rightHandTrackedPoseDriver, expectedMode);
125+
126+
// Release the grab state and validate that we are in the default mode
127+
yield return rightHand.SetHandshape(HandshapeTypes.HandshapeId.Open);
128+
yield return RuntimeTestUtilities.WaitForUpdates();
129+
ValidateInteractionModeActive(rightHandTrackedPoseDriver, expectedMode);
118130
}
119131

120132
/// <summary>

0 commit comments

Comments
 (0)