I'm working on an application that allows me to show and hide split planes.
I've read some articles on how to get this but its not what I'm looking for.
here's the code Ive written:
Im currently using netbeans.
private void jSplitPane1MouseEntered(java.awt.event.MouseEvent evt) {
if(MouseInfo.getPointerInfo().getLocation() == jSplitPane1.getLeftComponent().getLocation()){
jSplitPane1.setDividerLocation(100);
System.out.println("Mouse Entered");
}else{
jSplitPane1.setDividerLocation(20);
System.out.println("Mouse Exited");
}
}
I have referred to these posts:
How to make JSplitPane auto expand on mouse hover?
Get Mouse Position
What I want to happen is when I mouse over the left side of the jSplitPane, I would get the divider to extend to 100 as per my first if statement, and when it exists the left side, it contracts back to divider location 20.
This is really, really tricky.
You could use a MouseListener on the "left" component and monitor the mouseEntered and mouseExited events, but these will also get triggered when when you move into and out of a child component which has a MouseListener of it's own (like a JButton).
Okay, you could use a MouseMotionListener on the JSplitPane and monitor for the mouseMoved event and check where the mouse cursor is, but this goes to hell the moment the components (left/right) get their own MouseListener, as the MouseEvents are no longer delivered to the JSplitPane
So, one of the last options you have is to attach a global AWTListener to the event queue and monitor for events which occur on the JSplitPane itself, for example...
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 BorderLayout());
JSplitPane pane = new JSplitPane();
pane.setLeftComponent(makePane(Color.RED));
pane.setRightComponent(makePane(Color.BLUE));
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if (event instanceof MouseEvent) {
MouseEvent me = (MouseEvent) event;
if (pane.getBounds().contains(me.getPoint())) {
System.out.println("Global Motion in the pane...");
me = SwingUtilities.convertMouseEvent(me.getComponent(), me, pane);
Component left = pane.getLeftComponent();
if (left.getBounds().contains(me.getPoint())) {
pane.setDividerLocation(100);
} else {
pane.setDividerLocation(20);
}
}
}
}
}, MouseEvent.MOUSE_MOTION_EVENT_MASK);
// You don't need this, this is to demonstrate
// that mouse events aren't hitting your component
// via the listener
pane.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
System.out.println("Motion in the pane...");
Component left = pane.getLeftComponent();
if (left.getBounds().contains(e.getPoint())) {
pane.setDividerLocation(100);
} else {
pane.setDividerLocation(20);
}
}
});
pane.setDividerLocation(20);
add(pane);
}
protected JPanel makePane(Color background) {
JPanel pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
pane.setLayout(new GridBagLayout());
pane.add(new JButton("..."));
pane.setBackground(background);
pane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("...");
}
});
return pane;
}
}
}
Related
I must use a swing-ui designer tool to create my UI, that only supports graphically editing JPanels. Those panels (they basically contain complex button designs) to work like a JButton. I cannot use anything other than JPanel as base class of these panels (UI editor limitation).
What is the most generic solution to do this?
Create a custom button that uses the panel's draw method instead of
it's own?
Create a base-panel class that reimplements the whole
button?
Another more elegant solution?
Here is a quick demo, to show you how you could use borders to simulate a button.
The demo also reacts to mouse and key events :
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
public class JPanelButton extends JPanel {
Border raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
Border loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
public static void main(final String[] args) {
JFrame frame = new JFrame();
final JPanelButton panel = new JPanelButton();
panel.raiseBorder();
panel.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
panel.lowerBorder();
}
#Override
public void mouseReleased(final MouseEvent e) {
panel.raiseBorder();
}
});
panel.setFocusable(true); // you need this or the panel won't get the key events
panel.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(final KeyEvent e) {
panel.lowerBorder();
}
#Override
public void keyReleased(final KeyEvent e) {
panel.raiseBorder();
}
});
frame.setContentPane(panel);
frame.setSize(100, 100);
frame.setVisible(true);
}
public void raiseBorder() {
setBorder(raisedetched);
}
public void lowerBorder() {
setBorder(loweredetched);
}
}
Simply add MouseListener.
JPanel jp = new JPanel();
jp.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
System.out.println("Clicked");
}
});
If this answer isn't specific enough, leave a comment and I'll give you more explanation.
I am trying to create a JTextArea which scrolls to bottom every time a text is appended to that text area. Otherwise, the user should be able to scroll top and see previous message. I used this code:
JTextArea terminalText = new JTextArea();
JPanel terminal = new JPanel();
terminal.setLayout(new BorderLayout());
add(terminal); //Adds the terminal to mother JPanel
//I added scrollbar to my JTextArea
JScrollPane scroll = new JScrollPane(terminalText);
terminal.add(scroll, BorderLayout.CENTER);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scroll.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
e.getAdjustable().setValue(e.getAdjustable().getMaximum());
}});
So far this code seems to make my text area scroll to bottom of the terminalText text area every time I append something to terminalText using terminalText.append.
However, the user cannot use scroll bar to scroll to the top to see previous message. Is there a way to fix this? Should I be using DocumentListener to achieve this?
Check out Smart Scrolling.
If the scrollbar is at the bottom, then as text is appended you will see the new text.
If the user has scrolled to a different position, then the viewport will stay there until the user scrolls back to the bottom.
As a simple (and rough) proof of concept...
This basically adds a DocumentListener to the JTextArea and on any Document event, use setCaretPosition to move the caret to the end of the document.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.WeakHashMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
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();
}
JTextArea ta = new JTextArea(10, 20);
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
MoveToTheBottom.install(ta);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(ta));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ta.append(new Date().toString() + "\n");
}
});
timer.start();
}
});
}
public static class MoveToTheBottom implements DocumentListener {
private static WeakHashMap<JTextComponent, DocumentListener> registry = new WeakHashMap<>(25);
private JTextComponent parent;
protected MoveToTheBottom(JTextComponent parent) {
this.parent = parent;
parent.getDocument().addDocumentListener(this);
}
public static void install(JTextComponent parent) {
MoveToTheBottom bottom = new MoveToTheBottom(parent);
registry.put(parent, bottom);
}
public static void uninstall(JTextComponent parent) {
DocumentListener listener = registry.remove(parent);
if (listener != null) {
parent.getDocument().removeDocumentListener(listener);
}
}
#Override
public void insertUpdate(DocumentEvent e) {
parent.setCaretPosition(e.getDocument().getLength());
}
#Override
public void removeUpdate(DocumentEvent e) {
parent.setCaretPosition(e.getDocument().getLength());
}
#Override
public void changedUpdate(DocumentEvent e) {
parent.setCaretPosition(e.getDocument().getLength());
}
}
}
The example demonstrates a possible re-usable API which you can use to "install" and "uninstall" the support as reqiured
I have one outer panel, in the outer panel I have another jPanel placed on it.
if i do a right click on the inner panel, outer panel's right click action should happen. if i do left click on the inner panel, its own inner panel's click action should happen.
Is it possible to pass click event from one panel to another panel?
There are a number issues you need to resolve for this to work.
The first is understanding that a mouse event is contextual to the component that created it, in particular, the location information. The click point is the offset from the source components x/y position (which will be 0x0 for the mouse event). This means if you want to redispatch the event, you need to, at least, translate the location context into the parent components space.
The source component should also be changed. Failing to do this could cause issues for other parts of the API that may rely on it to make determinations (such as a popup, which will want to translate the location information from the component space to the screen space).
Secondly, you should make no assumptions about the parent. That is, you should make no assumption that the parent may or may not implement a mouse listener interfaces.
A possible solution would be to use SwingUtilities.convertMouseEvent to convert the MouseEvent raised in the inner panel to be compatible with the outer panel and then use Component#dispatchEvent to mimick the standard event handling process, for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class TestTree {
public static void main(String[] args) {
new TestTree();
}
public TestTree() {
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 OutterPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class InnerPane extends JPanel {
public InnerPane() {
setBackground(Color.RED);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Inner was clicked at : " + e.getPoint());
MouseEvent convertMouseEvent = SwingUtilities.convertMouseEvent(e.getComponent(), e, getParent());
getParent().dispatchEvent(convertMouseEvent);
}
});
}
}
public class OutterPane extends JPanel {
public OutterPane() {
setLayout(new BorderLayout());
setBorder(new EmptyBorder(10, 10, 10, 10));
add(new InnerPane());
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Outter was clicked at : " + e.getPoint());
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Have you tried something like in your inner panel?
void mouseClicked(MouseEvent e) {
if ( e.getButton() == MouseEvent.BUTTON2 ) {
outter.mouseClicked( e );
}
else {
// do job for your inner panel
}
}
Below code worked for me
JPanel parent = (JPanel) me.getComponent().getParent();
MouseMotionListener[] ml = parent.getMouseMotionListeners();
ml[0].mouseClicked(me);
Thanks all
Here's the code. It prints out the mouse location when it's in the panel but not the JTextArea. I added the mouse listener to the text area as well? The problem is that the coordinates are not consistent throughout the JFrame. Is there a way to just have one mouselistener that covers the entire jframe?
Is there a way to disable the mouse listener in the textarea?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class test extends JFrame {
public test(){
setPreferredSize(new Dimension(600,400));
JPanel p = new JPanel();
p.setBackground(Color.blue);
p.setPreferredSize(new Dimension(600,200));
JTextArea t = new JTextArea();
t.setPreferredSize(new Dimension(600,200));
add(p,BorderLayout.NORTH);
add(t,BorderLayout.SOUTH);
pack();
MouseInput m = new MouseInput();
addMouseMotionListener(m);
t.addMouseMotionListener(m);
setVisible(true);
}
public static void main(String[] args){
new test();
}
public class MouseInput implements MouseMotionListener{
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
int mx = e.getX();
int my = e.getY();
System.out.println(mx + "," + my);
}
}
}
Think of your mouse events like rain. They fall from the top of your component hierarchy down until something stops them.
Once stopped, they will no long notify other listeners lower in the hierarchy.
In you program you have and JPanel and JTextField sitting on top of another component (the content pane) sitting on a JLayeredPane sitting on top of the frame. Any one of these may be consuming the mouse event.
Try adding the MouseInput to your JPanel, p instead
Updated
This is an example of a global mouse listener (as suggested by #Hovercraft Full Of Eels, it WILL get hammered, as every mouse event will pass through it.
It also demonstrates how to translate a mouse point from it's local context to another context.
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GloablMouseListener {
public static void main(String[] args) {
new GloablMouseListener();
}
public GloablMouseListener() {
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 {
public TestPane() {
setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setBackground(Color.BLUE);
JTextArea ta = new JTextArea(10, 20);
add(panel, BorderLayout.NORTH);
add(new JScrollPane(ta), BorderLayout.SOUTH);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if (event instanceof MouseEvent) {
MouseEvent e = (MouseEvent) event;
System.out.println("Local point = " + e.getPoint());
Point p = e.getPoint();
Window window = SwingUtilities.getWindowAncestor(e.getComponent());
if (window != e.getSource() && window != null) {
p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), window);
}
System.out.println("Global point = " + p);
}
}
}, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
The JTextArea has its own MouseListener/MouseMotionListener that grabs the mouse information before any underlying class with a MouseListener or motion listener can.
This may be fixable by using an AWTEventListener, but I have not tried this myself yet.
Edit
OK, I have tried this as a for instance:
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class Test2 extends JPanel {
JTextArea textarea = new JTextArea(15, 60);
public Test2() {
JPanel topPanel = new JPanel();
topPanel.setBackground(Color.blue);
setLayout(new GridLayout(0, 1));
add(topPanel);
add(new JScrollPane(textarea));
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.printf("%20s [%03d, %03d]%n", "From MouseAdapter:", x, y);
}
});
long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK;
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent awtEvent) {
MouseEvent mouseEvent = (MouseEvent) awtEvent;
Component component = (Component) awtEvent.getSource();
Point location = component.getLocationOnScreen();
Point test2Location = Test2.this.getLocationOnScreen();
// Normalized to find the mouse location relative to the main JPanel,
// the Test2 "this" JPanel.
int x = mouseEvent.getX() + location.x - test2Location.x;
int y = mouseEvent.getY() + location.y - test2Location.y;
System.out.printf("%20s [%03d, %03d]%n", "From AWTEvent:", x, y);
}
}, eventMask );
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Test2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Test2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Try adding a MouseListener to your application's GlassPane.
See these following link. It includes a Java Web Start demo of something similar to what you want to do.
How to Use Root Panes
The keyListener does not fire action though i registered the frame to the keyListener.
In the other code, implementing the KeyListener interface, registering the JFrame and overriding the appropriate methods was all that was needed for the keyListener to work.
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Mains implements KeyListener {
private static Point point = new Point();
public Mains() {
final JFrame frame = new JFrame();
frame.setUndecorated(true);
JButton button = new JButton("Close Me");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
frame.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
point.x = e.getX();
point.y = e.getY();
}
});
frame.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
Point p = frame.getLocation();
frame.setLocation(p.x + e.getX() - point.x, p.y + e.getY()
- point.y);
}
});
frame.setSize(300, 300);
frame.setLocation(200, 200);
frame.setLayout(new BorderLayout());
frame.getContentPane().add(button, BorderLayout.NORTH);
frame.getContentPane().add(new JLabel("Drag Me", JLabel.CENTER),
BorderLayout.CENTER);
frame.setVisible(true);
frame.addKeyListener(this);
}
public static void main(String[] args) {
new Mains();
}
public void keyPressed(KeyEvent e) {
System.out.println("pressed");
}
public void keyReleased(KeyEvent e) {
System.out.println("released");
}
public void keyTyped(KeyEvent e) {
}
}
A KeyListerner is only capable of receiving key events if the component it is attached to can receive focus and has focus.
The problem with a JFrame is that it contains a JRootPane, which contains a content pane which occupies the entire surface of frame, which will prevent the frame from ever gaining focus, this, never be capable of receiving key events.
KeyListener is rarely the right API of choice, normally, it's better to use key bindings
You may also find How to use root panes of interest
By default, JFrame window components are not focusable. Although you can call JFrame.setFocusable(true), it is far better to use Key Bindings.
KeyListener requires focus with components for the interaction with KeyEvents to work. In contrast, when using Key Bindings, you can map an Action to a KeyStroke even when a component doesn't have focus.