Programming a GUI with resizable components using Java.swing - java

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.

Related

Java: Adding events to dynamically created components

I'm building a UI in Java. I want to create new components, like a JLabel, using a button. So every time I click a button it creates a new JLabel and places them in a specific JPanel.
Then, I want to be able to do some things with the labels based on how the user clicks on them.
With a left mouse press I want them to be able to drag the labels around the screen.
With a right mouse click I want to be open a new window where certain data can be entered, tied to the label (which might involve dynamically creating variables).
I've been toying around with some code I've Googled around for. I can get a button to create new labels in a panel, but when I try to get them to drag, I can only get one label at a time to appear, and after a second button press, moving the label isn't smooth, it jumps around.
I haven't even tried to implement any of the right mouse click things yet. If anyone can point me in the right direction, I'd appreciate it.
public class Testing {
JFrame frame;
//Launch the application.
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
Testing window = new Testing();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
//Create the application.
public Testing() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
JPanel area;
JButton btnCreate;
JLabel dragLabel;
frame = new JFrame();
frame.setBounds(100, 100, 511, 542);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setVisible(true);
area = new JPanel();
area.setBounds(10, 11, 477, 404);
frame.getContentPane().add(area);
area.setLayout(new BorderLayout());
btnCreate = new JButton("Create Label");
dragLabel = new JLabel("Drag Me");
btnCreate.setBounds(10, 425, 477, 67);
frame.getContentPane().add(btnCreate);
btnCreate.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
area.add(dragLabel);
area.revalidate();
DragListener drag = new DragListener();
dragLabel.addMouseListener(drag);
dragLabel.addMouseMotionListener(drag);
}
});
}
}
class DragListener extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me) {
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
if(SwingUtilities.isLeftMouseButton(me)){
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}
}
EDIT - I'm fairly certain the primary issue is in how the JLabel itself is being added to the panel. Every time the button is being pushed it's adding the same label over again, and this is gumming up the works.
Unfortunately, I'm not sure how to deal with that. I've done a bit more digging, and since dynamic variables aren't possible, I'm going to have to use an array or a map or some sort. With that, it appears I can declare arrays of components. Would something like that be necessary for my purposes?
Really odd stuff in your code. I don't want to go everything, and I'm not an expert by any stretch of the imagination, but I tried to remove redundant or contradictory stuff. I suspect a part of what you did was just copy pasting bits without really fitting them into the code.
Anyway, you needed to create the label inside the listener, so that it creates a new one everytime you click. Otherwise you only ever create one label and just reuse the same everytime.
I implemented a dialog on right click to enter the label name, don't know what you wanted to do but at least it detects right clicks.
Also in general it's easier to use layout managers instead of hardcoding everything. Here you had a borderlayout but were ignoring it.
class Main {
//Launch the application.
public static void main(String[] args) {
DrageableLabel window = new DrageableLabel();
}
}
public class DrageableLabel {
public DrageableLabel() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
JFrame frame = new JFrame();
Container area = frame.getContentPane();
area.setLayout(new BorderLayout());
JButton btnCreate = new JButton("Create Label");
btnCreate.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
/*
This is where you create your new window
for now I've added a dialog that takes a string parameter and creates a label with that string
I moved the method code to create a new drageable label outside the actionlistener to make it less confusing and reuseable
Either build w-e you want directly in here
or call a method that does it (which I prefer)
*/
String string = JOptionPane.showInputDialog(frame, "Enter your message", "Messages", JOptionPane.CANCEL_OPTION);
addDrageableLabel(string, area);
} else if (SwingUtilities.isLeftMouseButton(e)) {
addDrageableLabel("Drag me", area);
}
}
});
area.add(btnCreate, BorderLayout.SOUTH);
frame.setBounds(100, 100, 511, 542);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
// This is the method that creates and adds a drageable label
public void addDrageableLabel(String labelName, Container container) {
JLabel dragLabel = new JLabel(labelName);
container.add(dragLabel, BorderLayout.CENTER);
container.validate();
DragListener drag = new DragListener();
dragLabel.addMouseListener(drag);
dragLabel.addMouseMotionListener(drag);
}
}
class DragListener extends MouseInputAdapter {
Point location;
MouseEvent pressed;
#Override
public void mousePressed(MouseEvent me) {
pressed = me;
}
#Override
public void mouseDragged(MouseEvent me) {
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}

Clicking JPanel with inner components

I have a main JPanel class (not exact code):
class Panel extends JPanel {
public void initGUI() {
setLayout(...);
JTabbedPane tabbedPane = new JTabbedPane();
JPanel boxPanel = new JPanel(...);
tabbedPane.addTab("test", boxPanel);
JLabel label = new JLabel("Label")
boxPanel.add(label);
add(tabbedPane);
}
}
I want to be able to click anywhere on the Panel or its inner components and return the Panel.
public class PanelMouseAdapter extends MouseAdapter {
public void mouseReleased(MouseEvent e) {
Panel panel = (Panel)e.getSource();
//do other stuff
}
}
And for each Panel I'm adding this mouse listener.
But it only works around the edges of the Panel, any inner components are ignored. I need it to be able to click anywhere in that Panel.
I need to maintain that anywhere I click it will return the Panel object (as in the mouse listener).
Thanks for any feedback.
Not sure I understand the question. Your demo code only shows a single panel, so why do you care what parent panel is clicked on? The better your explanation of the requirement the better the solution we can provide.
Anyway, check out Global Event Listeners. This will allow you to listen for a mousePressed event (which is a better then listening for a mouseClicked).
Next you need to create a custom panel (MyCustomPanel) that you use for the top level panel.
Now, whenever a mousePressed is generated you can get the source of the event and then use:
MyCustomPanel panel = SwingUtilties.getAncestorOfClass(MyCustomPanel.class, (Component)event.getSource());
You can use Container#getComponents() for this case. For example consider the code snippet given below (look for addListeners(Container) method):
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
class SPanel extends JPanel
{
public void init()
{
this.add(new JButton("Button"));
this.add(new JLabel("Click"));
JPanel pan = new JPanel();
pan.add(new JButton("PanButton"));
pan.add(new JTextField(29));
add(pan);
addListeners(this);
}
public void addListeners(Container comp)
{
Component[] components = comp.getComponents();
for (Component component : components)
{
if (component instanceof Container)
{
Component[] child = ((Container)component).getComponents();
if (child != null && child.length > 0)
{
addListeners((Container)component);
}
}
component.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent evt)
{
System.out.println(evt.getSource().getClass());
}
});
}
}
public static void main(String[] args)
{
SwingUtilities.invokeLater( new Runnable()
{
#Override
public void run()
{
SPanel sp = new SPanel();
sp.init();
JFrame frame = new JFrame("Frame");
frame.getContentPane().add(sp);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
Solved issue.
Initially I was going to open a popup menu once I had the mouse listener in place.
But now I added JComponent.setComponentPopupMenu as Polum suggested, not to the Panel but to the tabbedPane.
Then I added a listener to the popup menu, got the source object via event in popupMenuWillBecomeVisible method, then the component via source.getInvoker(), then get the parent of the invoker component and check if its an instance of PairBox.

Java Swing Scrolling By Dragging the Mouse

I am trying to create a hand scroller that will scroll as you drag your mouse across a JPanel. So far I cannot get the view to change. Here is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class HandScroller extends JFrame {
public static void main(String[] args) {
new HandScroller();
}
public HandScroller() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
final JPanel background = new JPanel();
background.add(new JLabel("Hand"));
background.add(new JLabel("Scroller"));
background.add(new JLabel("Test"));
background.add(new JLabel("Click"));
background.add(new JLabel("To"));
background.add(new JLabel("Scroll"));
final JScrollPane scrollPane = new JScrollPane(background);
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
JViewport viewPort = scrollPane.getViewport();
Point vpp = viewPort.getViewPosition();
vpp.translate(10, 10);
background.scrollRectToVisible(new Rectangle(vpp, viewPort.getSize()));
}
};
scrollPane.getViewport().addMouseListener(mouseAdapter);
scrollPane.getViewport().addMouseMotionListener(mouseAdapter);
setContentPane(scrollPane);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
}
I would think that this would move the view by 10 in the x and y directions, but it is not doing anything at all. Is there something more that I should be doing?
Thanks.
I think so you can move the view in all directions with this code
public void mouseDragged(MouseEvent e) {
JViewport viewPort = scroll.getViewport();
Point vpp = viewPort.getViewPosition();
vpp.translate(mouseStartX-e.getX(), mouseStartY-e.getY());
scrollRectToVisible(new Rectangle(vpp, viewPort.getSize()));
}
public void mousePressed(MouseEvent e) {
mouseStartX = e.getX();
mouseStartY = e.getY();
}
Your code does work. Simply, there is nothing to scroll, as the window is large enough (actually, pack() has caused the JFrame to resize to fit the preferred size and layouts of its subcomponents)
Remove pack(); and replace that line with, say, setSize(60,100); to see the effect.
It is working. However, when you run this code, the JScrollPane is made large enough to fit all your items. Add (for instance)
scrollPane.setPreferredSize(new Dimension(50, 50));
and you'll see that your mouse events work fine.

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.

Java Swing - Get X,Y From CLick

public class cPan extends JPanel implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
}
}
I have the above code which catches actions from within my JPanel.
Im confused about how i would get an x,y cordinate from within my JPanel e.g. where i click
So if i click on 100,200 (x,y) i would like to be able to see this.
I've look the function givens from arg0 but cant find anything useful.
Where am i going wrong?
Use a MouseListener instead. This way, you'll get a MouseEvent, from which you can get the clicked point by calling MouseEvent#getPoint().
public class cPan extends JPanel implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
// or
int x = e.getX();
int y = e.getY();
}
}
public class cPan extends JPanel implements ActionListener{
should be
public class cPan extends JPanel implements MouseListener{
more in Oracle turorial How to Write a Mouse Listener and with compare with wrong listener How to Write an Action Listener for MouseEvents
You need to add mouse listener:
JPanel panel = new JPanel ();
panel.setPreferredSize (new Dimension (640, 480));
panel.addMouseListener (new MouseAdapter() {
#Override
public void mouseClicked (MouseEvent e) {
JOptionPane.showMessageDialog(
e.getComponent (), "X: " + e.getX () + ", Y: " + e.getY ());
}
});
JFrame frame = new JFrame ("Click");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane ().setLayout (new BorderLayout ());
frame.getContentPane ().add (panel, BorderLayout.CENTER);
frame.pack ();
frame.setVisible (true);
ActionListener is used to notify you when, well, some kind of nondescript action has occurred.
There is no way to extract information about what caused the action (like mouse click or key action)
To get information about mouse events, you need to use a MouseListener attached to the component(s) you are interested in monitoring.
Check out How to use Mouse Listeners for more information

Categories