ViveSoftware / ViveInputUtility-Unity

A toolkit that helps developing/prototyping VR apps.
http://u3d.as/uF7
Other
353 stars 82 forks source link

How to disable grab and scroll on UI elements #32

Closed wirelessdreamer closed 6 years ago

wirelessdreamer commented 6 years ago

With UI elements that have a vertical or horizontal scroll box, and trying to click on items in the scroll box, the only consistant way I've found many users are able to click on them is with higher pose easer, and pose stabalizer script values, but that makes the ui feel very sluggish.

The issue stems from as most users are clicking, their cursor is moving a tiny about, we're using the touchpad scrolling on the background fore movement, and have no need of the cursor grab and scroll on the background, though we use it on the slider on the right side of the list.

What is a clean way to fully disable the grab and scroll, and to send the click event before the full button click happens? I've been digging through the code, but haven't found the section that controls when the selection event happens, that should be enough of a workaround for me to be able to resolve the current issue i'm troubleshooting.

lawwong commented 6 years ago

Do you mean disable ScrollRect dragging only? You may try this https://forum.unity.com/threads/scroll-rect-disable-dragging.303841/

lawwong commented 6 years ago

For example

using UnityEngine.EventSystems;
using UnityEngine.UI;

public class NoDragScrollRect : ScrollRect
{
    public override void OnBeginDrag(PointerEventData eventData) { }
    public override void OnDrag(PointerEventData eventData) { }
    public override void OnEndDrag(PointerEventData eventData) { }
}

You can even keep the editor

using UnityEditor;
using UnityEditor.UI;

[CustomEditor(typeof(NoDragScrollRect))]
public class NoDragScrollRectEditor : ScrollRectEditor { }
wirelessdreamer commented 6 years ago

Thanks for the quick reply. I just tried that solution, and result from it still doesn't give the behavior i'm trying to get, its still awkward for users to select elements on a large list. The best solution would be to be able to send the click even sooner on button press, like having a limit on the analog trigger, and sending the button press before the button is all the way down, I'm just not sure how to implement that with this framework. in VRTK there was an option to use trigger touch instead of trigger click.

lawwong commented 6 years ago

If you are using VivePointers prefab, find ViveRaycaster component under VivePointers>Right/Left>PoseTracker>EventRaycaster . Here you can change behavior to emit "Mouse Button Left" event. You may try Trigger, TriggerTouch, HairTrigger, FullTrigger to see which behavior works better for user.

wirelessdreamer commented 6 years ago

I've tested a couple different times, and am not noticing the behavior change no matter which type of trigger input I select.

wirelessdreamer commented 6 years ago

I found a better way to clarify the issue. open example 7: RoleBindingExample

define your controller, and change the Event Raycaster Mouse button left to any of the trigger function. They all only fire after the trigger has been released. The solution to the issue is to fire the click even as soon as the trigger starts moving down, as opposed to only on release.

Where would be the right place to update the plugin so one of the options for input can fire when the trigger starts to move down, instead of just be released

if you point at the plus button to add a controller and start to click, but the cursor leaves the plus before it is released, no click is registered, which can provide a confusing experience for users, when they care about clicking on what they are pointing at when the start to pull the trigger.

lawwong commented 6 years ago

I think I got it.

Just invoke event in UnityEngine.UI.Button.IPointerDownHandler

using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class PointerDownButton : Button
{
    [Serializable]
    public class ButtonDownEvent : UnityEvent { }

    [SerializeField]
    private ButtonDownEvent m_onDown = new ButtonDownEvent();

    public ButtonDownEvent onDown { get { return m_onDown; } set { m_onDown = value; } }

    public override void OnPointerDown(PointerEventData eventData)
    {
        base.OnPointerDown(eventData);
        if (!IsActive() || !IsInteractable()) { return; }
        m_onDown.Invoke();
    }
}

Add editor as well

using UnityEditor;
using UnityEditor.UI;

[CustomEditor(typeof(PointerDownButton), true)]
[CanEditMultipleObjects]
public class PointerDownButtonEditor : ButtonEditor
{
    SerializedProperty m_onDownProperty;

    protected override void OnEnable()
    {
        base.OnEnable();
        m_onDownProperty = serializedObject.FindProperty("m_onDown");
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        serializedObject.Update();
        EditorGUILayout.PropertyField(m_onDownProperty);
        serializedObject.ApplyModifiedProperties();
    }
}

The reason why UnityEngine.UI.Button doesn't fit your use case is because it implement IPointerClickHandler, and the behavior you described is exactly what PointerClick event designed to be. See Standalone Input Module Source Code for the origin implementation.

There are more handlers you can manipulate with. Additionally, VIU v1.7.1 added 2 handlers that origin handlers can't provide:

wirelessdreamer commented 6 years ago

We implemented something very similar to this as a workaround last week. We'll take our workaround as the right solution then. Thanks :)