Saturday, February 26, 2011

Mouse Click Events: Adding Wiggle Room

MouseListeners have a mouseClicked() method, but it is only triggered if the mouse does not move between the mouse being pressed and later being released.

This is working as designed, but as touchpads and tablets become more available: I think this design is outdated. It is very easy on certain devices to press the mouse, accidentally move the mouse, and then release the mouse. (Even the word "mouse" here is outdated: we're dealing with fingers and tablet pens.)

The good news is: this does not affect buttons. JButtons use a more complicated model to trigger any associated ActionListeners (based on ButtonModels, armed and pressed states). The issue I'm describing only relates to when MouseListener.mouseClicked() is called.

Here is an applet that demonstrates the problem, and a potential solution:



When mouseClicked() is called the color changes to green. If it is called again then the color changes to yellow.

If you press the mouse down and move it a couple pixels before releasing: one label will highlight green and the other will not. (Based on one mouse click neither label should turn yellow: if it does then two calls to mouseClicked() are being made, and that's a serious bug.)

The pixel tolerance is a global static field. I picked 10 as an arbitrary number; you can fine tune this as needed.

Implementation


The ClickEventEnabler has only one method: install().

The current implementation just adds one AWTEventListener to the Toolkit. When mousePressed() and later mouseReleased() is called: we check to see if the distance between the mouse pressed and released location is too large.

If it is within an acceptable range, we create a Runnable to invoke later. If in the meantime a call mouseClicked() comes in for the same component: the runnable is cancelled. So if the user doesn't move the mouse, or if future Java implementations address this concern: this event listener won't send a redundant mouseClicked() event.

If everything passes inspection: then we create our own MouseEvent.MOUSE_CLICKED event and dispatch it ourselves.

So this requires top-level access to the event queue, but aside from possibly dispatching click events: it is very benign.

In the future: it might be useful to implement a similar change to recognize double-clicks. For now I'm pilot testing this single-click solution.

The javadoc link above references the source code, but there's also a jar to download here.

No comments:

Post a Comment