JRootpane - setting glasspane to visible doesnt intercept mouse events - java

I have a JFrame and a JPanel hierarchy inside it, i want to implement an inner panel that i can make it look "disabled"(while other panels dont change), that is, to cover it by a semi transparent gray layer and intercept all mouse and perhaps even keyboard events that are dispatched to this panel. ive been searching for a solution and didnt really find a good one yet.
The closest i got to a solution was when i used JRootPane, whenever i want it disabled i make its glasspane visible. the glasspane had been set to be opaque and with semi transparent background.
A simple example of my attempt :
public class Test extends JFrame {
private final JPanel jPanel;
public Test() {
jPanel = new JPanel();
final JButton jButton = new JButton("Hidden");
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("hidden is clicked!");
}
});
final JRootPane jRootPane = new JRootPane();
jPanel.add(jRootPane);
final JPanel glassPane = new JPanel();
final JButton jButton2 = new JButton();
jButton2.addActionListener(new ActionListener() {
private boolean visible = true;
#Override
public void actionPerformed(ActionEvent e) {
glassPane.setVisible(visible = !visible);
}
});
jPanel.add(jButton2);
jRootPane.getContentPane().add(new JScrollPane(jButton));
glassPane.setBackground(new Color(0.5f, 0.5f, 0.5f, 0.2f));
glassPane.setOpaque(true);
jRootPane.setGlassPane(glassPane);
glassPane.setVisible(true);
getContentPane().add(jPanel);
}
public static void main(String[] strings) {
final Test test = new Test();
test.pack();
test.setVisible(true);
}
}
But the problem is that even when the glass is visible on top of the content, it doesnt intercepts events from getting to the content as it should, as documented here.

In your test class your glasspane doesn't intercept events, because you didn't tell it to intercept events (intercepting events is not a default behavior).
In the documentation link, it says
The glass pane
The glass pane is useful when you want to be able to catch events or paint over an area that already contains one or more components. For example, you can deactivate mouse events for a multi-component region by having the glass pane intercept the events. Or you can display an image over multiple components using the glass pane.
You can intercept Mouse Events this way:
glassPane.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
e.consume();
}
#Override
public void mousePressed(MouseEvent e)
{
e.consume();
}
});
You can intercept KeyBoard Events this way:
glassPane.setFocusable(true);
glassPane.addKeyListener(new KeyListener()
{
#Override
public void keyTyped(KeyEvent e)
{
e.consume();
}
#Override
public void keyReleased(KeyEvent e)
{
e.consume();
}
#Override
public void keyPressed(KeyEvent e)
{
e.consume();
}
});
Note: The JPanel has to be focasable to intercept the keyboard events.

Related

JButtons MouseEvents

I want my buttons to change color when mouse is on them (hover), when they are pressed (active) and stay this way until user will choose other options. So one of them will be always choosen.
I have three buttons - with cricle, square and traingle. My code:
private static MouseListener ButtonMouseListener = new MouseAdapter () {
public void mouseEntered(MouseEvent evt) {
Component source = evt.getComponent();
source.setBackground(new Color(91, 90, 90));
}
public void mouseExited(MouseEvent evt) {
Component source = evt.getComponent();
source.setBackground(new Color(64, 64, 64));
}
public void mousePressed(MouseEvent evt) {
Component source = evt.getComponent();
source.setBackground(new Color(46, 46, 46));
}
};
So I want buttons to change color when hovering over them (mouseEntered), and again change color to default when someone stops hovering (that's why I have mouseExited). The next thing is that I want them to change color when they are choosen, so mousePressed . The problem is that when I move the cursor outside the button it changes to another color because of mouseExited and I don't want that. It has to stay the 'pressed color' until user chooses another of three buttons. I have no idea how to achieve that, I tried different options but nothing works the way I want.
For entry color change you need to implement the Event->Mouse->mouseEntered. Make sure you are implementing it on mouseExited
On mouse pressed color change you need to implement Event->Action. Inside actionPerformed, you can set the button color. For example :
jButton7.setBackground(new Color(11, 118, 219));
jButton1.setBackground(new Color(15,44,123));
jButton8.setBackground(new Color(15,44,123));
jButton9.setBackground(new Color(15,44,123));
You can use JToggleButton instead of JButton since they have their own UI property for selection (pressed) background. You can override it like this:
UIManager.put("ToggleButton.select", Color.GREEN);
An SSCCE of what I would do:
public class ToggleButtonsExample extends JFrame {
private static final Color PRESSED_COLOR = Color.BLUE;
private static final Color HOVER_COLOR = Color.RED;
public ToggleButtonsExample() {
super("Example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
JToggleButton button1 = new JToggleButton("button1");
JToggleButton button2 = new JToggleButton("button2");
JToggleButton button3 = new JToggleButton("button3");
final Color defaultBackgroundColor = button1.getBackground();
MouseListener hoverColorMouseListener = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
Component component = e.getComponent();
component.setBackground(HOVER_COLOR);
}
#Override
public void mouseExited(MouseEvent e) {
Component component = e.getComponent();
component.setBackground(defaultBackgroundColor);
}
};
List<AbstractButton> buttons = Arrays.asList(button1, button2, button3);
buttons.forEach(this::add);
buttons.forEach(b -> b.addMouseListener(hoverColorMouseListener));
setLocationByPlatform(true);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
UIManager.put("ToggleButton.select", PRESSED_COLOR);
new ToggleButtonsExample().setVisible(true);
});
}
}
And a preview image:

AddMouseListener into another AddMouseListener

I had a problem while calling two mouse events, one into the other. I wanted to show a second frame (frame2) when the user clicks on a component (component1) from the first frame (frame1), then returns to the previous frame (frame1) if the component2 is clicked on. All this using one file.
This is what I wrote:
component1.addMouseListener(this on);
#Override
public void mouseClicked(MouseEvent e)
{
if(e.getSource() == component1)
{
frame1.dispose();
frame2.setVisible(true);
component2.addMouseListener(new MouseAdapter() {
public void mouseClicked() {
frame2.dispose();
frame1.setVisible(true);
}
});
}
}
The first event works, but not the second.
Thank you for answering.
Here is a fully functional example where there are 2 frames, each with a label that, when clicked, hides one frame and shows the other, done in Java 10. See if this works for you and explain how your code differs from this. Note that I only created 2 MouseListeners, one for each frame. I did not recreate the MouseListener in the other MouseListener's code. Also, I did not dispose the frame, which will likely cause problems. If I had disposed frame1, I would most likely have to create a new JFrame and assign it to the frame1 instance member.
Please note you have to click on the label itself, not somewhere else on the frame.
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TwoFrames {
public static void main(String[] args) {
TwoFrames twoFrames = new TwoFrames();
twoFrames.start();
}
private void start() {
setupFrames();
}
JFrame frame1 = new JFrame("Frame 1"),
frame2 = new JFrame("Frame 2");
JLabel component1 = new JLabel("Click me 1"),
component2 = new JLabel("Click me 2");
private void setupFrames() {
frame1.getContentPane().add(component1);
frame2.getContentPane().add(component2);
component1.setOpaque(true);
component2.setOpaque(true);
component1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
frame1.setVisible(false);
frame2.setVisible(true);
}
});
component2.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
frame2.setVisible(false);
frame1.setVisible(true);
}
});
frame1.setSize(300, 300);
frame2.setSize(400, 400);
frame1.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame2.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(() -> frame1.setVisible(true));
}
}
The dispose() method actually destroys the window and so, frame1 should become null and you are most likely getting a null pointer exception.
Consider calling frame1.setVisible(false) and frame2.setVisible(false) instead of the dispose() method.
Also, you could consider using to separate mouse listener objects instead of adding a new mouse listener to component2 when component1 is clicked.

MouseEvents are Captured but not KeyEvents Java JComponant

I'm working on an old game project called Lemmings, and the Principle Game Panel is working well and receiving the MouseEvents but not the KeyEvents, which is not very logic for me, so I copied down the Code of this file for you guys to see whats going on.
The GamePanel class extends JComponent SWING class
public class GameFrame {
private class GamePanel extends JComponent {
GamePanel(Dimension dim) {
setPreferredSize(dim);
//first version of the question was with the keyListner
/*addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
System.out.println(e.getKeyCode());
//nothing show up
}
});*/
//I tried using this, but it didn't work
//getInputMap().put(KeyStroke.getKeyStroke("A"), "action");
// this works cause we use the right inputMap not the one by default
getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("A"), "action");
getActionMap().put("action",new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("A is pressed");
//now it works
}
});
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
System.out.println(e.getPoint());
}
});
setVisible(true);
}
}
private JFrame window;
private GamePanel panel;
public GameFrame() {
window = new JFrame("Test");
window.setLocationRelativeTo(null);
panel = new GamePanel(new Dimension(400, 400));
window.setContentPane(panel);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
public static void main(String[] args) {
new GameFrame();
}
}
UPDATE WITH SOLUTION
I learned that JComponants are not focusable and so it doesn't receive KeyEvents so we have to use the Key Bindings method
I found out that every JComponent has three inputMaps referred to by WHEN_IN_FOCUSED_WINDOW, WHEN_FOCUSED, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and we have to make sure that we are using the right one for our work
for more informations look on How to use key Bindings and check the methods getInputMap() and getInputMap(int)
Key events are only dispatch to a focusable component.
By default a JPanel is not focusable so it does not receive key events.
If you are attempting to invoke some kind of Action based on a KeyEvent then you should be using Key Bindings, not a KeyListener. A key binding will allow you to listen for a KeyStroke even if the component doesn't have focus.
Read the section from the Swing tutorial on How to Use Key Bindings for more information and working examples.
the problem can be solved simply by adding a KeyListener to the JFrame in case we don't need to create Actions and stuff
this solution can only be possible if there are no other components focusable.
in the file GameFrame.java
in the function void init();
add
window.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed (KeyEvent e) {
super.keyPressed(e);
System.out.println("test "+e.getKeyChar());
}
});

Custom wheel listener prevent ScrollPane Wheel Listener

I add a custom mousewheellistener to my JPanel, because I will implement zoom feature for this panel which working with ctrl+mousewheel. Also my JPanel is in a JScrollpane. My problem is; when I adding MouseWheelListener to the panel, mouse wheel doesn't work for scrollpane even if Ctrl is not pressed. I want that; mouse wheel work for zoom when ctrl pressed and if not pressed it should work for scrollpane. How can I do that?
public class ZoomTest {
public static void main(String[] args) {
JPanel jZoomPanel = new JPanel();
jZoomPanel.setPreferredSize(new Dimension(300, 500));
jZoomPanel.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if(e.isControlDown()) {
// TODO implement zoom
}
}
});
JScrollPane jsc = new JScrollPane(jZoomPanel);
JFrame jDemoFrame = new JFrame();
jDemoFrame.getContentPane().add(jsc);
jDemoFrame.setSize(300, 300);
jDemoFrame.setVisible(true);
}
}
You should dispatch your event like this:
jZoomPanel.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if(e.isControlDown()) {
// TODO implement zoom
} else {
JPanel panel = (JPanel) me.getSource();
MouseEvent newMe = SwingUtilities.convertMouseEvent(panel, me, jsc);
jsc.dispatchEvent(me);
}
}
});

Why is the focus never lost from my component when I press the JFrame?

I have a problem with the focus listener implemented by CustomTextField class. The focus listener is only called when another Swing component is getting the focus. But If I move the JFrame istelf by dragging it with the mouse, the focusLost() method is never called (in other words, it doesn´t seem that the focus is shifting from CustomTextField to JFrame).
EDIT: The solution of my question is below:
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import javax.swing.*;
public class ScrollFocus extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollFocus();
}
});
}
public ScrollFocus() {
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Vector<String> values = new Vector<>();
values.add("a");
values.add("b");
values.add("c");
values.add("d");
values.add("e");
JComboBox<String> comboBox = new JComboBox<>(values);
JScrollPane scrollPane = new JScrollPane(comboBox);
this.add(scrollPane, BorderLayout.NORTH);
CustomTextField customTextField = new CustomTextField();
this.add(customTextField, BorderLayout.CENTER);
JButton button = new JButton("press");
final JPopupMenu menu = new JPopupMenu("Menu");
menu.add(new JMenuItem("Test"));
button.setComponentPopupMenu(menu);
this.add(button, BorderLayout.SOUTH);
pack();
setVisible(true);
}
class CustomTextField extends JTextField implements FocusListener {
private CustomPopup customPopup = new CustomPopup();
public CustomTextField() {
this.addFocusListener(this);
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "VK_UP");
this.getActionMap().put("VK_UP", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
setPopupSize();
customPopup.show(CustomTextField.this, CustomTextField.this.getX(), CustomTextField.this.getY() + CustomTextField.this.getHeight());
customPopup.setSelectedIndex(0);
}
});
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "VK_DOWN");
this.getActionMap().put("VK_DOWN", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
setPopupSize();
customPopup.show(CustomTextField.this, CustomTextField.this.getX(), CustomTextField.this.getY() + CustomTextField.this.getHeight());
customPopup.setSelectedIndex(0);
}
});
}
public void setPopupSize() {
customPopup.setPopupSize(new Dimension(this.getWidth(), 110));
}
#Override
public void focusGained(FocusEvent e) {
}
#Override
public void focusLost(FocusEvent e) {
}
class CustomPopup extends JPopupMenu {
String[] values = new String[]{"Value1", "Value2", "Value3", "Value4", "Value5", "Value6", "Value7",
"Value8","Value9", "Value10", "Value11", "Value12", "Value13", "Value14", "Value15", "Value16",};
JList<String> list = new JList<>(values);
JScrollPane scrollPane = new JScrollPane(list);
public int index = 0;
public CustomPopup() {
this.setLayout(new GridLayout(0,1));
this.add(scrollPane);
this.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP){
if(customPopup.index > 0)
customPopup.setSelectedIndex(--customPopup.index);
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN){
if(customPopup.index < customPopup.getListSize()-1)
customPopup.setSelectedIndex(++customPopup.index);
}
}
});
this.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
index=0;
}
});
pack();
}
public void setSelectedIndex(int index) {
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
requestFocus();
}
public int getListSize() {
return values.length;
}
}
}
}
//customPopup.setVisible(true);
customPopup.show((JComponent)e.getSource(), 0, 20);
You should be using the show(...) method to show the popup. This must add some listeners to the popup so you will no longer need the FocusListener on the text field.
However, now this is a different problem. The text field loses focus so the Action never get invoked. That would be ok, but the JList never gains focus so it doesn't respond to the up/down keys unless you click on the list box first. I'm not sure what the problem is here.
Maybe you can try to make the popup, scrollpane and list all non-focusable so that focus remains on the text field?
'Focus', which is admittedly a slightly ambiguous term, generally applies to a component, not to an entire window. We think of the "window with focus", but I think what we really mean is "the current window, the one which contains the focus." I would not expect focus_lost to be called if I moved the window (aka JFrame) itself.
Another way to think of it; if I had a text field, clicked in it, and typed a letter or two, I would see those letters in that text field. If I then moved the window slightly and typed another letter or two, I would still expect those letters to appear in that field. It still has focus, and never lost it.

Categories

Resources