- Home /
How do I have clickable buttons in a scroll rect?
I found a similar question that addresses the same problem but I couldn't quite understand what the dude did to solve his one.
Basically, I have a scroll rect with buttons inside. I tried scrolling, however, when my mouse is over a button the thing doesn't scroll. When it is between the buttons, (not over anything), it scrolls perfectly. I've added a viewport panel above so that I can scroll anywhere but then that makes it so that the buttons don't work.
I've tried a few things but I can't figure it out. Can anyone help?
Answer by Johandea · Oct 21, 2018 at 02:38 PM
There might be a better way of doing this, but I've solved a similar problem for myself with the eventSystem.
On your button: add a script implementing the IBeginDragHandler, IDragHandler, IEndDragHandler.
Within IBeginDragHandler, IDragHandler and IEndDragHandler pass on the event to your scrollRect with
ExecuteEvents.Execute(scrollRect.gameObject, pointerEventData, ExecuteEvents.beginDragHandler);
ExecuteEvents.Execute(scrollRect.gameObject, pointerEventData, ExecuteEvents.dragHandler);
ExecuteEvents.Execute(scrollRect.gameObject, pointerEventData, ExecuteEvents.endDragHandler);
respectively. This makes your button send the same event (i.e. you clicking and dragging it) the button received to the scrollrect, making it scroll.
If you need to you can add some logic before initializing the first ExecuteEvents. In my case I made it so you have to drag it past a certain threshold before passing it on.
Below is a draft for how it could look. Untested as is
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class yourScript : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public ScrollRect yourScrollRect;
private bool passingEvent = false;
public void OnBeginDrag(PointerEventData pointerEventData)
{
// If you only need to pass the drag through use
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.beginDragHandler);
passingEvent = true;
}
public void OnDrag(PointerEventData pointerEventData)
{
if (passingEvent) // Don't send dragHandler before beginDragHandler has been called. It gives unwanted results...
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.dragHandler);
}
}
public void OnEndDrag(PointerEventData pointerEventData)
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.endDragHandler);
passingEvent = false;
}
}
And if you need logic to check whether the event should be passed on use something like this:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class yourScript : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public ScrollRect yourScrollRect;
private Vector3 mousePosOnDragStart;
private bool passingEvent = false;
public void OnBeginDrag(PointerEventData pointerEventData)
{
mousePosOnDragStart = Input.mousePosition;
// Or something else you need to do at the start of the drag.
}
public void OnDrag(PointerEventData pointerEventData)
{
if ((Input.mousePosition - mousePosOnDragStart).sqrMagnitude > 1) // Checks if mouse has moved further than 1. Use your on logic here
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.beginDragHandler);
passingEvent = true;
}
if (passingEvent) // Don't send dragHandler before beginDragHandler has been called. It gives unwanted results...
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.dragHandler);
}
}
public void OnEndDrag(PointerEventData pointerEventData)
{
ExecuteEvents.Execute(yourScrollRect.gameObject, pointerEventData, ExecuteEvents.endDragHandler);
passingEvent = false;
}
}
Thanks, your answer solved my problem!
In my case, I had to use IScrollHandler instead of the ones you suggested, because I'm using a mouse wheel.
excellent thank you. I used OnScroll, and ended up with this which works a treat
public ScrollRect myVerticalScrollRect;
public void OnScroll(PointerEventData eventData)
{
_("OnScroll - passing on scroll message so buttons dont steal it");
//in order to have mousewheel scrolling and clickable buttons we need to intercept the scroll and pass it or it wont work!
//this is because buttons steal the focus and stop the scrolling.
ExecuteEvents.Execute(myVerticalScrollRect.gameObject, eventData, ExecuteEvents.scrollHandler);
}
Your answer
Follow this Question
Related Questions
Drag object and position(snap) on another similar one using Raycasting? 0 Answers
What's an easy way to align a UI element with a Raycast? 0 Answers
Help, Use event system IPointerClickHandler 1 Answer
Create UI raycast like a mouse Click 2 Answers
How do I asign this script to a button successfully? 1 Answer