Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change mouse button config to a function (e: Event) => ACTION #459

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from

Conversation

jamesnakagawa
Copy link

Regarding #458,

I propose a new structure for configuring mouse buttons. Instead of a configuration object, user can pass a configuration function which receives an Event (either wheel event, click event, or contextmenu event) and which returns the intended action they want. With this, user can access event.shiftKey and all other modifier key flags and reliably set them without overly verbose configuration.

I have also updated the example at examples/mouse-drag-with-modifier-keys.html to show how it could be used.

Lastly, I wrote a custom setter for the previous configuration, mouseButtons, so that this is not a breaking change.

I'm not familiar with the coding conventions of this project, so please excuse if I didn't document something correctly. I look forward to hearing your feedback on my proposal!

Comment on lines +740 to +742
this._isDragging && ( event.buttons & MOUSE_BUTTON.ALL ) > 0 ) {

this._state = this._state | this.mouseButtons.middle;

}

if ( this._isDragging && ( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {

this._state = this._state | this.mouseButtons.right;
this._state = this._state | this.mouseEventToAction( event );
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to simplify this conditional because of this change.

( event.buttons & MOUSE_BUTTON.ALL ) > 0 looks a little weird, and technically it would exclude event.buttons === 8 || event.buttons === 16, which is not a common use case. I wanted the result of the function to match previous behaviour exactly.

Comment on lines +1022 to +1024
} else if ( ! this._lockedPointer && ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ||
( event.buttons & MOUSE_BUTTON.MIDDLE ) === MOUSE_BUTTON.MIDDLE ||
( event.buttons & MOUSE_BUTTON.RIGHT ) === MOUSE_BUTTON.RIGHT ) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not use MOUSE_BUTTON.ALL here because having ! this._lockedPointer && changes the logic.

@jamesnakagawa jamesnakagawa changed the title Change mouse button config to a function (e: Event) => ACTION Change mouse button config to a function (e: Event) => ACTION Sep 23, 2023
@jamesnakagawa
Copy link
Author

@yomotsu Just wondering if you had a chance to see this?

@yomotsu
Copy link
Owner

yomotsu commented Sep 29, 2023

Sorry for the delay, I've been busy with house moving.
And thank you for the PR.

This contains a lot change than I thought...and going to be a breaking change (no compatibility with older versions).
Let me confirm a little bit:

  • How do we handle if the user presses with "A" key or others, like, rotate for the R key, translate for the T key, etc. This could be multiple keys at the same time. (Therefore, the demo code was handling additional keys without MouseEvents)

  • Camera Controls accepts touch events via Pointer Events too. Can mouseEventToAction handle any pointer events?

@jamesnakagawa
Copy link
Author

jamesnakagawa commented Sep 29, 2023

引っ越しおめでとう!

Thank you for the response.

  1. If I understand correctly, using "R" for rotate, "T" for transform, means that pressing once would change the mode but it does not need to be held down?

In that case, it could be done using closure:

let mode = 'rotate';

document.addEventListener('keypress', e => {
    if (e.key === 'R' || e.key === 'r') {
        mode = 'rotate';
    } else if (e.key === 'T' || e.key === 't') {
        mode = 'translate';
    }
});

cameraControls.mouseEventToAction = event => {
    if ( ( event.buttons & MOUSE_BUTTON.LEFT ) === MOUSE_BUTTON.LEFT ) {
        if (mode === 'translate') {
            return ACTION.TRUCK;
        }
        if (mode === 'rotate') {
            return ACTION.ROTATE;
        }
        // etc
    }
};
  1. For PointerEvent, it subclasses MouseEvent and properly sets event.buttons and event.shiftKey/metaKey/etc, so I see no incompatibility here.

  2. Re: breaking change. Now that I think about this more, you are right, you can't set deep values using this compatibility bridge and turning mouseEventToAction back into mouseButtons is quite complex. In any case, probably not worth it.

Could this wait for the next major version of camera-controls?

Otherwise, the alternative would be to extend mouseButtons like this:

this.mouseButtons = {
	left: ACTION.ROTATE,
	middle: ACTION.DOLLY,
	right: ACTION.TRUCK,
	wheel: ACTION.DOLLY,
	'left.ctrl': ACTION.TRUCK,
	'left.shift': ACTION.TRUCK,
	'left.meta': ACTION.DOLLY,
	'left.ctrl.shift': null,
	'left.ctrl.shift.meta': ACTION.DOLLY,
};

Where a null/undefined value would indicate to inherit from the base setting. i.e 'left.ctrl.shift': null would inherit from 'left.ctrl': ACTION.TRUCK instead.

This would be much more verbose, which is why I think you didn't want this feature in the first place.

@jamesnakagawa
Copy link
Author

@yomotsu Any thoughts on this? I'm happy to do another PR or make changes based on the direction you'd like to go.

@yomotsu
Copy link
Owner

yomotsu commented Oct 12, 2023

Sorry again for the delay.
Could this be a none break change PR?
I mean,

  • It's okay to add the interrupting feature as an advanced usage.
  • However, I also would like to keep the simple setting feature.

Also, what do you think about the following:

Renaming MOUSE_BUTTON.ALL to MOUSE_BUTTON.ANY.
Renaming mouseEventToAction to pointerEventToAction and can handle other pointer events such as touch and pen.

@jamesnakagawa
Copy link
Author

We could do it that way, yes. If pointerEventToAction == null or undefined, then use mouseButtons, otherwise mouseButtons will be ignored. Is that what you were thinking?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants