///
/// Sets the active tab of the panel to the .
@@ -197,6 +213,11 @@ void CloseTab(PanelTab panelTab)
}
}
+ protected override void UpdateVR()
+ {
+ UpdateDesktop();
+ }
+
///
/// Initializes the dynamic panel on the panel canvas.
///
diff --git a/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs b/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs
index 78572ae3e6..7c17499681 100644
--- a/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs
+++ b/Assets/SEE/UI/Window/PropertyWindow/PropertyWindowContextMenu.cs
@@ -10,6 +10,7 @@
using TMPro;
using UnityEngine;
using UnityEngine.Events;
+using SEE.XR;
namespace SEE.UI.Window.PropertyWindow
{
@@ -86,8 +87,17 @@ public PropertyWindowContextMenu(PopupMenu.PopupMenu contextMenu,
ResetGroup();
this.filterButton.clickEvent.AddListener(ShowFilterMenu);
+ this.filterButton.clickEvent.AddListener(() => {
+ XRSEEActions.OnSelectToggle = true;
+ });
this.sortButton.clickEvent.AddListener(ShowSortMenu);
+ this.sortButton.clickEvent.AddListener(() => {
+ XRSEEActions.OnSelectToggle = true;
+ });
this.groupButton.clickEvent.AddListener(ShowGroupMenu);
+ this.groupButton.clickEvent.AddListener(() => {
+ XRSEEActions.OnSelectToggle = true;
+ });
}
#region Filter menu
diff --git a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs
index 1f83bf4240..0c4c521d84 100644
--- a/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs
+++ b/Assets/SEE/UI/Window/TreeWindow/DesktopTreeWindow.cs
@@ -16,6 +16,7 @@
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
+using SEE.XR;
using ArgumentException = System.ArgumentException;
using Edge = SEE.DataModel.DG.Edge;
using Node = SEE.DataModel.DG.Node;
@@ -392,6 +393,35 @@ void RegisterClickHandler()
{
if (item.TryGetComponentOrLog(out PointerHelper pointerHelper))
{
+ if (SceneSettings.InputType == PlayerInputType.VRPlayer)
+ {
+ pointerHelper.EnterEvent.AddListener(_ =>
+ {
+ XRSEEActions.OnTreeViewToggle = true;
+ XRSEEActions.TreeViewEntry = item;
+ });
+ pointerHelper.ExitEvent.AddListener(_ => XRSEEActions.OnTreeViewToggle = false);
+ pointerHelper.ThumbstickEvent.AddListener(e =>
+ {
+ if (XRSEEActions.TooltipToggle)
+ {
+ if (representedGraphElement == null)
+ {
+ // There are no applicable actions for this item.
+ return;
+ }
+
+ // We want all applicable actions for the element, except ones where the element
+ // is shown in the TreeWindow, since we are already in the TreeWindow.
+ IEnumerable actions = CreateContextMenuActions(contextMenu, e.position, representedGraphElement, representedGameObject);
+ XRSEEActions.TooltipToggle = false;
+ XRSEEActions.OnSelectToggle = true;
+ XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit ray);
+ contextMenu.ShowWith(actions, ray.point);
+ }
+ });
+ }
+
// Right click opens the context menu, left/middle click expands/collapses the item.
pointerHelper.ClickEvent.AddListener(e =>
{
@@ -405,21 +435,7 @@ void RegisterClickHandler()
// We want all applicable actions for the element, except ones where the element
// is shown in the TreeWindow, since we are already in the TreeWindow.
- List appends = new ()
- {
- new("Hide in TreeWindow", () =>
- {
- searcher.Filter.ExcludeElements.Add(representedGraphElement);
- Rebuild();
- }, Icons.Hide)
- };
- IEnumerable actions = ContextMenuAction
- .GetOptionsForTreeView(contextMenu.ContextMenu,
- e.position,
- representedGraphElement,
- representedGameObject,
- appends);
- actions = actions.Concat(appends);
+ IEnumerable actions = CreateContextMenuActions(contextMenu, e.position, representedGraphElement, representedGameObject);
contextMenu.ShowWith(actions, e.position);
}
else
@@ -435,6 +451,23 @@ void RegisterClickHandler()
}
});
}
+
+ IEnumerable CreateContextMenuActions(TreeWindowContextMenu contextMenu, Vector2 position, GraphElement representedGraphElement, GameObject representedGameObject)
+ {
+ List appends = new()
+ {
+ new("Hide in TreeWindow", () =>
+ {
+ searcher.Filter.ExcludeElements.Add(representedGraphElement);
+ Rebuild();
+ }, Icons.Hide)
+ };
+
+ IEnumerable actions = ContextMenuAction
+ .GetOptionsForTreeView(contextMenu.ContextMenu, position, representedGraphElement, representedGameObject, appends);
+
+ return actions.Concat(appends);
+ }
}
}
@@ -706,7 +739,10 @@ private void SearchFor(string searchTerm)
expandItem: (_, _) => RevealElementAsync(node).Forget());
}
- items.position = items.position.WithXYZ(y: 0);
+ if (SceneSettings.InputType == PlayerInputType.DesktopPlayer)
+ {
+ items.position = items.position.WithXYZ(y: 0);
+ }
}
///
@@ -854,8 +890,17 @@ protected override void StartDesktop()
searchField.onValueChanged.AddListener(SearchFor);
filterButton = root.Find("Search/Filter").gameObject.MustGetComponent();
+ filterButton.clickEvent.AddListener(() => {
+ XRSEEActions.OnSelectToggle = true;
+ });
sortButton = root.Find("Search/Sort").gameObject.MustGetComponent();
+ sortButton.clickEvent.AddListener(() => {
+ XRSEEActions.OnSelectToggle = true;
+ });
groupButton = root.Find("Search/Group").gameObject.MustGetComponent();
+ groupButton.clickEvent.AddListener(() => {
+ XRSEEActions.OnSelectToggle = true;
+ });
PopupMenu.PopupMenu popupMenu = gameObject.AddComponent();
contextMenu = new TreeWindowContextMenu(popupMenu, searcher, grouper, Rebuild,
filterButton, sortButton, groupButton);
@@ -863,6 +908,11 @@ protected override void StartDesktop()
Rebuild();
}
+ protected override void StartVR()
+ {
+ StartDesktop();
+ }
+
///
/// Rebuilds the tree window.
///
diff --git a/Assets/SEE/Utils/PointerHelper.cs b/Assets/SEE/Utils/PointerHelper.cs
index 90d850f9a5..f57af47926 100644
--- a/Assets/SEE/Utils/PointerHelper.cs
+++ b/Assets/SEE/Utils/PointerHelper.cs
@@ -31,6 +31,12 @@ public class PointerHelper: MonoBehaviour, IPointerExitHandler, IPointerEnterHan
///
public readonly UnityEvent ClickEvent = new();
+ ///
+ /// This event will be triggered whenever the user clicks the GameObject
+ /// this component is attached to with the thumbstickbutton in VR.
+ ///
+ public readonly UnityEvent ThumbstickEvent = new();
+
///
/// Invokes the .
///
diff --git a/Assets/SEE/Utils/Raycasting.cs b/Assets/SEE/Utils/Raycasting.cs
index 325e8036e5..45362bf88b 100644
--- a/Assets/SEE/Utils/Raycasting.cs
+++ b/Assets/SEE/Utils/Raycasting.cs
@@ -7,6 +7,7 @@
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;
+using SEE.XR;
namespace SEE.Utils
{
@@ -66,7 +67,7 @@ public static class Raycasting
///
public static HitGraphElement RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef elementRef)
{
- if (!IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out RaycastHit hit))
+ if (((SceneSettings.InputType == PlayerInputType.DesktopPlayer && !IsMouseOverGUI()) || SceneSettings.InputType == PlayerInputType.VRPlayer) && Physics.Raycast(UserPointsTo(), out RaycastHit hit))
{
raycastHit = hit;
if (hit.transform.TryGetComponent(out NodeRef nodeRef))
@@ -106,7 +107,14 @@ public static bool RaycastAnything(out RaycastHit raycastHit, float maxDistance
{
raycastHit = new RaycastHit();
Physics.queriesHitBackfaces = true;
- return !IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out raycastHit, maxDistance);
+ if (SceneSettings.InputType == PlayerInputType.DesktopPlayer)
+ {
+ return !IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out raycastHit, maxDistance);
+ }
+ else
+ {
+ return Physics.Raycast(UserPointsTo(), out raycastHit, maxDistance);
+ }
}
///
@@ -193,21 +201,42 @@ public static HitGraphElement RaycastInteractableObject(out RaycastHit raycastHi
{
HitGraphElement result = HitGraphElement.None;
+ static HitGraphElement DetermineHit(InteractableObject element)
+ {
+ HitGraphElement result = element.GraphElemRef.Elem switch
+ {
+ null => HitGraphElement.None,
+ Node => HitGraphElement.Node,
+ Edge => HitGraphElement.Edge,
+ _ => throw new System.ArgumentOutOfRangeException()
+ };
+ return result;
+ }
+
raycastHit = new RaycastHit();
obj = null;
- if (!IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out RaycastHit hit))
+ if (SceneSettings.InputType == PlayerInputType.VRPlayer)
{
- raycastHit = hit;
- if (hit.transform.TryGetComponent(out InteractableObject io))
+ if (XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit ray))
+ {
+ raycastHit = ray;
+ if (ray.transform.TryGetComponent(out InteractableObject io))
+ {
+ result = DetermineHit(io);
+ obj = io;
+ }
+ }
+ }
+ else
+ {
+ if (!IsMouseOverGUI() && Physics.Raycast(UserPointsTo(), out RaycastHit hit))
{
- result = io.GraphElemRef.Elem switch
+ raycastHit = hit;
+ if (hit.transform.TryGetComponent(out InteractableObject io))
{
- null => HitGraphElement.None,
- Node => HitGraphElement.Node,
- Edge => HitGraphElement.Edge,
- _ => throw new System.ArgumentOutOfRangeException()
- };
- obj = io;
+ result = DetermineHit(io);
+ obj = io;
+ }
}
}
return result;
@@ -295,8 +324,19 @@ public static Ray UserPointsTo()
{
// FIXME: We need to an interaction for VR, too.
Camera mainCamera = MainCamera.Camera;
+ Vector3 screenPoint;
+ if (SceneSettings.InputType == PlayerInputType.VRPlayer)
+ {
+ XRSEEActions.RayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit);
+ screenPoint = mainCamera.WorldToScreenPoint(hit.point);
+ }
+ else
+ {
+ screenPoint = Input.mousePosition;
+ }
+
return mainCamera != null
- ? mainCamera.ScreenPointToRay(Input.mousePosition)
+ ? mainCamera.ScreenPointToRay(screenPoint)
: new Ray(origin: Vector3.zero, direction: Vector3.zero);
}
}
diff --git a/Assets/SEE/XR/KeyboardInputHandler.cs b/Assets/SEE/XR/KeyboardInputHandler.cs
new file mode 100644
index 0000000000..f6feaabd36
--- /dev/null
+++ b/Assets/SEE/XR/KeyboardInputHandler.cs
@@ -0,0 +1,44 @@
+using SEE.Controls;
+using SEE.GO;
+using TMPro;
+using UnityEngine;
+using UnityEngine.EventSystems;
+
+namespace SEE.XR
+{
+ ///
+ /// This class changes the inputfield for the VR-Keyboard and
+ /// also activates the keyboard.
+ /// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g
+ ///
+ public class KeyboardInputHandler : MonoBehaviour, IPointerClickHandler
+ {
+ ///
+ /// The reference to the keyboard GameObject.
+ ///
+ private GameObject keyboardGameObject;
+
+ private void Start()
+ {
+ // Cache the reference to the keyboard GameObject
+ if (SceneSettings.InputType == PlayerInputType.VRPlayer)
+ {
+ keyboardGameObject = KeyboardManager.instance.gameObject.transform.Find("Keyboard").gameObject;
+ }
+ }
+
+ ///
+ /// This method gets called when the user clicks the input field.
+ /// If the user is in VR, the keyboard gets activated.
+ ///
+ /// Event data associated with the event when the user clicks on the inputfield.
+ public void OnPointerClick(PointerEventData eventdata)
+ {
+ if (SceneSettings.InputType == PlayerInputType.VRPlayer)
+ {
+ KeyboardManager.instance.inputField = GetComponent();
+ keyboardGameObject.SetActive(true);
+ }
+ }
+ }
+}
diff --git a/Assets/SEE/XR/KeyboardInputHandler.cs.meta b/Assets/SEE/XR/KeyboardInputHandler.cs.meta
new file mode 100644
index 0000000000..6a6464a545
--- /dev/null
+++ b/Assets/SEE/XR/KeyboardInputHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8200dca4f813a2949a733aadb413d2f7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SEE/XR/KeyboardKey.cs b/Assets/SEE/XR/KeyboardKey.cs
new file mode 100644
index 0000000000..5684eb8311
--- /dev/null
+++ b/Assets/SEE/XR/KeyboardKey.cs
@@ -0,0 +1,110 @@
+using TMPro;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace SEE.XR
+{
+ ///
+ /// This class handles a single keyboard key.
+ /// This script is based on this tutorial: https://www.youtube.com/watch?v=vTonHBr4t4g
+ ///
+ public class KeyboardKey : MonoBehaviour
+ {
+ ///
+ /// The character of the key.
+ ///
+ public string character;
+
+ ///
+ /// The shifted character.
+ ///
+ public string shiftCharacter;
+
+ ///
+ /// Is true when the key is shifted.
+ ///
+ private bool isShifted = false;
+
+ ///
+ /// The actual keyboard key.
+ ///
+ private Button key;
+
+ ///
+ /// The label of the keyboard key.
+ ///
+ public TextMeshProUGUI keyLabel;
+
+ private void Start()
+ {
+ KeyboardManager.instance.shiftButton.onClick.AddListener(HandleShift);
+ key = GetComponent