Hello i'm making a simple game right now, and I have it when your on the play screen you can press esc to open up the menu. There is a main menu button which takes you from the play screen to the main menu screen, and the problem is it takes them to the main menu screen, but then clicks the button which is in the same spot on the main screen as the ingame menu, so it clicks the button on the main menu before you are able to release the button. Here are some of my classes
this is my menu class (Main screen) http://pastebin.com/kc5Dx2bp
this is my play class (The play screen) http://pastebin.com/Gr3y9Amj
Is there a way to make it so you can't click another button until the mouse button is released?
Basically, you can use the ButtonModel to track finer changes to the button state then ActionListener gives you.
This example monitors the armed and pressed states of the model and takes action based on a certain condition.
This is a small trick, as the state you are looking for will occur not only when the button is released, but also when the button is first pressed. What we do is wait until the previous state had the button pressed and the buttons pressed state is false, then we know we have a button release
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestButton05 {
public static void main(String[] args) {
new TestButton05();
}
public TestButton05() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final JButton btn = new JButton("Clicked");
btn.getModel().addChangeListener(new ChangeListener() {
private boolean wasPressed = false;
#Override
public void stateChanged(ChangeEvent e) {
ButtonModel model = (ButtonModel) e.getSource();
if (model.isArmed() && !model.isPressed() && wasPressed) {
System.out.println("Released");
} else {
wasPressed = model.isPressed();
}
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(btn);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Create a boolean in your class called canClick.
Create a MouseListener.
Add the MouseListener to each button (button.addMouseListener(...);)
In the MouseListener, implement all needed methods.
Set the boolean to true or false as needed in each method.
When a button is clicked (ActionListener), check the boolean.
If false, cancel it (or just disable all buttons until true)
Something like that.
Related
I have a class that I've written that uses the console for user input. I am trying to create a GUI around it to enhance the program. As a result, I've created a new GUI class that i would like to use to pass values back to the existing class. I've spent hours scouring the forum and cant seem to find an answer the matches my specific problem.
The closest thing i found was pass radiobutton value that selected to another class and I've actually used the recommendation from that class. It seems to work "sometimes", though. What i mean is when i select the radio button "one" for the first time, nothing happens. I then click on the second radio button and nothing happens (as expected). When i click on the first radio button again, it then prints text to the console as expected. I cant figure out why it doesn't work on the first click. Secondly, each time i click on the seecond button and back on the first, it prints the expected output 2x more than the time before.
/// Radio Button Class
package views;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.EmptyBorder;
import common.ButtonTester;
public class RadioButtons extends JFrame {
private JPanel contentPane;
private JRadioButton rdbtnOne, rdbtnTwo;
private ButtonGroup grp;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
RadioButtons frame = new RadioButtons();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public RadioButtons() {
setTitle("Button Demo");
initComponents();
createEvents();
}
**/// Components**
private void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
rdbtnOne = new JRadioButton("One");
rdbtnTwo = new JRadioButton("Two");
grp = new ButtonGroup();
grp.add(rdbtnOne);
grp.add(rdbtnTwo);
GroupLayout gl_contentPane = new GroupLayout(contentPane);
gl_contentPane.setHorizontalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addGap(126)
.addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING)
.addComponent(rdbtnTwo)
.addComponent(rdbtnOne))
.addContainerGap(189, Short.MAX_VALUE))
);
gl_contentPane.setVerticalGroup(
gl_contentPane.createParallelGroup(Alignment.LEADING)
.addGroup(gl_contentPane.createSequentialGroup()
.addGap(87)
.addComponent(rdbtnOne)
.addGap(48)
.addComponent(rdbtnTwo)
.addContainerGap(70, Short.MAX_VALUE))
);
contentPane.setLayout(gl_contentPane);
}
**/// Event handlers**
private void createEvents() {
rdbtnOne.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
rdbtnOne.addActionListener(new ButtonTester());
}
});
}
}
/// ButtonTester Class
package common;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ButtonTester implements ActionListener {
public static void main(String[] args) {
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello. I'm in the action Performed Method.");
}
}
I expect that each time i click on radio button one, the sysout line executes once.
private void createEvents() {
rdbtnOne.addActionListener(new ActionListener() {
// ****** A *****
public void actionPerformed(ActionEvent e) {
// ***** B ****
rdbtnOne.addActionListener(new ButtonTester());
}
});
}
The line below (A) adds an ActionListener within an ActionListener, something that really makes no sense and is an unnecessary over-complication. This is why the first time you press the button, no visible output occurs as all that happens is that behind the scenes another ActionListener is added to the radio button at line (B). The 2nd time both listeners fire, the 3rd press, more listeners fire since you keep adding new listeners to the button.
The solution: simplify -- add a single listener to the button and add it once and only once:
private void createEvents() {
rdbtnOne.addActionListener(new ButtonTester());
}
A JButton has a different appearance when rolled over. That appearance is different from the "selected" appearance.
I want to display my button "as if" it was rolled over, so that user understands that if he hits the Return key, that button will be triggered.
The problem is not the same as setting the default button, because I am in a situation where I really want to get the user to understand that although he wouldn't expect it, if he hits enter that button will be activated. More details below for those who want some. Setting button as default would make button the default one, but wouldn't be significantly signaling to the user.
In my case the strong enough signal is the appearance that the button has when it is rolled over.
How to do that ?
More details on the situation, for those who want some :
I have a list of buttons representing options, and a text box at the top, which acts as a filter on the buttons
when filter is such that only one option remains, hitting return directly clicks that option's button
in reality user would have had to select the button with tab or arrow, and then hit enter.
since that shortcut is not obvious I want to signal it to user
Based on your question, what you "really" want, is the JRootPane#setDefaultButton, which will highlight the button, in a OS specific manner and if the user presses the default "action" key (Enter in most cases) will call it's ActionListener
For example...
The "normal" button is just a plain old JButton, the Hacked sets the rollOver to enabled and Default has been set as the default button for the JRootPane
As you can see, you're suggest fix does nothing on MacOS, don't know what it might do on other platforms
I suggest you have a look at How to Use Root Panes for more details
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 {
private JButton asDefault = new JButton("Default");
public TestPane() {
JButton hack = new JButton("Hacked");
hack.getModel().setRollover(true);
hack.setRolloverEnabled(true);
asDefault.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Defaulted");
}
});
add(new JButton("Normal"));
add(hack);
add(asDefault);
}
#Override
public void addNotify() {
super.addNotify();
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null) {
rootPane.setDefaultButton(asDefault);
}
}
}
}
So using button.getModel().setRollover(true); doesn't work on all platforms and on those platforms it does work on, I suspect the user will simply need to move the mouse through it to return it to normal
button.getModel().setRollover(true);
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.
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
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...
With the keyboard...
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();
}
});
}
}
}
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).
i have three problems with a JPopupmenu on Mac, all can be reproduced by the enclosed java
program or e.g. the netbeans java application.
The first thing is that Java applications don't block the dock when a popup menu is shown.
So when i right click in my java application to open a popup menu, i can still
move the mouse over the dock area and the dock appears.
In non java applications (Outlook, Textwrangler, Finder...) the dock won't
appear if a context menu is shown in these applications.
Is there a way to make a java application behave like a 'native' OS X application, so
the dock will not be shown in this context?
The next problem is more annoying.
if the context menu is shown by the java application and now the user switches (cmd-TAB or by
the dock) to another application lets say Outlook, the context menu of the java application is still
visible on top of the other application window.
Is there a way to hide the popup menu of the java application if another application has the focus?
And the last problem.
Lets say an application is in front of netbeans and now you right click into the netbeans window,
a popup menu from netbeans is shown, but if you move the mouse over the menu items, no menu item
will be highlighted. You're able to select a menu item by pressing the mouse, but by moving the
mouse over the menu items they are not highlighted.
Why are the menu items not highlighted, is there a workaround?
Mac OS X 10.6.8
Java: 1.6.0_35
package popupmenu;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
public class PopupMenuApp {
private JPopupMenu popup;
private class PopupListener extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
#Override
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
private void start() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
popup = new JPopupMenu();
popup.add(new JMenuItem("A popup menu item"));
frame.addMouseListener(new PopupListener());
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setAlwaysOnTop(true);
}
});
}
public static void main(String[] args) {
PopupMenuApp app = new PopupMenuApp();
app.start();
}
}
For the second issue, adding a WindowFocusListener does the trick:
popupMenu.show(button, 0, bounds.height + 1);
frame.addWindowFocusListener(new WindowFocusListener() {
#Override public void windowGainedFocus(WindowEvent arg0) {}
#Override public void windowLostFocus(WindowEvent arg0) {
popupMenu.setVisible(false);
frame.removeWindowFocusListener(this);
}
});
I have a Swing application that deals with date and time, so a lot of tests are done changing the system's date and time settings.
During the tests, we noticed that after decreasing the clock, the first click is ignored by the application.
Is it a bug of Swing/Java/Windows? Is there a workaround to this?
Interestingly, this issue only happens when decreasing the date/time settings. If I increase it, the application behaves normally.
Situation:
Swing application running.
Decrease Windows date and time settings (e.g. change time from 15:00 to 14:00).
Notice that the first click in the Swing application does not fire any action.
Code example (you can use it to testify the situation):
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
final JFrame frame = new JFrame("frame");
final JButton button = new JButton("button");
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println("Button Pressed!");
}
});
frame.add(button);
frame.setSize(200, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
System.exit(0);
}
});
}
}
As seen here the Swing uses a date to check when the event occoured. So, in some way, probally a handler is acting here, by discarting your action, since it happened "before" the last action. I can't confirm you this, but probally some Layout Manager or another handler is messing with something here to prevent delayed events to mess up with the current flow.
I've debugged it via Eclipse and found out what is happening.
Clock at 15:00h.
Click at the button. Swing record last event time to 15:00.
Change the clock to 14:00h.
Click at the button. Swing ignores the event because it looks like a multi-click.
The problem here is that the comparison made by Swing checking for multi-click is this:
if (lastTime != -1 && currentTime - lastTime < multiClickThreshhold) {
shouldDiscardRelease = true;
Here, currentTime - lastTime yields a negative value. It's less than 0 (my multiClickThreshhold), so it does not fire the action event:
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
// Support for multiClickThreshhold
if (shouldDiscardRelease) {
shouldDiscardRelease = false;
return;
}
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
model.setPressed(false);
model.setArmed(false);
}
}
All the source listed above is in javax.swing.plaf.basic.BasicButtonListener.
The Button class does have a setMultiClickThreshhold, but it throws IllegalArgumentException in case the threshhold is less than 0.
So, as a workaround, I did this:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Field;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) throws Exception {
final JFrame frame = new JFrame("frame");
final JButton button = new JButton("button");
removeMulticlickThreshold(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println("Button Pressed!");
}
});
frame.add(button);
frame.setSize(200, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
System.exit(0);
}
});
}
private static void removeMulticlickThreshold(final JButton button) throws Exception {
final Field multiClickThreshhold = AbstractButton.class.getDeclaredField("multiClickThreshhold");
multiClickThreshhold.setAccessible(true);
multiClickThreshhold.set(button, Long.MIN_VALUE);
}
}