I have two JPanels
equal in size, one over the top of the other. The top layer serves as a drag selection panel, and the other one has other components added to it. My problem is that the mouse event handlers of these added components aren't triggered, because they are handled by the overlaying panel instead. How can I still drag over the top of these added components, but still have mouseEntered
and mouseExited
enabled for the underlaying components?
Here is a screenshot:
As you can see, the selection rectangle is painted on the overlaying JPanel
, but it's as if my mouse can't get through this panel to see what's underneath (in search of a better way to explain that).
An alternative to re-inventing the wheel is using JLayer (new to jdk7, available for jdk6 in the SwingLabs subproject JXLayer):
JLayer is a universal decorator for Swing components which enables you to implement various advanced painting effects as well as receive notifications of all AWTEvents generated within its borders
Below is a quick example - just to demonstrate its usage, logic obviously incomplete :) - of spanning a rubberband with mouseEvents
// UI which allows to span a rubberband on top of the component
public static class RubberBandUI<V extends JComponent> extends LayerUI<V> {
private JLayer<?> l;
private Rectangle rubberband;
private boolean selecting;
@Override
public void installUI(JComponent c) {
super.installUI(c);
l = (JLayer<?>) c;
// this LayerUI will receive mouse/motion events
l.setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
@Override
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
// JLayer must be returned to its initial state
l.setLayerEventMask(0);
l = null;
}
@Override
public void paint(Graphics g, JComponent l) {
// this paints layer as is
super.paint(g, l);
if (rubberband == null) return;
Graphics2D g2 = (Graphics2D) g;
// custom painting is here
g2.setColor(Color.RED);
g2.setStroke(new BasicStroke(2f));
g2.draw(rubberband);
}
// intercept events as appropriate
@Override
protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends V> l) {
super.processMouseMotionEvent(e, l);
if (e.getID() == MouseEvent.MOUSE_DRAGGED && selecting) {
Point point = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
adjustRubberband(point);
l.repaint();
}
}
@Override
protected void processMouseEvent(MouseEvent e, JLayer<? extends V> l) {
super.processMouseEvent(e, l);
if (e.getID() == MouseEvent.MOUSE_RELEASED) {
endRubberband();
}
if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getSource() == l) {
startRubberband(e.getPoint());
}
}
// logic to start/stop/adjust the rubberband
private void adjustRubberband(Point point) {
// logic to span the rubberband
int width = point.x - rubberband.x;
int height = point.y - rubberband.y;
rubberband.setSize(width, height);
}
private void startRubberband(Point p) {
rubberband = new Rectangle(p);
selecting = true;
// block events to child components while drawing
l.getGlassPane().setVisible(true);
l.repaint();
}
private void endRubberband() {
selecting = false;
l.getGlassPane().setVisible(false);
l.repaint();
}
public void clear() {
rubberband = null;
l.repaint();
}
}
Sample usage snippet:
JPanel panel = new JPanel();
for (int i = 0; i < 3; i++) {
panel.add(new JButton("JButton"));
panel.add(new JCheckBox("JCheckBox"));
panel.add(new JTextField("JTextField"));
}
JLayer<JComponent> l = new JLayer<JComponent>(panel, new RubberBandUI<JComponent>());
frame.add(l);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With