dispatching mouse events to the deepest visible JComponent - java

There are a transparent JPanel on top of the screen, and a background JPanel and a JButton added to it(All are visible and have mouse listeners)
After handling mouse events in the transparent panel's listeners, they should be dispatched to the next deeper component(i.e. background panel or its button)
Clicking on background panel is OK but clicking on the button cause an unwanted ClassCastException.
example:
//making frame
frame=new JFrame();
frame.setVisible(true);
//making layered pane
layeredPane = new JLayeredPane();
frame.add(layeredPane);
//transparent panel on top
frontPanel = new JPanel();
frontPanel.setOpaque(false);
layeredPane.add(frontPanel,Integer.valueOf(1));
//background panel containing a button
backPanel = new JPanel();
JButton button = new JButton();
backPanel.add(button);
layeredPane.add(backPanel,Integer.valueOf(0));
//listeners
button.addMouseListener(new MouseListener()...a blank listener...);
backPanel.addMouseListener(new MouseListener()...a blank listener...);
frontPanel.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
SwingUtilities.getDeepestComponentAt(backPanel, e.getX(), e.getY()).dispatchEvent(e);
}
#Override
public void mousePressed(MouseEvent e) {
SwingUtilities.getDeepestComponentAt(backPanel, e.getX(), e.getY()).dispatchEvent(e);
}
#Override
public void mouseReleased(MouseEvent e) {
SwingUtilities.getDeepestComponentAt(backPanel, e.getX(), e.getY()).dispatchEvent(e);
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
//setting dimensions
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frontPanel.setSize(screenSize);
backPanel.setSize(screenSize);
frame.setSize(screenSize);
//now clicking on the button cause a ClassCastException: JPanel cannot be cast to javax.swing.AbstractButton. why?

You can't just dispatch the old event. The old event contains information about the original event (ie. the component source).
You need to create a new MouseEvent before dispatching it.
For example see the GlassPaneDemo from the Swing tutorial on How to Use Root Panes.

Related

adding a MouseListener to two adjacents JComponent, Bug

I created a component Named FAdjacentTooltips, which show information about another JComponent (JLabel,JPanel...), when the mouse enter to this one and disappear when the mouse exit it, here is the events initialisation in the FAdjacentToolTips class:
this.holderComponent.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
FAdjacentToolTips.this.setSize(0, 0);
FAdjacentToolTips.this.revalidate();
FAdjacentToolTips.this.repaint();
}
#Override
public void mouseEntered
(MouseEvent e) {
if (!activationFlag) {
FAdjacentToolTips.this.build();
activationFlag = true;
}
FAdjacentToolTips.this.setSize(toolTypeWidth, toolTypeHeight);
FAdjacentToolTips.this.revalidate();
FAdjacentToolTips.this.repaint();
}
});
when the mouse enter to the JComponent : holderComponent, the FAdjacentToolTips is set to a given size, and when the mouse exit the holderComponent the FAdjacentToolTips size is set to zero.
It work well when adding FAdjacentToolTips to one component, but having issue
when adding it to two Adjacents JComponents (One near another), here is an example with two JLabel
public static void main(String[] args) throws FileNotFoundException, IOException {
JFrame frame = new JFrame();
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
frame.setMinimumSize(new Dimension(1280, 700));
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
CustomLabel firstLabel = new CustomLabel("first Label", Color.yellow);
firstLabel.setLocation(100, 100);
CustomLabel secondeLabel = new CustomLabel("second Label", Color.blue);
secondeLabel.setLocation(100, 300);
JPanel panel = new JPanel(null);
panel.setOpaque(true);
panel.setPreferredSize(new Dimension(2000, 2000));
panel.setBackground(Color.red);
panel.add(secondeLabel);
panel.add(firstLabel);
firstLabel.addFAdjacentToolTip("AAAAAAAA", FAdjacentToolTipable.LEFT_OF_CONTAINER);
secondeLabel.addFAdjacentToolTip("BBBBB", FAdjacentToolTipable.LEFT_OF_CONTAINER);
panel.setPreferredSize(new Dimension(2000, 2000));
frame.getRootPane().getContentPane().add(panel, BorderLayout.CENTER);
frame.setVisible(true);
}
class CustomLabel extends JLabel implements FAdjacentToolTipable {
public CustomLabel(String text, Color color) {
super(text);
this.setSize(new Dimension(200, 200));
this.setOpaque(true);
this.setBackground(color);
}
#Override
public void addFAdjacentToolTip(String text, int side) {
FAdjacentToolTips fAdjacentToolTips = new FAdjacentToolTips(this, text, side, Color.WHITE, Color.BLACK, Color.black, this.getFont());
fAdjacentToolTips.activate();
}
}
firstLabel is on the top of secondLabel, here is the issue behavior:
it work well when moving the mouse from the outside to one of the labels, the FAdjacentToolTips appear/disappear when the mouse Enter/Exit the labels
when moving the mouse from firstLabel to secondLabel, the firstLabel FAdjacentToolTips disappear (as needed) but the secondLabel FAdjacentToolTips don't appear.
after many testing here the informations I had gathered:
even if the FAdjacentToolTips don't show up on the screen, the mouseEnter method is called, and the size is set correctly.
My question is: Why the FAdjacentToolTips don't show up when moving from one label to another, even if the events are triggered well, and the size is set correctly?
here is a runnable of a test programme :
here is the source code :http://www.filedropper.com/javatest
here is the runnable : http://www.filedropper.com/javatest_1
to understand my problem :
1-try to move the mouse from the JFrame to one of the labels.
2-try to move the mouse from one label to another
please tell me if you need more informations.

Adding multiple Panels and events to a GUI

I'm new to java and working on a GUI assignment and I'm running into some issues. The premise is a simple GUI window that has a few mouse events and a few keyboard events.
I put together a window with some mouse events and once that was working started to add a couple JTextFields to the window but they're not showing up on the window and I'm not exactly sure why.
Here is the problem now. I created a new panel (panel2) to add the JTextFields to the window and I now see the JTextFields but it overtakes the entire window and the Mouse Events do not work with it. If I add the JTF to the panel above the Mouse Events then the JTF do not show up and the Mouse Events work.......
code
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class EventDemo extends JFrame {
private JPanel mousePanel;
private JPanel panel1;
private JPanel panel2;
private JPanel panel3;
private JLabel statusBar;
private JLabel directions1;
private JLabel directions2;
private JLabel directions3;
private JTextField textField1;
private JTextField textField2;
public EventDemo() {
super("EVENT DEMO PROGRAM");
panel1 = new JPanel();
panel2 = new JPanel();
//Add directions for the events to top of the window.
directions1 = new JLabel("Enter & Leave window." +
" Press & hold, release, drag, move cursor to display a message in statusbar." +
" Clicking in one spot will display coordinates.");
panel1.add(directions1);
add(panel1, BorderLayout.PAGE_START);
//Add mouse and statusBar to panel.
mousePanel = new JPanel();
mousePanel.setBackground(Color.WHITE);
add(mousePanel, BorderLayout.CENTER);
statusBar = new JLabel("Default");
add(statusBar, BorderLayout.SOUTH);
//Create handler object for Mouse events
TheHandler handler = new TheHandler();
mousePanel.addMouseListener(handler);
mousePanel.addMouseMotionListener(handler);
textField1 = new JTextField(10);
panel2.add(textField1, BorderLayout.SOUTH);
textField2 = new JTextField("Enter Text Here");
panel2.add(textField2, BorderLayout.SOUTH);
add(panel2);
TheHandler handlerJTF = new TheHandler();
textField1.addActionListener(handlerJTF);
textField2.addActionListener(handlerJTF);
}
private class TheHandler implements MouseListener, MouseMotionListener {
public void mouseClicked(MouseEvent event) {
statusBar.setText(String.format("YOU CLICKED THE MOUSE AT %d, %d", event.getX(), event.getY()));
}
public void mousePressed(MouseEvent event) {
statusBar.setText("YOU HAVE PRESSED THE MOUSE BUTTON.");
}
public void mouseReleased(MouseEvent event) {
statusBar.setText("YOU HAVE RELEASED THE MOUSE BUTTON.");
}
public void mouseEntered(MouseEvent event) {
statusBar.setText("YOU HAVE ENTERED THE WINDOW THE BACKGROUND CHANGES TO RED.");
mousePanel.setBackground(Color.RED);
}
public void mouseExited(MouseEvent event) {
statusBar.setText("EXITING THE WINDOW, BACKGROUND CHANGES BACK TO WHITE.");
mousePanel.setBackground(Color.WHITE);
}
//Mouse Motion events.
public void mouseDragged(MouseEvent event) {
statusBar.setText("YOU ARE DRAGGING THE MOUSE.");
}
public void mouseMoved(MouseEvent event) {
statusBar.setText("YOU ARE MOVING THE MOUSE AROUND.");
}
}
public void actionPerformed(ActionEvent event) {
textField1.getText();
textField2.getText();
}
public static void main(String[] args) {
EventDemo go = new EventDemo();
go.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
go.setSize(960, 300);
go.setVisible(true);
new EventDemo();
}
}
TheHandler does not implement ActionListener which is the required type of JTextField#addActionListener. See How to Write an Action Listeners and How to Use Text Fields for more details
Your actionPerformed method isn't actually defined in TheHandler class, but is a method of the EventDemo class
You should use the #Override annotation when you think you're overriding methods (or implementing methods from an interface) as this will cause a compiler error when you're wrong (for some reason)
Your fields aren't appearing for two reasons, first, textField is added to panel2 which is never added to anything and textField2 is been added to the frame (at the default position of BorderLayout.CENTER), but is been overriden/circumvented by add(mousePanel, BorderLayout.CENTER);, so it's actually never laid out by the frames BorderLayout. Have a look at How to Use Borders for more details

Custom painting not working as intended

I'm making a GUI application which finds and draws convex hull for a set of points.
Here's the frame:
public class Frame extends JFrame{
Panel panel = new Panel();
JButton drawHull = new JButton("Draw Convex Hull");
Frame(String title) {
super(title);
// setLayout
setLayout(new BorderLayout());
// add components to the frame
add(panel, BorderLayout.CENTER);
add(drawHull, BorderLayout.SOUTH);
// add actionListener for drawHull button
drawHull.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
panel.drawConvexHull();
}
}
);
}
}
The frame contains a button (at the bottom of the frame) and a Panel object.
Here's the Panel class:
public class Panel extends JPanel{
ArrayList<Point> points = new ArrayList<Point>();
public Panel() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.add(new Point(e.getX(), e.getY()));
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
if(!points.isEmpty()) {
Point p = points.get(points.size()-1);
g.fillOval(p.x-2, p.y-2, 4, 4);
}
}
public void drawConvexHull() {
// code for finding convex hull
}
}
I have added a mouseListener to the Panel class so that when the user clicks anywhere on the panel, a point is drawn.
When I run the code, everything looks fine. Here's a sample run.
Now here's the problem:
As soon as I click the panel, this happens
A button appears on the top of the frame. Why is this happening?
One more thing. When I click this button on the top, a point is drawn. Here, have a look:
Without calling super.paintComponent(g) in paintComponent the background is not repainted leading to unpredictable results on the panel component.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point point: points) {
g.fillOval(point.x - 2, point.y - 2, 4, 4);
}
}

JLabel ToolTip interferes with MouseListener

I have Java Swing application ToolTipMouseTest
The critical line is label.setToolTipText("label" + i);. Once it is commented out very click on a label produces 2 mousePressed in console. With this line enabled click on labels would produce nothing.
Is this expected behaviour or a bug? My goal is to show tooltips without disabling MouseListener from working.
Almost SSCCE, but without imports:
public class ToolTipMouseTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ToolTipMouseTest();
}
});
}
public ToolTipMouseTest() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JLayeredPane lpane = new JLayeredPane() {
#Override
public Dimension getPreferredSize() {
return new Dimension(600,400);
}
};
MouseAdapter1 mouseAdapter1 = new MouseAdapter1();
lpane.addMouseListener(mouseAdapter1);
frame.add(lpane);
JPanel panel1 = new JPanel();
panel1.setSize(new Dimension(600, 400));
panel1.setOpaque(false);
lpane.add(panel1, JLayeredPane.PALETTE_LAYER);
JPanel panel2 = new JPanel();
for (int i = 0; i < 5; i++) {
JLabel label = new JLabel("Label " + i);
panel2.add(label);
label.setToolTipText("label" + i); //HERE!!
}
JScrollPane spane = new JScrollPane(panel2) {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
};
MouseAdapter2 mouseAdapter2 = new MouseAdapter2();
spane.addMouseListener(mouseAdapter2);
panel1.add(spane);
frame.pack();
frame.setVisible(true);
}
private class MouseAdapter1 extends MouseAdapter {
#Override
public void mousePressed (MouseEvent me) {
System.out.println("1 mousePressed");
}
}
private class MouseAdapter2 extends MouseAdapter {
#Override
public void mousePressed (MouseEvent me) {
System.out.println("2 mousePressed");
}
}
}
It is working as intended. Let me explain why.
When a component has no listener, the mouse events will be propagated.
When any component has at least one MouseListener set on it - it will consume any mouse enter/exit/click/press/release events from going down in the components hierarchy.
It's the same for any listener such as MouseMotionListener with
mouse dragged/moved.
When you are adding a tooltip to a component (JLabel in your case), the component automatically receive a new MouseListener and a MouseMotionListener from ToolTipManager. The registerComponent method from ToolTipManager class do this (it's invoked by setToolTipText) :
public void registerComponent(JComponent component) {
component.removeMouseListener(this);
component.addMouseListener(this);
component.removeMouseMotionListener(moveBeforeEnterListener);
component.addMouseMotionListener(moveBeforeEnterListener);
component.removeKeyListener(accessibilityKeyListener);
component.addKeyListener(accessibilityKeyListener);
}
In your case - JLabels are consuming mouse events and mouse motion events, and as such prevents from propagating the events to the JLayeredPane because ToolTipManager listener added itself when the tooltip is set (setToolTipText) on the component.
In order to work around this, register a listener that will pass events down. You can add that listener to every component with a tooltip that should pass mouse events down (e.g to a JLayeredPane, a JScrollPane, etc).
Here is a small example of how that could be done:
var destinationComponent = // the JLayeredPane, JScrollPane, etc with mouse listeners
componentWithToolTip.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent event) {
destinationComponent.dispatchEvent(
SwingUtilities.convertMouseEvent(
event.getComponent(), // the component with the tooltip
event,
destinationComponent
)
);
}
// implements other mouse* handlers as required.
});
In that setup componentWithToolTip will have 2 listeners, the one from the ToolTipManager and the propagating one. When componentWithToolTip all its listeners will be triggered, and the propagating listener will dispatch to the declared destination component destinationComponent. So that destinationComponent listeners receive the mouse events as well.

Programming a GUI with resizable components using Java.swing

I'm supposed to program a GUI. Every component in this GUI has to have the possibility to be resized dynamically.
So far I worked with GlassPane and ContentPane, added a JPanel and on it a button. When clicking the GlassPane gets the event, analyses the underlying component, creates a new handle for this special component and handles it over to the said component.
I added a button that should change its size automatically. Works like I wanted it to work.
BUT: When I change the size of the frame and click on the button now, nothing happens. The GlassPane is able to identify the button, but something seems to be wrong...
Here's the code for the interception of GlassPane and giving the event to the component:
private void resendEvent(MouseEvent e) {
//Point p = SwingUtilities.convertPoint(content.getGlassPane(), e.getPoint(), content.getContentPane());
Point p = e.getPoint();
Component component = SwingUtilities.getDeepestComponentAt(content.getContentPane(), p.x, p.y);
System.out.println(component.toString());
Point p2 = component.getLocation();
MouseEvent event = new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(), p2.x, p2.y, e.getClickCount(), e.isPopupTrigger());
//following lines have the same effects
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
//component.dispatchEvent(event);
}
Thanks for your help/suggestions
Okay, here's some more Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import resize.ResizablePanel;
public class ExampleProblem extends JFrame {
public ExampleProblem () {
JPanel glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.addMouseListener(new MouseAdapter(){
#Override
public void mousePressed(MouseEvent e) {
resendEvent(e);
}
#Override
public void mouseReleased(MouseEvent e) {
resendEvent(e);
}
});
this.setGlassPane(glassPane);
glassPane.setVisible(true);
JButton b = new JButton("Test");
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Button clicked");
}});
JPanel p = new JPanel();
p.add(b);
setContentPane(p);
pack();
setVisible(true);
}
private void resendEvent(MouseEvent e) {//Point p = SwingUtilities.convertPoint(content.getGlassPane(), e.getPoint(), content.getContentPane());
Point p = e.getPoint();
Component component = SwingUtilities.getDeepestComponentAt(this.getContentPane(), p.x, p.y);
System.out.println(component.toString());
Point p2 = component.getLocation();
MouseEvent event = new MouseEvent(component, e.getID(), e.getWhen(), e.getModifiers(), p2.x, p2.y, e.getClickCount(), e.isPopupTrigger());
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(event);
}
public static void main(String[] args) {
new ExampleProblem();
}
}
Hope, the problem is a bit clearer now.
Is it possible, that I shouldn't have used setContentPane()? But we had to overwrite the JContentPane for resizing the components on it...
You have to determine the component point from your coordinate. Use this instead of p2 to to create the event:
Point p2 = SwingUtilities.convertPoint(this.getGlassPane(), p, component);
You might be able to use the Component Resizer. It would need to be added to all components in advance.
Or maybe you could add the listener when you select the component and then remove the listener when you deselect the component.
The code you posted is of little help when guessing what your actual program is doing. When you have problems with your code you need to post a SSCCE.

Categories