Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Receive onClick event and pass it on to lower UI elements.

Discussion in 'UGUI & TextMesh Pro' started by perchik, Jan 26, 2015.

  1. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    I've built a dropdown list component that has an invisible overlay behind it [heavily based on this thread.] The idea is that when you click the drop down list, the panel appears. If you select an item in the list, the top button is updated with the selected item [blue click in my image]. If you click off of the panel somewhere [green click], the invisible overlay receives the click and collapses the menu.

    The issue is when you click off of the panel ONTO another UI element. In my example, clicking in the red spot while the Menu is being shown properly collapses the menu, but does *not* call the onClick of the button. If I understand correctly, this is usually desired behaviour, as you usually want the top element to receive clicks and nothing below it. Is there anyway to forward that click on to the next UI element? Perhaps something with raycasting from the click position? Or maybe creating a new click at that point and executing it?

    ddl.PNG


    [Also, once I've gotten this dropdown perfected, I'll make it available for free, since it's heavily built on other community scripts]
     
    DragonCoder likes this.
  2. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Would be great if you could add it to this bitbucket repo when ready as i'm trying to put as many free scripts here as possible (both my own and others from the community)
    https://bitbucket.org/ddreaper/unity-ui-extensions

    As to your issue,without looking at your existing code, it is curious as to why the second button wouldn't receive an onclick event as the eventsystem should manage that.
    Does any event fire when the 2nd button is clicked?, I take it the combo is collapsing as it should when it does.

    The trick in trying to cause the click event yourself is that you need to know what was clicked to call the ExecuteEvents function. This is mormally handle by the GraphicsRaycaster and the pointer input system. So it's curious as to what is blocking that (unless you are suspending something when he combo is open)
     
  3. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    ddl2.png
    Here's a picture to clarify. The previous "invisible overlay" has been tinted green.

    When you click the dropdown, there's a screensized overlay panel [now tinted green]. When a click is received on that panel, the dropdown panel hides (if a click is recieved on the dropdown panel it hides, but also selects an item). The problem is that if you click on the "otherButton" while the overlay is shown, the overlay panel receives the click event and the button does not.
     
  4. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    (annoyingly enough: having nor read your thread and only responding based on our conversations) Have you looked into adding a canvas group to the overlay element and setting it to pass clicks thru? Currently researching if there is a better way.
     
  5. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    The canvas group will let clicks pass through, but then the overlay doesn't receive the click, so then you can click the button but the drop down panel doesn't close.
     
  6. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Any chance you can post the sample on a repo (git?) to try out?
     
  7. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
  8. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Humph.

    In, say, MacOS, you can't click thru and close the menu.

    If, on a Mac, you open a menu, you can move the pointer around and the menu panel stays open.

    If I click outside of the panel, the panel closes, but any UI element under the mouse is NOT clicked.

    You have already replicated this. You have a stretched panel that accepts a click and closes the current menu.

    I guess you've reached the current capability of a professional OS!

    How imperative is it that you need to click off the menu and successfully activate a UI element in the same click?
     
  9. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    It's not imperative, but I feel it's still incorrect from a usability perspective. When the "overlay" is active, and you click the Other Button, nothing happens to indicate to the user that they actyually clicked off of the dropdown and need to click the Other Button again. Remember, the overlay is usually completely clear, not green tinted.

    There's a good chance that never occurs in practice, but now it's more of a nagging problem that I want to solve :D
     
    Last edited: Jan 27, 2015
  10. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Out of curiosity... Are you using windows? If so, what is the behaviour of your operating system? I'm not in the state where I can re-roll into windows... but I'd be curious how Win behaves.

    Intellectually, I see where you're coming from, but I've never felt I've lost any functionality, and OSX behaves as we've described above...

    ... you may be able to just skip this step and have what people expect.
     
  11. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    I did find a similar case on Windows and it does what mine does... one click to close the dropdown and another to interact with the UI element. On Windows, it's even worse, because the onHover state of the button is triggered, so it's even more convincing that you should be clicking it!

    I suppose you're right and that's just accepted functionality. I'm still curious as to how you'd go about manually triggering UI elements or manually creating a Click from script, but nothing show stopping.

    Now, onto making this a text box with a dropdown field....
     
  12. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    (^_^) If I think of anything that is not a complete hack, I'll let you know. I did pester the UI Team as well, as I'm going to be teaching a class on menus and menu systems and I was curious, and no one had an easy answer...
     
  13. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    I'd say this is a fairly common scenario, whether it's an app / game or even web UI.
    When you have a combo box, you should be able to close the dropdown whenever you click away from it, if you also click on another button, the closing of the combobox shouldn't interfere with that.

    Thanks for the code @perchik will check that out later tonight (peeked, euch, prefabs. Been looking for a ComboBox solution that didn't require prefabs, or at least a dual mode one, where you can use text OR a prefab :D)

    Hmm more food for thought
    p.s. did you check the other combobox solutions on the scripts forum post? (http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/) It's where most of the script for my UI Scripts repository have come from or evolved from. (https://bitbucket.org/ddreaper/unity-ui-extensions)
     
  14. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    Yes, my combo box was based on the one from that thread; it seems to have the same problem. I purposefully made it use prefabs, one of the scripts I've found creates the entire box from script, along with all the UI properties. If you're going to do that (and not read anything in from a file) it's the same every time, and it might as well be a prefab. I'm not sure why so many people are so anti-prefab, when it works just fine.

    In any case, I prefer to separate logic of "drawing the component" from the logic of what the component does. Prefab is the best way to accomplish that, but that's not the point of this thread ;)
     
  15. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Now Anti-Prefab, they have their uses for styling or instancing. But in UI they don't play so well at the moment.
     
  16. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Simon: I've not encountered any issues with prefabs and the UI, but then again, I may have a different work-flow.

    What issues are you having? I'm curious and I'd like to have a look-see.
     
  17. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    None myself per-se, just a lot of feedback I've seen in this area and a few issues reported @Adam Buckner hopefully it'lll get better over time. Still encouraging devs to report errors when they find them.
    (P.S. that was supposed to say NOT against prefabs :D)
     
  18. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    We use prefabs heavily in our UI system. Each piece of a menu is a prefab, usually with the base piece being a panel. With the correct logic for updating the rectTransform, it actually works great for us.
     
  19. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Great to hear some positive comments on the use of Prefabs with UI @perchik
    In what way are you using the prefabs, are you using them multiple times as instances, or just singular styled controls?
     
  20. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    In general i use prefabs to set the style of my UI. I will set up my buttons, panels, and other common UI elements… And save these as a prefab.

    Now that I have a prefab that has my graphics and typefaces properly set, I use these for instantiating my UI as I'm building at any time.

    Now, if I want to make a change to my "style", I change the base prefab and this propagates throughout my entire scene ... or thru all the scenes in my game.

    The one thing I have to be careful with in this workflow is to not create too many nested prefabs, and to not override any of the details, so prefab changes propagate properly.
     
  21. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    Adam's use is similar to what we do. We have a "Stylesheet prefab" that has all of our correctly styled UI element prefabs, then we use those when building menus.

    Before describing our use case, I should mention that our "game" is a serious game for physical rehabilitation. Therefore the majority of our UI is for the therapist to customize how the gameplay works, so it's almost a windows-Form like environment, inside of Unity. It consists of multiple pages of menus and settings.

    Our system has two types of "Menu" objects -> containers and menus. A container can have one or more menu objects displayed inside of it. For example, the first time most menus are used is in a Wizard process, that has a breadcrumb trail at the top and forward/back buttons at the bottom. From the Unity perspective, this is a panel that has a big empty space in the middle, with a panel at the top for the breadcrumbs, and a panel at the bottom with the buttons. The big empty space is also a Panel. The container object is responsible for the logic of which menu to show in the container next (or previously) and gets those menus from prefabs. Later in the use case, all of the same menus can be pulled up from the pause menu, and we reuse the prefabs, but they're set with the Canvas as the parent instead of the wizard container content. With the dynamic sizing of things, the menus work both inside the Container and as a top level component.

    ********************

    In our overall system, we don't use prefabs for everything like some games. If we really just need a gameobject with a specific component on it, it's usually created at runtime. The only prefabs that we use are ones that have more complex gameobject/component relationships, or visual objects in the scene.
     
  22. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    In other news, almost done with the textbox with suggestions! ComboAndDDl.png
     
  23. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    That is shaping up to be really good @perchik , keep that repo updated.
    Planning to do an update of the UI extensions repo over the next day or so
     
  24. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    Any update @perchik ?
    Finalising the updated release now including Editor menu commands to create all controls for this release
     
  25. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    I'm still cleaning up the script, I'll update this thread once it's done
     
  26. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,689
    OK, your current version is already in there. Can always update it later depending how far you are off
     
  27. perchik

    perchik

    Joined:
    Jul 25, 2012
    Posts:
    19
    The original one re-instantiates everything almost every frame, (based on the other forum combobox thread) I've since fixed that issue
     
  28. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    198
    Hey, Perchik, are you a student from Kiev?

    Bonus points if you get the reference!!
     
  29. jonaslindberg

    jonaslindberg

    Joined:
    Nov 28, 2017
    Posts:
    5
    This is how I solved passing on any PointerEvent type to the next Raycast-blocking item underneath. It's easy to implement some check for a specific Tag, Component etc. if you have many blocking objects stacked...

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.EventSystems;
    6.  
    7. public class PassOnPointerEvent : MonoBehaviour, IPointerDownHandler, IDragHandler, IBeginDragHandler, IPointerUpHandler, IEndDragHandler
    8. {
    9.     GameObject newTarget;
    10.  
    11.     public void OnPointerDown(PointerEventData eventData)
    12.     {
    13.         List<RaycastResult> raycastResults = new List<RaycastResult>();
    14.         EventSystem.current.RaycastAll(eventData, raycastResults);
    15.         newTarget = raycastResults[1].gameObject; //Array item 1 should be the one next underneath, handy to implement for-loop with check here if necessary.
    16.         print($"Passing on click to {newTarget}"); //Just make sure you caught the right object
    17.  
    18.         ExecuteEvents.Execute(newTarget, eventData, ExecuteEvents.pointerDownHandler);
    19.     }
    20.  
    21.     public void OnPointerUp(PointerEventData eventData)
    22.     {
    23.         List<RaycastResult> raycastResults = new List<RaycastResult>();
    24.         EventSystem.current.RaycastAll(eventData, raycastResults);
    25.         newTarget = raycastResults[1].gameObject; //Array item 1 should be the one next underneath, handy to implement for-loop with check here if necessary.
    26.         ExecuteEvents.Execute(newTarget, eventData, ExecuteEvents.pointerUpHandler);
    27.     }
    28.  
    29.     public void OnBeginDrag(PointerEventData eventData)
    30.     {
    31.         ExecuteEvents.Execute(newTarget, eventData, ExecuteEvents.beginDragHandler);
    32.     }
    33.  
    34.     public void OnDrag(PointerEventData eventData)
    35.     {
    36.         ExecuteEvents.Execute(newTarget, eventData, ExecuteEvents.dragHandler);
    37.         //Don't update newTarget...
    38.     }
    39.  
    40.     public void OnEndDrag(PointerEventData eventData)
    41.     {
    42.         ExecuteEvents.Execute(newTarget, eventData, ExecuteEvents.endDragHandler);
    43.     }
    44. }
    45.  
    Hope it can help someone!
     
  30. lucbloom

    lucbloom

    Joined:
    Mar 27, 2020
    Posts:
    44
    Sorry for necro posting, but I was searching for this and am grateful. I would like to contribute to the community. This is a component that can catch a left, right or middle click without blocking the other variants:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3. using System;
    4. using System.Collections.Generic;
    5.  
    6. public class AltClick : MonoBehaviour, IPointerClickHandler
    7. {
    8.     public Action LeftClickAction;
    9.     public Action MiddleClickAction;
    10.     public Action RightClickAction;
    11.  
    12.     public void OnPointerClick(PointerEventData eventData)
    13.     {
    14.         if (eventData.button == PointerEventData.InputButton.Left)
    15.         {
    16.             ExecuteOrPropagate(eventData, LeftClickAction, ExecuteEvents.pointerClickHandler);
    17.         }
    18.         else if (eventData.button == PointerEventData.InputButton.Middle)
    19.         {
    20.             ExecuteOrPropagate(eventData, MiddleClickAction, ExecuteEvents.pointerClickHandler);
    21.         }
    22.         else if (eventData.button == PointerEventData.InputButton.Right)
    23.         {
    24.             ExecuteOrPropagate(eventData, RightClickAction, ExecuteEvents.pointerClickHandler);
    25.         }
    26.     }
    27.  
    28.     void ExecuteOrPropagate<T>(PointerEventData eventData, Action clickAction, ExecuteEvents.EventFunction<T> pointerClickHandler) where T : IEventSystemHandler
    29.     {
    30.         if (clickAction != null)
    31.         {
    32.             clickAction();
    33.             return;
    34.         }
    35.  
    36.         var raycastResults = new List<RaycastResult>();
    37.         EventSystem.current.RaycastAll(eventData, raycastResults);
    38.         var nextObjectIndex = 0;
    39.         for (int i = 0; i < raycastResults.Count; ++i)
    40.         {
    41.             if (raycastResults[i].gameObject == gameObject)
    42.             {
    43.                 nextObjectIndex = i + 1;
    44.                 break;
    45.             }
    46.         }
    47.         if (nextObjectIndex >= raycastResults.Count)
    48.         {
    49.             return;
    50.         }
    51.  
    52.         ExecuteEvents.Execute(raycastResults[nextObjectIndex].gameObject, eventData, pointerClickHandler);
    53.     }
    54. }
    You can use it as follows:
    Code (CSharp):
    1. gameObject.AddComponent<AltClick>().RightClickAction = () => OnRightClick(this);
    PS: Unity could add a function "Propagate()" to the "AbstractEventData" class, so they can handle it in their own loop without the need for a 2nd raycast. Meh, it's not very useful for most users.
     
    Last edited: Feb 1, 2024