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

MouseWatcherRegion issues related to mouse input #843

Epihaius opened this issue Jan 12, 2020 · 3 comments

MouseWatcherRegion issues related to mouse input #843

Epihaius opened this issue Jan 12, 2020 · 3 comments


Copy link

@Epihaius Epihaius commented Jan 12, 2020


What follows is a list of what I would consider to be problematic behaviour of MouseWatcherRegions, caused by the current way of handling mouse button presses, as well as a suggestion that could add more user-control over this behaviour.

As MouseWatcherRegions are fundamental components of DirectGui widgets, these are equally affected by these issues. So let's start by examining what happens when one presses a DirectButton and drags the mouse around while holding down the left mouse button (LMB):

  1. When the mouse is dragged over other DirectButtons (or MouseWatcherRegions), these do not respond. Only the enter and leave events of the pressed DirectButton are fired. Whether this is desirable or not depends on the use case (see below), but I think we can agree that for most purposes this is indeed what one would want.
  2. While keeping the LMB down, press and release any other mouse button (or roll the mouse wheel); suddenly, all other DirectButtons and MouseWatcherRegions will react to mouse-over events, which can easily lead to bugs in the user code. In the case of a DirectButton, this already causes a serious visual bug: even when the LMB is released, the DirectButton will show its press state when mousing over it instead of its rollover state. Only left-clicking it again can undo this. And when the LMB is released over the DirectButton itself, its associated command is not executed in this case. Checking if another button gets pressed (e.g. the RMB, by trying to catch the mouse3 event) seems to not work either while the mouse is over the DirectButton, so it is hard to handle this manually.
  3. When the mouse is moved (not dragged) out of a MouseWatcherRegion across a side that lies on the border of a DisplayRegion, the leave event is fired twice in succession, which may also cause problems for associated code that should be called only once.

Regarding 1., it may sometimes be useful to have other MouseWatcherRegions respond to mouse-overs while dragging the mouse over them, e.g. in an RPG-like context where one would like to drag&drop an item icon into a grid-like inventory implemented using MouseWatcherRegions. Another example would be a GUI widget such as a list made up of separate "cell" widgets where one could multi-select several entries at once by dragging over them.
For such cases, perhaps it might be possible to set an always_active flag (or something to that effect) on a MouseWatcherRegion to prevent it from being deactivated when a mouse button is held down?

Here is some code to reproduce the above issues:

from panda3d.core import *
from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectGui import *

showbase = ShowBase()

def command():

    print("Button pressed")

for i in range(4):
    text = ("Button {}".format(i), "Pressed {}".format(i),
        "Hilited {}".format(i), "Disabled {}".format(i))
    pos = (-.4, 0., .65 - i * .4)
    DirectButton(text=text, text_scale=.1, borderWidth=(.05, .05),
        pos=pos, command=command)

def on_region_enter(*args):

    name = args[0].get_name()

    if name.startswith("region"):
        print("Entered {}".format(name))

def on_region_leave(*args):

    name = args[0].get_name()

    if name.startswith("region"):
        print("Left {}".format(name))

def on_right_down():

    print("Right mouse button pressed")

cm = CardMaker("cell")
r = .2
t = .1
cm.set_frame(-r, r, -t, t)
parent = showbase.render2d
mouse_watcher = showbase.mouseWatcherNode
showbase.accept("region_enter", on_region_enter)
showbase.accept("region_leave", on_region_leave)
showbase.accept("mouse3", on_right_down)

for i in range(4):
    pos = Point3(.4, 0., .65 - i * .4)
    cell = parent.attach_new_node(cm.generate())
    region = MouseWatcherRegion("region{}".format(i),
        pos.x - r, pos.x + r, pos.z - t, pos.z + t)

pos = Point3(-1. + r, 0., 1. - t)
cell = parent.attach_new_node(cm.generate())
region = MouseWatcherRegion("region_topleft",
    -1., -1. + r * 2., 1. - t * 2., 1.)

The white rectangles have associated MouseWatcherRegions to handle mouse-over events.
To reproduce 3., move the mouse over the white rectangle in the top left hand corner of the window and then move it out of the window.

This was tested on Windows.

It would be great if you'd want to look into this, thanks!


This comment has been minimized.

Copy link

@rdb rdb commented Jan 22, 2020

For future reference, please file separate issues for distinct bugs.

I investigated the issue with 2: upon a cursory glance, is that MouseWatcher sets _button_down back to false upon the release of any mouse button. It should rather keep track of which mouse buttons were pressed, and not clear the button-down-region until either last button is released, or the same button is released that initiated this.

The odd PGButton behaviour happens as a result of the above because the button has a different idea of what "pressed" constitutes than MouseWatcher. Observe this sequence of events:

  • The mouse hovers over the button, and an "enter" event is fired.
  • The left button is pressed.
  • The mouse leaves, and an "exit" event is fired, but the button remembers that its button was down.
  • The right button is pressed.
  • The right button is released. A "release" event is fired, which is sent to the button. The button ignores it, because the button isn't configured to accept right-click events.
  • The left button is released. Nothing happens, because due to the last point, the button is no longer considered the "button-down region".

There's a comment in the source that acknowledges that there is a problem:

    // There is some danger of losing button-up events here.  If more than one
    // button goes down together, we won't detect both of the button-up events
    // properly.

I'm unable to reproduce no. 3.


This comment was marked as off-topic.

Copy link

@rdb rdb commented Jan 22, 2020

Regarding 1, I can imagine that it might be possible to have a property set on a region to indicate whether it should capture the mouse or not. Or, the mousedown event handler should be able to decide whether it should capture or not.


This comment has been minimized.

Copy link
Contributor Author

@Epihaius Epihaius commented Jan 23, 2020

Alright, I have created a feature request for 1 and filed a separate issue report for 3 (it might be helpful if you could state on what operating system you tried and failed to reproduce 3 in a comment there; note that I added a different code sample there, which might be less confusing to test this particular issue with).

I investigated the issue with 2: upon a cursory glance, is that MouseWatcher sets _button_down back to false upon the release of any mouse button. It should rather keep track of which mouse buttons were pressed, and not clear the button-down-region until either last button is released, or the same button is released that initiated this.

Yes, that sounds like a good solution.
Thanks for looking into this!

@rdb rdb added the bug label Jan 24, 2020
@rdb rdb added this to the 1.10.6 milestone Mar 2, 2020
@rdb rdb self-assigned this Mar 18, 2020
@rdb rdb closed this in 43fa7ef Mar 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.