I built a Swing GUI application, and everything works quite well, except for a little detail: I have two bottons, and each of them has a mouseclick event attached. The problem is, when i click them, they stay "pressed" : everything else works fine, but it's just odd seeing those two buttons ... maybe i used the wrong event? or i should set them to "depressed" manually ?
"Answers from comments" C.W.-style.
You shouldn't add MouseListeners to JButtons, but instead should use ActionListeners or Action objects. This is the preferred method and is generally simpler if it covers the required behavior.
Make sure to propagate the event (call the default event handlers). The "stays depressed" is because an event is being swallowed and not propagated as it should be.
Is you listener perhaps throwing an exception? Whether this prevents the event getting to the correct other listener depends upon unspecified ordering and one event failing may cause the entire dispatch to fail.
There's gotta be an exception getting thrown that prevents the EDT from completing the button refresh. This is pretty typical.
Related
I'm developing a plugin for IntelliJ IDEA, which obviously uses Swing.
For a feature I have introduced I'd like to stop a JPopupMenu which uses JCheckBoxMenuItems from losing focus and closing.
You can see it in action.
I've debugged the code, but I couldn't figure out how to do it, also being I'm not that into Swing.
Could you maybe point me to useful listeners/blocks of code/ways to prevent this?
If you want to see code, the IntelliJ classes are
ActionPopupMenuImpl.MyMenu
ActionMenuItem
Edit: a better way need to be found as the uiRefreshed event isn't always called at the right time.
Just coordinate your code in a good way ;)
The Swing mechanism in IDEA is too complicated, and maybe it's better to not touch it. Just know that the mouse events are handled by a special listener and then redirected to Component(s).
That said, having an hold on the menu ActionButton. You can listen for the Lookup's uiRefreshed event and programmatically:
myMenuButton.click()
That's all.
You need to call it after the UI has been refreshed because the LookupUi might have changed in dimension or location.
I have a JTextPane that has a DocumentListener waiting for changes to the underlying HTML document. When the content changes, the event uses JTextPane.scrollToReference to move the view to a certain reference anchor. The problem is that the underlying View in the JTextPane is also listening to the document changes, and doesn't update until after my listener executes, which causes an exception. Is there any way I can force my DocumentListener to execute after any other event listeners for that particular event? Or is there some way I can wait for the view to be updated before executing my code?
First try to use SwingUtilities.invokeLater() for the listener that has to be executed as last. If that doesn't work, build your own priority queue.
Document is model fro JTextComponents, then not good idea put there two or more Listeners wrote changes to the Document,
all event should be done if model invoke all implemented event to the view, then only if are all events done in the view, then is possible moving with JViewport
no idea whats Listener you are implemented, but DocumentListener (e.g.) with FocusListener (e.i.) can creating endless loop with nice exception from RepaintManager
remove Listener that generating exceptions, add Listener if is really required, remove uselles Listeners immediatelly,
Swing quite no guarentee ordering of Listeners, nor events from multiplayed Listeners betweens model_to_view and vice versa
you can testing if Listeners firing events subsequently or gradually by pushing of required event from Swing Action (delayed from Swing Timer) or for asynchronous Listeners (freezed) by Thread.sleep(int)
I wound up having to extend the Viewport and JScrollPane classes to accomplish this. I exposed a listener for the viewport's change event and added my logic in there (which ensured it was the last code executed in the rendering chain).
I have a JPanel with a set of items (for example combo boxes and text fields). Some action listeners are implemented on those items to register user updates.
If the user selects a value in a JComboBox (for example), the action listener captures the event. The corresponding underlying bean method is called and the panel is refreshed. Changing can have an impact on other fields displayed in the pane.
The problem is that when the panel is refreshed, all listeners are triggered, and they call for a refresh themselves. This leads to an infinite loop.
How can I avoid this? I can't get rid of the listeners, because I need to capture user updates, but I don't want these to fire when I am only refreshing the panel content.
One option is to have a central boolean value or some indicator that each listener can check to prevent the chaining of events.
Another option is to not refresh the field if the value does not change. That way each component is updated at most once per refresh.
I can't get rid of the listeners, because I need to capture user updates, but I don't want these to fire when I am only refreshing the pane content
Then remove the listeners, refresh the pane content and then restore the listeners. This way the listeners only fire when a user change is made.
I think that if your problem is in combobox it just points to a bug. Really, if user changes the value of the combobox, that somehow triggers refresh of the pane the value of the combo box should not be changed second time! So if it is onValueChanged() (or something like this) it should not be called at all when pane is being refreshed.
But if for some reason it happens you can verify whether the old and new values are the same and exit the listener.
If this still does not help I'd suggest you some non-standard solution: try to investigate the stack trace into the listener. Can you identify whether the listener was called as a direct reaction to user's action or after the pane refresh? In this case you can create utility method and put it in the beginning of all relevant listeners.
My applications also suffered from this problem, and solution with the flag, that I should check in every listener and enable/disable in code, feels not very good for me. I always forgot to set this flag to true/false in necessary places.
That is why I decide to implement another solution.
I just subclass all default swing components that I am using often, and implemented custom ValueChanged event that I fire after mouse/keyboard/clipboard/etc events. Now I am always know, that if ValueChanged event is fired, it means, that value was issued by user, not by code. Event handling in this way much more cleaner. This solution solves my problem.
I have a design related question that I am trying to find an answer to.
Here is the scenario.
Suppose that you want to do something expensive (time-consuming) as a result of user input (e.g. loading huge amounts data from some database, reading large files). The strongly recommended way is to do the time-consuming work in a separate thread and never ever block the EDT, or else the GUI will become unresponsive.
There are scenarios however when you should not provide inputs to the GUI unless the background task is finished. In my specific case, only after the background work is finished, I can determine which GUI elements should be visible and enabled/disabled. Only those GUI elements which should be visible and enabled should respond to the user inputs otherwise the behavior may be unpredictable in my specific case.
This is what I am doing to handle such a scenario.
Step 1: Before I am about to start a time-consuming operation.
Change the cursor to a busy cursor.
Add mouse listeners to the glasspane of component's top-level frame.
Make the glasspane visible so that it can receive mouse events. The glasspane doesn't do anything as a result of mouse inputs.
Step 2: Execute the time-consuming operation in a background thread. The background thread has a finally block that notifies the event thread when the job is finished (completed or aborted due to an error).
Step 3:
Switch the mouse cursor back to normal.
Remove listeners from the glass pane.
Make the glasspane invisible, so that mouse events go to their intended recipients.
Is this the correct approach to handle such situations?
What do you guys recommend?
SwingWorker can be used in this context. Related controls can be disabled when the background task is started and re-enabled in done(). In this related example, the run button is conditioned to toggle between "Run" and "Cancel".
Addendum: A back-port to Java 1.5 is available here.
Is there anyway that I can control the order in which events are fired in my Swing application?
For example, I have a MouseListener and an ActionListener for a component. Can I make it so the MouseListener always fires before the ActionListener?
Seems to me if I add the MouseListener before the ActionListner, I am just praying that they will fire in that order.
Now I understand that coding this way is bad practice and that you should not count on the order of events firing, but for this situation I am encountering, it is a must.
Thanks
If you have an action triggered off of a one listener that needs to happen before the other then change your logic.
Either combine the listeners so that a single listener gets both events and holds onto one temporarily if needed. Or only add one listener and have it call the appropriate method or create a new event after it finishes.
Wrap the ActionListener code in a SwingUtilities.invokeLater(...). The code will be added to the end of the EDT.
I would still have both listeners in the same class so the dependency is well documented.