Custom Button Control with Right Click

I am trying to make a custom button as talked about here Can the UI buttons detect a right mouse-click?

Here is my Custom Button Class

using UnityEngine.EventSystems;
using UnityEngine.Scripting;
using UnityEngine.UIElements;

public class BetterButton : Button
{
#region UXML
    [Preserve]
    public new class UxmlFactory : UxmlFactory<BetterButton, UxmlTraits>
    {
    }

    [Preserve]
    public new class UxmlTraits : VisualElement.UxmlTraits
    {
    }
#endregion

    public UnityEngine.Events.UnityEvent onRightClick = new UnityEngine.Events.UnityEvent();
    public UnityEngine.Events.UnityEvent onLeftClick = new UnityEngine.Events.UnityEvent();
    public UnityEngine.Events.UnityEvent onMiddleClick = new UnityEngine.Events.UnityEvent();

    public override void OnPointerClick(PointerEventData eventData)
    {
        if (eventData.button == PointerEventData.InputButton.Left)
        {
            // Invoke the left click event
            base.OnPointerClick(eventData);
            //onLeftClick.Invoke();
        }
        else if (eventData.button == PointerEventData.InputButton.Right)
        {
            // Invoke the right click event
            onRightClick.Invoke();
        }
        else if (eventData.button == PointerEventData.InputButton.Middle)
        {
            // Invoke the right click event
            onMiddleClick.Invoke();
        }
    }
}

but it says " ‘OnPointerClick(PointerEventData)’: no suitable method found to override"

I know this must be a simple fix but ive been racking my brain for days now

It looks like you have mixed elements from UGUI and UI Toolkit.
The example you are linking to is for UGUI, UI Toolkit is a different system and the Button class is different. It does not have a OnPointerClick method to override.
Instead, you don’t need to create a new button class, just use a normal Button and register for the mouse-down event:

I tried this

Slot = SlotUI.Instantiate();
Slot.<Button>("InteractionButton").Registercallback<MouseDownEvent>(evt => MouseClick(evt));
Invholder.Add(Slot);

But I can’t figure out how to get the .button and .currentTarget properties within the MouseClick function

You can do var button = evt.target as Button;
You could also pass it into the event, e.g

var button = Slot.Q<Button>("InteractionButton");
button.RegisterCallback<MouseDownEvent>(evt => MouseClick(evt, button));

Okay that works for the target but what about the button (left, right or middle mouse button) I still can’t access the mousedownevent.button property

It should be evt.button. does that not work?

Thank you that works, I had the type as EventData not MouseDownEvent.

Although this approach triggers when the button is “pressed” not “clicked” is there a simple way to work like the clickable.clicked event where it requires a MouseDownEvent and a mouseupevent on the same object? Or would I have to duplicate this code and have global flags to check if an object had both events triggered?

This is why I wanted to make my own custom button so this functionality was baked in

You can use the Clickable manipulator.

var clickable = new Clickable(OnClick);
clickable.activators.Add(new ManipulatorActivationFilter { button = MouseButton.MiddleMouse });
clickable.activators.Add(new ManipulatorActivationFilter { button = MouseButton.RightMouse });
button.AddManipulator(clickable);

I’ve tried that too but nothing in the documentation says how to get clickable to call a different function depending on the button pressed, a right click should call func1 and a left click should call func2 ect

You can use multiple manipulators. Add one for right click and one for middle click.

Hello there, I just wanted to do sth similar, and found the workflow with adding new activators on another thread. This seems to only work half way though.
Adding the activators caused my button to react visually (with a color transition added in my uss-style)
But the ClickEvent is never propagated to the callback that’s registered on the button.

_identifierLabelButton.clickable.activators.Add(new ManipulatorActivationFilter() {button = MouseButton.LeftMouse, modifiers = EventModifiers.None});
_identifierLabelButton.clickable.activators.Add(new ManipulatorActivationFilter() {button = MouseButton.LeftMouse, modifiers = EventModifiers.Alt});
_identifierLabelButton.clickable.activators.Add(new ManipulatorActivationFilter() {button = MouseButton.RightMouse, modifiers = EventModifiers.None});
_identifierLabelButton.RegisterCallback<ClickEvent>(LabelButtonClickedHandler);
//...

private void LabelButtonClickedHandler(ClickEvent evt)
{
    Log.Info("C"); //<- never printed on right click... But works for left-click & left-click + alt
    switch (evt.button)
//...

UnityVersion: 2023.2.20f1
Unity UI Version: 2.0.0

Update:
Assigning a new Clickable to the button with a callback directly being set propagates right clicks, but as you can’t cast BaseEvent to ClickEvent (Exception being thrown) and the BaseEvent not delivering any context about what modifier / button combination was being used does make that pretty much useless, as you can’t assign multiple “clickables” with different callbacks based on their activations. I don’t see any way to have one (ui-)button handling different (mouse-)buttons atm.
Correction: I simply overlooked the ‘Add Clickable as Manipulator’ part. But one thing still stands, you only get an EventBase. How do I know which modifier was pressed along my mouse-button, do I really need to register a extra callback for each combination of mouse-button/modifier?!
Why doesn’t the default btn.RegisterCallback callback is invoked for additional activators with proper context-info, it should be able to detect right/middle clicks alongside modifiers…
What is the button-Index of ClickEvent for when there’s no easy way to use it with other buttons than left one? Do I need to construct the click event myself somehow with a mix of UITK / IMGUI code? This is so frustratingly chaotic (+verbose)…

1 Like
    public void InitedPage(VisualElement root)
    {
        PropertyEx.Panel.RegisterCallback<ClickEvent>(OnPanelClick);
    }
    //
    private void OnPanelClick(ClickEvent evt)
    {
        Vector2 position = PositionTransfer(evt.position);
        switch (evt.button)
        {
            case 0:
                OnPanelClickLeft(position.x, position.y);
                break;
            case 1:
                OnPanelClickRight(position.x, position.y);
                break;
            case 2:
                OnPanelClickMiddle(position.x, position.y);
                break;
        }
    }

Why can’t this approach listen for middle and right mouse button events? This is obviously unreasonable, as the click event isn’t triggered at all when the middle or right mouse buttons are clicked.

As per the docs: https://docs.unity3d.com/ScriptReference/UIElements.ClickEvent.html

You need to use manipulators for the other buttons.

It’s worked.But how can i get the click position?

Just register to this delegate rather than the normal clicked one: Unity - Scripting API: UIElements.Clickable.clickedWithEventInfo

1 Like