How to make a JButton appear "as rolled over" - java

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

Related

multiple dispatchEvent calls on a JButton

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).

Tooltip to show a keystroke in java

Pressing the Control key I want to show a Jtooltip of a Jbutton.
How can I show a tooltip to the desired keystrokes?
Not sure if I understand the question, but you can use Control+F1 to display the tooltip of the currently focused component.
There is a private method in the TooltipManager called show which takes a JComponent as a parameter, which is used to show the tooltip. This is actually used by the TooltipManager when CTRL+F1 is pressed...
So, my first recommendation would be, use CTRL+F1 because it's built in. My second recommendation is to use CTRL+F1 because people press CTRL for a lot of reasons (like copy/paste, menu short cuts, etc), which could be rather annoying if you keep popping up tool tips all the time. My third recommendation is to use CTRL+F1 because the show method is private
However, because I'm simply curious (and completely crazy), you "could" (but I wouldn't recommend it) use a dirty, dirty hack (I fell like Phillip Fry), which is just as likely to blow up in your face as solve the problem (but I was curious about how to bind an action to the CTRL key)
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import static javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.ToolTipManager;
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 {
public TestPane() {
JButton btn = new JButton("Banana");
btn.setToolTipText("Hello");
add(btn);
TooltipPopup.register(this);
}
}
public static class TooltipPopup {
public static void register(JComponent comp) {
new TooltipPopup(comp);
}
private JComponent parent;
private boolean showing = false;
private TooltipPopup(JComponent parent) {
this.parent = parent;
bindKeyStrokeTo(parent,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
"help.press",
KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, InputEvent.CTRL_DOWN_MASK),
new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if (!showing) {
Component comp = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (comp != null && comp instanceof JComponent) {
JComponent focused = (JComponent) comp;
try {
Class clazz = ToolTipManager.sharedInstance().getClass();
Method method = clazz.getDeclaredMethod("show", JComponent.class);
method.setAccessible(true);
method.invoke(ToolTipManager.sharedInstance(), focused);
showing = true;
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
ex.printStackTrace();
}
}
}
}
});
bindKeyStrokeTo(parent,
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
"help.release",
KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0, true),
new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
showing = false;
}
});
}
public void bindKeyStrokeTo(JComponent parent, int condition, String name, KeyStroke keyStroke, Action action) {
InputMap im = parent.getInputMap(condition);
ActionMap am = parent.getActionMap();
im.put(keyStroke, name);
am.put(name, action);
}
}
}
All this does is binds a press and release Action to the CTRL for a given component (parent container), which will find the currently focused component and show it's tool tip.
It uses a reflection "tick" (hack) to call the private show method of the ToolTipManager.
You need the "press" and "release" actions, because otherwise you will get a repeated key event, which will make the tool tip "flash"
camickr solution is the better (and correct) choice, this was a simply "I wonder how" hack

How to make JSplitPane auto expand on mouse hover?

I want to make a Java program with Swing layout where there is a menu on the left that is expanded when a mouse hovers over the menu area, but auto contracts after your mouse leaves.
I am trying to imitate the effect of something like mobile Youtube for Android, or Weebly's editor. Fro those who don't know, both layouts have menus on the left that expand when your mouse hovers over them. Then after your mouse leaves the area, the menu contracts again and is out of view.
I was able to create the JSplitPane containing my menu successfully, but I have no idea how to make it automatically expand when the user's mouse hovers over the JSplitPane component, or how to make it contract after the mouse leaves the area.
In case anyone is wondering why: This type of menu is easy for the user to use but because it hides away when unneeded, allows me to have more space for the main part of the program.
Any help would be appreciated thanks!
Well make use of jSplitPan.setDividerLocation(location);: Sets the location of the divider. location - an int specifying a UI-specific value (typically a pixel count).
jSplitPane1.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent evt) {
// use evt.getSource() if needed
jSplitPan1.setDividerLocation(location);
}
});
You will probably need to compute the divider location a little bit intelligently. hints: by computing the relevant preferred-size hints. The part relevant should be discovered by you.
Take a look into official tutorial page to know: How to use MouseMotionListeners including other event listeners.
There are two basic problems...
You need to detect when the mouse hovers over the divider and
When it exists the "menu"
The second is relatively easy, you can use a MouseListener and monitor the mouseExit event, setting the position of the split pane divider as the mouse leaves.
This is complicated though, as if the user exists the "menu" by crossing over the divider, this may trigger the "menu" to made visible again...
The first problem is more complicated, as the JSplitPane contains three components, the left and right components, but also a divider component. JSplitPane doesn't actually allow access to the divider, which is rather annoying and because it uses it's own mouse listener, it blocks mouse events going to the split itself.
Instead, you need to gain access to it via the UI delegate.
Once you've set up the mouse listener, you need to ensure that the menu will only be shown when the menu is hidden, for this, I simply checked the size of the "menu" component
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
public class TestSpltPane {
public static void main(String[] args) {
new TestSpltPane();
}
public TestSpltPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final JPanel left = new JPanel();
left.setBackground(Color.RED);
left.setPreferredSize(new Dimension(100, 100));
JPanel right = new JPanel();
right.setBackground(Color.BLUE);
right.setPreferredSize(new Dimension(100, 100));
final JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
sp.setDividerLocation(0);
BasicSplitPaneDivider divider = ((BasicSplitPaneUI) sp.getUI()).getDivider();
divider.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (left.getWidth() == 0) {
sp.setDividerLocation(100);
}
}
});
left.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
sp.setDividerLocation(0);
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Don't click until button is released

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.

Java Swing JFrame suddenly stops responding to mouse input, but still takes keyboard inputs

I have a game that uses a JFrame that displays the game info. The window updates whenever a player sends a move object to the server. It works perfectly fine for any number of move objects. However once the 3nd turn starts it hits a wall and here is what happens:
The Jframe completely stops responding to left and right mouse clicks (it makes a windows ding sound when you try to click)
The JFrame still responds to mouse scrolls and keyboard inputs
The JFrame vanishes from the alt-tab program list.
NO error message or stack trace.
Using souts it appears that the code reaches all points of necessary code properly
I can't even click the "X" Window button or right-click close on the task bar
The 3rd turn object is structurally identical to previous turn objects
what on earth can cause a program to do this??
The event dispatch thread has thrown an exception. It is automatically restarted, but your program remains in the state you describe. See also How can I catch Event Dispatch Thread (EDT) exceptions and this answer.
Addendum: How uncaught exceptions are handled and Uncaught exceptions in GUI applications may be helpful. Also check for empty exception handlers.
Addendum: Here's an example.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/** #see https://stackoverflow.com/a/9935287/230513 */
public class Fail extends JPanel {
private static final JLabel label = new JLabel(
"12345678901234567890", JLabel.CENTER);
public Fail() {
this.setLayout(new GridLayout(0, 1));
this.add(label);
this.add(new JButton(new AbstractAction("Kill me, now!") {
#Override
public void actionPerformed(ActionEvent e) {
JButton b = (JButton) e.getSource();
b.setText(String.valueOf(1 / 0));
}
}));
new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(String.valueOf(System.nanoTime()));
}
}).start();
}
private void display() {
JFrame f = new JFrame("Example");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Fail().display();
}
});
}
}
Check if your frame class do not overrides isEnabled() method.
I spent couple of hours searching for exception but the responce was pretty trivial: I have implemented interface with such method.

Categories