I'd like to know what object a swing MouseMotionEvent (or MouseReleased) ends in. The problem is that both the MousePressed and MouseReleased events go to the object that was under the "press", not the release.
Here's a contrived example that may explain better:
The user sees a screen with some balls and some baskets and is told to drag a ball to a basket. Each ball represents some entity in the application space and each basket represents some action to take in the application space. From the Swing point of view, the balls and baskets are implemented separately as highly-overridden JButtons. On mousePressed the ball stores its identity in a known place. I'd like mouseReleased to be caught by a MouseListener in the basket which checks up the balls identity in the known place and then goes off into program logic and do the task represented by that basket.
But as I understand Swing (actually the AWT) the mouseReleased event goes to the component that contained the mousePressed event (ie the ball). Other than looking at X and Y (which seems an atrocious kludge) how do I figure out which basket the mouseReleased happened in? (If the mouseRelease happened outside of any basket, I'll need to take some sort of default reset action. Than can be done by a mouseEvent handler in the underlying JPanel).
(Please don't tell me this is a poor interface. The example I've given is not real. It abstracts out the problem I have in a way that I think is easy to visualize and understand.)
If the mouseRelease happened outside of any basket, I'll need to take some sort of default reset action -
Use the Drag and Drop API and then you will only be able to drop on components that support your drop.
Other than looking at X and Y (which seems an atrocious kludge)
Why? The event doesn't have the information so you need to get it somehow. So if you don't want to use the DnD API then you need to do this yourself.
There are methods in the API to help you do this:
Window window = SwingUtilities.windowForComponent( e.getComponent() );
Point dropPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), window);
Component dropComponent = SwingUtilities.getDeepestComponentAt(window, dropPoint.x, dropPoint.y);
Related
In a fairly complicated codebase, there is functionality both to
(a) drag objects places, implemented with DragGestureRecognizer,
(b) and the ability to drag boxes around things to select them, implemented via e.g. mousePressed() listeners.
Normally they behave "correctly".
However there are some objects which are marked immovable, and when the user begins a mouse gesture on top of one of them, the DragGestureRecognizer is finding it, and apparently consuming the mouse event.
What I'd like to be able to do is e.g. add something to my DragGestureRecognizer to say "oh look, we found an immovable object, let's not have this be a drag after all", and allow the dragging-a-box-around-things to take control.
I realize I'm not providing code because there is way too much to provide, but short of disabling the DragGestureRecognizer entirely (bad), I haven't found a way to get it to (selectively) turn loose of mouse events. Any help much appreciated!
I am looking for a reliable way to be informed when the mouse moves and when the component beneath the mouse moves. While the first part can be rather easily implemented by using a MouseMotionListener, I am currently struggling with the second part.
Right now I have these two ideas:
1. Listen for all relevant changes
This one seems to be rather tough, so far I have
ComponentListener: When the component is moved
AncestorListener: When the ancestor is moved
MouseMotionListener: When the mouse is moved
And when a JScrollPane is used:
ChangeListener: When the view changes
Pros
Only triggers event based.
Cons
Breaks when JScrollPanes are used and you are not aware of it.
Additionally for my specific usecase I want to implement this hover behavior for a JEditorPane and the text layout could change. As described in the comments, for example changing text orientation of a text/rtf editor pane by pressing Ctrl + Shift + O.
2. Regularly check position and inform components
Regularly get the mouse position and inform the child component containing this position about it.
Pros
Catches all situations.
Cons
Always triggers, even if no change happened.
Is not immediate.
The component lookup is maybe rather expensive, especially with many nested components; I am currently using Container#findComponentAt but have not done any tests. (But maybe only as expensive as the lookup for MouseMotionListeners.)
Do you have other solution ideas, additions or changes to the ideas above?
I've had this problem repeatedly over the years with AWT and Swing based interfaces: some mouse clicks events do not trigger mouseClicked in a MouseListener because the mouse moved at least one pixel during the click (which is most clicks with some mice). This is interpreted as a drag operation instead.
Is there a way to tell AWT/Swing to be a bit more permissive in its definition of a click?
Manually implementing a workaround is quite cumbersome if you want a full solution (if your components must also handle drag operations for instance):
Adding a unique MouseListener and MouseMotionListener to every component
Doing some sort of calculation of a click in mouseMoved, mouseDragged and mouseReleased (which requires measuring time elapsed, distance traveled...)
Forwarding the "fixed" mouse events to a new mouse input interface implemented by the components
There's got to be a better way! I'm hoping for a global setting but I just can't find one...
Related posts
https://stackoverflow.com/questions/20493509/drag-threshold-for-some-components-too-low
Making a component less sensitive to Dragging in Swing
Try setting the sensitivity of the D&D using:
System.setProperty("awt.dnd.drag.threshold", "5");
Note: this is only honored by List, Table, Text and Tree components!
See usages of javax.swing.plaf.basic.DragRecognitionSupport#mouseDragged() method.
I've been playing with the Robot class recently and I have it doing what I want, but I haven't figured out how to interrupt/stop its actions via user input.
For example: I want it to click the desktop a hundred times but I decide forty clicks in that I want to make it quit (or pause).
I'd like to be able to do something simple like press a certain key or press the middle mouse button in order to tell it to stop. In order to do this, it must be able to listen for input outside of the Java application, since the actions the Robot is performing are in other programs.
As edward said, there doesn't seem to be a way to do exactly what I was looking for. So this answer is to explain how I achieved an acceptable substitute.
The other question edward linked to had stated that
MouseInfo.getPointerInfo().getLocation()
is capable of fetching the mouse coordinates regardless of what the mouse is doing. My program uses the robot class to control the mouse within a specific range of coordinates. I also wanted to be able to disable the program via user input.
To achieve this result, I compared the x and y coordinates of the mouse to the x and y coordinates that the robot last set it to. If the two do not match, the program exits.
Pausing the program by this method would be impractical because resuming would require going back to the original x and y coordinate prior to pausing, but it does at least give an example of how to achieve stopping without actually being focused on the java parent program.
In order to pause the program, you would instead compare the coordinates to a range of coordinates (have the coordinates create an imaginary, 2D box). If the mouse's coordinates are within that range: pause. To resume, do the opposite check (mouse not being in that range).
You may be able to use some of the code from this answer:
Simulate a key held down in Java
And then add a Listener to whatever action, component, whatever to call the stop method on the robot command.
Does that provide you with some inspiration?
Edit After further discussion the real question is:
How to respond to external Mouse Events (Outside the Java Application) inside a Java application?
It Seems that you can't without native code and Mouse Hooks as it's OS Dependent.
For Further Discussion see Is it possible to have a MouseMotionListener listen to all system mouse motion events?
I am programming a simple game in Java but I am trying to do it 'right' with a nice clean design and no hacks.
I have two classes GamePanel that receives clicks and keypresses and Model which contains all the Entities and is responsible for their updates. The Model needs to know where the user's mouse is but I can't decide on the 'right' way to do it.
Should the Model and every Entity within keep a reference to the GamePanel or just cache the last known mouse position and receive updates from the GamePanel regularly. With the first option when the model is created it will need to be given a reference to the GamePanel and with the second the last mouse position will be sent as a parameter to the World.update() method.
Neither of these solutions seem elegant so I was wondering if there is a 'right' way to do this that I have missed.
Thanks,
Ben.
In my opinion, it would depend on how your classes are interacting. Is a change in the mouse position triggering the entities in the Model class? or Is the Model class independent of the GamePanel and only works on the current values of Mouse position?
If the later and in that case I agree with what jeff has said before me. Pass a handle on the GamePanel to the Model class when creating it and let every entity use the handle to access the mouse position whenever needed. That way the updated mouse position is always used.
If the former I would suggest use Observers to let the Model class know when the mouse position value has changed. Then Model class could use the same design (i.e let the Model class have an handle on the GamePanel class always) and access the current values in the GamePanel.
To sum up my answer to your question, I think it is only logical and conforming to OO concepts to let the GamePanel hold the values of the mouse position and let other classes access that information using a GamePanel instance.
Thanks,
Rohan.
A common solution is to fire events. In this case GamePanel would fire an event informing all interested (subscribed to the event) objects (Entity) in Model of the new mouseposition.
In this way, GamePanel does not need to know explicitly which Entities to inform, and the Entities do not need to keep an instance of GamePanel around.
My advice: read up on event listeners. (Java.util)
You could always have the current coordinates be a public property of the GamePanel, so that the Entitys/Models can simply access it from there.
Does the model always need to know where the mouse is. IE: Do you need the mouse position at every update, or only at a specific points.
If you need it at every point or during every update then either solution that you have documented, or anyone solution here should be solid.
If you only need it at specific points then only grab it then. (IE: if your game responds to mouse clicks then do something on click.) Meaning Read about Event Listeners.
Good luck! Hope that helps.
If you want to poll the current mouse position, you could run this code snipplet in the Event Dispatch Thread at any time you wish:
Point pt = MouseInfo.getPointerInfo().getLocation();
Point cpt = GamePanel.getLocationOnScreen();
Point rel = new Point(pt.x - cpt.x, pt.y - cpt.y);
And rel contains the relative position of the mouse.
Alternatively, you could have a look at JInput, as it is designed to cope with keyboard/mouse/whatever inputs.