I want to create auto-disposing message dialog. Everything works beside that there is still an ok button to click.
I want to get read the button.
Please check out this code -> works perfectly:
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class CloseOptionPane {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final JLabel label = new JLabel();
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
int timeLeft = 3;
#Override
public void actionPerformed(ActionEvent e) {
if(timeLeft > 0) {
label.setText("Human player. It is your move!");
timeLeft--;
} else {
((Timer) e.getSource()).stop();
Window win = SwingUtilities.getWindowAncestor(label);
win.setVisible(false);
}
}
}) {
{
setInitialDelay(0);
}
}.start();
JOptionPane.showMessageDialog(null, label);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Related
I've written a test program with making the jButton invisible and visible:
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
public class Blink
{
private JButton btn;
private static JFrame f;
public static void delay(int ms)
{
try
{
Thread.sleep(ms);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
public Blink()
{
f = new JFrame("Blink");
f.setPreferredSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
btn = new JButton("Click me and I'll blink!");
f.add(btn);
btn.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
buttonClicked();
}
});
f.pack();
f.setVisible(true);
}
private void buttonClicked()
{
for (int i = 0; i < 5; i++)
{
delay(300);
btn.setVisible(false);
delay(300);
btn.setVisible(true);
}
}
public static void main(String[] args)
{
new Blink();
}
}
Unfortunately, the jButton does not blink. And when the buttonClicked() function is changed, so that the jButton is set invisible 5 times and is not set visible back, the jButton disappears only when the for-loop finishes. How to make the jButton disappear an reappear instantaneously?
You cannot use Thread.sleep method in Swing Thread (all listeners are called in Event Dispatcher Thread - EDT). To achieve blinking you must use javax.swing.Timer class. For more information look here and here
Here is your reworked example:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Blink {
private JButton btn;
private JFrame f;
public void delay(int ms, boolean show) {
Timer timer = new Timer(ms, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setVisible(show);
btn.getParent().revalidate();
btn.getParent().repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public Blink() {
f = new JFrame("Blink");
f.setPreferredSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
btn = new JButton("Click me and I'll blink!");
f.add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
buttonClicked();
}
});
f.pack();
f.setVisible(true);
}
private void buttonClicked() {
for (int i = 1; i <= 10; i += 2) {
delay(300 * i, false);
delay(300 * (i + 1), true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Blink();
}
});
}
}
For some complicated layouts, call setVisible(false) may have side-effects. In this case the CardLayout with your component and an empty panel should be used.
Here is the variant with CardLayout
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Blink {
private static final String BUTTON_CARD = "button";
private static final String EMPTY_CARD = "empty";
private JButton btn;
private JFrame f;
private final CardLayout cardLayout = new CardLayout();
public void delay(int ms, boolean show) {
Timer timer = new Timer(ms, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(btn.getParent(), show ? BUTTON_CARD : EMPTY_CARD);
btn.getParent().revalidate();
btn.getParent().repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public Blink() {
f = new JFrame("Blink");
f.setPreferredSize(new Dimension(500, 500));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(cardLayout);
btn = new JButton("Click me and I'll blink!");
f.add(btn, BUTTON_CARD);
f.add(new JPanel(), EMPTY_CARD);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
buttonClicked();
}
});
f.pack();
f.setVisible(true);
}
private void buttonClicked() {
for (int i = 1; i <= 10; i += 2) {
delay(300 * i, false);
delay(300 * (i + 1), true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Blink();
}
});
}
}
As #Sergiy points out - make sure you're running from the EDT, and don't sleep on the EDT, use a swing timer instead.
To make your jButton appear "invisbile", you can do something like this:
public void setInvisible(jButton jb) {
jb.setOpaque(false);
jb.setContentAreaFilled(false);
jb.setBorderPainted(false);
jb.setText("");
}
// Assuming you have the original text saved in a variable
public void setRevisible(jButton jb) {
jb.setOpaque(true);
jb.setContentAreaFilled(true);
jb.setBorderPainted(true);
jb.setText(originalString);
}
Depending on if you want the button to be clickable when it's invisible, you can also add btn.setEnabled(bool);
I use some classes to get my main frame preferences (locaion and size). Here the following code I use. It works well on Linux and Windows. Buton macOs I have a strange error.
Indeed, if I resize the windows to fit the screen (manually, not with the maximize button) and I close the app. The app will reopen with a tiny size at the left bottom of my screen. It only occurs when I quit with the frame having a full size.
It is like if frame.setSize(dim) does not work when dim has dimension greater than the screen.
However, I also tried to write this code in WindoHandler with no sucess:
Preferences prefs = Preferences.userRoot().node(WindowHandler.class.getSimpleName() + "-" + frameUniqueId);
Dimension savedDim = getFrameSize(prefs, defaultW, defaultH);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
// The following test is needed on macOS.
if ((savedDim.getWidth() >= screenSize.getWidth()) || (savedDim.getHeight() >= screenSize.getHeight())) {
frame.setSize(screenSize);
frame.setLocation(new Point(0, 0));
} else {
frame.setSize(savedDim);
frame.setLocation(getFrameLocation(prefs, defaultX, defaultY));
}
EDIT:
I also tried:
frame.setPreferredSize(getFrameSize(prefs, defaultW, defaultH));
frame.setLocation(getFrameLocation(prefs, defaultX, defaultY));
frame.pack();
with no sucess too....
My classes:
package org.spview.preferences;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.prefs.Preferences;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.spview.Main;
//import com.apple.eawt.FullScreenUtilities;
public class WindowHandler {
public static void registerFrame(JFrame frame, String frameUniqueId, int defaultX, int defaultY, int defaultW,
int defaultH) {
Preferences prefs = Preferences.userRoot().node(WindowHandler.class.getSimpleName() + "-" + frameUniqueId);
frame.setSize(getFrameSize(prefs, defaultW, defaultH));
frame.setLocation(getFrameLocation(prefs, defaultX, defaultY));
CoalescedEventUpdater updater = new CoalescedEventUpdater(400, () -> updatePref(frame, prefs));
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
updater.update();
}
#Override
public void componentMoved(ComponentEvent e) {
updater.update();
}
});
}
private static void updatePref(JFrame frame, Preferences prefs) {
Point location = frame.getLocation();
prefs.putInt("x", location.x);
prefs.putInt("y", location.y);
Dimension size = frame.getSize();
prefs.putInt("w", size.width);
prefs.putInt("h", size.height);
}
private static Dimension getFrameSize(Preferences pref, int defaultW, int defaultH) {
int w = pref.getInt("w", defaultW);
int h = pref.getInt("h", defaultH);
return new Dimension(w, h);
}
private static Point getFrameLocation(Preferences pref, int defaultX, int defaultY) {
int x = pref.getInt("x", defaultX);
int y = pref.getInt("y", defaultY);
return new Point(x, y);
}
}
package org.spview.preferences;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class CoalescedEventUpdater {
private Timer timer;
public CoalescedEventUpdater(int delay, Runnable callback) {
timer = new Timer(delay, e -> {
timer.stop();
callback.run();
});
}
public void update() {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> {
timer.restart();
});
} else {
timer.restart();
}
}
}
package org.spview;
import org.spview.preferences.WindowHandler;
import org.spview.gui.JobPlay;
import java.util.Locale;
/**
* This is the entry point to SPVIEW application.
*/
public class Main {
public final static String PACKAGE_NAME = "SPVIEW";
public final static String VERSION = "1.99";
/**
* Start SPVIEW application.
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
Locale.setDefault(Locale.ENGLISH); // messages in english
WindowHandler.setLookAndFeel();
JobPlay jp = new JobPlay(); // instantiate the JobPlay main window
WindowHandler.registerFrame(jp, Main.class.getName(), 0, 0, 1024, 768);
jp.setVisible(true); // show it
}
});
}
}
In an example below, an attempt made to open dialog on worker start and to close it on worker end.
Unfortunately, done() is not executed until dialog closed manually.
Why?
package tests.javax.swing;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Try_SwingWorker_Modality {
private static final Logger log = LoggerFactory.getLogger(Try_SwingWorker_Modality.class);
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AbstractAction popupAction = new AbstractAction("popup") {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "popup");
}
};
final JDialog dialog = new JDialog((JFrame)null, true);
dialog.setLayout(new FlowLayout());
dialog.add(new JButton(popupAction));
dialog.pack();
dialog.setLocationRelativeTo(null);
SwingWorker<Object,Object> swingWorker = new SwingWorker<Object,Object>() {
#Override
protected Object doInBackground() throws Exception {
log.debug("doInBackground()");
return null;
}
#Override
protected void done() {
log.debug("done");
//dialog.dispose();
}
};
swingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
log.debug("event = {}", evt);
if( "state".equals(evt.getPropertyName())) {
if( SwingWorker.StateValue.STARTED == evt.getNewValue() ) {
dialog.setVisible(true);
log.debug("after setVisible()");
}
else if( SwingWorker.StateValue.DONE == evt.getNewValue() ) {
dialog.dispose();
}
}
}
});
swingWorker.execute();
}
});
}
}
You're freezing your SwingWorker's PropertyChangeListener with that code. I would never set the dialog visible from within the PCL.
Rather, I'd do it here:
swingWorker.execute();
dialog.setVisible(true);
How can I adjust this Timer code so that it executes four times and then stops?
timer = new Timer(1250, new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
System.out.println("Say hello");
}
});
timer.start();
You could do:
Timer timercasovac = new Timer(1250, new ActionListener() {
private int counter;
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Say hello");
counter++;
if (counter == 4) {
((Timer)e.getSource()).stop();
}
}
});
timercasovac.start();
You need to count yourself and then stop the Timer manually:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestTimer {
private int count = 0;
private Timer timer;
private JLabel label;
private void initUI() {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel(String.valueOf(count));
frame.add(label);
frame.pack();
frame.setVisible(true);
timer = new Timer(1250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (count < 4) {
count++;
label.setText(String.valueOf(count));
} else {
timer.stop();
}
}
});
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTimer().initUI();
}
});
}
}
I want to have the user press a button to kick off a background thread.
While the thread is processing, I want two things to happen:
1) A WAIT_CURSOR should be displayed.
2) The application should not respond to mouse events.
As per the setCursor documentation "This cursor image is displayed when the contains method for this component returns true for the current cursor location, and this Component is visible, displayable, and enabled. ".
I want my application to be disabled while this background thread is processing.
Any ideas how to get the functionality I want?
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class WaitCursor extends JFrame
{
private static final long serialVersionUID = 1L;
public WaitCursor()
{
setResizable(false);
setName(getClass().getSimpleName());
setTitle("My Frame");
setSize(300, 300);
getContentPane().add(new MyButtonPanel());
}
private class MyButtonPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public MyButtonPanel()
{
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new BtnStartActionListener());
add(btnStart);
}
private class BtnStartActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
// Change to WAIT_CURSOR
Component root = SwingUtilities.getRoot((JButton) e.getSource());
JOptionPane.showMessageDialog(root, "Wait 10 seconds");
root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// TODO: Disabling the root component prevents the WAIT_CURSOR from being displayed
root.setEnabled(false);
new Thread(new TimeKiller(root)).start();
}
}
}
private class TimeKiller implements Runnable
{
Component _root;
public TimeKiller(Component root)
{
_root = root;
}
public void run()
{
try
{
Thread.sleep(10 * 1000);
}
catch (InterruptedException e)
{
// Ignore it
}
// Change back to DEFAULT CURSOR
JOptionPane.showMessageDialog(_root, "Done waiting");
_root.setCursor(Cursor.getDefaultCursor());
_root.setEnabled(true);
}
}
private static void createAndShowGUI()
{
// Create and set up the window.
WaitCursor frame = new WaitCursor();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
createAndShowGUI();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
});
}
}
One way to disable it is to use the glass pane to block mouse input.
For example:
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import javax.swing.*;
#SuppressWarnings("serial")
public class WaitCursor2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private JComponent glassPane;
private JButton runBackgroundProcBtn;
private JTextArea textarea = new JTextArea(15, 30);
public WaitCursor2(JComponent glassPane) {
this.glassPane = glassPane;
glassPane.setFocusable(true);
glassPane.addMouseListener(new MouseAdapter() {
}); // so it will trap mouse events.
add(new JTextField(10));
add(runBackgroundProcBtn = new JButton(new AbstractAction(
"Run Background Process") {
#Override
public void actionPerformed(ActionEvent arg0) {
runBackgroundProcessAction();
}
}));
add(new JScrollPane(textarea));
}
private void runBackgroundProcessAction() {
disableSystem(true);
glassPane.setVisible(true);
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
long sleepTime = 5000;
Thread.sleep(sleepTime);
return null;
}
#Override
protected void done() {
disableSystem(false);
}
}.execute();
}
public void disableSystem(boolean disable) {
glassPane.setVisible(disable);
runBackgroundProcBtn.setEnabled(!disable);
if (disable) {
System.out.println("started");
glassPane.requestFocusInWindow(); // so can't add text to text components
glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
} else {
System.out.println("done");
glassPane.setCursor(Cursor.getDefaultCursor());
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("WaitCursor2");
WaitCursor2 mainPanel = new WaitCursor2((JComponent) frame.getGlassPane());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
The glass pane will trap mouse events if it set visible and given a MouseListener. It will lose t his ability if it is set invisible. Likewise it will pull the caret from text components if you make it focusable and give it focus.
added a field current_active and at method actionPerformed, do a simple check. Albeit it is not perfect but for simple app, i think this do the trick. A crude way of solving your two requirement. :-) Hope it works for you too.
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class WaitCursor extends JFrame
{
private static boolean current_active = false;
public WaitCursor()
{
setResizable(false);
setName(getClass().getSimpleName());
setTitle("My Frame");
setSize(300, 300);
getContentPane().add(new MyButtonPanel());
}
private class MyButtonPanel extends JPanel
{
public MyButtonPanel()
{
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new BtnStartActionListener());
add(btnStart);
}
private class BtnStartActionListener implements ActionListener
{
// change to wait_cursor
public void actionPerformed(ActionEvent e)
{
if (!current_active)
{
Component root = SwingUtilities.getRoot((JButton) e.getSource());
JOptionPane.showMessageDialog(root, "Wait 10 seconds");
root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// TODO: Disabling the root component prevents the WAIT_CURSOR from being displayed
//root.setEnabled(false);
current_active = true;
new Thread(new TimeKiller(root)).start();
}
}
}
}
private class TimeKiller implements Runnable
{
Component m_root;
public TimeKiller(Component p_root)
{
m_root = p_root;
}
#Override
public void run()
{
try
{
Thread.sleep(10 * 1000);
}
catch (InterruptedException e)
{
//Ignore it
}
// Change back to DEFAULT CURSOR
JOptionPane.showMessageDialog(m_root, "Done waiting");
m_root.setCursor(Cursor.getDefaultCursor());
current_active = false;
}
}
// create and setup the window.
public static void createAndShowGUI()
{
WaitCursor frame = new WaitCursor();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
createAndShowGUI();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
});
}
}