Add/implement pc.EVENT_MOUSEOUT and pc.EVENT_MOUSEENTER
Fixes: https://github.com/playcanvas/engine/issues/739
This event is missing, so devs always had to add custom event handling in their scripts, like here:
https://github.com/playcanvas/engine/blob/b07a438362a7a7e8ea4c9353dafecc808640556a/scripts/camera/orbit-camera.js#L378-L388
This will also simplify event handling later on based on https://github.com/playcanvas/engine/issues/4910, in short ScriptType#listen provides:
- Less code
- Automatically removing events on
disable/destroy - Automatically adding them back on
enable - Harder to get it wrong
I confirm I have read the contributing guidelines and signed the Contributor License Agreement.
I also noticed that mouse out event fires in unexpected situations such as:
When I click drag and move out of the window, I get the event on the move out and also on button release
https://user-images.githubusercontent.com/16639049/207322718-6f046f0c-020c-4efd-ae85-81523987494f.mp4
When the PlayCanvas window is not in focus and moving between the active browser window and the PlayCanvas one
https://user-images.githubusercontent.com/16639049/207322812-a76654d9-8bae-4e09-8dbb-d3be1312020a.mp4
@yaustar Thank you for checking, it seems to be default behaviour, I get the same in Firefox and Chrome.
The easiest way to test it is any empty page and then just:
window.onmouseout = e => console.log(e.target.tagName);

I don't know why browsers do it, but do you think PC events should try to filter such cases?
I don't know why browsers do it, but do you think PC events should try to filter such cases?
I'm in two minds about this. On one hand, it makes sense to mirror the native events like we do everywhere else. On the other, it would be nice to have mouse in and mouse out instead of both being on the same event?
On the other, it would be nice to have mouse in and mouse out instead of both being on the same event?
Yea, I share your concerns here, seems like this works best:
document.body.onmouseout = console.log;
It isn't confused between BODY/HTML, but what I don't like is that it's accessing body which can be undefined (coming from a <script> in <head> e.g.)
document.body.onmouseout = console.log;
Yeah, let's not do that as the mouse DOM element can be any DOM element too on attach.
Wait, is target element not used in the mouse input? 👀 We just listen the window regardless?
Wait, is target element not used in the mouse input? :eyes: We just listen the window regardless?
Right, _target is just used for some context menu handling and position calculations, the rest attaches events onto window:
https://github.com/playcanvas/engine/blob/c3d5ff7ca2103490f463c5a24703617493b32e57/src/platform/input/mouse.js#L91-L102
Can we use it to know if we are 'mousing out' or 'in'?
Can we use it to know if we are 'mousing out' or 'in'?
You mean to have e.g. a small 100x100px canvas and then trigger mouseout when this small area is left and not the window?
It should be possible, just that it would deviate from how e.g. the OrbitCamera script used to use it (if we speak about the same thing here).
I tested window.onmouseenter but that doesn't fires at all for me. Having a pc.EVENT_MOUSEIN should still be quite easy using the mouse move events tho (or _target of pc.app.mouse)
It should be possible, just that it would deviate from how e.g. the OrbitCamera script used to use it (if we speak about the same thing here).
That's fine, I wrote that years ago when I wasn't really thinking that closely where I wanted mouse out 😅
Yea, it seems more sensible to have it on Mouse#_target as it will behave less surprisingly.
(I can rewrite it tomorrow if no one comes up with a counter-argument :sweat_smile:)
@kungfooman Is this ready for a re-review?
@kungfooman Is this ready for a re-review?
Yep, I would like that, quick intro:
pc.app.mouse.on(pc.EVENT_MOUSEENTER, console.log);
pc.app.mouse.on(pc.EVENT_MOUSEOUT , console.log);
or:
pc.app.mouse.on(pc.EVENT_MOUSEENTER, _ => console.log("mouse enter", _));
pc.app.mouse.on(pc.EVENT_MOUSEOUT , _ => console.log("mouse out" , _));
If the window is not in focus, when the mouse enters, it fires both the ENTER and OUT events (see end of the video). Was this intended?
https://user-images.githubusercontent.com/16639049/209349691-b30fcc81-4ae5-454e-b941-5a853dd48665.mp4
If the window is not in focus, when the mouse enters, it fires both the ENTER and OUT events (see end of the video). Was this intended?
Thank you for testing! It might be a Chrome bug on Mac, I can't reproduce on Linux/Chrome:

Definitely Chrome specific but it's difficult to ship this when it 'doesn't work' with the major browser user share 😅
Edit: Wondering if we should only fire the event if the window is in focus?
Yeah, I also remember having issues with onmouseout in the past. At least on Windows. Have you tried onmouseleave?
I reported mouse issues in the past and it usually takes some time until issues are fixed, but imo that is the way to go - just someone needs to create a Chrome issue and keep up with it.
Issue 356090: mouseout on element triggered on mouseup after entering the element with the mouse button pressed
- https://bugs.chromium.org/p/chromium/issues/detail?id=356090
Sounds like they had it before, apparently "fixed", but not quite it seems.
For my sake it works good enough besides this have-another-window-over-canvas and I'm not trying to find Chrome specific hacks for that case.
Edit: Wondering if we should only fire the event if the window is in focus?
Just for testing:
setInterval(()=>console.log(document.hasFocus()), 1000)
But I don't see how that would work out nicely, as soon you click outside the tab you won't have any "entering" events anymore?
But I don't see how that would work out nicely, as soon you click outside the tab you won't have any "entering" events anymore?
There aren't any mousemove events on the app if the tab isn't in focus either so I would argue it's consistent behaviour?
There aren't any mousemove events on the app if the tab isn't in focus either so I would argue it's consistent behaviour?
The mousemove events are always fired, focused or not:
pc.app.mouse.on("mousemove", console.log);
E.g. for devtools in https://playcanvas.com/viewer if you want to test. Or do you mean something different?
Hmm, I get different behaviour on Chrome/Mac
https://user-images.githubusercontent.com/16639049/210997265-996da4bb-c4b2-45cc-b4cd-302553a1b154.mp4
Funny how different the same browser is on different platforms:
https://user-images.githubusercontent.com/5236548/211007086-27078234-4184-4ecf-bbf2-016fc7620bfd.mp4
IIRC Chrome on Windows also sends mousemove even though the window is unfocused, so Mac is the oddball here.
Technically it imo makes more sense to not filter events too early, since once they are filtered, there is no way back. And for a dev it would be simple enough to just if (!document.hasFocus()) return; if they want that?
Technically it imo makes more sense to not filter events too early, since once they are filtered, there is no way back. And for a dev it would be simple enough to just if (!document.hasFocus()) return; if they want that?
Part of me feels like the engine should be as consistent as possible in the events it fires. If a developer wants the raw events, then there's nothing stopping them registering events on the DOM
Part of me feels like the engine should be as consistent as possible in the events it fires.
So mousemove shall also be filtered now to make everything work like on a Chrome Mac?
So mousemove shall also be filtered now to make everything work like on a Chrome Mac?
Ideally, it should be consistent otherwise we would end up with bug reports in the engine.
The fact that mouse move is different on a per OS basis is frustrating and truthfully, I don't know what the best way forward here is.
We could ship this as is where the behaviour is consistent across OS's browser when the tab is in focus by the user. I think that would be okay given noone has reported about mouse move being an issue when the browser is not in focus.
Thank you for testing! It might be a Chrome bug on Mac, I can't reproduce on Linux/Chrome:
It works fine if the active window is not another Chrome window. That's probably why you can't reproduce it as you are checking against VSCode
It works fine if the active window is not another Chrome window.
Did you test that on Linux? I tried with another tab of Chrome and I couldn't trigger the bug either. Also tried it with other apps like KolourPaint and it worked nicely.
So far I believe we could just point out it only happens on Chrome/Mac, which should be fixed in Chrome/Mac's OS code (until someone brings this issue to some Mac developer attention).
Did you test that on Linux? I tried with another tab of Chrome and I couldn't trigger the bug either. Also tried it with other apps like KolourPaint and it worked nicely.
On have access to Mac unfortunately 😅 Either way, I don't think it's a blocker for the PR
@kungfooman - any thoughts on the last comments here?
Yea, it's kinda confusing, because mouseout triggers when entering other elements:
However, good catch @willeastcott, because the EVENT_MOUSEENTER description should be more specific.
I will rewrite it to make it clearer, also happy about suggestions.
Looking like this in VSCode: