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.
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'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);
I'm working on a (legacy) Java-Swing application and ran into a problem:
The main GUI-component in this application is a Gantt chart, that displays tasks, as rectangles with a label, basically.
A right-click on a task opens a context-menu, while hovering over a task with the mouse will show a customized, scrollable JTooltip. This constellation leads to my problem.
If I position the tool-tip too far away from the current mouse position, the tool-tip disappears, before I can move the mouse to one of the scroll-bar handles (horizontal/vertical).
If I position the tool-tip too near to the current mouse position, the context menu won't open anymore, because the tool-tip hides the underlying task and the right-click is therefore captured by the tool-tip and not the task.
What I've tried so far:
searched for some kind of delay in ToolTipManager, to control how long the tool-tip is shown, after the mouse leaves the control, which triggered the tool-tip to be shown. As far as I can say, there is no such delay-property.
tried to find the right distance between tool-tip and current mouse position, so that the scroll-bar handles of the tool-tip can be reached and the context-menu is also shown. -> I found some distance, where both works, but often you have to try several times, until one can reach the scroll-bar handles.
So my question is:
Is there any way to control when a JToolTip is hidden after the mouse leaves the corresponding component?
create JWindow, better undecorated JDialog with correct modality (then could be easiest to catch MouseEvents)
only one window with setDefaultCloseOperation-DO_NOTHING_ON_CLOSE or HIDE_ON_CLOSE, by toggling only with setVisible false / true, to reuse this container for whole JVM instance, clear windows content before is setVisible(false) called
put there Swing Timer with (for example) 5-10 seconds for logical autoclosing, by testing SwingTimer.isRunning, if Mouse Scrolling continues and SwingTimer.isRunning returns true then to call SwingTimer.restart
override mouseClicked for whole JVM instance e.g.
if (window.isVisible)
window.getContentPane.removeAll()
window.setVisible(false)
else
someThingWithRealEventFromMouseListener
there can be used some of better Listener that returns Boolean value instead of using low level instance of MouseListener
you can to (re)dispatch() mouse scrolling (only inside of Bound of the current parent - JFrame, JDialog) to the popup window, by using two - three methods from SwingUtilities
I'm making a map editor using java swing for my tile based java game. the swing application has two major components, the "upper" component is the game map preview, and the "lower" component is modifyable properties of the map, like its height and width.
Currently the user types in to a jtextfield for the map width, then I use a change listener to set that value to the GameMap object. The GameMap object when changed fires a notification event to GameMapListeners, the primary listener it has is the preview display of the map inside the swing application.
This lets the user change the map width and instnatly see the results in a preview pane.
Now I want to go to the other way. I want the user to be able to click and drag the edges of the map in the preview pane, but then the results need to then be sent to the properties panel so it shows the updated width value.
This is where the problem is, if I update the jtextfield it'll fire a change event, which would update the GameMap and update the preview display, and then that would fire an event that changes the jtextfield again (so on and on until the program crashes due to stack overflow)
Are there any kind of design patterns i could use instead, or is there some common way to solve this issue?
In this type of case, you have at least two choices...
You Could
Remove the listener to the other component when you want to trigger a change, adding it back after you've raised the event...
You Could
Change the state of a flag to indicate that you should ignore any changes that might come in, resetting after you're raised the event...
Which one you choose will depend on how much code you want to add and how readily available the reference to the listeners in question are (ie, if you don't have a reference to the listener you want to remove, it's kind of hard to implement)
If I update the jtextfield it'll fire a change event, which would update the GameMap and update the preview display, and then that would fire an event that changes the jtextfield again (so on and on until the program crashes due to stack overflow).
When you have a situation like this, you can temporarily remove event listeners, fire the change event, and add the event listeners back. Yes, this is as much of a pain as it sounds, but it's a good way to prevent the stack overflow.
You can see a detailed explanation as well as a working example of managing event listeners in my Sudoku Solver Swing GUI article.
You can use action events for a JTextField. Action events don't trigger when you change the component programmatically.