0

I have a customized JButton in a Java Swing application. it changes it appearances according to mouse event.
MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button. MouseEvent.MOUSE_PRESSED - will trigger the pressed image.
MouseEvent.MOUSE_RELEASED - will change the foreground to gray and render the button disabled.
This is working fine with actual mouse clicks.

I want to add a support for pressing the ENTER key.
simply calling button.doClick() did not go through the hover-press-release cycle, but simply jumped to the release event.

So I have this short and effective code for doing this.

InputMap im = workspacePnl.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = workspacePnl.getActionMap();
im.put(KeyStroke.getKeyStroke("ENTER"), "connect");
am.put("connect", new ConectAction());

private class ConectAction extends AbstractAction {
        @Override
        public void actionPerformed(ActionEvent ev) {
            simulateClick();   
}

and the problematic code:

public void simulateClick() {
        MouseEvent evt;

        evt = new MouseEvent(connectBtn,
                MouseEvent.MOUSE_ENTERED, 1, 0, 0, 0, 1, false);
        connectBtn.dispatchEvent((AWTEvent) evt);

        //CommonUtil.sleep(300);

         evt = new MouseEvent(connectBtn,
                MouseEvent.MOUSE_PRESSED, 8, 0, 0, 0, 1, false);
        connectBtn.dispatchEvent((AWTEvent) evt);

        //CommonUtil.sleep(300);

        evt = new MouseEvent(connectBtn,
                MouseEvent.MOUSE_RELEASED, 20, 0, 0, 0, 1, false);
        connectBtn.dispatchEvent((AWTEvent) evt);
    }

I am trying to make the ENTER press go through the same route:
trigger a MOUSE_ENTERED event which will alter the button's appearance for hover, followed by MOUSE_PRESSED and MOUSE_RELEASED.
But I only see the last event effect. it is as if i'm only firing the last event alone which lacks the liveliness for an interactive software. I tried (as can be seen commented out) to have the thread go to sleep after each event firing, but it has no effect. If I try to fire each of the other two events they are noticeable on the screen by themselves. it's the batching together that messes things up.

How can I fire a series of dispatchEvents one by one which will all be noticed by the user? how can i make the program wait for the current dispatchEvent to work it's magic before striding on to the next on?

Any help or insights would be greatly appreciated.

uzil24
  • 156
  • 4
  • 13
  • 1
    *"MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button. "* - [`JButton#setRolloverEnabled`](http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#setRolloverEnabled(boolean)) & [`JButton#setRolloverIcon`](http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#setRolloverIcon(javax.swing.Icon)) – MadProgrammer Jul 16 '15 at 08:21
  • 1
    *"MouseEvent.MOUSE_PRESSED - will trigger the pressed image."* - [`JButton#setRolloverSelectedIcon`](http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#setRolloverSelectedIcon(javax.swing.Icon)) &/or [`JButton#setSelectedIcon`](http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#setSelectedIcon(javax.swing.Icon)) – MadProgrammer Jul 16 '15 at 08:22
  • `public void simulateClick() {` ... is there anything wrong with `doClick`? – MadProgrammer Jul 16 '15 at 08:23
  • This does not solve my problem. it's true that I could have used this methods for better customizing the button (perhaps. I didn't try it so I can't tell if that would not introduce errors), but I still need to call those events manually without the help of a physical mouse, to create the illusion of actual pressing. I wrote that doClick did nothing. – uzil24 Jul 16 '15 at 08:27
  • Actually, it does for the most part. For your needs, you might need to fudge the "selected icon" with a `ChangeListener` on the `JButton`'s `ButtonModel`, assuming you want the "selected icon" to be displayed when you press the [Enter] button. The reason `doClick` doesn't work for you is you're not monitoring the change the button's model – MadProgrammer Jul 16 '15 at 08:49
  • unfortunately, the problem is a bit more complex. the software is vast and contains dozens of different kinds of buttons. for some of those buttons I had to use JPanel or JLabel since only those JComponents wore the graphics i put on them appropriately. This is ugly practice but I have deadlines. so.. I had to use mouse events and handle the hover by myself. the question is: can i control those mouse events or not? – uzil24 Jul 16 '15 at 12:33
  • Then the answer should be no (or don't), as you're simply placing one hack on top of another which will ultimately explode in your face, instead of making use of the how the API was designed. The hack solution you've selected violates the single thread rules of swing, which might work in an isolated case but which would ultimatly produce a bunch of random and seemingly unrelated weird issues – MadProgrammer Jul 17 '15 at 01:27

3 Answers3

1

How can I fire a series of dispatchEvents one by one which will all be noticed by the user? how can i make the program wait for the current dispatchEvent to work it's magic before striding on to the next on?

  • Mouse and Key Event are correctly implemented in ButtonComponents, don't use MouseListener, to use events from ButtonModel, by using ChangeListener, for example
Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
1

MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button.

Make use of the roll over support supplied by the button, see JButton#setRolloverEnabled & JButton#setRolloverIcon

MouseEvent.MOUSE_PRESSED - will trigger the pressed image.

Is a little more difficult, but you can use listener to the ButtonModel for changes and update the icon based on you requirements

MouseEvent.MOUSE_RELEASED - will change the foreground to gray and render the button disabled.

Should probably be achieved through the use a ActionListener

I am trying to make the ENTER press go through the same route:

JButton#doClick will go through the isArmed and isPressed states of the model automatically, which will trigger the state changes provided by the previous comments...

With the mouse...

WithMouse

With the keyboard...

WithKeyboard

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {

            setLayout(new GridBagLayout());
            JButton btn = new JButton();
            try {
                btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash01.png"))));
                btn.setRolloverIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash02.png"))));
                btn.setRolloverEnabled(true);
//              btn.setSelectedIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash03.png"))));
            } catch (IOException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }

            btn.getModel().addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    if (btn.getModel().isPressed()) {
                        try {
                            btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash03.png"))));
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    } else {
                        try {
                            btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash01.png"))));
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }

                    System.out.println("Armed: " + btn.getModel().isArmed());
                    System.out.println("Enabled: " + btn.getModel().isEnabled());
                    System.out.println("Pressed: " + btn.getModel().isPressed());
                    System.out.println("Rollover: " + btn.getModel().isRollover());
                    System.out.println("Selected: " + btn.getModel().isSelected());

                }
            });

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(btn, gbc);
            add(new JTextField("Stealer of focus"), gbc);

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    btn.setEnabled(false);
                }
            });

            InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();
            im.put(KeyStroke.getKeyStroke("ENTER"), "connect");
            am.put("connect", new AbstractAction() {

                @Override
                public void actionPerformed(ActionEvent ev) {
                    System.out.println("click");
                    btn.doClick();
                }
            });
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
0

It's batching together because your whole code is running on the EventDispatchingThread (EDT).

When you call simulateClick() from actionPerformed() call it on a new thread instead of the same thread (which will be EDT).

Codebender
  • 14,221
  • 7
  • 48
  • 85