Couple of minor issues with Clickable

Related to Clickable.ProcessMoveEvent and MouseMoveEvent

I think code explains it easier

        protected override void ProcessMoveEvent(EventBase evt, Vector2 localPosition)
        {
            base.ProcessMoveEvent(evt, localPosition);

            if (evt is MouseMoveEvent moveEvent)
            {
                // BUG: this currently always returns 0 so can't differentiate between left/right buttons
                // BUG: middle mouse button never triggers ProcessMoveEvent either
                var button = moveEvent.button;

I should say I’m working around the left/right mouse button no problem storing it from the down event

        protected override void ProcessDownEvent(EventBase evt, Vector2 localPosition, int pointerId)
        {
            this.mouseButtonDown = evt is MouseDownEvent moveEvent ? moveEvent.button : -1;
        }

and using that instead

        protected override void ProcessMoveEvent(EventBase evt, Vector2 localPosition)
        {
            base.ProcessMoveEvent(evt, localPosition);

            if (evt is MouseMoveEvent)
            {
                switch (this.mouseButtonDown)

The Clickable manipulator has this in its constructor:

activators.Add(new ManipulatorActivationFilter { button = MouseButton.LeftMouse });

This means, it only responds to Left click. Not sure why Right click still works (kinda), but it shouldn’t at all.

I would inherit from MouseManipulator directly instead of Clickable. That way, you can set your own activation filter. Here’s an example:

That’s interesting, because Clickable.clickedWithEventInfo definitely fires on right click as I’ve been using it to pop up options menus on a editor window

            this.clickedWithEventInfo += evt =>
            {
                if (evt is MouseUpEvent mouseEvent)
                {
                    switch (mouseEvent.button)
                    {
                        case 0:
                            this.ClickedLeft?.Invoke();
                            break;

                        case 1:
                            this.ClickedRight?.Invoke();
                            break;
                    }
                }
            };

As for using MouseManipulator, I think I tried that originally but ran into an issue with something being internal, however this was a while ago and I have a better understanding of how things work now. If this is not the intended behavior of clickable I’ll definitely go have a look and try to re-implement it from that.

Background: I implemented this because I wanted to add some drag support while stopping clicks from triggering. Full code here:

/// <summary> Extended Clickable manipulator that provides dragging support. </summary>
    public class Draggable : Clickable
    {
        private readonly long clickGrace;
        private IVisualElementScheduledItem gracePeriodRepeater;

        private bool hasDragged;
        private bool allowClick;
        private int mouseButtonDown;

        /// <summary> Initializes a new instance of the <see cref="Draggable"/> class. </summary>
        /// <param name="clickGrace"> The grace period for clicking to avoid tiny movements stopping clicks. </param>
        /// <param name="delay"> <see cref="Clickable.m_Delay"/>. </param>
        /// <param name="interval"> <see cref="Clickable.m_Interval"/>.</param>
        public Draggable(long clickGrace = 200L,  long delay = 250L, long interval = 30L)
            : base(null, delay, interval)
        {
            this.clickGrace = clickGrace;
            this.activators.Add(new ManipulatorActivationFilter { button = MouseButton.RightMouse });

            this.clickedWithEventInfo += evt =>
            {
                if (evt is MouseUpEvent mouseEvent)
                {
                    switch (mouseEvent.button)
                    {
                        case 0:
                            this.ClickedLeft?.Invoke();
                            break;

                        case 1:
                            this.ClickedRight?.Invoke();
                            break;
                    }
                }
            };
        }

        /// <summary> Any mouse button is dragging. </summary>
        public event Action Dragging;

        /// <summary> Left mouse button dragging. </summary>
        public event Action DraggingLeft;

        /// <summary> Right mouse button dragging. </summary>
        public event Action DraggingRight;

        /// <summary> Left button clicked. </summary>
        public event Action ClickedLeft;

        /// <summary>  Right button clicked. </summary>
        public event Action ClickedRight;

        /// <summary> Gets the start position of the drag. </summary>
        [SuppressMessage("ReSharper", "SA1300", Justification = "Matching unity style of lastMousePosition")]
        [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Matching unity style of lastMousePosition")]
        public Vector2 startMousePosition { get; private set; }

        /// <summary> Gets the frames delta. </summary>
        public Vector2 Delta => this.lastMousePosition - this.startMousePosition;

        /// <inheritdoc/>
        protected override void ProcessDownEvent(EventBase evt, Vector2 localPosition, int pointerId)
        {
            this.hasDragged = false;

            this.startMousePosition = localPosition;
            base.ProcessDownEvent(evt, localPosition, pointerId);

            this.mouseButtonDown = evt is MouseDownEvent moveEvent ? moveEvent.button : -1;

            if (this.gracePeriodRepeater == null)
            {
                // Target is null in constructor so need to delay it
                this.gracePeriodRepeater = this.target.schedule.Execute(() => this.allowClick = false);
            }
            else
            {
                this.gracePeriodRepeater.ExecuteLater(this.clickGrace);
            }

            this.allowClick = true;
        }

        /// <inheritdoc/>
        protected override void ProcessMoveEvent(EventBase evt, Vector2 localPosition)
        {
            base.ProcessMoveEvent(evt, localPosition);

            if (evt is MouseMoveEvent)
            {
                this.Dragging?.Invoke();

                // moveEvent.button currently always returns 0 so can't differentiate between left/right instead stored on down
                switch (this.mouseButtonDown)
                {
                    case 0: this.DraggingLeft?.Invoke();
                        break;
                    case 1: this.DraggingRight?.Invoke();
                        break;
                }
            }

            this.hasDragged = true;
        }

        /// <inheritdoc/>
        protected override void ProcessUpEvent(EventBase evt, Vector2 localPosition, int pointerId)
        {
            base.ProcessUpEvent(evt, localPosition, pointerId);

            this.gracePeriodRepeater.Pause();

            // Because we are a repeating does not trigger
            if (this.target.ContainsPoint(localPosition) && (!this.hasDragged || this.allowClick))
            {
                this.Invoke(evt);
            }
        }
    }

Used in a node editor

6227589--685167--upload_2020-8-21_8-45-8.png

Where you can drag around the nodes, but right click to popup menus etc.

Anyway, thanks for the advice will have a look at the example.

1 Like