I want to block input to a window, but still be able to move it.
If there was a modal dialog type allowing the window that spawned it to move, then I would be happy.
Say I have a window that opens another window. This second window then opens a modal dialog, which blocks input to the other two windows (fine), but also locks these two windows in place (why – Amigas didn’t do this 🙂 ?).
My problem is that I may need to visually read something in the first window for use in the dialog, but this may not be possible because the second window is locked in place, covering it.
I have almost solved this with glass panes, I think. I set the class below to be the glass pane of the root pane of my window, then I call setVisible(true) on it when I want to block and setVisible(false) when I want to unlock the window. When locked, the window greys out to indicate this.
Mouse input is blocked except for closing the window which is fine for now – the problem is that I can still tab around the components on the blocked window and if I get to an editable one, I can edit it with the keyboard, regardless of my empty KeyListener.
Is there an easy way I can prevent the components behind the glass pane from gaining focus?
I am hoping it can be done on the “InputSink” class itself.
I have tried adding its own selfish focus traversal policy and requesting focus when it is visible, but this has no effect.
I have also tried an example I found where a FocusListener was added, whose focusLost method requests focus if the glass pane is visible, but that is overkill, as the window then always stays at front.
Does anybody know a solution in between those two extremes? This is what I have:
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;
import javax.swing.JPanel;
public class InputSink extends JPanel {
public InputSink() {
this(0.2f); //Default opacity.
}
public InputSink(float alpha) {
setOpaque(false);
setBackground(new Color(0, 0, 0, alpha)); //Just store it here.
addMouseListener(new MouseAdapter() {});
addKeyListener(new KeyAdapter() {});
setFocusTraversalPolicy(new FocusTraversalPolicy() {
@Override
public Component getLastComponent(Container aContainer) {
return InputSink.this;
}
@Override
public Component getFirstComponent(Container aContainer) {
return InputSink.this;
}
@Override
public Component getDefaultComponent(Container aContainer) {
return InputSink.this;
}
@Override
public Component getComponentBefore(Container aContainer, Component aComponent) {
return InputSink.this;
}
@Override
public Component getComponentAfter(Container aContainer, Component aComponent) {
return InputSink.this;
}
});
}
public void paintComponent(final Graphics gfx) { //Handle grey-out.
gfx.setColor(getBackground());
Rectangle rect = gfx.getClipBounds();
gfx.fillRect(rect.x, rect.y, rect.width, rect.height);
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible)
requestFocus();
}
}
So the version I used following Guillaume Polet’s suggestion was
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class InputSink extends JPanel {
KeyEventDispatcher blockingDispatcher = new KeyEventDispatcher() {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
return InputSink.this == ((JFrame) SwingUtilities.getWindowAncestor((Component) e.getSource())).getGlassPane(); //Consume!
}
};
public InputSink) {
this(0.2f); //Default opacity.
}
public InputSinkfloat alpha) {
setOpaque(false);
setBackground(new Color(0, 0, 0, alpha)); //Just store it here.
addMouseListener(new MouseAdapter() {});
addKeyListener(new KeyAdapter() {});
}
public void paintComponent(final Graphics gfx) { //Handle grey-out.
gfx.setColor(getBackground());
Rectangle rect = gfx.getClipBounds();
gfx.fillRect(rect.x, rect.y, rect.width, rect.height);
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible)
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(blockingDispatcher);
else
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(blockingDispatcher);
}
}
Thank you!
You can add a
KeyEventDispatcherto theKeyboardFocusManagerto block keyboard input.Small demo below:
Normally, the button can be activated with the Space key, but you will see that it actually gets blocked.