Close current window using ctrl+w - java

I have a Java program that uses a bunch of JFrame objects. To make it easier to clean up desktop, I want to implement that the current focused window can be closed with Ctrl + w.
I tried to use a keybinding (in the superclass of any view) whose Action's actionPerformed method contains this:
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
This works quite well – as long as I use only one window. It only works, when the last-opened frame is focused, and it only closes that one.
My question is:
Why does the keybinding behaves like this? (I guess it's by design.)
How to create a keybinding per frame without adding any single component to a KeyListener.

Why does the keybinding behaves like this? (I guess it's by design.)
I'd guess you're doing something wrong, but without any kind of example code, it's impossible to know what
How to create a keybinding per frame without adding any single component to a KeyListener
There's a number of ways you might use to achieve this...
Global based solution...
One approach is to take a "global" approach, using a system which doesn't rely on you extending from a root solution, but which can be applied to just about any existing or future project.
AWTEventListener
One solution might be to attach a AWTEventListener to the Toolkit. This is quite low level and provides you access into the ALL the key events which the system is processing
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import javax.swing.FocusManager;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
private int count = 0;
private int xPos = 10;
private int yPos = 10;
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
installKeyboardMonitor();
for (int index = 0; index < 10; index++) {
makeWindow();
}
}
});
}
public static void installKeyboardMonitor() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
KeyEvent ke = (KeyEvent) event;
if (ke.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("Pressed");
if (ke.getKeyCode() == KeyEvent.VK_W) {
System.out.println("W Key");
if (ke.isControlDown()) {
System.out.println("Control down");
Window window = FocusManager.getCurrentManager().getActiveWindow();
if (window != null) {
window.dispose();
}
}
}
}
}
}, AWTEvent.KEY_EVENT_MASK);
}
public void makeWindow() {
count++;
JFrame frame = new JFrame("Test " + count);
frame.setContentPane(new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
});
frame.add(new JLabel("Window " + count));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocation(xPos, yPos);
frame.setVisible(true);
xPos += 100;
yPos += 100;
}
}
KeyEventDispatcher
This is slightly less low level then the AWTEventListener but it focus only on KeyEvents which makes it a little easier to manage, but is essentially the same idea
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.awt.event.KeyEvent;
import javax.swing.FocusManager;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
private int count = 0;
private int xPos = 10;
private int yPos = 10;
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
installKeyboardMonitor();
for (int index = 0; index < 10; index++) {
makeWindow();
}
}
});
}
public static void installKeyboardMonitor() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent ke) {
if (ke.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("Pressed");
if (ke.getKeyCode() == KeyEvent.VK_W) {
System.out.println("W Key");
if (ke.isControlDown()) {
System.out.println("Control down");
Window window = FocusManager.getCurrentManager().getActiveWindow();
if (window != null) {
window.dispose();
return true;
}
}
}
}
return false;
}
});
}
public void makeWindow() {
count++;
JFrame frame = new JFrame("Test " + count);
frame.setContentPane(new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
});
frame.add(new JLabel("Window " + count));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocation(xPos, yPos);
frame.setVisible(true);
xPos += 100;
yPos += 100;
}
}
Configurable solution
Another solution is to provide a "configuration" based solution. This is similar to the concept of having a base component, but frees you from been locked into a single extension point.
This approach is a little more troublesome, as you actually need to remember to apply to every window and dialog your application might create.
It simply uses the Key Bindings API to register a binding against the windows JRootPane, but you could use just about any component which you know isn't going to be removed from the window.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
private int count = 0;
private int xPos = 10;
private int yPos = 10;
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int index = 0; index < 10; index++) {
makeWindow();
}
}
});
}
public static void installKeyBindings(JComponent component) {
InputMap inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = component.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK), "Window.close");
actionMap.put("Window.close", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Window window = SwingUtilities.windowForComponent(component);
if (window != null) {
window.dispose();
}
}
});
}
public void makeWindow() {
count++;
JFrame frame = new JFrame("Test " + count);
installKeyBindings(frame.getRootPane());
frame.setContentPane(new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
});
frame.add(new JLabel("Window " + count));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocation(xPos, yPos);
frame.setVisible(true);
xPos += 100;
yPos += 100;
}
}
After thoughts
This is just three possible solutions. It wouldn't take to much effort to provide a more configurable based solution around each one (so you could supply the key stroke), but I'll leave that up to you

Related

Progress Bar value is not updating [duplicate]

I have made a very simple code to show it here, i have a button that should show a JDialog to check the progress status, i am using the invoke late to go through EDT and my loop isn't in the run method, so why isn't my bar updating ?
here is the code
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class JBarEx extends JFrame {
private JTextField progStatus = new JTextField("Undefined");
private JButton dialogBtn = new JButton("Show Progression dialog");
final JDialog dlg = new JDialog((JFrame) null, "prog Title", false);
final JProgressBar dpb = new JProgressBar(0, 100);
public JBarEx() {
JPanel pan = new JPanel();
dialogBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
showProgress();
}
});
progStatus.setEditable(false);
pan.add(progStatus);
pan.add(dialogBtn);
setContentPane(pan);
this.setSize(200, 100);
setVisible(true);
}
public void showProgress() {
dlg.add(BorderLayout.CENTER, dpb);
dlg.add(BorderLayout.NORTH, new JLabel("prog message"));
dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(null);
dlg.setVisible(true);
for (int i = 0; i < 100; i++) {
final int ii = i;
try {
Thread.sleep(25);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
updateBar(ii);
}
});
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void updateBar(int newValue) {
dpb.setValue(newValue);
}
public static void main(String[] args) {
JBarEx jbx = new JBarEx();
}
}
Your showProgress method is being executed within the context of the Event Dispatching Thread. The EDT is responsible for, amongst other things, processing paint requests. This means that so long as your for-loop is executing, the EDT can not process any new paint requests (or handle the invokeLater events either) as it is blocking the EDT.
While there are any number of possible ways to solve the problem, based on your code example, the simplest would be to use a SwingWorker.
It has the capacity to allow your to execute the long running task the a background thread (freeing up the EDT), but also allows you means for publishing updates (if required) so that they can be processed in the EDT and also provides handy progress notification.
For example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class SwingWorkerProgress {
public static void main(String[] args) {
new SwingWorkerProgress();
}
public SwingWorkerProgress() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar pbProgress;
private JButton start;
public TestPane() {
setBorder(new EmptyBorder(10, 10, 10, 10));
pbProgress = new JProgressBar();
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridx = 0;
gbc.gridy = 0;
add(pbProgress, gbc);
start = new JButton("Start");
gbc.gridy++;
add(start, gbc);
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start.setEnabled(false);
ProgressWorker pw = new ProgressWorker();
pw.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if (name.equals("progress")) {
int progress = (int) evt.getNewValue();
pbProgress.setValue(progress);
repaint();
} else if (name.equals("state")) {
SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
switch (state) {
case DONE:
start.setEnabled(true);
break;
}
}
}
});
pw.execute();
}
});
}
}
public class ProgressWorker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
for (int i = 0; i < 100; i++) {
setProgress(i);
try {
Thread.sleep(25);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
}
Check out Concurrency in Swing for more details
Even if you fix the loop as others have pointed out, you'd still block the event dispatch thread. The for loop is run in showProgress() which is called from an event listener. The updates are pushed to the event queue, but that does not get processed until the loop has completed.
Use a Swing Timer instead. Something like this:
Timer timer = new Timer(25, new ActionListener() {
private int position;
#Override
public void actionPerformed(ActionEvent e) {
position++;
if (position < lastPosition) {
updateBar(position);
} else {
((Timer) e.getSource).stop();
}
}
});
timer.start();
where lastPosition would be the state where you want the progress bar to stop.
Unrelated to that bug, but a bug still, you should not create swing components outside the event dispatch thread. It's best to do it right from the start:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JBarEx jbx = new JBarEx();
}
});
}
for (int i = 0; i < 0; i++) {
You will never enter this code so will never call the updateBar(..) method
i needs to be greater than 0 in this case. If it is 1 then updateBar will be called once, if 2 then updateBar will be called twice etc
Also rather than doing
Thread.sleep(25);
take a look at java executors as these will help with your scheduling and remove the need for the sleep

Issue with frame.setSize on macOS (java code)

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
}
});
}
}

Java Swing Sending Navigation Commands Using Arrow Buttons

I need to send navigation commands using JButtons and the keyboard. If the "Up" button/up key is pressed I need to send "Move North" command, if up and left buttons are pressed(from keyboard), I need to send "Move North West" command etc.. The commands should be sent periodically(every 1 second). The following is the code I have.
To explain more,
There are three JButtons on the view. Let's call them jUp, jLeft and jRight. When the user presses the jUp button on the view, the program should send moveNorth commands periodically until the user releases the jUp button. When the user presses the up button on the keyboard, the same thing should happen, and the jUp button should look pressed until the user releases the keyboard up button. When the user presses the keyboard up and left buttons together, the jUp and jLeft buttons should appear pressed until user releases the keyboard buttons. And until user releases the keyboard buttons, a move northWest command should be sent periodically. In the code I have just printed the command using a System.out.println.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class ButtonDemo {
private JPanel buttons;
private Timer t;
private JButton upButton;
private JButton leftButton;
private JButton rightButton;
public static void main(String[] args) {
new ButtonDemo().run();
}
public ButtonDemo() {
buttons = new JPanel(new BorderLayout());
this.t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (upButton.isSelected() && leftButton.isSelected()) {
System.out.println("Move north west");
} else if (upButton.isSelected() && rightButton.isSelected()) {
System.out.println("Move north east");
} else if (upButton.isSelected()) {
System.out.println("Move north");
} else {
t.stop();
}
}
});
}
void run() {
this.upButton = new JButton("Up");
buttons.add(upButton, BorderLayout.NORTH);
setupButton(upButton, "Up", KeyEvent.VK_UP);
this.leftButton = new JButton("Left");
buttons.add(leftButton, BorderLayout.WEST);
setupButton(leftButton, "Left", KeyEvent.VK_LEFT);
this.rightButton = new JButton("Right");
buttons.add(rightButton, BorderLayout.EAST);
setupButton(rightButton, "Right", KeyEvent.VK_RIGHT);
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(buttons, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private void setupButton(JButton button, String key, int vkUp) {
buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(vkUp, 0),
key + " pressed");
buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(vkUp, 0, true),
key + " released");
buttons.getActionMap().put(key + " pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
button.setSelected(true);
pressed(key);
}
});
buttons.getActionMap().put(key + " released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
button.setSelected(false);
}
});
}
private void pressed(String key) {
if (!t.isRunning()) {
t.start();
}
}
}
Now for the questions.
a) Even though I call the setSelected method, the button state does not change to pressed. (Visually it doesn't change to pressed state). How can I achieve this?
b) Is there a better/more standard way of achieving this functionality? Using mnemonics/ExecutorService etc..? Am I correct in adding the actions to the "buttons" element's input map. (Is buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) correct?) The panel will be in a tab and the buttons should work when that tab is selected.
JButton doesn't have a selected state, it has an armed. In order to have a button which maintain a "pressed" state when the mouse or key is released, you must use a JToggleButton.
I would, personally, move away from monitoring the button states, personally, and instead, use some kind enum or other constants which can be added and removed from a Set. This decouples the means by which the state is achieved from the process that is acting upon the state.
From there, you can use a single Action which would be used to notify some kind of observer that the state has changed
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.Timer;
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 static class TestPane extends JPanel {
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
private JToggleButton[] buttons;
private Set keys;
private Timer timer;
private JLabel direction;
public TestPane() {
keys = new HashSet();
direction = new JLabel("Stopped");
timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (keys.isEmpty()) {
((Timer) e.getSource()).stop();
direction.setText("Stopped");
} else {
StringJoiner joiner = new StringJoiner("-");
if (keys.contains(Direction.UP)) {
joiner.add("North");
}
if (keys.contains(Direction.DOWN)) {
joiner.add("South");
}
if (keys.contains(Direction.LEFT)) {
joiner.add("West");
}
if (keys.contains(Direction.RIGHT)) {
joiner.add("East");
}
direction.setText(joiner.toString());
}
}
});
Monitor monitor = new Monitor() {
#Override
public void pressed(Direction direction) {
keys.add(direction);
timer.restart();
}
#Override
public void released(Direction direction) {
keys.remove(direction);
}
};
MovementAction up = new MovementAction("Up", Direction.UP, monitor);
MovementAction down = new MovementAction("Down", Direction.DOWN, monitor);
MovementAction left = new MovementAction("Left", Direction.LEFT, monitor);
MovementAction right = new MovementAction("Right", Direction.RIGHT, monitor);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.gridx = 1;
buttons = new JToggleButton[4];
buttons[0] = new JToggleButton(up);
buttons[1] = new JToggleButton(down);
buttons[2] = new JToggleButton(left);
buttons[3] = new JToggleButton(right);
add(buttons[0], gbc);
gbc.gridy = 2;
add(buttons[1], gbc);
gbc.gridy = 1;
gbc.gridx = 0;
add(buttons[2], gbc);
gbc.gridx++;
add(direction, gbc);
gbc.gridx++;
add(buttons[3], gbc);
addTriggerKeyBindingTo(buttons[0], KeyEvent.VK_UP, KeyEvent.VK_W, KeyEvent.VK_NUMPAD8);
addTriggerKeyBindingTo(buttons[1], KeyEvent.VK_DOWN, KeyEvent.VK_S, KeyEvent.VK_NUMPAD2);
addTriggerKeyBindingTo(buttons[2], KeyEvent.VK_LEFT, KeyEvent.VK_A, KeyEvent.VK_NUMPAD6);
addTriggerKeyBindingTo(buttons[3], KeyEvent.VK_RIGHT, KeyEvent.VK_D, KeyEvent.VK_NUMPAD4);
}
protected void addTriggerKeyBindingTo(JToggleButton comp, int... virtualKeys) {
InputMap im = comp.getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = comp.getActionMap();
for (int key : virtualKeys) {
im.put(KeyStroke.getKeyStroke(key, 0), "trigger");
}
am.put("trigger", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JToggleButton button = (JToggleButton) e.getSource();
button.doClick();
}
});
}
protected class MovementAction extends AbstractAction {
private Direction direction;
private Monitor monitor;
public MovementAction(String name, Direction direction, Monitor monitor) {
putValue(NAME, name);
this.direction = direction;
this.monitor = monitor;
putValue(SELECTED_KEY, false);
}
#Override
public void actionPerformed(ActionEvent e) {
boolean selected = (boolean) getValue(SELECTED_KEY);
if (selected) {
monitor.pressed(direction);
} else {
monitor.released(direction);
}
}
}
public interface Monitor {
public void pressed(Direction direction);
public void released(Direction direction);
}
}
}
Now, this example doesn't care, but you could use the Monitor to control which key/buttons where triggered at any one time, probably by returning a boolean value from pressed for example...

Is it possible to make some items in the menu to fade in with 500 ms onset delay in Java?

I have a JMenu of 16 JMenuItems, of which I want 3 items to be displayed upfront and the rest 13 items to fade in with a 500 ms delay. Is there a way to do this animation in Java?
This is not as easy as it sounds.
Basically I originally thought "I'll attach a popup listener to the popup menu that the menu items are added to"...but apparently this doesn't work so well. The menu popup is built dynamically on demand. Makes sense, but it's still a pain.
So instead, I've found that if I wait for addNotify I can simply start the animation engine.
The animation engine is a simple concept. It has a javax.swing.Timer that ticks at a regular interval. Coupled with a start time and a duration, we can calculate the progress of the animation and generate the alpha value as required.
The only thing left is then to notify all the interested parties that the animation has changed and voila...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class FadeMenu {
private AnimationEngine engine;
public static void main(String[] args) {
new FadeMenu();
}
public FadeMenu() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
engine = new AnimationEngine();
JMenuBar mb = new JMenuBar();
JMenu flip = new JMenu("Flip");
flip.add("Static 1");
flip.add("Static 2");
flip.add("Static 3");
flip.add(new FadeMenuItem("Fade 1"));
flip.add(new FadeMenuItem("Fade 2"));
flip.add(new FadeMenuItem("Fade 3"));
flip.add(new FadeMenuItem("Fade 4"));
mb.add(flip);
JFrame frame = new JFrame("Testing");
frame.setJMenuBar(mb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class FadeMenuItem extends JMenuItem {
public FadeMenuItem(String text) {
super(text);
engine.addTimingListener(new TimingListener() {
#Override
public void timingEvent() {
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(engine.getAlpha()));
super.paintComponent(g2d);
g2d.dispose();
}
#Override
public void removeNotify() {
Container parent = getParent();
if (parent instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) parent;
engine.stop();
}
super.removeNotify();
}
#Override
public void addNotify() {
super.addNotify();
Container parent = getParent();
if (parent instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) parent;
engine.restart();
}
}
}
public interface TimingListener {
public void timingEvent();
}
public class AnimationEngine {
private Timer fade;
private float alpha;
private long startTime;
private long duration = 1000;
private List<TimingListener> listeners;
public AnimationEngine() {
listeners = new ArrayList<>(5);
fade = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed >= duration) {
((Timer) e.getSource()).stop();
alpha = 1f;
} else {
alpha = (float) elapsed / (float) duration;
}
fireTimingEvent();
}
});
fade.setRepeats(true);
fade.setCoalesce(true);
fade.setInitialDelay(500);
}
public void addTimingListener(TimingListener listener) {
listeners.add(listener);
}
public void removeTimingListener(TimingListener listener) {
listeners.add(listener);
}
protected void fireTimingEvent() {
for (TimingListener listener : listeners) {
listener.timingEvent();
}
}
public void restart() {
fade.stop();
alpha = 0;
fireTimingEvent();
startTime = System.currentTimeMillis();
fade.start();
}
public float getAlpha() {
return alpha;
}
public void stop() {
fade.stop();
}
}
}
While this works on Windows, I'd be concerned that it might not work on other platforms, as the means by which the menus are generated are controlled (in part) by the UI delegate. This could become very messy, very quickly
start a timer to fire an event to fade in

Hiding and showing Panel during runtime

I'm trying to show and hide a panel during run time, so I call the methods from another class:
Con.Action(1);
to this method:
public void Action(int whichPanel) {
if (whichPanel == 1) {
if (frame.Data.isVisible()) {
frame.Data.setVisible(false);
// frame.splitPaneSec.remove(frame.Data);
} else {
System.out.println(".....");
//frame.Data.setVisible(false);
frame.Data.setVisible(true);
//frame.getContentPane().validate();
//frame.revalidate();
//frame.repaint();
//frame.pack();
}
}
So far i'm able to hide a panel but I can't show it again once I hidden it.
I've tried many ways some of them are commented out.
Any help is appreciated, cheers
I think I would rather remove and add it back.
parent.remove(dataPanel);
parent.validate();
parent.repaint();
parent.add(dataPanel);
parent.validate();
parent.repaint();
I highly recommed you to use enums for Tasks like this:
public void Action(int whichPanel) {
if (whichPanel == 1) {}
//Better;
public void Action(enumType myPanel){
if(myPanel == enumType.LoginScreen){}
with `
public enum enumType
{
LoginScreen,EditScreen //...
}`
You can hide and show Enums if you perfom the setVisible method directly on the Panel you want to show:
public void showLoginScreen()
{
loginPanel.setVisible(true);
registerMenu.setVisible(false);
}
The issue was because after setting the component back to visible
it was required to GUIClass.splitPane.setDividerLocation(157);
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
public class HidePanel extends JFrame {
private static final int PANEL_HEIGHT = 50;
private static final int INITIAL_PANEL_WIDTH = 50;
private static final int TIMER_INTERVAL = 1;
private static final int PIXEL_DELTA = 2;
private Dimension panelDimension = new Dimension(INITIAL_PANEL_WIDTH, PANEL_HEIGHT);
private int vertOffset = -PANEL_HEIGHT;
private Timer showTimer = new HidePanel.ShowPanelTimer();
private Timer hideTimer = new HidePanel.HidePanelTimer();
static JLayeredPane lpane = new JLayeredPane();
JPanel panel = new JPanel();
public HidePanel() {
setBackground(Color.BLACK);
setLayout(null);
panel.setBounds(1200, 50, 100, 600);
panel.setBorder(new TitledBorder(new EtchedBorder(), "Hide"));
panel.add(new JButton("Click Me"));
lpane.add(panel);//, //new Integer(0), 0);
add(panel);
addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
final int height = e.getY() - 100 ;
if (height < 50 && !showTimer.isRunning() && !panel.isVisible()) {
showTimer.start();
} else if (height > PANEL_HEIGHT + 5 && !hideTimer.isRunning() && panel.isVisible()) {
hideTimer.start();
}
}
});
}
public static void main(String args[]) {
HidePanel h = new HidePanel();
h.setSize(1350, 700);
h.setVisible(true);
h.add(lpane, BorderLayout.CENTER);
}
private void positionPanel(final int offset) {
int panelWidth = getWidth();
System.out.println(" offset = " + offset);
panelDimension.setSize(panelWidth, PANEL_HEIGHT);
//panel.setBounds(insets.left, offset + insets.top, size.width, size.height);
}
private class ShowPanelTimer extends Timer implements ActionListener {
ShowPanelTimer() {
super(TIMER_INTERVAL, null);
addActionListener(this);
}
public void start() {
vertOffset = -PANEL_HEIGHT;
panel.setVisible(true);
super.start();
}
public void actionPerformed(ActionEvent e) {
if (vertOffset <= 0) {
positionPanel(vertOffset);
} else {
showTimer.stop();
}
vertOffset += PIXEL_DELTA;
}
}
private class HidePanelTimer extends Timer implements ActionListener {
HidePanelTimer() {
super(TIMER_INTERVAL, null);
addActionListener(this);
}
public void stop() {
panel.setVisible(false);
super.stop();
}
public void actionPerformed(ActionEvent e) {
if (vertOffset >= (-PANEL_HEIGHT)) {
vertOffset -= PIXEL_DELTA;
positionPanel(vertOffset);
} else {
hideTimer.stop();
}
}
}
}

Categories